Skip to content

Commit

Permalink
feat: change icon generation logic
Browse files Browse the repository at this point in the history
  • Loading branch information
mrkpatchaa committed Jan 23, 2025
1 parent 949268e commit ae16180
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ android/keystores/debug.keystore
/lib/

# ignore generated icon files
/src/
src/*
!src/lib/
32 changes: 22 additions & 10 deletions generator/generate-svg.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import path from 'path';
import fs from 'fs-extra';
import Case from 'case';
import chalk from 'chalk';

import * as prettier from 'prettier';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

Expand Down Expand Up @@ -40,6 +40,9 @@ const componentNameMap = {
Infinity: 'InfinityIcon',
};

// Some duotone colors do not have a color and opacity
const duotoneEscape = ['cell-signal-none', 'wifi-none'];

const srcDir = path.join(__dirname, '../src');

const generateIconsDefs = async (icon, weight) => {
Expand Down Expand Up @@ -80,9 +83,15 @@ const getIconList = () => {
// yarn generate true
if (process.argv[2] === 'true') {
return files.filter((file) =>
['acorn', 'palette', 'pencil-line', 'swap', 'list', 'test-tube'].includes(
file
)
[
'acorn',
'palette',
'pencil-line',
'swap',
'list',
'test-tube',
'',
].includes(file)
);
}
return files;
Expand All @@ -101,21 +110,24 @@ const generateAllIconsDefs = () => {
defs[weight] = await generateIconsDefs(icon, weight);
}

let defString = `\
let defString = await prettier.format(
`\
/* GENERATED FILE */
import type { ReactElement, FC } from "react";
import type { ReactElement, FC } from 'react';
import { Path } from 'react-native-svg';
import { type IconWeight } from "../lib";
import { type IconWeight } from '../lib';
export default new Map<IconWeight, ReactElement | FC<{ duotoneColor?: string; duotoneOpacity?: number }>>([
${Object.entries(defs)
.map(
([weight, jsx]) =>
`["${weight}", ${weight === 'duotone' ? '({duotoneColor,duotoneOpacity}: {duotoneColor?: string;duotoneOpacity?: number;}) => ' : ''}(<>${jsx.trim()}</>)]`
`["${weight}", ${weight === 'duotone' ? (duotoneEscape.includes(icon) ? '() =>' : '({duotoneColor,duotoneOpacity}: {duotoneColor?: string;duotoneOpacity?: number;}) => ') : ''}(<>${jsx.trim()}</>)]`
)
.join(',')}
.join(',\n')}
]);
`;
`,
{ semi: true, parser: 'babel-ts', singleQuote: true }
);
// console.log(defString);
const outDir = path.join(srcDir, 'defs');

Expand Down
68 changes: 68 additions & 0 deletions src/lib/icon-base.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useContext, type ReactElement, type FC } from 'react';
import Svg from 'react-native-svg';
import { type IconProps, type IconWeight, IconContext } from '../lib';

interface IconBaseProps extends IconProps {
weights: Map<
IconWeight,
ReactElement | FC<{ duotoneColor?: string; duotoneOpacity?: number }>
>;
}

function IconBase({
weight,
color,
size,
style,
mirrored,
duotoneColor,
duotoneOpacity,
title,
titleId,
weights,
...props
}: IconBaseProps) {
const {
color: contextColor = '#000',
size: contextSize = 24,
weight: contextWeight = 'regular',
mirrored: contextMirrored = false,
style: contextStyle,
duotoneColor: contextDuotoneColor = '#000',
duotoneOpacity: contextDuotoneOpacity = 0.2,
} = useContext(IconContext);

return (
<Svg
style={[
contextStyle,
style,
{
...((mirrored ?? contextMirrored) && {
transform: [{ scaleX: -1 }],
}),
},
]}
className={`acorn-${weight}__svg-icon-phosphor`}
testID={props.testID ?? 'phosphor-react-native-acorn-bold'}
fill="currentColor"
viewBox="0 0 256 256"
width={size ?? contextSize}
height={size ?? contextSize}
color={color ?? contextColor}
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
{(weight ?? contextWeight) === 'duotone'
? // @ts-expect-error not callable
weights.get(weight ?? contextWeight)({
duotoneColor: duotoneColor ?? contextDuotoneColor ?? color,
duotoneOpacity: duotoneOpacity ?? contextDuotoneOpacity,
})
: weights.get(weight ?? contextWeight)}
</Svg>
);
}

export default IconBase;
35 changes: 35 additions & 0 deletions src/lib/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { createContext } from 'react';
import { StyleProp, ViewStyle } from 'react-native';

export type IconWeight =
| 'thin'
| 'light'
| 'regular'
| 'bold'
| 'fill'
| 'duotone';

export type PaintFunction = (color: string) => React.ReactNode | null;

export interface IconProps {
color?: string;
size?: string | number;
weight?: IconWeight;
style?: StyleProp<ViewStyle>;
mirrored?: boolean;
testID?: string;
duotoneColor?: string;
duotoneOpacity?: number;
title?: string; // SVGRProps
titleId?: string; // SVGRProps
}

export type Icon = React.FC<IconProps>;

export const IconContext = createContext<IconProps>({
color: '#000',
size: 24,
weight: 'regular',
mirrored: false,
duotoneOpacity: 0.2,
});

0 comments on commit ae16180

Please sign in to comment.