Compare commits

...

10 Commits

Author SHA1 Message Date
Eric Fennis
ba3d66dc53 Merge branch 'main' of https://github.com/lucide-icons/lucide into backwards-compatible-classnames 2025-05-02 15:44:38 +02:00
Eric Fennis
c663a141b6 Update snapshots 2025-04-04 16:27:24 +02:00
Eric Fennis
35a1ec15cc Merge branch 'main' of https://github.com/lucide-icons/lucide into backwards-compatible-classnames 2025-04-04 14:46:42 +02:00
Eric Fennis
8a44e63e80 Adjust classes lucide vue next 2025-03-22 11:26:25 +01:00
Eric Fennis
35afa59c99 Add support for icon alias classnames in lucide-svelte 2025-03-21 11:18:03 +01:00
Eric Fennis
c42676b0fd Add support for alias classNames in lucide-static 2025-03-21 10:56:54 +01:00
Eric Fennis
613cac84d0 Add support for alias classnames in lucide-solid 2025-03-19 11:38:26 +01:00
Eric Fennis
cf48dbb2d3 Add support for preact 2025-03-19 11:14:07 +01:00
Eric Fennis
4edcf24fc9 Support aliased class names in lucide-react 2025-03-19 10:58:24 +01:00
Eric Fennis
1efc9eb8b5 Add support for aliases names in export template 2025-03-19 10:51:36 +01:00
39 changed files with 152 additions and 72 deletions

View File

@@ -14,5 +14,5 @@
<path d="M9 9h.01" /> <path d="M9 9h.01" />
<circle cx="20" cy="16" r="2" /> <circle cx="20" cy="16" r="2" />
<circle cx="9" cy="9" r="7" /> <circle cx="9" cy="9" r="7" />
<rect x="4" y="16" width="10" height="6" rx="2" /> <rect width="10" height="6" x="4" y="16" rx="2" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 439 B

After

Width:  |  Height:  |  Size: 439 B

View File

@@ -13,5 +13,5 @@
<path d="M16 4h2a2 2 0 0 1 2 2v1.344" /> <path d="M16 4h2a2 2 0 0 1 2 2v1.344" />
<path d="m17 18 4-4-4-4" /> <path d="m17 18 4-4-4-4" />
<path d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113" /> <path d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113" />
<rect x="8" y="2" width="8" height="4" rx="1" /> <rect width="8" height="4" x="8" y="2" rx="1" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 435 B

After

Width:  |  Height:  |  Size: 435 B

View File

@@ -21,4 +21,4 @@
<path d="m21.7 19.4-.9-.3" /> <path d="m21.7 19.4-.9-.3" />
<path d="M9 3v18" /> <path d="M9 3v18" />
<circle cx="18" cy="18" r="3" /> <circle cx="18" cy="18" r="3" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 617 B

After

Width:  |  Height:  |  Size: 618 B

View File

@@ -21,6 +21,6 @@
<path d="M20 7h2" /> <path d="M20 7h2" />
<path d="M7 20v2" /> <path d="M7 20v2" />
<path d="M7 2v2" /> <path d="M7 2v2" />
<rect x="4" y="4" width="16" height="16" rx="2" /> <rect width="16" height="16" x="4" y="4" rx="2" />
<rect x="8" y="8" width="8" height="8" rx="1" /> <rect width="8" height="8" x="8" y="8" rx="1" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 590 B

After

Width:  |  Height:  |  Size: 590 B

View File

@@ -12,5 +12,5 @@
<path d="m13 21-3-3 3-3" /> <path d="m13 21-3-3 3-3" />
<path d="M20 18H10" /> <path d="M20 18H10" />
<path d="M3 11h.01" /> <path d="M3 11h.01" />
<rect x="6" y="3" width="5" height="8" rx="2.5" /> <rect width="5" height="8" x="6" y="3" rx="2.5" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 341 B

After

Width:  |  Height:  |  Size: 341 B

View File

@@ -12,6 +12,6 @@
<path d="M10 18h10" /> <path d="M10 18h10" />
<path d="m17 21 3-3-3-3" /> <path d="m17 21 3-3-3-3" />
<path d="M3 11h.01" /> <path d="M3 11h.01" />
<rect x="15" y="3" width="5" height="8" rx="2.5" /> <rect width="5" height="8" x="15" y="3" rx="2.5" />
<rect x="6" y="3" width="5" height="8" rx="2.5" /> <rect width="5" height="8" x="6" y="3" rx="2.5" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 395 B

