Compare commits

..

8 Commits

Author SHA1 Message Date
Eric Fennis
5fab4e7bc8 Merge branch 'main' of https://github.com/lucide-icons/lucide into next 2025-12-22 08:12:08 +01:00
Karsa
d391bda369 feat: add Android to brand stopwords (#3895) 2025-12-22 07:54:08 +01:00
David Castilla Ortiz
69bf052ee5 Enable ligatures in font build configuration (#3876) 2025-12-18 12:17:28 +01:00
Karsa
6b4075b89b feat(icons): added toolbox icon (#3871)
* Added icons/toolbox.svg

* Added icons/toolbox.json
2025-12-18 11:44:47 +01:00
Jacek Tomaszewski
7a68e10b12 fix(lucide-react-native): remove icons namespace export to enable tree-shaking (#3868)
* fix(lucide-react-native): remove icons namespace export to enable tree-shaking

The `export * as icons from './icons'` statement defeats tree-shaking
because bundlers cannot determine which exports from the namespace are
actually used at build time. This causes all 1600+ icons to be included
in the final bundle even when only a few are imported.

This change removes the namespace re-export while keeping all individual
icon exports available via `export * from './icons'`.

BREAKING CHANGE: The `icons` namespace export is no longer available.
Users should import icons directly: `import { Activity } from 'lucide-react-native'`
instead of `import { icons } from 'lucide-react-native'; icons.Activity`.

* Add icons entry file to improve treeshaking

* Format code

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-18 11:44:26 +01:00
Jakob Guddas
a4531a9985 fix(react-native-web): only add className prop to parent Icon component (#3892) 2025-12-18 11:43:31 +01:00
taimar
3edcd9e0c3 fix and unify color-picker font-size (#3889) 2025-12-15 14:59:14 +01:00
Jakob Guddas
0b8f99326c fix(icons): changed paint-bucket icon (#3880)
* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.json

* Updated icons/paint-bucket.json
2025-12-12 13:27:37 +01:00
57 changed files with 376 additions and 1286 deletions

View File

@@ -2,6 +2,7 @@
"adobe": "Adobe",
"airplay": "AirPlay",
"amazon": "Amazon",
"android": "Android",
"angular": "Angular",
"aws": "AWS",
"azure": "Azure",

View File

@@ -70,7 +70,7 @@ const value = computed({
color: var(--vp-c-text-2);
padding: 3px 8px 3px 3px;
height: auto;
font-size: 14px;
font-size: 13px;
text-align: left;
border: 1px solid transparent;
cursor: text;
@@ -90,7 +90,7 @@ const value = computed({
border: none;
background: transparent;
color: var(--vp-c-text-1);
font-size: 14px;
font-size: 13px;
text-align: left;
border-radius: 8px;
cursor: text;

View File

@@ -102,10 +102,16 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
### Icon Component Example
```jsx
import { icons } from 'lucide-react-native';
```tsx
import * as icons from 'lucide-react-native/icons';
const Icon = ({ name, color, size }) => {
interface IconProps {
name: keyof typeof icons;
color?: string;
size?: number;
}
const Icon = ({ name, color, size }: IconProps) => {
const LucideIcon = icons[name];
return <LucideIcon color={color} size={size} />;
@@ -116,11 +122,11 @@ export default Icon;
#### Using the Icon Component
```jsx
```tsx
import Icon from './Icon';
const App = () => {
return <Icon name="house" />;
return <Icon name="House" />;
};
export default App;

View File

@@ -9,8 +9,8 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M19 12H2" />
<path d="M11 7 6 2" />
<path d="M18.992 12H2.041" />
<path d="M21.145 18.38A3.34 3.34 0 0 1 20 16.5a3.3 3.3 0 0 1-1.145 1.88c-.575.46-.855 1.02-.855 1.595A2 2 0 0 0 20 22a2 2 0 0 0 2-2.025c0-.58-.285-1.13-.855-1.595" />
<path d="m6 2 5 5" />
<path d="m8.5 4.5 2.148-2.148a1.205 1.205 0 0 1 1.704 0l7.296 7.296a1.205 1.205 0 0 1 0 1.704l-7.592 7.592a3.615 3.615 0 0 1-5.112 0l-3.888-3.888a3.615 3.615 0 0 1 0-5.112L5.67 7.33" />
</svg>

Before

Width:  |  Height:  |  Size: 613 B

After

Width:  |  Height:  |  Size: 622 B

37
icons/toolbox.json Normal file
View File

@@ -0,0 +1,37 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere"
],
"tags": [
"toolkit",
"tools",
"trunk",
"chest",
"box",
"storage",
"utility",
"utilities",
"container",
"kit",
"set",
"repair",
"fix",
"service",
"maintenance",
"mechanic",
"workshop",
"construction",
"hardware",
"equipment",
"gear",
"handyman",
"engineering",
"craft",
"diy"
],
"categories": [
"tools",
"home"
]
}

17
icons/toolbox.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M16 12v4" />
<path d="M16 6a2 2 0 0 1 1.414.586l4 4A2 2 0 0 1 22 12v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 .586-1.414l4-4A2 2 0 0 1 8 6z" />
<path d="M16 6V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" />
<path d="M2 14h20" />
<path d="M8 12v4" />
</svg>

After

Width:  |  Height:  |  Size: 471 B

View File

@@ -37,7 +37,6 @@
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts",
"build:bundles": "rollup -c ./rollup.config.mjs",
"test": "pnpm build:icons && vitest run",
"test:watch": "vitest watch",
"version": "pnpm version --git-tag-version=false"
},
"devDependencies": {
@@ -48,7 +47,7 @@
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/preact": "^3.2.3",
"jest-serializer-html": "^7.1.0",
"preact": "^10.26.9",
"preact": "^10.19.2",
"rollup": "^4.53.3",
"rollup-plugin-dts": "^6.2.3",
"typescript": "^5.8.3",

View File

@@ -1,8 +1,6 @@
import { h, toChildArray } from 'preact';
import defaultAttributes from './defaultAttributes';
import type { IconNode, LucideProps } from './types';
import { useLucideContext } from './context';
import { mergeClasses } from '@lucide/shared';
interface IconComponentProps extends LucideProps {
iconNode: IconNode;
@@ -24,41 +22,29 @@ interface IconComponentProps extends LucideProps {
* @returns {ForwardRefExoticComponent} LucideIcon
*/
const Icon = ({
color,
size,
strokeWidth,
color = 'currentColor',
size = 24,
strokeWidth = 2,
absoluteStrokeWidth,
children,
iconNode,
class: classes = '',
...rest
}: IconComponentProps) => {
const {
size: contextSize = 24,
strokeWidth: contextStrokeWidth = 2,
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
color: contextColor = 'currentColor',
class: contextClass = '',
} = useLucideContext() ?? {};
const calculatedStrokeWidth =
absoluteStrokeWidth ?? contextAbsoluteStrokeWidth
? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize)
: strokeWidth ?? contextStrokeWidth;
return h(
}: IconComponentProps) =>
h(
'svg',
{
...defaultAttributes,
width: size ?? contextSize ?? 24,
height: size ?? contextSize ?? 24,
stroke: color ?? contextColor,
['stroke-width' as 'strokeWidth']: calculatedStrokeWidth,
class: mergeClasses('lucide', contextClass, classes),
width: String(size),
height: size,
stroke: color,
['stroke-width' as 'strokeWidth']: absoluteStrokeWidth
? (Number(strokeWidth) * 24) / Number(size)
: strokeWidth,
class: ['lucide', classes].join(' '),
...rest,
},
[...iconNode.map(([tag, attrs]) => h(tag, attrs)), ...toChildArray(children)],
);
};
export default Icon;

View File

@@ -1,49 +0,0 @@
import { createContext, type ComponentChildren } from 'preact';
import { useContext, useMemo } from 'preact/hooks';
const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
class?: string;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
class: '',
});
interface LucideProviderProps {
children: ComponentChildren;
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
class?: string;
}
export function LucideProvider({
children,
size,
color,
strokeWidth,
absoluteStrokeWidth,
class: className,
}: LucideProviderProps) {
const value = useMemo(
() => ({
size,
color,
strokeWidth,
absoluteStrokeWidth,
class: className,
}),
[size, color, strokeWidth, absoluteStrokeWidth, className],
);
return <LucideContext.Provider value={value}>{children}</LucideContext.Provider>;
}
export const useLucideContext = () => useContext(LucideContext);

View File

@@ -2,7 +2,6 @@ export * from './icons';
export * as icons from './icons';
export * from './aliases';
export * from './types';
export * from './context';
export { default as createLucideIcon } from './createLucideIcon';
export { default as Icon } from './Icon';

View File

@@ -2,7 +2,7 @@
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
<svg
class="lucide"
class="lucide "
fill="none"
height="48"
stroke="red"

View File

@@ -1,23 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using LucideProvider > should render the icon with LucideProvider 1`] = `
<svg
class="lucide lucide-house"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
/>
<path
d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
/>
</svg>
`;

View File

@@ -1,88 +0,0 @@
import { render } from '@testing-library/preact';
import { describe, expect, it } from 'vitest';
import { House, LucideProvider } from '../src/lucide-preact';
describe('Using LucideProvider', () => {
it('should render the icon with LucideProvider', () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
>
<House />
</LucideProvider>,
);
expect(container.firstChild).toMatchSnapshot();
});
it('should render the icon with LucideProvider and custom strokeWidth', () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '48');
expect(IconComponent).toHaveAttribute('height', '48');
expect(IconComponent).toHaveAttribute('stroke', 'red');
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it("should override the provider's global props when passing props to the icon", () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House
size={24}
color="blue"
strokeWidth={2}
/>
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '24');
expect(IconComponent).toHaveAttribute('height', '24');
expect(IconComponent).toHaveAttribute('stroke', 'blue');
expect(IconComponent).toHaveAttribute('stroke-width', '2');
});
it('should merge class names from LucideProvider and icon props', () => {
const { container } = render(
<LucideProvider class="provider-class">
<House class="icon-class" />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('class', 'lucide provider-class lucide-house icon-class');
});
});

View File

@@ -27,6 +27,20 @@
"module": "dist/esm/lucide-react-native.js",
"typings": "dist/lucide-react-native.d.ts",
"react-native": "dist/esm/lucide-react-native.js",
"exports": {
".": {
"types": "./dist/lucide-react-native.d.ts",
"import": "./dist/esm/lucide-react-native.js",
"browser": "./dist/esm/lucide-react-native.js",
"require": "./dist/cjs/lucide-react-native.js"
},
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/esm/icons/index.js",
"browser": "./dist/esm/icons/index.js",
"require": "./dist/cjs/icons/index.js"
}
},
"sideEffects": false,
"files": [
"dist"
@@ -38,7 +52,6 @@
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --iconFileExtension=.ts --exportFileName=index.ts --withAliases --aliasesFileExtension=.ts",
"build:bundles": "rollup -c ./rollup.config.mjs",
"test": "pnpm build:icons && vitest run",
"test:watch": "vitest watch",
"version": "pnpm version --git-tag-version=false"
},
"devDependencies": {

View File

@@ -5,7 +5,7 @@ import pkg from './package.json' with { type: 'json' };
const packageName = 'LucideReact';
const outputFileName = 'lucide-react-native';
const outputDir = 'dist';
const inputs = ['src/lucide-react-native.ts'];
const inputs = ['src/lucide-react-native.ts', 'src/icons/index.ts'];
const bundles = [
{
format: 'cjs',
@@ -60,6 +60,16 @@ export default [
],
plugins: [dts()],
},
{
input: inputs[1],
output: [
{
file: `dist/icons.d.ts`,
format: 'es',
},
],
plugins: [dts()],
},
{
input: `src/${outputFileName}.suffixed.ts`,
output: [

View File

@@ -2,11 +2,9 @@ import { createElement, forwardRef, type FunctionComponent } from 'react';
import * as NativeSvg from 'react-native-svg';
import defaultAttributes, { childDefaultAttributes } from './defaultAttributes';
import { IconNode, LucideProps } from './types';
import { useLucideContext } from './context';
interface IconComponentProps extends LucideProps {
iconNode: IconNode;
testID?: string;
}
/**
@@ -25,22 +23,22 @@ interface IconComponentProps extends LucideProps {
* @returns {ForwardRefExoticComponent} LucideIcon
*/
const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
({ color, size, strokeWidth, absoluteStrokeWidth, children, iconNode, testID, ...rest }, ref) => {
const {
size: contextSize = 24,
strokeWidth: contextStrokeWidth = 2,
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
color: contextColor = 'currentColor',
} = useLucideContext() ?? {};
const calculatedStrokeWidth =
absoluteStrokeWidth ?? contextAbsoluteStrokeWidth
? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize)
: strokeWidth ?? contextStrokeWidth;
(
{
color = 'currentColor',
size = 24,
strokeWidth = 2,
absoluteStrokeWidth,
children,
iconNode,
className,
...rest
},
ref,
) => {
const customAttrs = {
stroke: color ?? contextColor ?? defaultAttributes.stroke,
strokeWidth: calculatedStrokeWidth,
stroke: color,
strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
...rest,
};
@@ -49,9 +47,9 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
{
ref,
...defaultAttributes,
width: size ?? contextSize ?? defaultAttributes.width,
height: size ?? contextSize ?? defaultAttributes.height,
'data-testid': testID,
className,
width: size,
height: size,
...customAttrs,
},
[

View File

@@ -1,43 +0,0 @@
import { createContext, type ReactNode, useContext, useMemo } from 'react';
const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
});
interface LucideProviderProps {
children: ReactNode;
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
}
export function LucideProvider({
children,
size,
color,
strokeWidth,
absoluteStrokeWidth,
}: LucideProviderProps) {
const value = useMemo(
() => ({
size,
color,
strokeWidth,
absoluteStrokeWidth,
}),
[size, color, strokeWidth, absoluteStrokeWidth],
);
return <LucideContext.Provider value={value}>{children}</LucideContext.Provider>;
}
export const useLucideContext = () => useContext(LucideContext);

View File

@@ -1,24 +1,55 @@
import { forwardRef, createElement } from 'react';
import { IconNode, LucideProps } from './types';
import { toPascalCase } from '@lucide/shared';
import Icon from './Icon';
import { forwardRef, createElement, FunctionComponent } from 'react';
import * as NativeSvg from 'react-native-svg';
import defaultAttributes, { childDefaultAttributes } from './defaultAttributes';
import { IconNode, LucideIcon, LucideProps } from './types';
/**
* Create a Lucide icon component
* @param {string} iconName
* @param {array} iconNode
* @returns {ForwardRefExoticComponent} LucideIcon
*/
const createLucideIcon = (iconName: string, iconNode: IconNode) => {
const Component = forwardRef<SVGSVGElement, LucideProps>((props, ref) =>
createElement(Icon, {
const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
const Component = forwardRef(
(
{
color = 'currentColor',
size = 24,
strokeWidth = 2,
absoluteStrokeWidth,
children,
'data-testid': dataTestId,
...rest
}: LucideProps,
ref,
iconNode,
...props,
}),
) => {
const customAttrs = {
stroke: color,
strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
...rest,
};
return createElement(
NativeSvg.Svg as unknown as string,
{
ref,
...defaultAttributes,
width: size,
height: size,
'data-testid': dataTestId,
...customAttrs,
},
[
...iconNode.map(([tag, attrs]) => {
const upperCasedTag = (tag.charAt(0).toUpperCase() +
tag.slice(1)) as keyof typeof NativeSvg;
// duplicating the attributes here because generating the OTA update bundles don't inherit the SVG properties from parent (codepush, expo-updates)
return createElement(
NativeSvg[upperCasedTag] as FunctionComponent<LucideProps>,
{ ...childDefaultAttributes, ...customAttrs, ...attrs } as LucideProps,
);
}),
...((Array.isArray(children) ? children : [children]) || []),
],
);
},
);
Component.displayName = toPascalCase(iconName);
Component.displayName = `${iconName}`;
return Component;
};

View File

@@ -1,5 +1,4 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases/prefixed';
export * from './types';

View File

@@ -1,5 +1,4 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases/suffixed';
export * from './types';

View File

@@ -1,8 +1,6 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases';
export * from './types';
export * from './context';
export { default as createLucideIcon } from './createLucideIcon';
export { default as Icon } from './Icon';

View File

@@ -1,32 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using LucideProvider > should render the icon with LucideProvider 1`] = `
<svg
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
/>
<path
d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
/>
</svg>
`;

View File

@@ -1,62 +1,60 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
<svg
data-testid="grid-icon"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="4"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect
fill="none"
height="18"
rx="2"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4"
width="18"
x="3"
y="3"
/>
<path
d="M3 9h18"
fill="none"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4"
/>
<path
d="M3 15h18"
fill="none"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4"
/>
<path
d="M9 3v18"
fill="none"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4"
/>
<path
d="M15 3v18"
fill="none"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4"
/>
<rect fill="none"
stroke="red"
stroke-width="4"
stroke-linecap="round"
stroke-linejoin="round"
width="18"
height="18"
x="3"
y="3"
rx="2"
>
</rect>
<path fill="none"
stroke="red"
stroke-width="4"
stroke-linecap="round"
stroke-linejoin="round"
d="M3 9h18"
>
</path>
<path fill="none"
stroke="red"
stroke-width="4"
stroke-linecap="round"
stroke-linejoin="round"
d="M3 15h18"
>
</path>
<path fill="none"
stroke="red"
stroke-width="4"
stroke-linecap="round"
stroke-linejoin="round"
d="M9 3v18"
>
</path>
<path fill="none"
stroke="red"
stroke-width="4"
stroke-linecap="round"
stroke-linejoin="round"
d="M15 3v18"
>
</path>
</svg>
`;

View File

@@ -1,80 +0,0 @@
import { cleanup, render } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { House, LucideProvider } from '../src/lucide-react-native';
vi.mock('react-native-svg');
describe('Using LucideProvider', () => {
it('should render the icon with LucideProvider', () => {
cleanup();
const { container } = render(
<LucideProvider>
<House />
</LucideProvider>,
);
expect(container.firstChild).toMatchSnapshot();
});
it('should render the icon with LucideProvider and custom strokeWidth', () => {
cleanup();
const testId = 'house-icon';
const { getByTestId } = render(
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House testID={testId} />
</LucideProvider>,
);
const IconComponent = getByTestId(testId);
expect(IconComponent).toHaveAttribute('width', '48');
expect(IconComponent).toHaveAttribute('height', '48');
expect(IconComponent).toHaveAttribute('stroke', 'red');
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
cleanup();
const { container } = render(
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it("should override the provider's global props when passing props to the icon", () => {
cleanup();
const { container } = render(
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House
size={32}
color="blue"
strokeWidth={3}
/>
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '32');
expect(IconComponent).toHaveAttribute('height', '32');
expect(IconComponent).toHaveAttribute('stroke', 'blue');
expect(IconComponent).toHaveAttribute('stroke-width', '3');
});
});

View File

@@ -14,53 +14,49 @@ describe('Using lucide icon components', () => {
});
it('should adjust the size, stroke color and stroke width', () => {
const testId = 'grid-icon';
const { getByTestId } = render(
const { container } = render(
<Grid
size={48}
stroke="red"
strokeWidth={4}
testID={testId}
/>,
);
const GridIcon = getByTestId(testId);
const SVGElement = container.firstElementChild;
expect(GridIcon).toHaveAttribute('stroke', 'red');
expect(GridIcon).toHaveAttribute('width', '48');
expect(GridIcon).toHaveAttribute('height', '48');
expect(GridIcon).toHaveAttribute('stroke-width', '4');
expect(SVGElement).toHaveAttribute('stroke', 'red');
expect(SVGElement).toHaveAttribute('width', '48');
expect(SVGElement).toHaveAttribute('height', '48');
expect(SVGElement).toHaveAttribute('stroke-width', '4');
expect(GridIcon).toMatchSnapshot();
expect(container.innerHTML).toMatchSnapshot();
});
it('should render the alias icon', () => {
const penTestId = 'pen-icon';
const { getByTestId: getByTestId1 } = render(
const testId = 'pen-icon';
const { container } = render(
<Pen
testID={penTestId}
data-testid={testId}
size={48}
stroke="red"
strokeWidth={4}
/>,
);
const penIcon = getByTestId1(penTestId);
const PenIconRenderedHTML = container.innerHTML;
cleanup();
const { getByTestId: getByTestId2 } = render(
const { container: Edit2Container } = render(
<Edit2
testID={penTestId}
data-testid={testId}
size={48}
stroke="red"
strokeWidth={4}
/>,
);
const edit2Icon = getByTestId2(penTestId);
expect(penIcon).toStrictEqual(edit2Icon);
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
});
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
@@ -87,8 +83,8 @@ describe('Using lucide icon components', () => {
const childId = 'child';
const { container, getByTestId } = render(
<Grid testID={testId}>
<Grid testID={childId} />
<Grid data-testid={testId}>
<Grid data-testid={childId} />
</Grid>,
);
const { children } = container.firstElementChild ?? {};
@@ -104,9 +100,9 @@ describe('Using lucide icon components', () => {
const childId2 = 'child2';
const { container, getByTestId } = render(
<Grid testID={testId}>
<Grid testID={childId1} />
<Grid testID={childId2} />
<Grid data-testid={testId}>
<Grid data-testid={childId1} />
<Grid data-testid={childId2} />
</Grid>,
);
const { children } = getByTestId(testId) as unknown as { children: HTMLCollection };
@@ -127,7 +123,7 @@ describe('Using lucide icon components', () => {
const { container, getByTestId } = render(
<Grid
testID={testId}
data-testid={testId}
fill={fill}
color={color}
strokeWidth={strokeWidth}

View File

@@ -55,7 +55,7 @@
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^14.1.2",
"@types/react": "^18.2.37",
"@vitejs/plugin-react": "^4.6.0",
"@vitejs/plugin-react": "^4.4.1",
"jest-serializer-html": "^7.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",

View File

@@ -2,7 +2,6 @@ import { createElement, forwardRef } from 'react';
import defaultAttributes from './defaultAttributes';
import { IconNode, LucideProps } from './types';
import { mergeClasses, hasA11yProp } from '@lucide/shared';
import { useLucideContext } from './context';
interface IconComponentProps extends LucideProps {
iconNode: IconNode;
@@ -25,32 +24,28 @@ interface IconComponentProps extends LucideProps {
*/
const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
(
{ color, size, strokeWidth, absoluteStrokeWidth, className = '', children, iconNode, ...rest },
{
color = 'currentColor',
size = 24,
strokeWidth = 2,
absoluteStrokeWidth,
className = '',
children,
iconNode,
...rest
},
ref,
) => {
const {
size: contextSize = 24,
strokeWidth: contextStrokeWidth = 2,
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
color: contextColor = 'currentColor',
className: contextClass = '',
} = useLucideContext() ?? {};
const calculatedStrokeWidth =
absoluteStrokeWidth ?? contextAbsoluteStrokeWidth
? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize)
: strokeWidth ?? contextStrokeWidth;
return createElement(
) =>
createElement(
'svg',
{
ref,
...defaultAttributes,
width: size ?? contextSize ?? defaultAttributes.width,
height: size ?? contextSize ?? defaultAttributes.height,
stroke: color ?? contextColor,
strokeWidth: calculatedStrokeWidth,
className: mergeClasses('lucide', contextClass, className),
width: size,
height: size,
stroke: color,
strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
className: mergeClasses('lucide', className),
...(!children && !hasA11yProp(rest) && { 'aria-hidden': 'true' }),
...rest,
},
@@ -58,8 +53,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
...(Array.isArray(children) ? children : [children]),
],
);
},
),
);
export default Icon;

View File

@@ -1,40 +0,0 @@
import { createContext, type ReactNode, useContext, useMemo } from 'react';
import { LucideProps } from './types';
type LucideConfig = {
size: number;
color: string;
strokeWidth: number;
absoluteStrokeWidth: boolean;
className: string;
};
const LucideContext = createContext<LucideProps>({});
type LucideProviderProps = {
children: ReactNode;
} & Partial<LucideConfig>;
export function LucideProvider({
children,
size,
color,
strokeWidth,
absoluteStrokeWidth,
className,
}: LucideProviderProps) {
const value = useMemo(
() => ({
size,
color,
strokeWidth,
absoluteStrokeWidth,
className,
}),
[size, color, strokeWidth, absoluteStrokeWidth, className],
);
return <LucideContext.Provider value={value}>{children}</LucideContext.Provider>;
}
export const useLucideContext = () => useContext(LucideContext);

View File

@@ -2,7 +2,6 @@ export * from './icons';
export * as icons from './icons';
export * from './aliases';
export * from './types';
export * from './context';
export { default as createLucideIcon } from './createLucideIcon';
export { default as Icon } from './Icon';

View File

@@ -1,24 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using LucideProvider > should render the icon with LucideProvider 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-house"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
/>
<path
d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
/>
</svg>
`;

View File

@@ -1,99 +0,0 @@
import { render } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { House, LucideProvider } from '../src/lucide-react';
describe('Using LucideProvider', () => {
it('should render the icon with LucideProvider', () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
>
<House />
</LucideProvider>,
);
expect(container.firstChild).toMatchSnapshot();
});
it('should render the icon with default props when no provider is used', () => {
const { container } = render(<House />);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '24');
expect(IconComponent).toHaveAttribute('height', '24');
expect(IconComponent).toHaveAttribute('stroke', 'currentColor');
expect(IconComponent).toHaveAttribute('stroke-width', '2');
});
it('should render the icon with LucideProvider and custom strokeWidth', () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '48');
expect(IconComponent).toHaveAttribute('height', '48');
expect(IconComponent).toHaveAttribute('stroke', 'red');
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it("should override the provider's global props when passing props to the icon", () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House
size={24}
color="blue"
strokeWidth={2}
/>
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '24');
expect(IconComponent).toHaveAttribute('height', '24');
expect(IconComponent).toHaveAttribute('stroke', 'blue');
expect(IconComponent).toHaveAttribute('stroke-width', '2');
});
it('should merge className from provider and icon', () => {
const { container } = render(
<LucideProvider className="provider-class">
<House className="icon-class" />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('class', 'lucide provider-class lucide-house icon-class');
});
});

View File

@@ -62,10 +62,7 @@
"build:version": "node ./scripts/replaceVersion.mjs",
"build:bundle": "rollup -c rollup.config.mjs",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --separateAliasesFile --separateAliasesFileIgnore=fingerprint --aliasesFileExtension=.ts --iconFileExtension=.tsx --exportFileName=index.ts",
"typecheck": "tsc",
"typecheck:watch": "tsc -w",
"test": "pnpm build:icons && vitest run",
"test:watch": "vitest watch",
"version": "pnpm version --git-tag-version=false"
},
"devDependencies": {

View File

@@ -1,9 +1,8 @@
import { For, splitProps, useContext } from 'solid-js';
import { For, splitProps } from 'solid-js';
import { Dynamic } from 'solid-js/web';
import defaultAttributes from './defaultAttributes';
import { IconNode, LucideProps } from './types';
import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
import { LucideContext } from './context';
interface IconProps {
name?: string;
@@ -22,40 +21,28 @@ const Icon = (props: LucideProps & IconProps) => {
'absoluteStrokeWidth',
]);
const globalProps = useContext(LucideContext);
return (
<svg
{...defaultAttributes}
width={localProps.size ?? globalProps.size ?? defaultAttributes.width}
height={localProps.size ?? globalProps.size ?? defaultAttributes.height}
stroke={localProps.color ?? globalProps.color ?? defaultAttributes.stroke}
width={localProps.size ?? defaultAttributes.width}
height={localProps.size ?? defaultAttributes.height}
stroke={localProps.color ?? defaultAttributes.stroke}
stroke-width={
(localProps.absoluteStrokeWidth ?? globalProps.absoluteStrokeWidth) === true
? (Number(
localProps.strokeWidth ??
globalProps.strokeWidth ??
defaultAttributes['stroke-width'],
) *
24) /
Number(localProps.size ?? globalProps.size)
: Number(
localProps.strokeWidth ??
globalProps.strokeWidth ??
defaultAttributes['stroke-width'],
)
localProps.absoluteStrokeWidth
? (Number(localProps.strokeWidth ?? defaultAttributes['stroke-width']) * 24) /
Number(localProps.size)
: Number(localProps.strokeWidth ?? defaultAttributes['stroke-width'])
}
class={mergeClasses(
'lucide',
'lucide-icon',
globalProps.class,
...(localProps.name != null
? [
`lucide-${toKebabCase(toPascalCase(localProps.name))}`,
`lucide-${toKebabCase(localProps.name)}`,
]
: []),
localProps.class,
localProps.class != null ? localProps.class : '',
)}
{...rest}
>

View File

@@ -1,36 +0,0 @@
import { createContext, splitProps, type JSXElement } from 'solid-js';
export const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
class?: string;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
class: '',
});
interface LucideProviderProps {
children: JSXElement;
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
class?: string;
}
export function LucideProvider(props: LucideProviderProps) {
const [value, rest] = splitProps(props, [
'size',
'color',
'strokeWidth',
'absoluteStrokeWidth',
'class',
]);
return <LucideContext.Provider value={value}>{rest.children}</LucideContext.Provider>;
}

View File

@@ -2,6 +2,5 @@ export * from './icons';
export * as icons from './icons';
export * from './aliases';
export * from './types';
export * from './context';
export { default as Icon } from './Icon';

View File

@@ -1,25 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using LucideProvider > should render the icon with LucideProvider 1`] = `
<svg
class="lucide lucide-icon lucide-house"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
key="5wwlr5"
/>
<path
d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
key="r6nss1"
/>
</svg>
`;

View File

@@ -1,102 +0,0 @@
import { render } from '@solidjs/testing-library';
import { describe, expect, it } from 'vitest';
import { House, LucideProvider } from '../src/lucide-solid';
describe('Using LucideProvider', () => {
it('should render the icon with LucideProvider', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
>
<House />
</LucideProvider>
));
expect(container.firstChild).toMatchSnapshot();
});
it('should render the icon with default props when no provider is used', () => {
const { container } = render(() => <House />);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '24');
expect(IconComponent).toHaveAttribute('height', '24');
expect(IconComponent).toHaveAttribute('stroke', 'currentColor');
expect(IconComponent).toHaveAttribute('stroke-width', '2');
});
it('should render the icon with LucideProvider and custom strokeWidth', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '48');
expect(IconComponent).toHaveAttribute('height', '48');
expect(IconComponent).toHaveAttribute('stroke', 'red');
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
absoluteStrokeWidth
>
<House />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it("should override the provider's global props when passing props to the icon", () => {
const { container } = render(() => (
<LucideProvider
size={48}
color="red"
strokeWidth={4}
>
<House
size={24}
color="blue"
strokeWidth={2}
/>
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '24');
expect(IconComponent).toHaveAttribute('height', '24');
expect(IconComponent).toHaveAttribute('stroke', 'blue');
expect(IconComponent).toHaveAttribute('stroke-width', '2');
});
it('should merge className from provider and icon', () => {
const { container } = render(() => (
<LucideProvider class="provider-class">
<House class="icon-class" />
</LucideProvider>
));
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute(
'class',
'lucide lucide-icon provider-class lucide-house icon-class',
);
});
});

View File

@@ -1,7 +1,6 @@
import base64SVG from '@lucide/build-icons/utils/base64SVG';
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
export default defineExportTemplate(async({
export default async ({
componentName,
iconName,
children,
@@ -14,9 +13,6 @@ export default defineExportTemplate(async({
return `
import createLucideIcon from '../createLucideIcon';
import { IconNode } from '../types';
export const __iconNode: IconNode = ${JSON.stringify(children)}
/**
* @component @name ${componentName}
@@ -29,8 +25,8 @@ export const __iconNode: IconNode = ${JSON.stringify(children)}
* @returns {FunctionalComponent} Vue component
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName} = createLucideIcon('${iconName}', __iconNode);
const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)});
export default ${componentName};
`;
});
};

View File

@@ -1,24 +1,17 @@
<script lang="ts">
import defaultAttributes from './defaultAttributes.js';
import type { IconProps } from './types.js';
import { getLucideContext } from './context.js';
const globalProps = getLucideContext() ?? {};
const {
name,
color = globalProps.color ?? 'currentColor',
size = globalProps.size ?? 24,
strokeWidth = globalProps.strokeWidth ?? 2,
absoluteStrokeWidth = globalProps.absoluteStrokeWidth ?? false,
color = 'currentColor',
size = 24,
strokeWidth = 2,
absoluteStrokeWidth = false,
iconNode = [],
children,
...props
}: IconProps = $props();
const calculatedStrokeWidth = $derived(
absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
);
</script>
<svg
@@ -27,8 +20,8 @@
width={size}
height={size}
stroke={color}
stroke-width={calculatedStrokeWidth}
class={['lucide-icon lucide', globalProps.class, name && `lucide-${name}`, props.class]}
stroke-width={absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth}
class={['lucide-icon lucide', name && `lucide-${name}`, props.class]}
>
{#each iconNode as [tag, attrs]}
<svelte:element

View File

@@ -1,16 +0,0 @@
import { getContext, setContext } from 'svelte';
const LucideContext = Symbol('lucide-context');
export interface LucideGlobalContext {
color?: string;
size?: number;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
class?: string;
}
export const setLucideProps = (globalProps: LucideGlobalContext) =>
setContext(LucideContext, globalProps);
export const getLucideContext = () => getContext<LucideGlobalContext>(LucideContext);

View File

@@ -4,4 +4,3 @@ export * from './aliases/index.js';
export { default as defaultAttributes } from './defaultAttributes.js';
export * from './types.js';
export { default as Icon } from './Icon.svelte';
export * from './context.js';

View File

@@ -1,15 +0,0 @@
<script lang="ts">
import Smile from '../src/icons/smile.svelte';
import { setLucideProps } from '../src/lucide-svelte.js';
setLucideProps({
size: 32,
color: 'red',
strokeWidth: 1,
class: 'provider-class',
});
</script>
<Smile aria-label="smile">
<text>Test</text>
</Smile>

View File

@@ -126,6 +126,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
data-testid="smile-icon"
class="lucide-icon lucide lucide-smile"
>
<circle cx="12"

View File

@@ -2,7 +2,6 @@ import { describe, it, expect, afterEach } from 'vitest';
import { render, cleanup } from '@testing-library/svelte';
import { Smile, Pen, Edit2 } from '../src/lucide-svelte.js';
import TestSlots from './TestSlots.svelte';
import ContextWrapper from './ContextWrapper.svelte';
describe('Using lucide icon components', () => {
afterEach(() => cleanup());
@@ -13,9 +12,11 @@ describe('Using lucide icon components', () => {
it('should adjust the size, stroke color and stroke width', () => {
const { container } = render(Smile, {
size: 48,
color: 'red',
strokeWidth: 4,
props: {
size: 48,
color: 'red',
strokeWidth: 4,
},
});
expect(container).toMatchSnapshot();
@@ -23,27 +24,30 @@ describe('Using lucide icon components', () => {
it('should add a class to the element', () => {
const testClass = 'my-icon';
const { container } = render(Smile, {
class: testClass,
render(Smile, {
props: {
class: testClass,
},
});
const IconComponent = container.firstElementChild;
const [icon] = document.getElementsByClassName(testClass);
expect(IconComponent).toBeInTheDocument();
expect(IconComponent).toMatchSnapshot();
expect(IconComponent).toHaveClass(testClass);
expect(IconComponent).toHaveClass('lucide');
expect(IconComponent).toHaveClass('lucide-smile');
expect(icon).toBeInTheDocument();
expect(icon).toMatchSnapshot();
expect(icon).toHaveClass(testClass);
expect(icon).toHaveClass('lucide');
expect(icon).toHaveClass('lucide-smile');
});
it('should add a style attribute to the element', () => {
const { container } = render(Smile, {
style: 'position: absolute;',
render(Smile, {
props: {
style: 'position: absolute;',
},
});
const [icon] = document.getElementsByClassName('lucide');
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('style', 'position: absolute;');
expect(icon.getAttribute('style')).toContain('position: absolute');
});
it('should render an icon slot', () => {
@@ -67,30 +71,22 @@ describe('Using lucide icon components', () => {
});
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
const { container } = render(Smile, {
const testId = 'smile-icon';
const { container, getByTestId } = render(Smile, {
'data-testid': testId,
color: 'red',
size: 48,
absoluteStrokeWidth: true,
});
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '48');
expect(IconComponent).toHaveAttribute('height', '48');
expect(IconComponent).toHaveAttribute('stroke', 'red');
expect(IconComponent).toHaveAttribute('stroke-width', '1');
const { attributes } = getByTestId(testId) as unknown as {
attributes: Record<string, { value: string }>;
};
expect(attributes.stroke.value).toBe('red');
expect(attributes.width.value).toBe('48');
expect(attributes.height.value).toBe('48');
expect(attributes['stroke-width'].value).toBe('1');
expect(container.innerHTML).toMatchSnapshot();
});
it('should use context values from the global set properties', () => {
const { container } = render(ContextWrapper);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '32');
expect(IconComponent).toHaveAttribute('height', '32');
expect(IconComponent).toHaveAttribute('stroke', 'red');
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
});

View File

@@ -23,10 +23,10 @@ export const __iconNode: IconNode = ${JSON.stringify(children)}
* @description Lucide SVG icon component, renders SVG Element with children.
*
* @preview ![img](data:image/svg+xml;base64,${svgBase64}) - https://lucide.dev/icons/${iconName}
* @see https://lucide.dev/guide/packages/lucide-vue-next - Documentation
* @see https://lucide.dev/guide/packages/lucide-react - Documentation
*
* @param {Object} props - Lucide icons props and any valid SVG attribute
* @returns {FunctionalComponent} Vue component
* @returns {JSX.Element} JSX Element
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName} = createLucideIcon('${iconName}', __iconNode);

View File

@@ -1,8 +1,7 @@
import { computed, type FunctionalComponent, h } from 'vue';
import { isEmptyString, mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
import { type FunctionalComponent, h } from 'vue';
import { mergeClasses, toKebabCase, toPascalCase, isEmptyString } from '@lucide/shared';
import defaultAttributes from './defaultAttributes';
import { IconNode, LucideProps } from './types';
import { useLucideProps } from './context';
interface IconProps {
iconNode: IconNode;
@@ -17,55 +16,32 @@ const Icon: FunctionalComponent<LucideProps & IconProps> = (
'absolute-stroke-width': absoluteStrokeWidthKebabCase,
strokeWidth,
'stroke-width': strokeWidthKebabCase,
size,
color,
size = defaultAttributes.width,
color = defaultAttributes.stroke,
...props
},
{ slots },
) => {
const {
size: contextSize,
color: contextColor,
strokeWidth: contextStrokeWidth = 2,
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
class: contextClass = '',
} = useLucideProps();
const calculatedStrokeWidth = computed(() => {
const isAbsoluteStrokeWidth =
isEmptyString(absoluteStrokeWidth) ||
isEmptyString(absoluteStrokeWidthKebabCase) ||
absoluteStrokeWidth === true ||
absoluteStrokeWidthKebabCase === true ||
contextAbsoluteStrokeWidth === true;
const strokeWidthValue =
strokeWidth ||
strokeWidthKebabCase ||
contextStrokeWidth ||
defaultAttributes['stroke-width'];
if (isAbsoluteStrokeWidth) {
return (
(Number(strokeWidthValue) * 24) / Number(size ?? contextSize ?? defaultAttributes.width)
);
}
return strokeWidthValue;
});
return h(
'svg',
{
...defaultAttributes,
...props,
width: size ?? contextSize ?? defaultAttributes.width,
height: size ?? contextSize ?? defaultAttributes.height,
stroke: color ?? contextColor ?? defaultAttributes.stroke,
'stroke-width': calculatedStrokeWidth.value,
width: size,
height: size,
stroke: color,
'stroke-width':
isEmptyString(absoluteStrokeWidth) ||
isEmptyString(absoluteStrokeWidthKebabCase) ||
absoluteStrokeWidth === true ||
absoluteStrokeWidthKebabCase === true
? (Number(strokeWidth || strokeWidthKebabCase || defaultAttributes['stroke-width']) *
24) /
Number(size)
: strokeWidth || strokeWidthKebabCase || defaultAttributes['stroke-width'],
class: mergeClasses(
'lucide',
contextClass,
props.class,
...(name
? [`lucide-${toKebabCase(toPascalCase(name))}-icon`, `lucide-${toKebabCase(name)}`]
: ['lucide-icon']),

View File

@@ -1,19 +0,0 @@
import { provide, inject } from 'vue';
export const LUCIDE_CONTEXT = Symbol('lucide-icons');
interface LucideIconsContext {
size?: number;
color?: string;
strokeWidth?: number;
absoluteStrokeWidth?: boolean;
class?: string;
}
export function setLucideProps(props: LucideIconsContext) {
return provide(LUCIDE_CONTEXT, props);
}
export function useLucideProps() {
return inject<LucideIconsContext>(LUCIDE_CONTEXT, {});
}

View File

@@ -1,7 +1,10 @@
import { h } from 'vue';
import { IconNode, LucideIcon } from './types';
import type { FunctionalComponent } from 'vue';
import { IconNode, LucideProps } from './types';
import Icon from './Icon';
// Create interface extending SVGAttributes
/**
* Create a Lucide icon component
* @param {string} iconName
@@ -9,7 +12,7 @@ import Icon from './Icon';
* @returns {FunctionalComponent} LucideIcon
*/
const createLucideIcon =
(iconName: string, iconNode: IconNode): LucideIcon =>
(iconName: string, iconNode: IconNode): FunctionalComponent<LucideProps> =>
(props, { slots, attrs }) =>
h(
Icon,

View File

@@ -4,8 +4,10 @@ export interface LucideProps extends Partial<SVGAttributes> {
size?: 24 | number;
strokeWidth?: number | string;
absoluteStrokeWidth?: boolean;
'absolute-stroke-width'?: boolean;
}
export type IconNode = [elementName: string, attrs: Record<string, string>][];
export type LucideIcon = FunctionalComponent<LucideProps>;
// Legacy exports
export type SVGProps = LucideProps;

View File

@@ -1,21 +0,0 @@
<script setup lang="ts">
import { setLucideProps } from '../src/context';
import { House } from '../src/lucide-vue';
const props = defineProps<{
size?: number;
color?: string;
strokeWidth?: number;
}>();
setLucideProps({
size: 48,
color: 'red',
strokeWidth: 4,
class: 'provider-class',
});
</script>
<template>
<House v-bind="props" />
</template>

View File

@@ -1,23 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using lucide icon context > should render the icon with LucideProvider 1`] = `
<svg
class="lucide provider-class lucide-house-icon lucide-house"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8"
/>
<path
d="M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
/>
</svg>
`;

View File

@@ -3,7 +3,7 @@
exports[`Using lucide icon components > should add a class to the element 1`] = `
<div>
<svg
class="lucide lucide-smile-icon lucide-smile my-icon"
class="lucide my-icon lucide-smile-icon lucide-smile my-icon"
fill="none"
height="24"
stroke="currentColor"

View File

@@ -1,65 +0,0 @@
import { render } from '@testing-library/vue';
import { describe, expect, it } from 'vitest';
// @ts-ignore
import ContextWrapper from './ContextWrapper.vue';
describe('Using lucide icon context', () => {
it('should render the icon with LucideProvider', () => {
const { container } = render(ContextWrapper);
expect(container.firstChild).toMatchSnapshot();
});
it('should render the icon with LucideProvider and custom strokeWidth', () => {
const { container } = render(ContextWrapper);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '48');
expect(IconComponent).toHaveAttribute('height', '48');
expect(IconComponent).toHaveAttribute('stroke', 'red');
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it('should render the icon with LucideProvider and custom absoluteStrokeWidth', () => {
const { container } = render(ContextWrapper);
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '48');
expect(IconComponent).toHaveAttribute('height', '48');
expect(IconComponent).toHaveAttribute('stroke', 'red');
expect(IconComponent).toHaveAttribute('stroke-width', '4');
});
it("should override the provider's global props when passing props to the icon", () => {
const { container } = render(ContextWrapper, {
props: {
strokeWidth: 1,
color: 'blue',
size: 24,
},
});
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('width', '24');
expect(IconComponent).toHaveAttribute('height', '24');
expect(IconComponent).toHaveAttribute('stroke', 'blue');
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it('should merge className from provider and icon', () => {
const { container } = render(ContextWrapper, {
props: {
class: 'icon-class',
},
});
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute(
'class',
'lucide provider-class lucide-house-icon lucide-house icon-class',
);
});
});

View File

@@ -85,7 +85,7 @@ describe('Using lucide icon components', () => {
const icon = container.firstElementChild;
await fireEvent.click(icon as Element);
await fireEvent.click(icon);
expect(onClick).toHaveBeenCalled();
});
@@ -153,7 +153,7 @@ describe('Using lucide icon components', () => {
props: {
size: 48,
color: 'red',
absoluteStrokeWidth: '' as unknown as boolean, // Vue treats empty string as true for boolean props
absoluteStrokeWidth: '',
},
});
@@ -170,7 +170,7 @@ describe('Using lucide icon components', () => {
size: 48,
color: 'red',
'stroke-width': '2',
'absolute-stroke-width': '' as unknown as boolean, // Vue treats empty string as true for boolean props
'absolute-stroke-width': '',
},
});

View File

@@ -13,6 +13,6 @@
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true,
"types": ["@testing-library/jest-dom", "vitest/globals"],
"types": ["@testing-library/jest-dom"],
},
}

192
pnpm-lock.yaml generated
View File

@@ -409,7 +409,7 @@ importers:
specifier: ^7.1.0
version: 7.1.0
preact:
specifier: ^10.26.9
specifier: ^10.19.2
version: 10.27.2
rollup:
specifier: ^4.53.3
@@ -448,7 +448,7 @@ importers:
specifier: ^18.2.37
version: 18.3.27
'@vitejs/plugin-react':
specifier: ^4.6.0
specifier: ^4.4.1
version: 4.7.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0))
jest-serializer-html:
specifier: ^7.1.0
@@ -8827,9 +8827,6 @@ packages:
resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
engines: {node: '>= 0.4'}
is-bigint@1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
is-bigint@1.1.0:
resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
engines: {node: '>= 0.4'}
@@ -8838,10 +8835,6 @@ packages:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
is-boolean-object@1.1.2:
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
engines: {node: '>= 0.4'}
is-boolean-object@1.2.2:
resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
engines: {node: '>= 0.4'}
@@ -8868,10 +8861,6 @@ packages:
resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
engines: {node: '>= 0.4'}
is-date-object@1.0.5:
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
engines: {node: '>= 0.4'}
is-date-object@1.1.0:
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
engines: {node: '>= 0.4'}
@@ -8929,9 +8918,6 @@ packages:
is-lambda@1.0.1:
resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
is-map@2.0.2:
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
is-map@2.0.3:
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
engines: {node: '>= 0.4'}
@@ -8943,10 +8929,6 @@ packages:
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
engines: {node: '>= 0.4'}
is-number-object@1.0.7:
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
engines: {node: '>= 0.4'}
is-number-object@1.1.1:
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
engines: {node: '>= 0.4'}
@@ -9032,9 +9014,6 @@ packages:
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
engines: {node: '>=10'}
is-weakmap@2.0.1:
resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
is-weakmap@2.0.2:
resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
engines: {node: '>= 0.4'}
@@ -9043,9 +9022,6 @@ packages:
resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
engines: {node: '>= 0.4'}
is-weakset@2.0.2:
resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
is-weakset@2.0.4:
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
engines: {node: '>= 0.4'}
@@ -11734,10 +11710,6 @@ packages:
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
engines: {node: '>= 0.4'}
side-channel@1.0.6:
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
engines: {node: '>= 0.4'}
side-channel@1.1.0:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
@@ -12911,46 +12883,6 @@ packages:
terser:
optional: true
vite@6.3.6:
resolution: {integrity: sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
jiti: '>=1.21.0'
less: '*'
lightningcss: ^1.21.0
sass: '*'
sass-embedded: '*'
stylus: '*'
sugarss: '*'
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
peerDependenciesMeta:
'@types/node':
optional: true
jiti:
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
vite@6.4.1:
resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -13258,9 +13190,6 @@ packages:
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'}
@@ -13269,9 +13198,6 @@ packages:
resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
engines: {node: '>= 0.4'}
which-collection@1.0.1:
resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
which-collection@1.0.2:
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
engines: {node: '>= 0.4'}
@@ -15778,7 +15704,7 @@ snapshots:
'@babel/parser': 7.28.5
'@babel/template': 7.27.2
'@babel/types': 7.28.5
debug: 4.3.5
debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -18110,7 +18036,7 @@ snapshots:
'@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.5.3(svelte@4.2.20)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(terser@5.44.1)(yaml@2.8.0)))(svelte@4.2.20)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(terser@5.44.1)(yaml@2.8.0))':
dependencies:
'@sveltejs/vite-plugin-svelte': 2.5.3(svelte@4.2.20)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(terser@5.44.1)(yaml@2.8.0))
debug: 4.4.0
debug: 4.4.3
svelte: 4.2.20
vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)
transitivePeerDependencies:
@@ -18119,7 +18045,7 @@ snapshots:
'@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.43.14)(vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)))(svelte@5.43.14)(vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0))':
dependencies:
'@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.43.14)(vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0))
debug: 4.4.1
debug: 4.4.3
svelte: 5.43.14
vite: 6.4.1(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)
transitivePeerDependencies:
@@ -18261,7 +18187,7 @@ snapshots:
'@testing-library/dom@8.20.1':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/runtime': 7.26.0
'@babel/runtime': 7.27.1
'@types/aria-query': 5.0.4
aria-query: 5.1.3
chalk: 4.1.2
@@ -18272,7 +18198,7 @@ snapshots:
'@testing-library/dom@9.3.4':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/runtime': 7.26.0
'@babel/runtime': 7.27.1
'@types/aria-query': 5.0.4
aria-query: 5.1.3
chalk: 4.1.2
@@ -18728,7 +18654,7 @@ snapshots:
dependencies:
'@typescript-eslint/typescript-estree': 6.21.0(typescript@5.9.3)
'@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.9.3)
debug: 4.4.0
debug: 4.4.3
eslint: 8.57.1
ts-api-utils: 1.4.3(typescript@5.9.3)
optionalDependencies:
@@ -18774,7 +18700,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 6.21.0
'@typescript-eslint/visitor-keys': 6.21.0
debug: 4.4.0
debug: 4.4.3
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.3
@@ -18937,13 +18863,13 @@ snapshots:
chai: 6.2.1
tinyrainbow: 3.0.3
'@vitest/mocker@4.0.12(vite@6.3.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(terser@5.44.1)(yaml@2.8.0))':
'@vitest/mocker@4.0.12(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(terser@5.44.1)(yaml@2.8.0))':
dependencies:
'@vitest/spy': 4.0.12
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 6.3.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)
vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)
'@vitest/pretty-format@4.0.12':
dependencies:
@@ -20022,7 +19948,7 @@ snapshots:
'@npmcli/move-file': 1.1.2
chownr: 2.0.0
fs-minipass: 2.1.0
glob: 7.2.3
glob: 7.2.0
infer-owner: 1.0.4
lru-cache: 6.0.0
minipass: 3.3.6
@@ -20694,7 +20620,7 @@ snapshots:
deep-equal@1.1.2:
dependencies:
is-arguments: 1.1.1
is-date-object: 1.0.5
is-date-object: 1.1.0
is-regex: 1.2.1
object-is: 1.1.6
object-keys: 1.1.1
@@ -20708,7 +20634,7 @@ snapshots:
get-intrinsic: 1.3.0
is-arguments: 1.1.1
is-array-buffer: 3.0.5
is-date-object: 1.0.5
is-date-object: 1.1.0
is-regex: 1.2.1
is-shared-array-buffer: 1.0.4
isarray: 2.0.5
@@ -20716,9 +20642,9 @@ snapshots:
object-keys: 1.1.1
object.assign: 4.1.7
regexp.prototype.flags: 1.5.4
side-channel: 1.0.6
which-boxed-primitive: 1.0.2
which-collection: 1.0.1
side-channel: 1.1.0
which-boxed-primitive: 1.1.1
which-collection: 1.0.2
which-typed-array: 1.1.19
deep-is@0.1.4: {}
@@ -21163,7 +21089,7 @@ snapshots:
get-intrinsic: 1.3.0
has-symbols: 1.1.0
is-arguments: 1.1.1
is-map: 2.0.2
is-map: 2.0.3
is-set: 2.0.3
is-string: 1.1.1
isarray: 2.0.5
@@ -22541,7 +22467,7 @@ snapshots:
dependencies:
'@tootallnate/once': 1.1.2
agent-base: 6.0.2
debug: 4.3.3
debug: 4.4.3
transitivePeerDependencies:
- supports-color
@@ -22773,10 +22699,6 @@ snapshots:
has-tostringtag: 1.0.2
safe-regex-test: 1.1.0
is-bigint@1.0.4:
dependencies:
has-bigints: 1.1.0
is-bigint@1.1.0:
dependencies:
has-bigints: 1.1.0
@@ -22785,11 +22707,6 @@ snapshots:
dependencies:
binary-extensions: 2.3.0
is-boolean-object@1.1.2:
dependencies:
call-bind: 1.0.8
has-tostringtag: 1.0.2
is-boolean-object@1.2.2:
dependencies:
call-bound: 1.0.4
@@ -22817,10 +22734,6 @@ snapshots:
get-intrinsic: 1.3.0
is-typed-array: 1.1.15
is-date-object@1.0.5:
dependencies:
has-tostringtag: 1.0.2
is-date-object@1.1.0:
dependencies:
call-bound: 1.0.4
@@ -22863,18 +22776,12 @@ snapshots:
is-lambda@1.0.1: {}
is-map@2.0.2: {}
is-map@2.0.3: {}
is-module@1.0.0: {}
is-negative-zero@2.0.3: {}
is-number-object@1.0.7:
dependencies:
has-tostringtag: 1.0.2
is-number-object@1.1.1:
dependencies:
call-bound: 1.0.4
@@ -22944,19 +22851,12 @@ snapshots:
is-unicode-supported@0.1.0: {}
is-weakmap@2.0.1: {}
is-weakmap@2.0.2: {}
is-weakref@1.1.1:
dependencies:
call-bound: 1.0.4
is-weakset@2.0.2:
dependencies:
call-bind: 1.0.8
get-intrinsic: 1.3.0
is-weakset@2.0.4:
dependencies:
call-bound: 1.0.4
@@ -23958,7 +23858,7 @@ snapshots:
metro-runtime@0.81.0:
dependencies:
'@babel/runtime': 7.26.0
'@babel/runtime': 7.27.1
flow-enums-runtime: 0.0.6
metro-runtime@0.81.5:
@@ -25885,7 +25785,7 @@ snapshots:
qs@6.13.0:
dependencies:
side-channel: 1.0.6
side-channel: 1.1.0
quansync@0.2.11: {}
@@ -26796,13 +26696,6 @@ snapshots:
object-inspect: 1.13.4
side-channel-map: 1.0.1
side-channel@1.0.6:
dependencies:
call-bind: 1.0.8
es-errors: 1.3.0
get-intrinsic: 1.3.0
object-inspect: 1.13.4
side-channel@1.1.0:
dependencies:
es-errors: 1.3.0
@@ -26894,7 +26787,7 @@ snapshots:
socks-proxy-agent@6.2.1:
dependencies:
agent-base: 6.0.2
debug: 4.3.3
debug: 4.4.3
socks: 2.8.3
transitivePeerDependencies:
- supports-color
@@ -26902,7 +26795,7 @@ snapshots:
socks-proxy-agent@7.0.0:
dependencies:
agent-base: 6.0.2
debug: 4.3.3
debug: 4.4.3
socks: 2.8.3
transitivePeerDependencies:
- supports-color
@@ -27202,7 +27095,7 @@ snapshots:
dependencies:
css: 3.0.0
debug: 4.4.3
glob: 7.2.3
glob: 7.2.0
safer-buffer: 2.1.2
sax: 1.2.4
source-map: 0.7.4
@@ -28117,24 +28010,6 @@ snapshots:
stylus: 0.56.0
terser: 5.44.1
vite@6.3.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0):
dependencies:
esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.53.3
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 24.10.1
fsevents: 2.3.3
jiti: 2.6.1
less: 4.2.0
sass: 1.77.8
stylus: 0.56.0
terser: 5.44.1
yaml: 2.8.0
vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0):
dependencies:
esbuild: 0.25.10
@@ -28235,7 +28110,7 @@ snapshots:
vitest@4.0.12(@types/debug@4.1.12)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@20.0.3)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0):
dependencies:
'@vitest/expect': 4.0.12
'@vitest/mocker': 4.0.12(vite@6.3.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(terser@5.44.1)(yaml@2.8.0))
'@vitest/mocker': 4.0.12(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(terser@5.44.1)(yaml@2.8.0))
'@vitest/pretty-format': 4.0.12
'@vitest/runner': 4.0.12
'@vitest/snapshot': 4.0.12
@@ -28252,7 +28127,7 @@ snapshots:
tinyexec: 0.3.2
tinyglobby: 0.2.15
tinyrainbow: 3.0.3
vite: 6.3.6(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)
vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/debug': 4.1.12
@@ -28454,14 +28329,6 @@ snapshots:
tr46: 0.0.3
webidl-conversions: 3.0.1
which-boxed-primitive@1.0.2:
dependencies:
is-bigint: 1.0.4
is-boolean-object: 1.1.2
is-number-object: 1.0.7
is-string: 1.1.1
is-symbol: 1.1.1
which-boxed-primitive@1.1.1:
dependencies:
is-bigint: 1.1.0
@@ -28486,13 +28353,6 @@ snapshots:
which-collection: 1.0.2
which-typed-array: 1.1.19
which-collection@1.0.1:
dependencies:
is-map: 2.0.2
is-set: 2.0.3
is-weakmap: 2.0.1
is-weakset: 2.0.2
which-collection@1.0.2:
dependencies:
is-map: 2.0.3

View File

@@ -101,6 +101,7 @@ async function init() {
useCSSVars: false,
outSVGReact: false,
outSVGPath: false,
addLigatures: true,
svgicons2svgfont: {
fontHeight: 1000, // At least 1000 is recommended
normalize: false,