mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-11 13:28:19 +00:00
Compare commits
218
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
917d2ca847 | ||
|
|
113a3b1a3b | ||
|
|
ea29af9ae8 | ||
|
|
249af14dc6 | ||
|
|
79fc531b30 | ||
|
|
dd4da98c1f | ||
|
|
cceab3d776 | ||
|
|
232b1ea84b | ||
|
|
08e27e3387 | ||
|
|
0f8d48b266 | ||
|
|
07df1c7ca2 | ||
|
|
31f5872aa0 | ||
|
|
0f7ac1f988 | ||
|
|
f3a26ccff1 | ||
|
|
701be9088a | ||
|
|
8503b1d4b7 | ||
|
|
53766c77ec | ||
|
|
510f495687 | ||
|
|
c4b02a0c2e | ||
|
|
801d773d1f | ||
|
|
4993e7893f | ||
|
|
d958c44840 | ||
|
|
caa622b4ea | ||
|
|
6b9e93615c | ||
|
|
79d4dd48a7 | ||
|
|
8298424ae0 | ||
|
|
7c8f38774b | ||
|
|
237ab7fa68 | ||
|
|
1d7da164bb | ||
|
|
8294fabe0c | ||
|
|
c0b203a996 | ||
|
|
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 | ||
|
|
0241b57c35 | ||
|
|
24dd855b87 | ||
|
|
4e5fce4adc | ||
|
|
ae94b5118b | ||
|
|
2dd4be802a | ||
|
|
f4fa0512d3 | ||
|
|
d1150cea7a | ||
|
|
4afccc1842 | ||
|
|
4e161a15ef | ||
|
|
76175250cd | ||
|
|
2e58e319e7 | ||
|
|
283e4b2805 | ||
|
|
db63e66333 | ||
|
|
4b3521a3e3 | ||
|
|
f664af1933 | ||
|
|
03ad294db0 | ||
|
|
a2ec77fc7f | ||
|
|
602bca62a2 | ||
|
|
423afc6d03 | ||
|
|
374398b58d | ||
|
|
6c2b8e6fa0 | ||
|
|
ecc612044e | ||
|
|
896aba0d19 | ||
|
|
45740bef8b | ||
|
|
84ab8524b5 | ||
|
|
071e4d2738 | ||
|
|
3c9d1a56a4 | ||
|
|
c20dd8464d | ||
|
|
29af709846 | ||
|
|
410dfc3577 | ||
|
|
574e156701 | ||
|
|
22cfe96931 | ||
|
|
1475fc02e0 | ||
|
|
7f14e74e4d | ||
|
|
5b40f2c5a7 | ||
|
|
bac39e6c53 | ||
|
|
523026ac18 | ||
|
|
2214caa407 | ||
|
|
105f501730 | ||
|
|
5f787f956b | ||
|
|
437c370821 | ||
|
|
fb885f333a | ||
|
|
71ec48ce30 | ||
|
|
d624fc51ab | ||
|
|
1c381a075c | ||
|
|
a168a87834 | ||
|
|
7e9def13a6 | ||
|
|
228c86ed12 | ||
|
|
50b7038d27 | ||
|
|
a51e94b25c | ||
|
|
3d7a12949b | ||
|
|
07c885e6c1 | ||
|
|
446e644e63 | ||
|
|
6d38f90429 | ||
|
|
56e49f1216 | ||
|
|
cc5129073e | ||
|
|
1da4059d59 | ||
|
|
4b2d07b79c | ||
|
|
50d8af5a10 | ||
|
|
92e02f5042 | ||
|
|
1675213eb6 | ||
|
|
ac25ea327d | ||
|
|
b9e922b100 | ||
|
|
6e42a8792a | ||
|
|
d7f8d17986 | ||
|
|
c81e6846c5 | ||
|
|
804d25586b | ||
|
|
39e3964a9b | ||
|
|
2f8257d8b4 | ||
|
|
fe0bd9b887 | ||
|
|
d0229f4cfc | ||
|
|
cc1caae101 | ||
|
|
d632a8e272 | ||
|
|
653e44b832 | ||
|
|
f0eaba827e | ||
|
|
dc803026e0 | ||
|
|
3c62e4bfef | ||
|
|
6d6da7ca3a | ||
|
|
2453b6467d | ||
|
|
a88e7070cc | ||
|
|
a7611e2957 | ||
|
|
7c2ff2e5d6 | ||
|
|
51e37e2e2c | ||
|
|
5faceb5495 | ||
|
|
3814a64cd5 | ||
|
|
43c76b1f08 | ||
|
|
2401a39571 | ||
|
|
7623e23f78 | ||
|
|
663a9d7fc3 | ||
|
|
d53f615cf3 | ||
|
|
d8700b257a | ||
|
|
30bb61917b |
@@ -9,3 +9,4 @@ strikethrough
|
||||
touchpad
|
||||
ungroup
|
||||
toc
|
||||
sunlounger
|
||||
|
||||
@@ -13,21 +13,19 @@ body:
|
||||
description: Which Lucide packages are affected? You may select more than one.
|
||||
options:
|
||||
- label: lucide
|
||||
- label: lucide-angular (old version)
|
||||
- label: '@lucide/angular (new version)'
|
||||
- label: '@lucide/astro'
|
||||
- label: lucide-flutter
|
||||
- label: lucide-preact
|
||||
- label: lucide-react
|
||||
- label: '@lucide/vue'
|
||||
- label: '@lucide/svelte'
|
||||
- label: '@lucide/angular'
|
||||
- label: '@lucide/astro'
|
||||
- label: lucide-react-native
|
||||
- label: lucide-preact
|
||||
- label: lucide-solid
|
||||
- label: lucide-static
|
||||
- label: lucide-svelte (old version)
|
||||
- label: '@lucide/svelte (new version)'
|
||||
- label: lucide-vue
|
||||
- label: lucide-vue-next
|
||||
- label: lucide-astro
|
||||
- label: '@lucide/icons'
|
||||
- label: lucide-vue-next (deprecated)
|
||||
- label: lucide-svelte (deprecated)
|
||||
- label: lucide-angular (deprecated)
|
||||
- label: Figma plugin
|
||||
- label: source/main
|
||||
- label: other/not relevant
|
||||
@@ -38,7 +36,7 @@ body:
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of Lucide are you running?
|
||||
placeholder: e.g. 0.289.1
|
||||
placeholder: e.g. 1.1.0
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
|
||||
@@ -13,21 +13,19 @@ body:
|
||||
description: Which Lucide project do you wish this feature were added to? You may select more than one.
|
||||
options:
|
||||
- label: lucide
|
||||
- label: lucide-angular (old version)
|
||||
- label: '@lucide/angular (new version)'
|
||||
- label: '@lucide/astro'
|
||||
- label: lucide-flutter
|
||||
- label: lucide-preact
|
||||
- label: lucide-react
|
||||
- label: '@lucide/vue'
|
||||
- label: '@lucide/svelte'
|
||||
- label: '@lucide/angular'
|
||||
- label: '@lucide/astro'
|
||||
- label: lucide-react-native
|
||||
- label: lucide-preact
|
||||
- label: lucide-solid
|
||||
- label: lucide-static
|
||||
- label: lucide-svelte (old version)
|
||||
- label: '@lucide/svelte (new version)'
|
||||
- label: lucide-vue
|
||||
- label: lucide-vue-next
|
||||
- label: lucide-astro
|
||||
- label: '@lucide/icons'
|
||||
- label: lucide-vue-next (deprecated)
|
||||
- label: lucide-svelte (deprecated)
|
||||
- label: lucide-angular (deprecated)
|
||||
- label: Figma plugin
|
||||
- label: all JS packages
|
||||
- label: site
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
## Reviewing PRs
|
||||
|
||||
When reviewing PRs, please respond in a friendly and constructive manner.
|
||||
|
||||
- First, thank the contributor for their contribution in an enthusiastic way.
|
||||
- If you have any suggestions for improvement, provide them clearly and concisely.
|
||||
- If you have any questions about the PR, ask them respectfully.
|
||||
- Let the contributor know that we have many PRs to review and that we appreciate their patience while we review their contribution.
|
||||
@@ -0,0 +1,104 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
react-deps:
|
||||
patterns:
|
||||
- '@testing-library/react'
|
||||
- '@types/prop-types'
|
||||
- '@types/react'
|
||||
- '@vitejs/plugin-react'
|
||||
- 'react'
|
||||
- 'react-dom'
|
||||
vue-deps:
|
||||
patterns:
|
||||
- '@testing-library/vue'
|
||||
- '@vitejs/plugin-vue'
|
||||
- '@vue/test-utils'
|
||||
- 'vue'
|
||||
svelte-deps:
|
||||
patterns:
|
||||
- '@sveltejs/*'
|
||||
- '@testing-library/svelte'
|
||||
- '@tsconfig/svelte'
|
||||
- 'svelte'
|
||||
- 'svelte-check'
|
||||
- 'svelte-preprocess'
|
||||
solid-deps:
|
||||
patterns:
|
||||
- '@babel/*'
|
||||
- '@rollup/plugin-babel'
|
||||
- '@solidjs/testing-library'
|
||||
- 'babel-preset-solid'
|
||||
- 'solid-js'
|
||||
- 'vite-plugin-solid'
|
||||
react-native-deps:
|
||||
patterns:
|
||||
- 'react-native'
|
||||
- 'react-native-svg'
|
||||
angular-deps:
|
||||
patterns:
|
||||
- '@angular/*'
|
||||
- '@angular-eslint/*'
|
||||
- '@vitest/browser-playwright'
|
||||
- 'angular-eslint'
|
||||
- 'ng-packagr'
|
||||
- 'rxjs'
|
||||
- 'tslib'
|
||||
preact-deps:
|
||||
patterns:
|
||||
- '@preact/preset-vite'
|
||||
- '@testing-library/preact'
|
||||
- 'preact'
|
||||
astro-deps:
|
||||
patterns:
|
||||
- '@astrojs/*'
|
||||
- 'astro'
|
||||
testing-deps:
|
||||
patterns:
|
||||
- '@testing-library/*'
|
||||
- '@vitest/*'
|
||||
- 'jest-serializer-html'
|
||||
- 'jsdom'
|
||||
- 'linkedom'
|
||||
- 'vitest'
|
||||
build-deps:
|
||||
patterns:
|
||||
- '@rollup/*'
|
||||
- '@vitejs/*'
|
||||
- 'esbuild'
|
||||
- 'rollup'
|
||||
- 'rollup-plugin-*'
|
||||
- 'typescript'
|
||||
- 'vite'
|
||||
- 'vite-plugin-*'
|
||||
linting-deps:
|
||||
patterns:
|
||||
- '@html-eslint/*'
|
||||
- '@typescript-eslint/*'
|
||||
- 'cspell'
|
||||
- 'eslint'
|
||||
- 'eslint-*'
|
||||
- 'husky'
|
||||
- 'lint-staged'
|
||||
- 'prettier'
|
||||
- 'prettier-plugin-*'
|
||||
types-deps:
|
||||
patterns:
|
||||
- '@types/*'
|
||||
remaining-deps:
|
||||
patterns:
|
||||
- '*'
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- '*'
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
applyTo: "icons/*.json"
|
||||
---
|
||||
# JSON Metadata Descriptor
|
||||
|
||||
The schema for the JSON metadata descriptor can be found in `icon.schema.json`. It defines the required and optional properties for the JSON files that accompany each icon. The JSON metadata descriptor should be placed in the same directory as the SVG file of the icon and should have the same name as the SVG file, but with a `.json` extension. For example, if the SVG file is named `home.svg`, the JSON metadata descriptor should be named `home.json`.
|
||||
|
||||
## Contributors
|
||||
|
||||
The `contributors` property is a required array of GitHub usernames for the people who created or contributed to the icon. It is used to give credit to contributors and to track the icon's history. Add the PR author to the `contributors` array if they are not already listed and they have made a significant contribution to the icon's SVG.
|
||||
|
||||
## Tags
|
||||
|
||||
The `tags` property is an array of strings that describe the icon and can be used for searching.
|
||||
Validate the tags against the `icon.schema.json` to ensure they are correctly formatted and adhere to the defined structure.
|
||||
Provide tag suggestions based on the name of the icon and the use cases provided in the PR description. Use the existing tags in the repository as a reference for consistency and to avoid duplicates. Don't suggest words like: 'icon' and preferably use single words. Tags should always be in lowercase, and may also contain spaces (e.g. `magnifying glass`). The name of icon should not be included in the tags, as it is already specified.
|
||||
|
||||
# Categories
|
||||
|
||||
The `categories` property is an array of strings that specify the categories to which the icon belongs, such as "devices", "interface", "media", etc. See the `categories` property in the `icon.schema.json` for more details on the allowed values. The categories should be chosen based on the use cases provided in the PR description and the existing categories in the repository. The categories should be relevant to the icon and should help users find the icon when searching for specific types of icons. The name of icon should not be included in the categories, as it is already specified. Suggest categories based on the name of the icon and the use cases provided in the PR description. Use the existing categories in the repository as a reference for consistency and to avoid duplicates. Categories should always be in lowercase and should not contain spaces.
|
||||
+2
-10
@@ -52,15 +52,13 @@
|
||||
💎 vue package:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'packages/lucide-vue/*'
|
||||
- 'packages/lucide-vue-next/*'
|
||||
- 'packages/vue/*'
|
||||
|
||||
# For changes in the Lucide angular package
|
||||
🅰️ angular package:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'packages/angular/*'
|
||||
- 'packages/lucide-angular/*'
|
||||
|
||||
# For changes in the Lucide preact package
|
||||
⚛️ preact package:
|
||||
@@ -72,7 +70,7 @@
|
||||
🧣 svelte package:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'packages/lucide-svelte/*'
|
||||
- 'packages/svelte/*'
|
||||
|
||||
# For changes in the Lucide solid package
|
||||
🪝 solid package:
|
||||
@@ -97,9 +95,3 @@
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'packages/lucide-static/*'
|
||||
|
||||
# For changes in the Lucide flutter package
|
||||
🏹 flutter package:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'packages/lucide-flutter/*'
|
||||
|
||||
@@ -41,13 +41,15 @@ Common scopes: icons, docs, studio, site, dev
|
||||
|
||||
### Naming <!-- ONLY for new icons -->
|
||||
<!-- All of these requirements must be fulfilled. -->
|
||||
- [ ] I've read and followed the [naming conventions](https://lucide.dev/guide/design/icon-design-guide#naming-conventions)
|
||||
- [ ] I've read and followed the [naming conventions](https://lucide.dev/contribute/icon-design-guide#naming-conventions)
|
||||
- [ ] I've named icons by what they are rather than their use case.
|
||||
- [ ] I've provided meta JSON files in `icons/[iconName].json`.
|
||||
|
||||
### Design <!-- ONLY for new icons -->
|
||||
<!-- All of these requirements must be fulfilled. -->
|
||||
- [ ] I've read and followed the [icon design guidelines](https://lucide.dev/guide/design/icon-design-guide)
|
||||
|
||||
- [ ] 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.
|
||||
|
||||
@@ -7,13 +7,16 @@ on:
|
||||
- tools/build-icons/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -27,9 +30,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
+50
-15
@@ -23,26 +23,55 @@ on:
|
||||
- beta
|
||||
- alpha
|
||||
|
||||
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
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)') && github.event_name != 'workflow_dispatch'
|
||||
check-dispatch-gate:
|
||||
if: github.repository == 'lucide-icons/lucide' &&
|
||||
startsWith(github.event.head_commit.message, 'feat(icons)') &&
|
||||
github.event_name != 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
RELEASE_ENVIRONMENT: ${{ steps.environment.outputs.RELEASE_ENVIRONMENT }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Fetch tags
|
||||
run: git fetch --all --tags
|
||||
|
||||
- name: Check if latest tag was created today
|
||||
id: environment
|
||||
run: |
|
||||
LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)
|
||||
TAG_DATE=$(git log -1 --format=%cs "$LATEST_TAG")
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
|
||||
if [ "$TAG_DATE" == "$TODAY" ]; then
|
||||
echo "::warning::A release tag already exists for today ($TODAY): $LATEST_TAG. Manual approval will be required to proceed."
|
||||
echo "RELEASE_ENVIRONMENT=Icon Release Gated" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "RELEASE_ENVIRONMENT=Icon Release" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
create-release:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
needs: check-dispatch-gate
|
||||
environment: ${{ needs.check-dispatch-gate.outputs.RELEASE_ENVIRONMENT }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # Required to create the release and tag
|
||||
outputs:
|
||||
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
# No cache restore here: this job cuts the release, and caches
|
||||
# are writable from other workflow runs. The lockfile still
|
||||
# guards the install.
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -75,7 +104,7 @@ jobs:
|
||||
echo '${{ steps.change-log.outputs.CHANGE_LOG }}'
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
|
||||
with:
|
||||
tag_name: ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
name: Version ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
@@ -86,13 +115,19 @@ jobs:
|
||||
needs: create-release
|
||||
uses: './.github/workflows/release.yml'
|
||||
secrets: inherit
|
||||
permissions:
|
||||
id-token: write # Required for OIDC (npm provenance)
|
||||
contents: write # Required to upload release assets
|
||||
with:
|
||||
version: ${{ needs.create-release.outputs.VERSION }}
|
||||
|
||||
dispatch-release:
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
if: github.repository == 'lucide-icons/lucide' && github.event_name == 'workflow_dispatch'
|
||||
uses: './.github/workflows/release.yml'
|
||||
secrets: inherit
|
||||
permissions:
|
||||
id-token: write # Required for OIDC (npm provenance)
|
||||
contents: write # Required to upload release assets
|
||||
with:
|
||||
version: ${{ inputs.version }}
|
||||
tag: ${{ inputs.tag }}
|
||||
|
||||
@@ -4,14 +4,21 @@ on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
jobs:
|
||||
block_phrases:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Load stopwords from JSON & check issue title & body
|
||||
if: contains(github.event.issue.labels.*.name, '🙌 icon request')
|
||||
@@ -33,7 +40,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})
|
||||
|
||||
@@ -43,6 +50,3 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
@@ -3,13 +3,16 @@ on:
|
||||
schedule:
|
||||
- cron: '45 1 * * *'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-pr-message: This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 5 days.
|
||||
|
||||
@@ -6,15 +6,22 @@ on:
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read # Required to download the artifact from the triggering run
|
||||
pull-requests: write
|
||||
if: >
|
||||
github.repository == 'lucide-icons/lucide' &&
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
- name: 'Download artifact'
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
@@ -42,7 +49,7 @@ jobs:
|
||||
id: pr-number
|
||||
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v2
|
||||
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
|
||||
id: pr-comment
|
||||
with:
|
||||
issue-number: ${{ steps.pr-number.outputs.number }}
|
||||
@@ -50,7 +57,7 @@ jobs:
|
||||
body-includes: Added or changed icons
|
||||
|
||||
- name: Create or update comment
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
|
||||
with:
|
||||
comment-id: ${{ steps.pr-comment.outputs.comment-id }}
|
||||
issue-number: ${{ steps.pr-number.outputs.number }}
|
||||
|
||||
@@ -9,13 +9,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -29,9 +32,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -2,12 +2,15 @@ name: 'Pull Request Labeler'
|
||||
on:
|
||||
- pull_request_target
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
name: Linting PR
|
||||
name: Linting PR code
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- icons/*.svg
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint-code:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -21,3 +22,19 @@ jobs:
|
||||
|
||||
- name: Run Linter
|
||||
run: pnpm lint
|
||||
|
||||
lint-actions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Verify actions are pinned to commit SHAs
|
||||
run: |
|
||||
unpinned=$(grep -rnE "^[[:space:]]*(-[[:space:]]+)?uses:" .github/workflows/ \
|
||||
| grep -vE "uses:[[:space:]]*['\"]?\./" \
|
||||
| grep -vE "uses:[[:space:]]*['\"]?[^@]+@[0-9a-f]{40}([[:space:]]|$|#)" || true)
|
||||
if [ -n "$unpinned" ]; then
|
||||
echo "::error::GitHub Actions must be pinned to a full commit SHA, not a version tag."
|
||||
echo "$unpinned"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Linting PR
|
||||
name: Linting PR title
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -7,12 +7,16 @@ on:
|
||||
- edited
|
||||
- reopened
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read # Required to read the PR title
|
||||
|
||||
jobs:
|
||||
lint-pr-title:
|
||||
name: PR Title Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5
|
||||
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
@@ -7,6 +7,9 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'icons/*'
|
||||
- 'lab/*'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
lint-filenames:
|
||||
@@ -16,27 +19,54 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
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
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
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
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Check Uniqueness of Aliases
|
||||
run: "! cat <(printf \"%s\\n\" icons/*.json | while read -r name; do basename \"$name\" .json; done) <(jq -cr 'select(.aliases) | .aliases[] | if type==\"string\" then . else .name end' icons/*.json) | sort | uniq -c | grep -ve '^\\s*1 '"
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
name: Lucide Angular checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/lucide-angular/**
|
||||
- tools/build-icons/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
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: Build
|
||||
run: pnpm --filter lucide-angular build
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
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: Test
|
||||
run: pnpm --filter lucide-angular test
|
||||
@@ -11,13 +11,16 @@ on:
|
||||
- tools/build-icons/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
cache: 'pnpm'
|
||||
@@ -31,9 +34,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v3.8.1
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
cache: 'pnpm'
|
||||
|
||||
@@ -9,13 +9,16 @@ on:
|
||||
- tools/build-font/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lucide-font:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -23,11 +26,14 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Execute unit tests for build-font
|
||||
run: pnpm test:font
|
||||
|
||||
- name: Create font in ./lucide-font
|
||||
run: pnpm build:font
|
||||
|
||||
- name: 'Upload to Artifacts'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
|
||||
@@ -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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter @lucide/lab build
|
||||
@@ -12,13 +12,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lucide-preact:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -12,13 +12,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -32,9 +35,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -13,13 +13,16 @@ on:
|
||||
- scripts/generateNextJSAliases.mjs
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -33,9 +36,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -9,13 +9,16 @@ on:
|
||||
- packages/shared/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -12,13 +12,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -32,9 +35,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -7,13 +7,16 @@ on:
|
||||
- tools/build-icons/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lucide-static:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -12,13 +12,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -32,9 +35,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -48,9 +51,9 @@ jobs:
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
name: Lucide Svelte checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/lucide-svelte/**
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
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: Build
|
||||
run: pnpm --filter lucide-svelte build
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
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: Test
|
||||
run: pnpm --filter lucide-svelte test
|
||||
@@ -1,46 +0,0 @@
|
||||
name: Lucide Vue Next checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/lucide-vue-next/**
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
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: Build
|
||||
run: pnpm --filter lucide-vue-next build
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
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: Test
|
||||
run: pnpm --filter lucide-vue-next test
|
||||
@@ -6,19 +6,22 @@ on:
|
||||
- next
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/lucide-vue/**
|
||||
- packages/vue/**
|
||||
- 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
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -32,9 +35,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -11,13 +11,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -31,9 +34,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -5,27 +5,30 @@ on:
|
||||
paths:
|
||||
- 'icons/*.svg'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
generate-changed-icons-comment:
|
||||
name: Generate Changed Icons Comment
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v46
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files: icons/*.svg
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
- name: Install svgson for code preview (safer and faster than installing all deps)
|
||||
run: npm install svgson@5.3.1 --force
|
||||
|
||||
@@ -40,7 +43,7 @@ jobs:
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: pr_number
|
||||
path: pr/
|
||||
|
||||
+10
-7
@@ -1,13 +1,16 @@
|
||||
name: Pull request tags suggestions
|
||||
name: Pull request metadata suggestions
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'icons/*.json'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pull-request-tags-suggestions:
|
||||
name: Pull Request Tags Suggestions
|
||||
pull-request-metadata-suggestions:
|
||||
name: Pull Request Metadata Suggestions
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -16,11 +19,11 @@ jobs:
|
||||
steps:
|
||||
# We checkout the main branch of main repository for security reasons.
|
||||
# This is to prevent the workflow from running on a forked repository.
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
repository: lucide-icons/lucide
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -29,7 +32,7 @@ jobs:
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Generate comment markup
|
||||
run: node ./scripts/suggestTags.mts
|
||||
run: node ./scripts/suggestMetaData.mts
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
PULL_REQUEST_NUMBER: ${{ github.event.number }}
|
||||
@@ -15,13 +15,12 @@ on:
|
||||
description: NPM tag
|
||||
default: 'latest'
|
||||
type: string
|
||||
secrets:
|
||||
BLOB_READ_WRITE_TOKEN:
|
||||
description: Token used by build-font to upload the code points blob
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
@@ -29,7 +28,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
VERSION: ${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
steps:
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
@@ -63,9 +61,9 @@ jobs:
|
||||
'@lucide/vue',
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -74,7 +72,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
|
||||
@@ -96,10 +94,13 @@ jobs:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -108,7 +109,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
|
||||
@@ -125,10 +126,12 @@ jobs:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -136,13 +139,16 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Execute unit tests for build-font
|
||||
run: pnpm test:font
|
||||
|
||||
- name: Create font in ./lucide-font
|
||||
run: pnpm build:font --saveCodePoints
|
||||
env:
|
||||
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
|
||||
|
||||
- name: 'Upload to Artifacts'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
@@ -152,18 +158,21 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [prepare, lucide-font]
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: write
|
||||
contents: write # Required to upload the release assets
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
|
||||
- name: Zip font and icons
|
||||
run: |
|
||||
zip -r lucide-font-${{ needs.prepare.outputs.VERSION }}.zip lucide-font
|
||||
zip -r lucide-icons-${{ needs.prepare.outputs.VERSION }}.zip icons
|
||||
|
||||
- name: Release zip and fonts
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
|
||||
with:
|
||||
tag_name: ${{ needs.prepare.outputs.VERSION }}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
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:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Node.js environment
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
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,25 +1,27 @@
|
||||
name: 'Request Review'
|
||||
name: Request Review
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
paths:
|
||||
- icons/*.svg
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
request-review:
|
||||
if: github.event.pull_request.draft == false
|
||||
if: github.repository == 'lucide-icons/lucide' && github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v41
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files: icons/*.svg
|
||||
- run: |
|
||||
|
||||
+7
-1
@@ -26,9 +26,11 @@ packages/**/src/utils/*.ts
|
||||
packages/**/src/dynamicIconImports.ts
|
||||
packages/**/DynamicIcon.d.ts
|
||||
packages/**/dynamicIconImports.js
|
||||
packages/**/dynamicIconImports.mjs
|
||||
packages/**/dynamicIconImports.d.ts
|
||||
packages/**/dynamicIconImports.d.mts
|
||||
packages/**/dynamicIconImports.js.map
|
||||
packages/**/dynamicIconImports.mjs.map
|
||||
packages/**/dynamic.d.ts
|
||||
packages/**/dynamic.d.mts
|
||||
packages/**/dynamic.mjs.map
|
||||
@@ -37,6 +39,7 @@ packages/**/LICENSE
|
||||
categories.json
|
||||
tags.json
|
||||
.vercel
|
||||
package-lock.json
|
||||
|
||||
# docs
|
||||
docs/.vitepress/cache
|
||||
@@ -50,8 +53,11 @@ docs/.vitepress/data/categoriesData.json
|
||||
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
|
||||
|
||||
+5
-4
@@ -7,8 +7,8 @@ docs/.vitepress/cache
|
||||
docs/.vitepress/data
|
||||
docs/.nitro
|
||||
|
||||
# lucide-angular
|
||||
packages/lucide-angular/.angular/cache
|
||||
# @lucide/angular
|
||||
packages/angular/.angular
|
||||
|
||||
# lucide-static
|
||||
packages/lucide-static/icons
|
||||
@@ -17,7 +17,8 @@ packages/lucide-static/sprite.svg
|
||||
packages/lucide-static/tags.json
|
||||
packages/lucide-static/icon-nodes.json
|
||||
packages/lucide-static/font
|
||||
packages/svelte/.svelte-kit
|
||||
|
||||
# lucide-svelte
|
||||
packages/lucide-svelte/src/icons/*.svelte
|
||||
packages/lucide-svelte/.svelte-kit
|
||||
packages/svelte/src/icons/*.svelte
|
||||
packages/svelte/.svelte-kit
|
||||
|
||||
@@ -39,7 +39,7 @@ This means adding them could:
|
||||
Lucide is all about **visual consistency**.
|
||||
|
||||
Adding brand logos would:
|
||||
- Break [our own design rules](https://lucide.dev/guide/design/icon-design-guide#icon-design-principles) for shapes, proportions, and stroke.
|
||||
- Break [our own design rules](https://lucide.dev/contribute/icon-design-guide#icon-design-principles) for shapes, proportions, and stroke.
|
||||
- Mix two fundamentally different categories of graphics (pictograms vs. corporate logos).
|
||||
- Create a library where a subset of icons will always look "out of place".
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/lucide-icons/lucide/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/lucide" alt="license"></a>
|
||||
<a href="https://github.com/lucide-icons/lucide/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-ISC-green" alt="license"></a>
|
||||
<a href="https://www.figma.com/community/plugin/939567362549682242/Lucide-Icons"><img src="https://img.shields.io/badge/Figma-F24E1E?logo=figma&logoColor=white" alt="figma installs"></a>
|
||||
<a href="https://github.com/lucide-icons/lucide/actions/workflows/release.yml"><img src="https://github.com/lucide-icons/lucide/actions/workflows/release.yml/badge.svg" alt="build status"></a>
|
||||
<a href="https://github.com/lucide-icons/lucide/actions/workflows/ci.yml"><img src="https://github.com/lucide-icons/lucide/actions/workflows/ci.yml/badge.svg" alt="build status"></a>
|
||||
<a href="https://discord.gg/EH6nSts"><img src="https://img.shields.io/discord/723074157486800936?label=chat&logo=discord&logoColor=%23ffffff&colorB=%237289DA" alt="discord chat"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
@@ -26,22 +26,22 @@
|
||||
|
||||
# Lucide
|
||||
|
||||
Lucide is an open-source icon library that provides 1000+ vector (svg) files for displaying icons and symbols in digital and non-digital projects. The library aims to make it easier for designers and developers to incorporate icons into their projects by providing several official [packages](https://lucide.dev/packages) to make it easier to use these icons in your project.
|
||||
Lucide is an open-source icon library that provides 1600+ vector (svg) files for displaying icons and symbols in digital and non-digital projects. The library aims to make it easier for designers and developers to incorporate icons into their projects by providing several official [packages](https://lucide.dev/packages) to make it easier to use these icons in your project.
|
||||
|
||||
## Packages
|
||||
|
||||
| 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/astro.svg" alt="Astro logo" width="48"> | **`@lucide/astro`** | [](https://www.npmjs.com/package/@lucide/astro) |  | [Docs](https://lucide.dev/guide/packages/lucide-astro) · [Source](./packages/astro) |
|
||||
| <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) |
|
||||
| <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/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) |
|
||||
| <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/angular) · [Source](./packages/angular) |
|
||||
| <img src="https://lucide.dev/framework-logos/astro.svg" alt="Astro logo" width="48"> | **`@lucide/astro`** | [](https://www.npmjs.com/package/@lucide/astro) |  | [Docs](https://lucide.dev/guide/astro) · [Source](./packages/astro) |
|
||||
| <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/static) · [Source](./packages/lucide-static) |
|
||||
|
||||
### Figma
|
||||
|
||||
@@ -80,6 +80,8 @@ Thank you to all the people who contributed to Lucide!
|
||||
<img src="https://opencollective.com/lucide-icons/contributors.svg?width=800" />
|
||||
</a>
|
||||
|
||||
[//]: <> (Sponsors)
|
||||
|
||||
## Sponsors
|
||||
|
||||
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
|
||||
@@ -88,7 +90,10 @@ Thank you to all the people who contributed to Lucide!
|
||||
|
||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="docs/public/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||
|
||||
[//]: <> (Open Collective backers)
|
||||
### Hero backers 🦸
|
||||
|
||||
<a href="https://zephyr-cloud.io/"><img src="docs/public/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
|
||||
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="docs/public/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
|
||||
|
||||
@@ -54,10 +54,8 @@
|
||||
"jira": "Jira",
|
||||
"kotlin": "Kotlin",
|
||||
"kubernetes": "Kubernetes",
|
||||
"less": "Less",
|
||||
"leetcode": "LeetCode",
|
||||
"leet-code": "LeetCode",
|
||||
"line": "LINE",
|
||||
"linkedin": "LinkedIn",
|
||||
"lua": "Lua",
|
||||
"mariadb": "MariaDB",
|
||||
@@ -70,7 +68,7 @@
|
||||
"nestjs": "NestJS",
|
||||
"netflix": "Netflix",
|
||||
"netlify": "Netlify",
|
||||
"next": "Next.js",
|
||||
"nextjs": "Next.js",
|
||||
"nodejs": "Node.js",
|
||||
"notion": "Notion",
|
||||
"nostr": "Nostr",
|
||||
@@ -106,7 +104,8 @@
|
||||
"shopify": "Shopify",
|
||||
"skype": "Skype",
|
||||
"slack": "Slack",
|
||||
"solid": "SolidJS",
|
||||
"snapchat": "Snapchat",
|
||||
"solidjs": "SolidJS",
|
||||
"soundcloud": "SoundCloud",
|
||||
"spotify": "Spotify",
|
||||
"sqlite": "SQLite",
|
||||
@@ -145,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"
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export default eventHandler(async (event) => {
|
||||
const name = params.data.split('/').at(-3);
|
||||
const iconSizeString = params.data.split('/').at(-2);
|
||||
const svgData = params.data.split('/').at(-1);
|
||||
const iconSize = parseInt(iconSizeString, 10);
|
||||
const iconSize = Math.min(parseInt(iconSizeString, 10) || imageSize, 256);
|
||||
const data = svgData.slice(0, -4);
|
||||
|
||||
const prevSvg = iconNodes[name]
|
||||
|
||||
@@ -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;
|
||||
}, {});
|
||||
});
|
||||
+14
-195
@@ -1,14 +1,14 @@
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
import { defineConfig } from 'vitepress';
|
||||
import { defineConfig, UserConfig } from 'vitepress';
|
||||
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons';
|
||||
import sidebar from './sidebar';
|
||||
import snackPlayer from './markdown/snackPlayer';
|
||||
import sandpackPlugin from './markdown/sandpack';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { resourcesSidebar } from './sidebar/resources';
|
||||
import getStructuredData from './getStructuredData';
|
||||
import { createGeneralOGImage, createIconOGImage } from './createOGImage';
|
||||
import llmstxt from 'vitepress-plugin-llms';
|
||||
import { transformPageData } from './transformPageData';
|
||||
import getHeadConfig from './getHeadConfig';
|
||||
|
||||
const defaultSandpackCSS = await readFile(
|
||||
fileURLToPath(new URL('./theme/sandpack-default.css', import.meta.url)),
|
||||
@@ -56,6 +56,12 @@ export default defineConfig({
|
||||
new URL('./theme/components/overrides/VPFooter.vue', import.meta.url),
|
||||
),
|
||||
},
|
||||
{
|
||||
find: /^.*\/VPCarbonAds\.vue$/,
|
||||
replacement: fileURLToPath(
|
||||
new URL('./theme/components/overrides/VPCarbonAds.vue', import.meta.url),
|
||||
),
|
||||
},
|
||||
{
|
||||
find: '~/.vitepress',
|
||||
replacement: fileURLToPath(new URL('./', import.meta.url)),
|
||||
@@ -73,199 +79,11 @@ export default defineConfig({
|
||||
'brand-logo-statement.md',
|
||||
'icons/**', // Not working, need investigation
|
||||
],
|
||||
}),
|
||||
}) as unknown as UserConfig['vite']['plugins'][0],
|
||||
],
|
||||
},
|
||||
head: [
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'preconnect',
|
||||
href: 'https://analytics.lucide.dev',
|
||||
},
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{
|
||||
src: 'https://analytics.lucide.dev/js/script.js',
|
||||
'data-domain': 'lucide.dev',
|
||||
defer: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:locale',
|
||||
content: 'en_US',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:type',
|
||||
content: 'website',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:site_name',
|
||||
content: title,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:title',
|
||||
content: socialTitle,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:description',
|
||||
content: description,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:url',
|
||||
content: 'https://lucide.dev',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content: 'https://lucide.dev/og.png',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image:width',
|
||||
content: '1200',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image:height',
|
||||
content: '630',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image:type',
|
||||
content: 'image/png',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:card',
|
||||
content: 'summary_large_image',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:title',
|
||||
content: socialTitle,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:description',
|
||||
content: description,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:image',
|
||||
content: 'https://lucide.dev/og.png',
|
||||
},
|
||||
],
|
||||
],
|
||||
async transformPageData(pageData) {
|
||||
pageData.frontmatter.head ??= [];
|
||||
if (
|
||||
pageData.relativePath.startsWith('icons/') &&
|
||||
!pageData.relativePath.startsWith('icons/lab/') &&
|
||||
pageData.params?.name
|
||||
) {
|
||||
const iconName = pageData.params.name;
|
||||
pageData.title = `${iconName} icon details`;
|
||||
|
||||
const taggedAs = pageData.params?.tags?.length
|
||||
? `Tagged as: ${pageData.params.tags.join(', ')}.`
|
||||
: '';
|
||||
const categorizedIn = pageData.params?.category?.length
|
||||
? `Categorized in: ${pageData.params.category.join(', ')}.`
|
||||
: '';
|
||||
|
||||
pageData.description =
|
||||
`Details and related icons for ${iconName} icon. ${taggedAs} ${categorizedIn}`.trim();
|
||||
|
||||
const structuredData = await getStructuredData(iconName, pageData);
|
||||
|
||||
const ogPath = await createIconOGImage(iconName, pageData.params?.tags || []);
|
||||
|
||||
if (ogPath) {
|
||||
const content = `https://lucide.dev${ogPath}`;
|
||||
pageData.frontmatter.head.push(
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
pageData.frontmatter.head.push([
|
||||
'script',
|
||||
{ type: 'application/ld+json' },
|
||||
JSON.stringify(structuredData),
|
||||
]);
|
||||
}
|
||||
|
||||
if (pageData.relativePath.startsWith('guide/')) {
|
||||
const ogPath = await createGeneralOGImage(pageData);
|
||||
|
||||
if (ogPath) {
|
||||
const content = `https://lucide.dev${ogPath}`;
|
||||
pageData.frontmatter.head.push(
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
head: getHeadConfig({ title, description, socialTitle }),
|
||||
transformPageData,
|
||||
themeConfig: {
|
||||
logo: {
|
||||
light: '/logo.light.svg',
|
||||
@@ -283,6 +101,7 @@ export default defineConfig({
|
||||
},
|
||||
{ text: 'Packages', link: '/packages' },
|
||||
{ text: 'Showcase', link: '/showcase' },
|
||||
{ text: 'Merch', link: 'https://merch.lucide.dev/' },
|
||||
],
|
||||
sidebar,
|
||||
socialLinks: [
|
||||
@@ -291,7 +110,7 @@ export default defineConfig({
|
||||
],
|
||||
footer: {
|
||||
message: 'Released under the ISC License.',
|
||||
copyright: `Copyright © ${new Date().getFullYear()} Lucide Contributors`,
|
||||
copyright: `Copyright © ${new Date().getFullYear()} Lucide Icons`,
|
||||
},
|
||||
editLink: {
|
||||
pattern: 'https://github.com/lucide-icons/lucide/edit/main/docs/:path',
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
},
|
||||
{
|
||||
"name": "navigation",
|
||||
"title": "Navigation, Maps, and POIs"
|
||||
"title": "Navigation & Places"
|
||||
},
|
||||
{
|
||||
"name": "notifications",
|
||||
|
||||
@@ -46,21 +46,5 @@
|
||||
"light": "/company-logos/open-collective-light.svg",
|
||||
"dark": "/company-logos/open-collective-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Super",
|
||||
"url": "https://super.so",
|
||||
"image": {
|
||||
"light": "/company-logos/super-light.svg",
|
||||
"dark": "/company-logos/super-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Noodle",
|
||||
"url": "https://noodle.run/",
|
||||
"image": {
|
||||
"light": "/company-logos/noodle-light.svg",
|
||||
"dark": "/company-logos/noodle-dark.svg"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -50,20 +50,21 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-svelte": {
|
||||
"@lucide/svelte": {
|
||||
"order": 3,
|
||||
"icon": "svelte",
|
||||
"docsAlias": "svelte",
|
||||
"packageDirname": "svelte",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/v/lucide-svelte",
|
||||
"href": "https://www.npmjs.com/package/lucide-svelte"
|
||||
"src": "https://img.shields.io/npm/v/@lucide/svelte",
|
||||
"href": "https://www.npmjs.com/package/@lucide/svelte"
|
||||
},
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/dw/lucide-svelte",
|
||||
"href": "https://www.npmjs.com/package/lucide-svelte"
|
||||
"src": "https://img.shields.io/npm/dw/@lucide/svelte",
|
||||
"href": "https://www.npmjs.com/package/@lucide/svelte"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -119,24 +120,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-angular": {
|
||||
"order": 7,
|
||||
"icon": "angular-old",
|
||||
"description": "Our legacy Angular package with a module based API.",
|
||||
"documentation": "https://v0.lucide.dev/guide/packages/lucide-angular",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/v/lucide-angular",
|
||||
"href": "https://www.npmjs.com/package/lucide-angular"
|
||||
},
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/dw/lucide-angular",
|
||||
"href": "https://www.npmjs.com/package/lucide-angular"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-preact": {
|
||||
"order": 8,
|
||||
"icon": "preact",
|
||||
@@ -209,4 +192,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import { HeadConfig } from 'vitepress';
|
||||
|
||||
const getHeadConfig = ({
|
||||
title,
|
||||
description,
|
||||
socialTitle,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
socialTitle?: string;
|
||||
}): HeadConfig[] => [
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'preconnect',
|
||||
href: 'https://analytics.lucide.dev',
|
||||
},
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{
|
||||
src: 'https://analytics.lucide.dev/js/script.js',
|
||||
'data-domain': 'lucide.dev',
|
||||
defer: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:locale',
|
||||
content: 'en_US',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:type',
|
||||
content: 'website',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:site_name',
|
||||
content: title,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:title',
|
||||
content: socialTitle,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:description',
|
||||
content: description,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:url',
|
||||
content: 'https://lucide.dev',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content: 'https://lucide.dev/og.png',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image:width',
|
||||
content: '1200',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image:height',
|
||||
content: '630',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image:type',
|
||||
content: 'image/png',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:card',
|
||||
content: 'summary_large_image',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:title',
|
||||
content: socialTitle,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:description',
|
||||
content: description,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:image',
|
||||
content: 'https://lucide.dev/og.png',
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
export default getHeadConfig;
|
||||
@@ -459,7 +459,20 @@ const SvgPreview = (
|
||||
/>
|
||||
<ColoredPath
|
||||
paths={paths}
|
||||
colors={['#dfdfd6']}
|
||||
colors={[
|
||||
'#1982c4',
|
||||
'#4267AC',
|
||||
'#6a4c93',
|
||||
'#B55379',
|
||||
'#FF595E',
|
||||
'#FF7655',
|
||||
'#ff924c',
|
||||
'#FFAE43',
|
||||
'#ffca3a',
|
||||
'#C5CA30',
|
||||
'#8ac926',
|
||||
'#52A675',
|
||||
]}
|
||||
/>
|
||||
<Radii
|
||||
paths={paths}
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
<script setup lang="ts">
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import IconButton from './base/IconButton.vue';
|
||||
import { x } from '../../data/iconNodes';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
|
||||
|
||||
const showAskSurvey = useLocalStorage('show-ask-overlay', true, {
|
||||
initOnMounted: true,
|
||||
});
|
||||
|
||||
const delayedShow = ref(false)
|
||||
|
||||
const showPopup = computed(() => showAskSurvey.value && delayedShow.value );
|
||||
let isInitialized = false
|
||||
|
||||
function startTally() {
|
||||
window.Tally.openPopup('EkvOpB', {
|
||||
layout: 'modal',
|
||||
width: 700,
|
||||
autoClose: 5000,
|
||||
onClose() {
|
||||
showAskSurvey.value = false;
|
||||
},
|
||||
onSubmit() {
|
||||
showAskSurvey.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
delayedShow.value = true
|
||||
}, 10000)
|
||||
})
|
||||
|
||||
function initTally() {
|
||||
try {
|
||||
if (!isInitialized) {
|
||||
isInitialized = true
|
||||
const s = document.createElement('script')
|
||||
s.src = `//tally.so/widgets/embed.js`
|
||||
s.async = true
|
||||
s.onload = startTally
|
||||
document.body.appendChild(s)
|
||||
} else {
|
||||
startTally();
|
||||
}
|
||||
} catch (error) {
|
||||
window.open('https://tally.so/r/EkvOpB', '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
function hidePopup() {
|
||||
showAskSurvey.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div
|
||||
:class="{
|
||||
'show-popup': showPopup,
|
||||
}"
|
||||
class="ask-overlay"
|
||||
>
|
||||
<IconButton
|
||||
@click="hidePopup"
|
||||
class="hide-button"
|
||||
>
|
||||
<Icon
|
||||
:iconNode="x"
|
||||
:size="20"
|
||||
absoluteStrokeWidth
|
||||
/>
|
||||
</IconButton>
|
||||
|
||||
<h2 class="title">
|
||||
We value your feedback!
|
||||
</h2>
|
||||
|
||||
<p class="description">
|
||||
Can you spare a moment to take our survey? <br>Your feedback helps us improve Lucide and make it better for everyone.
|
||||
</p>
|
||||
|
||||
<div class="buttons">
|
||||
<VPButton
|
||||
text="Take the Survey"
|
||||
theme="brand"
|
||||
@click="initTally"
|
||||
/>
|
||||
|
||||
<VPButton
|
||||
text="Close"
|
||||
theme="alt"
|
||||
@click="hidePopup"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ask-overlay {
|
||||
position: fixed;
|
||||
z-index: 99;;
|
||||
bottom: 24px;
|
||||
width: calc(100% - 48px);
|
||||
max-width: 420px;
|
||||
left: 24px;
|
||||
transition:
|
||||
opacity 0.25s,
|
||||
transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--vp-shadow-4);
|
||||
background-color: var(--vp-carbon-ads-bg-color);
|
||||
opacity: 0;
|
||||
transform: translateY(200px);
|
||||
}
|
||||
|
||||
.ask-overlay .title {
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ask-overlay .description {
|
||||
padding-top: 8px;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.ask-overlay.show-popup {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
transition:
|
||||
opacity 0.5s,
|
||||
transform 0.25s ease;
|
||||
}
|
||||
|
||||
.ask-overlay .buttons {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.hide-button {
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
z-index: 3;
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
@@ -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,15 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import RocketDuotoneIcon from '../home/RocketDuotoneIcon.ts';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="top-notification">
|
||||
<p>
|
||||
<Icon
|
||||
:iconNode="RocketDuotoneIcon"
|
||||
size="18"
|
||||
class="rocket-icon"
|
||||
/>
|
||||
<VPLink
|
||||
class="notification-link"
|
||||
href="/guide/version-1"
|
||||
>Lucide v1 is out!</VPLink
|
||||
>🚀<br />
|
||||
>
|
||||
You're looking at the site for v1, for v0 go to
|
||||
<VPLink
|
||||
class="notification-link"
|
||||
@@ -21,11 +28,7 @@ import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
</template>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--vp-layout-top-height: 64px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
@media (min-width: 960px) {
|
||||
:root {
|
||||
--vp-layout-top-height: 32px;
|
||||
}
|
||||
@@ -34,19 +37,19 @@ import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
|
||||
<style scoped>
|
||||
.top-notification {
|
||||
position: fixed;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
height: var(--vp-layout-top-height);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: var(--vp-c-brand-dark);
|
||||
color: var(--vp-c-bg-alt-down);
|
||||
font-size: 14px;
|
||||
padding: 0 16px;
|
||||
padding: 4px 16px;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.notification-link {
|
||||
@@ -58,9 +61,9 @@ import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.top-notification br {
|
||||
display: none;
|
||||
}
|
||||
.rocket-icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,10 +6,10 @@ import SelectIcon from './SelectIcon.vue';
|
||||
|
||||
defineProps<{
|
||||
id?: string;
|
||||
items?: { name: string; route: string; icon: string; iconDark?: string }[];
|
||||
items?: { name: string; value: string; icon?: string; iconDark?: string }[];
|
||||
}>();
|
||||
|
||||
const selected = defineModel<{ name: string; icon: string; iconDark?: string }>();
|
||||
const selected = defineModel<{ name: string; value: string; icon?: string; iconDark?: string }>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -19,14 +19,18 @@ const selected = defineModel<{ name: string; icon: string; iconDark?: string }>(
|
||||
class="select-button"
|
||||
:id="id"
|
||||
>
|
||||
<SelectIcon :item="selected" />
|
||||
<slot name="start-icon">
|
||||
<SelectIcon v-if="selected.icon" :item="selected" />
|
||||
</slot>
|
||||
<span class="select-text">{{ selected.name }}</span>
|
||||
<span class="select-icon">
|
||||
<Icon
|
||||
:iconNode="chevronsUpDown"
|
||||
class="chevron-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<slot name="end-icon">
|
||||
<Icon
|
||||
:iconNode="chevronsUpDown"
|
||||
class="chevron-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</slot>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
|
||||
@@ -43,8 +47,9 @@ const selected = defineModel<{ name: string; icon: string; iconDark?: string }>(
|
||||
:value="item"
|
||||
as="template"
|
||||
>
|
||||
|
||||
<li :class="['select-option', { active, selected }]">
|
||||
<SelectIcon :item="item" />
|
||||
<SelectIcon v-if="item.icon" :item="item" />
|
||||
<span :class="['option-text', { selected }]">{{ item.name }}</span>
|
||||
<span
|
||||
v-if="selected"
|
||||
|
||||
@@ -2,25 +2,28 @@
|
||||
const { item } = defineProps<{
|
||||
item: {
|
||||
name: string;
|
||||
icon: string;
|
||||
icon?: string;
|
||||
iconDark?: string;
|
||||
};
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<img
|
||||
:src="item.icon"
|
||||
:class="{ 'select-item-icon': true, light: item.iconDark }"
|
||||
:alt="`${item.name} logo`"
|
||||
loading="lazy"
|
||||
/>
|
||||
<img
|
||||
v-if="item.iconDark"
|
||||
:src="item.iconDark"
|
||||
:alt="`${item.name} logo`"
|
||||
class="select-item-icon dark"
|
||||
/>
|
||||
<div v-if="item.icon || item.iconDark">
|
||||
<img
|
||||
v-if="item.icon"
|
||||
:src="item.icon"
|
||||
:class="{ 'select-item-icon': true, light: item.iconDark }"
|
||||
:alt="`${item.name} logo`"
|
||||
loading="lazy"
|
||||
/>
|
||||
<img
|
||||
v-if="item.iconDark"
|
||||
:src="item.iconDark"
|
||||
:alt="`${item.name} logo`"
|
||||
class="select-item-icon dark"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -11,49 +11,49 @@ const { page } = useData();
|
||||
const router = useRouter();
|
||||
|
||||
const frameworks = [
|
||||
{ name: 'Vanilla', icon: '/framework-logos/js.svg', route: '/guide/lucide/' },
|
||||
{ name: 'React', icon: '/framework-logos/react.svg', route: '/guide/react/' },
|
||||
{ name: 'Vue', icon: '/framework-logos/vue.svg', route: '/guide/vue/' },
|
||||
{ name: 'Svelte', icon: '/framework-logos/svelte.svg', route: '/guide/svelte/' },
|
||||
{ name: 'Solid', icon: '/framework-logos/solid.svg', route: '/guide/solid/' },
|
||||
{ name: 'Angular', icon: '/framework-logos/angular.svg', route: '/guide/angular/' },
|
||||
{ name: 'Preact', icon: '/framework-logos/preact.svg', route: '/guide/preact/' },
|
||||
{ name: 'Vanilla', icon: '/framework-logos/js.svg', value: '/guide/lucide/' },
|
||||
{ name: 'React', icon: '/framework-logos/react.svg', value: '/guide/react/' },
|
||||
{ name: 'Vue', icon: '/framework-logos/vue.svg', value: '/guide/vue/' },
|
||||
{ name: 'Svelte', icon: '/framework-logos/svelte.svg', value: '/guide/svelte/' },
|
||||
{ name: 'Solid', icon: '/framework-logos/solid.svg', value: '/guide/solid/' },
|
||||
{ name: 'Angular', icon: '/framework-logos/angular.svg', value: '/guide/angular/' },
|
||||
{ name: 'Preact', icon: '/framework-logos/preact.svg', value: '/guide/preact/' },
|
||||
{
|
||||
name: 'React Native',
|
||||
icon: '/framework-logos/react-native.svg',
|
||||
route: '/guide/react-native/',
|
||||
value: '/guide/react-native/',
|
||||
},
|
||||
{
|
||||
name: 'Astro',
|
||||
icon: '/framework-logos/astro.svg',
|
||||
iconDark: '/framework-logos/astro-dark.svg',
|
||||
route: '/guide/astro/',
|
||||
value: '/guide/astro/',
|
||||
},
|
||||
{ name: 'Static', icon: '/framework-logos/svg.svg', route: '/guide/static/' },
|
||||
{ name: 'Static', icon: '/framework-logos/svg.svg', value: '/guide/static/' },
|
||||
];
|
||||
|
||||
const fallbackFramework = useLocalStorage('lucide-docs-fallback-framework', frameworks[1]);
|
||||
|
||||
const selected = computed(() => {
|
||||
const current = frameworks.find(({ route }) => {
|
||||
const current = frameworks.find(({ value }) => {
|
||||
if (router.route.path?.startsWith?.('/guide')) {
|
||||
const [, , framework] = router.route.path.split('/');
|
||||
const [, frameWorkRoute] = route.split('/').filter(Boolean);
|
||||
const [, frameWorkRoute] = value.split('/').filter(Boolean);
|
||||
|
||||
return framework === frameWorkRoute;
|
||||
}
|
||||
return router.route.path.split('/').slice(0, 3).join('/') === route;
|
||||
return router.route.path.split('/').slice(0, 3).join('/') === value;
|
||||
});
|
||||
|
||||
return current || fallbackFramework.value;
|
||||
return current || (fallbackFramework.value?.value ? fallbackFramework.value : frameworks[0]);
|
||||
});
|
||||
|
||||
function onSelectFramework(item: { name: string; icon: string; iconDark?: string; route: string }) {
|
||||
function onSelectFramework(item: { name: string; icon: string; iconDark?: string; value: string }) {
|
||||
fallbackFramework.value = item;
|
||||
if (item.route !== router.route.path) {
|
||||
const likeRoute = router.route.path.replace(selected.value.route, item.route);
|
||||
if (item.value !== router.route.path) {
|
||||
const likeRoute = router.route.path.replace(selected.value.value, item.value);
|
||||
|
||||
const hasRoute = sidebar[item.route]?.some((section) =>
|
||||
const hasRoute = sidebar[item.value]?.some((section) =>
|
||||
section?.items?.some(({ link }) => link === likeRoute),
|
||||
);
|
||||
|
||||
@@ -62,7 +62,7 @@ function onSelectFramework(item: { name: string; icon: string; iconDark?: string
|
||||
return;
|
||||
}
|
||||
|
||||
router.go(item.route);
|
||||
router.go(item.value);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -85,12 +85,12 @@ function onSelectFramework(item: { name: string; icon: string; iconDark?: string
|
||||
/>
|
||||
</div>
|
||||
<VPSidebarGroup
|
||||
:key="selected.route"
|
||||
:key="selected.value"
|
||||
v-if="
|
||||
page?.relativePath?.startsWith?.('guide') &&
|
||||
!page?.relativePath?.startsWith?.(selected.route.substring(1))
|
||||
!page?.relativePath?.startsWith?.(selected.value.substring(1))
|
||||
"
|
||||
:items="sidebar[selected.route]"
|
||||
:items="sidebar[selected.value]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -118,5 +118,15 @@ label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.framework-select:before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
inset: 0 -16px -24px;
|
||||
background: color-mix(in srgb, var(--vp-c-bg-alt), transparent 50%);
|
||||
backdrop-filter: blur(4px);
|
||||
mask-image: linear-gradient(to bottom, black calc(100% - 16px), transparent 100%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,25 +3,7 @@ import Badge from '../base/Badge.vue';
|
||||
import { moveRight } from '../../../data/iconNodes';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { data } from './HomeHeroInfoBefore.data';
|
||||
const rocketDuoTone = [
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M8 18a2 2 0 0 0-2-2c-2.53-.078-3.247 3.605-3.5 5.5 1.895-.253 5.578-.97 5.5-3.5',
|
||||
fill: 'currentColor',
|
||||
opacity: 0.5,
|
||||
},
|
||||
],
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M22 2c0 2.72-.78 7.5-6 11 0 0 1.08 3.38 0 5-.969 1.448-4 2-4 2v-5l-3-3H4s.553-3.033 2-4c1.616-1.077 4.982.043 5 .05A12.88 12.88 0 0 1 22 2m-6 3a3 3 0 1 0 0 6 3 3 0 0 0 0-6',
|
||||
fill: 'currentColor',
|
||||
'fill-rule': 'evenodd',
|
||||
'clip-rule': 'evenodd',
|
||||
},
|
||||
],
|
||||
] as const;
|
||||
import RocketDuotoneIcon from './RocketDuotoneIcon.ts';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -33,7 +15,7 @@ const rocketDuoTone = [
|
||||
class="badge-special confetti-button animate"
|
||||
href="/guide/version-1"
|
||||
>
|
||||
<Icon :iconNode="rocketDuoTone" />
|
||||
<Icon :iconNode="RocketDuotoneIcon" />
|
||||
Version 1
|
||||
<Icon
|
||||
:iconNode="moveRight"
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
const RocketDuotoneIcon = [
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M8 18a2 2 0 0 0-2-2c-2.53-.078-3.247 3.605-3.5 5.5 1.895-.253 5.578-.97 5.5-3.5',
|
||||
fill: 'currentColor',
|
||||
opacity: 0.5,
|
||||
},
|
||||
],
|
||||
[
|
||||
'path',
|
||||
{
|
||||
d: 'M22 2c0 2.72-.78 7.5-6 11 0 0 1.08 3.38 0 5-.969 1.448-4 2-4 2v-5l-3-3H4s.553-3.033 2-4c1.616-1.077 4.982.043 5 .05A12.88 12.88 0 0 1 22 2m-6 3a3 3 0 1 0 0 6 3 3 0 0 0 0-6',
|
||||
fill: 'currentColor',
|
||||
'fill-rule': 'evenodd',
|
||||
'clip-rule': 'evenodd',
|
||||
},
|
||||
],
|
||||
] as const;
|
||||
|
||||
export default RocketDuotoneIcon;
|
||||
@@ -5,11 +5,18 @@ import IconButton from '../base/IconButton.vue';
|
||||
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue';
|
||||
import { x } from '../../../data/iconNodes';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
const { theme } = useData();
|
||||
const showAd = useSessionStorage('show-carbon-ads', true);
|
||||
const showAd = useSessionStorage('show-carbon-ads', true, {
|
||||
initOnMounted: true,
|
||||
});
|
||||
const carbonLoaded = ref(true);
|
||||
const shouldHideAd = computed(() => !showAd.value || !carbonLoaded.value);
|
||||
|
||||
function hideAd() {
|
||||
showAd.value = false;
|
||||
}
|
||||
|
||||
defineProps<{
|
||||
drawerOpen: boolean;
|
||||
@@ -28,13 +35,13 @@ onMounted(() => {
|
||||
<div
|
||||
:class="{
|
||||
'drawer-open': drawerOpen,
|
||||
'hide-ad': !(showAd && carbonLoaded),
|
||||
'hide-ad': shouldHideAd,
|
||||
}"
|
||||
class="floating-ad"
|
||||
v-if="theme.carbonAds"
|
||||
>
|
||||
<IconButton
|
||||
@click="showAd = false"
|
||||
@click="hideAd"
|
||||
class="hide-button"
|
||||
>
|
||||
<Icon
|
||||
@@ -43,7 +50,7 @@ onMounted(() => {
|
||||
absoluteStrokeWidth
|
||||
/>
|
||||
</IconButton>
|
||||
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" />
|
||||
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" overlay />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -66,6 +73,7 @@ onMounted(() => {
|
||||
.floating-ad.hide-ad {
|
||||
transform: translateX(224px);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.floating-ad.drawer-open.hide-ad {
|
||||
@@ -96,6 +104,7 @@ onMounted(() => {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
z-index: 3;
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,10 +7,14 @@ import { useActiveAnchor } from '../../composables/useActiveAnchor'
|
||||
import { data } from './CategoryList.data'
|
||||
import CategoryListItem from './CategoryListItem.vue'
|
||||
import SidebarTitle from './SidebarTitle.vue'
|
||||
import { useCategoryView } from '../../composables/useCategoryView'
|
||||
import { useCategoryViewSync } from '../../composables/useCategoryView'
|
||||
|
||||
const { page } = useData()
|
||||
const { categoryCounts } = useCategoryView();
|
||||
const { categoryCounts, selectedCategory } = useCategoryViewSync();
|
||||
|
||||
function onCategoriesClick() {
|
||||
selectedCategory.value = '';
|
||||
}
|
||||
|
||||
const categoriesIsActive = computed(() => {
|
||||
return isActive(page.value.relativePath, '/icons/categories');
|
||||
@@ -53,7 +57,7 @@ watch(headers, () => {
|
||||
<VPLink class="sidebar-link sidebar-text" href="/icons/" :class="{ 'active': overviewIsActive } ">
|
||||
All
|
||||
</VPLink>
|
||||
<VPLink class="sidebar-link sidebar-text" href="/icons/categories" :class="{ 'active': categoriesIsActive } ">
|
||||
<VPLink class="sidebar-link sidebar-text" href="/icons/categories" :class="{ 'active': categoriesIsActive }" @click="onCategoriesClick">
|
||||
Categories
|
||||
</VPLink>
|
||||
<div class="content">
|
||||
|
||||
@@ -28,7 +28,10 @@ function onClick(categoryName: string) {
|
||||
const heading = document.querySelector<HTMLAnchorElement>(categoryName);
|
||||
heading?.focus();
|
||||
|
||||
window.history.pushState({}, '', `/icons/categories#${categoryName}`)
|
||||
const url = new URL(window.location.href);
|
||||
url.pathname = '/icons/categories';
|
||||
url.hash = categoryName;
|
||||
window.history.pushState({}, '', url);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -85,6 +88,7 @@ function onClick(categoryName: string) {
|
||||
|
||||
.outline-link.inactive {
|
||||
color: var(--vp-c-text-4);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.outline-link.nested {
|
||||
|
||||
@@ -188,7 +188,7 @@ const deprecatedTitle = computed(() => {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
scrollbar-width: thin; /* can also be normal, or none, to not render scrollbar */
|
||||
scrollbar-color: currentColor transparent; /* foreground background */
|
||||
scrollbar-color: var(--vp-c-text-4) transparent; /* foreground background */
|
||||
}
|
||||
.horizontal-scroller::-webkit-scrollbar {
|
||||
width: 0;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, defineAsyncComponent, onMounted } from 'vue';
|
||||
import { ref, computed, watch, defineAsyncComponent, onMounted } from 'vue';
|
||||
import type { IconEntity, Category } from '../../types';
|
||||
import useSearch from '../../composables/useSearch';
|
||||
import InputSearch from '../base/InputSearch.vue';
|
||||
@@ -12,6 +12,7 @@ import useFetchCategories from '../../composables/useFetchCategories';
|
||||
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
|
||||
import chunkArray from '../../utils/chunkArray';
|
||||
import useScrollToCategory from '../../composables/useScrollToCategory';
|
||||
import { useCategoryView } from '../../composables/useCategoryView';
|
||||
import CarbonAdOverlay from './CarbonAdOverlay.vue';
|
||||
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
|
||||
|
||||
@@ -26,8 +27,15 @@ const props = defineProps<{
|
||||
|
||||
const activeIconName = ref(null);
|
||||
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
|
||||
const { selectedCategory } = useCategoryView();
|
||||
const isSearching = computed(() => !!searchQuery.value);
|
||||
|
||||
watch(searchQueryDebounced, (searchString) => {
|
||||
if (searchString !== '') {
|
||||
selectedCategory.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
|
||||
searchInput.value?.focus();
|
||||
});
|
||||
@@ -36,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);
|
||||
@@ -109,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,
|
||||
@@ -127,22 +142,39 @@ 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'));
|
||||
|
||||
function handleCloseDrawer() {
|
||||
setActiveIconName('');
|
||||
|
||||
window.history.pushState({}, '', '/icons/categories');
|
||||
const url = new URL(window.location);
|
||||
url.pathname = '/icons/categories';
|
||||
|
||||
if (searchQueryDebounced.value) {
|
||||
url.searchParams.set('search', searchQueryDebounced.value);
|
||||
}
|
||||
|
||||
if (selectedCategory.value) {
|
||||
url.hash = selectedCategory.value;
|
||||
}
|
||||
|
||||
window.history.pushState({}, '', url);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -150,6 +182,7 @@ function handleCloseDrawer() {
|
||||
<div
|
||||
ref="overviewEl"
|
||||
class="overview-container"
|
||||
:class="{ 'icon-drawer-open': activeIconName }"
|
||||
>
|
||||
<StickyBar class="category-search">
|
||||
<InputSearch
|
||||
@@ -158,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 = ''"
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { IconEntity } from '../../types';
|
||||
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
|
||||
import { useRoute } from 'vitepress';
|
||||
import IconGrid from './IconGrid.vue';
|
||||
import Select from '../base/Select.vue';
|
||||
import InputSearch from '../base/InputSearch.vue';
|
||||
import useSearch from '../../composables/useSearch';
|
||||
import useSearchInput from '../../composables/useSearchInput';
|
||||
@@ -14,9 +15,26 @@ import useFetchCategories from '../../composables/useFetchCategories';
|
||||
import chunkArray from '../../utils/chunkArray';
|
||||
import CarbonAdOverlay from './CarbonAdOverlay.vue';
|
||||
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { listSortDescending } from '~/.vitepress/data/iconNodes';
|
||||
|
||||
const ICON_SIZE = 56;
|
||||
const ICON_GRID_GAP = 8;
|
||||
const SORTING = [
|
||||
{
|
||||
name: 'Popularity',
|
||||
value: 'popularity',
|
||||
},
|
||||
{
|
||||
name: 'Release date',
|
||||
value: 'release-date',
|
||||
},
|
||||
{
|
||||
name: 'Name',
|
||||
value: 'name',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
const initialGridItems = computed(() => {
|
||||
if (containerWidth.value === 0) return 120;
|
||||
@@ -32,9 +50,14 @@ 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);
|
||||
@@ -43,12 +66,29 @@ const columnSize = computed(() => {
|
||||
return Math.floor(containerWidth.value / (ICON_SIZE + ICON_GRID_GAP));
|
||||
});
|
||||
|
||||
const sortedIcons = computed(() => {
|
||||
switch (selectedSort.value.value) {
|
||||
case 'popularity':
|
||||
return [...props.icons].sort((a, b) => (b.popularity || 0) - (a.popularity || 0));
|
||||
case 'release-date':
|
||||
return [...props.icons].sort((a, b) => {
|
||||
const aDate = a.createdRelease?.date ? new Date(a.createdRelease.date).getTime() : 0;
|
||||
const bDate = b.createdRelease?.date ? new Date(b.createdRelease.date).getTime() : 0;
|
||||
return bDate - aDate;
|
||||
});
|
||||
case 'name':
|
||||
return [...props.icons].sort((a, b) => a.name.localeCompare(b.name));
|
||||
default:
|
||||
return props.icons;
|
||||
}
|
||||
});
|
||||
|
||||
const mappedIcons = computed(() => {
|
||||
if (tags.value == null) {
|
||||
return props.icons;
|
||||
return sortedIcons.value;
|
||||
}
|
||||
|
||||
return props.icons.map((icon) => {
|
||||
return sortedIcons.value.map((icon) => {
|
||||
const iconTags = tags.value[icon.name];
|
||||
const iconCategories = categories.value?.[icon.name] ?? [];
|
||||
|
||||
@@ -68,11 +108,15 @@ const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
|
||||
|
||||
const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
|
||||
{ name: 'name', weight: 3 },
|
||||
{ name: 'aliases', weight: 3 },
|
||||
{ name: 'aliases', weight: 8 },
|
||||
{ name: 'tags', weight: 2 },
|
||||
{ name: 'categories', weight: 1 },
|
||||
]);
|
||||
|
||||
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);
|
||||
@@ -98,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'));
|
||||
@@ -118,7 +168,14 @@ watch(searchQueryDebounced, () => {
|
||||
function handleCloseDrawer() {
|
||||
setActiveIconName('');
|
||||
|
||||
window.history.pushState({}, '', '/icons/');
|
||||
const url = new URL(window.location);
|
||||
url.pathname = '/icons/';
|
||||
|
||||
if (searchQueryDebounced.value) {
|
||||
url.searchParams.set('search', searchQueryDebounced.value);
|
||||
}
|
||||
|
||||
window.history.pushState({}, '', url);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -126,6 +183,7 @@ function handleCloseDrawer() {
|
||||
<div
|
||||
ref="overviewEl"
|
||||
class="overview-container"
|
||||
:class="{ 'icon-drawer-open': activeIconName }"
|
||||
>
|
||||
<StickyBar>
|
||||
<InputSearch
|
||||
@@ -134,11 +192,25 @@ function handleCloseDrawer() {
|
||||
ref="searchInput"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
@focus="onFocusSearchInput"
|
||||
@focus="loadSearchMetadata"
|
||||
/>
|
||||
|
||||
<Select
|
||||
id="sort-select"
|
||||
:items="SORTING"
|
||||
v-model="selectedSort"
|
||||
>
|
||||
<template #start-icon>
|
||||
<Icon
|
||||
:iconNode="listSortDescending"
|
||||
class="chevron-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</template>
|
||||
</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>
|
||||
|
||||
@@ -9,20 +9,63 @@
|
||||
margin-bottom: 16px;
|
||||
position: sticky;
|
||||
z-index: 10;
|
||||
top: 48px;
|
||||
padding-top: 24px;
|
||||
margin-top: -32px;
|
||||
top: var(--vp-layout-top-height);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin-bottom: 32px;
|
||||
background: var(--vp-c-bg);
|
||||
box-shadow: 0 16px 24px var(--vp-c-bg);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-button) {
|
||||
padding: 11px 14px;
|
||||
background-color: var(--vp-c-bg-alt);
|
||||
color: var(--vp-c-text-2);
|
||||
flex-shrink: 0;
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-button):hover,
|
||||
.search-bar :deep(.select-button):focus {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-bg-alt);
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-options) {
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-option) {
|
||||
padding-right: 36px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-icon) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-button) .select-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.search-bar :deep(.select-button) {
|
||||
padding: 11px 16px 11px 16px;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-button) .select-text {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.search-bar {
|
||||
padding-top: 32px;
|
||||
top: 64px;
|
||||
margin-top: calc((var(--vp-layout-top-height, 0px) + 32px) * -1);
|
||||
top: calc(var(--vp-layout-top-height, 0px) + 64px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<script setup lang="ts">
|
||||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { useAside } from 'vitepress/dist/client/theme-default/composables/aside.js'
|
||||
import { useData } from 'vitepress/dist/client/theme-default/composables/data.js'
|
||||
|
||||
const { page } = useData()
|
||||
const props = defineProps<{
|
||||
carbonAds: DefaultTheme.CarbonAdsOptions
|
||||
overlay?: boolean
|
||||
}>()
|
||||
|
||||
const carbonOptions = props.carbonAds
|
||||
|
||||
const { isAsideEnabled } = useAside()
|
||||
const container = ref()
|
||||
|
||||
let isInitialized = false
|
||||
|
||||
function init() {
|
||||
if (!isInitialized) {
|
||||
isInitialized = true
|
||||
const params = new URLSearchParams({
|
||||
serve: carbonOptions.code,
|
||||
placement: carbonOptions.placement,
|
||||
format: 'responsive',
|
||||
})
|
||||
const s = document.createElement('script')
|
||||
s.id = '_carbonads_js'
|
||||
s.src = `//cdn.carbonads.com/carbon.js?${params.toString()}`
|
||||
s.async = true
|
||||
container.value.appendChild(s)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => page.value.relativePath, () => {
|
||||
if (isInitialized && isAsideEnabled.value) {
|
||||
;(window as any)._carbonads?.refresh()
|
||||
}
|
||||
})
|
||||
|
||||
// no need to account for option changes during dev, we can just
|
||||
// refresh the page
|
||||
if (carbonOptions) {
|
||||
onMounted(() => {
|
||||
// if the page is loaded when aside is active, load carbon directly.
|
||||
// otherwise, only load it if the page resizes to wide enough. this avoids
|
||||
// loading carbon at all on mobile where it's never shown
|
||||
if (isAsideEnabled.value || props.overlay) {
|
||||
init()
|
||||
} else {
|
||||
watch(isAsideEnabled, (wide) => wide && init())
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="VPCarbonAds" ref="container" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.VPCarbonAds {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
min-height: 256px;
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
background-color: var(--vp-carbon-ads-bg-color);
|
||||
}
|
||||
|
||||
.VPCarbonAds :deep(img) {
|
||||
margin: 0 auto;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.VPCarbonAds :deep(.carbon-text) {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding-top: 12px;
|
||||
color: var(--vp-carbon-ads-text-color);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.VPCarbonAds :deep(.carbon-text:hover) {
|
||||
color: var(--vp-carbon-ads-hover-text-color);
|
||||
}
|
||||
.VPCarbonAds :deep(#carbon-responsive .carbon-poweredby),
|
||||
.VPCarbonAds :deep(.carbon-poweredby) {
|
||||
display: block;
|
||||
padding-bottom: 2px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-carbon-ads-poweredby-color);
|
||||
text-transform: uppercase;
|
||||
transition: color 0.25s;
|
||||
}
|
||||
.VPCarbonAds :deep(#carbon-responsive) {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.VPCarbonAds :deep(#carbon-responsive .carbon-poweredby) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.VPCarbonAds :deep(#carbon-responsive .carbon-poweredby:hover),
|
||||
.VPCarbonAds :deep(.carbon-poweredby:hover) {
|
||||
color: var(--vp-carbon-ads-hover-poweredby-color);
|
||||
}
|
||||
|
||||
.VPCarbonAds :deep(> div) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.VPCarbonAds :deep(> div:first-of-type) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.VPCarbonAds :deep(#carbon-responsive) {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.VPCarbonAds :deep(#carbon-responsive .carbon-responsive-wrap) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -3,6 +3,7 @@ import { useData } from 'vitepress';
|
||||
import { useSidebar } from 'vitepress/dist/client/theme-default/composables/sidebar';
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
import { computed } from 'vue';
|
||||
import SurveyAskOverlay from '../SurveyAskOverlay.vue';
|
||||
|
||||
const { theme } = useData();
|
||||
const { hasSidebar } = useSidebar();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useRoute } from 'vitepress';
|
||||
import { ref, inject, Ref, onMounted, watch } from 'vue';
|
||||
import { ref, inject, Ref, onMounted, onUnmounted, watch } from 'vue';
|
||||
|
||||
export const CATEGORY_VIEW_CONTEXT = Symbol('categoryView');
|
||||
|
||||
@@ -13,25 +13,53 @@ export const categoryViewContext = {
|
||||
categoryCounts: ref({}),
|
||||
};
|
||||
|
||||
const CATEGORIES_PATH = '/icons/categories';
|
||||
|
||||
export function useCategoryView(): CategoryViewContext {
|
||||
const context = inject<CategoryViewContext>(CATEGORY_VIEW_CONTEXT);
|
||||
const route = useRoute();
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useCategoryView must be used with categoryView context');
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (window.location.hash) {
|
||||
context.selectedCategory.value = decodeURIComponent(window.location.hash.slice(1));
|
||||
return context;
|
||||
}
|
||||
|
||||
export function useCategoryViewSync(): CategoryViewContext {
|
||||
const context = useCategoryView();
|
||||
const route = useRoute();
|
||||
|
||||
function syncCategoryFromLocation() {
|
||||
if (!window.location.pathname.startsWith(CATEGORIES_PATH)) {
|
||||
context.selectedCategory.value = '';
|
||||
context.categoryCounts.value = {};
|
||||
return;
|
||||
}
|
||||
|
||||
context.selectedCategory.value = window.location.hash
|
||||
? decodeURIComponent(window.location.hash.slice(1))
|
||||
: '';
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
syncCategoryFromLocation();
|
||||
window.addEventListener('hashchange', syncCategoryFromLocation);
|
||||
window.addEventListener('popstate', syncCategoryFromLocation);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('hashchange', syncCategoryFromLocation);
|
||||
window.removeEventListener('popstate', syncCategoryFromLocation);
|
||||
});
|
||||
|
||||
watch(route, (currentRoute) => {
|
||||
if (currentRoute.path !== '/icons/categories') {
|
||||
if (currentRoute.path !== CATEGORIES_PATH) {
|
||||
context.selectedCategory.value = '';
|
||||
context.categoryCounts.value = {};
|
||||
return;
|
||||
}
|
||||
|
||||
syncCategoryFromLocation();
|
||||
});
|
||||
|
||||
return context;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,13 @@ export default function useScrollToCategory({
|
||||
}
|
||||
}
|
||||
|
||||
watch(selectedCategory, scrollToSelectedCategory);
|
||||
watch(selectedCategory, (value) => {
|
||||
if (value) {
|
||||
scrollToSelectedCategory(value);
|
||||
} else {
|
||||
scrollTo(0);
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
@@ -43,7 +49,11 @@ export default function useScrollToCategory({
|
||||
});
|
||||
|
||||
watch(searchQueryDebounced, () => {
|
||||
scrollTo(0);
|
||||
if (selectedCategory.value) {
|
||||
scrollToSelectedCategory(selectedCategory.value);
|
||||
} else {
|
||||
scrollTo(0);
|
||||
}
|
||||
});
|
||||
|
||||
watch(categories, (items) => {
|
||||
|
||||
@@ -20,16 +20,6 @@ const useSearch = <T>(
|
||||
return index.value.search(query.value).map((result) => result.item);
|
||||
}
|
||||
|
||||
if (keys.length !== 0) {
|
||||
const mainKey = keys[0].name;
|
||||
|
||||
return collection.value.sort((a, b) => {
|
||||
const aString = a[mainKey as keyof T] as string;
|
||||
const bString = b[mainKey as keyof T] as string;
|
||||
return aString.localeCompare(bString);
|
||||
});
|
||||
}
|
||||
|
||||
return collection.value;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useDebounce } from '@vueuse/core';
|
||||
import { nextTick, onMounted, ref, watch } from 'vue';
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
|
||||
const useSearchInput = () => {
|
||||
const searchInput = ref();
|
||||
@@ -13,11 +13,10 @@ const useSearchInput = () => {
|
||||
newUrl.searchParams.delete('search');
|
||||
} else {
|
||||
newUrl.searchParams.set('search', searchString);
|
||||
newUrl.hash = '';
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
window.history.replaceState({}, '', newUrl);
|
||||
});
|
||||
window.history.replaceState({}, '', newUrl);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -7,7 +7,6 @@ import IconsSidebarNavAfter from './layouts/IconsSidebarNavAfter.vue';
|
||||
import HomeHeroIconsCard from './components/home/HomeHeroIconsCard.vue';
|
||||
import HomeHeroAfter from './components/home/HomeHeroAfter.vue';
|
||||
import HomeHeroInfoBefore from './components/home/HomeHeroInfoBefore.vue';
|
||||
import LayoutTop from './components/base/LayoutTop.vue';
|
||||
import { ICON_STYLE_CONTEXT, iconStyleContext } from './composables/useIconStyle';
|
||||
import { CATEGORY_VIEW_CONTEXT, categoryViewContext } from './composables/useCategoryView';
|
||||
import { EXTERNAL_LIBS_CONTEXT, externalLibContext } from './composables/useExternalLibs';
|
||||
@@ -23,7 +22,8 @@ const theme: Partial<Theme> = {
|
||||
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
|
||||
'home-hero-image': () => h(HomeHeroIconsCard),
|
||||
'home-hero-actions-after': () => h(HomeHeroAfter),
|
||||
'layout-top': () => h(LayoutTop),
|
||||
// Keep this here for now, we might want to add more things to the top bar in the future
|
||||
// 'layout-top': () => h(LayoutTop),
|
||||
});
|
||||
},
|
||||
enhanceApp({ app }) {
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
animation: .3s transition 0s ease;
|
||||
} */
|
||||
|
||||
html {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.VPHome {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
@@ -117,6 +121,10 @@
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.overview-container.icon-drawer-open {
|
||||
padding-bottom: 288px;
|
||||
}
|
||||
|
||||
.VPHomeHero .container {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
@@ -154,3 +162,24 @@
|
||||
margin: 0 auto 48px;
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
.VPSidebar {
|
||||
/* Hide Scrollbar */
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
scrollbar-width: thin; /* can also be normal, or none, to not render scrollbar */
|
||||
scrollbar-color: var(--vp-c-text-4) transparent; /* foreground background */
|
||||
}
|
||||
.VPSidebar::-webkit-scrollbar {
|
||||
width: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.VPSidebar::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.VPSidebar::-webkit-scrollbar-thumb {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface IconEntity extends IconMetaData {
|
||||
externalLibrary?: ExternalLibs;
|
||||
createdRelease?: Release;
|
||||
changedRelease?: Release;
|
||||
popularity?: number;
|
||||
}
|
||||
|
||||
export interface Category {
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import { type PageData } from 'vitepress';
|
||||
import getStructuredData from './getStructuredData';
|
||||
import { createGeneralOGImage, createIconOGImage } from './createOGImage';
|
||||
|
||||
export async function transformPageData(pageData: PageData) {
|
||||
pageData.frontmatter.head ??= [];
|
||||
if (
|
||||
pageData.relativePath.startsWith('icons/') &&
|
||||
!pageData.relativePath.startsWith('icons/lab/') &&
|
||||
pageData.params?.name
|
||||
) {
|
||||
const iconName = pageData.params.name;
|
||||
pageData.title = `${iconName} icon details`;
|
||||
|
||||
const taggedAs = pageData.params?.tags?.length
|
||||
? `Tagged as: ${pageData.params.tags.join(', ')}.`
|
||||
: '';
|
||||
const categorizedIn = pageData.params?.category?.length
|
||||
? `Categorized in: ${pageData.params.category.join(', ')}.`
|
||||
: '';
|
||||
|
||||
pageData.description =
|
||||
`Details and related icons for ${iconName} icon. ${taggedAs} ${categorizedIn}`.trim();
|
||||
|
||||
const structuredData = await getStructuredData(iconName, pageData);
|
||||
|
||||
const ogPath = await createIconOGImage(iconName, pageData.params?.tags || []);
|
||||
|
||||
if (ogPath) {
|
||||
const content = `https://lucide.dev${ogPath}`;
|
||||
pageData.frontmatter.head.push(
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
pageData.frontmatter.head.push([
|
||||
'script',
|
||||
{ type: 'application/ld+json' },
|
||||
JSON.stringify(structuredData),
|
||||
]);
|
||||
}
|
||||
|
||||
if (pageData.relativePath.startsWith('guide/')) {
|
||||
const ogPath = await createGeneralOGImage(pageData);
|
||||
|
||||
if (ogPath) {
|
||||
const content = `https://lucide.dev${ogPath}`;
|
||||
pageData.frontmatter.head.push(
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+38
@@ -46,5 +46,43 @@ declare global {
|
||||
*/
|
||||
append(container: Element): void;
|
||||
};
|
||||
Tally: {
|
||||
// From https://tally.so/help/developer-resources#2f676e40530a460ea6a634b8441f6001
|
||||
openPopup: (
|
||||
formId: string,
|
||||
options?: {
|
||||
key?: string; // This is used as a unique identifier used for the "Show only once" and "Don't show after submit" functionality
|
||||
layout?: 'default' | 'modal';
|
||||
width?: number;
|
||||
alignLeft?: boolean;
|
||||
hideTitle?: boolean;
|
||||
overlay?: boolean;
|
||||
emoji?: {
|
||||
text: string;
|
||||
animation:
|
||||
| 'none'
|
||||
| 'wave'
|
||||
| 'tada'
|
||||
| 'heart-beat'
|
||||
| 'spin'
|
||||
| 'flash'
|
||||
| 'bounce'
|
||||
| 'rubber-band'
|
||||
| 'head-shake';
|
||||
};
|
||||
autoClose?: number; // in milliseconds
|
||||
showOnce?: boolean;
|
||||
doNotShowAfterSubmit?: boolean;
|
||||
customFormUrl?: string; // when you want to load the form via it's custom domain URL
|
||||
hiddenFields?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
onOpen?: () => void;
|
||||
onClose?: () => void;
|
||||
onPageView?: (page: number) => void;
|
||||
onSubmit?: (payload: SubmissionPayload) => void;
|
||||
},
|
||||
) => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"}
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ However, not everyone can understand them easily. Read more about [how to use Lu
|
||||
|
||||
## Official Packages
|
||||
|
||||
Lucide's official packages are designed to work on different platforms, making it easier for users to integrate icons into their projects. The packages are available for various technologies, including [Web (Vanilla)](https://lucide.dev/guide/packages/lucide), [React](https://lucide.dev/guide/packages/lucide-react), [React Native](https://lucide.dev/guide/packages/lucide-react-native), [Vue](https://lucide.dev/guide/packages/lucide-vue), [Vue 3](https://lucide.dev/guide/packages/lucide-vue-next), [Svelte](https://lucide.dev/guide/packages/lucide-svelte), [Preact](https://lucide.dev/guide/packages/lucide-preact), [Solid](https://lucide.dev/guide/packages/lucide-solid), [Angular](https://lucide.dev/guide/packages/angular), [Astro](https://lucide.dev/guide/packages/lucide-astro), and [NodeJS](https://lucide.dev/guide/packages/lucide-static#nodejs).
|
||||
Lucide's official packages are designed to work on different platforms, making it easier for users to integrate icons into their projects. The packages are available for various technologies, including [Web (Vanilla)](./lucide/index.md), [React](./react/index.md), [React Native](./react-native/index.md), [Vue](./vue/index.md), [Svelte](./svelte/index.md), [Preact](./preact/index.md), [Solid](./solid/index.md), [Angular](./angular/index.md), [Astro](./astro/index.md), and [Static](./static/index.md).
|
||||
|
||||
## Community
|
||||
|
||||
|
||||
+64
-28
@@ -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
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
::: warning
|
||||
This documentation is for `@lucide/angular`.
|
||||
|
||||
To learn about our legacy package for Angular, please refer to [`lucide-angular`](./lucide-angular).
|
||||
To learn about our legacy package for Angular, please refer to [`lucide-angular`](https://v0.lucide.dev/guide/packages/lucide-angular).
|
||||
:::
|
||||
|
||||
A standalone, signal-based, zoneless implementation of Lucide icons for Angular.
|
||||
|
||||
**What you can accomplish:**
|
||||
|
||||
- Use icons as standalone Angular components with full dependency injection support
|
||||
- Configure icons globally through modern Angular providers
|
||||
- Integrate with Angular's reactive forms and data binding
|
||||
@@ -118,6 +119,7 @@ Alternatively, the component also accepts string inputs.
|
||||
To use icons this way, first, you have to provide icons via `provideLucideIcons`:
|
||||
|
||||
:::code-group
|
||||
|
||||
```ts{7-10} [app.config.ts]
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideLucideIcons, LucideCircleCheck, LucideCircleX } from '@lucide/angular';
|
||||
@@ -148,6 +150,7 @@ import { LucideIcon } from '@lucide/angular';
|
||||
})
|
||||
export class Foobar { }
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: tip
|
||||
@@ -259,19 +262,25 @@ export class Foobar {
|
||||
## Troubleshooting
|
||||
|
||||
### The icon is not being displayed
|
||||
|
||||
If using per-icon-components:
|
||||
|
||||
1. Ensure that the icon component is being imported, if using per-icon-components
|
||||
2. Check that the icon name matches exactly (case-sensitive)
|
||||
|
||||
If using the dynamic component:
|
||||
|
||||
1. Ensure the icon is provided via `provideLucideIcons()` if using string names
|
||||
2. Verify the icon is imported from `@lucide/angular` and not the legacy package
|
||||
|
||||
### TypeScript errors?
|
||||
|
||||
Make sure you're importing from `@lucide/angular` and not `lucide-angular`.
|
||||
|
||||
### Icons render with wrong defaults
|
||||
|
||||
Ensure `provideLucideConfig()` is used at the right level.
|
||||
|
||||
## Migration guide
|
||||
|
||||
Migrating from `lucide-angular`? Read our [comprehensive migration guide](https://github.com/lucide-icons/lucide/blob/main/packages/angular/MIGRATION.md).
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
# Lucide Angular
|
||||
|
||||
::: warning
|
||||
This documentation is for our legacy package for Angular.
|
||||
|
||||
For our modern, standalone-first implementation, please refer to [`@lucide/angular`](./angular).
|
||||
:::
|
||||
|
||||
Angular components and services for Lucide icons that integrate with Angular's dependency injection and component system. Provides both traditional module-based and modern standalone component approaches for maximum flexibility in Angular applications.
|
||||
|
||||
**What you can accomplish:**
|
||||
- Use icons as Angular components with full dependency injection support
|
||||
- Configure icons globally through Angular services and providers
|
||||
- Choose from multiple component selectors (lucide-angular, lucide-icon, i-lucide, span-lucide)
|
||||
- Integrate with Angular's reactive forms and data binding
|
||||
- Build scalable applications with tree-shaken icon bundles and lazy loading support
|
||||
|
||||
## Installation
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add lucide-angular
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add lucide-angular
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install lucide-angular
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add lucide-angular
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## How to use
|
||||
|
||||
### Step 1: Import `LucideAngularModule`
|
||||
|
||||
In any Angular module you wish to use Lucide icons in, you have to import `LucideAngularModule`, and pick any icons you wish to use:
|
||||
|
||||
```js
|
||||
import { LucideAngularModule, File, House, Menu, UserCheck } from 'lucide-angular';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
LucideAngularModule.pick({File, House, Menu, UserCheck})
|
||||
]
|
||||
})
|
||||
export class AppModule { }
|
||||
```
|
||||
|
||||
or using standalone version:
|
||||
|
||||
```js
|
||||
import { Component } from '@angular/core';
|
||||
import { LucideAngularModule, FileIcon } from 'lucide-angular';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss',
|
||||
imports: [LucideAngularModule]
|
||||
})
|
||||
export class AppComponent {
|
||||
readonly FileIcon = FileIcon;
|
||||
}
|
||||
```
|
||||
### Step 2: Use the icons in templates
|
||||
|
||||
Within your templates you may now use one of the following component tags to insert an icon:
|
||||
|
||||
```html
|
||||
<lucide-angular name="file" class="my-icon"></lucide-angular>
|
||||
<lucide-icon name="house" class="my-icon"></lucide-icon>
|
||||
<i-lucide name="menu" class="my-icon"></i-lucide>
|
||||
<span-lucide name="user-check" class="my-icon"></span-lucide>
|
||||
```
|
||||
for standalone
|
||||
```html
|
||||
<lucide-angular [img]="FileIcon" class="my-icon"></lucide-angular>
|
||||
<lucide-icon [img]="FileIcon" class="my-icon"></lucide-icon>
|
||||
<i-lucide [img]="FileIcon" class="my-icon"></i-lucide>
|
||||
<span-lucide [img]="FileIcon" class="my-icon"></span-lucide>
|
||||
```
|
||||
|
||||
### Props
|
||||
|
||||
You can pass additional props to adjust the icon appearance.
|
||||
|
||||
| name | type | default |
|
||||
| --------------------- | --------- | ------------ |
|
||||
| `size` | *number* | 24 |
|
||||
| `color` | *string* | currentColor |
|
||||
| `strokeWidth` | *number* | 2 |
|
||||
| `absoluteStrokeWidth` | *boolean* | false |
|
||||
|
||||
```html
|
||||
<i-lucide name="house" [size]="48" color="red" [strokeWidth]="1"></i-lucide>
|
||||
```
|
||||
|
||||
### Global configuration
|
||||
|
||||
You can inject the `LucideIconConfig` service in your root component to globally configure the default property values as defined above.
|
||||
|
||||
### Styling using a custom CSS class
|
||||
|
||||
Any extra HTML attribute is ignored, but the `class` attribute
|
||||
is passed onto the internal SVG image element and it can be used to style it:
|
||||
|
||||
```css
|
||||
svg.my-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
stroke-width: 3;
|
||||
}
|
||||
```
|
||||
|
||||
## Injecting multiple icon providers
|
||||
|
||||
You may provide additional icons using the `LUCIDE_ICONS` injection token,
|
||||
which accepts multiple providers of the interface `LucideIconsProviderInterface`
|
||||
with the utility class `LucideIconsProvider` available for easier usage:
|
||||
|
||||
```js
|
||||
import { LUCIDE_ICONS, LucideIconProvider } from 'lucide-angular';
|
||||
import { MyIcon } from './icons/my-icon';
|
||||
|
||||
const myIcons = {MyIcon};
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
{provide: LUCIDE_ICONS, multi: true, useValue: new LucideIconProvider(myIcons)},
|
||||
]
|
||||
})
|
||||
export class AppModule { }
|
||||
```
|
||||
|
||||
To add custom icons, you will first need to convert them to an [svgson format](https://github.com/elrumordelaluz/svgson).
|
||||
|
||||
## Loading all icons
|
||||
|
||||
::: danger
|
||||
You may also opt to import all icons if necessary using the following format but be aware that this will significantly increase your application build size.
|
||||
:::
|
||||
|
||||
```js
|
||||
import { icons } from 'lucide-angular';
|
||||
|
||||
...
|
||||
|
||||
LucideAngularModule.pick(icons)
|
||||
```
|
||||
|
||||
## With Lucide Lab or custom icons
|
||||
|
||||
[Lucide Lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||
They can be used in the same way as the official icons.
|
||||
|
||||
```js
|
||||
import { LucideAngularModule } from 'lucide-angular';
|
||||
import { coconut } from '@lucide/lab';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
LucideAngularModule.pick({ coconut })
|
||||
]
|
||||
})
|
||||
export class AppModule { }
|
||||
```
|
||||
|
||||
## Accessibility
|
||||
|
||||
By default, we hide icons from screen readers using `aria-hidden="true"`.
|
||||
|
||||
You can add accessibility attributes using aria-labels.
|
||||
|
||||
```html
|
||||
<lucide-icon name="check" aria-label="Task completed"></lucide-icon>
|
||||
```
|
||||
|
||||
For best practices on accessibility, please see our [accessibility guide](../accessibility.md).
|
||||
@@ -3,6 +3,7 @@
|
||||
Vue 2 components for Lucide icons that integrate with Vue's Options API and template system. Each icon is a Vue component that renders as an inline SVG, providing familiar Vue development patterns for legacy applications still using Vue 2.
|
||||
|
||||
**What you can accomplish:**
|
||||
|
||||
- Use icons as Vue 2 components with Options API integration
|
||||
- Maintain legacy Vue 2 applications with modern icon components
|
||||
- Integrate with Vue 2's template system and component lifecycle
|
||||
@@ -155,7 +156,7 @@ You can add accessibility attributes using aria-labels.
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { Check } from 'lucide-vue-next';
|
||||
import { Check } from '@lucide/vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -9,7 +9,7 @@ import Sandpack from '~/.vitepress/theme/components/editors/SandpackPreact.vue'
|
||||
# Combining icons
|
||||
|
||||
You can combine multiple icons into a single icon by nesting SVG elements.
|
||||
This is useful if you want to create custom icons icons by combining existing ones.
|
||||
This is useful if you want to create custom icons by combining existing ones.
|
||||
|
||||
::: sandpack {showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="lucide-preact"}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ description: Learn how to combine multiple icons into a single icon nested SVG e
|
||||
# Combining icons
|
||||
|
||||
You can combine multiple icons into a single icon by nesting SVG elements.
|
||||
This is useful if you want to create custom icons icons by combining existing ones.
|
||||
This is useful if you want to create custom icons by combining existing ones.
|
||||
|
||||
```SnackPlayer name=State&ext=js&dependencies=react-native-svg,lucide-react-native
|
||||
import React, {useState, useEffect} from 'react';
|
||||
|
||||
@@ -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.
|
||||
:::
|
||||
@@ -9,7 +9,7 @@ import Sandpack from '~/.vitepress/theme/components/editors/Sandpack.vue'
|
||||
# Combining icons
|
||||
|
||||
You can combine multiple icons into a single icon by nesting SVG elements.
|
||||
This is useful if you want to create custom icons icons by combining existing ones.
|
||||
This is useful if you want to create custom icons by combining existing ones.
|
||||
|
||||
::: sandpack {template=react showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="lucide-react"}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ description: Learn about the different types exported by the `lucide-react` pack
|
||||
# TypeScript Support
|
||||
|
||||
List of exported types from the `lucide-react` package.
|
||||
These can be used to type your components when using Lucide icons in a TypeScript React project
|
||||
These can be used to type your components when using Lucide icons in a TypeScript React project.
|
||||
|
||||
## `LucideProps`
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user