View File

@@ -13,5 +13,5 @@
<path d="M18 9V6a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v14" /> <path d="M18 9V6a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v14" />
<path d="M2 20h8" /> <path d="M2 20h8" />
<path d="M20 17v-2a2 2 0 1 0-4 0v2" /> <path d="M20 17v-2a2 2 0 1 0-4 0v2" />
<rect x="14" y="17" width="8" height="5" rx="1" /> <rect width="8" height="5" x="14" y="17" rx="1" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 407 B

After

Width:  |  Height:  |  Size: 407 B

View File

@@ -10,5 +10,5 @@
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" /> <path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" />
<rect x="2" y="4" width="20" height="16" rx="2" /> <rect width="20" height="16" x="2" y="4" rx="2" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 316 B

View File

@@ -10,8 +10,8 @@
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z" /> <path d="M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z" />
<circle cx="13.5" cy="6.5" r=".5" fill="currentColor" /> <circle cx="13.5" cy="6.5" r=".5" />
<circle cx="17.5" cy="10.5" r=".5" fill="currentColor" /> <circle cx="17.5" cy="10.5" r=".5" />
<circle cx="6.5" cy="12.5" r=".5" fill="currentColor" /> <circle cx="6.5" cy="12.5" r=".5" />
<circle cx="8.5" cy="7.5" r=".5" fill="currentColor" /> <circle cx="8.5" cy="7.5" r=".5" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 571 B

After

Width:  |  Height:  |  Size: 491 B

View File

@@ -15,5 +15,5 @@
<path d="M20 8V4" /> <path d="M20 8V4" />
<path d="M4 8V4" /> <path d="M4 8V4" />
<path d="M8 15v-3.014" /> <path d="M8 15v-3.014" />
<rect x="3" y="12" width="18" height="7" rx="1" /> <rect width="18" height="7" x="3" y="12" rx="1" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 415 B

After

Width:  |  Height:  |  Size: 415 B

View File

@@ -13,4 +13,4 @@
<path d="M5 17A12 12 0 0 1 17 5" /> <path d="M5 17A12 12 0 0 1 17 5" />
<circle cx="19" cy="5" r="2" /> <circle cx="19" cy="5" r="2" />
<circle cx="5" cy="19" r="2" /> <circle cx="5" cy="19" r="2" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 458 B

After

Width:  |  Height:  |  Size: 459 B

View File

@@ -11,4 +11,4 @@
> >
<path d="M16 12v2a2 2 0 0 1-2 2H9a1 1 0 0 0-1 1v3a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2h0" /> <path d="M16 12v2a2 2 0 0 1-2 2H9a1 1 0 0 0-1 1v3a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2h0" />
<path d="M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v3a1 1 0 0 1-1 1h-5a2 2 0 0 0-2 2v2" /> <path d="M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v3a1 1 0 0 1-1 1h-5a2 2 0 0 0-2 2v2" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 416 B

After

Width:  |  Height:  |  Size: 417 B

View File

@@ -20,4 +20,4 @@
<path d="M4 16a2 2 0 0 1-2-2" /> <path d="M4 16a2 2 0 0 1-2-2" />
<path d="M8 10a2 2 0 0 1 2-2h5a1 1 0 0 1 1 1v5a2 2 0 0 1-2 2H9a1 1 0 0 1-1-1z" /> <path d="M8 10a2 2 0 0 1 2-2h5a1 1 0 0 1 1 1v5a2 2 0 0 1-2 2H9a1 1 0 0 1-1-1z" />
<path d="M8 2h2" /> <path d="M8 2h2" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 597 B

View File

@@ -15,4 +15,4 @@
<path d="M20 8a2 2 0 0 1 2 2" /> <path d="M20 8a2 2 0 0 1 2 2" />
<path d="M22 14v2" /> <path d="M22 14v2" />
<path d="M22 20a2 2 0 0 1-2 2" /> <path d="M22 20a2 2 0 0 1-2 2" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 481 B

After

Width:  |  Height:  |  Size: 482 B

