Skip to content

Commit

Permalink
feat(icon-loader): implement icon-loader package
Browse files Browse the repository at this point in the history
  • Loading branch information
oktaysenkan committed Oct 5, 2024
1 parent 811319c commit 1f4b78a
Show file tree
Hide file tree
Showing 19 changed files with 240 additions and 135 deletions.
1 change: 1 addition & 0 deletions apps/rollup-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@rollup/plugin-commonjs": "^28.0.0",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-replace": "^6.0.1",
"rollup-plugin-serve": "^1.1.1",
"babel-core": "^6.26.3",
"babel-loader": "^9.2.1",
"rollup": "^4.24.0",
Expand Down
4 changes: 4 additions & 0 deletions apps/rollup-react/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import commonjs from "@rollup/plugin-commonjs";
import babel from "@rollup/plugin-babel";
import replace from "@rollup/plugin-replace";
import { IconifyPlugin } from "@oktaytest/rollup";
import serve from "rollup-plugin-serve";

const isDev = process.env.NODE_ENV === "development";

/** @type {import('rollup')} */
export default {
Expand All @@ -26,5 +29,6 @@ export default {
preventAssignment: false,
"process.env.NODE_ENV": '"development"',
}),
...(isDev ? [serve("public")] : []),
],
};
1 change: 1 addition & 0 deletions apps/rspack-react/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ function App() {
return (
<div className="App">
<Iconify name="mdi:home" />
<Iconify name="invalid:icon" />
</div>
);
}
Expand Down
1 change: 1 addition & 0 deletions apps/vite-react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ function App() {
<Iconify name="mdi:home" />
<Iconify name="logos:active-campaign" />
<Iconify name="logos:apache-superset-icon" />
<Iconify name="invalid:icon" />
</main>
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type IconifyOptions = {
type?: "cjs" | "esm";
};

type Icon = {
export type Icon = {
svg: string;
width: number;
height: number;
Expand Down
28 changes: 28 additions & 0 deletions packages/icon-loader/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
node_modules
.pnp
.pnp.js


# misc
.DS_Store
*.pem

# build
dist

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# turbo
.turbo
20 changes: 20 additions & 0 deletions packages/icon-loader/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# @oktaytest/core

## 0.0.119

### Patch Changes

- add svelte support

## 0.0.118

### Patch Changes

- add prepack scripts
- 47ee90f: add prepack scripts

## 0.0.114

### Patch Changes

- add vite and nuxt support
39 changes: 39 additions & 0 deletions packages/icon-loader/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@oktaytest/icon-loader",
"version": "0.0.119",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./constants": {
"import": "./dist/constants.mjs",
"require": "./dist/constants.js",
"types": "./dist/constants.d.mts"
}
},
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"clean": "rm -rf dist",
"prepack": "yarn build"
},
"devDependencies": {
"@oktaytest/typescript-config": "*",
"@oktaytest/core": "*",
"tsup": "^8.0.1",
"typescript": "^5.3.3"
},
"dependencies": {
"svgson": "^5.3.1"
}
}
65 changes: 65 additions & 0 deletions packages/icon-loader/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { Icon } from "@oktaytest/core";
import { parseSync, stringify } from "svgson";

// @ts-ignore
import _icons from "oktay";

const icons = _icons as Record<string, Icon>;

export const fallbackIcon: Icon = {
svg: '<svg width="32" height="32" viewBox="0 0 24 24" > <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10m0-2a8 8 0 1 0 0-16a8 8 0 0 0 0 16m-1-5h2v2h-2zm2-1.645V14h-2v-1.5a1 1 0 0 1 1-1a1.5 1.5 0 1 0-1.471-1.794l-1.962-.393A3.501 3.501 0 1 1 13 13.355" /> </svg>',
width: 32,
height: 32,
};

export type IconProps = {
name: string;
size?: number;
color?: string;
};

export type GetIconDetailsOptions = {
icon: Icon;
props: IconProps;
};

const loadIcon = (iconName: string) => {
const icon = icons?.[iconName];

if (icon) return icon;

console.warn(
`[Iconify] The icon "${iconName}" is missing from the configuration. To resolve this, ensure it is added to the 'icons' array within the Iconify plugin's configuration.`
);

return fallbackIcon;
};

