Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
143 changes: 66 additions & 77 deletions generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,86 +2,79 @@ const fs = require('fs')
const path = require('path')
const axios = require('axios')

// Base url for icons
const iconURL = 'https://fonts.google.com/metadata/icons'
const GOOGLE_FONTS_URL = 'https://fonts.google.com/metadata/icons'
const ICON_FAMILIES = [
{ id: 'materialicons', postfix: '' },
{ id: 'materialiconsoutlined', postfix: 'Outlined' },
{ id: 'materialiconsround', postfix: 'Round' },
{ id: 'materialiconssharp', postfix: 'Sharp' },
{ id: 'materialiconstwotone', postfix: 'TwoTone' },
]

function setup() {
;(async () => {
generatePropsFile()

const res = await axios.get(GOOGLE_FONTS_URL)

// remove )]}' from the response
const data = res.data.substring(4)
const icons = await JSON.parse(data)

for (let i = 0; i < icons.icons.length; i++) {
generateComponentsForAllFamilies(icons.icons[i])
}
})()

function generatePropsFile() {
const typesFile = `
import React from 'react'
export interface IconProps extends React.SVGProps<SVGSVGElement> {
title?: string
}
`

// Create types file
fs.writeFileSync(path.join(__dirname, 'src', 'types.ts'), typesFile)
}

/**
* Format name.
* Covert from snake case to camel case and prevent numbers at the beginning
*
* @param string - String to format
* @returns {string} - Formatted string
*/
function formatName(string) {
async function generateComponentsForAllFamilies(icon) {
for (let i = 0; i < ICON_FAMILIES.length; i++) {
await generateComponent(icon, ICON_FAMILIES[i])
}
}

async function generateComponent(icon, family) {
try {
const name = formatName(icon.name, family.postfix)
const svg = await downloadSVG(icon.name, family.id, icon.version)

console.log(`Downloading ${name}`)

await fs.writeFileSync(
path.join(__dirname, 'src', `${name}.tsx`),
mapSVGToTemplate(name, svg)
)
} catch {
process.abort()
}
}

function formatName(string, familyPostfix) {
const formattedString = string
.replace(/_/g, ' ')
.replace(/\w\S*/g, (txt) => {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
})
.replace(/ /g, '')
return 'Icon' + formattedString
}

/**
* Component template for functional react icon component
*
* @param name - Component name
* @param svg - Icon SVG
* @returns {string} - Component
*/
function componentTemplate(name, svg) {
return `
import React from 'react'
import { IconProps } from './types'

const ${name}: React.FC<IconProps> = ({ ...props }) => (
${svg}
)

export { ${name} as default }
`
return 'Icon' + formattedString + familyPostfix
}

/**
* Generate React Icon components
*
* @param icon - Icon object
*/
function generateComponent(icon) {
icon.forEach(async (c) => {
const name = formatName(c.name)
const svg = await getSVGFile(c.name, c.version)

fs.writeFileSync(
path.join(__dirname, 'src', `${name}.tsx`),
componentTemplate(name, svg)
async function downloadSVG(icon, familyId, version) {
const svg = await axios
.get(
`https://fonts.gstatic.com/s/i/${familyId}/${icon}/v${version}/24px.svg`
)
})
}

/**
* Get SVG data form google static fonts api
*
* @param icon - Icon name
* @param version - Icon Version
* @returns {Promise<any>} - SVG Data
*/
async function getSVGFile(icon, version) {
const svg = await axios.get(
`https://fonts.gstatic.com/s/i/materialicons/${icon}/v${version}/24px.svg`
)
.catch((err) => console.log(err))

return svg.data
.replace('height="24"', '')
Expand All @@ -93,23 +86,19 @@ async function getSVGFile(icon, version) {
.replace('>', '>{props.title && <title>{props.title}</title>}')
.replace(/style="enableBackground:(.*?);?"/g, 'enableBackground="$1"')
.replace(/style="fill:(.*?);?"/g, 'fill="$1"')
.replace(/xmlns:xlink/g, 'xmlnsXlink')
.replace(/xlink:href/g, 'xlinkHref')
}

/**
* Main function
*/
;(async () => {
// Setup source folder
setup()

// Get icon information from google fonts
const res = await axios.get(iconURL)
// remove )]}' from the response
const data = res.data.substring(4)

// Parse response from json to js object
const icons = await JSON.parse(data)

// Generate icon components and Index
await generateComponent(icons.icons)
})()
function mapSVGToTemplate(name, svg) {
return `
import React from 'react'
import { IconProps } from './types'

const ${name}: React.FC<IconProps> = ({ ...props }) => (
${svg}
)

export { ${name} as default }
`
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
},
"scripts": {
"build": "tsc",
"generate": "rm -rf src/* && node generator.js",
"clear": "rm -rf src/*",
"generate": "node generator.js",
"lint": "eslint --ext js,ts,tsx src ./generator.js",
"lint:fix": "npm run lint -- --fix",
"typecheck": "tsc --noEmit",
Expand Down
12 changes: 10 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,21 @@ import IconCached from '@aboutbits/react-material-icons/dist/IconCached'

const MyCommponent = () => {
return (
<IconCached />
);
<IconCached/>
);
}
```

SVG related parameters like height and width can be passed as props.

To import the different variants of an icon you can add the variants as a postfix:

- `Icon10k` > Default filled
- `Icon10kOutlined` > Outlined
- `Icon10kRound` > Rounded
- `Icon10kSharp` > Sharp
- `Icon10kTwoTone` > Tow tone

## Generate Components

To generate the components, simply run the following command:
Expand Down
27 changes: 27 additions & 0 deletions src/Icon10kOutlined.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react'
import { IconProps } from './types'

const Icon10kOutlined: React.FC<IconProps> = ({ ...props }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
viewBox="0 0 24 24"
{...props}
>
{props.title && <title>{props.title}</title>}
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<g>
<g>
<path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,9v6v4H5v-8.5h1V15h1.5V9H5V5 h14V9z" />
<polygon points="15.5,11.25 15.5,9 14,9 14,15 15.5,15 15.5,12.75 17.25,15 19,15 16.75,12 19,9 17.25,9" />
<path d="M9.5,15H12c0.55,0,1-0.45,1-1v-4c0-0.55-0.45-1-1-1H9.5c-0.55,0-1,0.45-1,1v4C8.5,14.55,8.95,15,9.5,15z M10,10.5h1.5v3 H10V10.5z" />
</g>
</g>
</g>
</svg>
)

export { Icon10kOutlined as default }
21 changes: 21 additions & 0 deletions src/Icon10kRound.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import { IconProps } from './types'

const Icon10kRound: React.FC<IconProps> = ({ ...props }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
viewBox="0 0 24 24"
{...props}
>
{props.title && <title>{props.title}</title>}
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<path d="M10,10.5h1.5v3H10V10.5z M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M6.75,15 L6.75,15C6.34,15,6,14.66,6,14.25V10.5H5.25c-0.41,0-0.75-0.34-0.75-0.75v0C4.5,9.34,4.84,9,5.25,9H6.5c0.55,0,1,0.45,1,1v4.25 C7.5,14.66,7.16,15,6.75,15z M13,14c0,0.55-0.45,1-1,1H9.5c-0.55,0-1-0.45-1-1v-4c0-0.55,0.45-1,1-1H12c0.55,0,1,0.45,1,1V14z M17.59,15L17.59,15c-0.22,0-0.42-0.1-0.55-0.27l-1.54-1.98v1.5c0,0.41-0.34,0.75-0.75,0.75h0C14.34,15,14,14.66,14,14.25v-4.5 C14,9.34,14.34,9,14.75,9h0c0.41,0,0.75,0.34,0.75,0.75v1.5l1.54-1.98C17.17,9.1,17.38,9,17.59,9l0,0c0.58,0,0.91,0.66,0.56,1.12 L16.75,12l1.41,1.88C18.5,14.34,18.17,15,17.59,15z" />
</g>
</svg>
)

export { Icon10kRound as default }
21 changes: 21 additions & 0 deletions src/Icon10kSharp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import { IconProps } from './types'

const Icon10kSharp: React.FC<IconProps> = ({ ...props }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
viewBox="0 0 24 24"
{...props}
>
{props.title && <title>{props.title}</title>}
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<path d="M10,10.5h1.5v3H10V10.5z M21,3H3v18h18V3z M7.5,15H6v-4.5H4.5V9h3V15z M13,9v6H8.5V9H13z M19,15h-1.75l-1.75-2.25V15H14V9 h1.5v2.25L17.25,9H19l-2.25,3L19,15z" />
</g>
</svg>
)

export { Icon10kSharp as default }
31 changes: 31 additions & 0 deletions src/Icon10kTwoTone.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react'
import { IconProps } from './types'

const Icon10kTwoTone: React.FC<IconProps> = ({ ...props }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
viewBox="0 0 24 24"
{...props}
>
{props.title && <title>{props.title}</title>}
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<g>
<rect height="3" opacity=".3" width="1.5" x="10" y="10.5" />
<polygon opacity=".3" points="19,15 19,9 16.75,12" />
<path
d="M5,9h2.5v6H6v-4.5H5V19h14v-4h-1.75l-1.75-2.25V15H14V9h1.5v2.25L17.25,9H19V5H5V9z M8.5,10 c0-0.55,0.45-1,1-1H12c0.55,0,1,0.45,1,1v4c0,0.55-0.45,1-1,1H9.5c-0.55,0-1-0.45-1-1V10z"
opacity=".3"
/>
<path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,9v6v4H5v-8.5h1V15h1.5V9H5V5 h14V9z" />
<polygon points="15.5,11.25 15.5,9 14,9 14,15 15.5,15 15.5,12.75 17.25,15 19,15 16.75,12 19,9 17.25,9" />
<path d="M9.5,15H12c0.55,0,1-0.45,1-1v-4c0-0.55-0.45-1-1-1H9.5c-0.55,0-1,0.45-1,1v4C8.5,14.55,8.95,15,9.5,15z M10,10.5h1.5v3 H10V10.5z" />
</g>
</g>
</svg>
)

export { Icon10kTwoTone as default }
37 changes: 37 additions & 0 deletions src/Icon10mpOutlined.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'
import { IconProps } from './types'

const Icon10mpOutlined: React.FC<IconProps> = ({ ...props }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
viewBox="0 0 24 24"
{...props}
>
{props.title && <title>{props.title}</title>}
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<g>
<g>
<path d="M13,11.5h2.5c0.55,0,1-0.45,1-1v-4c0-0.55-0.45-1-1-1H13c-0.55,0-1,0.45-1,1v4C12,11.05,12.45,11.5,13,11.5z M13.5,7H15 v3h-1.5V7z" />
</g>
<g>
<path d="M7.5,14h1v3H10v-3h1v4.5h1.5v-5c0-0.55-0.45-1-1-1H7c-0.55,0-1,0.45-1,1v5h1.5V14z" />
</g>
<g>
<polygon points="8.5,11.5 10,11.5 10,5.5 7,5.5 7,7 8.5,7" />
</g>
<g>
<path d="M13.5,18.5H15V17h2c0.55,0,1-0.45,1-1v-2.5c0-0.55-0.45-1-1-1h-3.5V18.5z M15,14h1.5v1.5H15V14z" />
</g>
<g>
<path d="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M19,19H5V5h14V19z" />
</g>
</g>
</g>
</svg>
)

export { Icon10mpOutlined as default }
23 changes: 23 additions & 0 deletions src/Icon10mpRound.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'
import { IconProps } from './types'

const Icon10mpRound: React.FC<IconProps> = ({ ...props }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
viewBox="0 0 24 24"
{...props}
>
{props.title && <title>{props.title}</title>}
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<g>
<path d="M13.5,7H15v3h-1.5V7z M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M11.75,18.5 L11.75,18.5c-0.41,0-0.75-0.34-0.75-0.75V14h-1v2.25C10,16.66,9.66,17,9.25,17h0c-0.41,0-0.75-0.34-0.75-0.75V14h-1v3.75 c0,0.41-0.34,0.75-0.75,0.75h0C6.34,18.5,6,18.16,6,17.75V13.5c0-0.55,0.45-1,1-1h4.5c0.55,0,1,0.45,1,1v4.25 C12.5,18.16,12.16,18.5,11.75,18.5z M14.25,18.5L14.25,18.5c-0.41,0-0.75-0.34-0.75-0.75V13.5c0-0.55,0.45-1,1-1H17 c0.55,0,1,0.45,1,1V16c0,0.55-0.45,1-1,1h-2v0.75C15,18.16,14.66,18.5,14.25,18.5z M10,6.5v4.25c0,0.41-0.34,0.75-0.75,0.75h0 c-0.41,0-0.75-0.34-0.75-0.75V7H7.75C7.34,7,7,6.66,7,6.25v0C7,5.84,7.34,5.5,7.75,5.5H9C9.55,5.5,10,5.95,10,6.5z M16.5,10.5 c0,0.55-0.45,1-1,1H13c-0.55,0-1-0.45-1-1v-4c0-0.55,0.45-1,1-1h2.5c0.55,0,1,0.45,1,1V10.5z M15,14h1.5v1.5H15V14z" />
</g>
</g>
</svg>
)

export { Icon10mpRound as default }
Loading