View File

@@ -10,4 +10,4 @@
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v3a1 1 0 0 0 1 1h3a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2v-3a1 1 0 0 0-1-1z" /> <path d="M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v3a1 1 0 0 0 1 1h3a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2v-3a1 1 0 0 0-1-1z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 359 B

After

Width:  |  Height:  |  Size: 360 B

View File

@@ -39,6 +39,7 @@
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts", "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts",
"build:bundles": "rollup -c ./rollup.config.mjs", "build:bundles": "rollup -c ./rollup.config.mjs",
"test": "pnpm build:icons && vitest run", "test": "pnpm build:icons && vitest run",
"test:update": "vitest run -u",
"version": "pnpm version --git-tag-version=false" "version": "pnpm version --git-tag-version=false"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -8,6 +8,7 @@ export default async ({
getSvg, getSvg,
deprecated, deprecated,
deprecationReason, deprecationReason,
iconNameAliases,
}) => { }) => {
const svgContents = await getSvg(); const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents); const svgBase64 = base64SVG(svgContents);
@@ -26,7 +27,9 @@ import createLucideIcon from '../createLucideIcon';
* @returns {JSX.Element} JSX Element * @returns {JSX.Element} JSX Element
* ${deprecated ? `@deprecated ${deprecationReason}` : ''} * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/ */
const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)}); const ${componentName} = createLucideIcon('${componentName}', ${JSON.stringify(children)}${
iconNameAliases != null ? `, ${JSON.stringify(iconNameAliases)}` : ''
});
export default ${componentName}; export default ${componentName};
`; `;

View File

@@ -1,5 +1,5 @@
import { h, type JSX } from 'preact'; import { h, type JSX } from 'preact';
import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared'; import { createLucideClassNames, mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
import Icon from './Icon'; import Icon from './Icon';
import type { IconNode, LucideIcon, LucideProps } from './types'; import type { IconNode, LucideIcon, LucideProps } from './types';
@@ -9,7 +9,12 @@ import type { IconNode, LucideIcon, LucideProps } from './types';
* @param {array} iconNode * @param {array} iconNode
* @returns {FunctionComponent} LucideIcon * @returns {FunctionComponent} LucideIcon
*/ */
const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => { const createLucideIcon = (iconName: string, iconNode: IconNode, aliasNames: string[] = []): LucideIcon => {
const lucideClassNames = createLucideClassNames([
iconName,
...aliasNames,
]);
const Component = ({ class: classes = '', children, ...props }: LucideProps) => const Component = ({ class: classes = '', children, ...props }: LucideProps) =>
h( h(
Icon, Icon,
@@ -17,8 +22,8 @@ const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
...props, ...props,
iconNode, iconNode,
class: mergeClasses<string | JSX.SignalLike<string | undefined>>( class: mergeClasses<string | JSX.SignalLike<string | undefined>>(
lucideClassNames,
`lucide-${toKebabCase(toPascalCase(iconName))}`, `lucide-${toKebabCase(toPascalCase(iconName))}`,
`lucide-${toKebabCase(iconName)}`,
classes, classes,
), ),
}, },

View File

@@ -58,7 +58,7 @@ exports[`Using createLucideIcon > should create a component from an iconNode wit
exports[`Using createLucideIcon > should include backwards compatible className 1`] = ` exports[`Using createLucideIcon > should include backwards compatible className 1`] = `
<svg <svg
class="lucide lucide-layout2 lucide-layout-2" class="lucide lucide-layout-2 lucide-layout2"
fill="none" fill="none"
height="24" height="24"
stroke="currentColor" stroke="currentColor"

View File

@@ -10,7 +10,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
stroke-width="4" stroke-width="4"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="lucide lucide-grid3x3 lucide-grid-3x3" class="lucide lucide-grid3x3 lucide-grid lucide-grid-3-x-3 lucide-grid3x3"
> >
<rect width="18" <rect width="18"
height="18" height="18"
@@ -40,7 +40,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
stroke-width="1" stroke-width="1"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="lucide lucide-grid3x3 lucide-grid-3x3" class="lucide lucide-grid3x3 lucide-grid lucide-grid-3-x-3 lucide-grid3x3"
> >
<rect width="18" <rect width="18"
height="18" height="18"
@@ -70,7 +70,7 @@ exports[`Using lucide icon components > should render an component 1`] = `
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="lucide lucide-grid3x3 lucide-grid-3x3" class="lucide lucide-grid3x3 lucide-grid lucide-grid-3-x-3 lucide-grid3x3"
> >
<rect width="18" <rect width="18"
height="18" height="18"

View File

@@ -70,6 +70,16 @@ describe('Using lucide icon components', () => {
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
}); });
it('should render the alias icon name classNames', () => {
const { container } = render(
<Pen />,
);
const PenIcon = container.firstChild;
expect(PenIcon).toHaveClass('lucide-edit-2');
})
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
const { container } = render( const { container } = render(
<Grid <Grid

View File

@@ -47,6 +47,7 @@
"typecheck": "tsc", "typecheck": "tsc",
"typecheck:watch": "tsc -w", "typecheck:watch": "tsc -w",
"test": "pnpm build:icons && vitest run", "test": "pnpm build:icons && vitest run",
"test:update": "vitest run -u",
"test:watch": "vitest watch", "test:watch": "vitest watch",
"version": "pnpm version --git-tag-version=false" "version": "pnpm version --git-tag-version=false"
}, },

View File

@@ -8,6 +8,7 @@ export default async ({
getSvg, getSvg,
deprecated, deprecated,
deprecationReason, deprecationReason,
iconNameAliases,
}) => { }) => {
const svgContents = await getSvg(); const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents); const svgBase64 = base64SVG(svgContents);
@@ -29,7 +30,9 @@ export const __iconNode: IconNode = ${JSON.stringify(children)}
* @returns {JSX.Element} JSX Element * @returns {JSX.Element} JSX Element
* ${deprecated ? `@deprecated ${deprecationReason}` : ''} * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/ */
const ${componentName} = createLucideIcon('${iconName}', __iconNode); const ${componentName} = createLucideIcon('${iconName}', __iconNode${
iconNameAliases != null ? `, ${JSON.stringify(iconNameAliases)}` : ''
});
export default ${componentName}; export default ${componentName};
`; `;

