Skip to content

Commit

Permalink
feat(icon-component): Creating icons with iconNodes (#1997)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ericfennis authored Apr 26, 2024
1 parent 65deefa commit e50582e
Show file tree
Hide file tree
Showing 77 changed files with 1,706 additions and 429 deletions.
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions docs/guide/packages/lucide-angular.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 { }
```
20 changes: 20 additions & 0 deletions docs/guide/packages/lucide-preact.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 20 additions & 0 deletions docs/guide/packages/lucide-react-native.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 20 additions & 0 deletions docs/guide/packages/lucide-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 20 additions & 0 deletions docs/guide/packages/lucide-solid.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 21 additions & 0 deletions docs/guide/packages/lucide-svelte.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
30 changes: 26 additions & 4 deletions docs/guide/packages/lucide-vue-next.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
15 changes: 15 additions & 0 deletions docs/guide/packages/lucide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 50 additions & 0 deletions packages/lucide-preact/src/Icon.ts
Original file line number Diff line number Diff line change
@@ -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;
47 changes: 13 additions & 34 deletions packages/lucide-preact/src/createLucideIcon.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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}`;
Expand Down
3 changes: 3 additions & 0 deletions packages/lucide-preact/src/lucide-preact.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions packages/lucide-preact/src/types.ts
Original file line number Diff line number Diff line change
@@ -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>;
Loading

0 comments on commit e50582e

Please sign in to comment.