Skip to content

Commit ae16180

Browse files
committed
feat: change icon generation logic
1 parent 949268e commit ae16180

File tree

4 files changed

+126
-11
lines changed

4 files changed

+126
-11
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,5 @@ android/keystores/debug.keystore
5656
/lib/
5757

5858
# ignore generated icon files
59-
/src/
59+
src/*
6060
!src/lib/

generator/generate-svg.mjs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import path from 'path';
66
import fs from 'fs-extra';
77
import Case from 'case';
88
import chalk from 'chalk';
9-
9+
import * as prettier from 'prettier';
1010
const __filename = fileURLToPath(import.meta.url);
1111
const __dirname = path.dirname(__filename);
1212

@@ -40,6 +40,9 @@ const componentNameMap = {
4040
Infinity: 'InfinityIcon',
4141
};
4242

43+
// Some duotone colors do not have a color and opacity
44+
const duotoneEscape = ['cell-signal-none', 'wifi-none'];
45+
4346
const srcDir = path.join(__dirname, '../src');
4447

4548
const generateIconsDefs = async (icon, weight) => {
@@ -80,9 +83,15 @@ const getIconList = () => {
8083
// yarn generate true
8184
if (process.argv[2] === 'true') {
8285
return files.filter((file) =>
83-
['acorn', 'palette', 'pencil-line', 'swap', 'list', 'test-tube'].includes(
84-
file
85-
)
86+
[
87+
'acorn',
88+
'palette',
89+
'pencil-line',
90+
'swap',
91+
'list',
92+
'test-tube',
93+
'',
94+
].includes(file)
8695
);
8796
}
8897
return files;
@@ -101,21 +110,24 @@ const generateAllIconsDefs = () => {
101110
defs[weight] = await generateIconsDefs(icon, weight);
102111
}
103112

104-
let defString = `\
113+
let defString = await prettier.format(
114+
`\
105115
/* GENERATED FILE */
106-
import type { ReactElement, FC } from "react";
116+
import type { ReactElement, FC } from 'react';
107117
import { Path } from 'react-native-svg';
108-
import { type IconWeight } from "../lib";
118+
import { type IconWeight } from '../lib';
109119
110120
export default new Map<IconWeight, ReactElement | FC<{ duotoneColor?: string; duotoneOpacity?: number }>>([
111121
${Object.entries(defs)
112122
.map(
113123
([weight, jsx]) =>
114-
`["${weight}", ${weight === 'duotone' ? '({duotoneColor,duotoneOpacity}: {duotoneColor?: string;duotoneOpacity?: number;}) => ' : ''}(<>${jsx.trim()}</>)]`
124+
`["${weight}", ${weight === 'duotone' ? (duotoneEscape.includes(icon) ? '() =>' : '({duotoneColor,duotoneOpacity}: {duotoneColor?: string;duotoneOpacity?: number;}) => ') : ''}(<>${jsx.trim()}</>)]`
115125
)
116-
.join(',')}
126+
.join(',\n')}
117127
]);
118-
`;
128+
`,
129+
{ semi: true, parser: 'babel-ts', singleQuote: true }
130+
);
119131
// console.log(defString);
120132
const outDir = path.join(srcDir, 'defs');
121133

src/lib/icon-base.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { useContext, type ReactElement, type FC } from 'react';
2+
import Svg from 'react-native-svg';
3+
import { type IconProps, type IconWeight, IconContext } from '../lib';
4+
5+
interface IconBaseProps extends IconProps {
6+
weights: Map<
7+
IconWeight,
8+
ReactElement | FC<{ duotoneColor?: string; duotoneOpacity?: number }>
9+
>;
10+
}
11+
12+
function IconBase({
13+
weight,
14+
color,
15+
size,
16+
style,
17+
mirrored,
18+
duotoneColor,
19+
duotoneOpacity,
20+
title,
21+
titleId,
22+
weights,
23+
...props
24+
}: IconBaseProps) {
25+
const {
26+
color: contextColor = '#000',
27+
size: contextSize = 24,
28+
weight: contextWeight = 'regular',
29+
mirrored: contextMirrored = false,
30+
style: contextStyle,
31+
duotoneColor: contextDuotoneColor = '#000',
32+
duotoneOpacity: contextDuotoneOpacity = 0.2,
33+
} = useContext(IconContext);
34+
35+
return (
36+
<Svg
37+
style={[
38+
contextStyle,
39+
style,
40+
{
41+
...((mirrored ?? contextMirrored) && {
42+
transform: [{ scaleX: -1 }],
43+
}),
44+
},
45+
]}
46+
className={`acorn-${weight}__svg-icon-phosphor`}
47+
testID={props.testID ?? 'phosphor-react-native-acorn-bold'}
48+
fill="currentColor"
49+
viewBox="0 0 256 256"
50+
width={size ?? contextSize}
51+
height={size ?? contextSize}
52+
color={color ?? contextColor}
53+
aria-labelledby={titleId}
54+
{...props}
55+
>
56+
{title ? <title id={titleId}>{title}</title> : null}
57+
{(weight ?? contextWeight) === 'duotone'
58+
? // @ts-expect-error not callable
59+
weights.get(weight ?? contextWeight)({
60+
duotoneColor: duotoneColor ?? contextDuotoneColor ?? color,
61+
duotoneOpacity: duotoneOpacity ?? contextDuotoneOpacity,
62+
})
63+
: weights.get(weight ?? contextWeight)}
64+
</Svg>
65+
);
66+
}
67+
68+
export default IconBase;

src/lib/index.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React, { createContext } from 'react';
2+
import { StyleProp, ViewStyle } from 'react-native';
3+
4+
export type IconWeight =
5+
| 'thin'
6+
| 'light'
7+
| 'regular'
8+
| 'bold'
9+
| 'fill'
10+
| 'duotone';
11+
12+
export type PaintFunction = (color: string) => React.ReactNode | null;
13+
14+
export interface IconProps {
15+
color?: string;
16+
size?: string | number;
17+
weight?: IconWeight;
18+
style?: StyleProp<ViewStyle>;
19+
mirrored?: boolean;
20+
testID?: string;
21+
duotoneColor?: string;
22+
duotoneOpacity?: number;
23+
title?: string; // SVGRProps
24+
titleId?: string; // SVGRProps
25+
}
26+
27+
export type Icon = React.FC<IconProps>;
28+
29+
export const IconContext = createContext<IconProps>({
30+
color: '#000',
31+
size: 24,
32+
weight: 'regular',
33+
mirrored: false,
34+
duotoneOpacity: 0.2,
35+
});

0 commit comments

Comments
 (0)