Compare commits

..

43 Commits

Author SHA1 Message Date
Jakob Guddas
a8f578fa8b fix: fixed async migration issue in generate changed icons comment markup 2025-02-20 21:55:53 +01:00
Eric Fennis
97f214934d refactor(scripts): Formatting in readAllMetadata 2025-02-19 14:32:10 +01:00
Eric Fennis
34dfc63ce2 refactor: Optimize readAllMetadata function to use Promise.all for concurrent metadata reading 2025-02-19 13:55:31 +01:00
Eric Fennis
b46927e510 fix(lucide-react): Revert exports property package.json, fixing edge worker environments. (#2814)
* Revert exports prop

* Adjust export

* Adjust export path

* Add to gitignore

* Adjust build process

* Update .gitignore and add dynamicIconImports module

* Formatting
2025-02-19 11:33:08 +01:00
Eric Fennis
1828a392c8 fix: Add await to checkIconsAndCategories script 2025-02-19 11:19:53 +01:00
dependabot[bot]
3ab6c373a0 build(deps-dev): bump vite from 5.4.12 to 5.4.13 (#2798)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.12 to 5.4.13.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.13/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.13/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-14 08:53:09 +01:00
dependabot[bot]
ba2c4b526f build(deps-dev): bump vite from 5.1.8 to 5.4.12 (#2786)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.1.8 to 5.4.12.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.12/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.12/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-12 09:41:52 +01:00
dependabot[bot]
bde3f01e0b build(deps): bump esbuild from 0.19.12 to 0.25.0 (#2791)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.19.12 to 0.25.0.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2024.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.19.12...v0.25.0)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-12 09:41:40 +01:00
Eric Fennis
50630b3aaf ci: Improve build speeds (#2778)
* Revert sync to async functions

* Replace more sync fs functions

* Format files

* Fix build svelte package
2025-02-10 14:13:52 +01:00
Jamie Law
e28426a871 feat(icons): Add gender icons (#2607)
* Add gender icons

* Rename `nonbinary.*` to `non-binary.*`

* Update icon categories

* Remove `neuter` icon

Pending additional use cases or significant demand

* Add more gender-related tags

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-02-07 15:45:34 +01:00
Eric Fennis
eb158561d3 fix(lucide): Support for iconNodes from other libs like Lab (#2752)
* Add support for iconNodes

* formatting
2025-02-07 15:44:54 +01:00
realguse
410ae434fa docs(readme): improve packages table (#2755) 2025-02-07 15:31:21 +01:00
Ikko Eltociear Ashimine
0801b89e4d chore: fix typo (#2777)
* chore: update replaceElement.spec.js

retuns -> returns

* chore: update replaceElement.spec.js.snap

retuns -> returns
2025-02-07 15:29:29 +01:00
Eric Fennis
a1d17eedc9 fix: Redirect aliases site (#2769)
* Fix redirects site

* Remove console.log
2025-02-06 15:39:07 +01:00
Eric Fennis
7481dd0a3f format(license): add missing semicolon in license script 2025-01-24 16:54:10 +01:00
Eric Fennis
f9e93824f1 format package.svg 2025-01-24 14:34:07 +01:00
Abdalrhman Almarakeby
4ba4cf2f35 feat(cog-icon): update tags to add more relevant keywords (#2748) 2025-01-24 14:32:55 +01:00
Eric Fennis
6b29716aa9 Hide add if script is blocked (#2750) 2025-01-24 14:28:54 +01:00
Jakob Guddas
97bbe1d6b2 fix(icons): changed expand icon (#2677)
* Updated icons/expand.svg

* Updated icons/expand.json
2025-01-23 16:54:06 +01:00
Abdalrhman Almarakeby
608da04bdf fix(bolt-icon): correct "contruction" typo in tags to "construction" (#2749) 2025-01-23 16:25:28 +01:00
ケイラ
961404d5cc replace keyof ReactSVG with SVGElementType (#2668)
* replace `keyof ReactSVG` with `SVGElementType`

* define `SVGElementType` locally

* 🧹

* update comments

* Format types.ts

* Update types.ts

* Update types.ts

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-01-18 16:35:52 +01:00
Sebastian Aarnio
f3100b8af1 fix(icons): update package.svg to be consistent with other package-derived icons (#2706) 2025-01-17 16:31:05 +01:00
Footages
0df4e2991c feat(icons): added battery-plus icon (#2693)
* Added icons/battery-plus.svg

* Added icons/battery-plus.json

* Updated icons/battery-plus.svg

* Updated icons/battery-plus.json

* Update battery-plus.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-01-17 12:10:28 +01:00
Sean
2b8242fa14 feat(icons): added map-plus icon (#2697)
* Added icons/map-plus.svg

* Added icons/map-plus.json
2025-01-17 12:09:02 +01:00
Lettnald
27c667556b Make sure license ends up in SvelteKit bundle (#2728) 2025-01-17 09:13:47 +01:00
Eric Fennis
31c3fefc17 fix(lucide-react) Adds type module in package.json (#2731)
* Adds type module in package.json

* fix typo
2025-01-13 10:58:15 +01:00
Yoh
389fed8770 feat(icons): adds triangle-dashed icon (#2652)
* feat(icons): adds triangle-dashed icon

* Format triangle-dashed.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-01-10 14:36:22 +01:00
Eric Fennis
58c2e108c3 feat(lucide-react): Add DynamicIcon component (#2686)
* Adding the DynamicIcon component

* Fix imports

* Add docs

* Formatting

* Fix use client in output rollup

* revert changes

* Fix formatting

* Revert changes in icons directory

* Revert time command

* update exports
2025-01-10 14:35:28 +01:00
Akshay Memane
d5fe5a0ef4 feat(icons): Add house-wifi icon (#2723)
* feat(icons): Add home-wifi icon

* updated icon

* updated icon as per guidelines

* updated icon name

* updated contributors
2025-01-10 11:52:43 +01:00
Axel
db30ab89f4 Added city tag to building-2.json (#2698)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-01-06 14:14:46 +01:00
Julien Zapata Duque
fa7120cbe0 fix(site): use lab icon name in vue and svelte examples (#2715) 2025-01-06 14:12:20 +01:00
Eric Fennis
419d47019c fix(scripts): Fix auto formatting script (#2712)
* Fix ./scripts/generateNextJSAliases.mjs

* Fix generateChangeIconsMarkup scripts
2025-01-06 10:42:53 +01:00
Jakob Guddas
655111e4aa fix(icons): changed rat icon (#2692)
* Updated icons/rat.svg

* Updated icons/rat.svg

* Updated icons/rat.svg
2025-01-02 17:24:58 +01:00
Karsa
ea0ac2f92b feat(icons): upgrade snowflake icons to match real life (#2610)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2024-12-20 10:36:02 +01:00
Akseli Palmer
1a5ee439ef Fixed typo in License section of Docs (#2699)
The phrase "personally use" is grammatically incorrect. The correct term is "personal use" because "use" is a noun in this context, and "personal" is the adjective that modifies it.
2024-12-20 10:24:44 +01:00
realguse
5947ca82d5 chore(metadata): add more tags to trophy icon (#2664)
* feat: add more tags to `trophy` icon

* chore: add contributor

* revert: add contributor

Refs: cd99369
2024-12-19 22:43:36 +01:00
dependabot[bot]
edf46adc9e build(deps): bump vite from 4.4.9 to 5.1.8 (#2685)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.4.9 to 5.1.8.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.1.8/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.1.8/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2024-12-19 10:44:47 +01:00
Rodrigo Mesquita
94782f53c1 Fix heading on guide index (#2670)
* Fix heading on guide index

Fixes the heading level of a section on the index page of the guide.

* Update docs/guide/index.md

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2024-12-11 10:45:55 +01:00
Kyle Angelo Galendez
79bbfa958e chore(metadata): add more tags to rotate-ccw icon metadata (#2658)
* feat(icons): add more tags

This close #2602

* Update icons/rotate-ccw.json

Add suggested by @realguse

* Add contributor

* Removing non-icon-contributors
2024-12-11 10:36:51 +01:00
Rik Smale
970fc3d4be fix(lucide-react): support React 19 (#2666) 2024-12-11 09:54:28 +01:00
Jakob Guddas
f12b0de177 feat(icons): added waves-ladder icon (#2529)
* Added icons/ladder-over-wave.svg

* Added icons/ladder-over-wave.json

* Rename ladder-over-wave.json to waves-ladder.json

* Rename ladder-over-wave.svg to waves-ladder.svg

* Update icons/waves-ladder.json

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2024-12-05 13:18:26 +01:00
Jakob Guddas
67cbce66e9 Updated icons/book-dashed.svg (#2399)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2024-12-05 12:56:32 +01:00
Jakob Guddas
b927275150 feat(icons): added scan-heart icon (#2385)
* Added icons/scan-heart.svg

* Added icons/scan-heart.json

* Updated icons/scan-heart.json

* Updated icons/scan-heart.svg

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2024-12-05 12:56:02 +01:00
126 changed files with 2065 additions and 665 deletions

6
.gitignore vendored
View File

@@ -20,9 +20,13 @@ packages/**/src/aliases/*.ts
packages/**/src/aliases.ts
!packages/**/src/aliases/index.ts
packages/**/src/dynamicIconImports.ts
packages/**/DynamicIcon.d.ts
packages/**/dynamicIconImports.js
packages/**/dynamicIconImports.d.ts
packages/**/dynamicIconImports.js.map
packages/**/dynamic.d.ts
packages/**/dynamic.mjs.map
packages/**/dynamic.mjs
packages/**/LICENSE
categories.json
tags.json
@@ -41,3 +45,5 @@ docs/.vitepress/data/iconDetails
docs/.vitepress/data/relatedIcons.json
docs/.vercel
docs/.nitro
.gitignore

View File

@@ -30,17 +30,17 @@ Lucide is an open-source icon library that provides 1000+ vector (svg) files for
## Packages
| | Package | Version & Downloads | Links |
| --- | --- | --- | --- |
| <img src="https://lucide.dev/framework-logos/js.svg" alt="JS logo" width="48"> | `lucide` | [![npm](https://img.shields.io/npm/v/lucide)](https://www.npmjs.com/package/lucide) ![NPM Downloads](https://img.shields.io/npm/dw/lucide) | [Docs](https://lucide.dev/guide/packages/lucide) [Source](./packages/lucide) |
| <img src="https://lucide.dev/framework-logos/react.svg" alt="React logo" width="48"> | `lucide-react` | [![npm](https://img.shields.io/npm/v/lucide-react)](https://www.npmjs.com/package/lucide-react) ![NPM Downloads](https://img.shields.io/npm/dw/lucide-react) | [Docs](https://lucide.dev/guide/packages/lucide-react) [Source](./packages/lucide-react) |
| <img src="https://lucide.dev/framework-logos/vue.svg" alt="Vue logo" width="48"> | `lucide-vue-next` | [![npm](https://img.shields.io/npm/v/lucide-vue-next)](https://www.npmjs.com/package/lucide-vue-next) ![NPM Downloads](https://img.shields.io/npm/dw/lucide-vue-next) | [Docs](https://lucide.dev/guide/packages/lucide-vue-next) [Source](./packages/lucide-vue-next) |
| <img src="https://lucide.dev/framework-logos/svelte.svg" alt="Svelte logo" width="48"> | `lucide-svelte` | [![npm](https://img.shields.io/npm/v/lucide-svelte)](https://www.npmjs.com/package/lucide-svelte) ![NPM Downloads](https://img.shields.io/npm/dw/lucide-svelte) | [Docs](https://lucide.dev/guide/packages/lucide-svelte) [Source](./packages/lucide-svelte) |
| <img src="https://lucide.dev/framework-logos/solid.svg" alt="Solid logo" width="48"> | `lucide-solid` | [![npm](https://img.shields.io/npm/v/lucide-solid)](https://www.npmjs.com/package/lucide-solid) ![NPM Downloads](https://img.shields.io/npm/dw/lucide-solid) | [Docs](https://lucide.dev/guide/packages/lucide-solid) [Source](./packages/lucide-solid) |
| <img src="https://lucide.dev/framework-logos/preact.svg" alt="Preact logo" width="48"> | `lucide-preact` | [![npm](https://img.shields.io/npm/v/lucide-preact)](https://www.npmjs.com/package/lucide-preact) ![NPM Downloads](https://img.shields.io/npm/dw/lucide-preact) | [Docs](https://lucide.dev/guide/packages/lucide-preact) [Source](./packages/lucide-preact) |
| <img src="https://lucide.dev/framework-logos/react-native.svg" alt="React Native logo" width="48"> | `lucide-react-native` | [![npm](https://img.shields.io/npm/v/lucide-react-native)](https://www.npmjs.com/package/lucide-react-native) ![NPM Downloads](https://img.shields.io/npm/dw/lucide-react-native) | [Docs](https://lucide.dev/guide/packages/lucide-react-native) [Source](./packages/lucide-react-native) |
| <img src="https://lucide.dev/framework-logos/angular.svg" alt="Angular logo" width="48"> | `lucide-angular` | [![npm](https://img.shields.io/npm/v/lucide-angular)](https://www.npmjs.com/package/lucide-angular) ![NPM Downloads](https://img.shields.io/npm/dw/lucide-angular) | [Docs](https://lucide.dev/guide/packages/lucide-angular) [Source](./packages/lucide-angular) |
| <img src="https://lucide.dev/framework-logos/svg.svg" alt="SVG logo" width="48"> | `lucide-static` | [![npm](https://img.shields.io/npm/v/lucide-static)](https://www.npmjs.com/package/lucide-static) ![NPM Downloads](https://img.shields.io/npm/dw/lucide-static) | [Docs](https://lucide.dev/guide/packages/lucide-static) [Source](./packages/lucide-static) |
| Logo | Package | Version | Downloads | Links |
| ---- | ------- | ------- | --------- | ----- |
| <img src="https://lucide.dev/framework-logos/js.svg" alt="JS logo" width="48"> | **`lucide`** | [![npm](https://img.shields.io/npm/v/lucide)](https://www.npmjs.com/package/lucide) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide) | [Docs](https://lucide.dev/guide/packages/lucide) · [Source](./packages/lucide) |
| <img src="https://lucide.dev/framework-logos/react.svg" alt="React logo" width="48"> | **`lucide-react`** | [![npm](https://img.shields.io/npm/v/lucide-react)](https://www.npmjs.com/package/lucide-react) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-react) | [Docs](https://lucide.dev/guide/packages/lucide-react) · [Source](./packages/lucide-react) |
| <img src="https://lucide.dev/framework-logos/vue.svg" alt="Vue logo" width="48"> | **`lucide-vue-next`** | [![npm](https://img.shields.io/npm/v/lucide-vue-next)](https://www.npmjs.com/package/lucide-vue-next) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-vue-next) | [Docs](https://lucide.dev/guide/packages/lucide-vue-next) · [Source](./packages/lucide-vue-next) |
| <img src="https://lucide.dev/framework-logos/svelte.svg" alt="Svelte logo" width="48"> | **`lucide-svelte`** | [![npm](https://img.shields.io/npm/v/lucide-svelte)](https://www.npmjs.com/package/lucide-svelte) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-svelte) | [Docs](https://lucide.dev/guide/packages/lucide-svelte) · [Source](./packages/lucide-svelte) |
| <img src="https://lucide.dev/framework-logos/solid.svg" alt="Solid logo" width="48"> | **`lucide-solid`** | [![npm](https://img.shields.io/npm/v/lucide-solid)](https://www.npmjs.com/package/lucide-solid) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-solid) | [Docs](https://lucide.dev/guide/packages/lucide-solid) · [Source](./packages/lucide-solid) |
| <img src="https://lucide.dev/framework-logos/preact.svg" alt="Preact logo" width="48"> | **`lucide-preact`** | [![npm](https://img.shields.io/npm/v/lucide-preact)](https://www.npmjs.com/package/lucide-preact) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-preact) | [Docs](https://lucide.dev/guide/packages/lucide-preact) · [Source](./packages/lucide-preact) |
| <img src="https://lucide.dev/framework-logos/react-native.svg" alt="React Native logo" width="48"> | **`lucide-react-native`** | [![npm](https://img.shields.io/npm/v/lucide-react-native)](https://www.npmjs.com/package/lucide-react-native) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-react-native) | [Docs](https://lucide.dev/guide/packages/lucide-react-native) · [Source](./packages/lucide-react-native) |
| <img src="https://lucide.dev/framework-logos/angular.svg" alt="Angular logo" width="48"> | **`lucide-angular`** | [![npm](https://img.shields.io/npm/v/lucide-angular)](https://www.npmjs.com/package/lucide-angular) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-angular) | [Docs](https://lucide.dev/guide/packages/lucide-angular) · [Source](./packages/lucide-angular) |
| <img src="https://lucide.dev/framework-logos/svg.svg" alt="SVG logo" width="48"> | **`lucide-static`** | [![npm](https://img.shields.io/npm/v/lucide-static)](https://www.npmjs.com/package/lucide-static) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-static) | [Docs](https://lucide.dev/guide/packages/lucide-static) · [Source](./packages/lucide-static) |
### Figma
@@ -62,7 +62,7 @@ Join the community on our [Discord](https://discord.gg/EH6nSts) server!
## License
Lucide is totally free for commercial use and personally use, this software is licensed under the [ISC License](https://github.com/lucide-icons/lucide/blob/main/LICENSE).
Lucide is totally free for commercial use and personal use, this software is licensed under the [ISC License](https://github.com/lucide-icons/lucide/blob/main/LICENSE).
## Credits

View File

@@ -61,7 +61,7 @@
},
{
"name": "finance",
"title": "Money"
"title": "Finance"
},
{
"name": "food-beverage",
@@ -85,7 +85,7 @@
},
{
"name": "math",
"title": "Math"
"title": "Mathematics"
},
{
"name": "medical",

View File

@@ -49,7 +49,7 @@ import { $CamelCase } from '@lucide/lab';
</script>
<template>
<Icon :iconNode="burger" />
<Icon :iconNode="$CamelCase" />
</template>
`,
},
@@ -61,7 +61,7 @@ import { Icon } from 'lucide-svelte';
import { $CamelCase } from '@lucide/lab';
</script>
<Icon iconNode={burger} />
<Icon iconNode={$CamelCase} />
`,
},
{

View File

@@ -5,22 +5,32 @@ import IconButton from '../base/IconButton.vue';
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue'
import { x } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import { onMounted, ref } from 'vue';
const { theme } = useData()
const showAd = useSessionStorage('show-carbon-ads',true)
const showAd = useSessionStorage('show-carbon-ads', true)
const carbonLoaded = ref(true)
defineProps<{
drawerOpen: boolean
}>()
const CloseIcon = createLucideIcon('Close', x)
onMounted(() => {
setTimeout(() => {
if (window?._carbonads == null) {
carbonLoaded.value = false
}
}, 5000)
})
</script>
<template>
<div
:class="{
'drawer-open': drawerOpen,
'hide-ad': !showAd
'hide-ad': !(showAd && carbonLoaded)
}"
class="floating-ad"
v-if="theme.carbonAds"

View File

@@ -12,7 +12,8 @@ Lucide is an open-source icon library that provides 1000+ vector (svg) files for
## Available Icons
Lucide contains icons with different variants and states, allowing users to choose the most suitable icon for their needs. And if a desired icon isn't available yet, users can open a design request, and the Lucide community contributors will help provide new icons. With more icons to choose from, users have more options to work with in their projects.
Complete Set of Icons
### Complete Set of Icons
As new applications with specific features arise, Lucide aims to provide a complete set of icons for every project. The community follows a set of design rules when designing new icons. These rules maintain standards for the icons, such as recognizability, consistency in style, and readability at all sizes. While creativity is valued in new icons, recognizable design conventions are important to ensure that the icons are easily identifiable by users.

View File

@@ -85,108 +85,21 @@ const App = () => (
);
```
## One generic icon component
## Dynamic Icon Component
It is possible to create one generic icon component to load icons, but it is not recommended.
Since it is importing all icons during build. This increases build time and the different modules it will create.
::: danger
The example below imports all ES Modules, so exercise caution when using it. Importing all icons will significantly increase the build size of the application, negatively affecting its performance. This is especially important to keep in mind when using bundlers like `Webpack`, `Rollup`, or `Vite`.
`DynamicIcon` is useful for applications that want to show icons dynamically by icon name. For example, when using a content management system with where icon names are stored in a database.
This is not the case for the latest NextJS, because it uses server side rendering. The icons will be streamed to the client when needed. For NextJS with Dynamic Imports, see [dynamic imports](#nextjs-example) section for more information.
:::
For static use cases, it is recommended to import the icons directly.
### Icon Component Example
The same props can be passed to adjust the icon appearance. The `name` prop is required to load the correct icon.
```jsx
import { icons } from 'lucide-react';
import { DynamicIcon } from 'lucide-react/dynamic';
const Icon = ({ name, color, size }) => {
const LucideIcon = icons[name];
return <LucideIcon color={color} size={size} />;
};
export default Icon;
```
#### Using the Icon Component
```jsx
import Icon from './Icon';
const App = () => {
return <Icon name="Home" />;
};
export default App;
```
#### With Dynamic Imports
Lucide react exports a dynamic import map `dynamicIconImports`, which is useful for applications that want to show icons dynamically by icon name. For example, when using a content management system with where icon names are stored in a database.
When using client side rendering, it will fetch the icon component when it's needed. This will reduce the initial bundle size.
The keys of the dynamic import map are the lucide original icon names (kebab case).
Example with React suspense:
```tsx
import React, { lazy, Suspense } from 'react';
import { LucideProps } from 'lucide-react';
import dynamicIconImports from 'lucide-react/dynamicIconImports';
const fallback = <div style={{ background: '#ddd', width: 24, height: 24 }}/>
interface IconProps extends Omit<LucideProps, 'ref'> {
name: keyof typeof dynamicIconImports;
}
const Icon = ({ name, ...props }: IconProps) => {
const LucideIcon = lazy(dynamicIconImports[name]);
return (
<Suspense fallback={fallback}>
<LucideIcon {...props} />
</Suspense>
);
}
export default Icon
```
##### NextJS Example
In NextJS, [the dynamic function](https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#nextdynamic) can be used to dynamically load the icon component.
To make dynamic imports work with NextJS, you need to add `lucide-react` to the [`transpilePackages`](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages) option in your `next.config.js` like this:
```js
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['lucide-react'] // add this
}
module.exports = nextConfig
```
You can then start using it:
```tsx
import dynamic from 'next/dynamic'
import { LucideProps } from 'lucide-react';
import dynamicIconImports from 'lucide-react/dynamicIconImports';
interface IconProps extends LucideProps {
name: keyof typeof dynamicIconImports;
}
const Icon = ({ name, ...props }: IconProps) => {
const LucideIcon = dynamic(dynamicIconImports[name])
return <LucideIcon {...props} />;
};
export default Icon;
const App = () => (
<DynamicIcon name="camera" color="red" size={48} />
);
```

View File

@@ -1,16 +1,17 @@
import fs from 'fs';
import fs from 'fs/promises';
import path from 'path';
const currentDir = process.cwd();
const dataDirectory = path.resolve(currentDir, '.vitepress/data');
const directory = path.join(process.cwd(), '../categories');
function getAllCategoryFiles() {
const fileNames = fs.readdirSync(directory).filter((file) => path.extname(file) === '.json');
async function getAllCategoryFiles() {
const categoryDirectoryContents = await fs.readdir(directory);
const fileNames = categoryDirectoryContents.filter((file) => path.extname(file) === '.json');
return fileNames.map((fileName) => {
const categoryJSONReadPromises = fileNames.map(async (fileName) => {
const name = path.basename(fileName, '.json');
const fileContent = fs.readFileSync(path.join(directory, fileName), 'utf8');
const fileContent = await fs.readFile(path.join(directory, fileName), 'utf8');
const parsedFileContent = JSON.parse(fileContent);
@@ -19,14 +20,15 @@ function getAllCategoryFiles() {
title: parsedFileContent.title,
};
});
return Promise.all(categoryJSONReadPromises);
}
const categoriesFile = path.resolve(dataDirectory, `categoriesData.json`);
const categoriesData = getAllCategoryFiles();
const categoriesData = await getAllCategoryFiles();
fs.promises
.writeFile(categoriesFile, JSON.stringify(categoriesData, null, 2), 'utf-8')
fs.writeFile(categoriesFile, JSON.stringify(categoriesData, null, 2), 'utf-8')
.then(() => {
console.log('Successfully written categoriesData.json file');
})

View File

@@ -4,7 +4,7 @@ import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
const currentDir = process.cwd();
const ICONS_DIR = path.resolve(currentDir, '../icons');
const icons = readSvgDirectory(ICONS_DIR, '.json');
const icons = await readSvgDirectory(ICONS_DIR, '.json');
const iconDetailsDirectory = path.resolve(currentDir, '.vitepress/data', 'iconDetails');

View File

@@ -4,7 +4,7 @@ import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
const currentDir = process.cwd();
const ICONS_DIR = path.resolve(currentDir, '../icons');
const iconJsonFiles = readSvgDirectory(ICONS_DIR, '.json');
const iconJsonFiles = await readSvgDirectory(ICONS_DIR, '.json');
const location = path.resolve(currentDir, '.vitepress/data', 'iconMetaData.ts');

View File

@@ -5,8 +5,8 @@ import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
const currentDir = process.cwd();
const ICONS_DIR = path.resolve(currentDir, '../icons');
const svgFiles = readSvgDirectory(ICONS_DIR);
const icons = renderIconsObject(svgFiles, ICONS_DIR, true);
const svgFiles = await readSvgDirectory(ICONS_DIR);
const icons = await renderIconsObject(svgFiles, ICONS_DIR, true);
const iconNodesDirectory = path.resolve(currentDir, '.vitepress/data', 'iconNodes');
@@ -32,7 +32,7 @@ const writeIconFiles = Object.entries(icons).map(async ([iconName, { children }]
await fs.promises.writeFile(location, output, 'utf-8');
iconIndexFileImports.push(
`import ${toCamelCase(iconName)}Node from './${iconName}.node.json' assert { type: "json" };`,
`import ${toCamelCase(iconName)}Node from './${iconName}.node.json' with { type: "json" };`,
);
iconIndexFileExports.push(` ${toCamelCase(iconName)}Node as ${toCamelCase(iconName)},`);
iconIndexFileDefaultExports.push(` '${iconName}': ${toCamelCase(iconName)}Node,`);

View File

@@ -4,7 +4,7 @@ import { readSvgDirectory } from '@lucide/helpers';
const currentDir = process.cwd();
const ICONS_DIR = path.resolve(currentDir, '../icons');
const svgFiles = readSvgDirectory(ICONS_DIR, '.json');
const svgFiles = await readSvgDirectory(ICONS_DIR, '.json');
const location = path.resolve(currentDir, '.vitepress/data', 'relatedIcons.json');
@@ -18,9 +18,7 @@ const categoryWeight = 3;
const MAX_RELATED_ICONS = 4 * 17; // grid of 4x17 icons, = 68 icons
const arrayMatches = (a, b) => {
return a.filter((item) => b.includes(item)).length;
};
const arrayMatches = (a, b) => a.filter((item) => b.includes(item)).length;
const nameParts = (icon) =>
[
@@ -36,6 +34,7 @@ const getRelatedIcons = (currentIcon, icons) => {
nameWeight * arrayMatches(nameParts(item), nameParts(currentIcon)) +
categoryWeight * arrayMatches(item.categories ?? [], currentIcon.categories ?? []) +
tagWeight * arrayMatches(item.tags ?? [], currentIcon.tags ?? []);
return icons
.filter((i) => i.name !== currentIcon.name)
.map((icon) => ({ icon, similarity: iconSimilarity(icon) }))
@@ -46,7 +45,8 @@ const getRelatedIcons = (currentIcon, icons) => {
};
const iconsMetaDataPromises = svgFiles.map(async (iconName) => {
const metaData = JSON.parse(fs.readFileSync(`../icons/${iconName}`));
const metaDataFileContent = await fs.promises.readFile(`../icons/${iconName}`);
const metaData = JSON.parse(metaDataFileContent);
const name = iconName.replace('.json', '');

View File

@@ -11,7 +11,7 @@ const git = simpleGit();
const currentDir = process.cwd();
const ICONS_DIR = path.resolve(currentDir, '../icons');
const iconJsonFiles = readSvgDirectory(ICONS_DIR, '.json');
const iconJsonFiles = await readSvgDirectory(ICONS_DIR, '.json');
const location = path.resolve(currentDir, '.vitepress/data', 'releaseMetaData.json');
const releaseMetaDataDirectory = path.resolve(currentDir, '.vitepress/data', 'releaseMetadata');

View File

@@ -6,7 +6,7 @@ import { getCurrentDirPath } from '@lucide/helpers';
const currentDir = process.cwd();
const scriptDir = getCurrentDirPath(import.meta.url);
const iconMetaData = await getIconMetaData(path.resolve(scriptDir, '../icons'));
const iconMetaData = await getIconMetaData(path.resolve(scriptDir, '../../icons'));
const iconAliasesRedirectRoutes = Object.entries(iconMetaData)
.filter(([, { aliases }]) => aliases?.length)
@@ -16,7 +16,7 @@ const iconAliasesRedirectRoutes = Object.entries(iconMetaData)
const aliasRouteMatches = aliases.join('|');
return {
src: `/icons/(${aliasRouteMatches})`,
src: `/icons/${aliasRouteMatches}`,
status: 302,
headers: {
Location: `/icons/${iconName}`,
@@ -44,4 +44,4 @@ const output = JSON.stringify(vercelRouteConfig, null, 2);
const vercelOutputJSON = path.resolve(currentDir, '.vercel/output/config.json');
fs.writeFileSync(vercelOutputJSON, output, 'utf-8');
await fs.promises.writeFile(vercelOutputJSON, output, 'utf-8');

29
icons/battery-plus.json Normal file
View File

@@ -0,0 +1,29 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"ericfennis",
"jguddas",
"johnletey",
"Footagesus"
],
"tags": [
"power",
"electricity",
"energy",
"accumulator",
"charge",
"plus",
"economy",
"health",
"add",
"new",
"maximum",
"upgrade",
"extra",
"+"
],
"categories": [
"devices"
]
}

17
icons/battery-plus.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10 9v6" />
<path d="M13.5 7H16a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-2.5" />
<path d="M22 11v2" />
<path d="M6.5 17H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h2.5" />
<path d="M7 12h6" />
</svg>

After

Width:  |  Height:  |  Size: 398 B

View File

@@ -14,7 +14,7 @@
"diy",
"fixed",
"build",
"contruction",
"construction",
"parts"
],
"categories": [

View File

@@ -9,15 +9,15 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 17h2" />
<path d="M12 22h2" />
<path d="M12 2h2" />
<path d="M18 22h1a1 1 0 0 0 1-1" />
<path d="M18 2h1a1 1 0 0 1 1 1v1" />
<path d="M20 15v2h-2" />
<path d="M20 8v3" />
<path d="M4 11V9" />
<path d="M4 19.5V15" />
<path d="M4 5v-.5A2.5 2.5 0 0 1 6.5 2H8" />
<path d="M12 17h1.5" />
<path d="M12 22h1.5" />
<path d="M12 2h1.5" />
<path d="M17.5 22H19a1 1 0 0 0 1-1" />
<path d="M17.5 2H19a1 1 0 0 1 1 1v1.5" />
<path d="M20 14v3h-2.5" />
<path d="M20 8.5V10" />
<path d="M4 10V8.5" />
<path d="M4 19.5V14" />
<path d="M4 4.5A2.5 2.5 0 0 1 6.5 2H8" />
<path d="M8 22H6.5a1 1 0 0 1 0-5H8" />
</svg>

Before

Width:  |  Height:  |  Size: 542 B

After

Width:  |  Height:  |  Size: 561 B

View File

@@ -12,7 +12,8 @@
"enterprise",
"skyscraper",
"organisation",
"organization"
"organization",
"city"
],
"categories": [
"account",

16
icons/circle-small.json Normal file
View File

@@ -0,0 +1,16 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jamiemlaw"
],
"tags": [
"shape",
"bullet",
"gender",
"genderless"
],
"categories": [
"shapes",
"medical"
]
}

13
icons/circle-small.svg Normal file
View File

@@ -0,0 +1,13 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="6" />
</svg>

After

Width:  |  Height:  |  Size: 243 B

View File

@@ -10,7 +10,13 @@
"cog",
"edit",
"gear",
"preferences"
"preferences",
"controls",
"configuration",
"fixed",
"build",
"construction",
"parts"
],
"categories": [
"account"

View File

@@ -2,13 +2,18 @@
"$schema": "../icon.schema.json",
"contributors": [
"mittalyashu",
"ericfennis"
"ericfennis",
"jguddas"
],
"tags": [
"scale",
"fullscreen"
"fullscreen",
"maximize",
"minimize",
"contract"
],
"categories": [
"text"
"text",
"arrows"
]
}

View File

@@ -9,8 +9,12 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m21 21-6-6m6 6v-4.8m0 4.8h-4.8" />
<path d="M3 16.2V21m0 0h4.8M3 21l6-6" />
<path d="M21 7.8V3m0 0h-4.8M21 3l-6 6" />
<path d="M3 7.8V3m0 0h4.8M3 3l6 6" />
<path d="m15 15 6 6" />
<path d="m15 9 6-6" />
<path d="M21 16.2V21h-4.8" />
<path d="M21 7.8V3h-4.8" />
<path d="M3 16.2V21h4.8" />
<path d="m3 21 6-6" />
<path d="M3 7.8V3h4.8" />
<path d="M9 9 3 3" />
</svg>

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 428 B

View File

@@ -26,5 +26,8 @@
"text",
"layout",
"math"
],
"aliases": [
"grid-2-x-2-check"
]
}

View File

@@ -26,5 +26,8 @@
"text",
"layout",
"math"
],
"aliases": [
"grid-2-x-2-x"
]
}

20
icons/house-wifi.json Normal file
View File

@@ -0,0 +1,20 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"akshaymemane",
"jguddas",
"karsa-mistmere"
],
"tags": [
"home",
"living",
"building",
"wifi",
"connectivity"
],
"categories": [
"home",
"buildings",
"connectivity"
]
}

16
icons/house-wifi.svg Normal file
View File

@@ -0,0 +1,16 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M9.5 13.866a4 4 0 0 1 5 .01" />
<path d="M12 17h.01" />
<path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<path d="M7 10.754a8 8 0 0 1 10 0" />
</svg>

After

Width:  |  Height:  |  Size: 442 B

20
icons/map-plus.json Normal file
View File

@@ -0,0 +1,20 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"karsa-mistmere",
"ericfennis",
"Seanw265"
],
"tags": [
"location",
"navigation",
"travel",
"new",
"add",
"create"
],
"categories": [
"navigation"
]
}

17
icons/map-plus.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m11 19-1.106-.552a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0l4.212 2.106a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619V12" />
<path d="M15 5.764V12" />
<path d="M18 15v6" />
<path d="M21 18h-6" />
<path d="M9 3.236v15" />
</svg>

After

Width:  |  Height:  |  Size: 513 B

14
icons/mars-stroke.json Normal file
View File

@@ -0,0 +1,14 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jamiemlaw"
],
"tags": [
"gender",
"androgyne",
"transgender"
],
"categories": [
"medical"
]
}

16
icons/mars-stroke.svg Normal file
View File

@@ -0,0 +1,16 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m14 6 4 4" />
<path d="M17 3h4v4" />
<path d="m21 3-7.75 7.75" />
<circle cx="9" cy="15" r="6" />
</svg>

After

Width:  |  Height:  |  Size: 323 B

18
icons/mars.json Normal file
View File

@@ -0,0 +1,18 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jguddas",
"jamiemlaw"
],
"tags": [
"gender",
"sex",
"male",
"masculine",
"man",
"boy"
],
"categories": [
"medical"
]
}

15
icons/mars.svg Normal file
View File

@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M16 3h5v5" />
<path d="m21 3-6.75 6.75" />
<circle cx="10" cy="14" r="6" />
</svg>

After

Width:  |  Height:  |  Size: 299 B

14
icons/non-binary.json Normal file
View File

@@ -0,0 +1,14 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jamiemlaw"
],
"tags": [
"gender",
"nonbinary",
"enby"
],
"categories": [
"medical"
]
}

16
icons/non-binary.svg Normal file
View File

@@ -0,0 +1,16 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 2v10" />
<path d="m9 4 6 4" />
<path d="m9 8 6-4" />
<circle cx="12" cy="17" r="5" />
</svg>

After

Width:  |  Height:  |  Size: 315 B

View File

@@ -6,7 +6,8 @@
"ericfennis",
"karsa-mistmere",
"danielbayley",
"jguddas"
"jguddas",
"sezze"
],
"tags": [
"box",

View File

@@ -11,6 +11,6 @@
>
<path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z" />
<path d="M12 22V12" />
<path d="m3.3 7 7.703 4.734a2 2 0 0 0 1.994 0L20.7 7" />
<polyline points="3.29 7 12 12 20.71 7" />
<path d="m7.5 4.27 9 5.15" />
</svg>

Before

Width:  |  Height:  |  Size: 460 B

After

Width:  |  Height:  |  Size: 446 B

View File

@@ -9,9 +9,9 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M17 5c0-1.7-1.3-3-3-3s-3 1.3-3 3c0 .8.3 1.5.8 2H11c-3.9 0-7 3.1-7 7c0 2.2 1.8 4 4 4" />
<path d="M16.8 3.9c.3-.3.6-.5 1-.7 1.5-.6 3.3.1 3.9 1.6.6 1.5-.1 3.3-1.6 3.9l1.6 2.8c.2.3.2.7.2 1-.2.8-.9 1.2-1.7 1.1 0 0-1.6-.3-2.7-.6H17c-1.7 0-3 1.3-3 3" />
<path d="M13.2 18a3 3 0 0 0-2.2-5" />
<path d="M13 22H4a2 2 0 0 1 0-4h12" />
<path d="M13.236 18a3 3 0 0 0-2.2-5" />
<path d="M16 9h.01" />
<path d="M16.82 3.94a3 3 0 1 1 3.237 4.868l1.815 2.587a1.5 1.5 0 0 1-1.5 2.1l-2.872-.453a3 3 0 0 0-3.5 3" />
<path d="M17 4.988a3 3 0 1 0-5.2 2.052A7 7 0 0 0 4 14.015 4 4 0 0 0 8 18" />
</svg>

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 506 B

View File

@@ -17,7 +17,12 @@
"rerun",
"refresh",
"backup",
"undo"
"undo",
"replay",
"redo",
"retry",
"rewind",
"reverse"
],
"categories": [
"arrows",

19
icons/scan-heart.json Normal file
View File

@@ -0,0 +1,19 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere",
"jguddas"
],
"tags": [
"health",
"heart rate",
"pulse",
"monitoring",
"healthiness",
"screening",
"dashed"
],
"categories": [
"medical"
]
}

17
icons/scan-heart.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M11.246 16.657a1 1 0 0 0 1.508 0l3.57-4.101A2.75 2.75 0 1 0 12 9.168a2.75 2.75 0 1 0-4.324 3.388z" />
<path d="M17 3h2a2 2 0 0 1 2 2v2" />
<path d="M21 17v2a2 2 0 0 1-2 2h-2" />
<path d="M3 7V5a2 2 0 0 1 2-2h2" />
<path d="M7 21H5a2 2 0 0 1-2-2v-2" />
</svg>

After

Width:  |  Height:  |  Size: 479 B

View File

@@ -1,6 +1,7 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere",
"lscheibel",
"ericfennis"
],

View File

@@ -9,10 +9,16 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="2" x2="22" y1="12" y2="12" />
<line x1="12" x2="12" y1="2" y2="22" />
<path d="m20 16-4-4 4-4" />
<path d="m4 8 4 4-4 4" />
<path d="m16 4-4 4-4-4" />
<path d="m8 20 4-4 4 4" />
<path d="m10 20-1.25-2.5L6 18" />
<path d="M10 4 8.75 6.5 6 6" />
<path d="m14 20 1.25-2.5L18 18" />
<path d="m14 4 1.25 2.5L18 6" />
<path d="m17 21-3-6h-4" />
<path d="m17 3-3 6 1.5 3" />
<path d="M2 12h6.5L10 9" />
<path d="m20 10-1.5 2 1.5 2" />
<path d="M22 12h-6.5L14 15" />
<path d="m4 10 1.5 2L4 14" />
<path d="m7 21 3-6-1.5-3" />
<path d="m7 3 3 6h4" />
</svg>

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 596 B

View File

@@ -9,15 +9,15 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10 9a3 3 0 1 0 0 6" />
<path d="M2 12h1" />
<path d="M14 21V3" />
<path d="M10 4V3" />
<path d="M10 21v-1" />
<path d="M10 4V3" />
<path d="M10 9a3 3 0 0 0 0 6" />
<path d="m14 20 1.25-2.5L18 18" />
<path d="m14 4 1.25 2.5L18 6" />
<path d="m17 21-3-6 1.5-3H22" />
<path d="m17 3-3 6 1.5 3" />
<path d="M2 12h1" />
<path d="m20 10-1.5 2 1.5 2" />
<path d="m3.64 18.36.7-.7" />
<path d="m4.34 6.34-.7-.7" />
<path d="M14 12h8" />
<path d="m17 4-3 3" />
<path d="m14 17 3 3" />
<path d="m21 15-3-3 3-3" />
</svg>

Before

Width:  |  Height:  |  Size: 507 B

After

Width:  |  Height:  |  Size: 550 B

View File

@@ -1,8 +1,8 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"ericfennis",
"karsa-mistmere"
"karsa-mistmere",
"ericfennis"
],
"tags": [
"temperature",

View File

@@ -9,10 +9,12 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M2 12h10" />
<path d="M9 4v16" />
<path d="m3 9 3 3-3 3" />
<path d="M12 6 9 9 6 6" />
<path d="m6 18 3-3 1.5 1.5" />
<path d="M20 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z" />
<path d="m10 20-1.25-2.5L6 18" />
<path d="M10 4 8.75 6.5 6 6" />
<path d="M10.585 15H10" />
<path d="M2 12h6.5L10 9" />
<path d="M20 14.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0z" />
<path d="m4 10 1.5 2L4 14" />
<path d="m7 21 3-6-1.5-3" />
<path d="m7 3 3 6h2" />
</svg>

Before

Width:  |  Height:  |  Size: 403 B

After

Width:  |  Height:  |  Size: 482 B

14
icons/transgender.json Normal file
View File

@@ -0,0 +1,14 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jamiemlaw"
],
"tags": [
"gender",
"inclusive"
],
"categories": [
"medical",
"accessibility"
]
}

20
icons/transgender.svg Normal file
View File

@@ -0,0 +1,20 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 16v6" />
<path d="M14 20h-4" />
<path d="M18 2h4v4" />
<path d="m2 2 7.17 7.17" />
<path d="M2 5.355V2h3.357" />
<path d="m22 2-7.17 7.17" />
<path d="M8 5 5 8" />
<circle cx="12" cy="12" r="4" />
</svg>

After

Width:  |  Height:  |  Size: 434 B

View File

@@ -0,0 +1,21 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"csandman",
"ericfennis",
"karsa-mistmere",
"Yohh"
],
"tags": [
"equilateral",
"delta",
"shape",
"pyramid",
"hierarchy",
"dashed"
],
"categories": [
"shapes"
]
}

21
icons/triangle-dashed.svg Normal file
View File

@@ -0,0 +1,21 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10.17 4.193a2 2 0 0 1 3.666.013" />
<path d="M14 21h2" />
<path d="m15.874 7.743 1 1.732" />
<path d="m18.849 12.952 1 1.732" />
<path d="M21.824 18.18a2 2 0 0 1-1.835 2.824" />
<path d="M4.024 21a2 2 0 0 1-1.839-2.839" />
<path d="m5.136 12.952-1 1.732" />
<path d="M8 21h2" />
<path d="m8.102 7.743-1 1.732" />
</svg>

After

Width:  |  Height:  |  Size: 549 B

View File

@@ -8,7 +8,10 @@
"sports",
"winner",
"achievement",
"award"
"award",
"champion",
"celebration",
"victory"
],
"categories": [
"sports",

16
icons/venus-and-mars.json Normal file
View File

@@ -0,0 +1,16 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jamiemlaw"
],
"tags": [
"gender",
"sex",
"intersex",
"androgynous",
"hermaphrodite"
],
"categories": [
"medical"
]
}

17
icons/venus-and-mars.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10 20h4" />
<path d="M12 16v6" />
<path d="M17 2h4v4" />
<path d="m21 2-5.46 5.46" />
<circle cx="12" cy="11" r="5" />
</svg>

After

Width:  |  Height:  |  Size: 347 B

18
icons/venus.json Normal file
View File

@@ -0,0 +1,18 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jguddas",
"jamiemlaw"
],
"tags": [
"gender",
"sex",
"female",
"feminine",
"woman",
"girl"
],
"categories": [
"medical"
]
}

15
icons/venus.svg Normal file
View File

@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 15v7" />
<path d="M9 19h6" />
<circle cx="12" cy="9" r="6" />
</svg>

After

Width:  |  Height:  |  Size: 289 B

22
icons/waves-ladder.json Normal file
View File

@@ -0,0 +1,22 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere"
],
"tags": [
"swimming",
"water",
"pool",
"lifeguard",
"ocean",
"🌊",
"🏊‍♂️",
"🏊‍♀️",
"🏊",
"🥽"
],
"categories": [
"sports",
"home"
]
}

17
icons/waves-ladder.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M19 5a2 2 0 0 0-2 2v11" />
<path d="M2 18c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1" />
<path d="M7 13h10" />
<path d="M7 9h10" />
<path d="M9 5a2 2 0 0 0-2 2v11" />
</svg>

After

Width:  |  Height:  |  Size: 434 B

View File

@@ -1,8 +1,15 @@
/* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
export default ({ componentName, iconName, children, getSvg, deprecated, deprecationReason }) => {
const svgContents = getSvg();
export default async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `\

View File

@@ -53,7 +53,7 @@
"rollup": "^4.22.4",
"rollup-plugin-dts": "^6.1.0",
"typescript": "^5.3.3",
"vite": "5.1.8",
"vite": "5.4.13",
"vitest": "^1.1.1"
},
"peerDependencies": {

View File

@@ -1,6 +1,6 @@
import plugins from '@lucide/rollup-plugins';
import dts from 'rollup-plugin-dts';
import pkg from './package.json' assert { type: 'json' };
import pkg from './package.json' with { type: 'json' };
const packageName = 'LucidePreact';
const outputFileName = 'lucide-preact';

View File

@@ -1,8 +1,15 @@
/* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
export default ({ componentName, iconName, children, getSvg, deprecated, deprecationReason }) => {
const svgContents = getSvg();
export default async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `

View File

@@ -59,7 +59,7 @@
"rollup": "^4.22.4",
"rollup-plugin-dts": "^6.1.0",
"typescript": "^4.8.4",
"vite": "5.1.8",
"vite": "5.4.13",
"vitest": "^1.1.1"
},
"peerDependencies": {

View File

@@ -1,8 +1,15 @@
/* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
export default ({ componentName, iconName, children, getSvg, deprecated, deprecationReason }) => {
const svgContents = getSvg();
export default async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `

View File

@@ -1,10 +1,4 @@
import {
forwardRef,
createElement,
ReactSVG,
FunctionComponent,
ForwardRefExoticComponent,
} from 'react';
import { forwardRef, createElement, FunctionComponent } from 'react';
import * as NativeSvg from 'react-native-svg';
import defaultAttributes, { childDefaultAttributes } from './defaultAttributes';
import { IconNode, LucideIcon, LucideProps } from './types';

View File

@@ -1,7 +1,22 @@
import type { ForwardRefExoticComponent, ReactSVG } from 'react';
import type { ForwardRefExoticComponent } from 'react';
import type { SvgProps } from 'react-native-svg';
export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, string>][];
/**
* A reduced version of `SVGElementType` from @types/react. This type was added
* with the release of React 19, and is included here in order to support usage
* with older versions.
*/
type SVGElementType =
| 'circle'
| 'ellipse'
| 'g'
| 'line'
| 'path'
| 'polygon'
| 'polyline'
| 'rect';
export type IconNode = [elementName: SVGElementType, attrs: Record<string, string>][];
export interface LucideProps extends SvgProps {
size?: string | number;

View File

@@ -0,0 +1 @@
export { default } from './dist/esm/dynamicIconImports.js';

View File

@@ -31,14 +31,17 @@
"sideEffects": false,
"files": [
"dist",
"dynamicIconImports.js",
"dynamic.mjs",
"dynamic.js.map",
"dynamic.d.ts",
"dynamicIconImports.mjs",
"dynamicIconImports.js.map",
"dynamicIconImports.d.ts"
],
"scripts": {
"build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm typecheck && pnpm build:bundles",
"copy:license": "cp ../../LICENSE ./LICENSE",
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts && rm -f dynamicIconImports.*",
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts && rm -f dynamic.* && rm -f dynamicIconImports.d.ts",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --renderUniqueKey --withAliases --withDynamicImports --separateAliasesFile --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts",
"build:bundles": "rollup -c ./rollup.config.mjs",
"typecheck": "tsc",
@@ -60,11 +63,12 @@
"react-dom": "18.2.0",
"rollup": "^4.22.4",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-preserve-directives": "^0.4.0",
"typescript": "^4.9.5",
"vite": "5.1.8",
"vite": "5.4.13",
"vitest": "^1.1.1"
},
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
}

View File

@@ -1,4 +1,5 @@
import plugins from '@lucide/rollup-plugins';
import preserveDirectives from 'rollup-plugin-preserve-directives';
import pkg from './package.json' assert { type: 'json' };
import dts from 'rollup-plugin-dts';
import getAliasesEntryNames from './scripts/getAliasesEntryNames.mjs';
@@ -7,35 +8,34 @@ const aliasesEntries = await getAliasesEntryNames();
const packageName = 'LucideReact';
const outputFileName = 'lucide-react';
const outputDir = `dist`;
const inputs = [`src/lucide-react.ts`];
const bundles = [
{
format: 'umd',
inputs,
outputDir,
outputDir: 'dist/umd',
minify: true,
},
{
format: 'umd',
inputs,
outputDir,
outputDir: 'dist/umd',
},
{
format: 'cjs',
inputs,
outputDir,
outputDir: 'dist/cjs',
},
{
format: 'esm',
inputs: [...inputs, ...aliasesEntries],
outputDir,
inputs: [...inputs, , 'src/dynamicIconImports.ts', 'src/DynamicIcon.ts', ...aliasesEntries],
outputDir: 'dist/esm',
preserveModules: true,
},
{
format: 'esm',
inputs: ['src/dynamicIconImports.ts'],
outputFile: 'dynamicIconImports.js',
inputs: ['src/dynamic.ts'],
outputFile: 'dynamic.mjs',
external: [/src/],
paths: (id) => {
if (id.match(/src/)) {
@@ -62,18 +62,23 @@ const configs = bundles
}) =>
inputs.map((input) => ({
input,
plugins: plugins({ pkg, minify }),
plugins: [
...plugins({ pkg, minify }),
// Make sure we emit "use client" directive to make it compatible with Next.js
preserveDirectives({
include: 'src/DynamicIcon.ts',
suppressPreserveModulesWarning: true,
}),
],
external: ['react', 'prop-types', ...external],
output: {
name: packageName,
...(preserveModules
? {
dir: `${outputDir}/${format}`,
dir: outputDir,
}
: {
file:
outputFile ??
`${outputDir}/${format}/${outputFileName}${minify ? '.min' : ''}.js`,
file: outputFile ?? `${outputDir}/${outputFileName}${minify ? '.min' : ''}.js`,
}),
paths,
entryFileNames,
@@ -101,6 +106,16 @@ export default [
],
plugins: [dts()],
},
{
input: 'src/dynamic.ts',
output: [
{
file: `dynamic.d.ts`,
format: 'es',
},
],
plugins: [dts()],
},
{
input: inputs[0],
output: [

View File

@@ -1,12 +1,22 @@
/* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
export default ({ componentName, iconName, children, getSvg, deprecated, deprecationReason }) => {
const svgContents = getSvg();
export default async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `
import createLucideIcon from '../createLucideIcon';
import { IconNode } from '../types';
export const __iconNode: IconNode = ${JSON.stringify(children)}
/**
* @component @name ${componentName}
@@ -19,7 +29,7 @@ import createLucideIcon from '../createLucideIcon';
* @returns {JSX.Element} JSX Element
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName} = createLucideIcon('${componentName}', ${JSON.stringify(children)});
const ${componentName} = createLucideIcon('${componentName}', __iconNode);
export default ${componentName};
`;

View File

@@ -0,0 +1,73 @@
'use client';
import { createElement, forwardRef, useEffect, useState } from 'react';
import { IconNode, LucideIcon, LucideProps } from './types';
import dynamicIconImports from './dynamicIconImports';
import Icon from './Icon';
export type DynamicIconModule = { default: LucideIcon; __iconNode: IconNode };
export type IconName = keyof typeof dynamicIconImports;
export const iconNames = Object.keys(dynamicIconImports) as Array<IconName>;
interface DynamicIconComponentProps extends LucideProps {
name: IconName;
fallback?: () => JSX.Element | null;
}
async function getIconNode(name: IconName) {
if (!(name in dynamicIconImports)) {
throw new Error('[lucide-react]: Name in Lucide DynamicIcon not found');
}
// TODO: Replace this with a generic iconNode package.
const icon = (await dynamicIconImports[name]()) as DynamicIconModule;
return icon.__iconNode;
}
/**
* Dynamic 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 DynamicIcon = forwardRef<SVGSVGElement, DynamicIconComponentProps>(
({ name, fallback: Fallback, ...props }, ref) => {
const [iconNode, setIconNode] = useState<IconNode>();
useEffect(() => {
getIconNode(name)
.then(setIconNode)
.catch((error) => {
console.error(error);
});
}, [name]);
if (iconNode == null) {
if (Fallback == null) {
return null;
}
return createElement(Fallback);
}
return createElement(Icon, {
ref,
...props,
iconNode,
});
},
);
export default DynamicIcon;

View File

@@ -0,0 +1,7 @@
export {
default as DynamicIcon,
iconNames,
type DynamicIconModule,
type IconName,
} from './DynamicIcon';
export { default as dynamicIconImports } from './dynamicIconImports';

View File

@@ -1,6 +1,21 @@
import { ReactSVG, SVGProps, ForwardRefExoticComponent, RefAttributes } from 'react';
import type { SVGProps, ForwardRefExoticComponent, RefAttributes } from 'react';
export type IconNode = [elementName: keyof ReactSVG, attrs: Record<string, string>][];
/**
* A reduced version of `SVGElementType` from @types/react. This type was added
* with the release of React 19, and is included here in order to support usage
* with older versions.
*/
type SVGElementType =
| 'circle'
| 'ellipse'
| 'g'
| 'line'
| 'path'
| 'polygon'
| 'polyline'
| 'rect';
export type IconNode = [elementName: SVGElementType, attrs: Record<string, string>][];
export type SVGAttributes = Partial<SVGProps<SVGSVGElement>>;
type ElementAttributes = RefAttributes<SVGSVGElement> & SVGAttributes;

View File

@@ -0,0 +1,55 @@
import { describe, it, expect } from 'vitest';
import { act, render, waitFor, type RenderResult } from '@testing-library/react';
import DynamicIcon from '../src/DynamicIcon';
describe('Using DynamicIcon Component', () => {
it('should render icon by given name', async () => {
let container: RenderResult['container'];
await act(async () => {
const result = render(<DynamicIcon name="smile" />);
container = result.container;
});
await waitFor(() => {
// I'd look for a real text here that is renderer when the data loads
expect(container.firstChild).not.toBeNull();
});
});
it('should render icon by alias name', async () => {
let container: RenderResult['container'];
await act(async () => {
const result = render(<DynamicIcon name="home" />);
container = result.container;
});
await waitFor(() => {
// I'd look for a real text here that is renderer when the data loads
expect(container.firstChild).not.toBeNull();
});
});
it('should render icon and match snapshot', async () => {
const { container } = render(<DynamicIcon name="circle" />);
expect(container.firstChild).toMatchSnapshot();
});
it('should adjust the style based', async () => {
const { container } = render(
<DynamicIcon
name="circle"
size={48}
stroke="red"
absoluteStrokeWidth
/>,
);
expect(container.firstChild).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,5 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using DynamicIcon Component > should adjust the style based 1`] = `null`;
exports[`Using DynamicIcon Component > should render icon and match snapshot 1`] = `null`;

View File

@@ -83,10 +83,10 @@
"rollup": "^4.22.4",
"solid-js": "^1.8.7",
"typescript": "^4.9.4",
"vite": "5.1.8",
"vite": "5.4.13",
"vite-plugin-solid": "^2.10.1",
"vitest": "^1.1.1",
"esbuild": "^0.19.11"
"esbuild": "^0.25.0"
},
"peerDependencies": {
"solid-js": "^1.4.7"

View File

@@ -1,8 +1,15 @@
/* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
export default ({ componentName, iconName, children, getSvg, deprecated, deprecationReason }) => {
const svgContents = getSvg();
export default async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `

View File

@@ -30,8 +30,8 @@ const license = `@license ${pkg.name} v${pkg.version} - ${pkg.license}`;
createDirectory(LIB_DIR);
createDirectory(ICON_MODULE_DIR);
const svgFiles = readSvgDirectory(ICONS_DIR);
const svgs = readSvgs(svgFiles, ICONS_DIR);
const svgFiles = await readSvgDirectory(ICONS_DIR);
const svgs = await readSvgs(svgFiles, ICONS_DIR);
const parsedSvgs = svgs.map(({ name, contents }) => ({
name,
@@ -39,6 +39,8 @@ const parsedSvgs = svgs.map(({ name, contents }) => ({
parsedSvg: parseSync(contents),
}));
generateSprite(parsedSvgs, PACKAGE_DIR, license);
generateIconNodes(parsedSvgs, PACKAGE_DIR);
copyIcons(parsedSvgs, PACKAGE_DIR, license);
await Promise.all([
generateSprite(parsedSvgs, PACKAGE_DIR, license),
generateIconNodes(parsedSvgs, PACKAGE_DIR),
copyIcons(parsedSvgs, PACKAGE_DIR, license),
]);

View File

@@ -1,8 +1,8 @@
/* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
export default ({ componentName, iconName, children, getSvg, deprecated, deprecationReason }) => {
let svgContents = getSvg();
export default async ({ componentName, iconName, getSvg, deprecated, deprecationReason }) => {
let svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
svgContents = svgContents.replace(

View File

@@ -1,6 +1,6 @@
import { writeFile } from '@lucide/helpers';
export default function generateIconNodes(parsedSvgs, packageDir) {
export default async function generateIconNodes(parsedSvgs, packageDir) {
const iconNodes = parsedSvgs.reduce((acc, { name, parsedSvg }) => {
acc[name] = parsedSvg.children.map(({ name, attributes }) => [name, attributes]);
@@ -9,5 +9,5 @@ export default function generateIconNodes(parsedSvgs, packageDir) {
const iconNodesStringified = JSON.stringify(iconNodes, null, 2);
writeFile(iconNodesStringified, 'icon-nodes.json', packageDir);
await writeFile(iconNodesStringified, 'icon-nodes.json', packageDir);
}

View File

@@ -3,7 +3,7 @@ import { stringify } from 'svgson';
import { format } from 'prettier';
import { appendFile } from '@lucide/helpers';
export default function generateSprite(svgs, packageDir, license) {
export default async function generateSprite(svgs, packageDir, license) {
const symbols = svgs.map(({ name, parsedSvg }) => ({
name: 'symbol',
type: 'element',
@@ -34,6 +34,6 @@ export default function generateSprite(svgs, packageDir, license) {
const xmlMeta = `<?xml version="1.0" encoding="utf-8"?>\n<!-- ${license} -->\n`;
appendFile(xmlMeta, `sprite.svg`, packageDir);
appendFile(prettifiedSprite, `sprite.svg`, packageDir);
await appendFile(xmlMeta, `sprite.svg`, packageDir);
await appendFile(prettifiedSprite, `sprite.svg`, packageDir);
}

View File

@@ -9,10 +9,12 @@ import { readSvg } from '@lucide/helpers';
* @returns {Object}
*/
export default function readSVGs(svgFiles, iconsDirectory) {
return svgFiles.map((svgFile) => {
const SVGReadPromises = svgFiles.map(async (svgFile) => {
const name = basename(svgFile, '.svg');
const contents = readSvg(svgFile, iconsDirectory);
const contents = await readSvg(svgFile, iconsDirectory);
return { name, contents };
});
return Promise.all(SVGReadPromises);
}

View File

@@ -52,7 +52,7 @@
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --exportFileName=index.ts --iconFileExtension=.svelte --importImportFileExtension=.svelte --separateIconFileExport --separateIconFileExportExtension=.ts --withAliases --aliasesFileExtension=.ts --separateAliasesFile --separateAliasesFileExtension=.ts --aliasImportFileExtension=.js --pretty=false",
"build:package": "svelte-package --input ./src",
"build:license": "node ./scripts/appendBlockComments.mjs",
"test": "pnpm build:icons && vitest run",
"test": "pnpm copy:license && pnpm build:icons && vitest run",
"test:watch": "vitest watch",
"version": "pnpm version --git-tag-version=false"
},
@@ -70,7 +70,7 @@
"svelte-check": "^3.4.4",
"svelte-preprocess": "^5.0.4",
"typescript": "^5.1.6",
"vite": "5.1.8",
"vite": "5.4.13",
"vitest": "^1.1.1"
},
"peerDependencies": {

View File

@@ -4,7 +4,7 @@ import path from 'path';
import { getCurrentDirPath } from '@lucide/helpers';
import { getJSBanner } from './license.mjs';
const currentDir = getCurrentDirPath(import.meta.url);
const currentDir = await getCurrentDirPath(import.meta.url);
const targetDirectory = path.join(currentDir, '../dist');
const files = await readdir(targetDirectory, {

View File

@@ -2,8 +2,15 @@
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
import { getJSBanner } from './license.mjs';
export default ({ iconName, children, componentName, getSvg, deprecated, deprecationReason }) => {
const svgContents = getSvg();
export default async ({
iconName,
children,
componentName,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `\

View File

@@ -1,11 +1,13 @@
import fs from 'fs';
import pkg from '../package.json' with { type: 'json' };
const license = fs.readFileSync('LICENSE', 'utf-8');
export function getJSBanner() {
return `/**
* @license ${pkg.name} v${pkg.version} - ${pkg.license}
*
* This source code is licensed under the ${pkg.license} license.
* See the LICENSE file in the root directory of this source tree.
* ${license.split('\n').join('\n * ')}
*/
`;
}

View File

@@ -54,7 +54,7 @@
"@vue/test-utils": "2.4.5",
"rollup": "^4.22.4",
"rollup-plugin-dts": "^6.1.0",
"vite": "5.1.8",
"vite": "5.4.13",
"vitest": "^1.4.0",
"vue": "^3.4.21"
},

View File

@@ -1,8 +1,15 @@
/* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
export default ({ componentName, iconName, children, getSvg, deprecated, deprecationReason }) => {
const svgContents = getSvg();
export default async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `

View File

@@ -52,7 +52,7 @@
"@vue/test-utils": "1.3.0",
"rollup": "^3.29.5",
"typescript": "^4.9.5",
"vite": "5.1.8",
"vite": "5.4.13",
"vitest": "^0.32.2",
"vue": "2.7.14",
"vue-template-compiler": "2.7.14"

View File

@@ -1,8 +1,15 @@
/* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
export default ({ componentName, iconName, children, getSvg, deprecated, deprecationReason }) => {
const svgContents = getSvg();
export default async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `

View File

@@ -39,6 +39,7 @@
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --iconFileExtension=.ts --withAliases --aliasNamesOnly --aliasesFileExtension=.ts --exportFileName=index.ts",
"build:bundle": "rollup -c rollup.config.mjs",
"test": "pnpm build:icons && vitest run",
"test:watch": "vitest watch",
"version": "pnpm version --git-tag-version=false"
},
"devDependencies": {
@@ -50,7 +51,7 @@
"rollup": "^4.22.4",
"rollup-plugin-dts": "^6.1.0",
"typescript": "^4.9.3",
"vite": "5.1.8",
"vite": "5.4.13",
"vitest": "^1.1.1"
}
}

View File

@@ -1,8 +1,15 @@
/* eslint-disable import/no-extraneous-dependencies */
import base64SVG from '@lucide/build-icons/utils/base64SVG.mjs';
export default ({ componentName, iconName, children, getSvg, deprecated, deprecationReason }) => {
const svgContents = getSvg();
export default async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `
@@ -19,11 +26,7 @@ import type { IconNode } from '../types';
* @returns {Array}
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName}: IconNode = [
'svg',
defaultAttributes,
${JSON.stringify(children)}
];
const ${componentName}: IconNode = ${JSON.stringify(children)}
export default ${componentName};
`;

View File

@@ -1,4 +1,6 @@
import { IconNode, IconNodeChild, SVGProps } from './types';
import { IconNode, SVGProps } from './types';
type CreateElementParams = [tag: string, attrs: SVGProps, children?: IconNode];
/**
* Creates a new HTMLElement from icon node
@@ -7,16 +9,16 @@ import { IconNode, IconNodeChild, SVGProps } from './types';
* @param {array} children
* @returns {HTMLElement}
*/
const createElement = (tag: string, attrs: SVGProps, children: IconNodeChild[] = []) => {
const createElement = ([tag, attrs, children]: CreateElementParams) => {
const element = document.createElementNS('http://www.w3.org/2000/svg', tag);
Object.keys(attrs).forEach((name) => {
element.setAttribute(name, String(attrs[name]));
});
if (children.length) {
if (children?.length) {
children.forEach((child) => {
const childElement = createElement(...child);
const childElement = createElement(child);
element.appendChild(childElement);
});
@@ -25,9 +27,4 @@ const createElement = (tag: string, attrs: SVGProps, children: IconNodeChild[] =
return element;
};
/**
* Creates a new HTMLElement from icon node
* @param {[tag: string, attrs: object, children: array]} iconNode
* @returns {HTMLElement}
*/
export default ([tag, attrs, children]: IconNode) => createElement(tag, attrs, children);
export default createElement;

View File

@@ -1,4 +1,5 @@
import createElement from './createElement';
import defaultAttributes from './defaultAttributes';
import { Icons } from './types';
export type CustomAttrs = { [attr: string]: any };
@@ -19,7 +20,9 @@ export const getAttrs = (element: Element): Record<string, string> =>
* @param {Object} attrs
* @returns {Array}
*/
export const getClassNames = (attrs: Record<string, string> | string): string | string[] => {
export const getClassNames = (
attrs: Record<string, string | string[]> | string,
): string | string[] => {
if (typeof attrs === 'string') return attrs;
if (!attrs || !attrs.class) return '';
if (attrs.class && typeof attrs.class === 'string') {
@@ -36,7 +39,9 @@ export const getClassNames = (attrs: Record<string, string> | string): string |
* @param {array} arrayOfClassnames
* @returns {string}
*/
export const combineClassNames = (arrayOfClassnames: (string | Record<string, string>)[]) => {
export const combineClassNames = (
arrayOfClassnames: (string | Record<string, string | string[]>)[],
) => {
const classNameArray = arrayOfClassnames.flatMap(getClassNames);
return classNameArray
@@ -77,10 +82,9 @@ const replaceElement = (element: Element, { nameAttr, icons, attrs }: ReplaceEle
}
const elementAttrs = getAttrs(element);
const [tag, iconAttributes, children] = iconNode;
const iconAttrs = {
...iconAttributes,
...defaultAttributes,
'data-lucide': iconName,
...attrs,
...elementAttrs,
@@ -94,7 +98,7 @@ const replaceElement = (element: Element, { nameAttr, icons, attrs }: ReplaceEle
});
}
const svgElement = createElement([tag, iconAttrs, children]);
const svgElement = createElement(['svg', iconAttrs, iconNode]);
return element.parentNode?.replaceChild(svgElement, element);
};

View File

@@ -1,6 +1,5 @@
// className is not supported in svg elements
export type SVGProps = Record<string, string | number>;
export type IconNodeChild = readonly [tag: string, attrs: SVGProps];
export type IconNode = readonly [tag: string, attrs: SVGProps, children?: IconNodeChild[]];
export type IconNode = [tag: string, attrs: SVGProps][];
export type Icons = { [key: string]: IconNode };

View File

@@ -1,3 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`combineClassNames > should retuns a string of classNames 1`] = `"item item1 item2 item3 item4 item5 item6 item7 item8 item9"`;

View File

@@ -0,0 +1,3 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`combineClassNames > should returns a string of classNames 1`] = `"item item1 item2 item3 item4 item5 item6 item7 item8 item9"`;

View File

@@ -6,7 +6,7 @@ import { parseSync, stringify } from 'svgson';
const ICONS_DIR = path.resolve(__dirname, '../../../icons');
const getOriginalSvg = (iconName, aliasName) => {
const getOriginalSvg = (iconName: string, aliasName?: string) => {
const svgContent = fs.readFileSync(path.join(ICONS_DIR, `${iconName}.svg`), 'utf8');
const svgParsed = parseSync(svgContent);
@@ -51,14 +51,17 @@ describe('createIcons', () => {
createIcons({ icons, attrs });
const element = document.querySelector('svg');
const element = document.querySelector('svg') as SVGSVGElement;
const attributes = element.getAttributeNames();
const attributesAndValues = attributes.reduce((acc, item) => {
acc[item] = element.getAttribute(item);
const attributesAndValues = attributes.reduce(
(acc, item) => {
acc[item] = element.getAttribute(item);
return acc;
}, {});
return acc;
},
{} as Record<string, string | null>,
);
expect(document.body.innerHTML).toMatchSnapshot();
@@ -74,14 +77,17 @@ describe('createIcons', () => {
createIcons({ icons });
const element = document.querySelector('svg');
const element = document.querySelector('svg') as SVGSVGElement;
const attributes = element.getAttributeNames();
const attributesAndValues = attributes.reduce((acc, item) => {
acc[item] = element.getAttribute(item);
const attributesAndValues = attributes.reduce(
(acc, item) => {
acc[item] = element.getAttribute(item);
return acc;
}, {});
return acc;
},
{} as Record<string, string | null>,
);
expect(attributesAndValues).toEqual(expect.objectContaining(attrs));
});

View File

@@ -16,7 +16,7 @@ describe('getAtts', () => {
],
};
const attrs = getAttrs(element);
const attrs = getAttrs(element as unknown as Element);
expect(attrs.class).toBe(element.attributes[0].value);
});
@@ -43,8 +43,8 @@ describe('getClassNames', () => {
});
describe('combineClassNames', () => {
it('should retuns a string of classNames', () => {
const arrayOfClassnames = [
it('should returns a string of classNames', () => {
const arrayOfClassnames: (string | Record<string, string[]>)[] = [
'item',
{
class: ['item1', 'item2', 'item3'],

Some files were not shown because too many files have changed in this diff Show More