mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-14 20:27:42 +01:00
feat(icon-component): Creating icons with iconNodes (#1997)
* Add useIconComponent, lucide-react * Add concept useIconComponent * add useIconComponents to packages * Add icon component * Add icon component * Add tests for react packages * Reset changes in icons * Add types * Add support for Icon components in Lucide Vue Next * update tests * Update tests * Enable Svelte component * Fix lucide-react-native tests * Update Solid package * update snapshots * Add docs * add docs * Update tests * Formatting * Formatting * Update package lock * Remove `useIconComponent` * Update guides * Update exports preact and solid package * Formatting * Format createIcons.ts * Add lucide lab repo link in docs
This commit is contained in:
@@ -2,6 +2,10 @@ pnpm-lock.yaml
|
||||
|
||||
# docs examples
|
||||
docs/**/examples/
|
||||
docs/.vitepress/.temp
|
||||
docs/.vitepress/cache
|
||||
docs/.vitepress/data
|
||||
docs/.nitro
|
||||
|
||||
# lucide-angular
|
||||
packages/lucide-angular/.angular/cache
|
||||
|
||||
@@ -115,3 +115,20 @@ import { icons } from 'lucide-angular';
|
||||
|
||||
LucideAngularModule.pick(icons)
|
||||
```
|
||||
|
||||
## With Lucide lab or custom icons
|
||||
|
||||
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||
They can be used in the same way as the official icons.
|
||||
|
||||
```js
|
||||
import { LucideAngularModule } from 'lucide-angular';
|
||||
import { burger } from '@lucide/lab';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
LucideAngularModule.pick({ burger })
|
||||
]
|
||||
})
|
||||
export class AppModule { }
|
||||
```
|
||||
|
||||
@@ -67,6 +67,26 @@ const App = () => {
|
||||
|
||||
> SVG attributes in Preact aren't transformed, so if you want to change for example the `stroke-linejoin` you need to pass it in kebabcase. Basically how the SVG spec want you to write it. See this topic in the [Preact documentation](https://preactjs.com/guide/v10/differences-to-react/#svg-inside-jsx).
|
||||
|
||||
## With Lucide lab or custom icons
|
||||
|
||||
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||
|
||||
They can be used by using the `Icon` component.
|
||||
All props like regular lucide icons can be passed to adjust the icon appearance.
|
||||
|
||||
### Using the `Icon` component
|
||||
|
||||
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
|
||||
|
||||
```jsx
|
||||
import { Icon } from 'lucide-preact';
|
||||
import { burger } from '@lucide/lab';
|
||||
|
||||
const App = () => (
|
||||
<Icon iconNode={burger} />
|
||||
);
|
||||
```
|
||||
|
||||
## One generic icon component
|
||||
|
||||
It is possible to create one generic icon component to load icons, but it is not recommended.
|
||||
|
||||
@@ -61,6 +61,26 @@ const App = () => {
|
||||
};
|
||||
```
|
||||
|
||||
## With Lucide lab or custom icons
|
||||
|
||||
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||
|
||||
They can be used by using the `Icon` component.
|
||||
All props like regular lucide icons can be passed to adjust the icon appearance.
|
||||
|
||||
### Using the `Icon` component
|
||||
|
||||
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
|
||||
|
||||
```jsx
|
||||
import { Icon } from 'lucide-react-native';
|
||||
import { burger } from '@lucide/lab';
|
||||
|
||||
const App = () => (
|
||||
<Icon iconNode={burger} />
|
||||
);
|
||||
```
|
||||
|
||||
## One generic icon component
|
||||
|
||||
It is possible to create one generic icon component to load icons, but it is not recommended.
|
||||
|
||||
@@ -61,6 +61,26 @@ const App = () => {
|
||||
};
|
||||
```
|
||||
|
||||
## With Lucide lab or custom icons
|
||||
|
||||
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||
|
||||
They can be used by using the `Icon` component.
|
||||
All props like regular lucide icons can be passed to adjust the icon appearance.
|
||||
|
||||
### Using the `Icon` component
|
||||
|
||||
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
|
||||
|
||||
```jsx
|
||||
import { Icon } from 'lucide-react';
|
||||
import { burger } from '@lucide/lab';
|
||||
|
||||
const App = () => (
|
||||
<Icon iconNode={burger} />
|
||||
);
|
||||
```
|
||||
|
||||
## One generic icon component
|
||||
|
||||
It is possible to create one generic icon component to load icons, but it is not recommended.
|
||||
|
||||
@@ -61,6 +61,26 @@ const App = () => {
|
||||
};
|
||||
```
|
||||
|
||||
## With Lucide lab or custom icons
|
||||
|
||||
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||
|
||||
They can be used by using the `Icon` component.
|
||||
All props like the regular Lucide icons can be passed to adjust the icon appearance.
|
||||
|
||||
### Using the `Icon` component
|
||||
|
||||
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
|
||||
|
||||
```jsx
|
||||
import { Icon } from 'lucide-solid';
|
||||
import { burger, sausage } from '@lucide/lab';
|
||||
|
||||
const App = () => (
|
||||
<Icon iconNode={sausage} color="red"/>
|
||||
);
|
||||
```
|
||||
|
||||
## One generic icon component
|
||||
|
||||
It is possible to create one generic icon component to load icons. It's not recommended.
|
||||
|
||||
@@ -166,6 +166,27 @@ The package includes type definitions for all icons. This is useful if you want
|
||||
|
||||
For more details about typing the `svelte:component` directive, see the [Svelte documentation](https://svelte.dev/docs/typescript#types-componenttype).
|
||||
|
||||
## With Lucide lab or custom icons
|
||||
|
||||
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||
|
||||
They can be used by using the `Icon` component.
|
||||
All props like the regular Lucide icons can be passed to adjust the icon appearance.
|
||||
|
||||
### Using the `Icon` component
|
||||
|
||||
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Icon } from 'lucide-svelte';
|
||||
import { burger, sausage } from '@lucide/lab';
|
||||
</script>
|
||||
|
||||
<Icon iconNode={burger} />
|
||||
<Icon iconNode={sausage} color="red"/>
|
||||
```
|
||||
|
||||
## One generic icon component
|
||||
|
||||
It is possible to create one generic icon component to load icons, but it is not recommended.
|
||||
|
||||
@@ -37,16 +37,16 @@ Each icon can be imported as a Vue component, which renders an inline SVG Elemen
|
||||
You can pass additional props to adjust the icon.
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { Camera } from 'lucide-vue-next';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Camera
|
||||
color="red"
|
||||
:size="32"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Camera } from 'lucide-vue-next';
|
||||
</script>
|
||||
```
|
||||
|
||||
## Props
|
||||
@@ -69,6 +69,28 @@ To customize the appearance of an icon, you can pass custom properties as props
|
||||
</template>
|
||||
```
|
||||
|
||||
## With Lucide lab or custom icons
|
||||
|
||||
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||
|
||||
They can be used by using the `Icon` component.
|
||||
All props like regular lucide icons can be passed to adjust the icon appearance.
|
||||
|
||||
### Using the `Icon` component
|
||||
|
||||
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { Icon } from 'lucide-vue-next';
|
||||
import { burger } from '@lucide/lab';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Icon :iconNode={burger} />
|
||||
</template>
|
||||
```
|
||||
|
||||
## One generic icon component
|
||||
|
||||
It is possible to create one generic icon component to load icons, but it is not recommended.
|
||||
|
||||
@@ -130,3 +130,18 @@ menuIcon.classList.add('my-icon-class');
|
||||
const myApp = document.getElementById('app');
|
||||
myApp.appendChild(menuIcon);
|
||||
```
|
||||
|
||||
### With Lucide lab or custom icons
|
||||
|
||||
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||
They can be used in the same way as the official icons.
|
||||
|
||||
```js
|
||||
import { burger } from '@lucide/lab';
|
||||
|
||||
createIcons({
|
||||
icons: {
|
||||
burger
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
50
packages/lucide-preact/src/Icon.ts
Normal file
50
packages/lucide-preact/src/Icon.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { h, toChildArray } from 'preact';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import type { IconNode, LucideProps } from './types';
|
||||
|
||||
interface IconComponentProps extends LucideProps {
|
||||
iconNode: IconNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lucide icon component
|
||||
*
|
||||
* @component Icon
|
||||
* @param {object} props
|
||||
* @param {string} props.color - The color of the icon
|
||||
* @param {number} props.size - The size of the icon
|
||||
* @param {number} props.strokeWidth - The stroke width of the icon
|
||||
* @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width
|
||||
* @param {string} props.class - The class name of the icon
|
||||
* @param {IconNode} props.children - The children of the icon
|
||||
* @param {IconNode} props.iconNode - The icon node of the icon
|
||||
*
|
||||
* @returns {ForwardRefExoticComponent} LucideIcon
|
||||
*/
|
||||
const Icon = ({
|
||||
color = 'currentColor',
|
||||
size = 24,
|
||||
strokeWidth = 2,
|
||||
absoluteStrokeWidth,
|
||||
children,
|
||||
iconNode,
|
||||
class: classes = '',
|
||||
...rest
|
||||
}: IconComponentProps) =>
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
...defaultAttributes,
|
||||
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;
|
||||
@@ -1,17 +1,7 @@
|
||||
import { type FunctionComponent, h, type JSX, toChildArray } from 'preact';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import { toKebabCase } from '@lucide/shared';
|
||||
|
||||
export type IconNode = [elementName: keyof JSX.IntrinsicElements, attrs: Record<string, string>][];
|
||||
|
||||
export interface LucideProps extends Partial<Omit<JSX.SVGAttributes, 'ref' | 'size'>> {
|
||||
color?: string;
|
||||
size?: string | number;
|
||||
strokeWidth?: string | number;
|
||||
absoluteStrokeWidth?: boolean;
|
||||
}
|
||||
|
||||
export type LucideIcon = FunctionComponent<LucideProps>;
|
||||
import { h, type JSX } from 'preact';
|
||||
import { mergeClasses, toKebabCase } from '@lucide/shared';
|
||||
import Icon from './Icon';
|
||||
import type { IconNode, LucideIcon, LucideProps } from './types';
|
||||
|
||||
/**
|
||||
* Create a Lucide icon component
|
||||
@@ -20,29 +10,18 @@ export type LucideIcon = FunctionComponent<LucideProps>;
|
||||
* @returns {FunctionComponent} LucideIcon
|
||||
*/
|
||||
const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
|
||||
const Component = ({
|
||||
color = 'currentColor',
|
||||
size = 24,
|
||||
strokeWidth = 2,
|
||||
absoluteStrokeWidth,
|
||||
children,
|
||||
class: classes = '',
|
||||
...rest
|
||||
}: LucideProps) =>
|
||||
const Component = ({ class: classes = '', children, ...props }: LucideProps) =>
|
||||
h(
|
||||
'svg',
|
||||
Icon,
|
||||
{
|
||||
...defaultAttributes,
|
||||
width: String(size),
|
||||
height: size,
|
||||
stroke: color,
|
||||
['stroke-width' as 'strokeWidth']: absoluteStrokeWidth
|
||||
? (Number(strokeWidth) * 24) / Number(size)
|
||||
: strokeWidth,
|
||||
class: ['lucide', `lucide-${toKebabCase(iconName)}`, classes].join(' '),
|
||||
...rest,
|
||||
...props,
|
||||
iconNode,
|
||||
class: mergeClasses<string | JSX.SignalLike<string | undefined>>(
|
||||
`lucide-${toKebabCase(iconName)}`,
|
||||
classes,
|
||||
),
|
||||
},
|
||||
[...iconNode.map(([tag, attrs]) => h(tag, attrs)), ...toChildArray(children)],
|
||||
children,
|
||||
);
|
||||
|
||||
Component.displayName = `${iconName}`;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export * from './icons';
|
||||
export * as icons from './icons';
|
||||
export * from './aliases';
|
||||
export * from './types';
|
||||
|
||||
export { default as createLucideIcon } from './createLucideIcon';
|
||||
export { default as Icon } from './Icon';
|
||||
|
||||
12
packages/lucide-preact/src/types.ts
Normal file
12
packages/lucide-preact/src/types.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { type FunctionComponent, type JSX } from 'preact';
|
||||
|
||||
export type IconNode = [elementName: keyof JSX.IntrinsicElements, attrs: Record<string, string>][];
|
||||
|
||||
export interface LucideProps extends Partial<Omit<JSX.SVGAttributes, 'ref' | 'size'>> {
|
||||
color?: string;
|
||||
size?: string | number;
|
||||
strokeWidth?: string | number;
|
||||
absoluteStrokeWidth?: boolean;
|
||||
}
|
||||
|
||||
export type LucideIcon = FunctionComponent<LucideProps>;
|
||||
33
packages/lucide-preact/tests/Icon.spec.tsx
Normal file
33
packages/lucide-preact/tests/Icon.spec.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render } from '@testing-library/preact';
|
||||
|
||||
import { airVent } from './testIconNodes';
|
||||
import { Icon } from '../src/lucide-preact';
|
||||
|
||||
describe('Using Icon Component', () => {
|
||||
it('should render icon based on a iconNode', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render icon and match snapshot', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
|
||||
<svg
|
||||
class="lucide "
|
||||
fill="none"
|
||||
height="48"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
viewBox="0 0 24 24"
|
||||
width="48"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
|
||||
/>
|
||||
<path
|
||||
d="M6 8h12"
|
||||
/>
|
||||
<path
|
||||
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
|
||||
/>
|
||||
<path
|
||||
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
|
||||
/>
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,29 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Using createLucideIcon > should create a component from an iconNode 1`] = `
|
||||
<svg
|
||||
class="lucide lucide-air-vent"
|
||||
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="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
|
||||
/>
|
||||
<path
|
||||
d="M6 8h12"
|
||||
/>
|
||||
<path
|
||||
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
|
||||
/>
|
||||
<path
|
||||
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
|
||||
/>
|
||||
</svg>
|
||||
`;
|
||||
@@ -10,8 +10,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid3x3 "
|
||||
data-testid="grid-icon"
|
||||
class="lucide lucide-grid3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
@@ -41,8 +40,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid3x3 "
|
||||
data-testid="grid-icon"
|
||||
class="lucide lucide-grid3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
@@ -72,7 +70,7 @@ exports[`Using lucide icon components > should render an component 1`] = `
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid3x3 "
|
||||
class="lucide lucide-grid3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
|
||||
15
packages/lucide-preact/tests/createLucideIcon.spec.tsx
Normal file
15
packages/lucide-preact/tests/createLucideIcon.spec.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { createLucideIcon } from '../src/lucide-preact';
|
||||
import { airVent } from './testIconNodes';
|
||||
import { render } from '@testing-library/preact';
|
||||
|
||||
describe('Using createLucideIcon', () => {
|
||||
it('should create a component from an iconNode', () => {
|
||||
const AirVent = createLucideIcon('AirVent', airVent);
|
||||
|
||||
const { container } = render(<AirVent />);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render, cleanup } from '@testing-library/preact';
|
||||
import { Pen, Edit2, Grid, Droplet } from '../src/lucide-preact';
|
||||
import defaultAttributes from '../src/defaultAttributes';
|
||||
|
||||
type AttributesAssertion = { attributes: Record<string, { value: string }> };
|
||||
|
||||
@@ -11,30 +12,43 @@ describe('Using lucide icon components', () => {
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the icon with the default attributes', () => {
|
||||
const { container } = render(<Grid />);
|
||||
|
||||
const SVGElement = container.firstElementChild;
|
||||
|
||||
expect(SVGElement).toHaveAttribute('xmlns', defaultAttributes.xmlns);
|
||||
expect(SVGElement).toHaveAttribute('width', String(defaultAttributes.width));
|
||||
expect(SVGElement).toHaveAttribute('height', String(defaultAttributes.height));
|
||||
expect(SVGElement).toHaveAttribute('viewBox', defaultAttributes.viewBox);
|
||||
expect(SVGElement).toHaveAttribute('fill', defaultAttributes.fill);
|
||||
expect(SVGElement).toHaveAttribute('stroke', defaultAttributes.stroke);
|
||||
expect(SVGElement).toHaveAttribute('stroke-width', String(defaultAttributes['stroke-width']));
|
||||
expect(SVGElement).toHaveAttribute('stroke-linecap', defaultAttributes['stroke-linecap']);
|
||||
expect(SVGElement).toHaveAttribute('stroke-linejoin', defaultAttributes['stroke-linejoin']);
|
||||
});
|
||||
|
||||
it('should adjust the size, stroke color and stroke width', () => {
|
||||
const testId = 'grid-icon';
|
||||
const { container, getByTestId } = render(
|
||||
const { container } = render(
|
||||
<Grid
|
||||
data-testid={testId}
|
||||
size={48}
|
||||
stroke="red"
|
||||
strokeWidth={4}
|
||||
/>,
|
||||
);
|
||||
|
||||
const { attributes } = getByTestId(testId) as unknown as AttributesAssertion;
|
||||
expect(attributes.stroke.value).toBe('red');
|
||||
expect(attributes.width.value).toBe('48');
|
||||
expect(attributes.height.value).toBe('48');
|
||||
expect(attributes['stroke-width'].value).toBe('4');
|
||||
const SVGElement = container.firstElementChild;
|
||||
|
||||
expect(SVGElement).toHaveAttribute('stroke', 'red');
|
||||
expect(SVGElement).toHaveAttribute('width', '48');
|
||||
expect(SVGElement).toHaveAttribute('height', '48');
|
||||
expect(SVGElement).toHaveAttribute('stroke-width', '4');
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the alias icon', () => {
|
||||
const testId = 'pen-icon';
|
||||
const { container } = render(
|
||||
<Pen
|
||||
data-testid={testId}
|
||||
size={48}
|
||||
stroke="red"
|
||||
strokeWidth={4}
|
||||
@@ -47,7 +61,6 @@ describe('Using lucide icon components', () => {
|
||||
|
||||
const { container: Edit2Container } = render(
|
||||
<Edit2
|
||||
data-testid={testId}
|
||||
size={48}
|
||||
stroke="red"
|
||||
strokeWidth={4}
|
||||
@@ -58,22 +71,21 @@ describe('Using lucide icon components', () => {
|
||||
});
|
||||
|
||||
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
|
||||
const testId = 'grid-icon';
|
||||
const { container, getByTestId } = render(
|
||||
const { container } = render(
|
||||
<Grid
|
||||
data-testid={testId}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
const { attributes } = getByTestId(testId) as unknown as AttributesAssertion;
|
||||
const SVGElement = container.firstElementChild;
|
||||
|
||||
expect(SVGElement).toHaveAttribute('stroke', 'red');
|
||||
expect(SVGElement).toHaveAttribute('width', '48');
|
||||
expect(SVGElement).toHaveAttribute('height', '48');
|
||||
expect(SVGElement).toHaveAttribute('stroke-width', '1');
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { expect } from 'vitest';
|
||||
import '@testing-library/jest-dom';
|
||||
import { expect, afterEach } from 'vitest';
|
||||
import { cleanup } from '@testing-library/preact';
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
import htmlSerializer from 'jest-serializer-html';
|
||||
|
||||
expect.addSnapshotSerializer(htmlSerializer);
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
22
packages/lucide-preact/tests/testIconNodes.ts
Normal file
22
packages/lucide-preact/tests/testIconNodes.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { IconNode } from '../src/createLucideIcon';
|
||||
|
||||
export const airVent: IconNode = [
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2',
|
||||
key: 'larmp2',
|
||||
},
|
||||
],
|
||||
['path', { d: 'M6 8h12', key: '6g4wlu' }],
|
||||
['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }],
|
||||
['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }],
|
||||
];
|
||||
|
||||
export const coffee: IconNode = [
|
||||
['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }],
|
||||
['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }],
|
||||
['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }],
|
||||
['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }],
|
||||
['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }],
|
||||
];
|
||||
@@ -45,6 +45,7 @@
|
||||
"devDependencies": {
|
||||
"@lucide/rollup-plugins": "workspace:*",
|
||||
"@lucide/build-icons": "workspace:*",
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@testing-library/jest-dom": "^6.1.6",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@types/prop-types": "^15.7.5",
|
||||
|
||||
69
packages/lucide-react-native/src/Icon.ts
Normal file
69
packages/lucide-react-native/src/Icon.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { createElement, forwardRef, type FunctionComponent } from 'react';
|
||||
import * as NativeSvg from 'react-native-svg';
|
||||
import defaultAttributes, { childDefaultAttributes } from './defaultAttributes';
|
||||
import { IconNode, LucideProps } from './types';
|
||||
|
||||
interface IconComponentProps extends LucideProps {
|
||||
iconNode: IconNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lucide icon component
|
||||
*
|
||||
* @component Icon
|
||||
* @param {object} props
|
||||
* @param {string} props.color - The color of the icon
|
||||
* @param {number} props.size - The size of the icon
|
||||
* @param {number} props.strokeWidth - The stroke width of the icon
|
||||
* @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width
|
||||
* @param {string} props.className - The class name of the icon
|
||||
* @param {IconNode} props.children - The children of the icon
|
||||
* @param {IconNode} props.iconNode - The icon node of the icon
|
||||
*
|
||||
* @returns {ForwardRefExoticComponent} LucideIcon
|
||||
*/
|
||||
const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
|
||||
(
|
||||
{
|
||||
color = 'currentColor',
|
||||
size = 24,
|
||||
strokeWidth = 2,
|
||||
absoluteStrokeWidth,
|
||||
children,
|
||||
iconNode,
|
||||
...rest
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
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,
|
||||
...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]) || []),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default Icon;
|
||||
@@ -7,17 +7,7 @@ import {
|
||||
} from 'react';
|
||||
import * as NativeSvg from 'react-native-svg';
|
||||
import defaultAttributes, { childDefaultAttributes } from './defaultAttributes';
|
||||
import type { SvgProps } from 'react-native-svg';
|
||||
|
||||
export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, string>][];
|
||||
|
||||
export interface LucideProps extends SvgProps {
|
||||
size?: string | number;
|
||||
absoluteStrokeWidth?: boolean;
|
||||
'data-testid'?: string;
|
||||
}
|
||||
|
||||
export type LucideIcon = ForwardRefExoticComponent<LucideProps>;
|
||||
import { IconNode, LucideIcon, LucideProps } from './types';
|
||||
|
||||
const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
|
||||
const Component = forwardRef(
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
export * from './icons';
|
||||
export * as icons from './icons';
|
||||
export * from './aliases';
|
||||
export {
|
||||
default as createLucideIcon,
|
||||
type IconNode,
|
||||
type LucideProps,
|
||||
type LucideIcon,
|
||||
} from './createLucideIcon';
|
||||
export * from './types';
|
||||
|
||||
export { default as createLucideIcon } from './createLucideIcon';
|
||||
export { default as Icon } from './Icon';
|
||||
|
||||
12
packages/lucide-react-native/src/types.ts
Normal file
12
packages/lucide-react-native/src/types.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { ForwardRefExoticComponent, ReactSVG } from 'react';
|
||||
import type { SvgProps } from 'react-native-svg';
|
||||
|
||||
export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, string>][];
|
||||
|
||||
export interface LucideProps extends SvgProps {
|
||||
size?: string | number;
|
||||
absoluteStrokeWidth?: boolean;
|
||||
'data-testid'?: string;
|
||||
}
|
||||
|
||||
export type LucideIcon = ForwardRefExoticComponent<LucideProps>;
|
||||
35
packages/lucide-react-native/tests/Icon.spec.tsx
Normal file
35
packages/lucide-react-native/tests/Icon.spec.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { airVent } from './testIconNodes';
|
||||
import { Icon } from '../src/lucide-react-native';
|
||||
|
||||
vi.mock('react-native-svg');
|
||||
|
||||
describe('Using Icon Component', () => {
|
||||
it('should render icon based on a iconNode', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render icon and match snapshot', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
|
||||
<svg
|
||||
fill="none"
|
||||
height="48"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
viewBox="0 0 24 24"
|
||||
width="48"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
|
||||
fill="none"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
/>
|
||||
<path
|
||||
d="M6 8h12"
|
||||
fill="none"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
/>
|
||||
<path
|
||||
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
|
||||
fill="none"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
/>
|
||||
<path
|
||||
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
|
||||
fill="none"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
/>
|
||||
</svg>
|
||||
`;
|
||||
@@ -10,7 +10,6 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
data-testid="grid-icon"
|
||||
>
|
||||
<rect fill="none"
|
||||
stroke="red"
|
||||
@@ -128,7 +127,6 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
data-testid="grid-icon"
|
||||
>
|
||||
<rect fill="none"
|
||||
stroke="red"
|
||||
|
||||
@@ -14,21 +14,20 @@ describe('Using lucide icon components', () => {
|
||||
});
|
||||
|
||||
it('should adjust the size, stroke color and stroke width', () => {
|
||||
const testId = 'grid-icon';
|
||||
const { container, getByTestId } = render(
|
||||
const { container } = render(
|
||||
<Grid
|
||||
data-testid={testId}
|
||||
size={48}
|
||||
stroke="red"
|
||||
strokeWidth={4}
|
||||
/>,
|
||||
);
|
||||
|
||||
const { attributes } = getByTestId(testId);
|
||||
expect((attributes as unknown as Attributes).stroke.value).toBe('red');
|
||||
expect((attributes as unknown as Attributes).width.value).toBe('48');
|
||||
expect((attributes as unknown as Attributes).height.value).toBe('48');
|
||||
expect((attributes as unknown as Attributes)['stroke-width'].value).toBe('4');
|
||||
const SVGElement = container.firstElementChild;
|
||||
|
||||
expect(SVGElement).toHaveAttribute('stroke', 'red');
|
||||
expect(SVGElement).toHaveAttribute('width', '48');
|
||||
expect(SVGElement).toHaveAttribute('height', '48');
|
||||
expect(SVGElement).toHaveAttribute('stroke-width', '4');
|
||||
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
@@ -61,23 +60,20 @@ describe('Using lucide icon components', () => {
|
||||
});
|
||||
|
||||
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
|
||||
const testId = 'grid-icon';
|
||||
const { container, getByTestId } = render(
|
||||
const { container } = render(
|
||||
<Grid
|
||||
data-testid={testId}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
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');
|
||||
const SVGElement = container.firstElementChild;
|
||||
|
||||
expect(SVGElement).toHaveAttribute('stroke', 'red');
|
||||
expect(SVGElement).toHaveAttribute('width', '48');
|
||||
expect(SVGElement).toHaveAttribute('height', '48');
|
||||
expect(SVGElement).toHaveAttribute('stroke-width', '1');
|
||||
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
@@ -91,8 +87,8 @@ describe('Using lucide icon components', () => {
|
||||
<Grid data-testid={childId} />
|
||||
</Grid>,
|
||||
);
|
||||
const { children } = getByTestId(testId) as unknown as { children: HTMLCollection };
|
||||
const lastChild = children[children.length - 1];
|
||||
const { children } = container.firstElementChild ?? {};
|
||||
const lastChild = children?.[children.length - 1];
|
||||
|
||||
expect(lastChild).toEqual(getByTestId(childId));
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
|
||||
22
packages/lucide-react-native/tests/testIconNodes.ts
Normal file
22
packages/lucide-react-native/tests/testIconNodes.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { IconNode } from '../src/types';
|
||||
|
||||
export const airVent: IconNode = [
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2',
|
||||
key: 'larmp2',
|
||||
},
|
||||
],
|
||||
['path', { d: 'M6 8h12', key: '6g4wlu' }],
|
||||
['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }],
|
||||
['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }],
|
||||
];
|
||||
|
||||
export const coffee: IconNode = [
|
||||
['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }],
|
||||
['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }],
|
||||
['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }],
|
||||
['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }],
|
||||
['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }],
|
||||
];
|
||||
59
packages/lucide-react/src/Icon.ts
Normal file
59
packages/lucide-react/src/Icon.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { createElement, forwardRef } from 'react';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import { IconNode, LucideProps } from './types';
|
||||
import { mergeClasses } from '@lucide/shared';
|
||||
|
||||
interface IconComponentProps extends LucideProps {
|
||||
iconNode: IconNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lucide icon component
|
||||
*
|
||||
* @component Icon
|
||||
* @param {object} props
|
||||
* @param {string} props.color - The color of the icon
|
||||
* @param {number} props.size - The size of the icon
|
||||
* @param {number} props.strokeWidth - The stroke width of the icon
|
||||
* @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width
|
||||
* @param {string} props.className - The class name of the icon
|
||||
* @param {IconNode} props.children - The children of the icon
|
||||
* @param {IconNode} props.iconNode - The icon node of the icon
|
||||
*
|
||||
* @returns {ForwardRefExoticComponent} LucideIcon
|
||||
*/
|
||||
const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
|
||||
(
|
||||
{
|
||||
color = 'currentColor',
|
||||
size = 24,
|
||||
strokeWidth = 2,
|
||||
absoluteStrokeWidth,
|
||||
className = '',
|
||||
children,
|
||||
iconNode,
|
||||
...rest
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
return createElement(
|
||||
'svg',
|
||||
{
|
||||
ref,
|
||||
...defaultAttributes,
|
||||
width: size,
|
||||
height: size,
|
||||
stroke: color,
|
||||
strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
|
||||
className: mergeClasses('lucide', className),
|
||||
...rest,
|
||||
},
|
||||
[
|
||||
...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
|
||||
...(Array.isArray(children) ? children : [children]),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default Icon;
|
||||
@@ -1,60 +1,22 @@
|
||||
import {
|
||||
forwardRef,
|
||||
createElement,
|
||||
ReactSVG,
|
||||
SVGProps,
|
||||
ForwardRefExoticComponent,
|
||||
RefAttributes,
|
||||
} from 'react';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import { toKebabCase } from '@lucide/shared';
|
||||
import { createElement, forwardRef } from 'react';
|
||||
import { mergeClasses, toKebabCase } from '@lucide/shared';
|
||||
import { IconNode, LucideProps } from './types';
|
||||
import Icon from './Icon';
|
||||
|
||||
export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, string>][];
|
||||
|
||||
export type SVGAttributes = Partial<SVGProps<SVGSVGElement>>;
|
||||
type ComponentAttributes = RefAttributes<SVGSVGElement> & SVGAttributes;
|
||||
|
||||
export interface LucideProps extends ComponentAttributes {
|
||||
size?: string | number;
|
||||
absoluteStrokeWidth?: boolean;
|
||||
}
|
||||
|
||||
export type LucideIcon = ForwardRefExoticComponent<LucideProps>;
|
||||
|
||||
const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
|
||||
const Component = forwardRef<SVGSVGElement, LucideProps>(
|
||||
(
|
||||
{
|
||||
color = 'currentColor',
|
||||
size = 24,
|
||||
strokeWidth = 2,
|
||||
absoluteStrokeWidth,
|
||||
className = '',
|
||||
children,
|
||||
...rest
|
||||
},
|
||||
/**
|
||||
* 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>(({ className, ...props }, ref) =>
|
||||
createElement(Icon, {
|
||||
ref,
|
||||
) => {
|
||||
return createElement(
|
||||
'svg',
|
||||
{
|
||||
ref,
|
||||
...defaultAttributes,
|
||||
width: size,
|
||||
height: size,
|
||||
stroke: color,
|
||||
strokeWidth: absoluteStrokeWidth
|
||||
? (Number(strokeWidth) * 24) / Number(size)
|
||||
: strokeWidth,
|
||||
className: ['lucide', `lucide-${toKebabCase(iconName)}`, className].join(' '),
|
||||
...rest,
|
||||
},
|
||||
[
|
||||
...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
|
||||
...(Array.isArray(children) ? children : [children]),
|
||||
],
|
||||
);
|
||||
},
|
||||
iconNode,
|
||||
className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),
|
||||
...props,
|
||||
}),
|
||||
);
|
||||
|
||||
Component.displayName = `${iconName}`;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
export * from './icons';
|
||||
export * as icons from './icons';
|
||||
export * from './aliases';
|
||||
export {
|
||||
default as createLucideIcon,
|
||||
type IconNode,
|
||||
type LucideProps,
|
||||
type LucideIcon,
|
||||
} from './createLucideIcon';
|
||||
export * from './types';
|
||||
|
||||
export { default as createLucideIcon } from './createLucideIcon';
|
||||
export { default as Icon } from './Icon';
|
||||
|
||||
15
packages/lucide-react/src/types.ts
Normal file
15
packages/lucide-react/src/types.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ReactSVG, SVGProps, ForwardRefExoticComponent, RefAttributes } from 'react';
|
||||
|
||||
export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, string>][];
|
||||
|
||||
export type SVGAttributes = Partial<SVGProps<SVGSVGElement>>;
|
||||
type ElementAttributes = RefAttributes<SVGSVGElement> & SVGAttributes;
|
||||
|
||||
export interface LucideProps extends ElementAttributes {
|
||||
size?: string | number;
|
||||
absoluteStrokeWidth?: boolean;
|
||||
}
|
||||
|
||||
export type LucideIcon = ForwardRefExoticComponent<
|
||||
Omit<LucideProps, 'ref'> & RefAttributes<SVGSVGElement>
|
||||
>;
|
||||
33
packages/lucide-react/tests/Icon.spec.tsx
Normal file
33
packages/lucide-react/tests/Icon.spec.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { airVent } from './testIconNodes';
|
||||
import { Icon } from '../src/lucide-react';
|
||||
|
||||
describe('Using Icon Component', () => {
|
||||
it('should render icon based on a iconNode', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render icon and match snapshot', async () => {
|
||||
const { container } = render(
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
29
packages/lucide-react/tests/__snapshots__/Icon.spec.tsx.snap
Normal file
29
packages/lucide-react/tests/__snapshots__/Icon.spec.tsx.snap
Normal file
@@ -0,0 +1,29 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
|
||||
<svg
|
||||
class="lucide"
|
||||
fill="none"
|
||||
height="48"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
viewBox="0 0 24 24"
|
||||
width="48"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
|
||||
/>
|
||||
<path
|
||||
d="M6 8h12"
|
||||
/>
|
||||
<path
|
||||
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
|
||||
/>
|
||||
<path
|
||||
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
|
||||
/>
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,29 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Using createLucideIcon > should create a component from an iconNode 1`] = `
|
||||
<svg
|
||||
class="lucide lucide-air-vent"
|
||||
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="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
|
||||
/>
|
||||
<path
|
||||
d="M6 8h12"
|
||||
/>
|
||||
<path
|
||||
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
|
||||
/>
|
||||
<path
|
||||
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
|
||||
/>
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,36 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Using dynamicImports > should render icons dynamically by using the dynamicIconImports module 1`] = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="48"
|
||||
viewbox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="red"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-smile"
|
||||
aria-label="smile"
|
||||
>
|
||||
<circle cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
>
|
||||
</circle>
|
||||
<path d="M8 14s1.5 2 4 2 4-2 4-2">
|
||||
</path>
|
||||
<line x1="9"
|
||||
x2="9.01"
|
||||
y1="9"
|
||||
y2="9"
|
||||
>
|
||||
</line>
|
||||
<line x1="15"
|
||||
x2="15.01"
|
||||
y1="9"
|
||||
y2="9"
|
||||
>
|
||||
</line>
|
||||
</svg>
|
||||
`;
|
||||
@@ -10,8 +10,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid3x3 "
|
||||
data-testid="grid-icon"
|
||||
class="lucide lucide-grid3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
@@ -41,8 +40,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid3x3 "
|
||||
data-testid="grid-icon"
|
||||
class="lucide lucide-grid3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
@@ -72,7 +70,7 @@ exports[`Using lucide icon components > should render an component 1`] = `
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-grid3x3 "
|
||||
class="lucide lucide-grid3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
@@ -91,38 +89,3 @@ exports[`Using lucide icon components > should render an component 1`] = `
|
||||
</path>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
exports[`Using lucide icon components > should render icons dynamically by using the dynamicIconImports module 1`] = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="48"
|
||||
viewbox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="red"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-smile "
|
||||
aria-label="smile"
|
||||
>
|
||||
<circle cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
>
|
||||
</circle>
|
||||
<path d="M8 14s1.5 2 4 2 4-2 4-2">
|
||||
</path>
|
||||
<line x1="9"
|
||||
x2="9.01"
|
||||
y1="9"
|
||||
y2="9"
|
||||
>
|
||||
</line>
|
||||
<line x1="15"
|
||||
x2="15.01"
|
||||
y1="9"
|
||||
y2="9"
|
||||
>
|
||||
</line>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
15
packages/lucide-react/tests/createLucideIcon.spec.tsx
Normal file
15
packages/lucide-react/tests/createLucideIcon.spec.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { createLucideIcon } from '../src/lucide-react';
|
||||
import { airVent } from './testIconNodes';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
describe('Using createLucideIcon', () => {
|
||||
it('should create a component from an iconNode', () => {
|
||||
const AirVent = createLucideIcon('AirVent', airVent);
|
||||
|
||||
const { container } = render(<AirVent />);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
});
|
||||
38
packages/lucide-react/tests/dynamicImports.spec.tsx
Normal file
38
packages/lucide-react/tests/dynamicImports.spec.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { Suspense, lazy } from 'react';
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
|
||||
import dynamicIconImports from '../src/dynamicIconImports';
|
||||
import { LucideProps } from '../src/types';
|
||||
|
||||
describe('Using dynamicImports', () => {
|
||||
it('should render icons dynamically by using the dynamicIconImports module', async () => {
|
||||
interface IconProps extends Omit<LucideProps, 'ref'> {
|
||||
name: keyof typeof dynamicIconImports;
|
||||
}
|
||||
|
||||
const Icon = ({ name, ...props }: IconProps) => {
|
||||
const LucideIcon = lazy(dynamicIconImports[name]);
|
||||
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<LucideIcon {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
const { container, getByLabelText } = render(
|
||||
<Icon
|
||||
aria-label="smile"
|
||||
name="smile"
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => getByLabelText('smile'));
|
||||
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render, cleanup, waitFor } from '@testing-library/react';
|
||||
import { Pen, Edit2, Grid, LucideProps, Droplet } from '../src/lucide-react';
|
||||
import { Suspense, lazy } from 'react';
|
||||
import dynamicIconImports from '../src/dynamicIconImports';
|
||||
import { render, cleanup } from '@testing-library/react';
|
||||
import { Pen, Edit2, Grid, Droplet } from '../src/lucide-react';
|
||||
import defaultAttributes from '../src/defaultAttributes';
|
||||
|
||||
describe('Using lucide icon components', () => {
|
||||
it('should render an component', () => {
|
||||
@@ -11,24 +10,37 @@ describe('Using lucide icon components', () => {
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the icon with default attributes', () => {
|
||||
const { container } = render(<Grid />);
|
||||
|
||||
const SVGElement = container.firstElementChild;
|
||||
|
||||
expect(SVGElement).toHaveAttribute('xmlns', defaultAttributes.xmlns);
|
||||
expect(SVGElement).toHaveAttribute('width', String(defaultAttributes.width));
|
||||
expect(SVGElement).toHaveAttribute('height', String(defaultAttributes.height));
|
||||
expect(SVGElement).toHaveAttribute('viewBox', defaultAttributes.viewBox);
|
||||
expect(SVGElement).toHaveAttribute('fill', defaultAttributes.fill);
|
||||
expect(SVGElement).toHaveAttribute('stroke', defaultAttributes.stroke);
|
||||
expect(SVGElement).toHaveAttribute('stroke-width', String(defaultAttributes.strokeWidth));
|
||||
expect(SVGElement).toHaveAttribute('stroke-linecap', defaultAttributes.strokeLinecap);
|
||||
expect(SVGElement).toHaveAttribute('stroke-linejoin', defaultAttributes.strokeLinejoin);
|
||||
});
|
||||
|
||||
it('should adjust the size, stroke color and stroke width', () => {
|
||||
const testId = 'grid-icon';
|
||||
const { container, getByTestId } = render(
|
||||
const { container } = render(
|
||||
<Grid
|
||||
data-testid={testId}
|
||||
size={48}
|
||||
stroke="red"
|
||||
strokeWidth={4}
|
||||
/>,
|
||||
);
|
||||
|
||||
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('4');
|
||||
const SVGElement = container.firstElementChild;
|
||||
|
||||
expect(SVGElement).toHaveAttribute('stroke', 'red');
|
||||
expect(SVGElement).toHaveAttribute('width', '48');
|
||||
expect(SVGElement).toHaveAttribute('height', '48');
|
||||
expect(SVGElement).toHaveAttribute('stroke-width', '4');
|
||||
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
@@ -58,23 +70,20 @@ describe('Using lucide icon components', () => {
|
||||
});
|
||||
|
||||
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
|
||||
const testId = 'grid-icon';
|
||||
const { container, getByTestId } = render(
|
||||
<Grid
|
||||
data-testid={testId}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
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');
|
||||
const SVGElement = container.firstElementChild;
|
||||
|
||||
expect(SVGElement).toHaveAttribute('stroke', 'red');
|
||||
expect(SVGElement).toHaveAttribute('width', '48');
|
||||
expect(SVGElement).toHaveAttribute('height', '48');
|
||||
expect(SVGElement).toHaveAttribute('stroke-width', '1');
|
||||
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
@@ -87,34 +96,4 @@ describe('Using lucide icon components', () => {
|
||||
expect(container.firstChild).toHaveClass('lucide');
|
||||
expect(container.firstChild).toHaveClass('lucide-droplet');
|
||||
});
|
||||
|
||||
it('should render icons dynamically by using the dynamicIconImports module', async () => {
|
||||
interface IconProps extends Omit<LucideProps, 'ref'> {
|
||||
name: keyof typeof dynamicIconImports;
|
||||
}
|
||||
|
||||
const Icon = ({ name, ...props }: IconProps) => {
|
||||
const LucideIcon = lazy(dynamicIconImports[name]);
|
||||
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<LucideIcon {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
const { container, getByLabelText } = render(
|
||||
<Icon
|
||||
aria-label="smile"
|
||||
name="smile"
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => getByLabelText('smile'));
|
||||
|
||||
expect(container.innerHTML).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
22
packages/lucide-react/tests/testIconNodes.ts
Normal file
22
packages/lucide-react/tests/testIconNodes.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { IconNode } from '../src/types';
|
||||
|
||||
export const airVent: IconNode = [
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2',
|
||||
key: 'larmp2',
|
||||
},
|
||||
],
|
||||
['path', { d: 'M6 8h12', key: '6g4wlu' }],
|
||||
['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }],
|
||||
['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }],
|
||||
];
|
||||
|
||||
export const coffee: IconNode = [
|
||||
['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }],
|
||||
['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }],
|
||||
['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }],
|
||||
['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }],
|
||||
['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }],
|
||||
];
|
||||
@@ -2,10 +2,10 @@ import { For, splitProps } from 'solid-js';
|
||||
import { Dynamic } from 'solid-js/web';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import { IconNode, LucideProps } from './types';
|
||||
import { toKebabCase } from '@lucide/shared';
|
||||
import { mergeClasses, toKebabCase } from '@lucide/shared';
|
||||
|
||||
interface IconProps {
|
||||
name: string;
|
||||
name?: string;
|
||||
iconNode: IconNode;
|
||||
}
|
||||
|
||||
@@ -33,9 +33,12 @@ const Icon = (props: LucideProps & IconProps) => {
|
||||
Number(localProps.size)
|
||||
: Number(localProps.strokeWidth ?? defaultAttributes['stroke-width'])
|
||||
}
|
||||
class={`lucide lucide-${toKebabCase(localProps?.name ?? 'icon')} ${
|
||||
localProps.class != null ? localProps.class : ''
|
||||
}`}
|
||||
class={mergeClasses(
|
||||
'lucide',
|
||||
'lucide-icon',
|
||||
localProps.name != null ? `lucide-${toKebabCase(localProps?.name)}` : undefined,
|
||||
localProps.class != null ? localProps.class : '',
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
<For each={localProps.iconNode}>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export * from './icons';
|
||||
export * as icons from './icons';
|
||||
export * from './aliases';
|
||||
export * from './types';
|
||||
|
||||
export { default as Icon } from './Icon';
|
||||
|
||||
@@ -11,3 +11,5 @@ export interface LucideProps extends SVGAttributes {
|
||||
class?: string;
|
||||
absoluteStrokeWidth?: boolean;
|
||||
}
|
||||
|
||||
export type LucideIcon = (props: LucideProps) => JSX.Element;
|
||||
|
||||
33
packages/lucide-solid/tests/Icon.spec.tsx
Normal file
33
packages/lucide-solid/tests/Icon.spec.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render } from '@solidjs/testing-library';
|
||||
|
||||
import { airVent } from './testIconNodes';
|
||||
import { Icon } from '../src/lucide-solid';
|
||||
|
||||
describe('Using Icon Component', () => {
|
||||
it('should render icon based on a iconNode', async () => {
|
||||
const { container } = render(() => (
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>
|
||||
));
|
||||
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render icon and match snapshot', async () => {
|
||||
const { container } = render(() => (
|
||||
<Icon
|
||||
iconNode={airVent}
|
||||
size={48}
|
||||
stroke="red"
|
||||
absoluteStrokeWidth
|
||||
/>
|
||||
));
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
33
packages/lucide-solid/tests/__snapshots__/Icon.spec.tsx.snap
Normal file
33
packages/lucide-solid/tests/__snapshots__/Icon.spec.tsx.snap
Normal file
@@ -0,0 +1,33 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
|
||||
<svg
|
||||
class="lucide lucide-icon"
|
||||
fill="none"
|
||||
height="48"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
viewBox="0 0 24 24"
|
||||
width="48"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
|
||||
key="larmp2"
|
||||
/>
|
||||
<path
|
||||
d="M6 8h12"
|
||||
key="6g4wlu"
|
||||
/>
|
||||
<path
|
||||
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
|
||||
key="1bo8pg"
|
||||
/>
|
||||
<path
|
||||
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
|
||||
key="t9h90c"
|
||||
/>
|
||||
</svg>
|
||||
`;
|
||||
@@ -10,7 +10,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
|
||||
height="48"
|
||||
stroke="red"
|
||||
stroke-width="4"
|
||||
class="lucide lucide-grid3x3 "
|
||||
class="lucide lucide-icon lucide-grid3x3"
|
||||
data-testid="grid-icon"
|
||||
>
|
||||
<rect width="18"
|
||||
@@ -50,7 +50,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
|
||||
height="48"
|
||||
stroke="red"
|
||||
stroke-width="1"
|
||||
class="lucide lucide-grid3x3 "
|
||||
class="lucide lucide-icon lucide-grid3x3"
|
||||
data-testid="grid-icon"
|
||||
>
|
||||
<rect width="18"
|
||||
@@ -90,7 +90,7 @@ exports[`Using lucide icon components > should render a component 1`] = `
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
class="lucide lucide-grid3x3 "
|
||||
class="lucide lucide-icon lucide-grid3x3"
|
||||
>
|
||||
<rect width="18"
|
||||
height="18"
|
||||
|
||||
22
packages/lucide-solid/tests/testIconNodes.ts
Normal file
22
packages/lucide-solid/tests/testIconNodes.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { IconNode } from '../src/types';
|
||||
|
||||
export const airVent: IconNode = [
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2',
|
||||
key: 'larmp2',
|
||||
},
|
||||
],
|
||||
['path', { d: 'M6 8h12', key: '6g4wlu' }],
|
||||
['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }],
|
||||
['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }],
|
||||
];
|
||||
|
||||
export const coffee: IconNode = [
|
||||
['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }],
|
||||
['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }],
|
||||
['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }],
|
||||
['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }],
|
||||
['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }],
|
||||
];
|
||||
@@ -52,10 +52,12 @@
|
||||
"build:package": "svelte-package --input ./src",
|
||||
"build:license": "node ./scripts/appendBlockComments.mjs",
|
||||
"test": "pnpm build:icons && vitest run",
|
||||
"test:watch": "vitest watch",
|
||||
"version": "pnpm version --git-tag-version=false"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lucide/build-icons": "workspace:*",
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@sveltejs/package": "^2.2.3",
|
||||
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
||||
"@testing-library/jest-dom": "^6.1.4",
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { mergeClasses } from '@lucide/shared'
|
||||
import defaultAttributes from './defaultAttributes'
|
||||
import type { IconNode } from './types';
|
||||
|
||||
export let name: string
|
||||
export let name: string | undefined = undefined
|
||||
export let color = 'currentColor'
|
||||
export let size: number | string = 24
|
||||
export let strokeWidth: number | string = 2
|
||||
@@ -21,7 +22,14 @@
|
||||
? Number(strokeWidth) * 24 / Number(size)
|
||||
: strokeWidth
|
||||
}
|
||||
class={`lucide-icon lucide lucide-${name} ${$$props.class ?? ''}`}
|
||||
class={
|
||||
mergeClasses(
|
||||
'lucide-icon',
|
||||
'lucide',
|
||||
name ? `lucide-${name}`: '',
|
||||
$$props.class
|
||||
)
|
||||
}
|
||||
>
|
||||
{#each iconNode as [tag, attrs]}
|
||||
<svelte:element this={tag} {...attrs}/>
|
||||
|
||||
@@ -3,3 +3,4 @@ export * as icons from './icons/index.js';
|
||||
export * from './aliases.js';
|
||||
export { default as defaultAttributes } from './defaultAttributes.js';
|
||||
export * from './types.js';
|
||||
export { default as Icon } from './Icon.svelte';
|
||||
|
||||
33
packages/lucide-svelte/tests/Icon.spec.ts
Normal file
33
packages/lucide-svelte/tests/Icon.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render } from '@testing-library/svelte';
|
||||
import { Icon } from '../src/lucide-svelte';
|
||||
|
||||
import { airVent } from './testIconNodes';
|
||||
|
||||
describe('Using Icon Component', () => {
|
||||
it('should render icon based on a iconNode', async () => {
|
||||
const { container } = render(Icon, {
|
||||
props: {
|
||||
iconNode: airVent,
|
||||
size: 48,
|
||||
color: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render icon and match snapshot', async () => {
|
||||
const { container } = render(Icon, {
|
||||
props: {
|
||||
iconNode: airVent,
|
||||
size: 48,
|
||||
color: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
36
packages/lucide-svelte/tests/__snapshots__/Icon.spec.ts.snap
Normal file
36
packages/lucide-svelte/tests/__snapshots__/Icon.spec.ts.snap
Normal file
@@ -0,0 +1,36 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
|
||||
<div>
|
||||
<svg
|
||||
class="lucide-icon lucide"
|
||||
fill="none"
|
||||
height="48"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
viewBox="0 0 24 24"
|
||||
width="48"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
|
||||
/>
|
||||
|
||||
<path
|
||||
d="M6 8h12"
|
||||
/>
|
||||
|
||||
<path
|
||||
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
|
||||
/>
|
||||
|
||||
<path
|
||||
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
|
||||
/>
|
||||
|
||||
|
||||
</svg>
|
||||
</div>
|
||||
`;
|
||||
@@ -45,7 +45,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
|
||||
<body>
|
||||
<div>
|
||||
<svg
|
||||
class="lucide-icon lucide lucide-smile "
|
||||
class="lucide-icon lucide lucide-smile"
|
||||
fill="none"
|
||||
height="48"
|
||||
stroke="red"
|
||||
@@ -98,7 +98,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
data-testid="smile-icon"
|
||||
class="lucide-icon lucide lucide-smile "
|
||||
class="lucide-icon lucide lucide-smile"
|
||||
>
|
||||
<circle cx="12"
|
||||
cy="12"
|
||||
@@ -127,7 +127,7 @@ exports[`Using lucide icon components > should render an component 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<svg
|
||||
class="lucide-icon lucide lucide-smile "
|
||||
class="lucide-icon lucide lucide-smile"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
@@ -172,7 +172,7 @@ exports[`Using lucide icon components > should render an icon slot 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<svg
|
||||
class="lucide-icon lucide lucide-smile "
|
||||
class="lucide-icon lucide lucide-smile"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
|
||||
21
packages/lucide-svelte/tests/testIconNodes.ts
Normal file
21
packages/lucide-svelte/tests/testIconNodes.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { IconNode } from '../src/lucide-svelte';
|
||||
|
||||
export const airVent: IconNode = [
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2',
|
||||
},
|
||||
],
|
||||
['path', { d: 'M6 8h12' }],
|
||||
['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12' }],
|
||||
['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5' }],
|
||||
];
|
||||
|
||||
export const coffee: IconNode = [
|
||||
['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1' }],
|
||||
['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z' }],
|
||||
['line', { x1: '6', x2: '6', y1: '2', y2: '4' }],
|
||||
['line', { x1: '10', x2: '10', y1: '2', y2: '4' }],
|
||||
['line', { x1: '14', x2: '14', y1: '2', y2: '4' }],
|
||||
];
|
||||
@@ -41,6 +41,7 @@
|
||||
"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",
|
||||
"test": "pnpm build:icons && vitest run",
|
||||
"test:watch": "vitest watch",
|
||||
"version": "pnpm version --git-tag-version=false"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -48,14 +49,14 @@
|
||||
"@lucide/rollup-plugins": "workspace:*",
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@testing-library/jest-dom": "^6.1.6",
|
||||
"@testing-library/vue": "^8.0.1",
|
||||
"@testing-library/vue": "^8.0.3",
|
||||
"@vitejs/plugin-vue": "^4.6.2",
|
||||
"@vue/test-utils": "2.4.3",
|
||||
"@vue/test-utils": "2.4.5",
|
||||
"rollup": "^4.9.2",
|
||||
"rollup-plugin-dts": "^6.1.0",
|
||||
"vite": "5.0.13",
|
||||
"vitest": "^1.1.1",
|
||||
"vue": "^3.0.1"
|
||||
"vitest": "^1.4.0",
|
||||
"vue": "^3.4.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">=3.0.1"
|
||||
|
||||
30
packages/lucide-vue-next/src/Icon.ts
Normal file
30
packages/lucide-vue-next/src/Icon.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { type FunctionalComponent, h } from 'vue';
|
||||
import { mergeClasses, toKebabCase } from '@lucide/shared';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import { IconNode, LucideProps } from './types';
|
||||
|
||||
interface IconProps {
|
||||
iconNode: IconNode;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const Icon: FunctionalComponent<LucideProps & IconProps> = (
|
||||
{ size, strokeWidth = 2, absoluteStrokeWidth, color, iconNode, name, class: classes, ...props },
|
||||
{ slots },
|
||||
) => {
|
||||
return h(
|
||||
'svg',
|
||||
{
|
||||
...defaultAttributes,
|
||||
width: size || defaultAttributes.width,
|
||||
height: size || defaultAttributes.height,
|
||||
stroke: color || defaultAttributes.stroke,
|
||||
'stroke-width': absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
|
||||
class: ['lucide', `lucide-${toKebabCase(name ?? 'icon')}`],
|
||||
...props,
|
||||
},
|
||||
[...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])],
|
||||
);
|
||||
};
|
||||
|
||||
export default Icon;
|
||||
@@ -1,17 +1,9 @@
|
||||
import { h } from 'vue';
|
||||
import type { SVGAttributes, FunctionalComponent } from 'vue';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import { toKebabCase } from '@lucide/shared';
|
||||
import type { FunctionalComponent } from 'vue';
|
||||
import { IconNode, LucideProps } from './types';
|
||||
import Icon from './Icon';
|
||||
|
||||
// Create interface extending SVGAttributes
|
||||
export interface SVGProps extends Partial<SVGAttributes> {
|
||||
size?: 24 | number;
|
||||
strokeWidth?: number | string;
|
||||
absoluteStrokeWidth?: boolean;
|
||||
}
|
||||
|
||||
export type IconNode = [elementName: string, attrs: Record<string, string>][];
|
||||
export type Icon = FunctionalComponent<SVGProps>;
|
||||
|
||||
/**
|
||||
* Create a Lucide icon component
|
||||
@@ -20,27 +12,16 @@ export type Icon = FunctionalComponent<SVGProps>;
|
||||
* @returns {FunctionalComponent} LucideIcon
|
||||
*/
|
||||
const createLucideIcon =
|
||||
(iconName: string, iconNode: IconNode): Icon =>
|
||||
(
|
||||
{ size, strokeWidth = 2, absoluteStrokeWidth, color, class: classes, ...props }, // props
|
||||
{ attrs, slots }, // context
|
||||
) => {
|
||||
return h(
|
||||
'svg',
|
||||
(iconName: string, iconNode: IconNode): FunctionalComponent<LucideProps> =>
|
||||
(props, { slots }) =>
|
||||
h(
|
||||
Icon,
|
||||
{
|
||||
...defaultAttributes,
|
||||
width: size || defaultAttributes.width,
|
||||
height: size || defaultAttributes.height,
|
||||
stroke: color || defaultAttributes.stroke,
|
||||
'stroke-width': absoluteStrokeWidth
|
||||
? (Number(strokeWidth) * 24) / Number(size)
|
||||
: strokeWidth,
|
||||
...attrs,
|
||||
class: ['lucide', `lucide-${toKebabCase(iconName)}`],
|
||||
...props,
|
||||
iconNode,
|
||||
name: iconName,
|
||||
},
|
||||
[...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])],
|
||||
slots,
|
||||
);
|
||||
};
|
||||
|
||||
export default createLucideIcon;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
export * from './icons';
|
||||
export * as icons from './icons';
|
||||
export * from './aliases';
|
||||
export * from './types';
|
||||
|
||||
export { default as createLucideIcon } from './createLucideIcon';
|
||||
export { default as Icon } from './Icon';
|
||||
|
||||
13
packages/lucide-vue-next/src/types.ts
Normal file
13
packages/lucide-vue-next/src/types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { FunctionalComponent, SVGAttributes } from 'vue';
|
||||
|
||||
export interface LucideProps extends Partial<SVGAttributes> {
|
||||
size?: 24 | number;
|
||||
strokeWidth?: number | string;
|
||||
absoluteStrokeWidth?: boolean;
|
||||
}
|
||||
|
||||
export type IconNode = [elementName: string, attrs: Record<string, string>][];
|
||||
export type LucideIcon = FunctionalComponent<LucideProps>;
|
||||
|
||||
// Legacy exports
|
||||
export type SVGProps = LucideProps;
|
||||
33
packages/lucide-vue-next/tests/Icon.spec.ts
Normal file
33
packages/lucide-vue-next/tests/Icon.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render } from '@testing-library/vue';
|
||||
|
||||
import { airVent } from './testIconNodes';
|
||||
import { Icon } from '../src/lucide-vue-next';
|
||||
|
||||
describe('Using Icon Component', () => {
|
||||
it('should render icon based on a iconNode', async () => {
|
||||
const { container } = render(Icon, {
|
||||
props: {
|
||||
iconNode: airVent,
|
||||
size: 48,
|
||||
color: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render icon and match snapshot', async () => {
|
||||
const { container } = render(Icon, {
|
||||
props: {
|
||||
iconNode: airVent,
|
||||
size: 48,
|
||||
color: 'red',
|
||||
absoluteStrokeWidth: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
|
||||
<svg
|
||||
class="lucide lucide-icon"
|
||||
fill="none"
|
||||
height="48"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
viewBox="0 0 24 24"
|
||||
width="48"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
|
||||
/>
|
||||
<path
|
||||
d="M6 8h12"
|
||||
/>
|
||||
<path
|
||||
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
|
||||
/>
|
||||
<path
|
||||
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
|
||||
/>
|
||||
</svg>
|
||||
`;
|
||||
@@ -81,10 +81,8 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
|
||||
<div>
|
||||
<svg
|
||||
class="lucide lucide-smile-icon"
|
||||
color="red"
|
||||
fill="none"
|
||||
height="48"
|
||||
size="48"
|
||||
stroke="red"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
import { render, fireEvent, cleanup } from '@testing-library/vue';
|
||||
import { Smile, Edit2, Pen } from '../src/lucide-vue-next';
|
||||
import defaultAttributes from '../src/defaultAttributes';
|
||||
|
||||
describe('Using lucide icon components', () => {
|
||||
afterEach(() => cleanup());
|
||||
@@ -10,6 +11,22 @@ describe('Using lucide icon components', () => {
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the icon with the default attributes', () => {
|
||||
const { container } = render(Smile);
|
||||
|
||||
const SVGElement = container.firstElementChild;
|
||||
|
||||
expect(SVGElement).toHaveAttribute('xmlns', defaultAttributes.xmlns);
|
||||
expect(SVGElement).toHaveAttribute('width', String(defaultAttributes.width));
|
||||
expect(SVGElement).toHaveAttribute('height', String(defaultAttributes.height));
|
||||
expect(SVGElement).toHaveAttribute('viewBox', defaultAttributes.viewBox);
|
||||
expect(SVGElement).toHaveAttribute('fill', defaultAttributes.fill);
|
||||
expect(SVGElement).toHaveAttribute('stroke', defaultAttributes.stroke);
|
||||
expect(SVGElement).toHaveAttribute('stroke-width', String(defaultAttributes['stroke-width']));
|
||||
expect(SVGElement).toHaveAttribute('stroke-linecap', defaultAttributes['stroke-linecap']);
|
||||
expect(SVGElement).toHaveAttribute('stroke-linejoin', defaultAttributes['stroke-linejoin']);
|
||||
});
|
||||
|
||||
it('should adjust the size, stroke color and stroke width', () => {
|
||||
const { container } = render(Smile, {
|
||||
props: {
|
||||
@@ -19,11 +36,11 @@ describe('Using lucide icon components', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const [icon] = document.getElementsByClassName('lucide');
|
||||
const SVGElement = container.firstElementChild;
|
||||
|
||||
expect(icon.getAttribute('width')).toBe('48');
|
||||
expect(icon.getAttribute('stroke')).toBe('red');
|
||||
expect(icon.getAttribute('stroke-width')).toBe('4');
|
||||
expect(SVGElement).toHaveAttribute('width', '48');
|
||||
expect(SVGElement).toHaveAttribute('stroke', 'red');
|
||||
expect(SVGElement).toHaveAttribute('stroke-width', '4');
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
@@ -37,7 +54,7 @@ describe('Using lucide icon components', () => {
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
const [icon] = document.getElementsByClassName('my-icon');
|
||||
const icon = container.firstElementChild;
|
||||
|
||||
expect(icon).toHaveClass('my-icon');
|
||||
expect(icon).toHaveClass('lucide');
|
||||
@@ -53,20 +70,20 @@ describe('Using lucide icon components', () => {
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
|
||||
const [icon] = document.getElementsByClassName('lucide');
|
||||
const icon = container.firstElementChild;
|
||||
|
||||
expect(icon).toHaveStyle({ position: 'absolute' });
|
||||
});
|
||||
|
||||
it('should call the onClick event', async () => {
|
||||
const onClick = vi.fn();
|
||||
render(Smile, {
|
||||
const { container } = render(Smile, {
|
||||
attrs: {
|
||||
onClick,
|
||||
},
|
||||
});
|
||||
|
||||
const [icon] = document.getElementsByClassName('lucide');
|
||||
const icon = container.firstElementChild;
|
||||
|
||||
await fireEvent.click(icon);
|
||||
|
||||
@@ -116,7 +133,7 @@ describe('Using lucide icon components', () => {
|
||||
});
|
||||
|
||||
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
|
||||
render(Pen, {
|
||||
const { container } = render(Pen, {
|
||||
props: {
|
||||
size: 48,
|
||||
color: 'red',
|
||||
@@ -124,10 +141,10 @@ describe('Using lucide icon components', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const [icon] = document.getElementsByClassName('lucide');
|
||||
const icon = container.firstElementChild;
|
||||
|
||||
expect(icon.getAttribute('width')).toBe('48');
|
||||
expect(icon.getAttribute('stroke')).toBe('red');
|
||||
expect(icon.getAttribute('stroke-width')).toBe('1');
|
||||
expect(icon).toHaveAttribute('width', '48');
|
||||
expect(icon).toHaveAttribute('stroke', 'red');
|
||||
expect(icon).toHaveAttribute('stroke-width', '1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1 +1 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
|
||||
22
packages/lucide-vue-next/tests/testIconNodes.ts
Normal file
22
packages/lucide-vue-next/tests/testIconNodes.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { IconNode } from '../src/createLucideIcon';
|
||||
|
||||
export const airVent: IconNode = [
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2',
|
||||
key: 'larmp2',
|
||||
},
|
||||
],
|
||||
['path', { d: 'M6 8h12', key: '6g4wlu' }],
|
||||
['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }],
|
||||
['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }],
|
||||
];
|
||||
|
||||
export const coffee: IconNode = [
|
||||
['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }],
|
||||
['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }],
|
||||
['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }],
|
||||
['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }],
|
||||
['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }],
|
||||
];
|
||||
37
packages/lucide/src/createIcons.ts
Normal file
37
packages/lucide/src/createIcons.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import replaceElement from './replaceElement';
|
||||
|
||||
/**
|
||||
* Replaces all elements with matching nameAttr with the defined icons
|
||||
* @param {{ icons?: object, nameAttr?: string, attrs?: object }} options
|
||||
*/
|
||||
const createIcons = ({ icons = {}, nameAttr = 'data-lucide', attrs = {} } = {}) => {
|
||||
if (!Object.values(icons).length) {
|
||||
throw new Error(
|
||||
"Please provide an icons object.\nIf you want to use all the icons you can import it like:\n `import { createIcons, icons } from 'lucide';\nlucide.createIcons({icons});`",
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof document === 'undefined') {
|
||||
throw new Error('`createIcons()` only works in a browser environment.');
|
||||
}
|
||||
|
||||
const elementsToReplace = document.querySelectorAll(`[${nameAttr}]`);
|
||||
Array.from(elementsToReplace).forEach((element) =>
|
||||
replaceElement(element, { nameAttr, icons, attrs }),
|
||||
);
|
||||
|
||||
/** @todo: remove this block in v1.0 */
|
||||
if (nameAttr === 'data-lucide') {
|
||||
const deprecatedElements = document.querySelectorAll('[icon-name]');
|
||||
if (deprecatedElements.length > 0) {
|
||||
console.warn(
|
||||
'[Lucide] Some icons were found with the now deprecated icon-name attribute. These will still be replaced for backwards compatibility, but will no longer be supported in v1.0 and you should switch to data-lucide',
|
||||
);
|
||||
Array.from(deprecatedElements).forEach((element) =>
|
||||
replaceElement(element, { nameAttr: 'icon-name', icons, attrs }),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default createIcons;
|
||||
@@ -1,41 +1,9 @@
|
||||
import replaceElement from './replaceElement';
|
||||
import * as iconAndAliases from './iconsAndAliases';
|
||||
|
||||
/**
|
||||
* Replaces all elements with matching nameAttr with the defined icons
|
||||
* @param {{ icons?: object, nameAttr?: string, attrs?: object }} options
|
||||
*/
|
||||
const createIcons = ({ icons = {}, nameAttr = 'data-lucide', attrs = {} } = {}) => {
|
||||
if (!Object.values(icons).length) {
|
||||
throw new Error(
|
||||
"Please provide an icons object.\nIf you want to use all the icons you can import it like:\n `import { createIcons, icons } from 'lucide';\nlucide.createIcons({icons});`",
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof document === 'undefined') {
|
||||
throw new Error('`createIcons()` only works in a browser environment.');
|
||||
}
|
||||
|
||||
const elementsToReplace = document.querySelectorAll(`[${nameAttr}]`);
|
||||
Array.from(elementsToReplace).forEach((element) =>
|
||||
replaceElement(element, { nameAttr, icons, attrs }),
|
||||
);
|
||||
|
||||
/** @todo: remove this block in v1.0 */
|
||||
if (nameAttr === 'data-lucide') {
|
||||
const deprecatedElements = document.querySelectorAll('[icon-name]');
|
||||
if (deprecatedElements.length > 0) {
|
||||
console.warn(
|
||||
'[Lucide] Some icons were found with the now deprecated icon-name attribute. These will still be replaced for backwards compatibility, but will no longer be supported in v1.0 and you should switch to data-lucide',
|
||||
);
|
||||
Array.from(deprecatedElements).forEach((element) =>
|
||||
replaceElement(element, { nameAttr: 'icon-name', icons, attrs }),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { createIcons };
|
||||
/*
|
||||
Create Icons function export.
|
||||
*/
|
||||
export { default as createIcons } from './createIcons';
|
||||
|
||||
/*
|
||||
Create Element function export.
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './utils';
|
||||
export * from './utility-types';
|
||||
|
||||
16
packages/shared/src/utility-types.ts
Normal file
16
packages/shared/src/utility-types.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Convert a type string from camelCase to PascalCase
|
||||
*
|
||||
* @example
|
||||
* type Test = CamelToPascal<'fooBar'> // 'FooBar'
|
||||
*/
|
||||
export type CamelToPascal<T extends string> = T extends `${infer FirstChar}${infer Rest}`
|
||||
? `${Capitalize<FirstChar>}${Rest}`
|
||||
: never;
|
||||
|
||||
/**
|
||||
* Creates a list of components from a list of component names and a component type
|
||||
*/
|
||||
export type ComponentList<ComponentNames, ComponentType> = {
|
||||
[Prop in keyof ComponentNames as CamelToPascal<Prop & string>]: ComponentType;
|
||||
};
|
||||
@@ -1,8 +1,37 @@
|
||||
import { CamelToPascal } from './utility-types';
|
||||
|
||||
/**
|
||||
* Converts string to KebabCase
|
||||
* Converts string to kebab case
|
||||
*
|
||||
* @param {string} string
|
||||
* @returns {string} A kebabized string
|
||||
*/
|
||||
export const toKebabCase = (string: string) =>
|
||||
string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
|
||||
/**
|
||||
* Converts string to pascal case
|
||||
*
|
||||
* @param {string} string
|
||||
* @returns {string} A pascalized string
|
||||
*/
|
||||
export const toPascalCase = <T extends string>(string: T): CamelToPascal<T> => {
|
||||
const camelCase = string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) =>
|
||||
p2 ? p2.toUpperCase() : p1.toLowerCase(),
|
||||
);
|
||||
|
||||
return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal<T>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges classes into a single string
|
||||
*
|
||||
* @param {array} classes
|
||||
* @returns {string} A string of classes
|
||||
*/
|
||||
export const mergeClasses = <ClassType = string | undefined | null>(...classes: ClassType[]) =>
|
||||
classes
|
||||
.filter((className, index, array) => {
|
||||
return Boolean(className) && array.indexOf(className) === index;
|
||||
})
|
||||
.join(' ');
|
||||
|
||||
346
pnpm-lock.yaml
generated
346
pnpm-lock.yaml
generated
@@ -457,6 +457,9 @@ importers:
|
||||
'@lucide/rollup-plugins':
|
||||
specifier: workspace:*
|
||||
version: link:../../tools/rollup-plugins
|
||||
'@lucide/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../shared
|
||||
'@testing-library/jest-dom':
|
||||
specifier: ^6.1.6
|
||||
version: 6.4.2(vitest@1.2.2)
|
||||
@@ -586,6 +589,9 @@ importers:
|
||||
'@lucide/build-icons':
|
||||
specifier: workspace:*
|
||||
version: link:../../tools/build-icons
|
||||
'@lucide/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../shared
|
||||
'@sveltejs/package':
|
||||
specifier: ^2.2.3
|
||||
version: 2.2.6(svelte@4.1.2)(typescript@5.1.6)
|
||||
@@ -681,16 +687,16 @@ importers:
|
||||
version: link:../shared
|
||||
'@testing-library/jest-dom':
|
||||
specifier: ^6.1.6
|
||||
version: 6.4.2(vitest@1.2.2)
|
||||
version: 6.4.2(vitest@1.4.0)
|
||||
'@testing-library/vue':
|
||||
specifier: ^8.0.1
|
||||
version: 8.0.2(vue@3.4.18)
|
||||
specifier: ^8.0.3
|
||||
version: 8.0.3(vue@3.4.21)
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^4.6.2
|
||||
version: 4.6.2(vite@5.0.13)(vue@3.4.18)
|
||||
version: 4.6.2(vite@5.0.13)(vue@3.4.21)
|
||||
'@vue/test-utils':
|
||||
specifier: 2.4.3
|
||||
version: 2.4.3(vue@3.4.18)
|
||||
specifier: 2.4.5
|
||||
version: 2.4.5
|
||||
rollup:
|
||||
specifier: ^4.9.2
|
||||
version: 4.9.6
|
||||
@@ -701,11 +707,11 @@ importers:
|
||||
specifier: 5.0.13
|
||||
version: 5.0.13
|
||||
vitest:
|
||||
specifier: ^1.1.1
|
||||
version: 1.2.2(jsdom@20.0.3)
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
vue:
|
||||
specifier: ^3.0.1
|
||||
version: 3.4.18(typescript@4.9.5)
|
||||
specifier: ^3.4.21
|
||||
version: 3.4.21(typescript@4.9.5)
|
||||
|
||||
packages/shared: {}
|
||||
|
||||
@@ -7452,6 +7458,38 @@ packages:
|
||||
vitest: 1.2.2(jsdom@20.0.3)
|
||||
dev: true
|
||||
|
||||
/@testing-library/jest-dom@6.4.2(vitest@1.4.0):
|
||||
resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==}
|
||||
engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
|
||||
peerDependencies:
|
||||
'@jest/globals': '>= 28'
|
||||
'@types/bun': latest
|
||||
'@types/jest': '>= 28'
|
||||
jest: '>= 28'
|
||||
vitest: '>= 0.32'
|
||||
peerDependenciesMeta:
|
||||
'@jest/globals':
|
||||
optional: true
|
||||
'@types/bun':
|
||||
optional: true
|
||||
'@types/jest':
|
||||
optional: true
|
||||
jest:
|
||||
optional: true
|
||||
vitest:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@adobe/css-tools': 4.3.3
|
||||
'@babel/runtime': 7.23.9
|
||||
aria-query: 5.3.0
|
||||
chalk: 3.0.0
|
||||
css.escape: 1.5.1
|
||||
dom-accessibility-api: 0.6.3
|
||||
lodash: 4.17.21
|
||||
redent: 3.0.0
|
||||
vitest: 1.4.0
|
||||
dev: true
|
||||
|
||||
/@testing-library/preact@3.2.3(preact@10.19.4):
|
||||
resolution: {integrity: sha512-y6Kklp1XK3f1X2fWCbujmJyzkf+1BgLYXNgAx21j9+D4CoqMTz5qC4SQufb1L6q/jxLGACzrQ90ewVOTBvHOfg==}
|
||||
engines: {node: '>= 12'}
|
||||
@@ -7500,8 +7538,8 @@ packages:
|
||||
vue-template-compiler: 2.7.14(vue@2.7.14)
|
||||
dev: true
|
||||
|
||||
/@testing-library/vue@8.0.2(vue@3.4.18):
|
||||
resolution: {integrity: sha512-A8wWX+qQn0o0izpQWnGCpwQt8wAdpsVP8vPP2h5Q/jcGhZ5yKXz9PPUqhQv+45LTFaWlyRf8bArTVaB/KFFd5A==}
|
||||
/@testing-library/vue@8.0.3(vue@3.4.21):
|
||||
resolution: {integrity: sha512-wSsbNlZ69ZFQgVlHMtc/ZC/g9BHO7MhyDrd4nHyfEubtMr3kToN/w4/BsSBknGIF8w9UmPbsgbIuq/CbdBHzCA==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@vue/compiler-sfc': '>= 3'
|
||||
@@ -7512,10 +7550,8 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.9
|
||||
'@testing-library/dom': 9.3.4
|
||||
'@vue/test-utils': 2.4.3(vue@3.4.18)
|
||||
vue: 3.4.18(typescript@4.9.5)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/server-renderer'
|
||||
'@vue/test-utils': 2.4.5
|
||||
vue: 3.4.21(typescript@4.9.5)
|
||||
dev: true
|
||||
|
||||
/@tokenizer/token@0.3.0:
|
||||
@@ -8378,7 +8414,7 @@ packages:
|
||||
vue: 2.7.14
|
||||
dev: true
|
||||
|
||||
/@vitejs/plugin-vue@4.6.2(vite@5.0.13)(vue@3.4.18):
|
||||
/@vitejs/plugin-vue@4.6.2(vite@5.0.13)(vue@3.4.21):
|
||||
resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
@@ -8386,7 +8422,7 @@ packages:
|
||||
vue: ^3.2.25
|
||||
dependencies:
|
||||
vite: 5.0.13
|
||||
vue: 3.4.18(typescript@4.9.5)
|
||||
vue: 3.4.21(typescript@4.9.5)
|
||||
dev: true
|
||||
|
||||
/@vitejs/plugin-vue@5.0.3(vite@5.0.12)(vue@3.4.18):
|
||||
@@ -8416,6 +8452,14 @@ packages:
|
||||
chai: 4.4.1
|
||||
dev: true
|
||||
|
||||
/@vitest/expect@1.4.0:
|
||||
resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==}
|
||||
dependencies:
|
||||
'@vitest/spy': 1.4.0
|
||||
'@vitest/utils': 1.4.0
|
||||
chai: 4.4.1
|
||||
dev: true
|
||||
|
||||
/@vitest/runner@0.32.4:
|
||||
resolution: {integrity: sha512-cHOVCkiRazobgdKLnczmz2oaKK9GJOw6ZyRcaPdssO1ej+wzHVIkWiCiNacb3TTYPdzMddYkCgMjZ4r8C0JFCw==}
|
||||
dependencies:
|
||||
@@ -8432,6 +8476,14 @@ packages:
|
||||
pathe: 1.1.2
|
||||
dev: true
|
||||
|
||||
/@vitest/runner@1.4.0:
|
||||
resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==}
|
||||
dependencies:
|
||||
'@vitest/utils': 1.4.0
|
||||
p-limit: 5.0.0
|
||||
pathe: 1.1.2
|
||||
dev: true
|
||||
|
||||
/@vitest/snapshot@0.32.4:
|
||||
resolution: {integrity: sha512-IRpyqn9t14uqsFlVI2d7DFMImGMs1Q9218of40bdQQgMePwVdmix33yMNnebXcTzDU5eiV3eUsoxxH5v0x/IQA==}
|
||||
dependencies:
|
||||
@@ -8448,6 +8500,14 @@ packages:
|
||||
pretty-format: 29.7.0
|
||||
dev: true
|
||||
|
||||
/@vitest/snapshot@1.4.0:
|
||||
resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==}
|
||||
dependencies:
|
||||
magic-string: 0.30.7
|
||||
pathe: 1.1.2
|
||||
pretty-format: 29.7.0
|
||||
dev: true
|
||||
|
||||
/@vitest/spy@0.32.4:
|
||||
resolution: {integrity: sha512-oA7rCOqVOOpE6rEoXuCOADX7Lla1LIa4hljI2MSccbpec54q+oifhziZIJXxlE/CvI2E+ElhBHzVu0VEvJGQKQ==}
|
||||
dependencies:
|
||||
@@ -8460,6 +8520,12 @@ packages:
|
||||
tinyspy: 2.2.1
|
||||
dev: true
|
||||
|
||||
/@vitest/spy@1.4.0:
|
||||
resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==}
|
||||
dependencies:
|
||||
tinyspy: 2.2.1
|
||||
dev: true
|
||||
|
||||
/@vitest/utils@0.32.4:
|
||||
resolution: {integrity: sha512-Gwnl8dhd1uJ+HXrYyV0eRqfmk9ek1ASE/LWfTCuWMw+d07ogHqp4hEAV28NiecimK6UY9DpSEPh+pXBA5gtTBg==}
|
||||
dependencies:
|
||||
@@ -8477,6 +8543,15 @@ packages:
|
||||
pretty-format: 29.7.0
|
||||
dev: true
|
||||
|
||||
/@vitest/utils@1.4.0:
|
||||
resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==}
|
||||
dependencies:
|
||||
diff-sequences: 29.6.3
|
||||
estree-walker: 3.0.3
|
||||
loupe: 2.3.7
|
||||
pretty-format: 29.7.0
|
||||
dev: true
|
||||
|
||||
/@vue/compiler-core@3.4.18:
|
||||
resolution: {integrity: sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==}
|
||||
dependencies:
|
||||
@@ -8486,12 +8561,29 @@ packages:
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.0.2
|
||||
|
||||
/@vue/compiler-core@3.4.21:
|
||||
resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
|
||||
dependencies:
|
||||
'@babel/parser': 7.23.9
|
||||
'@vue/shared': 3.4.21
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.0.2
|
||||
dev: true
|
||||
|
||||
/@vue/compiler-dom@3.4.18:
|
||||
resolution: {integrity: sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==}
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.4.18
|
||||
'@vue/shared': 3.4.18
|
||||
|
||||
/@vue/compiler-dom@3.4.21:
|
||||
resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.4.21
|
||||
'@vue/shared': 3.4.21
|
||||
dev: true
|
||||
|
||||
/@vue/compiler-sfc@2.7.14:
|
||||
resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
|
||||
dependencies:
|
||||
@@ -8513,12 +8605,33 @@ packages:
|
||||
postcss: 8.4.35
|
||||
source-map-js: 1.0.2
|
||||
|
||||
/@vue/compiler-sfc@3.4.21:
|
||||
resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==}
|
||||
dependencies:
|
||||
'@babel/parser': 7.23.9
|
||||
'@vue/compiler-core': 3.4.21
|
||||
'@vue/compiler-dom': 3.4.21
|
||||
'@vue/compiler-ssr': 3.4.21
|
||||
'@vue/shared': 3.4.21
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.7
|
||||
postcss: 8.4.35
|
||||
source-map-js: 1.0.2
|
||||
dev: true
|
||||
|
||||
/@vue/compiler-ssr@3.4.18:
|
||||
resolution: {integrity: sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==}
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.4.18
|
||||
'@vue/shared': 3.4.18
|
||||
|
||||
/@vue/compiler-ssr@3.4.21:
|
||||
resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==}
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.4.21
|
||||
'@vue/shared': 3.4.21
|
||||
dev: true
|
||||
|
||||
/@vue/devtools-api@6.5.1:
|
||||
resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==}
|
||||
dev: true
|
||||
@@ -8528,12 +8641,25 @@ packages:
|
||||
dependencies:
|
||||
'@vue/shared': 3.4.18
|
||||
|
||||
/@vue/reactivity@3.4.21:
|
||||
resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
|
||||
dependencies:
|
||||
'@vue/shared': 3.4.21
|
||||
dev: true
|
||||
|
||||
/@vue/runtime-core@3.4.18:
|
||||
resolution: {integrity: sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==}
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.4.18
|
||||
'@vue/shared': 3.4.18
|
||||
|
||||
/@vue/runtime-core@3.4.21:
|
||||
resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==}
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.4.21
|
||||
'@vue/shared': 3.4.21
|
||||
dev: true
|
||||
|
||||
/@vue/runtime-dom@3.4.18:
|
||||
resolution: {integrity: sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==}
|
||||
dependencies:
|
||||
@@ -8541,6 +8667,14 @@ packages:
|
||||
'@vue/shared': 3.4.18
|
||||
csstype: 3.1.3
|
||||
|
||||
/@vue/runtime-dom@3.4.21:
|
||||
resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==}
|
||||
dependencies:
|
||||
'@vue/runtime-core': 3.4.21
|
||||
'@vue/shared': 3.4.21
|
||||
csstype: 3.1.3
|
||||
dev: true
|
||||
|
||||
/@vue/server-renderer@3.4.18(vue@3.4.18):
|
||||
resolution: {integrity: sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==}
|
||||
peerDependencies:
|
||||
@@ -8550,9 +8684,23 @@ packages:
|
||||
'@vue/shared': 3.4.18
|
||||
vue: 3.4.18(typescript@4.9.5)
|
||||
|
||||
/@vue/server-renderer@3.4.21(vue@3.4.21):
|
||||
resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==}
|
||||
peerDependencies:
|
||||
vue: 3.4.21
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.4.21
|
||||
'@vue/shared': 3.4.21
|
||||
vue: 3.4.21(typescript@4.9.5)
|
||||
dev: true
|
||||
|
||||
/@vue/shared@3.4.18:
|
||||
resolution: {integrity: sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q==}
|
||||
|
||||
/@vue/shared@3.4.21:
|
||||
resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
|
||||
dev: true
|
||||
|
||||
/@vue/test-utils@1.3.0(vue-template-compiler@2.7.14)(vue@2.7.14):
|
||||
resolution: {integrity: sha512-Xk2Xiyj2k5dFb8eYUKkcN9PzqZSppTlx7LaQWBbdA8tqh3jHr/KHX2/YLhNFc/xwDrgeLybqd+4ZCPJSGPIqeA==}
|
||||
peerDependencies:
|
||||
@@ -8566,18 +8714,11 @@ packages:
|
||||
vue-template-compiler: 2.7.14(vue@2.7.14)
|
||||
dev: true
|
||||
|
||||
/@vue/test-utils@2.4.3(vue@3.4.18):
|
||||
resolution: {integrity: sha512-F4K7mF+ad++VlTrxMJVRnenKSJmO6fkQt2wpRDiKDesQMkfpniGWsqEi/JevxGBo2qEkwwjvTUAoiGJLNx++CA==}
|
||||
peerDependencies:
|
||||
'@vue/server-renderer': ^3.0.1
|
||||
vue: ^3.0.1
|
||||
peerDependenciesMeta:
|
||||
'@vue/server-renderer':
|
||||
optional: true
|
||||
/@vue/test-utils@2.4.5:
|
||||
resolution: {integrity: sha512-oo2u7vktOyKUked36R93NB7mg2B+N7Plr8lxp2JBGwr18ch6EggFjixSCdIVVLkT6Qr0z359Xvnafc9dcKyDUg==}
|
||||
dependencies:
|
||||
js-beautify: 1.14.9
|
||||
vue: 3.4.18(typescript@4.9.5)
|
||||
vue-component-type-helpers: 1.8.27
|
||||
vue-component-type-helpers: 2.0.7
|
||||
dev: true
|
||||
|
||||
/@vueuse/components@10.7.2(vue@3.4.18):
|
||||
@@ -9211,13 +9352,6 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/array-buffer-byte-length@1.0.0:
|
||||
resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
|
||||
dependencies:
|
||||
call-bind: 1.0.6
|
||||
is-array-buffer: 3.0.4
|
||||
dev: true
|
||||
|
||||
/array-buffer-byte-length@1.0.1:
|
||||
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -11013,12 +11147,12 @@ packages:
|
||||
/deep-equal@2.2.2:
|
||||
resolution: {integrity: sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==}
|
||||
dependencies:
|
||||
array-buffer-byte-length: 1.0.0
|
||||
array-buffer-byte-length: 1.0.1
|
||||
call-bind: 1.0.6
|
||||
es-get-iterator: 1.1.3
|
||||
get-intrinsic: 1.2.4
|
||||
is-arguments: 1.1.1
|
||||
is-array-buffer: 3.0.2
|
||||
is-array-buffer: 3.0.4
|
||||
is-date-object: 1.0.5
|
||||
is-regex: 1.1.4
|
||||
is-shared-array-buffer: 1.0.2
|
||||
@@ -11026,11 +11160,11 @@ packages:
|
||||
object-is: 1.1.5
|
||||
object-keys: 1.1.1
|
||||
object.assign: 4.1.4
|
||||
regexp.prototype.flags: 1.5.0
|
||||
regexp.prototype.flags: 1.5.1
|
||||
side-channel: 1.0.4
|
||||
which-boxed-primitive: 1.0.2
|
||||
which-collection: 1.0.1
|
||||
which-typed-array: 1.1.11
|
||||
which-typed-array: 1.1.14
|
||||
dev: true
|
||||
|
||||
/deep-is@0.1.4:
|
||||
@@ -14156,14 +14290,6 @@ packages:
|
||||
has-tostringtag: 1.0.2
|
||||
dev: true
|
||||
|
||||
/is-array-buffer@3.0.2:
|
||||
resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
|
||||
dependencies:
|
||||
call-bind: 1.0.6
|
||||
get-intrinsic: 1.2.4
|
||||
is-typed-array: 1.1.12
|
||||
dev: true
|
||||
|
||||
/is-array-buffer@3.0.4:
|
||||
resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -14763,6 +14889,10 @@ packages:
|
||||
/js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
/js-tokens@8.0.3:
|
||||
resolution: {integrity: sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==}
|
||||
dev: true
|
||||
|
||||
/js-yaml@3.14.1:
|
||||
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
|
||||
hasBin: true
|
||||
@@ -18754,15 +18884,6 @@ packages:
|
||||
resolution: {integrity: sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==}
|
||||
dev: true
|
||||
|
||||
/regexp.prototype.flags@1.5.0:
|
||||
resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
call-bind: 1.0.6
|
||||
define-properties: 1.2.1
|
||||
functions-have-names: 1.2.3
|
||||
dev: true
|
||||
|
||||
/regexp.prototype.flags@1.5.1:
|
||||
resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -20217,6 +20338,12 @@ packages:
|
||||
acorn: 8.11.3
|
||||
dev: true
|
||||
|
||||
/strip-literal@2.0.0:
|
||||
resolution: {integrity: sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==}
|
||||
dependencies:
|
||||
js-tokens: 8.0.3
|
||||
dev: true
|
||||
|
||||
/strnum@1.0.5:
|
||||
resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
|
||||
dev: true
|
||||
@@ -21569,6 +21696,27 @@ packages:
|
||||
- terser
|
||||
dev: true
|
||||
|
||||
/vite-node@1.4.0:
|
||||
resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.3.4
|
||||
pathe: 1.1.2
|
||||
picocolors: 1.0.0
|
||||
vite: 5.0.12
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- less
|
||||
- lightningcss
|
||||
- sass
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
dev: true
|
||||
|
||||
/vite-plugin-singlefile@0.5.1(vite@5.0.13):
|
||||
resolution: {integrity: sha512-yA9lWd6bSet0Br4/s34YPNnVBlDl2MbxlHDRrLrBCncD7q+HO5GGsw29Ymp+ydZ3eb4UU2GECgX2MJZW+qnoeQ==}
|
||||
peerDependencies:
|
||||
@@ -21909,6 +22057,61 @@ packages:
|
||||
- terser
|
||||
dev: true
|
||||
|
||||
/vitest@1.4.0:
|
||||
resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@types/node': ^18.0.0 || >=20.0.0
|
||||
'@vitest/browser': 1.4.0
|
||||
'@vitest/ui': 1.4.0
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
peerDependenciesMeta:
|
||||
'@edge-runtime/vm':
|
||||
optional: true
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitest/browser':
|
||||
optional: true
|
||||
'@vitest/ui':
|
||||
optional: true
|
||||
happy-dom:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@vitest/expect': 1.4.0
|
||||
'@vitest/runner': 1.4.0
|
||||
'@vitest/snapshot': 1.4.0
|
||||
'@vitest/spy': 1.4.0
|
||||
'@vitest/utils': 1.4.0
|
||||
acorn-walk: 8.3.2
|
||||
chai: 4.4.1
|
||||
debug: 4.3.4
|
||||
execa: 8.0.1
|
||||
local-pkg: 0.5.0
|
||||
magic-string: 0.30.7
|
||||
pathe: 1.1.2
|
||||
picocolors: 1.0.0
|
||||
std-env: 3.7.0
|
||||
strip-literal: 2.0.0
|
||||
tinybench: 2.6.0
|
||||
tinypool: 0.8.2
|
||||
vite: 5.0.12
|
||||
vite-node: 1.4.0
|
||||
why-is-node-running: 2.2.2
|
||||
transitivePeerDependencies:
|
||||
- less
|
||||
- lightningcss
|
||||
- sass
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
dev: true
|
||||
|
||||
/vlq@1.0.1:
|
||||
resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==}
|
||||
dev: true
|
||||
@@ -21918,8 +22121,8 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/vue-component-type-helpers@1.8.27:
|
||||
resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==}
|
||||
/vue-component-type-helpers@2.0.7:
|
||||
resolution: {integrity: sha512-7e12Evdll7JcTIocojgnCgwocX4WzIYStGClBQ+QuWPinZo/vQolv2EMq4a3lg16TKfwWafLimG77bxb56UauA==}
|
||||
dev: true
|
||||
|
||||
/vue-demi@0.14.5(vue@3.4.18):
|
||||
@@ -21983,6 +22186,22 @@ packages:
|
||||
'@vue/shared': 3.4.18
|
||||
typescript: 4.9.5
|
||||
|
||||
/vue@3.4.21(typescript@4.9.5):
|
||||
resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.4.21
|
||||
'@vue/compiler-sfc': 3.4.21
|
||||
'@vue/runtime-dom': 3.4.21
|
||||
'@vue/server-renderer': 3.4.21(vue@3.4.21)
|
||||
'@vue/shared': 3.4.21
|
||||
typescript: 4.9.5
|
||||
dev: true
|
||||
|
||||
/w3c-keyname@2.2.8:
|
||||
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
|
||||
dev: false
|
||||
@@ -22243,17 +22462,6 @@ packages:
|
||||
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
||||
dev: true
|
||||
|
||||
/which-typed-array@1.1.11:
|
||||
resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
available-typed-arrays: 1.0.6
|
||||
call-bind: 1.0.6
|
||||
for-each: 0.3.3
|
||||
gopd: 1.0.1
|
||||
has-tostringtag: 1.0.2
|
||||
dev: true
|
||||
|
||||
/which-typed-array@1.1.14:
|
||||
resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node ./main.mjs"
|
||||
"start": "node ./cli.mjs"
|
||||
},
|
||||
"bin": {
|
||||
"build-icons": "./main.mjs"
|
||||
"build-icons": "./cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
|
||||
Reference in New Issue
Block a user