Skip to content

Commit

Permalink
Update README.md and update generator react
Browse files Browse the repository at this point in the history
  • Loading branch information
afnizarnur committed Sep 3, 2024
1 parent 0bceee4 commit f758ead
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 92 deletions.
56 changes: 42 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
## Installation

Install with [npm](npmjs.com/).

```bash
npm install nataicons --save
```
Expand All @@ -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
<svg width="24" height="24" class="custom-class">
Expand All @@ -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
<template>
<div>
<AlarmIcon color="red" size="24" />
<NataIcon name="bell" color="blue" size="32" />
</div>
</template>
<script>
import { AlarmIcon } from "nataicons/vue"
export default {
components: { AlarmIcon }
}
</script>
```

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
<Home24 size="2x" class="custom-class" />
### React

// px based sizing
<Home24 size="20" class="custom-class" />
1. Import the icon components

```jsx
import { AlarmIcon, NataIcon } from "nataicons/react"
```

2. Use the icon components in your JSX

```jsx
function MyComponent() {
return (
<div>
<AlarmIcon color="red" size={24} />
<NataIcon name="bell" color="blue" size={32} />
</div>
)
}
```

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)
Expand All @@ -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.
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.
156 changes: 78 additions & 78 deletions scripts/build-react/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(/<svg([^>]+)>/, (match, attributes) => {
// Remove width and height attributes from the original SVG
const cleanedAttributes = attributes
.replace(/\s*width=["'][^"']*["']/g, "")
.replace(/\s*height=["'][^"']*["']/g, "")
return `<svg${cleanedAttributes}>`
})
.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+</g, "><")
.replace(/fill="([^"]+)"/g, 'fill={color || "$1"}')
.trim()

return `
const componentTemplate = (name, svg20, svg24) =>
`
import React from 'react';
function ${name}({ size = '${size}', color, ...props }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 ${size} ${size}"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
${svgContent}
</svg>
);
}
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 <IconComponent size={size} color={color} {...props} />;
const svg20 = ${JSON.stringify(svg20)};
const svg24 = ${JSON.stringify(svg24)};
return (
<div
style={{
display: 'inline-block',
width: getSize(),
height: getSize(),
}}
dangerouslySetInnerHTML={{
__html: updateSvg(size === '20' ? svg20 : svg24)
}}
{...props}
/>
);
};
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()];
Expand All @@ -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}",
Expand All @@ -95,47 +96,46 @@ 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...")

try {
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")
Expand Down

0 comments on commit f758ead

Please sign in to comment.