From f758ead5715ac973dbe2ec9c98b59209deea0d9f Mon Sep 17 00:00:00 2001 From: Afnizar Nur Ghifari Date: Tue, 3 Sep 2024 22:44:40 +0700 Subject: [PATCH] Update README.md and update generator react --- README.md | 56 +++++++++---- scripts/build-react/index.js | 156 +++++++++++++++++------------------ 2 files changed, 120 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index ce05a9b..aed2091 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ ## Installation Install with [npm](npmjs.com/). - ```bash npm install nataicons --save ``` @@ -37,7 +36,7 @@ Copy the SVGs you want to use from `icons/24x24` or `icons/20x20` inside `node_m ### SVG Sprite -1. Include an icon on your page with the following markup: +Include an icon on your page with the following markup: ```html @@ -47,26 +46,55 @@ Copy the SVGs you want to use from `icons/24x24` or `icons/20x20` inside `node_m ### Vue -1. Import the icon - -The Vue component located inside the package, so to import the component make sure to type the package name `nataicons/vue`. +1. Import the icon components ```js -import { Home24, Inbox24, Folder24, ... } from "nataicons/vue" +import { AlarmIcon, AlertIcon, NataIcon } from "nataicons/vue" ``` -2. Use the icon on your project +2. Use the icon components in your template + + +```jsx +
+ + +
+ + +``` -By default, each icon size will be on 24x24 and 20x20 depends on the variant. You can set a custom `size`: Multiple based sizing followed by an `x` or set a `px` directly by passing an integer. +You can set a custom `size` (in pixels) or use the default sizes (24px or 20px). The `color` prop allows you to change the icon color. -```js -// Multiple based sizing - +### React -// px based sizing - +1. Import the icon components + +```jsx +import { AlarmIcon, NataIcon } from "nataicons/react" +``` + +2. Use the icon components in your JSX + +```jsx +function MyComponent() { + return ( +
+ + +
+ ) +} ``` +Similar to Vue, you can set a custom `size` (in pixels) or use the default sizes. The `color` prop allows you to change the icon color. + ## Inspiration 1. [vue-hero-icons](https://github.com/matschik/vue-hero-icons) @@ -75,4 +103,4 @@ By default, each icon size will be on 24x24 and 20x20 depends on the variant. Yo ## License -Nataicons is licensed under the [MIT License](https://github.com/afnizarnur/nataicons//tree/main/LICENSE). In short, you are free to use this icons in any personal, open-source or commercial work. Attribution is optional but appreciated. \ No newline at end of file +Nataicons is licensed under the [MIT License](https://github.com/afnizarnur/nataicons//tree/main/LICENSE). In short, you are free to use this icons in any personal, open-source or commercial work. Attribution is optional but appreciated. diff --git a/scripts/build-react/index.js b/scripts/build-react/index.js index 0e3c60c..1bec394 100644 --- a/scripts/build-react/index.js +++ b/scripts/build-react/index.js @@ -6,68 +6,71 @@ const rimraf = promisify(require("rimraf")) const dedent = require("dedent") const pkg = require("../../package.json") -const componentTemplate = (name, svg, size) => { - const svgContent = svg - .replace(/]+)>/, (match, attributes) => { - // Remove width and height attributes from the original SVG - const cleanedAttributes = attributes - .replace(/\s*width=["'][^"']*["']/g, "") - .replace(/\s*height=["'][^"']*["']/g, "") - return `` - }) - .replace(/\s*xmlns=["']http:\/\/www\.w3\.org\/2000\/svg["']/g, "") - .replace(/\s*xmlnsXlink=["']http:\/\/www\.w3\.org\/1999\/xlink["']/g, "") - .replace(/\n+/g, " ") - .replace(/>\s+<") - .replace(/fill="([^"]+)"/g, 'fill={color || "$1"}') - .trim() - - return ` +const componentTemplate = (name, svg20, svg24) => + ` import React from 'react'; -function ${name}({ size = '${size}', color, ...props }) { - return ( - - ${svgContent} - - ); -} +const ${name}Icon = ({ size = '24', color = 'currentColor', ...props }) => { + const getSize = () => { + if (size.slice(-1) === 'x') + return size.slice(0, size.length - 1) + 'em'; + return parseInt(size) + 'px'; + }; -export default ${name}; -`.trim() -} + const updateSvg = (svgString) => { + const parser = new DOMParser(); + const doc = parser.parseFromString(svgString, 'image/svg+xml'); + const svg = doc.documentElement; -const wrapperTemplate = (name) => ` -import React from 'react'; -import { ${name}2020, ${name}2424 } from './index'; + svg.setAttribute('width', getSize()); + svg.setAttribute('height', getSize()); + + svg.querySelectorAll('[fill]:not([fill="none"])').forEach(el => { + el.setAttribute('fill', color); + }); + svg.querySelectorAll('[stroke]:not([stroke="none"])').forEach(el => { + el.setAttribute('stroke', color); + }); + + return svg.outerHTML; + }; -const ${name} = ({ size = '24', color, ...props }) => { - const IconComponent = size === '20' ? ${name}2020 : ${name}2424; - return ; + const svg20 = ${JSON.stringify(svg20)}; + const svg24 = ${JSON.stringify(svg24)}; + + return ( +
+ ); }; -export default ${name}; -` +export default ${name}Icon; +`.trim() const indexTemplate = (components) => ` -${components.map((comp) => `import ${comp} from './${comp}.jsx';`).join("\n")} +import React from 'react'; +${components + .map((comp) => `import ${comp}Icon from './${comp}Icon';`) + .join("\n")} export { - ${components.join(",\n ")} + ${components.map((comp) => `${comp}Icon`).join(",\n ")} }; -export const Icon = ({ name, ...props }) => { +export const NataIcon = ({ name, ...props }) => { const IconComponent = { ${components - .map((comp) => `${comp.toLowerCase()}: ${comp}`) + .map((comp) => `${comp.toLowerCase()}: ${comp}Icon`) .join(",\n ")} }[name.toLowerCase()]; @@ -80,9 +83,7 @@ const packageJSONTemplate = () => { "name": "nataicons-react", "version": "${pkg.version}", - "main": "lib/index.js", - "module": "es/index.js", - "jsnext:main": "es/index.js", + "main": "index.js", "license": "${pkg.license}", "homepage": "${pkg.homepage}", "description": "${pkg.description}", @@ -95,6 +96,11 @@ const packageJSONTemplate = () => } `.trim() +async function processSvgFile(filePath) { + const content = await fs.readFile(filePath, "utf8") + return content.trim().replace(/\n/g, " ") +} + async function buildReactComponents() { console.log("Building React icon components...") @@ -102,40 +108,34 @@ async function buildReactComponents() { await rimraf("./react/*") await fs.mkdir("./react", { recursive: true }) - const sizes = ["20x20", "24x24"] + const icons24 = await fs.readdir( + path.join(__dirname, "..", "..", "icons", "24x24") + ) const components = [] - const wrapperComponents = new Set() - - for (const size of sizes) { - const iconDir = `./icons/${size}` - const files = await fs.readdir(iconDir) - - for (const file of files) { - const content = await fs.readFile(path.join(iconDir, file), "utf8") - const baseName = camelcase(file.replace(/\.svg$/, ""), { - pascalCase: true, - }) - const componentName = `${baseName}${size.replace("x", "")}` - const component = componentTemplate( - componentName, - content, - size.split("x")[0] - ) - - await fs.writeFile(`./react/${componentName}.jsx`, dedent(component)) - components.push(componentName) - wrapperComponents.add(baseName) - } - } - for (const wrapperName of wrapperComponents) { - const wrapperContent = wrapperTemplate(wrapperName) - await fs.writeFile(`./react/${wrapperName}.jsx`, dedent(wrapperContent)) - components.push(wrapperName) + for (const file of icons24) { + const baseName = path.basename(file, ".svg") + const componentName = camelcase(baseName, { pascalCase: true }) + + const svg20Path = path.join(__dirname, "..", "..", "icons", "20x20", file) + const svg24Path = path.join(__dirname, "..", "..", "icons", "24x24", file) + + const [svg20Content, svg24Content] = await Promise.all([ + processSvgFile(svg20Path).catch(() => ""), + processSvgFile(svg24Path), + ]) + + const component = componentTemplate( + componentName, + svg20Content, + svg24Content + ) + await fs.writeFile(`./react/${componentName}Icon.js`, dedent(component)) + components.push(componentName) } const indexContent = indexTemplate(components) - await fs.writeFile("./react/index.jsx", dedent(indexContent)) + await fs.writeFile("./react/index.js", dedent(indexContent)) await fs.writeFile("./react/package.json", packageJSONTemplate()) await fs.copyFile("./README.md", "./react/README.md")