Compare commits
33 Commits
0.469.0
...
fix/fixed-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8f578fa8b | ||
|
|
97f214934d | ||
|
|
34dfc63ce2 | ||
|
|
b46927e510 | ||
|
|
1828a392c8 | ||
|
|
3ab6c373a0 | ||
|
|
ba2c4b526f | ||
|
|
bde3f01e0b | ||
|
|
50630b3aaf | ||
|
|
e28426a871 | ||
|
|
eb158561d3 | ||
|
|
410ae434fa | ||
|
|
0801b89e4d | ||
|
|
a1d17eedc9 | ||
|
|
7481dd0a3f | ||
|
|
f9e93824f1 | ||
|
|
4ba4cf2f35 | ||
|
|
6b29716aa9 | ||
|
|
97bbe1d6b2 | ||
|
|
608da04bdf | ||
|
|
961404d5cc | ||
|
|
f3100b8af1 | ||
|
|
0df4e2991c | ||
|
|
2b8242fa14 | ||
|
|
27c667556b | ||
|
|
31c3fefc17 | ||
|
|
389fed8770 | ||
|
|
58c2e108c3 | ||
|
|
d5fe5a0ef4 | ||
|
|
db30ab89f4 | ||
|
|
fa7120cbe0 | ||
|
|
419d47019c | ||
|
|
655111e4aa |
6
.gitignore
vendored
@@ -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
|
||||
|
||||
|
||||
22
README.md
@@ -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` | [](https://www.npmjs.com/package/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` | [](https://www.npmjs.com/package/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` | [](https://www.npmjs.com/package/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` | [](https://www.npmjs.com/package/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` | [](https://www.npmjs.com/package/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` | [](https://www.npmjs.com/package/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` | [](https://www.npmjs.com/package/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` | [](https://www.npmjs.com/package/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` | [](https://www.npmjs.com/package/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`** | [](https://www.npmjs.com/package/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`** | [](https://www.npmjs.com/package/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`** | [](https://www.npmjs.com/package/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`** | [](https://www.npmjs.com/package/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`** | [](https://www.npmjs.com/package/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`** | [](https://www.npmjs.com/package/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`** | [](https://www.npmjs.com/package/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`** | [](https://www.npmjs.com/package/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`** | [](https://www.npmjs.com/package/lucide-static) |  | [Docs](https://lucide.dev/guide/packages/lucide-static) · [Source](./packages/lucide-static) |
|
||||
|
||||
### Figma
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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} />
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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 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"
|
||||
|
||||
@@ -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>
|
||||
const App = () => (
|
||||
<DynamicIcon name="camera" color="red" size={48} />
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
```
|
||||
|
||||
@@ -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');
|
||||
})
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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,`);
|
||||
|
||||
@@ -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', '');
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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 |
@@ -14,7 +14,7 @@
|
||||
"diy",
|
||||
"fixed",
|
||||
"build",
|
||||
"contruction",
|
||||
"construction",
|
||||
"parts"
|
||||
],
|
||||
"categories": [
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
"enterprise",
|
||||
"skyscraper",
|
||||
"organisation",
|
||||
"organization"
|
||||
"organization",
|
||||
"city"
|
||||
],
|
||||
"categories": [
|
||||
"account",
|
||||
|
||||
16
icons/circle-small.json
Normal 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
@@ -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 |
@@ -10,7 +10,13 @@
|
||||
"cog",
|
||||
"edit",
|
||||
"gear",
|
||||
"preferences"
|
||||
"preferences",
|
||||
"controls",
|
||||
"configuration",
|
||||
"fixed",
|
||||
"build",
|
||||
"construction",
|
||||
"parts"
|
||||
],
|
||||
"categories": [
|
||||
"account"
|
||||
|
||||
@@ -2,13 +2,18 @@
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"mittalyashu",
|
||||
"ericfennis"
|
||||
"ericfennis",
|
||||
"jguddas"
|
||||
],
|
||||
"tags": [
|
||||
"scale",
|
||||
"fullscreen"
|
||||
"fullscreen",
|
||||
"maximize",
|
||||
"minimize",
|
||||
"contract"
|
||||
],
|
||||
"categories": [
|
||||
"text"
|
||||
"text",
|
||||
"arrows"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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 |
@@ -26,5 +26,8 @@
|
||||
"text",
|
||||
"layout",
|
||||
"math"
|
||||
],
|
||||
"aliases": [
|
||||
"grid-2-x-2-check"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -26,5 +26,8 @@
|
||||
"text",
|
||||
"layout",
|
||||
"math"
|
||||
],
|
||||
"aliases": [
|
||||
"grid-2-x-2-x"
|
||||
]
|
||||
}
|
||||
|
||||
20
icons/house-wifi.json
Normal 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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"jamiemlaw"
|
||||
],
|
||||
"tags": [
|
||||
"gender",
|
||||
"androgyne",
|
||||
"transgender"
|
||||
],
|
||||
"categories": [
|
||||
"medical"
|
||||
]
|
||||
}
|
||||
16
icons/mars-stroke.svg
Normal 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
@@ -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
@@ -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
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"jamiemlaw"
|
||||
],
|
||||
"tags": [
|
||||
"gender",
|
||||
"nonbinary",
|
||||
"enby"
|
||||
],
|
||||
"categories": [
|
||||
"medical"
|
||||
]
|
||||
}
|
||||
16
icons/non-binary.svg
Normal 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 |
@@ -6,7 +6,8 @@
|
||||
"ericfennis",
|
||||
"karsa-mistmere",
|
||||
"danielbayley",
|
||||
"jguddas"
|
||||
"jguddas",
|
||||
"sezze"
|
||||
],
|
||||
"tags": [
|
||||
"box",
|
||||
|
||||
@@ -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 |
@@ -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 |
14
icons/transgender.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"jamiemlaw"
|
||||
],
|
||||
"tags": [
|
||||
"gender",
|
||||
"inclusive"
|
||||
],
|
||||
"categories": [
|
||||
"medical",
|
||||
"accessibility"
|
||||
]
|
||||
}
|
||||
20
icons/transgender.svg
Normal 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 |
21
icons/triangle-dashed.json
Normal 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
@@ -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 |
16
icons/venus-and-mars.json
Normal 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
@@ -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
@@ -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
@@ -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 |
@@ -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 `\
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 `
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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 `
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
1
packages/lucide-react/dynamicIconImports.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './dist/esm/dynamicIconImports.js';
|
||||
@@ -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,8 +63,9 @@
|
||||
"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": {
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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};
|
||||
`;
|
||||
|
||||
73
packages/lucide-react/src/DynamicIcon.ts
Normal 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;
|
||||
7
packages/lucide-react/src/dynamic.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export {
|
||||
default as DynamicIcon,
|
||||
iconNames,
|
||||
type DynamicIconModule,
|
||||
type IconName,
|
||||
} from './DynamicIcon';
|
||||
export { default as dynamicIconImports } from './dynamicIconImports';
|
||||
@@ -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;
|
||||
|
||||
55
packages/lucide-react/tests/DynamicIcon.spec.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
@@ -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`;
|
||||
@@ -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"
|
||||
|
||||
@@ -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 `
|
||||
|
||||
@@ -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),
|
||||
]);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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 `\
|
||||
|
||||
@@ -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 * ')}
|
||||
*/
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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 `
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 `
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
`;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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"`;
|
||||
@@ -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"`;
|
||||
@@ -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) => {
|
||||
const attributesAndValues = attributes.reduce(
|
||||
(acc, item) => {
|
||||
acc[item] = element.getAttribute(item);
|
||||
|
||||
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) => {
|
||||
const attributesAndValues = attributes.reduce(
|
||||
(acc, item) => {
|
||||
acc[item] = element.getAttribute(item);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
{} as Record<string, string | null>,
|
||||
);
|
||||
|
||||
expect(attributesAndValues).toEqual(expect.objectContaining(attrs));
|
||||
});
|
||||
@@ -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'],
|
||||
1212
pnpm-lock.yaml
generated
@@ -7,13 +7,13 @@ import {
|
||||
|
||||
const currentDir = getCurrentDirPath(import.meta.url);
|
||||
const ICONS_DIR = path.resolve(currentDir, '../icons');
|
||||
const icons = readAllMetadata(ICONS_DIR);
|
||||
const icons = await readAllMetadata(ICONS_DIR);
|
||||
const CATEGORIES_DIR = path.resolve(currentDir, '../categories');
|
||||
const categories = readAllMetadata(CATEGORIES_DIR);
|
||||
const categories = await readAllMetadata(CATEGORIES_DIR);
|
||||
|
||||
console.log('Reading all icons');
|
||||
|
||||
const svgFiles = readSvgDirectory(ICONS_DIR);
|
||||
const svgFiles = await readSvgDirectory(ICONS_DIR);
|
||||
const iconNames = svgFiles.map((icon) => icon.split('.')[0]);
|
||||
|
||||
let error = false;
|
||||
|
||||
@@ -3,7 +3,6 @@ import path from 'path';
|
||||
import { parseSync } from 'svgson';
|
||||
import {
|
||||
shuffleArray,
|
||||
readSvgDirectory,
|
||||
getCurrentDirPath,
|
||||
minifySvg,
|
||||
toPascalCase,
|
||||
@@ -22,7 +21,7 @@ if (changedFilesPathString == null) {
|
||||
|
||||
const changedFiles = changedFilesPathString
|
||||
.split(' ')
|
||||
.map((file) => file.replace('.json', '.svg'))
|
||||
.filter((file) => file.includes('.svg'))
|
||||
.filter((file, idx, arr) => arr.indexOf(file) === idx);
|
||||
|
||||
if (changedFiles.length === 0) {
|
||||
@@ -43,7 +42,7 @@ const getImageTagsByFiles = (files, getBaseUrl, width) =>
|
||||
return `<img title="${file}" alt="${file}" ${widthAttr} src="${url}/${base64}.svg"/>`;
|
||||
});
|
||||
|
||||
const svgFiles = readSvgDirectory(ICONS_DIR).map((file) => `icons/${file}`);
|
||||
const svgFiles = fs.readdirSync(ICONS_DIR).map((file) => `icons/${file}`);
|
||||
|
||||
const iconsFilteredByName = (search) => svgFiles.filter((file) => file.includes(search));
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ Promise.all(
|
||||
aliases.push(iconNameKebabCaseNextjsFlavour);
|
||||
}
|
||||
|
||||
const output = JSON.stringify({ ...iconMetaData, aliases }, null, 2);
|
||||
let output = JSON.stringify({ ...iconMetaData, aliases }, null, 2);
|
||||
output = `${output}\n`;
|
||||
fs.writeFile(path.resolve(ICONS_DIR, `${iconName}.json`), output, 'utf-8');
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import categories from '../categories.json' assert { type: 'json' };
|
||||
import categories from '../categories.json' with { type: 'json' };
|
||||
import {
|
||||
mergeArrays,
|
||||
writeFile,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { writeFile, getCurrentDirPath, readAllMetadata } from '../tools/build-he
|
||||
|
||||
const currentDir = getCurrentDirPath(import.meta.url);
|
||||
const ICONS_DIR = path.resolve(currentDir, '../icons');
|
||||
const icons = readAllMetadata(ICONS_DIR);
|
||||
const icons = await readAllMetadata(ICONS_DIR);
|
||||
|
||||
const tags = Object.keys(icons)
|
||||
.sort()
|
||||
@@ -14,4 +14,4 @@ const tags = Object.keys(icons)
|
||||
|
||||
const tagsContent = JSON.stringify(tags, null, 2);
|
||||
|
||||
writeFile(tagsContent, 'tags.json', path.resolve(process.cwd()));
|
||||
await writeFile(tagsContent, 'tags.json', path.resolve(process.cwd()));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
@@ -10,4 +10,4 @@ import path from 'path';
|
||||
* @param {string} outputDirectory
|
||||
*/
|
||||
export const appendFile = (content, fileName, outputDirectory) =>
|
||||
fs.appendFileSync(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||
fs.appendFile(path.join(outputDirectory, fileName), content, 'utf-8');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { readMetadata } from './readMetadata.mjs';
|
||||
|
||||
@@ -9,11 +9,14 @@ import { readMetadata } from './readMetadata.mjs';
|
||||
* @param {string} directory
|
||||
* @returns {object} A map of icon or category metadata
|
||||
*/
|
||||
export const readAllMetadata = (directory) =>
|
||||
fs
|
||||
.readdirSync(directory)
|
||||
export const readAllMetadata = async (directory) => {
|
||||
const directoryContent = await fs.readdir(directory);
|
||||
|
||||
const metaDataPromises = directoryContent
|
||||
.filter((file) => path.extname(file) === '.json')
|
||||
.reduce((acc, fileName) => {
|
||||
acc[path.basename(fileName, '.json')] = readMetadata(fileName, directory);
|
||||
return acc;
|
||||
}, {});
|
||||
.map(async (file) => [path.basename(file, '.json'), await readMetadata(file, directory)]);
|
||||
|
||||
const metadata = await Promise.all(metaDataPromises);
|
||||
|
||||
return Object.fromEntries(metadata);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
@@ -8,4 +8,4 @@ import path from 'path';
|
||||
* @param {string} path
|
||||
* @returns {string} The contents of a file
|
||||
*/
|
||||
export const readFile = (path) => fs.readFileSync(path.resolve(__dirname, '../', path), 'utf-8');
|
||||
export const readFile = (path) => fs.readFile(path.resolve(__dirname, '../', path), 'utf-8');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
@@ -9,5 +9,8 @@ import path from 'path';
|
||||
* @param {string} directory
|
||||
* @returns {object} The metadata for the icon or category
|
||||
*/
|
||||
export const readMetadata = (fileName, directory) =>
|
||||
JSON.parse(fs.readFileSync(path.join(directory, fileName), 'utf-8'));
|
||||
export const readMetadata = async (fileName, directory) => {
|
||||
const metadataFileContent = await fs.readFile(path.join(directory, fileName), 'utf-8');
|
||||
|
||||
return JSON.parse(metadataFileContent);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
@@ -9,4 +9,4 @@ import path from 'path';
|
||||
* @param {string} directory
|
||||
*/
|
||||
export const readSvg = (fileName, directory) =>
|
||||
fs.readFileSync(path.join(directory, fileName), 'utf-8');
|
||||
fs.readFile(path.join(directory, fileName), 'utf-8');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
@@ -9,5 +9,8 @@ import path from 'path';
|
||||
* @param {string} fileExtension
|
||||
* @returns {array} An array of file paths containing svgs
|
||||
*/
|
||||
export const readSvgDirectory = (directory, fileExtension = '.svg') =>
|
||||
fs.readdirSync(directory).filter((file) => path.extname(file) === fileExtension);
|
||||
export const readSvgDirectory = async (directory, fileExtension = '.svg') => {
|
||||
const directoryContents = await fs.readdir(directory);
|
||||
|
||||
return directoryContents.filter((file) => path.extname(file) === fileExtension);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import fs from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
@@ -9,4 +9,4 @@ import path from 'path';
|
||||
* @param {string} outputDirectory
|
||||
*/
|
||||
export const resetFile = (fileName, outputDirectory) =>
|
||||
fs.writeFileSync(path.join(outputDirectory, fileName), '', 'utf-8');
|
||||
fs.writeFile(path.join(outputDirectory, fileName), '', 'utf-8');
|
||||
|
||||