export const getIconDetails = (props: IconProps) => {
const icon = loadIcon(props.name);

const parsed = parseSync(icon.svg);

let innerHtml = parsed.children.map((child) => stringify(child)).join("");

if (props.color) {
innerHtml = innerHtml.replace(/fill="([^"]+)"/, `fill="${props.color}"`);
}

const ratio = icon.width / icon.height;

const height = props.size ? props.size : icon.height;

const width = props.size ? props.size * ratio : icon.width;

const attributes: Record<string, string> = {
...parsed.attributes,
width: `${width}px`,
height: `${height}px`,
};

return {
innerHtml,
attributes,
};
};
8 changes: 8 additions & 0 deletions packages/icon-loader/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "@oktaytest/typescript-config/react-native-library",
"include": ["."],
"exclude": ["dist", "build", "node_modules"],
"compilerOptions": {
"strict": true
}
}
10 changes: 10 additions & 0 deletions packages/icon-loader/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig, Options } from "tsup";

export default defineConfig((options: Options) => ({
entry: ["src/index.ts"],
clean: true,
format: ["cjs", "esm"],
external: ["oktay"],
dts: true,
...options,
}));
4 changes: 2 additions & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
"typescript": "^5.3.3"
},
"dependencies": {
"html-react-parser": "^5.1.16",
"@oktaytest/core": "*"
"@oktaytest/core": "*",
"@oktaytest/icon-loader": "*"
},
"peerDependencies": {
"react": ">=18.2.0"
Expand Down
75 changes: 9 additions & 66 deletions packages/react/src/iconify.tsx
Original file line number Diff line number Diff line change
@@ -1,74 +1,17 @@
import React from "react";
import { getIconDetails } from "@oktaytest/icon-loader";

import { IconifyProps, RuntimeIcon, RuntimeIconifyProps } from "./types";
import { setAttributes } from "./utils";

const getIcon = (iconName: string) =>
new Promise<RuntimeIcon>(async (resolve, reject) => {
try {
// todo: add error handling
// @ts-ignore
const iconsImport = await import("oktay");

const icons = iconsImport.default ?? iconsImport;

let icon = icons[iconName];

if (!icon) {
console.warn(
`[Iconify] The icon "${iconName}" is missing from the configuration. To resolve this, ensure it is added to the 'icons' array within the Iconify plugin's configuration.`
);

// @ts-ignore
const constantsImport = await import("@oktaytest/core/constants");

const constants = constantsImport.default ?? constantsImport;

const fallbackIcon = constants.fallbackIcon;

icon = fallbackIcon;
}

resolve(icon);
} catch (error) {
reject(error);
}
});

const webIcon = async (props: RuntimeIconifyProps) => {
// @ts-ignore
const parse = await import("html-react-parser");

return parse.default(props.icon.svg);
};

const getComponent = async (props: RuntimeIconifyProps) => {
const formatted = setAttributes(props);

return webIcon(formatted);
};
import { IconifyProps } from "./types";

export const Iconify = (props: IconifyProps) => {
const [Component, setComponent] = React.useState<React.ReactNode | null>(
null
);
const details = getIconDetails(props);

const renderIcon = async () => {
const icon = await getIcon(props.name);

const component = await getComponent({
...props,
icon,
});

setComponent(component);
};

React.useEffect(() => {
renderIcon();
}, [props]);

return Component;
return (
<svg
{...details.attributes}
dangerouslySetInnerHTML={{ __html: details.innerHtml }}
/>
);
};

export default Iconify;
10 changes: 0 additions & 10 deletions packages/react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,3 @@ export interface IconifyProps {
size?: number;
color?: string;
}

export interface RuntimeIcon {
svg: string;
width: number;
height: number;
}

export interface RuntimeIconifyProps extends IconifyProps {
icon: RuntimeIcon;
}
30 changes: 0 additions & 30 deletions packages/react/src/utils.ts

This file was deleted.

3 changes: 3 additions & 0 deletions packages/vite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ export const IconifyPlugin = (options: IconifyOptions): PluginOption[] => [
async buildStart() {
await loadIcons({ type: "esm", ...options });
},
load(id) {
return null;
},
},
];
Loading

0 comments on commit 1f4b78a

Please sign in to comment.