Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d1fc51b9a | ||
|
|
1502285072 | ||
|
|
240118e521 | ||
|
|
4aec3f892f | ||
|
|
dbfe130652 | ||
|
|
d29db5e98e | ||
|
|
bb26580cfb | ||
|
|
74c46711c7 | ||
|
|
c3b6795394 | ||
|
|
33342b8790 | ||
|
|
9ecaafbc4d | ||
|
|
1d578ab536 | ||
|
|
c757cb9d4b | ||
|
|
5d36179bf4 | ||
|
|
357df0d660 | ||
|
|
5a06f7cb1d | ||
|
|
63d0ef6664 | ||
|
|
0adb963d07 | ||
|
|
89147bbecc | ||
|
|
0ad5aaf323 | ||
|
|
50f9af4ed2 | ||
|
|
4c61dc4826 | ||
|
|
99a8602c5d | ||
|
|
75b6ac1969 | ||
|
|
138ce8e748 | ||
|
|
87ec1a0587 | ||
|
|
02eaf471a9 | ||
|
|
3035ed309e | ||
|
|
4e50f0d759 | ||
|
|
a924c01b8e | ||
|
|
37f32cb73a | ||
|
|
4e2efb9350 | ||
|
|
658573b017 | ||
|
|
5136572c10 | ||
|
|
4f8088fd90 | ||
|
|
ae40cfbc73 | ||
|
|
cdd7db1ce6 | ||
|
|
f930bbe07c | ||
|
|
f229f83f0c | ||
|
|
b442632ee6 | ||
|
|
078a0b2339 | ||
|
|
5d78605650 | ||
|
|
ab61fa277b | ||
|
|
bb1329e512 | ||
|
|
b5b5d95933 | ||
|
|
7b589f2b5c | ||
|
|
2d36863568 | ||
|
|
7291cbcc0f | ||
|
|
4c663b9a32 | ||
|
|
63369b1ac0 | ||
|
|
f07465f82e | ||
|
|
60d583ce4e | ||
|
|
5828ecb38f | ||
|
|
031b226ac6 | ||
|
|
35d535a477 | ||
|
|
8930dfa827 | ||
|
|
c67c9bdbfb | ||
|
|
52d7c549c3 | ||
|
|
9cb4e49849 | ||
|
|
f59db07ef9 | ||
|
|
46bcf1fa89 | ||
|
|
7d15e8466a | ||
|
|
ce2d598d96 | ||
|
|
627b4c1672 | ||
|
|
ee02505468 | ||
|
|
7c8fbb4813 | ||
|
|
7e938845f0 | ||
|
|
0d6c47d545 | ||
|
|
2f5af2ceb5 | ||
|
|
448bcc90fe | ||
|
|
2fed527c00 | ||
|
|
8fc6b70aa5 | ||
|
|
cf0e1e44ba | ||
|
|
65ef069acc | ||
|
|
7983b2a262 | ||
|
|
7ddb1e237d | ||
|
|
5ff536e139 | ||
|
|
bb7d2d9839 | ||
|
|
fc20eb6c7a | ||
|
|
d850228c3c | ||
|
|
f6cef23cd7 | ||
|
|
766c98af22 | ||
|
|
1f0f070e84 | ||
|
|
a90271b282 | ||
|
|
c81680e066 | ||
|
|
0c57f7265a | ||
|
|
f917f247e3 | ||
|
|
aef0f98a68 | ||
|
|
6e5c250af0 | ||
|
|
f37f235d78 | ||
|
|
b973e42174 | ||
|
|
ce6fb3dd6c | ||
|
|
c3262987be | ||
|
|
52ee7b3692 | ||
|
|
04ee825d6b |
@@ -9,3 +9,4 @@ strikethrough
|
||||
touchpad
|
||||
ungroup
|
||||
toc
|
||||
sunlounger
|
||||
|
||||
@@ -47,7 +47,9 @@ Common scopes: icons, docs, studio, site, dev
|
||||
|
||||
### Design <!-- ONLY for new icons -->
|
||||
<!-- All of these requirements must be fulfilled. -->
|
||||
|
||||
- [ ] I've read and followed the [icon design guidelines](https://lucide.dev/contribute/icon-design-guide)
|
||||
- [ ] I've made sure that the icons don't [already exist](https://lucide.dev/icons), including the [Lab directory](https://github.com/lucide-icons/lucide/tree/main/lab).
|
||||
- [ ] I've made sure that the icons look sharp on low DPI displays.
|
||||
- [ ] I've made sure that the icons look consistent with the icon set in size, optical volume and density.
|
||||
- [ ] I've made sure that the icons are visually centered.
|
||||
|
||||
@@ -27,10 +27,6 @@ permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: write
|
||||
|
||||
# This is added to make sure we wait until last release is done, this is needed for the font-build process
|
||||
concurrency:
|
||||
group: continuous-integration
|
||||
|
||||
jobs:
|
||||
check-dispatch-gate:
|
||||
if: github.repository == 'lucide-icons/lucide' &&
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
Lucide **does not accept** brand logos, and we do not plan to add them in the future. This is due to a combination of **legal restrictions**, **design consistency concerns**, and **practical maintenance reasons**.
|
||||
|
||||
[Click here to read our official statement about brand logos in Lucide.](./BRAND_LOGOS_STATEMENT.md)
|
||||
[Click here to read our official statement about brand logos in Lucide.](https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md)
|
||||
|
||||
You can [search for similar issues.](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+${VALUE})
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
name: Linting PR
|
||||
name: Linting PR code
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- icons/*.svg
|
||||
|
||||
jobs:
|
||||
lint-code:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Linting PR
|
||||
name: Linting PR title
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
@@ -7,6 +7,7 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'icons/*'
|
||||
- 'lab/*'
|
||||
|
||||
jobs:
|
||||
lint-filenames:
|
||||
@@ -17,20 +18,47 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v46
|
||||
with:
|
||||
files: icons/*
|
||||
files: |
|
||||
icons/*
|
||||
lab/*
|
||||
|
||||
- name: Generate annotations
|
||||
run: node ./scripts/lintFilenames.mts
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
|
||||
check-lab-icons:
|
||||
name: Check Lab Icons
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Check lab icons do not already exist in icons
|
||||
run: node ./scripts/checkLabIcons.mts
|
||||
|
||||
lint-aliases:
|
||||
name: Check Uniqueness of Aliases
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
name: Lucide Lab checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/lab/**
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter @lucide/lab build
|
||||
@@ -20,9 +20,6 @@ permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere", "jguddas"]', github.actor)
|
||||
@@ -74,7 +71,7 @@ jobs:
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Set new version
|
||||
run: pnpm --fail-if-no-match --filter ${{ matrix.package }} version --new-version ${{ needs.prepare.outputs.VERSION }} --no-git-tag-version
|
||||
run: pnpm version ${{ needs.prepare.outputs.VERSION }} --filter ${{ matrix.package }} --fail-if-no-match --no-git-tag-version
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter ${{ matrix.package }} build
|
||||
@@ -108,7 +105,7 @@ jobs:
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Set new version
|
||||
run: pnpm --filter lucide-static version --new-version ${{ needs.prepare.outputs.VERSION }} --no-git-tag-version
|
||||
run: pnpm version ${{ needs.prepare.outputs.VERSION }} --filter lucide-static --no-git-tag-version
|
||||
|
||||
- name: Move Font
|
||||
run: cp -r lucide-font packages/lucide-static/font
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Weekly Repo Report
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# Run this workflow every Monday at 07:00 UTC
|
||||
schedule:
|
||||
- cron: '0 7 * * 1'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
weekly-summary:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Install Copilot CLI
|
||||
run: npm install -g @github/copilot
|
||||
|
||||
- name: Run the prompt
|
||||
env:
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
run: |
|
||||
copilot -p "${{ vars.REPO_REPORT_PROMPT }} Write the details to summary.md" --allow-tool='shell(git:*)' --allow-tool=write --no-ask-user
|
||||
cat summary.md >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Post report to Discord (private channel)
|
||||
env:
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.LUCIDE_DISCORD_WEBHOOK }}
|
||||
run: |
|
||||
jq -Rs '{
|
||||
embeds: [{
|
||||
title: "📊 Weekly Repo Report",
|
||||
description: (.[0:4096]),
|
||||
color: 5814783
|
||||
}]
|
||||
}' summary.md > payload.json
|
||||
curl -sS --fail -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @payload.json \
|
||||
"$DISCORD_WEBHOOK_URL"
|
||||
@@ -1,6 +1,6 @@
|
||||
name: 'Request Review'
|
||||
name: Request Review
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
paths:
|
||||
- icons/*.svg
|
||||
|
||||
@@ -54,8 +54,10 @@ docs/.vitepress/data/iconDetails
|
||||
docs/.vitepress/data/relatedIcons.json
|
||||
docs/.vitepress/data/brandStopwords.json
|
||||
docs/.vitepress/data/iconPopularity
|
||||
docs/.vitepress/data/lab/iconNodes
|
||||
docs/.vitepress/data/lab/iconMetaData.ts
|
||||
docs/.vitepress/data/lab/iconPopularity
|
||||
docs/.vercel
|
||||
docs/.nitro
|
||||
docs/public/og/**
|
||||
!docs/public/og/general.png
|
||||
.gitignore
|
||||
|
||||
@@ -30,7 +30,7 @@ Read it here: [ICON_GUIDELINES](https://lucide.dev/contribute/icon-design-guide)
|
||||
|
||||
#### Lucide Studio
|
||||
|
||||
For formatting and adjusting SVG icons, [@jguddas](https://github.com/jguddas) made a great tool called [Lucide Studio](https://studio.lucide.dev/). It is a web-based SVG editor that allows you to edit and adjust icons in the Lucide style. You can use it to create new icons or modify existing ones.
|
||||
For formatting and adjusting SVG icons, [@jguddas](https://github.com/jguddas) made a great tool called [Lucide Studio](https://studio.lucide.dev/?utm_source=lucide.dev&utm_medium=contributing-guide). It is a web-based SVG editor that allows you to edit and adjust icons in the Lucide style. You can use it to create new icons or modify existing ones.
|
||||
|
||||
#### Editor guides
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ Lucide is an open-source icon library that provides 1600+ vector (svg) files for
|
||||
| <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/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/react) · [Source](./packages/lucide-react) |
|
||||
| <img src="https://lucide.dev/framework-logos/vue.svg" alt="Vue logo" width="48"> | **`@lucide/vue`** | [](https://www.npmjs.com/package/@lucide/vue) |  | [Docs](https://lucide.dev/guide/vue) · [Source](./packages/vue) |
|
||||
| <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/svelte) · [Source](./packages/@lucide/svelte) |
|
||||
| <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/svelte) · [Source](./packages/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/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/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/react-native) · [Source](./packages/lucide-react-native) |
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
"kubernetes": "Kubernetes",
|
||||
"leetcode": "LeetCode",
|
||||
"leet-code": "LeetCode",
|
||||
"line": "LINE",
|
||||
"linkedin": "LinkedIn",
|
||||
"lua": "Lua",
|
||||
"mariadb": "MariaDB",
|
||||
@@ -69,7 +68,7 @@
|
||||
"nestjs": "NestJS",
|
||||
"netflix": "Netflix",
|
||||
"netlify": "Netlify",
|
||||
"next": "Next.js",
|
||||
"nextjs": "Next.js",
|
||||
"nodejs": "Node.js",
|
||||
"notion": "Notion",
|
||||
"nostr": "Nostr",
|
||||
@@ -105,7 +104,8 @@
|
||||
"shopify": "Shopify",
|
||||
"skype": "Skype",
|
||||
"slack": "Slack",
|
||||
"solid": "SolidJS",
|
||||
"snapchat": "Snapchat",
|
||||
"solidjs": "SolidJS",
|
||||
"soundcloud": "SoundCloud",
|
||||
"spotify": "Spotify",
|
||||
"sqlite": "SQLite",
|
||||
@@ -144,6 +144,5 @@
|
||||
"xbox": "Xbox",
|
||||
"yarn": "Yarn",
|
||||
"youtube": "YouTube",
|
||||
"zig": "Zig",
|
||||
"zoom": "Zoom"
|
||||
"zig": "Zig"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "../category.schema.json",
|
||||
"title": "Navigation, Maps, and POIs",
|
||||
"title": "Navigation & Places",
|
||||
"icon": "compass"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { eventHandler, setResponseHeader } from 'h3';
|
||||
import iconMetaData from '../../../data/lab/iconMetaData';
|
||||
import iconPopularity from '../../../data/lab/iconPopularity';
|
||||
import iconNodes from '../../../data/lab/iconNodes';
|
||||
|
||||
export default eventHandler((event) => {
|
||||
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(iconMetaData).map(([iconName, iconData]) => {
|
||||
// Remove $schema from the response
|
||||
delete iconData['$schema'];
|
||||
|
||||
return [
|
||||
iconName,
|
||||
{
|
||||
name: iconName,
|
||||
iconNode: iconNodes[iconName],
|
||||
popularity: iconPopularity[iconName]?.count ?? 0,
|
||||
...iconData,
|
||||
},
|
||||
];
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { eventHandler, setResponseHeader } from 'h3';
|
||||
import iconMetaData from '../../../data/lab/iconMetaData';
|
||||
|
||||
export default eventHandler((event) => {
|
||||
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400');
|
||||
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
|
||||
|
||||
for (const iconDetail in iconMetaData) {
|
||||
// Remove $schema from the response
|
||||
delete iconMetaData[iconDetail]['$schema'];
|
||||
}
|
||||
|
||||
return iconMetaData;
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import { eventHandler, getQuery, setResponseHeader } from 'h3';
|
||||
import iconNodes from '../../../data/lab/iconNodes';
|
||||
type IconNodeWithKeys = [elementName: string, attrs: Record<string, string> & { key?: string }][];
|
||||
|
||||
export default eventHandler((event) => {
|
||||
const query = getQuery(event);
|
||||
|
||||
const withUniqueKeys = query.withUniqueKeys === 'true';
|
||||
|
||||
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400');
|
||||
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
|
||||
|
||||
if (withUniqueKeys) {
|
||||
return iconNodes;
|
||||
}
|
||||
|
||||
return Object.entries(iconNodes).reduce((acc, [name, iconNode]) => {
|
||||
if (withUniqueKeys) {
|
||||
return [name, iconNode];
|
||||
}
|
||||
|
||||
const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => {
|
||||
return [name, attrs];
|
||||
});
|
||||
|
||||
acc[name] = newIconNode;
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
});
|
||||
@@ -101,6 +101,7 @@ export default defineConfig({
|
||||
},
|
||||
{ text: 'Packages', link: '/packages' },
|
||||
{ text: 'Showcase', link: '/showcase' },
|
||||
{ text: 'Merch', link: 'https://merch.lucide.dev/' },
|
||||
],
|
||||
sidebar,
|
||||
socialLinks: [
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
},
|
||||
{
|
||||
"name": "navigation",
|
||||
"title": "Navigation, Maps, and POIs"
|
||||
"title": "Navigation & Places"
|
||||
},
|
||||
{
|
||||
"name": "notifications",
|
||||
|
||||
@@ -209,5 +209,24 @@
|
||||
],
|
||||
"source": "https://github.com/shx08/strapi-lucide-icons",
|
||||
"documentation": "https://github.com/shx08/strapi-lucide-icons/blob/main/README.md"
|
||||
},
|
||||
{
|
||||
"name": "lucide-lit",
|
||||
"description": "Implementation of Lucide icons as Lit web components.",
|
||||
"icon": "/framework-logos/lit.svg",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "Latest Stable Version",
|
||||
"src": "https://img.shields.io/npm/v/@gyeonghokim/lucide-lit",
|
||||
"href": "https://www.npmjs.com/package/@gyeonghokim/lucide-lit"
|
||||
},
|
||||
{
|
||||
"alt": "Total Downloads",
|
||||
"src": "https://img.shields.io/npm/dw/@gyeonghokim/lucide-lit",
|
||||
"href": "https://www.npmjs.com/package/@gyeonghokim/lucide-lit"
|
||||
}
|
||||
],
|
||||
"source": "https://github.com/GyeongHoKim/lucide-lit",
|
||||
"documentation": "https://github.com/GyeongHoKim/lucide-lit/blob/main/README.md"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -24,13 +24,6 @@ const getHeadConfig = ({
|
||||
defer: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{
|
||||
src: 'https://media.bitterbrains.com/main.js?from=LUCIDE&type=top',
|
||||
async: 'true',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
|
||||
@@ -72,6 +72,11 @@ export const reactNativeSidebar = [
|
||||
link: '/guide/react-native/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
{
|
||||
text: 'Optimizations',
|
||||
link: '/guide/react-native/advanced/optimizations',
|
||||
desc: 'Optimizing your app for performance',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,6 +11,14 @@ const { isDark } = useData();
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
function limitHexInput(val: string) {
|
||||
if (val.startsWith('#')) {
|
||||
return val.slice(0, 9);
|
||||
}
|
||||
|
||||
return val.slice(0, 8);
|
||||
}
|
||||
|
||||
const value = computed({
|
||||
get: () => {
|
||||
if (props.modelValue == null || props.modelValue === 'currentColor') {
|
||||
@@ -19,7 +27,7 @@ const value = computed({
|
||||
|
||||
return props.modelValue;
|
||||
},
|
||||
set: (val) => emit('update:modelValue', val),
|
||||
set: (val) => emit('update:modelValue', limitHexInput(val)),
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -41,6 +49,7 @@ const value = computed({
|
||||
class="color-input-text"
|
||||
aria-label="Color picker input"
|
||||
v-model="value"
|
||||
maxlength="9"
|
||||
placeholder="[default]"
|
||||
/>
|
||||
</div>
|
||||
@@ -91,6 +100,7 @@ const value = computed({
|
||||
background: transparent;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 13px;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
text-align: left;
|
||||
border-radius: 8px;
|
||||
cursor: text;
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vitepress';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const frameworks = [
|
||||
{
|
||||
ariaLabel: 'Get JavaScript certificates from certificates.dev',
|
||||
image: '/certificates-dev/js-certificates-dev.jpg',
|
||||
href: 'https://certificates.dev/javascript?friend=LUCIDE',
|
||||
routes: [
|
||||
'/guide/lucide/',
|
||||
'/guide/svelte/',
|
||||
'/guide/solid/',
|
||||
'/guide/astro/',
|
||||
'/guide/static/'
|
||||
],
|
||||
},
|
||||
{
|
||||
ariaLabel: 'Get React certificates from certificates.dev',
|
||||
image: '/certificates-dev/react-certificates-dev.jpg',
|
||||
href: 'https://certificates.dev/react?friend=LUCIDE',
|
||||
routes: [
|
||||
'/guide/react/',
|
||||
'/guide/react-native/',
|
||||
'/guide/preact/',
|
||||
],
|
||||
},
|
||||
{
|
||||
ariaLabel: 'Get Vue certificates from certificates.dev',
|
||||
href: 'https://certificates.dev/vuejs?friend=LUCIDE',
|
||||
image: '/certificates-dev/vue-certificates-dev.jpg',
|
||||
routes: [
|
||||
'/guide/vue/',
|
||||
],
|
||||
},
|
||||
{
|
||||
ariaLabel: 'Get Angular certificates from certificates.dev',
|
||||
href: 'https://certificates.dev/angular?friend=LUCIDE',
|
||||
image: '/certificates-dev/angular-certificates-dev.jpg',
|
||||
routes: [
|
||||
'/guide/angular/',
|
||||
],
|
||||
}
|
||||
];
|
||||
|
||||
const selected = computed(() => {
|
||||
const current = frameworks.find(({ routes }) => {
|
||||
return routes.some(route => router.route.path.startsWith(route));
|
||||
});
|
||||
|
||||
return current || frameworks[0];
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a class="certificates-link" :href="selected.href" :aria-label="selected.ariaLabel" target="_blank" rel="noopener noreferrer">
|
||||
<img :src="selected.image" :alt="selected.ariaLabel" />
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.certificates-link {
|
||||
/* padding: 20px; */
|
||||
border-radius: 12px;
|
||||
min-height: 100px;
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
background-color: var(--vp-carbon-ads-bg-color);
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.certificates-link img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
border-radius: 6px;
|
||||
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.certificates-link:hover img {
|
||||
transform: scale(1.05);
|
||||
opacity: 0.9;
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -13,23 +13,21 @@ const { go } = useRouter()
|
||||
<HomeSectionTitle>Available For:</HomeSectionTitle>
|
||||
<div class="packages-list">
|
||||
<a
|
||||
v-for="{ name, logo, logoDark } in data.packages"
|
||||
:href="`/guide/packages/${name}`"
|
||||
v-for="{ name, label, path, logo, logoDark } in data.packages"
|
||||
:href="path"
|
||||
class="package-logo"
|
||||
:aria-label="`Read more about: ${name} package`"
|
||||
@click.prevent="go(`/guide/packages/${name}`)"
|
||||
@click.prevent="go(path)"
|
||||
>
|
||||
<img
|
||||
:src="logo"
|
||||
:class="{ light: logoDark, 'image-logo': true }"
|
||||
:alt="`${name} logo`"
|
||||
:alt="label"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
<img
|
||||
v-if="logoDark"
|
||||
:src="logoDark"
|
||||
:alt="`${name} logo`"
|
||||
:alt="label"
|
||||
class="image-logo dark"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { IconEntity } from '../../types';
|
||||
import { computed, ref } from 'vue';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import { useIconStyleContext } from '../../composables/useIconStyle';
|
||||
import { squarePen } from '../../../data/iconNodes';
|
||||
|
||||
const props = defineProps<{
|
||||
name: IconEntity['name'];
|
||||
@@ -19,6 +20,31 @@ const iconComponent = computed(() => {
|
||||
if (!props.name || !props.iconNode) return null;
|
||||
return createLucideIcon(props.name, props.iconNode);
|
||||
});
|
||||
|
||||
const SquarePenIcon = createLucideIcon('SquarePen', squarePen);
|
||||
|
||||
const innerSvg = computed(() => {
|
||||
if (!props.iconNode) return '';
|
||||
|
||||
const string = props.iconNode
|
||||
.map(([tag, attrs]) => {
|
||||
const attributes = Object.entries(attrs)
|
||||
.map(([key, value]) => `${key}="${value}"`)
|
||||
.join(' ');
|
||||
return `<${tag} ${attributes}/>`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
return string
|
||||
.replace(/>[\r\n ]+</g, '><')
|
||||
.replace(/(<.*?>)|\s+/g, (m, $1) => $1 || ' ')
|
||||
.trim();
|
||||
});
|
||||
|
||||
const base64InnerSvg = computed(() => {
|
||||
if (typeof window === 'undefined') return '';
|
||||
return btoa(innerSvg.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -57,6 +83,16 @@ const iconComponent = computed(() => {
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<a
|
||||
:href="`https://studio.lucide.dev/edit?value=${encodeURIComponent(base64InnerSvg)}&name=${name}&utm_source=lucide.dev&utm_medium=icon-detail-preview`"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="studio-button"
|
||||
title="Open in Lucide Studio"
|
||||
aria-label="Open in Lucide Studio"
|
||||
>
|
||||
<SquarePenIcon :size="14" />
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -75,6 +111,8 @@ const iconComponent = computed(() => {
|
||||
position: relative;
|
||||
background: var(--vp-c-bg-alt);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.icon-container > :deep(svg:not(.icon-grid)) {
|
||||
width: 100%;
|
||||
@@ -85,6 +123,34 @@ const iconComponent = computed(() => {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.studio-button {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--vp-c-text-2);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s, color 0.2s, background-color 0.2s;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.icon-container:hover .studio-button,
|
||||
.studio-button:focus-visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.studio-button:hover,
|
||||
.studio-button:focus-visible {
|
||||
background: var(--vp-c-bg-soft);
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.icon-component.customizable {
|
||||
will-change: width, height, stroke-width, stroke;
|
||||
/* color: var(--customize-color, currentColor);
|
||||
|
||||
@@ -44,8 +44,12 @@ function setActiveIconName(name: string) {
|
||||
activeIconName.value = name;
|
||||
}
|
||||
|
||||
const { execute: fetchTags, data: tags } = useFetchTags();
|
||||
const { execute: fetchCategories, data: categoriesMap } = useFetchCategories();
|
||||
const { execute: fetchTags, data: tags, isFetching: isFetchingTags } = useFetchTags();
|
||||
const {
|
||||
execute: fetchCategories,
|
||||
data: categoriesMap,
|
||||
isFetching: isFetchingCategories,
|
||||
} = useFetchCategories();
|
||||
|
||||
const overviewEl = ref<HTMLElement | null>(null);
|
||||
const { width: containerWidth } = useElementSize(overviewEl);
|
||||
@@ -117,6 +121,9 @@ const categoriesList = computed(() => {
|
||||
}, []);
|
||||
});
|
||||
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
|
||||
const isSearchMetadataLoading = computed(
|
||||
() => searchQuery.value.length > 0 && (tags.value == null || categoriesMap.value == null),
|
||||
);
|
||||
|
||||
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(categoriesList, {
|
||||
itemHeight: ICON_SIZE + ICON_GRID_GAP,
|
||||
@@ -135,15 +142,21 @@ onMounted(() => {
|
||||
useEventListener(window, 'scroll', containerProps.onScroll);
|
||||
});
|
||||
|
||||
function onFocusSearchInput() {
|
||||
if (tags.value == null) {
|
||||
fetchTags();
|
||||
function loadSearchMetadata() {
|
||||
if (tags.value == null && !isFetchingTags.value) {
|
||||
void fetchTags();
|
||||
}
|
||||
if (categoriesMap.value == null) {
|
||||
fetchCategories();
|
||||
if (categoriesMap.value == null && !isFetchingCategories.value) {
|
||||
void fetchCategories();
|
||||
}
|
||||
}
|
||||
|
||||
watch(searchQuery, (searchString) => {
|
||||
if (searchString !== '') {
|
||||
loadSearchMetadata();
|
||||
}
|
||||
});
|
||||
|
||||
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
|
||||
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
|
||||
|
||||
@@ -178,11 +191,11 @@ function handleCloseDrawer() {
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
ref="searchInput"
|
||||
@focus="onFocusSearchInput"
|
||||
@focus="loadSearchMetadata"
|
||||
/>
|
||||
</StickyBar>
|
||||
<NoResults
|
||||
v-if="searchPlaceholder.isNoResults"
|
||||
v-if="searchPlaceholder.isNoResults && !isSearchMetadataLoading"
|
||||
:searchQuery="searchPlaceholder.query"
|
||||
:isBrandSearch="searchPlaceholder.isBrand"
|
||||
@clear="searchQuery = ''"
|
||||
|
||||
@@ -52,8 +52,12 @@ const props = defineProps<{
|
||||
const activeIconName = ref(null);
|
||||
const selectedSort = ref(SORTING[0])
|
||||
|
||||
const { execute: fetchTags, data: tags } = useFetchTags();
|
||||
const { execute: fetchCategories, data: categories } = useFetchCategories();
|
||||
const { execute: fetchTags, data: tags, isFetching: isFetchingTags } = useFetchTags();
|
||||
const {
|
||||
execute: fetchCategories,
|
||||
data: categories,
|
||||
isFetching: isFetchingCategories,
|
||||
} = useFetchCategories();
|
||||
|
||||
const overviewEl = ref<HTMLElement | null>(null);
|
||||
const { width: containerWidth } = useElementSize(overviewEl);
|
||||
@@ -110,6 +114,9 @@ const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
|
||||
]);
|
||||
|
||||
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
|
||||
const isSearchMetadataLoading = computed(
|
||||
() => searchQuery.value.length > 0 && (tags.value == null || categories.value == null),
|
||||
);
|
||||
|
||||
const chunkedIcons = computed(() => {
|
||||
return chunkArray(searchResults.value, columnSize.value);
|
||||
@@ -135,15 +142,21 @@ function setActiveIconName(name: string) {
|
||||
activeIconName.value = name;
|
||||
}
|
||||
|
||||
function onFocusSearchInput() {
|
||||
if (tags.value == null) {
|
||||
fetchTags();
|
||||
function loadSearchMetadata() {
|
||||
if (tags.value == null && !isFetchingTags.value) {
|
||||
void fetchTags();
|
||||
}
|
||||
if (categories.value == null) {
|
||||
fetchCategories();
|
||||
if (categories.value == null && !isFetchingCategories.value) {
|
||||
void fetchCategories();
|
||||
}
|
||||
}
|
||||
|
||||
watch(searchQuery, (searchString) => {
|
||||
if (searchString !== '') {
|
||||
loadSearchMetadata();
|
||||
}
|
||||
});
|
||||
|
||||
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
|
||||
|
||||
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
|
||||
@@ -179,7 +192,7 @@ function handleCloseDrawer() {
|
||||
ref="searchInput"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
@focus="onFocusSearchInput"
|
||||
@focus="loadSearchMetadata"
|
||||
/>
|
||||
|
||||
<Select
|
||||
@@ -197,7 +210,7 @@ function handleCloseDrawer() {
|
||||
</Select>
|
||||
</StickyBar>
|
||||
<NoResults
|
||||
v-if="searchPlaceholder.isNoResults"
|
||||
v-if="searchPlaceholder.isNoResults && !isSearchMetadataLoading"
|
||||
:searchQuery="searchPlaceholder.query"
|
||||
:isBrandSearch="searchPlaceholder.isBrand"
|
||||
@clear="searchQuery = ''"
|
||||
|
||||
@@ -73,13 +73,11 @@ const customizingActive = computed(() => {
|
||||
id="icon-color"
|
||||
label="Color"
|
||||
>
|
||||
<template #display>
|
||||
<ColorPicker
|
||||
v-model="color"
|
||||
id="icon-color"
|
||||
class="color-picker"
|
||||
/>
|
||||
</template>
|
||||
<ColorPicker
|
||||
v-model="color"
|
||||
id="icon-color"
|
||||
class="color-picker"
|
||||
/>
|
||||
</InputField>
|
||||
|
||||
<InputField
|
||||
@@ -161,6 +159,6 @@ const customizingActive = computed(() => {
|
||||
}
|
||||
|
||||
.color-picker {
|
||||
margin-left: auto;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,7 +16,7 @@ export const externalLibContext = {
|
||||
};
|
||||
|
||||
const externalLibIconNodesAPI = {
|
||||
lab: 'https://lab.lucide.dev/api/icon-details',
|
||||
lab: `${import.meta.env.DEV ? 'http://localhost:3000' : ''}/api/lab/icon-details`,
|
||||
};
|
||||
|
||||
export function useExternalLibs(): ExternalLibContext {
|
||||
@@ -32,13 +32,15 @@ export function useExternalLibs(): ExternalLibContext {
|
||||
newExternalIconNodes[lib] = savedIconNodes[lib];
|
||||
} else {
|
||||
const response = await fetch(externalLibIconNodesAPI[lib]);
|
||||
const iconNodes = await response.json();
|
||||
const iconDetails = await response.json();
|
||||
|
||||
if (iconNodes) {
|
||||
newExternalIconNodes[lib] = Object.values(iconNodes).map((iconEntity: IconEntity) => ({
|
||||
...iconEntity,
|
||||
externalLibrary: lib,
|
||||
}));
|
||||
if (iconDetails) {
|
||||
newExternalIconNodes[lib] = Object.values(iconDetails).map((iconEntity: IconEntity) => {
|
||||
return {
|
||||
...iconEntity,
|
||||
externalLibrary: lib,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import { CATEGORY_VIEW_CONTEXT, categoryViewContext } from './composables/useCat
|
||||
import { EXTERNAL_LIBS_CONTEXT, externalLibContext } from './composables/useExternalLibs';
|
||||
import FrameworkSelect from './components/guide/FrameworkSelect.vue';
|
||||
import SnackPlayer from './components/editors/SnackPlayer.vue';
|
||||
import CertificatesLink from './components/guide/CertificatesLink.vue';
|
||||
|
||||
const theme: Partial<Theme> = {
|
||||
extends: DefaultTheme,
|
||||
@@ -23,7 +22,6 @@ const theme: Partial<Theme> = {
|
||||
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
|
||||
'home-hero-image': () => h(HomeHeroIconsCard),
|
||||
'home-hero-actions-after': () => h(HomeHeroAfter),
|
||||
'aside-ads-before': () => h(CertificatesLink),
|
||||
// Keep this here for now, we might want to add more things to the top bar in the future
|
||||
// 'layout-top': () => h(LayoutTop),
|
||||
});
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
animation: .3s transition 0s ease;
|
||||
} */
|
||||
|
||||
html {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.VPHome {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
@@ -148,14 +152,6 @@
|
||||
.VPHomeHero .container {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
html.has-bb-banner {
|
||||
--vp-layout-top-height: 72px;
|
||||
}
|
||||
|
||||
html.has-bb-banner {
|
||||
--Dark-Background: #161618;
|
||||
}
|
||||
}
|
||||
|
||||
.VPNavBarHamburger .container > span {
|
||||
@@ -167,12 +163,6 @@
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
html:not(.has-bb-banner) #bb-banner {
|
||||
display: none !important;
|
||||
visibility: hidden !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.VPSidebar {
|
||||
/* Hide Scrollbar */
|
||||
-ms-overflow-style: none;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PageData } from 'vitepress';
|
||||
import { type PageData } from 'vitepress';
|
||||
import getStructuredData from './getStructuredData';
|
||||
import { createGeneralOGImage, createIconOGImage } from './createOGImage';
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ For each icon these attributes are applied, corresponding to the above rules.
|
||||
### Minify paths
|
||||
|
||||
The code of paths can sometimes get quite large. To reduce file size we like to minify the code.
|
||||
We recommend to use [Lucide Studio](https://studio.lucide.dev/) to tidy paths to 3 points of precision.
|
||||
We recommend to use [Lucide Studio](https://studio.lucide.dev/?utm_source=lucide.dev&utm_medium=design-guide) to tidy paths to 3 points of precision.
|
||||
|
||||
### Allowed elements
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@ import Sandpack from '~/.vitepress/theme/components/editors/SandpackAngular.vue'
|
||||
|
||||
While they aren't provided as standalone components, they can be still be passed to the `LucideIcon` component the same way as official icons:
|
||||
|
||||
::: info Limitation
|
||||
Only custom icons that follow Lucide's [code conventions](../../../contribute/icon-design-guide.md#code-conventions) are supported.
|
||||
:::
|
||||
|
||||
### Directly as LucideIconData
|
||||
|
||||
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular,@lucide/lab"}
|
||||
|
||||
@@ -12,19 +12,23 @@ Implementation of the Lucide icon library for web applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add lucide@next
|
||||
pnpm add lucide
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add lucide@next
|
||||
yarn add lucide
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install lucide@next
|
||||
npm install lucide
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add lucide@next
|
||||
bun add lucide
|
||||
```
|
||||
|
||||
```sh [deno]
|
||||
deno add lucide
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -38,19 +42,23 @@ Implementation of the Lucide icon library for React applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add lucide-react@next
|
||||
pnpm add lucide-react
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add lucide-react@next
|
||||
yarn add lucide-react
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install lucide-react@next
|
||||
npm install lucide-react
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add lucide-react@next
|
||||
bun add lucide-react
|
||||
```
|
||||
|
||||
```sh [deno]
|
||||
deno add lucide-react
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -80,6 +88,10 @@ npm install @lucide/vue
|
||||
bun add @lucide/vue
|
||||
```
|
||||
|
||||
```sh [deno]
|
||||
deno add @lucide/vue
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
For more details, see the [documentation](./vue/index.md).
|
||||
@@ -91,19 +103,23 @@ Implementation of the Lucide icon library for Svelte applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add @lucide/svelte@next
|
||||
pnpm add @lucide/svelte
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add @lucide/svelte@next
|
||||
yarn add @lucide/svelte
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install @lucide/svelte@next
|
||||
npm install @lucide/svelte
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add @lucide/svelte@next
|
||||
bun add @lucide/svelte
|
||||
```
|
||||
|
||||
```sh [deno]
|
||||
deno add @lucide/svelte
|
||||
```
|
||||
:::
|
||||
> `@lucide/svelte` is only for Svelte 5, for Svelte 4 use the `lucide-svelte` package.
|
||||
@@ -117,19 +133,23 @@ Implementation of the Lucide icon library for Solid applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add lucide-solid@next
|
||||
pnpm add lucide-solid
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add lucide-solid@next
|
||||
yarn add lucide-solid
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install lucide-solid@next
|
||||
npm install lucide-solid
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add lucide-solid@next
|
||||
bun add lucide-solid
|
||||
```
|
||||
|
||||
```sh [deno]
|
||||
deno add lucide-solid
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -158,6 +178,10 @@ npm install @lucide/angular
|
||||
bun add @lucide/angular
|
||||
```
|
||||
|
||||
```sh [deno]
|
||||
deno add @lucide/angular
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
For more details, see the [documentation](./angular/index.md).
|
||||
@@ -169,19 +193,23 @@ Implementation of the Lucide icon library for preact applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add lucide-preact@next
|
||||
pnpm add lucide-preact
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add lucide-preact@next
|
||||
yarn add lucide-preact
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install lucide-preact@next
|
||||
npm install lucide-preact
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add lucide-preact@next
|
||||
bun add lucide-preact
|
||||
```
|
||||
|
||||
```sh [deno]
|
||||
deno add lucide-preact
|
||||
```
|
||||
|
||||
|
||||
@@ -196,19 +224,23 @@ Implementation of the Lucide icon library for Astro applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add @lucide/astro@next
|
||||
pnpm add @lucide/astro
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add @lucide/astro@next
|
||||
yarn add @lucide/astro
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install @lucide/astro@next
|
||||
npm install @lucide/astro
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add @lucide/astro@next
|
||||
bun add @lucide/astro
|
||||
```
|
||||
|
||||
```sh [deno]
|
||||
deno add @lucide/astro
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -222,19 +254,23 @@ Implementation of the Lucide icon library for multiple usages that like to use:
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add lucide-static@next
|
||||
pnpm add lucide-static
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add lucide-static@next
|
||||
yarn add lucide-static
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install lucide-static@next
|
||||
npm install lucide-static
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add lucide-static@next
|
||||
bun add lucide-static
|
||||
```
|
||||
|
||||
```sh [deno]
|
||||
deno add lucide-static
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: Optimizations - React Native
|
||||
description: Learn how to optimize the bundle size of your React Native and Expo web exports by tree-shaking Lucide icons.
|
||||
---
|
||||
|
||||
# Optimizations
|
||||
|
||||
By default you import icons from the `lucide-react-native` entry point:
|
||||
|
||||
```jsx
|
||||
import { Camera } from 'lucide-react-native';
|
||||
```
|
||||
|
||||
This is convenient, but it relies on the bundler to _tree-shake_ the entry point (a "barrel" file that re-exports every icon) so that only the icons you actually use end up in your bundle.
|
||||
|
||||
On native platforms this is usually fine. On **web exports** it can be a problem, because the Metro bundler is currently not very good at tree-shaking barrel imports and may include **all** icons in your final bundle.
|
||||
|
||||
## The Metro tree-shaking limitation
|
||||
|
||||
When you build for web with Expo (`expo export --platform web`), Metro is used to bundle your JavaScript. Metro bundles modules on-demand and, by default, does not remove unused exports from a barrel file.
|
||||
|
||||
This means an import like the one below can pull in the entire icon set, even though only a single icon is rendered:
|
||||
|
||||
```jsx
|
||||
// May bundle every Lucide icon on web exports
|
||||
import { Camera } from 'lucide-react-native';
|
||||
```
|
||||
|
||||
Expo documents an experimental optimization for [React Native web imports](https://docs.expo.dev/guides/tree-shaking/#react-native-web-imports) and for [removing unused imports and exports](https://docs.expo.dev/guides/tree-shaking/#remove-unused-imports-and-exports). In practice this tree-shaking does **not** currently work reliably for `lucide-react-native` barrel imports, so the unused icons are not removed from web exports.
|
||||
|
||||
## Recommended: import icons individually
|
||||
|
||||
To guarantee that only the icons you use are bundled — regardless of the bundler's tree-shaking capabilities — import each icon directly from its own module:
|
||||
|
||||
```jsx
|
||||
import Camera from 'lucide-react-native/icons/camera';
|
||||
|
||||
// Usage
|
||||
const App = () => {
|
||||
return <Camera />;
|
||||
};
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
Because each icon lives in its own file, the bundler only ever includes the icons you explicitly import. This keeps your web export small without depending on Metro's experimental tree-shaking.
|
||||
|
||||
The icon module name is the [kebab-case](https://developer.mozilla.org/en-US/docs/Glossary/Kebab_case) version of the icon name. For example, the `ArrowRight` icon is imported from `lucide-react-native/icons/arrow-right`:
|
||||
|
||||
```jsx
|
||||
import ArrowRight from 'lucide-react-native/icons/arrow-right';
|
||||
```
|
||||
|
||||
::: tip
|
||||
Individual icon imports work identically on native and web, so you can safely use this style across your whole project to keep bundles as small as possible on every platform.
|
||||
:::
|
||||
@@ -1,24 +1,26 @@
|
||||
import { IconEntity } from '../../.vitepress/theme/types';
|
||||
import iconNodes from '../../.vitepress/data/lab/iconNodes';
|
||||
import iconPopularity from '../../.vitepress/data/lab/iconPopularity';
|
||||
import iconMetaData from '../../.vitepress/data/lab/iconMetaData';
|
||||
import { type IconEntity } from '../../.vitepress/theme/types';
|
||||
|
||||
export default {
|
||||
paths: async () => {
|
||||
try {
|
||||
const iconDetailsResponse = await fetch('https://lab.lucide.dev/api/icon-details');
|
||||
const iconDetails = (await iconDetailsResponse.json()) as Record<string, IconEntity>;
|
||||
return (Object.entries(iconNodes) as unknown as IconEntity[]).map(([name, iconNode]) => {
|
||||
const iconDetails = iconMetaData[name];
|
||||
|
||||
return Object.values(iconDetails).map((iconEntity) => {
|
||||
const params = {
|
||||
externalLibrary: 'lab',
|
||||
...iconEntity,
|
||||
};
|
||||
delete iconDetails['$schema'];
|
||||
|
||||
return {
|
||||
params,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching icon details:', error);
|
||||
return [];
|
||||
}
|
||||
const params = {
|
||||
name,
|
||||
iconNode,
|
||||
externalLibrary: 'lab',
|
||||
popularity: iconPopularity[name]?.count ?? 0,
|
||||
...iconDetails,
|
||||
};
|
||||
|
||||
return {
|
||||
params,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -8,19 +8,21 @@
|
||||
"docs:dev": "pnpm run /^prebuild:.*/ && vitepress dev",
|
||||
"docs:build": "pnpm run /^prebuild:.*/ && vitepress build",
|
||||
"docs:preview": "vitepress preview",
|
||||
"build:docs": "vitepress build",
|
||||
"build:docs": "NODE_OPTIONS=--max-old-space-size=8192 vitepress build",
|
||||
"prebuild:iconNodes": "node ./scripts/writeIconNodes.mts",
|
||||
"prebuild:metaJson": "node ./scripts/writeIconMetaIndex.mts",
|
||||
"prebuild:releaseJson": "node ./scripts/writeReleaseMetadata.mts",
|
||||
"prebuild:categoriesJson": "node ./scripts/writeCategoriesMetadata.mts",
|
||||
"prebuild:relatedIcons": "node ./scripts/writeIconRelatedIcons.mts",
|
||||
"prebuild:iconDetails": "node ./scripts/writeIconDetails.mts",
|
||||
"prebuild:iconPopularity": "node ./scripts/writeiconPopularity.mts",
|
||||
"prebuild:iconPopularity": "node ./scripts/writeiconPopularity.mts && node ./scripts/writeiconPopularityIndex.mts",
|
||||
"prebuild:brandStopwords": "node ./scripts/writeBrandStopwords.mts",
|
||||
"prebuild:writeLabIconNodes": "node ./scripts/writeLabIconNodes.mts",
|
||||
"prebuild:writeLabIconMetaIndex": "node ./scripts/writeLabIconMetaIndex.mts",
|
||||
"postbuild:vercelJson": "node ./scripts/writeVercelOutput.mts",
|
||||
"dev": "npx nitropack dev",
|
||||
"prebuild:api": "npx nitropack prepare",
|
||||
"build:api": "npx nitropack build",
|
||||
"dev": "nitropack dev",
|
||||
"prebuild:api": "nitropack prepare",
|
||||
"build:api": "NODE_OPTIONS=--max-old-space-size=8192 nitropack build",
|
||||
"build": "pnpm run /^prebuild:.*/ && pnpm run build:api && pnpm run build:docs && pnpm postbuild:vercelJson",
|
||||
"preview": "node .output/server/index.mjs"
|
||||
},
|
||||
@@ -32,31 +34,32 @@
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@rollup/plugin-replace": "^6.0.3",
|
||||
"@types/semver": "^7.7.1",
|
||||
"h3": "^1.15.9",
|
||||
"h3": "^1.15.11",
|
||||
"markdown-it": "^14.3.0",
|
||||
"markdown-it-container": "^4.0.0",
|
||||
"nitropack": "^2.13.4",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"svg-path-commander": "^2.1.11",
|
||||
"vitepress": "^1.6.4",
|
||||
"vitepress-plugin-group-icons": "^1.6.5",
|
||||
"vitepress-plugin-llms": "^1.11.1",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markdown-it-container": "^4.0.0",
|
||||
"semver": "^7.7.3",
|
||||
"semver": "^7.8.5",
|
||||
"sharp": "^0.34.5",
|
||||
"shiki": "^3.15.0",
|
||||
"shiki": "^3.23.0",
|
||||
"simple-git": "^3.36.0",
|
||||
"sitemap": "^7.1.2",
|
||||
"sitemap": "^7.1.3",
|
||||
"svg-path-commander": "^2.2.1",
|
||||
"svg-pathdata": "^6.0.3",
|
||||
"svgson": "^5.3.1"
|
||||
"svgson": "^5.3.1",
|
||||
"vitepress": "^1.6.4",
|
||||
"vitepress-plugin-group-icons": "^1.7.5",
|
||||
"vitepress-plugin-llms": "^1.13.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/vue": "^1.1.9",
|
||||
"@floating-ui/vue": "^1.1.11",
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@lucide/vue": "workspace:*",
|
||||
"@vueuse/components": "^14.0.0",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"@vueuse/components": "^14.3.0",
|
||||
"@vueuse/core": "^14.3.0",
|
||||
"@vueuse/integrations": "^14.3.0",
|
||||
"element-to-path": "^1.2.1",
|
||||
"fuse.js": "^7.1.0",
|
||||
"fuse.js": "^7.5.0",
|
||||
"jszip": "^3.10.1",
|
||||
"lodash-es": "^4.18.1",
|
||||
"lucide": "workspace:*",
|
||||
@@ -65,6 +68,6 @@
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"sandpack-vue3": "3.1.12",
|
||||
"vue": "^3.5.24"
|
||||
"vue": "^3.5.39"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 79 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 200" fill="none">
|
||||
<path fill="#00e8ff" d="M40 120l20-60l90 90l-30 50l-40-40h-20" />
|
||||
<path fill="#283198" d="M80 160 L80 80 L120 40 L 120 120 M0 160 L40 200 L40 120 L20 120" />
|
||||
<path fill="#324fff" d="M40 120v-80l40-40v80M120 200v-80l40-40v80M0 160v-80l40 40" />
|
||||
<path fill="#0ff" d="M40 200v-80l40 40" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 377 B |
@@ -23,12 +23,12 @@ const writeIconFiles = icons.map(async (iconFileName) => {
|
||||
const location = path.resolve(iconDetailsDirectory, `${iconName}.ts`);
|
||||
|
||||
const contents = `\
|
||||
import iconNode from '../iconNodes/${iconName}.node.json'
|
||||
import metaData from '../../../../icons/${iconName}.json'
|
||||
import releaseData from '../releaseMetadata/${iconName}.json'
|
||||
import popularity from '../iconPopularity/${iconName}.json'
|
||||
import iconNode from '../iconNodes/${iconName}.node.json' with { type: 'json' };
|
||||
import metaData from '../../../../icons/${iconName}.json' with { type: 'json' };
|
||||
import releaseData from '../releaseMetadata/${iconName}.json' with { type: 'json' };
|
||||
import popularity from '../iconPopularity/${iconName}.json' with { type: 'json' };
|
||||
|
||||
const { tags, categories, contributors, aliases, deprecated, deprecationReason, toBeRemovedInVersion } = metaData
|
||||
const { tags, categories, contributors, aliases, deprecated, deprecationReason, toBeRemovedInVersion } = metaData;
|
||||
|
||||
const iconDetails = {
|
||||
name: '${iconName}',
|
||||
@@ -42,9 +42,9 @@ const iconDetails = {
|
||||
deprecationReason,
|
||||
toBeRemovedInVersion,
|
||||
...releaseData,
|
||||
}
|
||||
};
|
||||
|
||||
export default iconDetails
|
||||
export default iconDetails;
|
||||
`;
|
||||
|
||||
await fs.promises.writeFile(location, contents, 'utf-8');
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
|
||||
|
||||
const currentDir = process.cwd();
|
||||
const ICONS_DIR = path.resolve(currentDir, '../lab');
|
||||
const iconJsonFiles = await readSvgDirectory(ICONS_DIR, '.json');
|
||||
|
||||
const labDirectory = path.resolve(currentDir, '.vitepress/data/lab');
|
||||
const location = path.resolve(currentDir, labDirectory, 'iconMetaData.ts');
|
||||
|
||||
if (fs.existsSync(location)) {
|
||||
fs.unlinkSync(location);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(labDirectory)) {
|
||||
fs.mkdirSync(labDirectory);
|
||||
}
|
||||
|
||||
const iconMetaIndexFileImports = [];
|
||||
const iconMetaIndexFileExports = [];
|
||||
|
||||
iconJsonFiles.forEach((iconJsonFile) => {
|
||||
const iconName = path.basename(iconJsonFile, '.json');
|
||||
|
||||
iconMetaIndexFileImports.push(
|
||||
`import ${toCamelCase(iconName)}Metadata from '../../../../lab/${iconName}.json';`,
|
||||
);
|
||||
iconMetaIndexFileExports.push(` '${iconName}': ${toCamelCase(iconName)}Metadata,`);
|
||||
});
|
||||
|
||||
try {
|
||||
await fs.promises.writeFile(
|
||||
location,
|
||||
`\
|
||||
${iconMetaIndexFileImports.join('\n')}
|
||||
|
||||
|
||||
export default {
|
||||
${iconMetaIndexFileExports.join('\n')}
|
||||
}
|
||||
`,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
console.log('Successfully write icon json file index');
|
||||
} catch (error) {
|
||||
throw new Error(`Something went wrong generating icon json file index file,\n ${error}`);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { renderIconsObject } from '@lucide/build-icons';
|
||||
import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
|
||||
|
||||
const currentDir = process.cwd();
|
||||
const ICONS_DIR = path.resolve(currentDir, '../lab');
|
||||
const svgFiles = await readSvgDirectory(ICONS_DIR);
|
||||
const icons = await renderIconsObject(svgFiles, ICONS_DIR, true);
|
||||
|
||||
const iconNodesDirectory = path.resolve(currentDir, '.vitepress/data/lab', 'iconNodes');
|
||||
|
||||
if (fs.existsSync(iconNodesDirectory)) {
|
||||
fs.rmSync(iconNodesDirectory, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
if (!fs.existsSync(iconNodesDirectory)) {
|
||||
fs.mkdirSync(iconNodesDirectory);
|
||||
}
|
||||
|
||||
const iconIndexFile = path.resolve(iconNodesDirectory, `index.ts`);
|
||||
const iconIndexFileImports: string[] = [];
|
||||
const iconIndexFileExports: string[] = [];
|
||||
const iconIndexFileDefaultExports: string[] = [];
|
||||
|
||||
const writeIconFiles = Object.entries(icons).map(async ([iconName, { children }]) => {
|
||||
// We need to use .node.json because the there is a file called package, which is a reserved word for packages.
|
||||
const location = path.resolve(iconNodesDirectory, `${iconName}.node.json`);
|
||||
const iconNode = children.map(({ name, attributes }) => [name, attributes]);
|
||||
|
||||
const output = JSON.stringify(iconNode, null, 2);
|
||||
await fs.promises.writeFile(location, output, 'utf-8');
|
||||
|
||||
iconIndexFileImports.push(
|
||||
`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,`);
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.all(writeIconFiles);
|
||||
await fs.promises.writeFile(
|
||||
iconIndexFile,
|
||||
`\
|
||||
${iconIndexFileImports.join('\n')}
|
||||
|
||||
export {
|
||||
${iconIndexFileExports.join('\n')}
|
||||
}
|
||||
|
||||
export default {
|
||||
${iconIndexFileDefaultExports.join('\n')}
|
||||
}
|
||||
`,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
console.log('Successfully write', writeIconFiles.length, 'iconNodes.');
|
||||
} catch (error) {
|
||||
throw new Error(`Something went wrong generating iconNode files,\n ${error}`);
|
||||
}
|
||||
@@ -5,27 +5,59 @@ import { loadEnvFile } from 'node:process';
|
||||
import { readSvgDirectory } from '@lucide/helpers';
|
||||
|
||||
const currentDir = process.cwd();
|
||||
const ICONS_DIR = path.resolve(currentDir, '../icons');
|
||||
const iconPopularityDirectory = path.resolve(currentDir, '.vitepress/data', 'iconPopularity');
|
||||
const dataDirectory = path.resolve(currentDir, '.vitepress/data');
|
||||
|
||||
try {
|
||||
// Load environment variables from .env file, if it exists.
|
||||
loadEnvFile(`${currentDir}/.env`);
|
||||
} catch (error) {}
|
||||
|
||||
if (fs.existsSync(iconPopularityDirectory)) {
|
||||
fs.rmSync(iconPopularityDirectory, { recursive: true, force: true });
|
||||
interface IconCollection {
|
||||
/** Human readable name, used for logging. */
|
||||
name: string;
|
||||
/** Directory containing the icon `.json` files. */
|
||||
iconsDir: string;
|
||||
/** Directory the `${iconName}.json` popularity files are written to. */
|
||||
outputDir: string;
|
||||
/** Matches an analytics route and captures the icon name in group 1. */
|
||||
routePattern: RegExp;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(iconPopularityDirectory)) {
|
||||
fs.mkdirSync(iconPopularityDirectory);
|
||||
// Lab must come before the default collection because `/icons/lab/foo` also
|
||||
// matches the default `/icons/(...)` pattern; the first match wins per route.
|
||||
const iconCollections: IconCollection[] = [
|
||||
{
|
||||
name: 'lab',
|
||||
iconsDir: path.resolve(currentDir, '../lab'),
|
||||
outputDir: path.resolve(dataDirectory, 'lab', 'iconPopularity'),
|
||||
// e.g. /icons/lab/burger, /icons/lab/burger/ or /icons/lab/burger?ref=homepage
|
||||
routePattern: /^\/icons\/lab\/([^\/\?]+)(?:[\/\?]|$)/,
|
||||
},
|
||||
{
|
||||
name: 'default',
|
||||
iconsDir: path.resolve(currentDir, '../icons'),
|
||||
outputDir: path.resolve(dataDirectory, 'iconPopularity'),
|
||||
// e.g. /icons/activity, /icons/activity/ or /icons/activity?ref=homepage
|
||||
routePattern: /^\/icons\/([^\/\?]+)(?:[\/\?]|$)/,
|
||||
},
|
||||
];
|
||||
|
||||
interface CollectionData extends IconCollection {
|
||||
iconNames: string[];
|
||||
counts: Record<string, number>;
|
||||
}
|
||||
|
||||
const iconJsonFiles = await readSvgDirectory(ICONS_DIR, '.json');
|
||||
const iconNames = iconJsonFiles.map((file) => path.basename(file, '.json'));
|
||||
const collections: CollectionData[] = await Promise.all(
|
||||
iconCollections.map(async (collection) => {
|
||||
const iconJsonFiles = await readSvgDirectory(collection.iconsDir, '.json');
|
||||
const iconNames = iconJsonFiles.map((file) => path.basename(file, '.json'));
|
||||
|
||||
const defaultIconNamePopularity: Record<string, number> = Object.fromEntries(
|
||||
iconNames.map((iconName) => [iconName, 0])
|
||||
return {
|
||||
...collection,
|
||||
iconNames,
|
||||
counts: Object.fromEntries(iconNames.map((iconName) => [iconName, 0])),
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
interface AnalyticsResponse {
|
||||
@@ -35,9 +67,31 @@ interface AnalyticsResponse {
|
||||
}[];
|
||||
}
|
||||
|
||||
const writeIconPopularity = async ({ name, outputDir, counts }: CollectionData) => {
|
||||
if (fs.existsSync(outputDir)) {
|
||||
fs.rmSync(outputDir, { recursive: true, force: true });
|
||||
}
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
|
||||
const writePopularityPromises = Object.entries(counts).map(([iconName, count]) => {
|
||||
const location = path.resolve(outputDir, `${iconName}.json`);
|
||||
return fs.promises.writeFile(location, JSON.stringify({ count }, null, 2), 'utf-8');
|
||||
});
|
||||
|
||||
await Promise.all(writePopularityPromises);
|
||||
console.log(
|
||||
'Successfully written icon popularity data for',
|
||||
writePopularityPromises.length,
|
||||
`${name} icons.`,
|
||||
);
|
||||
};
|
||||
|
||||
const writeAllIconPopularity = () => Promise.all(collections.map(writeIconPopularity));
|
||||
|
||||
if (process.env.LUCIDE_ANALYTICS_TOKEN === undefined) {
|
||||
console.error('LUCIDE_ANALYTICS_TOKEN environment variable is not set. Please set it to fetch analytics data.');
|
||||
process.exit(1);
|
||||
console.warn('LUCIDE_ANALYTICS_TOKEN environment variable is not set. Writing default icon popularity data.');
|
||||
await writeAllIconPopularity();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -65,35 +119,34 @@ try {
|
||||
|
||||
const analyticsData: AnalyticsResponse = await response.json();
|
||||
|
||||
const iconVisitCounts = analyticsData.results.reduce((acc, { dimensions, metrics }) => {
|
||||
for (const { dimensions, metrics } of analyticsData.results) {
|
||||
const [route] = dimensions;
|
||||
const [count] = metrics;
|
||||
|
||||
if (!route.startsWith('/icons')) {
|
||||
return acc
|
||||
continue;
|
||||
}
|
||||
// Only match routes that start with /icons/ and end with an icon name, e.g. /icons/activity, /icons/activity/ or /icons/activity?ref=homepage
|
||||
const match = route.match(/^\/icons\/([^\/\?]+)(?:[\/\?]|$)/);
|
||||
if (match) {
|
||||
const iconName = match[1];
|
||||
|
||||
if (iconNames.includes(iconName)) {
|
||||
acc[iconName] = (acc[iconName] || 0) + count;
|
||||
// Route each visit to the first collection whose pattern matches. Lab is
|
||||
// checked first so `/icons/lab/foo` is not swallowed by the default pattern.
|
||||
for (const collection of collections) {
|
||||
const match = route.match(collection.routePattern);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const iconName = match[1];
|
||||
if (collection.iconNames.includes(iconName)) {
|
||||
collection.counts[iconName] += count;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, defaultIconNamePopularity);
|
||||
|
||||
const writePopularityPromises = Object.entries(iconVisitCounts).map(([iconName, count]) => {
|
||||
const location = path.resolve(iconPopularityDirectory, `${iconName}.json`);
|
||||
return fs.promises.writeFile(location, JSON.stringify({ count }, null, 2), 'utf-8');
|
||||
});
|
||||
|
||||
await Promise.all(writePopularityPromises);
|
||||
console.log('Successfully written icon popularity data for', writePopularityPromises.length, 'icons.');
|
||||
await writeAllIconPopularity();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching analytics data:', error);
|
||||
await writeAllIconPopularity();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
|
||||
|
||||
const currentDir = process.cwd();
|
||||
const ICONS_DIR = path.resolve(currentDir, '../lab');
|
||||
const iconJsonFiles = await readSvgDirectory(ICONS_DIR, '.json');
|
||||
|
||||
const labIconPopularityDirectory = path.resolve(currentDir, '.vitepress/data/lab/iconPopularity');
|
||||
const location = path.resolve(currentDir, labIconPopularityDirectory, 'index.ts');
|
||||
|
||||
if (fs.existsSync(location)) {
|
||||
fs.unlinkSync(location);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(labIconPopularityDirectory)) {
|
||||
fs.mkdirSync(labIconPopularityDirectory);
|
||||
}
|
||||
|
||||
const iconMetaIndexFileImports = [];
|
||||
const iconMetaIndexFileExports = [];
|
||||
|
||||
iconJsonFiles.forEach((iconJsonFile) => {
|
||||
const iconName = path.basename(iconJsonFile, '.json');
|
||||
|
||||
iconMetaIndexFileImports.push(
|
||||
`import ${toCamelCase(iconName)}Popularity from './${iconName}.json' with { type: 'json' };`,
|
||||
);
|
||||
iconMetaIndexFileExports.push(` '${iconName}': ${toCamelCase(iconName)}Popularity,`);
|
||||
});
|
||||
|
||||
try {
|
||||
await fs.promises.writeFile(
|
||||
location,
|
||||
`\
|
||||
${iconMetaIndexFileImports.join('\n')}
|
||||
|
||||
|
||||
export default {
|
||||
${iconMetaIndexFileExports.join('\n')}
|
||||
}
|
||||
`,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
console.log('Successfully write icon json file index');
|
||||
} catch (error) {
|
||||
throw new Error(`Something went wrong generating icon json file index file,\n ${error}`);
|
||||
}
|
||||
@@ -40,7 +40,7 @@
|
||||
}
|
||||
},
|
||||
"dependentRequired": {
|
||||
"deprecated": ["deprecationReason", "toBeRemovedInVersion"],
|
||||
"deprecated": ["deprecationReason"],
|
||||
"deprecationReason": ["deprecated"],
|
||||
"toBeRemovedInVersion": ["deprecated"]
|
||||
}
|
||||
@@ -134,7 +134,7 @@
|
||||
}
|
||||
},
|
||||
"dependentRequired": {
|
||||
"deprecated": ["deprecationReason", "toBeRemovedInVersion"],
|
||||
"deprecated": ["deprecationReason"],
|
||||
"deprecationReason": ["deprecated"],
|
||||
"toBeRemovedInVersion": ["deprecated"]
|
||||
},
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<path d="M15.697 14h5.606" />
|
||||
<path d="m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16" />
|
||||
<path d="M3.304 13h6.392" />
|
||||
</svg>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 391 B |
@@ -21,10 +21,15 @@
|
||||
"paid",
|
||||
"partner",
|
||||
"promo",
|
||||
"sponsor"
|
||||
"sponsor",
|
||||
"audio description",
|
||||
"video description",
|
||||
"described video",
|
||||
"visual description"
|
||||
],
|
||||
"categories": [
|
||||
"multimedia",
|
||||
"accessibility",
|
||||
"notifications"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
{
|
||||
"name": "alarm-check",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
{
|
||||
"name": "alarm-minus",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
{
|
||||
"name": "alarm-plus",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,10 +11,9 @@
|
||||
>
|
||||
<path d="M10 10H6" />
|
||||
<path d="M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2" />
|
||||
<path
|
||||
d="M19 18h2a1 1 0 0 0 1-1v-3.28a1 1 0 0 0-.684-.948l-1.923-.641a1 1 0 0 1-.578-.502l-1.539-3.076A1 1 0 0 0 16.382 8H14" />
|
||||
<path d="M19 18h2a1 1 0 0 0 1-1v-3.28a1 1 0 0 0-.684-.948l-1.923-.641a1 1 0 0 1-.578-.502l-1.539-3.076A1 1 0 0 0 16.382 8H14" />
|
||||
<path d="M8 8v4" />
|
||||
<path d="M9 18h6" />
|
||||
<circle cx="17" cy="18" r="2" />
|
||||
<circle cx="7" cy="18" r="2" />
|
||||
</svg>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 553 B After Width: | Height: | Size: 550 B |
@@ -25,8 +25,7 @@
|
||||
{
|
||||
"name": "arrow-down-01",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
{
|
||||
"name": "arrow-down-10",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
{
|
||||
"name": "arrow-down-az",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
{
|
||||
"name": "sort-desc",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
{
|
||||
"name": "arrow-down-za",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
{
|
||||
"name": "arrow-up-01",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
{
|
||||
"name": "arrow-up-10",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
{
|
||||
"name": "arrow-up-az",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
{
|
||||
"name": "sort-asc",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
{
|
||||
"name": "arrow-up-za",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
{
|
||||
"name": "axis-3-d",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
{
|
||||
"name": "verified",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
],
|
||||
"use-cases": [],
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
{
|
||||
"name": "badge-help",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"mfjramirezf"
|
||||
],
|
||||
"use-cases": [
|
||||
"confirm a paid product",
|
||||
"confirm a payment method was added successfully"
|
||||
],
|
||||
"tags": [
|
||||
"banknote",
|
||||
"bill",
|
||||
"currency",
|
||||
"payment",
|
||||
"money",
|
||||
"finance",
|
||||
"document",
|
||||
"verification",
|
||||
"tick",
|
||||
"verified",
|
||||
"accepted",
|
||||
"done",
|
||||
"complete",
|
||||
"found",
|
||||
"paid",
|
||||
"task",
|
||||
"check",
|
||||
"success"
|
||||
],
|
||||
"categories": [
|
||||
"finance"
|
||||
]
|
||||
}
|
||||
@@ -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.748 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v4.875" />
|
||||
<path d="m16 19 2 2 4-4" />
|
||||
<path d="M18 12h.01" />
|
||||
<path d="M6 12h.01" />
|
||||
<circle cx="12" cy="12" r="2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 405 B |
@@ -9,9 +9,9 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M10 3a41 41 0 0 0 0 18" />
|
||||
<path d="M14 3a41 41 0 0 1 0 18" />
|
||||
<path d="M17 3a2 2 0 0 1 1.68.92 15.25 15.25 0 0 1 0 16.16A2 2 0 0 1 17 21H7a2 2 0 0 1-1.68-.92 15.25 15.25 0 0 1 0-16.16A2 2 0 0 1 7 3z" />
|
||||
<path d="M3.84 17h16.32" />
|
||||
<path d="M3.84 7h16.32" />
|
||||
<path d="M10 3a41 41 0 000 18" />
|
||||
<path d="M14 3a41 41 0 010 18" />
|
||||
<path d="M16.997 21a2 2 0 001.68-.92 15.25 15.25 0 000-16.16 2 2 0 00-1.68-.92h-10a2 2 0 00-1.681.92 15.25 15.25 0 000 16.16 2 2 0 001.681.92z" />
|
||||
<path d="M3.54 16h16.914" />
|
||||
<path d="M3.54 8h16.914" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 490 B |
@@ -36,8 +36,7 @@
|
||||
{
|
||||
"name": "between-horizonal-end",
|
||||
"deprecated": true,
|
||||
"deprecationReason": "alias.typo",
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecationReason": "alias.typo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -36,8 +36,7 @@
|
||||
{
|
||||
"name": "between-horizonal-start",
|
||||
"deprecated": true,
|
||||
"deprecationReason": "alias.typo",
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecationReason": "alias.typo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"Patolord"
|
||||
],
|
||||
"use-cases": [
|
||||
"indicate injury or fracture status in health/fitness tracking",
|
||||
"show an error or failed state in healthcare UI flows"
|
||||
],
|
||||
"tags": [
|
||||
"bone",
|
||||
"fracture",
|
||||
"injury",
|
||||
"orthopedic",
|
||||
"medical",
|
||||
"anatomy",
|
||||
"skeletal",
|
||||
"broken",
|
||||
"xray",
|
||||
"trauma",
|
||||
"health",
|
||||
"vet",
|
||||
"veterinary",
|
||||
"crack",
|
||||
"break",
|
||||
"damage"
|
||||
],
|
||||
"categories": [
|
||||
"medical",
|
||||
"animals"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<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 4.5a1 1 0 0 1 5 0 .5.5 0 0 0 .5.5 1 1 0 0 1 0 5c-.81 0-1.8-.7-2.5 0l-1.958 1.957a.15.15 0 0 1-.252-.072l-.493-2.07a.15.15 0 0 0-.111-.112l-2.072-.494a.15.15 0 0 1-.072-.252L14 7c.7-.7 0-1.69 0-2.5" />
|
||||
<path d="m16 20-1-2" />
|
||||
<path d="m20 16-2-1" />
|
||||
<path d="m4 8 2 1" />
|
||||
<path d="m8 4 1 2" />
|
||||
<path d="M9.698 14.19a.15.15 0 0 0 .112.112l2.074.489a.15.15 0 0 1 .072.252L10 17c-.7.7 0 1.69 0 2.5a1 1 0 0 1-5 0 .495.495 0 0 0-.5-.5 1 1 0 0 1 0-5c.81 0 1.8.7 2.5 0l1.956-1.957a.15.15 0 0 1 .252.072z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 733 B |
@@ -29,8 +29,7 @@
|
||||
{
|
||||
"name": "book-template",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 21V7" />
|
||||
<path d="M12 5v16" />
|
||||
<path d="m16 12 2 2 4-4" />
|
||||
<path d="M22 6V4a1 1 0 0 0-1-1h-5a4 4 0 0 0-4 4 4 4 0 0 0-4-4H3a1 1 0 0 0-1 1v13a1 1 0 0 0 1 1h6a3 3 0 0 1 3 3 3 3 0 0 1 3-3h6a1 1 0 0 0 1-1v-1.3" />
|
||||
<path d="M22 6V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2h4.001A2 2 0 0022 17v-1.344" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 424 B |
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"danielbayley"
|
||||
"danielbayley",
|
||||
"karsa-mistmere"
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 7v14" />
|
||||
<path d="M16 12h2" />
|
||||
<path d="M16 8h2" />
|
||||
<path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" />
|
||||
<path d="M6 12h2" />
|
||||
<path d="M6 8h2" />
|
||||
<path d="M12 5v16" />
|
||||
<path d="M16 13h2" />
|
||||
<path d="M16 9h2" />
|
||||
<path d="M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z" />
|
||||
<path d="M6 13h2" />
|
||||
<path d="M6 9h2" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 470 B After Width: | Height: | Size: 479 B |
@@ -3,7 +3,8 @@
|
||||
"contributors": [
|
||||
"colebemis",
|
||||
"csandman",
|
||||
"ericfennis"
|
||||
"ericfennis",
|
||||
"karsa-mistmere"
|
||||
],
|
||||
"use-cases": [],
|
||||
"tags": [
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 7v14" />
|
||||
<path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" />
|
||||
<path d="M12 5v16" />
|
||||
<path d="M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 387 B |
@@ -16,4 +16,4 @@
|
||||
<path d="M8 8H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 1.414-.586" />
|
||||
<path d="M9 13v2" />
|
||||
<path d="M9.67 4H12v2.33" />
|
||||
</svg>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 457 B |
@@ -8,8 +8,7 @@
|
||||
{
|
||||
"name": "curly-braces",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
],
|
||||
"use-cases": [],
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M11 14h1v4" />
|
||||
<path d="M16 2v4" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="M8 2v4" />
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" />
|
||||
<path d="M11 13h1v4" />
|
||||
<path d="M16 2v3" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="M8 2v3" />
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 356 B After Width: | Height: | Size: 355 B |
@@ -9,10 +9,10 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m14 18 4 4 4-4" />
|
||||
<path d="M16 2v4" />
|
||||
<path d="M18 14v8" />
|
||||
<path d="M21 11.354V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7.343" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="M8 2v4" />
|
||||
<path d="m14 17 4 4 4-4" />
|
||||
<path d="M16 2v3" />
|
||||
<path d="M18 13v8" />
|
||||
<path d="M21 10.354V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h7.343" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="M8 2v3" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 412 B After Width: | Height: | Size: 407 B |
@@ -9,10 +9,10 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m14 18 4-4 4 4" />
|
||||
<path d="M16 2v4" />
|
||||
<path d="M18 22v-8" />
|
||||
<path d="M21 11.343V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h9" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="M8 2v4" />
|
||||
<path d="m14 17 4-4 4 4" />
|
||||
<path d="M16 2v3" />
|
||||
<path d="M18 21v-8" />
|
||||
<path d="M21 10.343V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h9" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="M8 2v3" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 404 B |
@@ -9,9 +9,15 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M8 2v4" />
|
||||
<path d="M16 2v4" />
|
||||
<path d="M21 14V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="m16 20 2 2 4-4" />
|
||||
<path d="M 19 3 L 5 3" />
|
||||
<path d="M 21 13 L 21 5" />
|
||||
<path d="M 21 5 A2 2 0 0 0 19 3" />
|
||||
<path d="M 3 19 A2 2 0 0 0 5 21" />
|
||||
<path d="M 3 5 L 3 19" />
|
||||
<path d="M 5 3 A2 2 0 0 0 3 5" />
|
||||
<path d="m16 19 2 2 4-4" />
|
||||
<path d="M16 2v3" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="M5 21 L12.5 21" />
|
||||
<path d="M8 2v3" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 534 B |
@@ -9,9 +9,9 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M8 2v4" />
|
||||
<path d="M16 2v4" />
|
||||
<rect width="18" height="18" x="3" y="4" rx="2" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="m9 16 2 2 4-4" />
|
||||
<path d="M8 2v3" />
|
||||
<path d="M16 2v3" />
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="m9 15 2 2 4-4" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 359 B After Width: | Height: | Size: 358 B |
@@ -10,9 +10,9 @@
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M16 14v2.2l1.6 1" />
|
||||
<path d="M16 2v4" />
|
||||
<path d="M21 7.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5" />
|
||||
<path d="M3 10h5" />
|
||||
<path d="M8 2v4" />
|
||||
<path d="M16 2v3" />
|
||||
<path d="M21 7.338V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h2.338" />
|
||||
<path d="M3 9h5.859" />
|
||||
<path d="M8 2v3" />
|
||||
<circle cx="16" cy="16" r="6" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 419 B After Width: | Height: | Size: 422 B |
@@ -11,15 +11,15 @@
|
||||
>
|
||||
<path d="m15.228 16.852-.923-.383" />
|
||||
<path d="m15.228 19.148-.923.383" />
|
||||
<path d="M16 2v4" />
|
||||
<path d="M16 2v3" />
|
||||
<path d="m16.47 14.305.382.923" />
|
||||
<path d="m16.852 20.772-.383.924" />
|
||||
<path d="m19.148 15.228.383-.923" />
|
||||
<path d="m19.53 21.696-.382-.924" />
|
||||
<path d="m20.772 16.852.924-.383" />
|
||||
<path d="m20.772 19.148.924.383" />
|
||||
<path d="M21 10.592V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="M8 2v4" />
|
||||
<path d="m20.773 16.852.924-.383" />
|
||||
<path d="m20.773 19.148.924.383" />
|
||||
<path d="M21 10.5V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h5.5" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="M8 2v3" />
|
||||
<circle cx="18" cy="18" r="3" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 694 B |
@@ -9,14 +9,14 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M8 2v4" />
|
||||
<path d="M16 2v4" />
|
||||
<rect width="18" height="18" x="3" y="4" rx="2" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="M8 14h.01" />
|
||||
<path d="M12 14h.01" />
|
||||
<path d="M16 14h.01" />
|
||||
<path d="M8 18h.01" />
|
||||
<path d="M12 18h.01" />
|
||||
<path d="M16 18h.01" />
|
||||
<path d="M8 2v3" />
|
||||
<path d="M16 2v3" />
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="M8 13h.01" />
|
||||
<path d="M12 13h.01" />
|
||||
<path d="M16 13h.01" />
|
||||
<path d="M8 17h.01" />
|
||||
<path d="M12 17h.01" />
|
||||
<path d="M16 17h.01" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 484 B After Width: | Height: | Size: 483 B |
@@ -9,9 +9,8 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M3 20a2 2 0 0 0 2 2h10a2.4 2.4 0 0 0 1.706-.706l3.588-3.588A2.4 2.4 0 0 0 21 16V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2z" />
|
||||
<path d="M15 22v-5a1 1 0 0 1 1-1h5" />
|
||||
<path d="M8 2v4" />
|
||||
<path d="M16 2v4" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="M16 2v3" />
|
||||
<path d="M21 15V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h10v-5a1 1 0 011-1za2.4 2.4 0 01-.706 1.706l-3.588 3.588A2.4 2.4 0 0115 21" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="M8 2v3" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 417 B |
@@ -9,9 +9,9 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12.127 22H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v5.125" />
|
||||
<path d="M14.62 18.8A2.25 2.25 0 1 1 18 15.836a2.25 2.25 0 1 1 3.38 2.966l-2.626 2.856a.998.998 0 0 1-1.507 0z" />
|
||||
<path d="M16 2v4" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="M8 2v4" />
|
||||
<path d="M12.127 21H5a2 2 0 01-2-2V5a2 2 0 012-2h14a2 2 0 012 2v5.125" />
|
||||
<path d="M14.62 17.8A2.25 2.25 0 1118 14.836a2.25 2.25 0 113.38 2.966l-2.626 2.856a.998.998 0 01-1.507 0z" />
|
||||
<path d="M16 2v3" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="M8 2v3" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 475 B After Width: | Height: | Size: 464 B |
@@ -9,9 +9,9 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M8 2v4" />
|
||||
<path d="M16 2v4" />
|
||||
<rect width="18" height="18" x="3" y="4" rx="2" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="M10 16h4" />
|
||||
<path d="M8 2v3" />
|
||||
<path d="M16 2v3" />
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="M10 15h4" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 353 B |
@@ -9,9 +9,9 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M16 19h6" />
|
||||
<path d="M16 2v4" />
|
||||
<path d="M21 15V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8.5" />
|
||||
<path d="M3 10h18" />
|
||||
<path d="M8 2v4" />
|
||||
<path d="M16 18h6" />
|
||||
<path d="M16 2v3" />
|
||||
<path d="M21 14V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h8.3" />
|
||||
<path d="M3 9h18" />
|
||||
<path d="M8 2v3" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 371 B |
@@ -9,10 +9,10 @@
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M4.2 4.2A2 2 0 0 0 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 1.82-1.18" />
|
||||
<path d="M21 15.5V6a2 2 0 0 0-2-2H9.5" />
|
||||
<path d="M16 2v4" />
|
||||
<path d="M3 10h7" />
|
||||
<path d="M21 10h-5.5" />
|
||||
<path d="M16 2v3" />
|
||||
<path d="m2 2 20 20" />
|
||||
<path d="M21 9h-5.5" />
|
||||
<path d="M3 9h6" />
|
||||
<path d="M3.586 3.586A2 2 0 003 5v14a2 2 0 002 2h14a2 2 0 001.414-.586" />
|
||||
<path d="M8.656 3H19a2 2 0 012 2v10.344" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 429 B After Width: | Height: | Size: 428 B |