View File

@@ -1,5 +1,5 @@
import { createElement, forwardRef } from 'react'; import { createElement, forwardRef } from 'react';
import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared'; import { mergeClasses, createLucideClassNames, toPascalCase, toKebabCase } from '@lucide/shared';
import { IconNode, LucideProps } from './types'; import { IconNode, LucideProps } from './types';
import Icon from './Icon'; import Icon from './Icon';
@@ -9,14 +9,19 @@ import Icon from './Icon';
* @param {array} iconNode * @param {array} iconNode
* @returns {ForwardRefExoticComponent} LucideIcon * @returns {ForwardRefExoticComponent} LucideIcon
*/ */
const createLucideIcon = (iconName: string, iconNode: IconNode) => { const createLucideIcon = (iconName: string, iconNode: IconNode, aliasNames: string[] = []) => {
const lucideClassNames = createLucideClassNames([
iconName,
...aliasNames,
]);
const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) => const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) =>
createElement(Icon, { createElement(Icon, {
ref, ref,
iconNode, iconNode,
className: mergeClasses( className: mergeClasses(
`lucide-${toKebabCase(toPascalCase(iconName))}`, `lucide-${toKebabCase(toPascalCase(iconName))}`,
`lucide-${iconName}`, lucideClassNames,
className, className,
), ),
...props, ...props,

View File

@@ -2,8 +2,7 @@
exports[`Using createLucideIcon > should create a component from an iconNode 1`] = ` exports[`Using createLucideIcon > should create a component from an iconNode 1`] = `
<svg <svg
aria-hidden="true" class="lucide lucide-air-vent"
class="lucide lucide-air-vent lucide-AirVent"
fill="none" fill="none"
height="24" height="24"
stroke="currentColor" stroke="currentColor"

View File

@@ -10,8 +10,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
stroke-width="4" stroke-width="4"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="lucide lucide-grid3x3 lucide-grid-3x3" class="lucide lucide-grid3x3 lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
> >
<rect width="18" <rect width="18"
height="18" height="18"
@@ -41,8 +40,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
stroke-width="1" stroke-width="1"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="lucide lucide-grid3x3 lucide-grid-3x3" class="lucide lucide-grid3x3 lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
> >
<rect width="18" <rect width="18"
height="18" height="18"
@@ -72,8 +70,7 @@ exports[`Using lucide icon components > should render an component 1`] = `
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="lucide lucide-grid3x3 lucide-grid-3x3" class="lucide lucide-grid3x3 lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
> >
<rect width="18" <rect width="18"
height="18" height="18"

View File

@@ -69,6 +69,16 @@ describe('Using lucide icon components', () => {
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
}); });
it('should render the alias icon name classNames', () => {
const { container } = render(
<Pen />,
);
const PenIcon = container.firstChild;
expect(PenIcon).toHaveClass('lucide-edit-2');
})
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
const { container, getByTestId } = render( const { container, getByTestId } = render(
<Grid <Grid

View File

@@ -63,6 +63,7 @@
"build:bundle": "rollup -c rollup.config.mjs", "build:bundle": "rollup -c rollup.config.mjs",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --renderUniqueKey --withAliases --separateAliasesFile --aliasesFileExtension=.ts --iconFileExtension=.tsx --exportFileName=index.ts", "build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --renderUniqueKey --withAliases --separateAliasesFile --aliasesFileExtension=.ts --iconFileExtension=.tsx --exportFileName=index.ts",
"test": "pnpm build:icons && vitest run", "test": "pnpm build:icons && vitest run",
"test:update": "vitest run -u",
"version": "pnpm version --git-tag-version=false" "version": "pnpm version --git-tag-version=false"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -8,6 +8,7 @@ export default async ({
getSvg, getSvg,
deprecated, deprecated,
deprecationReason, deprecationReason,
iconNameAliases,
}) => { }) => {
const svgContents = await getSvg(); const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents); const svgBase64 = base64SVG(svgContents);
@@ -30,7 +31,9 @@ const iconNode: IconNode = ${JSON.stringify(children)};
* ${deprecated ? `@deprecated ${deprecationReason}` : ''} * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/ */
const ${componentName} = (props: LucideProps) => ( const ${componentName} = (props: LucideProps) => (
<Icon {...props} iconNode={iconNode} name="${iconName}" /> <Icon {...props} name="${iconName}" iconNode={iconNode}${
iconNameAliases != null ? ` aliasNames={${JSON.stringify(iconNameAliases)}}` : ''
} />
) )
export default ${componentName}; export default ${componentName};

View File

@@ -2,11 +2,12 @@ import { For, splitProps } from 'solid-js';
import { Dynamic } from 'solid-js/web'; import { Dynamic } from 'solid-js/web';
import defaultAttributes from './defaultAttributes'; import defaultAttributes from './defaultAttributes';
import { IconNode, LucideProps } from './types'; import { IconNode, LucideProps } from './types';
import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared'; import { mergeClasses, toKebabCase, createLucideClassNames, toPascalCase } from '@lucide/shared';
interface IconProps { interface IconProps {
name?: string; name?: string;
iconNode: IconNode; iconNode: IconNode;
aliasNames?: string[];
} }
const Icon = (props: LucideProps & IconProps) => { const Icon = (props: LucideProps & IconProps) => {
@@ -19,6 +20,7 @@ const Icon = (props: LucideProps & IconProps) => {
'name', 'name',
'iconNode', 'iconNode',
'absoluteStrokeWidth', 'absoluteStrokeWidth',
'aliasNames'
]); ]);
return ( return (
@@ -42,6 +44,7 @@ const Icon = (props: LucideProps & IconProps) => {
`lucide-${toKebabCase(localProps.name)}`, `lucide-${toKebabCase(localProps.name)}`,
] ]
: []), : []),
localProps.aliasNames != null ? createLucideClassNames(localProps.aliasNames) : undefined,
localProps.class != null ? localProps.class : '', localProps.class != null ? localProps.class : '',
)} )}
{...rest} {...rest}

View File

@@ -10,8 +10,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
height="48" height="48"
stroke="red" stroke="red"
stroke-width="4" stroke-width="4"
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3" class="lucide lucide-icon lucide-grid3x3 lucide-grid lucide-grid-3-x-3"
data-testid="grid-icon"
> >
<rect width="18" <rect width="18"
height="18" height="18"
@@ -50,8 +49,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
height="48" height="48"
stroke="red" stroke="red"
stroke-width="1" stroke-width="1"
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3" class="lucide lucide-icon lucide-grid3x3 lucide-grid lucide-grid-3-x-3"
data-testid="grid-icon"
> >
<rect width="18" <rect width="18"
height="18" height="18"
@@ -90,7 +88,7 @@ exports[`Using lucide icon components > should render a component 1`] = `
height="24" height="24"
stroke="currentColor" stroke="currentColor"
stroke-width="2" stroke-width="2"
class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3" class="lucide lucide-icon lucide-grid3x3 lucide-grid lucide-grid-3-x-3"
> >
<rect width="18" <rect width="18"
height="18" height="18"

View File

@@ -10,31 +10,27 @@ describe('Using lucide icon components', () => {
}); });
it('should adjust the size, stroke color and stroke width', async () => { it('should adjust the size, stroke color and stroke width', async () => {
const testId = 'grid-icon';
const { container, getByTestId } = render(() => ( const { container, getByTestId } = render(() => (
<Grid <Grid
data-testid={testId}
size={48} size={48}
color="red" color="red"
strokeWidth={4} strokeWidth={4}
/> />
)); ));
const { attributes } = (await getByTestId(testId)) as unknown as { const gridIcon = container.firstChild as SVGElement;
attributes: Record<string, { value: string }>;
}; expect(gridIcon).toHaveAttribute('width', '48');
expect(attributes.stroke.value).toBe('red'); expect(gridIcon).toHaveAttribute('height', '48');
expect(attributes.width.value).toBe('48'); expect(gridIcon).toHaveAttribute('stroke', 'red');
expect(attributes.height.value).toBe('48'); expect(gridIcon).toHaveAttribute('stroke-width', '4');
expect(attributes['stroke-width'].value).toBe('4');
expect(container.innerHTML).toMatchSnapshot(); expect(container.innerHTML).toMatchSnapshot();
}); });
it('should render the alias icon', () => { it('should render the alias icon', () => {
const testId = 'pen-icon';
const { container } = render(() => ( const { container } = render(() => (
<Pen <Pen
data-testid={testId}
size={48} size={48}
stroke="red" stroke="red"
strokeWidth={4} strokeWidth={4}
@@ -47,7 +43,6 @@ describe('Using lucide icon components', () => {
const { container: Edit2Container } = render(() => ( const { container: Edit2Container } = render(() => (
<Edit2 <Edit2
data-testid={testId}
size={48} size={48}
stroke="red" stroke="red"
strokeWidth={4} strokeWidth={4}
@@ -57,24 +52,31 @@ describe('Using lucide icon components', () => {
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
}); });
it('should render the alias icon name classNames', () => {
const { container } = render(() => (
<Pen />
));
const PenIcon = container.firstChild;
expect(PenIcon).toHaveClass('lucide-edit-2');
})
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
const testId = 'grid-icon';
const { container, getByTestId } = render(() => ( const { container, getByTestId } = render(() => (
<Grid <Grid
data-testid={testId}
size={48} size={48}
color="red" color="red"
absoluteStrokeWidth absoluteStrokeWidth
/> />
)); ));
const { attributes } = getByTestId(testId) as unknown as { const gridIcon = container.firstChild as SVGElement;
attributes: Record<string, { value: string }>;
}; expect(gridIcon).toHaveAttribute('stroke-width', '1');
expect(attributes.stroke.value).toBe('red'); expect(gridIcon).toHaveAttribute('width', '48');
expect(attributes.width.value).toBe('48'); expect(gridIcon).toHaveAttribute('height', '48');
expect(attributes.height.value).toBe('48'); expect(gridIcon).toHaveAttribute('stroke', 'red');
expect(attributes['stroke-width'].value).toBe('1');
expect(container.innerHTML).toMatchSnapshot(); expect(container.innerHTML).toMatchSnapshot();
}); });

View File

@@ -1,15 +1,17 @@
/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs'; import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
export default async ({ componentName, iconName, getSvg, deprecated, deprecationReason }) => { export default async ({ componentName, iconName, getSvg, deprecated, deprecationReason, iconNameAliases = [] }) => {
let svgContents = await getSvg(); let svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents); const svgBase64 = base64SVG(svgContents);
const iconClassNames = [iconName, ...iconNameAliases].map((aliasName) => `lucide-${aliasName}`).join(' ')
svgContents = svgContents.replace( svgContents = svgContents.replace(
'<svg', '<svg',
` `
<svg <svg
class="lucide lucide-${iconName}"`, class="lucide ${iconClassNames}"`,
); );
return ` return `

View File

@@ -9,6 +9,7 @@ export default async ({
getSvg, getSvg,
deprecated, deprecated,
deprecationReason, deprecationReason,
iconNameAliases
}) => { }) => {
const svgContents = await getSvg(); const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents); const svgBase64 = base64SVG(svgContents);
@@ -36,7 +37,9 @@ const iconNode: IconNode = ${JSON.stringify(children)};
*/ */
</script> </script>
<Icon name="${iconName}" {...$$props} iconNode={iconNode}> <Icon name="${iconName}" {...$$props} iconNode={iconNode}${
iconNameAliases != null ? ` aliasNames={${JSON.stringify(iconNameAliases)}}` : ''
}>
<slot/> <slot/>
</Icon> </Icon>
`; `;

View File

@@ -8,6 +8,7 @@
export let strokeWidth: number | string = 2 export let strokeWidth: number | string = 2
export let absoluteStrokeWidth: boolean = false export let absoluteStrokeWidth: boolean = false
export let iconNode: IconNode = [] export let iconNode: IconNode = []
export let aliasNames: string[] = []
const mergeClasses = <ClassType = string | undefined | null>( const mergeClasses = <ClassType = string | undefined | null>(
...classes: ClassType[] ...classes: ClassType[]
@@ -34,6 +35,7 @@
'lucide-icon', 'lucide-icon',
'lucide', 'lucide',
name ? `lucide-${name}`: '', name ? `lucide-${name}`: '',
...aliasNames.map(alias => `lucide-${alias}`),
$$props.class $$props.class
) )
} }

View File

@@ -70,6 +70,14 @@ describe('Using lucide icon components', () => {
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML); expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
}); });
it('should render the alias icon name classNames', () => {
const { container } = render(Pen);
const PenIcon = container.firstChild;
expect(PenIcon).toHaveClass('lucide-edit-2');
})
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => { it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
const testId = 'smile-icon'; const testId = 'smile-icon';
const { container, getByTestId } = render(Smile, { const { container, getByTestId } = render(Smile, {

View File

@@ -12,7 +12,7 @@ import Icon from './Icon';
* @returns {FunctionalComponent} LucideIcon * @returns {FunctionalComponent} LucideIcon
*/ */
const createLucideIcon = const createLucideIcon =
(iconName: string, iconNode: IconNode): FunctionalComponent<LucideProps> => (iconName: string, iconNode: IconNode, aliasNames: string[] = []): FunctionalComponent<LucideProps> =>
(props, { slots }) => (props, { slots }) =>
h( h(
Icon, Icon,

View File

@@ -50,6 +50,22 @@ export const mergeClasses = <ClassType = string | undefined | null>(...classes:
.join(' ') .join(' ')
.trim(); .trim();
/**
* Create list of lucide icon names
*
* @param {array} classes
* @returns {string} A string of classes
*/
export const createLucideClassNames = (iconNames?: string[]) => {
if (iconNames == null) {
return '';
}
return iconNames
.map((aliasName) => `lucide-${toKebabCase(aliasName)}`)
.join(' ')
.trim();
/** /**
* Check if a component has an accessibility prop * Check if a component has an accessibility prop
* *

View File

@@ -40,6 +40,13 @@ function generateIconFiles({
}) })
: ''; : '';
const iconNameAliases = iconMetaData[iconName]?.aliases?.map((alias) => {
if (typeof alias === 'string') {
return alias;
}
return alias.name;
});
const elementTemplate = await template({ const elementTemplate = await template({
componentName, componentName,
iconName, iconName,
@@ -47,6 +54,7 @@ function generateIconFiles({
getSvg, getSvg,
deprecated, deprecated,
deprecationReason, deprecationReason,
iconNameAliases
}); });
const output = pretty const output = pretty