Skip to content

Commit 7dc2eba

Browse files
committed
feat: create IconBase component, add script to create Icon.tsx file
1 parent c53bc81 commit 7dc2eba

File tree

11 files changed

+181
-3
lines changed

11 files changed

+181
-3
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.eslintrc.cjs
2+
scripts/*.cjs
23

34
# Files with ESLint errors/warnings
45
src/Common/AddCDButton/AddCDButton.tsx

.husky/pre-commit

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,34 @@
1717

1818
. "$(dirname "$0")/_/husky.sh"
1919

20-
npx tsc --noEmit
21-
npm run lint-staged
20+
echo "Running pre-commit hook..."
21+
22+
# Check for changes in the Icon folder
23+
CHANGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep 'IconV2/')
24+
25+
if [ -n "$CHANGED_FILES" ]; then
26+
echo "Changes detected in the Icon folder. Running icon generation script..."
27+
28+
if ! npm run generate-icon; then
29+
echo "Error: Icon generation script failed."
30+
exit 1
31+
fi
32+
33+
git add src/Shared/Components/Icon/Icon.tsx
34+
else
35+
echo "No changes in the IconsV2 folder. Skipping icon generation."
36+
fi
37+
38+
# TypeScript check
39+
if ! npx tsc --noEmit; then
40+
echo "Error: TypeScript check failed."
41+
exit 1
42+
fi
43+
44+
# Lint-staged
45+
if ! npm run lint-staged; then
46+
echo "Error: Lint-staged failed."
47+
exit 1
48+
fi
49+
50+
echo "Pre-commit hook completed successfully."

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"build-lib": "vite build",
3333
"preview": "vite preview",
3434
"lint-staged": "lint-staged",
35-
"postinstall": "patch-package"
35+
"postinstall": "patch-package",
36+
"generate-icon": "node ./scripts/generate-icon.cjs"
3637
},
3738
"devDependencies": {
3839
"@esbuild-plugins/node-globals-polyfill": "0.2.3",

scripts/generate-icon.cjs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
const { exec } = require('child_process')
4+
5+
// Base path relative to the current script
6+
const basePath = path.resolve(__dirname, '../src')
7+
8+
// Directory containing SVG icons and the output file
9+
const iconsDir = path.join(basePath, 'Assets', 'IconV2')
10+
const outputFile = path.join(basePath, 'Shared', 'Components', 'Icon', 'Icon.tsx')
11+
12+
const runESLint = (filePath) => {
13+
exec(`npx eslint ${filePath} --fix`, (error, stdout, stderr) => {
14+
if (error) {
15+
console.error(`Error running ESLint: ${error.message}`)
16+
return
17+
}
18+
if (stderr) {
19+
console.error(`ESLint stderr: ${stderr}`)
20+
}
21+
if (stdout) {
22+
console.log(`ESLint output:\n${stdout}`)
23+
}
24+
console.log('ESLint completed successfully.')
25+
})
26+
}
27+
28+
const generateIconComponent = () => {
29+
// Read all files in the icons directory
30+
const files = fs.readdirSync(iconsDir)
31+
32+
// Filter for SVG files
33+
const svgFiles = files.filter((file) => file.endsWith('.svg'))
34+
35+
// Generate import statements and the icon map
36+
const imports = []
37+
const iconMapEntries = []
38+
39+
svgFiles.forEach((file) => {
40+
// Remove the .svg extension
41+
const iconName = path.basename(file, '.svg')
42+
// Convert icon-name to IconName for importName
43+
const importName = iconName
44+
.replace(/(^\w+)/, (match) => match.toUpperCase())
45+
.replace(/-./g, (match) => match[1].toUpperCase())
46+
// Push imports statement
47+
imports.push(`import { ReactComponent as ${importName} } from '@IconsV2/${file}'`)
48+
// Push icons to iconMap
49+
iconMapEntries.push(`["${iconName}"]: ${importName},`)
50+
})
51+
52+
// Generate the Icon.tsx content
53+
const content = `
54+
// NOTE: This file is auto-generated. Do not edit directly. Run the script \`npm run generate-icon\` to update.
55+
56+
${imports.join('\n')}
57+
58+
import { IconBase } from './IconBase';
59+
import { IconBaseProps } from './types';
60+
61+
const iconMap = {
62+
${iconMapEntries.join('\n')}
63+
};
64+
65+
export type IconName = keyof typeof iconMap;
66+
67+
export interface IconsProps extends Omit<IconBaseProps, 'name' | 'iconMap'> {
68+
name: keyof typeof iconMap;
69+
}
70+
71+
export const Icon = (props: IconsProps) => {
72+
return <IconBase {...props} iconMap={iconMap} />;
73+
};
74+
`
75+
76+
// Write the content to the Icon.tsx file
77+
fs.writeFileSync(outputFile, content.trim(), 'utf-8')
78+
console.log(`Icon component file generated at: ${outputFile}`)
79+
80+
// Run ESLint on the generated file
81+
runESLint(outputFile)
82+
}
83+
84+
// Run the script
85+
generateIconComponent()

src/Assets/IconV2/ic-clock.svg

Lines changed: 19 additions & 0 deletions
Loading

src/Shared/Components/Icon/Icon.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// NOTE: This file is auto-generated. Do not edit directly. Run the script `npm run generate-icon` to update.
2+
3+
import { ReactComponent as ICClock } from '@IconsV2/ic-clock.svg'
4+
5+
import { IconBase } from './IconBase'
6+
import { IconBaseProps } from './types'
7+
8+
const iconMap = {
9+
'ic-clock': ICClock,
10+
}
11+
12+
export type IconName = keyof typeof iconMap
13+
14+
export interface IconsProps extends Omit<IconBaseProps, 'name' | 'iconMap'> {
15+
name: keyof typeof iconMap
16+
}
17+
18+
export const Icon = (props: IconsProps) => <IconBase {...props} iconMap={iconMap} />
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { IconBaseProps } from './types'
2+
3+
export const IconBase = ({ name, iconMap, size = 24 }: IconBaseProps) => {
4+
const IconComponent = iconMap[name]
5+
6+
if (!IconComponent) {
7+
throw new Error(`Icon with name "${name}" does not exist.`)
8+
}
9+
10+
return <IconComponent className={`icon-dim-${size}`} />
11+
}

src/Shared/Components/Icon/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './Icon'

src/Shared/Components/Icon/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { FC, SVGProps } from 'react'
2+
3+
type IconMap = Record<string, FC<SVGProps<SVGSVGElement>>>
4+
5+
export interface IconBaseProps {
6+
name: keyof IconMap
7+
iconMap: IconMap
8+
size?: 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 28 | 30 | 32 | 34 | 36 | 40 | 42 | 44 | 48 | 72 | 80
9+
}

src/Shared/Components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,4 @@ export * from './ThemeSwitcher'
7676
export * from './TargetPlatforms'
7777
export * from './UnsavedChanges'
7878
export * from './UnsavedChangesDialog'
79+
export * from './Icon'

0 commit comments

Comments
 (0)