mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-17 16:17:43 +01:00
Compare commits
430 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f732b411d | ||
|
|
ce09c31f08 | ||
|
|
c2b059fb60 | ||
|
|
b3c80d027a | ||
|
|
20f30bb5ea | ||
|
|
c47ae67a3b | ||
|
|
7866a5a5c6 | ||
|
|
92bc88b001 | ||
|
|
e75fbcdec4 | ||
|
|
4cef8283a7 | ||
|
|
330f4b37db | ||
|
|
fd31cb44a8 | ||
|
|
790d30dbfa | ||
|
|
e7c075785f | ||
|
|
6d4c91707d | ||
|
|
c0ea92ebe7 | ||
|
|
42dc5508dd | ||
|
|
4dda432471 | ||
|
|
0775d8647e | ||
|
|
83ef8fc98d | ||
|
|
5b56ef705d | ||
|
|
dafe529892 | ||
|
|
151c5b145c | ||
|
|
d6f9043096 | ||
|
|
b4405f05ab | ||
|
|
9076da5f1b | ||
|
|
2e7d806282 | ||
|
|
c4e5730bc4 | ||
|
|
02b35e2518 | ||
|
|
f183c3ba20 | ||
|
|
67e9efb801 | ||
|
|
2e4c9a65be | ||
|
|
de4e8d0acd | ||
|
|
1f113d4274 | ||
|
|
dffffc7aff | ||
|
|
f78061b488 | ||
|
|
076e0bbcd9 | ||
|
|
33bb95edcd | ||
|
|
4c8650a7bf | ||
|
|
ebf400befd | ||
|
|
0f6dc52a8a | ||
|
|
83d5ccc3e8 | ||
|
|
a3886eb829 | ||
|
|
86957f0205 | ||
|
|
465b98df74 | ||
|
|
d3b2255c0a | ||
|
|
a4beac39a3 | ||
|
|
53311befff | ||
|
|
156b3379e4 | ||
|
|
8dd82dd592 | ||
|
|
80d6f737e0 | ||
|
|
a94044cc3a | ||
|
|
2c46ea9948 | ||
|
|
190e8372af | ||
|
|
d3826ce952 | ||
|
|
ee66dd258d | ||
|
|
265770857f | ||
|
|
1c20fc66d7 | ||
|
|
c34c5f2aea | ||
|
|
6432dddfe0 | ||
|
|
028fc12657 | ||
|
|
2e794a296a | ||
|
|
be59e647c1 | ||
|
|
e560b8bf3a | ||
|
|
74d6fbc941 | ||
|
|
a2a0821c66 | ||
|
|
b787576f4a | ||
|
|
375ad12aeb | ||
|
|
be55088e75 | ||
|
|
32e93c043f | ||
|
|
920fa7888b | ||
|
|
95766b6682 | ||
|
|
db15bb85c0 | ||
|
|
84c210acf5 | ||
|
|
3c7815c5c0 | ||
|
|
7bb27142c8 | ||
|
|
2811d60184 | ||
|
|
730c24ec55 | ||
|
|
56ebfeb596 | ||
|
|
905d38186c | ||
|
|
1cfb3ff70e | ||
|
|
31cfa79818 | ||
|
|
d1903220db | ||
|
|
ec567e59fc | ||
|
|
1d6b5d6858 | ||
|
|
c368d01d92 | ||
|
|
99427026a3 | ||
|
|
81a1f6e1dc | ||
|
|
7fb4ffc7a7 | ||
|
|
30529bbeb2 | ||
|
|
279ac9ce13 | ||
|
|
2dede9886b | ||
|
|
781fc399a2 | ||
|
|
087b59d55e | ||
|
|
e1f96d8f54 | ||
|
|
7cf38b5b6e | ||
|
|
9c441bd4ff | ||
|
|
507a86a890 | ||
|
|
8d4d9cd6d5 | ||
|
|
341be34b1f | ||
|
|
d52c369b99 | ||
|
|
a7a1973d3f | ||
|
|
b6dfcef41b | ||
|
|
1c5a9dafc1 | ||
|
|
546bb1f391 | ||
|
|
64c1a55e01 | ||
|
|
0e3ac6beb9 | ||
|
|
c7c0e2bead | ||
|
|
85dba80322 | ||
|
|
f8a1a76b83 | ||
|
|
bd50af7afb | ||
|
|
cfabc9f608 | ||
|
|
09f98127c6 | ||
|
|
867c0581af | ||
|
|
987edb14f8 | ||
|
|
9db4dc7e1c | ||
|
|
2150ce75ce | ||
|
|
106e37c3ce | ||
|
|
528ffd6fa5 | ||
|
|
3b92fae633 | ||
|
|
fcf42bf754 | ||
|
|
0b6149c02d | ||
|
|
bc9a8de419 | ||
|
|
26ab33a832 | ||
|
|
ac4c3581c4 | ||
|
|
1ddba6da04 | ||
|
|
8754c9b6fd | ||
|
|
e9febe4fab | ||
|
|
570f7d7e76 | ||
|
|
6d43b9feb3 | ||
|
|
16166f838b | ||
|
|
425bcc3f3d | ||
|
|
982af91724 | ||
|
|
a3ec4428f1 | ||
|
|
673e76831d | ||
|
|
b4291576b7 | ||
|
|
95f5ecacad | ||
|
|
ef5c1b9bdd | ||
|
|
b01715ebe8 | ||
|
|
05dd5fcfde | ||
|
|
e71198d9b3 | ||
|
|
57714e36ea | ||
|
|
358c9c1e80 | ||
|
|
64ddff76c5 | ||
|
|
31a9cdcbcc | ||
|
|
69319ee2f0 | ||
|
|
7beb3ee666 | ||
|
|
396d6d42ce | ||
|
|
37a60eefb2 | ||
|
|
c2c46b21ed | ||
|
|
0a0c8aa7e7 | ||
|
|
dc14e6ea8e | ||
|
|
1f67ef5307 | ||
|
|
ff6f9a09ad | ||
|
|
585b65f28f | ||
|
|
405feea8f6 | ||
|
|
5931a4a227 | ||
|
|
d3bafc2848 | ||
|
|
a6f4a08bda | ||
|
|
5a93aff903 | ||
|
|
e6c50d543b | ||
|
|
0d7c70cab6 | ||
|
|
379d1b1002 | ||
|
|
7d6998941d | ||
|
|
667bb8034d | ||
|
|
fd9c489d7a | ||
|
|
acb0e37ac2 | ||
|
|
6c25cbee67 | ||
|
|
08121af31e | ||
|
|
2d484469cd | ||
|
|
f66d167ead | ||
|
|
c63e5a456e | ||
|
|
aac12f991a | ||
|
|
3844a4b346 | ||
|
|
98b43826a4 | ||
|
|
e8194cbabe | ||
|
|
17bbbe1cbb | ||
|
|
c698121170 | ||
|
|
7a380a8177 | ||
|
|
55623e1611 | ||
|
|
803d237fef | ||
|
|
151bd33cd7 | ||
|
|
b3b8cfd59c | ||
|
|
996fc68c7e | ||
|
|
69e99093b8 | ||
|
|
030c7b9cb9 | ||
|
|
6f384680b4 | ||
|
|
e810b7273f | ||
|
|
599c517188 | ||
|
|
95830a0d88 | ||
|
|
5f0c19afaa | ||
|
|
37135af384 | ||
|
|
0fdc2413f6 | ||
|
|
d565b951d7 | ||
|
|
3e7d748827 | ||
|
|
0e54626bdb | ||
|
|
110d8cce27 | ||
|
|
9db00bfe3c | ||
|
|
e21d0e2dd6 | ||
|
|
483a594622 | ||
|
|
2136843515 | ||
|
|
ad842796dc | ||
|
|
f6a7eadd99 | ||
|
|
f51a01a7d0 | ||
|
|
30ed887ad1 | ||
|
|
a87ac013f7 | ||
|
|
87a67cc314 | ||
|
|
e418be64a1 | ||
|
|
0ca76a1de1 | ||
|
|
be81f20a6b | ||
|
|
41d32b8f40 | ||
|
|
c543914fb0 | ||
|
|
40d9e44b03 | ||
|
|
5821c62880 | ||
|
|
d15973e7ef | ||
|
|
475439bca9 | ||
|
|
13fc99f80d | ||
|
|
6a49a51164 | ||
|
|
ba5a701faf | ||
|
|
9e74dbb617 | ||
|
|
3e644fda2d | ||
|
|
7517894f2d | ||
|
|
5e363f48af | ||
|
|
537040bf81 | ||
|
|
308e738e0e | ||
|
|
e16e8b2783 | ||
|
|
e7f03df844 | ||
|
|
8aed9b826d | ||
|
|
e332d8b7ce | ||
|
|
92aeac569f | ||
|
|
53c16e191b | ||
|
|
4791674365 | ||
|
|
8527049827 | ||
|
|
8205ef68ed | ||
|
|
f5c708adf2 | ||
|
|
d42539d2b4 | ||
|
|
3614b6b46e | ||
|
|
86443054e2 | ||
|
|
cc09c2640f | ||
|
|
394399bfb8 | ||
|
|
af1b0e033e | ||
|
|
bdb5f6c137 | ||
|
|
d42a21610f | ||
|
|
f5be7a9862 | ||
|
|
52d408e6d0 | ||
|
|
f69c760598 | ||
|
|
22a43cbdeb | ||
|
|
1ca70df074 | ||
|
|
5514709fb3 | ||
|
|
34a8280ba8 | ||
|
|
f814c94672 | ||
|
|
e1cfbe14be | ||
|
|
a549fd65ef | ||
|
|
239bbd9a9a | ||
|
|
85b6de6684 | ||
|
|
19fa01b5fc | ||
|
|
e3e6dd9ccc | ||
|
|
ba77aa6a76 | ||
|
|
42f36a9da3 | ||
|
|
9abed5ee16 | ||
|
|
e6246ed5c6 | ||
|
|
877b467d96 | ||
|
|
781620d669 | ||
|
|
3ad1f07ffc | ||
|
|
47880403d3 | ||
|
|
89d204a35d | ||
|
|
bdcd417c1c | ||
|
|
4c7738fea1 | ||
|
|
12e7cfd7f7 | ||
|
|
09841dd721 | ||
|
|
8ae65a8fb1 | ||
|
|
fda32bee52 | ||
|
|
e913eab8bb | ||
|
|
2408b8bb8f | ||
|
|
8b257abaf6 | ||
|
|
c00ef3a835 | ||
|
|
0d3c8f4ea2 | ||
|
|
771cb0bbd6 | ||
|
|
3f34f4e7b6 | ||
|
|
e4f411ce9c | ||
|
|
0f47888a17 | ||
|
|
81ce8fed07 | ||
|
|
03b3a4f179 | ||
|
|
24178ca1bc | ||
|
|
e91dd659f0 | ||
|
|
6277ceb274 | ||
|
|
45b22c9f2d | ||
|
|
3a375e1a17 | ||
|
|
ac09719ba6 | ||
|
|
8d8c41c371 | ||
|
|
951c142e96 | ||
|
|
dcdc0b8bb3 | ||
|
|
e11e134ce1 | ||
|
|
e149de42ff | ||
|
|
5baa37b85a | ||
|
|
7916c44484 | ||
|
|
922ba5c781 | ||
|
|
c9c1b47e7a | ||
|
|
03eb86287a | ||
|
|
1ab3826607 | ||
|
|
2fde138c46 | ||
|
|
2640a7c585 | ||
|
|
25ce89b191 | ||
|
|
fd3f3eb607 | ||
|
|
b473c10af9 | ||
|
|
a01c15cc8c | ||
|
|
0fccc276c3 | ||
|
|
06c33964cb | ||
|
|
232d7b4557 | ||
|
|
3205e11173 | ||
|
|
0778ec87ad | ||
|
|
85983552ee | ||
|
|
7b954807da | ||
|
|
9105f0e3dd | ||
|
|
fe57bdd291 | ||
|
|
34cb6b12f8 | ||
|
|
6a8591c348 | ||
|
|
547b1846ae | ||
|
|
187ce5cff8 | ||
|
|
40f544433b | ||
|
|
05fa11e073 | ||
|
|
e4988bc1df | ||
|
|
3a24adec64 | ||
|
|
a620a0b08c | ||
|
|
2cc06fa703 | ||
|
|
9aac2e8424 | ||
|
|
2517eb642c | ||
|
|
7acf492f63 | ||
|
|
abe8910247 | ||
|
|
b35b16e96e | ||
|
|
b49c387cfe | ||
|
|
61d6f93023 | ||
|
|
1a3e9f273e | ||
|
|
8023c01f37 | ||
|
|
674a6bf799 | ||
|
|
e3a476a027 | ||
|
|
a021c1dbce | ||
|
|
a5ea7932dc | ||
|
|
6eb67bde5a | ||
|
|
19eb810537 | ||
|
|
6c865ccd9a | ||
|
|
667c80b39f | ||
|
|
dbada2a227 | ||
|
|
849d9c8af2 | ||
|
|
d61d47470f | ||
|
|
8b80cf2cef | ||
|
|
41f6c26228 | ||
|
|
02efe87c5a | ||
|
|
dc39f3d96a | ||
|
|
9879d8f4ed | ||
|
|
f95afd258a | ||
|
|
6ca07ea1bc | ||
|
|
3ff082cfd9 | ||
|
|
f776aaa9bc | ||
|
|
1587bee16d | ||
|
|
c78686ceca | ||
|
|
a11ba9e1cd | ||
|
|
99d992678a | ||
|
|
14822456aa | ||
|
|
64d6b10239 | ||
|
|
0232ce490d | ||
|
|
9d9e2c995a | ||
|
|
c68ee7f9a0 | ||
|
|
5f5e052038 | ||
|
|
aeb376f3a2 | ||
|
|
ed733916f3 | ||
|
|
64a8b7d4d0 | ||
|
|
c138c2e97d | ||
|
|
d82718b222 | ||
|
|
acdcf74faf | ||
|
|
9fa525e8c6 | ||
|
|
20c0012e62 | ||
|
|
70248c0f42 | ||
|
|
f8af6ae309 | ||
|
|
e8f64d8dcc | ||
|
|
4ea41dc329 | ||
|
|
4835ae67a9 | ||
|
|
6d6aa4c4cc | ||
|
|
70dad77b96 | ||
|
|
cc09030f9d | ||
|
|
73a6c8045d | ||
|
|
fccfaa3dd0 | ||
|
|
14fec0e184 | ||
|
|
fcebb5c4db | ||
|
|
1058ea5ae3 | ||
|
|
e08a1f7973 | ||
|
|
07f9d3ea79 | ||
|
|
73a2c9da6c | ||
|
|
afa4176737 | ||
|
|
cb86576661 | ||
|
|
9b738c1cb6 | ||
|
|
aefb710e5c | ||
|
|
df063fa378 | ||
|
|
2c3b6754d5 | ||
|
|
47303ac790 | ||
|
|
3aab3b9b08 | ||
|
|
3d29ab9279 | ||
|
|
e0fbbb6f2d | ||
|
|
c1331798f5 | ||
|
|
ddad14a96d | ||
|
|
a17609b5e4 | ||
|
|
e619963183 | ||
|
|
56948fe7c2 | ||
|
|
68116414d0 | ||
|
|
16a18f7908 | ||
|
|
ae8af191f7 | ||
|
|
1787b82cfe | ||
|
|
0e49b2b88c | ||
|
|
bab52d7333 | ||
|
|
c96260d82a | ||
|
|
3c23331e9e | ||
|
|
5a7c49dc55 | ||
|
|
6ae047557e | ||
|
|
fa064306fa | ||
|
|
4f46f43379 | ||
|
|
fc87643aae | ||
|
|
6f4f47684f | ||
|
|
c6632ce8d7 | ||
|
|
a59c5de61a | ||
|
|
2bd0ae55fd | ||
|
|
3f2ad5b2a6 | ||
|
|
1250fc276d | ||
|
|
97f214934d | ||
|
|
34dfc63ce2 | ||
|
|
b46927e510 | ||
|
|
1828a392c8 | ||
|
|
3ab6c373a0 | ||
|
|
ba2c4b526f | ||
|
|
bde3f01e0b | ||
|
|
50630b3aaf |
@@ -8,10 +8,10 @@ squircle
|
||||
strikethrough
|
||||
touchpad
|
||||
ungroup
|
||||
pilcrow
|
||||
toc
|
||||
|
||||
# Brands
|
||||
codepen
|
||||
codesandbox
|
||||
dribbble
|
||||
x.com
|
||||
|
||||
10
.github/ISSUE_TEMPLATE/01_icon_request.yml
vendored
10
.github/ISSUE_TEMPLATE/01_icon_request.yml
vendored
@@ -7,8 +7,10 @@ body:
|
||||
value: |
|
||||
Before submitting an icon request check if it has already been requested. If there is an open request, please add a 👍.
|
||||
|
||||
**Important note**: No new brand logos are allowed, see https://github.com/lucide-icons/lucide/issues/670.
|
||||
Existing brand icons will also be phased out. For brand icons, consider using https://simpleicons.org, which offers purpose-built SVGs that are also on a 24×24px grid.
|
||||
> [!CAUTION]
|
||||
> New brand logos are **not** allowed, see our official statement: https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md.
|
||||
>
|
||||
> Existing brand icons are being phased out. Consider using https://simpleicons.org, which offers purpose-built SVGs that are also on a 24×24px grid.
|
||||
- type: input
|
||||
id: name
|
||||
attributes:
|
||||
@@ -41,9 +43,9 @@ body:
|
||||
required: true
|
||||
- label: I have searched existing icons to make sure it does not already exist and I didn't find any.
|
||||
required: true
|
||||
- label: I am not requesting a brand logo and the art is not protected by copyright.
|
||||
- label: I am not requesting a brand logo and the art is not protected by copyright, see more at https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md
|
||||
required: true
|
||||
- label: I am not requesting an icon that includes religious, political imagery or hate symbols.
|
||||
- label: I am not requesting an icon that includes religious, war/violence related, political imagery or hate symbols.
|
||||
required: true
|
||||
- label: I have provided appropriate use cases for the icon(s) requested.
|
||||
required: true
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/02_bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/02_bug_report.yml
vendored
@@ -22,6 +22,7 @@ body:
|
||||
- label: lucide-svelte
|
||||
- label: lucide-vue
|
||||
- label: lucide-vue-next
|
||||
- label: lucide-astro
|
||||
- label: Figma plugin
|
||||
- label: source/main
|
||||
- label: other/not relevant
|
||||
|
||||
@@ -22,6 +22,7 @@ body:
|
||||
- label: lucide-svelte
|
||||
- label: lucide-vue
|
||||
- label: lucide-vue-next
|
||||
- label: lucide-astro
|
||||
- label: Figma plugin
|
||||
- label: all JS packages
|
||||
- label: site
|
||||
|
||||
44
.github/actions/build-and-test.yml
vendored
44
.github/actions/build-and-test.yml
vendored
@@ -1,44 +0,0 @@
|
||||
name: 'Build and Test'
|
||||
description: 'Builds and test a package'
|
||||
|
||||
inputs:
|
||||
name:
|
||||
description: “Name of the package”
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --filter lucide-preact
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter lucide-preact build
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter lucide-preact test
|
||||
41
.github/actions/check-icons.yml
vendored
41
.github/actions/check-icons.yml
vendored
@@ -1,41 +0,0 @@
|
||||
name: 'Check icons'
|
||||
description: 'Cross-checks icon and category references in JSON descriptors'
|
||||
|
||||
inputs:
|
||||
name:
|
||||
description: “Name of the package”
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --filter .
|
||||
|
||||
- name: Check icons and categories
|
||||
run: pnpm checkIcons
|
||||
6
.github/labeler.yml
vendored
6
.github/labeler.yml
vendored
@@ -79,6 +79,12 @@
|
||||
- any-glob-to-any-file:
|
||||
- 'packages/lucide-solid/*'
|
||||
|
||||
# For changes in the lucide astro package
|
||||
🚀 astro package:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'packages/astro/*'
|
||||
|
||||
# For changes in the lucide static package
|
||||
🪨 static package:
|
||||
- changed-files:
|
||||
|
||||
28
.github/pull_request_template.md
vendored
28
.github/pull_request_template.md
vendored
@@ -1,16 +1,18 @@
|
||||
<!-- Thank you for contributing! -->
|
||||
|
||||
<!--
|
||||
PR Title Guidelines:
|
||||
|
||||
Please use the format: <type>(<scope>): <short description>
|
||||
|
||||
Example: feat(icons): added `camera` icon
|
||||
|
||||
Available types: fix, feat, perf, docs, style, refactor, test, chore, ci, build
|
||||
Common scopes: icons, docs, studio, site, dev
|
||||
-->
|
||||
|
||||
<!-- Insert `closes #issueNumber` here if merging this PR will resolve an existing issue -->
|
||||
|
||||
## What is the purpose of this pull request?
|
||||
<!-- Please choose one of the following, and put an "x" next to it. -->
|
||||
- [ ] New Icon
|
||||
- [ ] Bug fix
|
||||
- [ ] New Feature
|
||||
- [ ] Documentation update
|
||||
- [ ] Other:
|
||||
|
||||
### Description
|
||||
## Description
|
||||
<!-- Please insert your description here and provide info about the "what" this PR is contribution -->
|
||||
|
||||
### Icon use case <!-- ONLY for new icons, remove this part if not icon PR -->
|
||||
@@ -23,10 +25,12 @@
|
||||
|
||||
### Concept <!-- ONLY for new icons -->
|
||||
<!-- All of these requirements must be fulfilled. -->
|
||||
<!-- IMPORTANT! Please read our official statement on brand logos in Lucide: -->
|
||||
<!-- https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md -->
|
||||
- [ ] I have provided valid use cases for each icon.
|
||||
- [ ] I have not added any a brand or logo icon.
|
||||
- [ ] I have [not added any brand or logo icon](https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md).
|
||||
- [ ] I have not used any hate symbols.
|
||||
- [ ] I have not included any religious or political imagery.
|
||||
- [ ] I have not included any religious, war/violence related or political imagery.
|
||||
|
||||
### Author, credits & license<!-- ONLY for new icons. -->
|
||||
<!-- Please choose one of the following, and put an "x" next to it. -->
|
||||
|
||||
40
.github/workflows/ci.yml
vendored
40
.github/workflows/ci.yml
vendored
@@ -7,20 +7,24 @@ on:
|
||||
paths:
|
||||
- icons/**/*.svg
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)')
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -32,25 +36,19 @@ jobs:
|
||||
id: latest-tag
|
||||
run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Log latest tag
|
||||
run: echo '${{ steps.latest-tag.outputs.LATEST_TAG }}'
|
||||
|
||||
- name: Check if we can patch
|
||||
run: .github/workflows/version-up.sh --minor
|
||||
run: pnpm semver $LATEST_TAG -i minor
|
||||
env:
|
||||
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
|
||||
|
||||
- name: Create new version
|
||||
id: new-version
|
||||
run: echo "NEW_VERSION=$(.github/workflows/version-up.sh --minor)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create change log
|
||||
id: change-log
|
||||
run: |
|
||||
CHANGE_LOG=$(pnpm run generate:changelog --old-tag=${{ steps.latest-tag.outputs.LATEST_TAG }})
|
||||
CHANGE_LOG=$(tail -n +5 <<< $CHANGE_LOG)
|
||||
echo $CHANGE_LOG
|
||||
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
|
||||
echo "CHANGE_LOG<<$EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CHANGE_LOG" >> $GITHUB_OUTPUT
|
||||
echo "$EOF" >> $GITHUB_OUTPUT
|
||||
run: echo "NEW_VERSION=$(pnpm semver $LATEST_TAG -i minor)" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
|
||||
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
|
||||
|
||||
- name: Check output
|
||||
run: |
|
||||
@@ -61,8 +59,8 @@ jobs:
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
name: New icons ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
body: ${{ steps.change-log.outputs.CHANGE_LOG }}
|
||||
name: Version ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
generate_release_notes: true
|
||||
|
||||
start-release:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Close Issue with Banned Phrases
|
||||
name: Close Icon Requests with Brand Terms
|
||||
|
||||
on:
|
||||
issues:
|
||||
@@ -11,25 +11,38 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check for blocked phrases in issue title
|
||||
- name: Load stopwords from JSON & check issue title & body
|
||||
if: contains(github.event.issue.labels.*.name, '🙌 icon request')
|
||||
run: |
|
||||
ISSUE_TITLE=$(jq -r '.issue.title' "$GITHUB_EVENT_PATH")
|
||||
BLOCKED_PHRASES=("twitter" "whatsapp" "logo" "google" "tiktok" "facebook" "slack" "discord" "bluesky")
|
||||
ISSUE_BODY=$(jq -r '.issue.body // ""' "$GITHUB_EVENT_PATH")
|
||||
ICON_NAME_SECTION=$(printf '%s\n' "$ISSUE_BODY" | awk '/### Icon name/{flag=1; next} /^### /{flag=0} flag')
|
||||
|
||||
# Check title and body for blocked phrases
|
||||
for PHRASE in "${BLOCKED_PHRASES[@]}"
|
||||
do
|
||||
if echo "$ISSUE_TITLE" | grep -i "$PHRASE"; then
|
||||
gh issue close ${{ github.event.issue.number }} --reason "not planned" --comment "This looks like a duplicate, use the [search](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+$PHRASE) to find similar issues.
|
||||
jq -r 'to_entries[] | "\(.key) \(.value)"' brand-stopwords.json | while read -r KEY VALUE; do
|
||||
SAFE_KEY=$(printf '%s\n' "$KEY" | sed 's/[][\.^$*]/\\&/g')
|
||||
SAFE_VALUE=$(printf '%s\n' "$VALUE" | sed 's/[][\.^$*]/\\&/g')
|
||||
|
||||
Read more about brand guideline rules at [We're not accepting new Brand icons #670](https://github.com/lucide-icons/lucide/issues/670).
|
||||
if echo "$ISSUE_TITLE" | grep -iqE "$SAFE_KEY|$SAFE_VALUE" || \
|
||||
{ [ -n "$ICON_NAME_SECTION" ] && echo "$ICON_NAME_SECTION" | grep -iqE "$SAFE_KEY|$SAFE_VALUE"; }; then
|
||||
|
||||
gh issue close ${{ github.event.issue.number }} \
|
||||
--reason "not_planned" \
|
||||
--comment "It looks like this request is about **${VALUE}**, which is a brand logo.
|
||||
|
||||
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)
|
||||
|
||||
You can [search for similar issues.](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+${VALUE})
|
||||
|
||||
We’re always happy to help on [Discord](https://discord.gg/EH6nSts)."
|
||||
|
||||
Always happy to help on [Discord](https://discord.gg/EH6nSts)."
|
||||
gh issue lock ${{ github.event.issue.number }} --reason spam
|
||||
exit 1
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
58
.github/workflows/comment-icon-preview.yml
vendored
Normal file
58
.github/workflows/comment-icon-preview.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: Icon preview comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['Pull request icon preview']
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
- name: 'Download artifact'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: context.payload.workflow_run.id,
|
||||
});
|
||||
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "pr_number"
|
||||
})[0];
|
||||
let download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
const fs = require('fs');
|
||||
fs.writeFileSync('${{github.workspace}}/pr_number.zip', Buffer.from(download.data));
|
||||
|
||||
- name: 'Unzip artifact'
|
||||
run: unzip pr_number.zip
|
||||
|
||||
- name: 'Get PR number'
|
||||
run: echo "number=$(cat NR)" >> $GITHUB_OUTPUT
|
||||
id: pr-number
|
||||
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v2
|
||||
id: pr-comment
|
||||
with:
|
||||
issue-number: ${{ steps.pr-number.outputs.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: Added or changed icons
|
||||
|
||||
- name: Create or update comment
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
comment-id: ${{ steps.pr-comment.outputs.comment-id }}
|
||||
issue-number: ${{ steps.pr-number.outputs.number }}
|
||||
body-path: comment-markup.md
|
||||
edit-mode: replace
|
||||
1
.github/workflows/labeler.yml
vendored
1
.github/workflows/labeler.yml
vendored
@@ -9,4 +9,5 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/labeler@v5
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
name: Linting
|
||||
name: Linting PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- icons/*.svg
|
||||
|
||||
jobs:
|
||||
linting:
|
||||
lint-code:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
32
.github/workflows/lint-pr-title.yml
vendored
Normal file
32
.github/workflows/lint-pr-title.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Linting PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- reopened
|
||||
|
||||
jobs:
|
||||
lint-pr-title:
|
||||
name: PR Title Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
types: |
|
||||
fix
|
||||
feat
|
||||
perf
|
||||
refactor
|
||||
test
|
||||
style
|
||||
docs
|
||||
ci
|
||||
build
|
||||
chore
|
||||
requireScope: true
|
||||
ignoreLabels: |
|
||||
bot
|
||||
39
.github/workflows/linting-icons.yml
vendored
Normal file
39
.github/workflows/linting-icons.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Linting Icons
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'icons/*'
|
||||
|
||||
jobs:
|
||||
lint-filenames:
|
||||
name: Lint Filenames
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v46
|
||||
with:
|
||||
files: icons/*
|
||||
|
||||
- name: Generate annotations
|
||||
run: node ./scripts/lintFilenames.mts
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
|
||||
lint-aliases:
|
||||
name: Check Uniqueness of Aliases
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- 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 '"
|
||||
16
.github/workflows/lucide-angular.yml
vendored
16
.github/workflows/lucide-angular.yml
vendored
@@ -11,12 +11,12 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -27,12 +27,12 @@ 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@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
name: Lucide Vue checks
|
||||
name: Lucide Astro Checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/lucide-vue/**
|
||||
- packages/astro/**
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
node-version-file: 'package.json'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter lucide-vue build
|
||||
run: pnpm --filter @lucide/astro build
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -33,11 +32,11 @@ jobs:
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v3.8.1
|
||||
with:
|
||||
node-version: 18
|
||||
node-version-file: 'package.json'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter lucide-vue test
|
||||
run: pnpm --filter @lucide/astro test
|
||||
11
.github/workflows/lucide-font.yml
vendored
11
.github/workflows/lucide-font.yml
vendored
@@ -3,7 +3,6 @@ name: Lucide font checks
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- icons/**
|
||||
- tools/build-font/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
@@ -11,12 +10,12 @@ jobs:
|
||||
lucide-font:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -28,7 +27,7 @@ jobs:
|
||||
run: pnpm build:font
|
||||
|
||||
- name: 'Upload to Artifacts'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
|
||||
8
.github/workflows/lucide-preact.yml
vendored
8
.github/workflows/lucide-preact.yml
vendored
@@ -13,12 +13,12 @@ jobs:
|
||||
lucide-preact:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
26
.github/workflows/lucide-react-native.yml
vendored
26
.github/workflows/lucide-react-native.yml
vendored
@@ -10,15 +10,31 @@ on:
|
||||
- pnpm-lock.yaml
|
||||
|
||||
jobs:
|
||||
lucide-react-native:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter lucide-react-native 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
|
||||
|
||||
16
.github/workflows/lucide-react.yml
vendored
16
.github/workflows/lucide-react.yml
vendored
@@ -14,12 +14,12 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -30,12 +30,12 @@ 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@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
8
.github/workflows/lucide-shared.yml
vendored
8
.github/workflows/lucide-shared.yml
vendored
@@ -10,12 +10,12 @@ 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@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
16
.github/workflows/lucide-solid.yml
vendored
16
.github/workflows/lucide-solid.yml
vendored
@@ -13,12 +13,12 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -29,12 +29,12 @@ 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@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
8
.github/workflows/lucide-static.yml
vendored
8
.github/workflows/lucide-static.yml
vendored
@@ -11,12 +11,12 @@ jobs:
|
||||
lucide-static:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
43
.github/workflows/lucide-svelte-5.yml
vendored
Normal file
43
.github/workflows/lucide-svelte-5.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Lucide Svelte 5 checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/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
|
||||
16
.github/workflows/lucide-svelte.yml
vendored
16
.github/workflows/lucide-svelte.yml
vendored
@@ -13,12 +13,12 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -29,12 +29,12 @@ 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@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
16
.github/workflows/lucide-vue-next.yml
vendored
16
.github/workflows/lucide-vue-next.yml
vendored
@@ -13,12 +13,12 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -29,12 +29,12 @@ 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@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
16
.github/workflows/lucide.yml
vendored
16
.github/workflows/lucide.yml
vendored
@@ -12,12 +12,12 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -28,12 +28,12 @@ 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@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
46
.github/workflows/pull-request-icon-preview.yml
vendored
Normal file
46
.github/workflows/pull-request-icon-preview.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Pull request icon preview
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'icons/*.svg'
|
||||
|
||||
jobs:
|
||||
generate-changed-icons-comment:
|
||||
name: Generate Changed Icons Comment
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v46
|
||||
with:
|
||||
files: icons/*.svg
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- name: Install svgson for code preview (safer and faster than installing all deps)
|
||||
run: npm install svgson@5.3.1 --force
|
||||
|
||||
- name: Save PR number
|
||||
run: |
|
||||
mkdir -p ./pr
|
||||
echo ${{ github.event.number }} > ./pr/NR
|
||||
|
||||
- name: Generate comment markup
|
||||
run: node ./scripts/generateChangedIconsCommentMarkup.mts >> ./pr/comment-markup.md
|
||||
id: comment-markup
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pr_number
|
||||
path: pr/
|
||||
37
.github/workflows/pull-request-tags-suggestions.yml
vendored
Normal file
37
.github/workflows/pull-request-tags-suggestions.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Pull request tags suggestions
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'icons/*.json'
|
||||
|
||||
jobs:
|
||||
pull-request-tags-suggestions:
|
||||
name: Pull Request Tags Suggestions
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
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
|
||||
with:
|
||||
repository: lucide-icons/lucide
|
||||
- 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: Generate comment markup
|
||||
run: node ./scripts/suggestTags.mts
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
PULL_REQUEST_NUMBER: ${{ github.event.number }}
|
||||
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
123
.github/workflows/pull-request.yml
vendored
123
.github/workflows/pull-request.yml
vendored
@@ -1,123 +0,0 @@
|
||||
name: Add Changed Icons comment
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'icons/*'
|
||||
branches:
|
||||
- main
|
||||
- fix-icon-preview
|
||||
|
||||
jobs:
|
||||
lint-filenames:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
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
|
||||
with:
|
||||
files: icons/*
|
||||
- name: Generate annotations
|
||||
run: node ./scripts/lintFilenames.mjs
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
|
||||
lint-contributors:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
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
|
||||
with:
|
||||
files: icons/*
|
||||
- uses: actions/setup-node@v4
|
||||
- name: Install simple-git (safer and faster than installing all deps)
|
||||
run: npm install simple-git
|
||||
- name: Generate annotations
|
||||
run: node ./scripts/updateContributors.mjs
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
FETCH_DEPTH: ${{ github.event.pull_request.commits }}
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
- name: Generate annotations
|
||||
env:
|
||||
ANNOTATION_SEVERITY: notice
|
||||
ANNOTATION_TITLE: Contributors have changed!
|
||||
ANNOTATION_DESCRIPTION: Don't add people who have only performed automatic optimizations.
|
||||
run: |
|
||||
git diff --unified=0 -- icons/*.json | # diff icon metadata (unified=0 gives the correct chunk line number)
|
||||
perl -ne '/^(\+|- |@)/ && print' | # get chunks (lines that start with "+++", "@@", "+ ", "- ")
|
||||
perl -pe 's/\n/%0A/' | # url encode line breaks (\n -> %0A)
|
||||
perl -pe 's/%0A(\+\+\+ b\/)/\n\1/g' | # split chunks(one chunk per line)
|
||||
perl -pe "s/\+\+\+ b\/([^@]*)%0A@@ -(\d+)[^\s]* \+(\d+)[^@]*@@(.*)/::$ANNOTATION_SEVERITY file=\1,line=\2,endLine=\3,title=$ANNOTATION_TITLE::$ANNOTATION_DESCRIPTION%0A\4/"
|
||||
# Example for the previous substitution
|
||||
# input: +++ b/icons/accessibility.json%0A@@ -2,0 +3 @@%0A+ "contributors": ["hi"],%0A@@ -13 +14 @@%0A+}%0A
|
||||
# output: ::$ANNOTATION_SEVERITY file=icons/accessibility.json,line=2,endLine=3,title=$ANNOTATION_TITLE::$ANNOTATION_DESCRIPTION%0A%0A+ "contributors": ["hi"],%0A@@ -13 +14 @@%0A+}%0A
|
||||
|
||||
lint-aliases:
|
||||
name: Check Uniqueness of Aliases
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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 '"
|
||||
|
||||
generate-changed-icons-comment:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
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
|
||||
with:
|
||||
files: icons/*.svg
|
||||
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v2
|
||||
id: pr-comment
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: Added or changed icons
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- name: Install svgson for code preview (safer and faster than installing all deps)
|
||||
run: npm install svgson
|
||||
|
||||
- name: Generate comment markup
|
||||
run: node ./scripts/generateChangedIconsCommentMarkup.mjs >> comment-markup.md
|
||||
id: comment-markup
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
|
||||
- name: Create or update comment
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
comment-id: ${{ steps.pr-comment.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body-path: ./comment-markup.md
|
||||
edit-mode: replace
|
||||
66
.github/workflows/release.yml
vendored
66
.github/workflows/release.yml
vendored
@@ -18,9 +18,13 @@ on:
|
||||
description: Version
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
pre-release:
|
||||
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere"]', github.actor)
|
||||
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere", "jguddas"]', github.actor)
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
VERSION: ${{ steps.get_version.outputs.VERSION }}
|
||||
@@ -38,6 +42,9 @@ jobs:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
needs: pre-release
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -46,27 +53,25 @@ jobs:
|
||||
'lucide',
|
||||
'lucide-react',
|
||||
'lucide-react-native',
|
||||
'lucide-vue',
|
||||
'lucide-vue-next',
|
||||
'lucide-angular',
|
||||
'lucide-preact',
|
||||
'lucide-solid',
|
||||
'lucide-svelte',
|
||||
'@lucide/astro',
|
||||
'@lucide/svelte',
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Set Auth Token
|
||||
run: npm config set //registry.npmjs.org/:_authToken ${{ inputs.NPM_TOKEN || secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Set new version
|
||||
run: pnpm --filter ${{ matrix.package }} version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
|
||||
|
||||
@@ -77,27 +82,30 @@ jobs:
|
||||
run: pnpm --filter ${{ matrix.package }} test
|
||||
|
||||
- name: Publish
|
||||
run: pnpm --filter ${{ matrix.package }} publish --no-git-checks --ignore-scripts
|
||||
run: pnpm --filter ${{ matrix.package }} publish --access public --no-git-checks --ignore-scripts
|
||||
env:
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
|
||||
lucide-static:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [pre-release, lucide-font]
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Set Auth Token
|
||||
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Set new version
|
||||
run: pnpm --filter lucide-static version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
|
||||
|
||||
@@ -109,18 +117,20 @@ jobs:
|
||||
|
||||
- name: Publish
|
||||
run: pnpm --filter lucide-static publish --no-git-checks
|
||||
env:
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
|
||||
lucide-font:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
needs: pre-release
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -132,7 +142,7 @@ jobs:
|
||||
run: pnpm build:font
|
||||
|
||||
- name: 'Upload to Artifacts'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
@@ -141,17 +151,19 @@ jobs:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [pre-release, lucide-font]
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/download-artifact@v4
|
||||
- name: Zip font and icons
|
||||
run: |
|
||||
zip -r lucide-font-${{ needs.pre-release.outputs.VERSION }}.zip lucide-font
|
||||
zip -r lucide-icons-${{ needs.pre-release.outputs.VERSION }}.zip icons
|
||||
|
||||
- name: Release zip and fonts
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ needs.pre-release.outputs.VERSION }}
|
||||
files: |
|
||||
|
||||
33
.github/workflows/request-review.yml
vendored
Normal file
33
.github/workflows/request-review.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: 'Request Review'
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
paths:
|
||||
- icons/*.svg
|
||||
|
||||
jobs:
|
||||
request-review:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
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
|
||||
with:
|
||||
files: icons/*.svg
|
||||
- run: |
|
||||
while IFS= read -r file; do
|
||||
jq -r '.contributors[]' "${file%.svg}.json"
|
||||
done <<< "$CHANGED_FILES" | while read -r contributor; do
|
||||
gh pr edit "${{ github.event.pull_request.number }}" --add-reviewer "$contributor" || true
|
||||
done
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
284
.github/workflows/version-up.sh
vendored
284
.github/workflows/version-up.sh
vendored
@@ -1,284 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
## Copyright (C) 2017, Oleksandr Kucherenko
|
||||
## Last revisit: 2017-09-29
|
||||
|
||||
## get highest version tag for all branches
|
||||
function highest_tag(){
|
||||
local TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
echo "$TAG"
|
||||
}
|
||||
|
||||
## extract current branch name
|
||||
function current_branch(){
|
||||
## expected: heads/{branch_name}
|
||||
## expected: {branch_name}
|
||||
local BRANCH=$(git rev-parse --abbrev-ref HEAD | cut -d"/" -f2)
|
||||
echo "$BRANCH"
|
||||
}
|
||||
|
||||
## get latest/head commit hash number
|
||||
function head_hash(){
|
||||
local COMMIT_HASH=$(git rev-parse --verify HEAD)
|
||||
echo "$COMMIT_HASH"
|
||||
}
|
||||
|
||||
## extract tag commit hash code, tag name provided by argument
|
||||
function tag_hash(){
|
||||
local TAG_HASH=$(git log -1 --format=format:"%H" $1 2>/dev/null | tail -n1)
|
||||
echo "$TAG_HASH"
|
||||
}
|
||||
|
||||
## get latest revision number
|
||||
function latest_revision(){
|
||||
local REV=$(git rev-list --count HEAD 2>/dev/null)
|
||||
echo "$REV"
|
||||
}
|
||||
|
||||
## parse last found tag, extract it PARTS
|
||||
function parse_last(){
|
||||
local position=$(($1-1))
|
||||
|
||||
# two parts found only
|
||||
local SUBS=( ${PARTS[$position]//-/ } )
|
||||
#echo ${SUBS[@]}, size: ${#SUBS}
|
||||
|
||||
# found NUMBER
|
||||
PARTS[$position]=${SUBS[0]}
|
||||
#echo ${PARTS[@]}
|
||||
|
||||
# found SUFFIX
|
||||
if [[ ${#SUBS} -ge 1 ]]; then
|
||||
PARTS[4]=${SUBS[1],,} #lowercase
|
||||
#echo ${PARTS[@]}, ${SUBS[@]}
|
||||
fi
|
||||
}
|
||||
|
||||
## increment REVISION part, don't touch STAGE
|
||||
function increment_revision(){
|
||||
PARTS[3]=$(( PARTS[3] + 1 ))
|
||||
IS_DIRTY=1
|
||||
}
|
||||
|
||||
## increment PATCH part, reset all other lower PARTS, don't touch STAGE
|
||||
function increment_patch(){
|
||||
PARTS[2]=$(( PARTS[2] + 1 ))
|
||||
PARTS[3]=0
|
||||
IS_DIRTY=1
|
||||
}
|
||||
|
||||
## increment MINOR part, reset all other lower PARTS, don't touch STAGE
|
||||
function increment_minor(){
|
||||
PARTS[1]=$(( PARTS[1] + 1 ))
|
||||
PARTS[2]=0
|
||||
PARTS[3]=0
|
||||
IS_DIRTY=1
|
||||
}
|
||||
|
||||
## increment MAJOR part, reset all other lower PARTS, don't touch STAGE
|
||||
function incremet_major(){
|
||||
PARTS[0]="v$(( PARTS[0] + 1 ))"
|
||||
PARTS[1]=0
|
||||
PARTS[2]=0
|
||||
PARTS[3]=0
|
||||
IS_DIRTY=1
|
||||
}
|
||||
|
||||
## increment the number only of last found PART: REVISION --> PATCH --> MINOR. don't touch STAGE
|
||||
function increment_last_found(){
|
||||
if [[ "${#PARTS[3]}" == 0 || "${PARTS[3]}" == "0" ]]; then
|
||||
if [[ "${#PARTS[2]}" == 0 || "${PARTS[2]}" == "0" ]]; then
|
||||
increment_minor
|
||||
else
|
||||
increment_patch
|
||||
fi
|
||||
else
|
||||
increment_revision
|
||||
fi
|
||||
|
||||
# stage part is not EMPTY
|
||||
if [[ "${#PARTS[4]}" != 0 ]]; then
|
||||
IS_SHIFT=1
|
||||
fi
|
||||
}
|
||||
|
||||
## compose version from PARTS
|
||||
function compose(){
|
||||
MAJOR="${PARTS[0]}"
|
||||
MINOR=".${PARTS[1]}"
|
||||
PATCH=".${PARTS[2]}"
|
||||
REVISION=".${PARTS[3]}"
|
||||
SUFFIX="-${PARTS[4]}"
|
||||
|
||||
if [[ "${#PATCH}" == 1 ]]; then # if empty {PATCH}
|
||||
PATCH=""
|
||||
fi
|
||||
|
||||
if [[ "${#REVISION}" == 1 ]]; then # if empty {REVISION}
|
||||
REVISION=""
|
||||
fi
|
||||
|
||||
if [[ "${PARTS[3]}" == "0" ]]; then # if revision is ZERO
|
||||
REVISION=""
|
||||
fi
|
||||
|
||||
# shrink patch and revision
|
||||
if [[ -z "${REVISION// }" ]]; then
|
||||
if [[ "${PARTS[2]}" == "0" ]]; then
|
||||
PATCH=".0"
|
||||
fi
|
||||
else # revision is not EMPTY
|
||||
if [[ "${#PATCH}" == 0 ]]; then
|
||||
PATCH=".0"
|
||||
fi
|
||||
fi
|
||||
|
||||
# remove suffix if we don't have a alpha/beta/rc
|
||||
if [[ "${#SUFFIX}" == 1 ]]; then
|
||||
SUFFIX=""
|
||||
fi
|
||||
|
||||
|
||||
echo "${MAJOR}${MINOR}${PATCH}${REVISION}${SUFFIX}" #full format
|
||||
}
|
||||
|
||||
# initial version used for repository without tags
|
||||
INIT_VERSION=0.0.0.0-alpha
|
||||
|
||||
# do GIT data extracting
|
||||
TAG=$(highest_tag)
|
||||
REVISION=$(latest_revision)
|
||||
BRANCH=$(current_branch)
|
||||
TAG_HASH=$(tag_hash $TAG)
|
||||
HEAD_HASH=$(head_hash)
|
||||
|
||||
# if tag and branch commit hashes are different, than print info about that
|
||||
#echo $HEAD_HASH vs $TAG_HASH
|
||||
if [[ "$@" == "" ]]; then
|
||||
if [[ "$TAG_HASH" == "$HEAD_HASH" ]]; then
|
||||
echo "Tag $TAG and HEAD are aligned. We will stay on the TAG version."
|
||||
echo ""
|
||||
NO_ARGS_VALUE='--stay'
|
||||
else
|
||||
PATTERN="^[0-9]+.[0-9]+(.[0-9]+)*(-(alpha|beta|rc))*$"
|
||||
|
||||
if [[ "$BRANCH" =~ $PATTERN ]]; then
|
||||
echo "Detected version branch '$BRANCH'. We will auto-increment the last version PART."
|
||||
echo ""
|
||||
NO_ARGS_VALUE='--default'
|
||||
else
|
||||
echo "Detected branch name '$BRANCH' than does not match version pattern. We will increase MINOR."
|
||||
echo ""
|
||||
NO_ARGS_VALUE='--minor'
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# {MAJOR}.{MINOR}[.{PATCH}[.{REVISION}][-(.*)]
|
||||
#
|
||||
# Suffix: alpha, beta, rc
|
||||
# No Suffix --> {NEW_VERSION}-alpha
|
||||
# alpha --> beta
|
||||
# beta --> rc
|
||||
# rc --> {VERSION}
|
||||
#
|
||||
PARTS=( ${TAG//./ } )
|
||||
parse_last ${#PARTS[@]} # array size as argument
|
||||
#echo ${PARTS[@]}
|
||||
|
||||
# if no parameters than emulate --default parameter
|
||||
if [[ "$@" == "" ]]; then
|
||||
set -- $NO_ARGS_VALUE
|
||||
fi
|
||||
|
||||
# parse input parameters
|
||||
for i in "$@"
|
||||
do
|
||||
key="$i"
|
||||
|
||||
case $key in
|
||||
-a|--alpha) # switched to ALPHA
|
||||
PARTS[4]="alpha"
|
||||
IS_SHIFT=1
|
||||
;;
|
||||
-b|--beta) # switched to BETA
|
||||
PARTS[4]="beta"
|
||||
IS_SHIFT=1
|
||||
;;
|
||||
-c|--release-candidate) # switched to RC
|
||||
PARTS[4]="rc"
|
||||
IS_SHIFT=1
|
||||
;;
|
||||
-r|--release) # switched to RELEASE
|
||||
PARTS[4]=""
|
||||
IS_SHIFT=1
|
||||
;;
|
||||
-p|--patch) # increment of PATCH
|
||||
increment_patch
|
||||
;;
|
||||
-e|--revision) # increment of REVISION
|
||||
increment_revision
|
||||
;;
|
||||
-g|--git-revision) # use git revision number as a revision part§
|
||||
PARTS[3]=$(( REVISION ))
|
||||
IS_DIRTY=1
|
||||
;;
|
||||
-i|--minor) # increment of MINOR by default
|
||||
increment_minor
|
||||
;;
|
||||
--default) # stay on the same stage, but increment only last found PART of version code
|
||||
increment_last_found
|
||||
;;
|
||||
-m|--major) # increment of MAJOR
|
||||
incremet_major
|
||||
;;
|
||||
-s|--stay) # extract version info
|
||||
IS_DIRTY=1
|
||||
NO_APPLY_MSG=1
|
||||
;;
|
||||
-t|--tag-only) # extract version info
|
||||
TAG_ONLY=1
|
||||
;;
|
||||
--apply)
|
||||
DO_APPLY=1
|
||||
;;
|
||||
-h|--help)
|
||||
help
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# detected shift, but no increment
|
||||
if [[ "$IS_SHIFT" == "1" ]]; then
|
||||
# temporary disable stage shift
|
||||
stage=${PARTS[4]}
|
||||
PARTS[4]=''
|
||||
|
||||
# detect first run on repository, INIT_VERSION was used
|
||||
if [[ "$(compose)" == "0.0" ]]; then
|
||||
increment_minor
|
||||
fi
|
||||
|
||||
PARTS[4]=$stage
|
||||
fi
|
||||
|
||||
# no increment applied yet and no shift of state, do minor increase
|
||||
if [[ "$IS_DIRTY$IS_SHIFT" == "" ]]; then
|
||||
increment_minor
|
||||
fi
|
||||
|
||||
compose
|
||||
|
||||
# is proposed tag in conflict with any other TAG
|
||||
PROPOSED_HASH=$(tag_hash $(compose))
|
||||
if [[ "${#PROPOSED_HASH}" -gt 0 && "$NO_APPLY_MSG" == "" ]]; then
|
||||
echo -e "\033[31mERROR:\033[0m "
|
||||
echo -e "\033[31mERROR:\033[0m Found conflict with existing tag \033[32m$(compose)\033[0m / $PROPOSED_HASH"
|
||||
echo -e "\033[31mERROR:\033[0m Only manual resolving is possible now."
|
||||
echo -e "\033[31mERROR:\033[0m "
|
||||
echo -e "\033[31mERROR:\033[0m To Resolve try to add --revision or --patch modifier."
|
||||
echo -e "\033[31mERROR:\033[0m "
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
.obsidian
|
||||
.now
|
||||
.idea
|
||||
.env
|
||||
node_modules
|
||||
dist
|
||||
build
|
||||
@@ -20,9 +21,13 @@ packages/**/src/aliases/*.ts
|
||||
packages/**/src/aliases.ts
|
||||
!packages/**/src/aliases/index.ts
|
||||
packages/**/src/dynamicIconImports.ts
|
||||
packages/**/DynamicIcon.d.ts
|
||||
packages/**/dynamicIconImports.js
|
||||
packages/**/dynamicIconImports.d.ts
|
||||
packages/**/dynamicIconImports.js.map
|
||||
packages/**/dynamic.d.ts
|
||||
packages/**/dynamic.mjs.map
|
||||
packages/**/dynamic.mjs
|
||||
packages/**/LICENSE
|
||||
categories.json
|
||||
tags.json
|
||||
@@ -39,5 +44,7 @@ docs/.vitepress/data/releaseMetaData
|
||||
docs/.vitepress/data/categoriesData.json
|
||||
docs/.vitepress/data/iconDetails
|
||||
docs/.vitepress/data/relatedIcons.json
|
||||
docs/.vitepress/data/brandStopwords.json
|
||||
docs/.vercel
|
||||
docs/.nitro
|
||||
.gitignore
|
||||
|
||||
11
.vscode/svg.code-snippets
vendored
11
.vscode/svg.code-snippets
vendored
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/Yash-Singh1/vscode-snippets-json-schema/main/schema.json",
|
||||
"Lucide SVG": {
|
||||
"scope": "xml",
|
||||
"description": "Base SVG with Lucide attributes.",
|
||||
@@ -51,6 +52,16 @@
|
||||
],
|
||||
"body": "<circle cx=\"${2:12}\" cy=\"${3:$2}\" r=\"${1|10,2,.5\" fill=\"currentColor|}\" />"
|
||||
},
|
||||
"Squircle": {
|
||||
"scope": "xml",
|
||||
"description": "SVG `path` with Lucide defaults.",
|
||||
"prefix": [
|
||||
"squircle",
|
||||
"path",
|
||||
"<path"
|
||||
],
|
||||
"body": "<path d=\"M12 3c7.2 0 9 1.8 9 9s-1.8 9-9 9-9-1.8-9-9 1.8-9 9-9\" />"
|
||||
},
|
||||
"Ellipse": {
|
||||
"scope": "xml",
|
||||
"description": "SVG `ellipse`.",
|
||||
|
||||
71
BRAND_LOGOS_STATEMENT.md
Normal file
71
BRAND_LOGOS_STATEMENT.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Our Official Stance on Including Brand Logos in Lucide
|
||||
|
||||
## TL;DR
|
||||
|
||||
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**.
|
||||
|
||||
If you need brand logos, we recommend [Simple Icons](https://simpleicons.org/), which provides an extensive, legally safer collection of brand marks.
|
||||
|
||||
---
|
||||
|
||||
## 1. Historical Context
|
||||
|
||||
This is not a new debate — other icon sets have gone through the same discussion:
|
||||
|
||||
- **Material Design Icons** [deprecated all brand icons](https://github.com/Templarian/MaterialDesign/issues/6602) because they didn't fit the style, didn't work well in one color, and often looked out of place in a 24×24px grid.
|
||||
- **Feather Icons** [came to the same conclusion](https://github.com/feathericons/feather/issues/763): brand logos have their own style, and forcing them into another inevitably leads to aesthetic compromises.
|
||||
- **Lucide** learned from these examples — we'd rather focus on making a consistent set of non-brand icons that all work together.
|
||||
|
||||
## 2. Legal Considerations
|
||||
|
||||
Most brand logos:
|
||||
- Are **protected by trademark or copyright**.
|
||||
- Have **strict rules** for how they can be used (colors, spacing, proportions, etc.).
|
||||
- **Don't allow modification** — but we'd have to change them to fit Lucide's style.
|
||||
|
||||
This means adding them could:
|
||||
1. Break copyright or trademark law.
|
||||
2. Make both you and the Lucide project legally responsible.
|
||||
3. Force us to review every new request one by one for legal issues — something we simply can't do.
|
||||
|
||||
> **Note:** Simple Icons avoids this by keeping logos exactly as the brand provides them — though even they sometimes face [legal challenges](https://github.com/simple-icons/simple-icons/issues/11236).
|
||||
|
||||
## 3. Design & Consistency
|
||||
|
||||
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.
|
||||
- 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".
|
||||
|
||||
If the logos are not in Lucide's style, why include them in Lucide at all? Better to use them from a dedicated brand icon source.
|
||||
|
||||
## 4. Maintenance Burden
|
||||
|
||||
Even with our current **"no brand icon requests"** policy, people still request them regularly.
|
||||
|
||||
Having any brand icons in the set:
|
||||
- Makes people think we might add more in the future.
|
||||
- Leads to repeated requests and the same conversations over and over.
|
||||
- Wastes maintainer time redirecting people to the same explanation.
|
||||
|
||||
Removing them entirely solves this problem.
|
||||
|
||||
## 5. Recommended Alternatives
|
||||
|
||||
If you need brand icons, try:
|
||||
- [Simple Icons](https://simpleicons.org/): offers a huge range of brands, in consistent SVG format, using a 24×24 viewBox, the same as ours.
|
||||
- Official brand asset pages: most major companies provide downloadable SVGs.
|
||||
|
||||
You can use these alongside Lucide without bloating our core library.
|
||||
|
||||
## Final Words
|
||||
|
||||
Lucide is an **icon** set, not a **logo** set.
|
||||
|
||||
Logos belong in dedicated logo resources.
|
||||
|
||||
We're focusing on what Lucide does best: providing a clean, cohesive, and legally safe collection of open-source icons.
|
||||
@@ -16,10 +16,10 @@ Guidelines for pull requests:
|
||||
|
||||
- __Make your commit messages as descriptive as possible.__ Include as much information as you can. Explain anything that the file diffs themselves won’t make apparent.
|
||||
- __Document your pull request__. Explain your fix, link to the relevant issue, add screenshots when adding new icons.
|
||||
- __Make sure the target of your pull request is the relevant branch__. Most of bugfix or new feature should go to the `main` branch.
|
||||
- __Include only related work__. If your pull request has unrelated commit, it won't be accepted.
|
||||
- __Make sure the target of your pull request is the relevant branch__. Most of bug fixes or new feature should go to the `main` branch.
|
||||
- __Include only related work__. If your pull request has unrelated commits, it won't be accepted.
|
||||
|
||||
### Pull Requests Including Icons
|
||||
### Icon Pull Requests
|
||||
|
||||
#### Guidelines
|
||||
|
||||
@@ -27,26 +27,30 @@ Please make sure you follow the icon guidelines, that should be followed to keep
|
||||
|
||||
Read it here: [ICON_GUIDELINES](https://lucide.dev/docs/icon-design-guide).
|
||||
|
||||
### Editor guides
|
||||
#### 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.
|
||||
|
||||
#### Editor guides
|
||||
|
||||
Here you can find instructions on how to implement the guidelines with different vector graphics editors:
|
||||
|
||||
#### [Adobe Illustrator Guide](https://lucide.dev/docs/illustrator-guide)
|
||||
##### [Adobe Illustrator Guide](https://lucide.dev/docs/illustrator-guide)
|
||||
|
||||
You can also [download an Adobe Illustrator template](https://github.com/lucide-icons/lucide/blob/main/docs/public/templates/illustrator_template.ai).
|
||||
|
||||
#### [Inkscape Guide](https://lucide.dev/docs/inkscape-guide)
|
||||
##### [Inkscape Guide](https://lucide.dev/docs/inkscape-guide)
|
||||
|
||||
#### [Figma Guide](https://lucide.dev/docs/figma-guide)
|
||||
##### [Figma Guide](https://lucide.dev/docs/figma-guide)
|
||||
|
||||
#### [Affinity Designer Guide](https://lucide.dev/guide/design/affinity-designer-guide)
|
||||
##### [Affinity Designer Guide](https://lucide.dev/guide/design/affinity-designer-guide)
|
||||
|
||||
### Submitting Multiple Icons
|
||||
#### Submitting Multiple Icons
|
||||
|
||||
If you want submit multiple icons, please separate the icons and group them. That makes reviewing the icons easier and keep the thread clean and scoped.
|
||||
So don't submit multiple icons in one PR that have noting to do with each other.
|
||||
If you want to submit multiple icons, please separate the icons and group them. That makes reviewing the icons easier and keeps the thread clean and scoped.
|
||||
So don't submit multiple icons in one PR that have nothing to do with each other.
|
||||
So for example don't create one PR with icons: `arrow-up`, `bicycle`, `arrow-down`.
|
||||
Seperate them by two PRs; 'pr-01' `arrow`, `arrow-down` and 'pr-02' `bicycle`.
|
||||
Separate them into two PRs; 'pr-01' `arrow`, `arrow-down` and 'pr-02' `bicycle`.
|
||||
|
||||
## Icon Requests
|
||||
|
||||
@@ -77,7 +81,7 @@ To distribute different packages we use [PNPM workspaces](https://pnpm.io/worksp
|
||||
The configured directory for workspaces is the [packages](https://github.com/lucide-icons/lucide/tree/main/packages) directory, located in the root directory. There you will find all the current packages from lucide.
|
||||
There are more workspaces defined, see [`pnpm-workspace.yaml`](https://github.com/lucide-icons/lucide/blob/main/pnpm-workspace.yaml).
|
||||
|
||||
> Note: One package is not managed by pnpm: **lucide-flutter**, this package is written in Dart and used pub for publishing.
|
||||
> Note: One package is not managed by pnpm: **lucide-flutter**, this package is written in Dart and uses pub for publishing.
|
||||
|
||||
### Generated Code
|
||||
|
||||
@@ -127,7 +131,7 @@ When adding new features to for example the icon component for a framework. It i
|
||||
|
||||
### Local Testing
|
||||
|
||||
To test changes in a local project, you can use `yarn link`, `npm link`, `bun link` or `pnpm link` to link the package. Before you do this make sure you builded the package first.
|
||||
To test changes in a local project, you can use `yarn link`, `npm link`, `bun link` or `pnpm link` to link the package. Before you do this make sure you've built the package first.
|
||||
|
||||
```sh
|
||||
# in packages/lucide-react
|
||||
@@ -157,6 +161,30 @@ lucide
|
||||
|
||||
The lucide.dev website is using [vitepress](https://vitepress.dev/) to generate the static website. The markdown files are located in the docs directory.
|
||||
|
||||
#### Running the Docs Website Locally
|
||||
|
||||
To test the docs website locally, follow these steps:
|
||||
|
||||
1. **Navigate to the docs directory**
|
||||
|
||||
```sh
|
||||
cd docs
|
||||
```
|
||||
|
||||
2. **Start the Local Development Server**
|
||||
|
||||
```sh
|
||||
pnpm run docs:dev
|
||||
```
|
||||
|
||||
3. **Open the Website Locally**
|
||||
|
||||
Vitepress should open with the following format:
|
||||
|
||||
VitePress dev server is running at:
|
||||
- **Local**: `http://localhost:3000/`
|
||||
- **Network**: `http://192.168.x.x:3000/`
|
||||
|
||||
### Guides
|
||||
|
||||
Detailed documentation about: installation, guides, packages, design guides etc.
|
||||
@@ -165,15 +193,13 @@ Detailed documentation about: installation, guides, packages, design guides etc.
|
||||
|
||||
All the icons of lucide in SVG format. These will be used as source for all the packages and other distributions for the lucide icons.
|
||||
|
||||
### packages
|
||||
### Packages
|
||||
|
||||
Includes all the (npm) packages of lucide.
|
||||
|
||||
> Note: One package is not managed by pnpm: **lucide-flutter**, this package is written in Dart and used pub for publishing.
|
||||
### Scripts
|
||||
|
||||
### scripts
|
||||
|
||||
Includes usefully scripts to automate certain jobs. Big part of the scripts is the template generation, for example it generates icon components for all the packages. These scripts are usually executed from the "scripts" section in the package.json.
|
||||
Includes useful scripts to automate certain jobs. Big part of the scripts is the template generation, for example it generates icon components for all the packages. These scripts are usually executed from the "scripts" section in the package.json.
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -190,4 +216,4 @@ If you need any help or have problems with you contribution. Please don't hesita
|
||||
Thank you to all the people who already contributed to Lucide!
|
||||
|
||||
<a href="https://github.com/lucide-icons/lucide/graphs/contributors">
|
||||
<img src="https://opencollective.com/lucide-icons/contributors.svg?width=890" /></a>
|
||||
<img src="https://opencollective.com/lucide-icons/contributors.svg?width=800" /></a>
|
||||
|
||||
26
LICENSE
26
LICENSE
@@ -1,6 +1,6 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2022.
|
||||
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2023 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2025.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -13,3 +13,27 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
The MIT License (MIT) (for portions derived from Feather)
|
||||
|
||||
Copyright (c) 2013-2023 Cole Bemis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
13
README.md
13
README.md
@@ -40,6 +40,7 @@ Lucide is an open-source icon library that provides 1000+ vector (svg) files for
|
||||
| <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) |
|
||||
|
||||
### Figma
|
||||
@@ -56,6 +57,12 @@ For more info on how to contribute please see the [contribution guidelines](http
|
||||
|
||||
Caught a mistake or want to contribute to the documentation? [Edit this page on Github](https://github.com/lucide-icons/lucide/blob/main/README.md)
|
||||
|
||||
## About brand logos
|
||||
|
||||
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)
|
||||
|
||||
## Community
|
||||
|
||||
Join the community on our [Discord](https://discord.gg/EH6nSts) server!
|
||||
@@ -69,7 +76,9 @@ Lucide is totally free for commercial use and personal use, this software is lic
|
||||
Thank you to all the people who contributed to Lucide!
|
||||
|
||||
<a href="https://github.com/lucide-icons/lucide/graphs/contributors">
|
||||
<img src="https://opencollective.com/lucide-icons/contributors.svg?width=890" /></a>
|
||||
|
||||
<img src="https://opencollective.com/lucide-icons/contributors.svg?width=800" />
|
||||
</a>
|
||||
|
||||
## Sponsors
|
||||
|
||||
@@ -81,5 +90,5 @@ Thank you to all the people who contributed to Lucide!
|
||||
|
||||
### Awesome backers 🍺
|
||||
|
||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="docs/public/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||
<a href="https://github.com/pdfme/pdfme"><img src="docs/public/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||
<a href="https://www.fina.money/"><img src="docs/public/sponsors/fina-money.png" width="180" alt="Fina Money sponsor badge" /></a>
|
||||
|
||||
149
brand-stopwords.json
Normal file
149
brand-stopwords.json
Normal file
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"adobe": "Adobe",
|
||||
"airplay": "AirPlay",
|
||||
"amazon": "Amazon",
|
||||
"angular": "Angular",
|
||||
"aws": "AWS",
|
||||
"azure": "Azure",
|
||||
"bandcamp": "Bandcamp",
|
||||
"behance": "Behance",
|
||||
"bitbucket": "Bitbucket",
|
||||
"blender": "Blender",
|
||||
"bluesky": "BlueSky",
|
||||
"bootstrap": "Bootstrap",
|
||||
"brave": "Brave",
|
||||
"chakra": "Chakra UI",
|
||||
"chrome": "Chrome",
|
||||
"codepen": "Codepen",
|
||||
"codesandbox": "CodeSandbox",
|
||||
"csharp": "C#",
|
||||
"cypress": "Cypress",
|
||||
"dart": "Dart",
|
||||
"deezer": "Deezer",
|
||||
"deno": "Deno",
|
||||
"discord": "Discord",
|
||||
"docker": "Docker",
|
||||
"dribbble": "Dribbble",
|
||||
"dropbox": "Dropbox",
|
||||
"edge": "Edge",
|
||||
"ember": "Ember",
|
||||
"epic": "Epic Games",
|
||||
"erlang": "Erlang",
|
||||
"esbuild": "esbuild",
|
||||
"eslint": "ESLint",
|
||||
"facebook": "Facebook",
|
||||
"figjam": "FigJam",
|
||||
"figma": "Figma",
|
||||
"firebase": "Firebase",
|
||||
"firefox": "Firefox",
|
||||
"framer": "Framer",
|
||||
"gatsby": "Gatsby",
|
||||
"gcp": "Google Cloud",
|
||||
"github": "GitHub",
|
||||
"gitlab": "GitLab",
|
||||
"golang": "GoLang",
|
||||
"google": "Google",
|
||||
"gmail": "Gmail",
|
||||
"gravatar": "Gravatar",
|
||||
"haskell": "Haskell",
|
||||
"instagram": "Instagram",
|
||||
"java": "Java",
|
||||
"javascript": "JavaScript",
|
||||
"jest": "Jest",
|
||||
"jira": "Jira",
|
||||
"kotlin": "Kotlin",
|
||||
"kubernetes": "Kubernetes",
|
||||
"less": "Less",
|
||||
"leetcode": "LeetCode",
|
||||
"leet-code": "LeetCode",
|
||||
"line": "LINE",
|
||||
"linkedin": "LinkedIn",
|
||||
"lua": "Lua",
|
||||
"mariadb": "MariaDB",
|
||||
"mcp": "MCP",
|
||||
"messenger": "Messenger",
|
||||
"microsoft": "Microsoft",
|
||||
"mongodb": "MongoDB",
|
||||
"mui": "Material UI",
|
||||
"mysql": "MySQL",
|
||||
"nestjs": "NestJS",
|
||||
"netflix": "Netflix",
|
||||
"netlify": "Netlify",
|
||||
"next": "Next.js",
|
||||
"nodejs": "Node.js",
|
||||
"notion": "Notion",
|
||||
"nostr": "Nostr",
|
||||
"npm": "npm",
|
||||
"nuxt": "Nuxt",
|
||||
"opera": "Opera",
|
||||
"oracle": "Oracle",
|
||||
"patreon": "Patreon",
|
||||
"paypal": "PayPal",
|
||||
"perl": "Perl",
|
||||
"php": "PHP",
|
||||
"pinterest": "Pinterest",
|
||||
"pix": "PiX",
|
||||
"playstation": "PlayStation",
|
||||
"playwright": "Playwright",
|
||||
"pnpm": "pnpm",
|
||||
"postcss": "PostCSS",
|
||||
"postgresql": "PostgreSQL",
|
||||
"prettier": "Prettier",
|
||||
"prisma": "Prisma",
|
||||
"python": "Python",
|
||||
"qwik": "Qwik",
|
||||
"react": "React",
|
||||
"reddit": "Reddit",
|
||||
"redis": "Redis",
|
||||
"rollup": "Rollup",
|
||||
"rust": "Rust",
|
||||
"safari": "Safari",
|
||||
"sass": "Sass",
|
||||
"scala": "Scala",
|
||||
"scss": "Sass",
|
||||
"semantic": "Semantic UI",
|
||||
"shopify": "Shopify",
|
||||
"skype": "Skype",
|
||||
"slack": "Slack",
|
||||
"solid": "SolidJS",
|
||||
"soundcloud": "SoundCloud",
|
||||
"spotify": "Spotify",
|
||||
"sqlite": "SQLite",
|
||||
"squarespace": "Squarespace",
|
||||
"steam": "Steam",
|
||||
"stripe": "Stripe",
|
||||
"substack": "Substack",
|
||||
"supabase": "Supabase",
|
||||
"surge": "Surge",
|
||||
"svelte": "Svelte",
|
||||
"swift": "Swift",
|
||||
"tailwind": "Tailwind CSS",
|
||||
"telegram": "Telegram",
|
||||
"terraform": "Terraform",
|
||||
"tesla": "Tesla",
|
||||
"tidal": "Tidal",
|
||||
"tiktok": "TikTok",
|
||||
"trello": "Trello",
|
||||
"twitch": "Twitch",
|
||||
"twitter": "Twitter",
|
||||
"typescript": "TypeScript",
|
||||
"unity": "Unity",
|
||||
"unreal": "Unreal Engine",
|
||||
"vercel": "Vercel",
|
||||
"vimeo": "Vimeo",
|
||||
"vite": "Vite",
|
||||
"vitest": "Vitest",
|
||||
"vue": "Vue",
|
||||
"webpack": "Webpack",
|
||||
"wechat": "WeChat",
|
||||
"whatsapp": "WhatsApp",
|
||||
"windows": "Windows",
|
||||
"wix": "Wix",
|
||||
"x.com": "X.com",
|
||||
"x-social": "X.com",
|
||||
"xbox": "Xbox",
|
||||
"yarn": "Yarn",
|
||||
"youtube": "YouTube",
|
||||
"zig": "Zig",
|
||||
"zoom": "Zoom"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "../category.schema.json",
|
||||
"title": "Navigation",
|
||||
"title": "Navigation, Maps, and POIs",
|
||||
"icon": "compass"
|
||||
}
|
||||
|
||||
@@ -13,10 +13,12 @@ export default eventHandler((event) => {
|
||||
const data = pathData.at(-1).slice(0, -4);
|
||||
const [name] = pathData;
|
||||
|
||||
const src = Buffer.from(data, 'base64')
|
||||
.toString('utf8')
|
||||
.replaceAll('\n', '')
|
||||
.replace(/<svg[^>]*>|<\/svg>/g, '');
|
||||
const src = Buffer.from(data, 'base64').toString('utf8').replaceAll('\n', '');
|
||||
|
||||
const width = parseInt((src.includes('<svg ') ? src.match(/width="(\d+)"/)?.[1] : null) ?? '24');
|
||||
const height = parseInt(
|
||||
(src.includes('<svg ') ? src.match(/height="(\d+)"/)?.[1] : null) ?? '24',
|
||||
);
|
||||
|
||||
const children = [];
|
||||
|
||||
@@ -38,7 +40,7 @@ export default eventHandler((event) => {
|
||||
children.push(
|
||||
createElement(Backdrop, {
|
||||
backdropString,
|
||||
src,
|
||||
src: src.replace(/<svg[^>]*>|<\/svg>/g, ''),
|
||||
color: '#777',
|
||||
}),
|
||||
);
|
||||
@@ -46,7 +48,18 @@ export default eventHandler((event) => {
|
||||
|
||||
const svg = Buffer.from(
|
||||
// We can't use jsx here, is not supported here by nitro.
|
||||
renderToString(createElement(SvgPreview, { src, showGrid: true }, children)),
|
||||
renderToString(
|
||||
createElement(
|
||||
SvgPreview,
|
||||
{
|
||||
src: src.replace(/<svg[^>]*>|<\/svg>/g, ''),
|
||||
height,
|
||||
width,
|
||||
showGrid: true,
|
||||
},
|
||||
children,
|
||||
),
|
||||
),
|
||||
).toString('utf8');
|
||||
|
||||
defaultContentType(event, 'image/svg+xml');
|
||||
|
||||
@@ -17,6 +17,13 @@ export default eventHandler((event) => {
|
||||
.replaceAll('\n', '')
|
||||
.replace(/<svg[^>]*>|<\/svg>/g, '');
|
||||
|
||||
const width = parseInt(
|
||||
(newSrc.includes('<svg ') ? newSrc.match(/width="(\d+)"/)?.[1] : null) ?? '24',
|
||||
);
|
||||
const height = parseInt(
|
||||
(newSrc.includes('<svg ') ? newSrc.match(/height="(\d+)"/)?.[1] : null) ?? '24',
|
||||
);
|
||||
|
||||
const children = [];
|
||||
|
||||
const oldSrc = iconNodes[name]
|
||||
@@ -27,7 +34,9 @@ export default eventHandler((event) => {
|
||||
|
||||
const svg = Buffer.from(
|
||||
// We can't use jsx here, is not supported here by nitro.
|
||||
renderToString(createElement(Diff, { oldSrc, newSrc, showGrid: true }, children)),
|
||||
renderToString(
|
||||
createElement(Diff, { oldSrc, newSrc, showGrid: true, height, width }, children),
|
||||
),
|
||||
).toString('utf8');
|
||||
|
||||
defaultContentType(event, 'image/svg+xml');
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
|
||||
import { Resvg, initWasm } from '@resvg/resvg-wasm';
|
||||
import iconNodes from '../../../data/iconNodes';
|
||||
import wasm from './loadWasm';
|
||||
import { createElement } from 'react';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import createLucideIcon from 'lucide-react/src/createLucideIcon';
|
||||
|
||||
var initializedResvg = initWasm(wasm);
|
||||
|
||||
@@ -9,27 +13,37 @@ export default eventHandler(async (event) => {
|
||||
await initializedResvg;
|
||||
|
||||
const imageSize = 96;
|
||||
const [iconSizeString, svgData] = params.data.split('/');
|
||||
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 data = svgData.slice(0, -4);
|
||||
|
||||
const prevSvg = iconNodes[name]
|
||||
? renderToStaticMarkup(createElement(createLucideIcon(name, iconNodes[name])))
|
||||
: undefined;
|
||||
|
||||
const src = Buffer.from(data, 'base64').toString('utf8');
|
||||
const svg = (src.includes('<svg') ? src : `<svg>${src}</svg>`)
|
||||
.replace(/(\r\n|\n|\r)/gm, '')
|
||||
.replace(
|
||||
/<svg[^>]*/,
|
||||
/<svg[^>]*>/,
|
||||
`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="${iconSize}"
|
||||
height="${iconSize}"
|
||||
viewBox="0 0 24 24"
|
||||
height="${prevSvg ? iconSize * 2 : iconSize}"
|
||||
viewBox="0 0 24 ${prevSvg ? 48 : 24}"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
${prevSvg?.replaceAll('\n', '').replace(/<svg[^>]*>|<\/svg>/g, '')}
|
||||
<g transform="translate(0, ${prevSvg ? 24 : 0})">
|
||||
`,
|
||||
);
|
||||
)
|
||||
.replace(/<\/svg>/, '</g></svg>');
|
||||
|
||||
const resvg = new Resvg(svg, { background: '#000' });
|
||||
const pngData = resvg.render();
|
||||
@@ -39,7 +53,7 @@ export default eventHandler(async (event) => {
|
||||
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
|
||||
|
||||
return `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="${imageSize}" height="${imageSize}" viewBox="0 0 ${imageSize} ${imageSize}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="${imageSize}" height="${prevSvg ? imageSize * 2 : imageSize}" viewBox="0 0 ${imageSize} ${prevSvg ? imageSize * 2 : imageSize}">
|
||||
<style>
|
||||
@media screen and (prefers-color-scheme: light) {
|
||||
#fallback-background { fill: transparent; }
|
||||
@@ -52,20 +66,20 @@ export default eventHandler(async (event) => {
|
||||
<mask id="mask">
|
||||
<image
|
||||
width="${imageSize}"
|
||||
height="${imageSize}"
|
||||
height="${prevSvg ? imageSize * 2 : imageSize}"
|
||||
href="data:image/png;base64,${pngBuffer.toString('base64')}"
|
||||
image-rendering="pixelated"
|
||||
/>
|
||||
</mask>
|
||||
<rect
|
||||
id="fallback-background"
|
||||
width="${imageSize}"
|
||||
height="${imageSize}" ry="${imageSize / 24}"
|
||||
width="100%"
|
||||
height="100%" ry="${imageSize / 24}"
|
||||
fill="#fff"
|
||||
/>
|
||||
<rect
|
||||
width="${imageSize}"
|
||||
height="${imageSize}"
|
||||
width="100%"
|
||||
height="100%"
|
||||
fill="#000"
|
||||
mask="url(#mask)"
|
||||
/>
|
||||
|
||||
@@ -9,6 +9,7 @@ if (process.env.NODE_ENV === 'development') {
|
||||
|
||||
wasm = fs.readFileSync(require.resolve('@resvg/resvg-wasm/index_bg.wasm'));
|
||||
} else {
|
||||
// @ts-ignore
|
||||
wasm = resvg_wasm;
|
||||
}
|
||||
|
||||
|
||||
55
docs/.vitepress/api/gh-icon/symmetry/[...data].get.ts
Normal file
55
docs/.vitepress/api/gh-icon/symmetry/[...data].get.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { createElement } from 'react';
|
||||
import Diff from '../../../lib/SvgPreview/Diff.tsx';
|
||||
|
||||
export default eventHandler((event) => {
|
||||
const { params } = event.context;
|
||||
|
||||
const pathData = params.data.split('/');
|
||||
const data = pathData.at(-1).slice(0, -4);
|
||||
const [operation] = pathData;
|
||||
|
||||
const newSrc = Buffer.from(data, 'base64')
|
||||
.toString('utf8')
|
||||
.replaceAll('\n', '')
|
||||
.replace(/<svg[^>]*>|<\/svg>/g, '');
|
||||
|
||||
const width = parseInt(
|
||||
(newSrc.includes('<svg ') ? newSrc.match(/width="(\d+)"/)?.[1] : null) ?? '24',
|
||||
);
|
||||
const height = parseInt(
|
||||
(newSrc.includes('<svg ') ? newSrc.match(/height="(\d+)"/)?.[1] : null) ?? '24',
|
||||
);
|
||||
|
||||
const children = [];
|
||||
|
||||
let oldSrc = '';
|
||||
if (operation.startsWith('rotate-')) {
|
||||
const degrees = parseInt(operation.replace('rotate-', ''));
|
||||
if (isNaN(degrees)) return '';
|
||||
oldSrc = `<g transform="rotate(${degrees} ${width / 2} ${height / 2})">${newSrc}</g>`;
|
||||
} else if (operation === 'flip-horizontal') {
|
||||
oldSrc = `<g transform="scale(1, -1) translate(0, -${height})">${newSrc}</g>`;
|
||||
} else if (operation === 'flip-vertical') {
|
||||
oldSrc = `<g transform="scale(-1, 1) translate(-${width}, 0)">${newSrc}</g>`;
|
||||
} else if (operation === 'flip-backslash') {
|
||||
oldSrc = `<g transform="rotate(90, ${width / 2}, ${height / 2}) scale(1, -1) translate(0, -${height})">${newSrc}</g>`;
|
||||
} else if (operation === 'flip-slash') {
|
||||
oldSrc = `<g transform="rotate(90, ${width / 2}, ${height / 2}) scale(-1, 1) translate(-${width}, 0)">${newSrc}</g>`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
const svg = Buffer.from(
|
||||
// We can't use jsx here, is not supported here by nitro.
|
||||
renderToString(
|
||||
createElement(Diff, { oldSrc, newSrc, showGrid: true, height, width }, children),
|
||||
),
|
||||
).toString('utf8');
|
||||
|
||||
defaultContentType(event, 'image/svg+xml');
|
||||
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
|
||||
|
||||
return svg;
|
||||
});
|
||||
@@ -36,6 +36,13 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
head: [
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'preconnect',
|
||||
href: 'https://analytics.lucide.dev',
|
||||
},
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
},
|
||||
{
|
||||
"name": "navigation",
|
||||
"title": "Navigation"
|
||||
"title": "Navigation, Maps, and POIs"
|
||||
},
|
||||
{
|
||||
"name": "notifications",
|
||||
|
||||
@@ -7,6 +7,14 @@
|
||||
"dark": "/company-logos/vercel-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "MDN Web Docs",
|
||||
"url": "https://developer.mozilla.org/",
|
||||
"image": {
|
||||
"light": "/company-logos/mdn-light.svg",
|
||||
"dark": "/company-logos/mdn-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Supabase",
|
||||
"url": "https://supabase.com",
|
||||
@@ -23,6 +31,14 @@
|
||||
"dark": "/company-logos/obsidian-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Nuxt",
|
||||
"url": "https://nuxt.com/",
|
||||
"image": {
|
||||
"light": "/company-logos/nuxt-light.svg",
|
||||
"dark": "/company-logos/nuxt-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Open Collective",
|
||||
"url": "https://opencollective.com",
|
||||
|
||||
@@ -127,8 +127,27 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-static": {
|
||||
"@lucide/astro": {
|
||||
"docsAlias": "lucide-astro",
|
||||
"packageDirname": "astro",
|
||||
"order": 8,
|
||||
"icon": "astro",
|
||||
"iconDark": "astro-dark",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/v/@lucide/astro",
|
||||
"href": "https://www.npmjs.com/package/@lucide/astro"
|
||||
},
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/dw/@lucide/astro",
|
||||
"href": "https://www.npmjs.com/package/@lucide/astro"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-static": {
|
||||
"order": 9,
|
||||
"icon": "svg",
|
||||
"shields": [
|
||||
{
|
||||
|
||||
@@ -77,9 +77,9 @@
|
||||
"source": "https://github.com/swisnl/nuxt-lucide-icons",
|
||||
"documentation": "https://github.com/swisnl/nuxt-lucide-icons/blob/main/README.md"
|
||||
},
|
||||
{
|
||||
"name": "lucide-lustre",
|
||||
"description": "A library providing https://lucide.dev icons to lustre",
|
||||
{
|
||||
"name": "lucide_lustre",
|
||||
"description": "A library providing https://lucide.dev icons to lustre.",
|
||||
"icon": "/framework-logos/lustre.webp",
|
||||
"shields": [
|
||||
{
|
||||
@@ -95,5 +95,81 @@
|
||||
],
|
||||
"source": "https://github.com/dinkelspiel/lucide_lustre",
|
||||
"documentation": "https://github.com/dinkelspiel/lucide_lustre/blob/master/README.md"
|
||||
},
|
||||
{
|
||||
"name": "lucide_icons_flutter",
|
||||
"description": "A library providing https://lucide.dev icons to Flutter.",
|
||||
"icon": "/framework-logos/flutter.svg",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "Latest Stable Version",
|
||||
"src": "https://img.shields.io/pub/v/lucide_icons_flutter",
|
||||
"href": "https://pub.dev/packages/lucide_icons_flutter"
|
||||
},
|
||||
{
|
||||
"alt": "Total Downloads",
|
||||
"src": "https://img.shields.io/pub/dm/lucide_icons_flutter",
|
||||
"href": "https://pub.dev/packages/lucide_icons_flutter"
|
||||
}
|
||||
],
|
||||
"source": "https://github.com/vqh2602/lucide-flutter-main",
|
||||
"documentation": "https://pub.dev/documentation/lucide_icons_flutter/latest/"
|
||||
},
|
||||
{
|
||||
"name": "lucide-slint",
|
||||
"description": "Implementation of the lucide icon library for Slint.",
|
||||
"icon": "/framework-logos/slint.svg",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "Latest Stable Version",
|
||||
"src": "https://img.shields.io/crates/v/lucide-slint",
|
||||
"href": "https://crates.io/crates/lucide-slint"
|
||||
},
|
||||
{
|
||||
"alt": "Total Downloads",
|
||||
"src": "https://img.shields.io/crates/d/lucide-slint",
|
||||
"href": "https://crates.io/crates/lucide-slint"
|
||||
}
|
||||
],
|
||||
"source": "https://github.com/cnlancehu/lucide-slint",
|
||||
"documentation": "https://github.com/cnlancehu/lucide-slint/blob/main/README.md"
|
||||
},
|
||||
{
|
||||
"name": "lucide-go",
|
||||
"description": "Implementation of Lucide icons for Go's html/template package.",
|
||||
"icon": "/framework-logos/go.svg",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "Latest Stable Version",
|
||||
"src": "https://img.shields.io/github/v/release/kaugesaar/lucide-go",
|
||||
"href": "https://github.com/kaugesaar/lucide-go/releases"
|
||||
},
|
||||
{
|
||||
"alt": "Go Reference",
|
||||
"src": "https://pkg.go.dev/badge/github.com/kaugesaar/lucide-go.svg",
|
||||
"href": "https://pkg.go.dev/github.com/kaugesaar/lucide-go"
|
||||
}
|
||||
],
|
||||
"source": "https://github.com/kaugesaar/lucide-go",
|
||||
"documentation": "https://github.com/kaugesaar/lucide-go/blob/master/README.md"
|
||||
},
|
||||
{
|
||||
"name": "lucide-rails",
|
||||
"description": "Ruby on Rails views helper method for rendering Lucide icons.",
|
||||
"icon": "/framework-logos/rails.svg",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "Latest Stable Version",
|
||||
"src": "https://img.shields.io/gem/v/lucide-rails",
|
||||
"href": "https://rubygems.org/gems/lucide-rails"
|
||||
},
|
||||
{
|
||||
"alt": "Total Downloads",
|
||||
"src": "https://img.shields.io/gem/dt/lucide-rails",
|
||||
"href": "https://rubygems.org/gems/lucide-rails"
|
||||
}
|
||||
],
|
||||
"source": "https://github.com/heyvito/lucide-rails",
|
||||
"documentation": "https://github.com/heyvito/lucide-rails/blob/master/README.md"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,8 +53,8 @@ const Backdrop = ({
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
width="100%"
|
||||
height="100%"
|
||||
fill="#fff"
|
||||
stroke="none"
|
||||
/>
|
||||
@@ -67,8 +67,8 @@ const Backdrop = ({
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
width="100%"
|
||||
height="100%"
|
||||
opacity={0.5}
|
||||
fill={`url(#pattern-${id})`}
|
||||
stroke="none"
|
||||
|
||||
@@ -7,15 +7,17 @@ const SvgPreview = React.forwardRef<
|
||||
{
|
||||
oldSrc: string;
|
||||
newSrc: string;
|
||||
height: number;
|
||||
width: number;
|
||||
} & React.SVGProps<SVGSVGElement>
|
||||
>(({ oldSrc, newSrc, children, ...props }, ref) => {
|
||||
>(({ oldSrc, newSrc, children, height, width, ...props }, ref) => {
|
||||
return (
|
||||
<svg
|
||||
ref={ref}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={24}
|
||||
height={24}
|
||||
viewBox="0 0 24 24"
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox={`0 0 ${width} ${height}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
@@ -25,6 +27,8 @@ const SvgPreview = React.forwardRef<
|
||||
>
|
||||
<style>{darkModeCss}</style>
|
||||
<Grid
|
||||
width={width}
|
||||
height={height}
|
||||
strokeWidth={0.1}
|
||||
stroke="#777"
|
||||
strokeOpacity={0.3}
|
||||
@@ -37,8 +41,8 @@ const SvgPreview = React.forwardRef<
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
width="100%"
|
||||
height="100%"
|
||||
fill="#000"
|
||||
stroke="none"
|
||||
/>
|
||||
|
||||
137
docs/.vitepress/lib/SvgPreview/GapViolationHighlight.tsx
Normal file
137
docs/.vitepress/lib/SvgPreview/GapViolationHighlight.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import React from 'react';
|
||||
import pathToPoints from './path-to-points';
|
||||
import { Path, PathProps } from './types';
|
||||
|
||||
export const GapViolationHighlight = ({
|
||||
radius,
|
||||
stroke,
|
||||
strokeWidth,
|
||||
strokeOpacity,
|
||||
paths,
|
||||
...props
|
||||
}: {
|
||||
paths: Path[];
|
||||
} & PathProps<'stroke' | 'strokeOpacity' | 'strokeWidth', 'd'>) => {
|
||||
const id = React.useId();
|
||||
|
||||
const groupedPaths = Object.entries(
|
||||
paths.reduce(
|
||||
(groups, val) => {
|
||||
const key = val.c.id;
|
||||
groups[key] = [...(groups[key] || []), val];
|
||||
return groups;
|
||||
},
|
||||
{} as Record<number, Path[]>,
|
||||
),
|
||||
);
|
||||
|
||||
const groups: Group[] = [];
|
||||
|
||||
for (const [, paths] of groupedPaths) {
|
||||
const d = paths.map((path) => path.d).join(' ');
|
||||
const points = paths.flatMap((path) => pathToPoints(path));
|
||||
groups.push({ id: d, points });
|
||||
}
|
||||
|
||||
const mergedGroups = mergeGroups(groups, 2);
|
||||
|
||||
return (
|
||||
<g {...props}>
|
||||
<defs xmlns="http://www.w3.org/2000/svg">
|
||||
<pattern
|
||||
id={`backdrop-pattern-${id}`}
|
||||
width=".1"
|
||||
height=".1"
|
||||
patternUnits="userSpaceOnUse"
|
||||
patternTransform="rotate(45 50 50)"
|
||||
>
|
||||
<line
|
||||
stroke={stroke}
|
||||
strokeWidth={0.1}
|
||||
y2={1}
|
||||
/>
|
||||
<line
|
||||
stroke={stroke}
|
||||
strokeWidth={0.1}
|
||||
y2={1}
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
{mergedGroups.flatMap((ds, idx, arr) =>
|
||||
arr.slice(0, idx).map((val, i) => (
|
||||
<g
|
||||
strokeWidth={strokeWidth}
|
||||
key={i}
|
||||
>
|
||||
<mask
|
||||
id={`svg-preview-backdrop-mask-${id}-${i}`}
|
||||
maskUnits="userSpaceOnUse"
|
||||
>
|
||||
<path
|
||||
stroke="white"
|
||||
d={val.join(' ')}
|
||||
/>
|
||||
</mask>
|
||||
<path
|
||||
d={ds.join(' ')}
|
||||
stroke={`url(#backdrop-pattern-${id})`}
|
||||
strokeWidth={strokeWidth}
|
||||
strokeOpacity={strokeOpacity}
|
||||
mask={`url(#svg-preview-backdrop-mask-${id}-${i})`}
|
||||
/>
|
||||
</g>
|
||||
)),
|
||||
)}
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
type Point = { x: number; y: number };
|
||||
type Group = { id: string; points: Point[] };
|
||||
|
||||
// Euclidean distance
|
||||
function distance(a: Point, b: Point): number {
|
||||
return Math.hypot(a.x - b.x, a.y - b.y);
|
||||
}
|
||||
|
||||
// Check if two groups should be merged based on minimum distance
|
||||
function shouldMerge(a: Group, b: Group, minDistance: number): boolean {
|
||||
for (const pa of a.points) {
|
||||
for (const pb of b.points) {
|
||||
if (distance(pa, pb) <= minDistance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Merge groups and return arrays of merged group IDs
|
||||
function mergeGroups(groups: Group[], minDistance: number): string[][] {
|
||||
const mergedGroups: Group[][] = groups.map((g) => [g]);
|
||||
|
||||
let changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
|
||||
outer: for (let i = 0; i < mergedGroups.length; i++) {
|
||||
for (let j = i + 1; j < mergedGroups.length; j++) {
|
||||
// Check if any group in mergedGroups[i] should merge with any in mergedGroups[j]
|
||||
if (
|
||||
mergedGroups[i].some((ga) =>
|
||||
mergedGroups[j].some((gb) => shouldMerge(ga, gb, minDistance)),
|
||||
)
|
||||
) {
|
||||
// Merge group j into group i
|
||||
mergedGroups[i] = [...mergedGroups[i], ...mergedGroups[j]];
|
||||
mergedGroups.splice(j, 1);
|
||||
changed = true;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return only arrays of IDs
|
||||
return mergedGroups.map((groupList) => groupList.map((g) => g.id));
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { PathProps, Path } from './types';
|
||||
import { getPaths, assert } from './utils';
|
||||
import getPaths, { assert } from './utils';
|
||||
import { GapViolationHighlight } from './GapViolationHighlight.tsx';
|
||||
|
||||
export const darkModeCss = `
|
||||
@media screen and (prefers-color-scheme: light) {
|
||||
@@ -20,10 +21,16 @@ export const darkModeCss = `
|
||||
|
||||
export const Grid = ({
|
||||
radius,
|
||||
fill = '#fff',
|
||||
fill,
|
||||
height,
|
||||
width,
|
||||
subGridSize = 0,
|
||||
...props
|
||||
}: {
|
||||
height: number;
|
||||
width: number;
|
||||
strokeWidth: number;
|
||||
subGridSize?: number;
|
||||
radius: number;
|
||||
} & PathProps<'stroke', 'strokeWidth'>) => (
|
||||
<g
|
||||
@@ -33,43 +40,53 @@ export const Grid = ({
|
||||
>
|
||||
<rect
|
||||
className="svg-preview-grid-rect"
|
||||
width={24 - props.strokeWidth}
|
||||
height={24 - props.strokeWidth}
|
||||
width={width - props.strokeWidth}
|
||||
height={height - props.strokeWidth}
|
||||
x={props.strokeWidth / 2}
|
||||
y={props.strokeWidth / 2}
|
||||
rx={radius}
|
||||
fill={fill}
|
||||
/>
|
||||
<path
|
||||
strokeDasharray={'0 0.1 ' + '0.1 0.15 '.repeat(11) + '0 0.15'}
|
||||
strokeDasharray={
|
||||
'0 0.1 ' + '0.1 0.15 '.repeat(subGridSize ? subGridSize * 4 - 1 : 95) + '0 0.15'
|
||||
}
|
||||
strokeWidth={0.1}
|
||||
d={
|
||||
props.d ||
|
||||
new Array(Math.floor(24 - 1))
|
||||
.fill(null)
|
||||
.map((_, i) => i)
|
||||
.filter((i) => i % 3 !== 2)
|
||||
.flatMap((i) => [
|
||||
`M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`,
|
||||
`M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`,
|
||||
])
|
||||
.join('')
|
||||
}
|
||||
/>
|
||||
<path
|
||||
d={
|
||||
props.d ||
|
||||
new Array(Math.floor(24 - 1))
|
||||
.fill(null)
|
||||
.map((_, i) => i)
|
||||
.filter((i) => i % 3 === 2)
|
||||
.flatMap((i) => [
|
||||
`M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`,
|
||||
`M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`,
|
||||
])
|
||||
.join('')
|
||||
[
|
||||
...new Array(Math.floor(width - 1))
|
||||
.fill(null)
|
||||
.map((_, i) => i)
|
||||
.filter((i) => !subGridSize || i % subGridSize !== subGridSize - 1)
|
||||
.flatMap((i) => [`M${i + 1} ${props.strokeWidth}v${height - props.strokeWidth * 2}`]),
|
||||
...new Array(Math.floor(height - 1))
|
||||
.fill(null)
|
||||
.map((_, i) => i)
|
||||
.filter((i) => !subGridSize || i % subGridSize !== subGridSize - 1)
|
||||
.flatMap((i) => [`M${props.strokeWidth} ${i + 1}h${width - props.strokeWidth * 2}`]),
|
||||
].join('')
|
||||
}
|
||||
/>
|
||||
{!!subGridSize && (
|
||||
<path
|
||||
d={
|
||||
props.d ||
|
||||
[
|
||||
...new Array(Math.floor(width - 1))
|
||||
.fill(null)
|
||||
.map((_, i) => i)
|
||||
.filter((i) => i % subGridSize === subGridSize - 1)
|
||||
.flatMap((i) => [`M${i + 1} ${props.strokeWidth}v${height - props.strokeWidth * 2}`]),
|
||||
...new Array(Math.floor(height - 1))
|
||||
.fill(null)
|
||||
.map((_, i) => i)
|
||||
.filter((i) => i % subGridSize === subGridSize - 1)
|
||||
.flatMap((i) => [`M${props.strokeWidth} ${i + 1}h${width - props.strokeWidth * 2}`]),
|
||||
].join('')
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
);
|
||||
|
||||
@@ -99,6 +116,7 @@ const Shadow = ({
|
||||
>
|
||||
{groupedPaths.map(([id, paths]) => (
|
||||
<mask
|
||||
key={`svg-preview-shadow-mask-${id}`}
|
||||
id={`svg-preview-shadow-mask-${id}`}
|
||||
maskUnits="userSpaceOnUse"
|
||||
strokeOpacity="1"
|
||||
@@ -108,8 +126,8 @@ const Shadow = ({
|
||||
<rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={24}
|
||||
height={24}
|
||||
width="100%"
|
||||
height="100%"
|
||||
fill="#fff"
|
||||
stroke="none"
|
||||
rx={radius}
|
||||
@@ -152,30 +170,34 @@ const ColoredPath = ({
|
||||
colors,
|
||||
paths,
|
||||
...props
|
||||
}: { paths: Path[]; colors: string[] } & PathProps<never, 'd' | 'stroke'>) => (
|
||||
<g
|
||||
className="svg-preview-colored-path-group"
|
||||
{...props}
|
||||
>
|
||||
{paths.map(({ d, c }, i) => (
|
||||
<path
|
||||
key={i}
|
||||
d={d}
|
||||
stroke={colors[(c.name === 'path' ? i : c.id) % colors.length]}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
);
|
||||
}: { paths: Path[]; colors: string[] } & PathProps<never, 'd' | 'stroke'>) => {
|
||||
let idx = 0;
|
||||
return (
|
||||
<g
|
||||
className="svg-preview-colored-path-group"
|
||||
{...props}
|
||||
>
|
||||
{paths.map(({ d, c }, i) => (
|
||||
<path
|
||||
key={i}
|
||||
d={d}
|
||||
stroke={colors[(c.name === 'path' ? idx++ : c.id) % colors.length]}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
const ControlPath = ({
|
||||
paths,
|
||||
radius,
|
||||
pointSize,
|
||||
...props
|
||||
}: { pointSize: number; paths: Path[]; radius: number } & PathProps<
|
||||
'stroke' | 'strokeWidth',
|
||||
'd'
|
||||
>) => {
|
||||
}: {
|
||||
pointSize: number;
|
||||
paths: Path[];
|
||||
radius: number;
|
||||
} & PathProps<'stroke' | 'strokeWidth', 'd'>) => {
|
||||
const controlPaths = paths.map((path, i) => {
|
||||
const element = paths.filter((p) => p.c.id === path.c.id);
|
||||
const lastElement = element.at(-1)?.next;
|
||||
@@ -207,8 +229,8 @@ const ControlPath = ({
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
width="100%"
|
||||
height="100%"
|
||||
fill="#fff"
|
||||
stroke="none"
|
||||
rx={radius}
|
||||
@@ -243,7 +265,7 @@ const ControlPath = ({
|
||||
)
|
||||
.join('')}
|
||||
/>
|
||||
{controlPaths.map(({ d, prev, next, startMarker, endMarker }, i) => (
|
||||
{controlPaths.map(({ prev, next, startMarker, endMarker }, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{startMarker && (
|
||||
<circle
|
||||
@@ -279,11 +301,37 @@ const Radii = ({
|
||||
{...props}
|
||||
>
|
||||
{paths.map(
|
||||
({ c, prev, next, circle }, i) =>
|
||||
({ circle, next, prev, c }, i) =>
|
||||
circle && (
|
||||
<React.Fragment key={i}>
|
||||
{c.name !== 'circle' && (
|
||||
<path d={`M${prev.x} ${prev.y} ${circle.x} ${circle.y} ${next.x} ${next.y}`} />
|
||||
{circle.tangentIntersection && c.name === 'path' && (
|
||||
<>
|
||||
<circle
|
||||
cx={next.x * 2 - circle.tangentIntersection.x}
|
||||
cy={next.y * 2 - circle.tangentIntersection.y}
|
||||
r={0.25}
|
||||
/>
|
||||
<circle
|
||||
cx={prev.x * 2 - circle.tangentIntersection.x}
|
||||
cy={prev.y * 2 - circle.tangentIntersection.y}
|
||||
r={0.25}
|
||||
/>
|
||||
<path
|
||||
d={`M${next.x * 2 - circle.tangentIntersection.x} ${
|
||||
next.y * 2 - circle.tangentIntersection.y
|
||||
}L${circle.tangentIntersection.x} ${circle.tangentIntersection.y}L${prev.x * 2 - circle.tangentIntersection.x} ${
|
||||
prev.y * 2 - circle.tangentIntersection.y
|
||||
}`}
|
||||
/>
|
||||
<circle
|
||||
cx={circle.tangentIntersection.x}
|
||||
cy={circle.tangentIntersection.y}
|
||||
r={0.25}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{c.name === 'path' && (
|
||||
<path d={`M${next.x} ${next.y}L${circle.x} ${circle.y}L${prev.x} ${prev.y}`} />
|
||||
)}
|
||||
<circle
|
||||
cy={circle.y}
|
||||
@@ -313,55 +361,60 @@ const Radii = ({
|
||||
const Handles = ({
|
||||
paths,
|
||||
...props
|
||||
}: { paths: Path[] } & PathProps<
|
||||
'strokeWidth' | 'stroke' | 'strokeDasharray' | 'strokeOpacity',
|
||||
any
|
||||
>) => {
|
||||
return (
|
||||
<g
|
||||
className="svg-preview-handles-group"
|
||||
{...props}
|
||||
>
|
||||
{paths.map(({ c, prev, next, cp1, cp2 }) => (
|
||||
<>
|
||||
{cp1 && <path d={`M${prev.x} ${prev.y} ${cp1.x} ${cp1.y}`} />}
|
||||
{cp1 && (
|
||||
<circle
|
||||
cy={cp1.y}
|
||||
cx={cp1.x}
|
||||
r={0.25}
|
||||
/>
|
||||
)}
|
||||
{cp2 && <path d={`M${next.x} ${next.y} ${cp2.x} ${cp2.y}`} />}
|
||||
{cp2 && (
|
||||
<circle
|
||||
cy={cp2.y}
|
||||
cx={cp2.x}
|
||||
r={0.25}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</g>
|
||||
);
|
||||
};
|
||||
}: { paths: Path[] } & PathProps<'strokeWidth' | 'stroke' | 'strokeOpacity', any>) => (
|
||||
<g
|
||||
className="svg-preview-handles-group"
|
||||
{...props}
|
||||
>
|
||||
{paths.map(({ c, prev, next, cp1, cp2 }, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{cp1 && <path d={`M${prev.x} ${prev.y} ${cp1.x} ${cp1.y}`} />}
|
||||
{cp1 && (
|
||||
<circle
|
||||
cy={cp1.y}
|
||||
cx={cp1.x}
|
||||
r={0.25}
|
||||
/>
|
||||
)}
|
||||
{cp2 && <path d={`M${next.x} ${next.y} ${cp2.x} ${cp2.y}`} />}
|
||||
{cp2 && (
|
||||
<circle
|
||||
cy={cp2.y}
|
||||
cx={cp2.x}
|
||||
r={0.25}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</g>
|
||||
);
|
||||
|
||||
const SvgPreview = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
{
|
||||
height?: number;
|
||||
width?: number;
|
||||
src: string | ReturnType<typeof getPaths>;
|
||||
showGrid?: boolean;
|
||||
} & React.SVGProps<SVGSVGElement>
|
||||
>(({ src, children, showGrid = false, ...props }, ref) => {
|
||||
>(({ src, children, height = 24, width = 24, showGrid = false, ...props }, ref) => {
|
||||
const subGridSize =
|
||||
Math.max(height, width) % 3 === 0
|
||||
? Math.max(height, width) > 24
|
||||
? 12
|
||||
: 3
|
||||
: Math.max(height, width) % 5 === 0
|
||||
? 5
|
||||
: 0;
|
||||
const paths = typeof src === 'string' ? getPaths(src) : src;
|
||||
|
||||
return (
|
||||
<svg
|
||||
ref={ref}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={24}
|
||||
height={24}
|
||||
viewBox="0 0 24 24"
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox={`0 0 ${width} ${height}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
@@ -372,8 +425,12 @@ const SvgPreview = React.forwardRef<
|
||||
<style>{darkModeCss}</style>
|
||||
{showGrid && (
|
||||
<Grid
|
||||
height={height}
|
||||
width={width}
|
||||
subGridSize={subGridSize}
|
||||
strokeWidth={0.1}
|
||||
stroke="#777"
|
||||
mask="url(#svg-preview-bounding-box-mask)"
|
||||
strokeOpacity={0.3}
|
||||
radius={1}
|
||||
/>
|
||||
@@ -385,6 +442,12 @@ const SvgPreview = React.forwardRef<
|
||||
radius={1}
|
||||
strokeOpacity={0.15}
|
||||
/>
|
||||
<GapViolationHighlight
|
||||
paths={paths}
|
||||
stroke="red"
|
||||
strokeOpacity={0.75}
|
||||
strokeWidth={4}
|
||||
/>
|
||||
<Handles
|
||||
paths={paths}
|
||||
strokeWidth={0.12}
|
||||
@@ -433,4 +496,6 @@ const SvgPreview = React.forwardRef<
|
||||
);
|
||||
});
|
||||
|
||||
SvgPreview.displayName = 'SvgPreview';
|
||||
|
||||
export default SvgPreview;
|
||||
|
||||
19
docs/.vitepress/lib/SvgPreview/path-to-points.ts
Normal file
19
docs/.vitepress/lib/SvgPreview/path-to-points.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import memoize from 'lodash/memoize';
|
||||
import SVGPathCommander from 'svg-path-commander';
|
||||
import { Path } from './types';
|
||||
|
||||
function pathToPoints({ d, prev, next }: Path, interval = 1) {
|
||||
const commander = new SVGPathCommander(d);
|
||||
const points = [];
|
||||
try {
|
||||
const totalLength = commander.getTotalLength();
|
||||
points.push(prev);
|
||||
for (let i = interval; i < totalLength - interval; i += interval) {
|
||||
points.push(commander.getPointAtLength(i));
|
||||
}
|
||||
points.push(next);
|
||||
} catch (err) {}
|
||||
return points;
|
||||
}
|
||||
|
||||
export default memoize(pathToPoints);
|
||||
@@ -1,7 +1,10 @@
|
||||
import { INode, parseSync } from 'svgson';
|
||||
// @ts-ignore
|
||||
import toPath from 'element-to-path';
|
||||
// @ts-ignore
|
||||
import { SVGPathData, encodeSVGPath } from 'svg-pathdata';
|
||||
import { Path, Point } from './types';
|
||||
import memoize from 'lodash/memoize';
|
||||
|
||||
function assertNever(x: never): never {
|
||||
throw new Error('Unknown type: ' + x['type']);
|
||||
@@ -44,17 +47,21 @@ const extractNodes = (node: INode): INode[] => {
|
||||
return [];
|
||||
};
|
||||
|
||||
export const getNodes = (src: string) =>
|
||||
extractNodes(parseSync(src.includes('<svg') ? src : `<svg>${src}</svg>`));
|
||||
export const getNodes = memoize((src: string) =>
|
||||
extractNodes(parseSync(src.includes('<svg') ? src : `<svg>${src}</svg>`)),
|
||||
);
|
||||
|
||||
export const getCommands = (src: string) =>
|
||||
getNodes(src)
|
||||
.map(convertToPathNode)
|
||||
.flatMap(({ d, name }, idx) =>
|
||||
new SVGPathData(d).toAbs().commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })),
|
||||
new SVGPathData(d)
|
||||
.toAbs()
|
||||
// @ts-ignore
|
||||
.commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })),
|
||||
);
|
||||
|
||||
export const getPaths = (src: string) => {
|
||||
const getPaths = (src: string) => {
|
||||
const commands = getCommands(src.includes('<svg') ? src : `<svg>${src}</svg>`);
|
||||
const paths: Path[] = [];
|
||||
let prev: Point | undefined = undefined;
|
||||
@@ -237,6 +244,7 @@ export const getPaths = (src: string) => {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// @ts-ignore
|
||||
assertNever(c);
|
||||
}
|
||||
}
|
||||
@@ -244,7 +252,7 @@ export const getPaths = (src: string) => {
|
||||
return paths;
|
||||
};
|
||||
|
||||
export const arcEllipseCenter = (
|
||||
const arcEllipseCenter = (
|
||||
x1: number,
|
||||
y1: number,
|
||||
rx: number,
|
||||
@@ -296,5 +304,52 @@ export const arcEllipseCenter = (
|
||||
M2[1][0] * Cp[0] + M2[1][1] * Cp[1] + V3[1],
|
||||
];
|
||||
|
||||
return { x: C[0], y: C[1] };
|
||||
return {
|
||||
x: C[0],
|
||||
y: C[1],
|
||||
tangentIntersection: intersectTangents(
|
||||
{ x: x1, y: y1 },
|
||||
{ x: x2, y: y2 },
|
||||
{ x: C[0], y: C[1] },
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
function getTangentDirection(p: Point, center: Point): Point {
|
||||
// Tangent is perpendicular to the radius vector (rotate radius 90°)
|
||||
const dx = p.x - center.x;
|
||||
const dy = p.y - center.y;
|
||||
return { x: -dy, y: dx }; // 90° rotation
|
||||
}
|
||||
|
||||
function intersectTangents(start: Point, end: Point, center: Point): Point | null {
|
||||
const t1 = getTangentDirection(start, center);
|
||||
const t2 = getTangentDirection(end, center);
|
||||
|
||||
// Solve: start + λ * t1 = end + μ * t2
|
||||
const A = [
|
||||
[t1.x, -t2.x],
|
||||
[t1.y, -t2.y],
|
||||
];
|
||||
const b = [end.x - start.x, end.y - start.y];
|
||||
|
||||
// Compute determinant
|
||||
const det = A[0][0] * A[1][1] - A[0][1] * A[1][0];
|
||||
|
||||
if (Math.abs(det) < 1e-10) {
|
||||
// Lines are parallel, no intersection
|
||||
return null;
|
||||
}
|
||||
|
||||
const invDet = 1 / det;
|
||||
|
||||
const lambda = (b[0] * A[1][1] - b[1] * A[0][1]) * invDet;
|
||||
|
||||
// Intersection point = start + lambda * t1
|
||||
return {
|
||||
x: start.x + lambda * t1.x,
|
||||
y: start.y + lambda * t1.y,
|
||||
};
|
||||
}
|
||||
|
||||
export default memoize(getPaths);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
|
||||
import { getHighlighter } from 'shikiji';
|
||||
import { bundledLanguages, createHighlighter, type ThemeRegistration } from 'shiki';
|
||||
|
||||
type CodeExampleType = {
|
||||
title: string;
|
||||
@@ -10,14 +9,20 @@ type CodeExampleType = {
|
||||
const getIconCodes = (): CodeExampleType => {
|
||||
return [
|
||||
{
|
||||
language: 'js',
|
||||
language: 'html',
|
||||
title: 'Vanilla',
|
||||
code: `\
|
||||
import { createIcons, icons } from 'lucide';
|
||||
<script>
|
||||
import { createIcons, $CamelCase } from 'lucide';
|
||||
|
||||
createIcons({ icons });
|
||||
createIcons({
|
||||
icons: {
|
||||
$CamelCase
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
document.body.append('<i data-lucide="$Name"></i>');\
|
||||
<i data-lucide="$Name"></i>\
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -102,13 +107,8 @@ import { LucideAngularModule, $PascalCase } from 'lucide-angular';
|
||||
},
|
||||
{
|
||||
language: 'html',
|
||||
title: 'Icon Font',
|
||||
code: `<style>
|
||||
@import ('~lucide-static/font/Lucide.css');
|
||||
</style>
|
||||
|
||||
<div class="icon-$Name"></div>
|
||||
`,
|
||||
title: 'Icon font',
|
||||
code: `<div class="icon-$Name"></div>`,
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -118,7 +118,7 @@ export type ThemeOptions =
|
||||
| { light: ThemeRegistration; dark: ThemeRegistration };
|
||||
|
||||
const highLightCode = async (code: string, lang: string, active?: boolean) => {
|
||||
const highlighter = await getHighlighter({
|
||||
const highlighter = await createHighlighter({
|
||||
themes: ['github-light', 'github-dark'],
|
||||
langs: Object.keys(bundledLanguages),
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
|
||||
import { getHighlighter } from 'shikiji';
|
||||
import { bundledLanguages, type ThemeRegistration } from 'shiki';
|
||||
import { createHighlighter } from 'shiki';
|
||||
|
||||
type CodeExampleType = {
|
||||
title: string;
|
||||
@@ -10,10 +10,11 @@ type CodeExampleType = {
|
||||
const getIconCodes = (): CodeExampleType => {
|
||||
return [
|
||||
{
|
||||
language: 'js',
|
||||
language: 'html',
|
||||
title: 'Vanilla',
|
||||
code: `\
|
||||
import { createIcons, icons } from 'lucide';
|
||||
<script>
|
||||
import { createIcons } from 'lucide';
|
||||
import { $CamelCase } from '@lucide/lab';
|
||||
|
||||
createIcons({
|
||||
@@ -21,8 +22,9 @@ createIcons({
|
||||
$CamelCase
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
document.body.append('<i data-lucide="$Name"></i>');\
|
||||
<i data-lucide="$Name"></i>\
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -119,7 +121,7 @@ export type ThemeOptions =
|
||||
| { light: ThemeRegistration; dark: ThemeRegistration };
|
||||
|
||||
const highLightCode = async (code: string, lang: string, active?: boolean) => {
|
||||
const highlighter = await getHighlighter({
|
||||
const highlighter = await createHighlighter({
|
||||
themes: ['github-light', 'github-dark'],
|
||||
langs: Object.keys(bundledLanguages),
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
|
||||
import { getHighlighter } from 'shikiji';
|
||||
import { bundledLanguages, type ThemeRegistration } from 'shiki';
|
||||
import { createHighlighter } from 'shiki';
|
||||
|
||||
export type ThemeOptions =
|
||||
| ThemeRegistration
|
||||
| { light: ThemeRegistration; dark: ThemeRegistration };
|
||||
|
||||
const highLightCode = async (code: string, lang: string, active?: boolean) => {
|
||||
const highlighter = await getHighlighter({
|
||||
const highlighter = await createHighlighter({
|
||||
themes: ['github-light', 'github-dark'],
|
||||
langs: Object.keys(bundledLanguages),
|
||||
});
|
||||
|
||||
@@ -96,6 +96,10 @@ const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
|
||||
text: 'Lucide Preact',
|
||||
link: '/guide/packages/lucide-preact',
|
||||
},
|
||||
{
|
||||
text: 'Lucide Astro',
|
||||
link: '/guide/packages/lucide-astro',
|
||||
},
|
||||
{
|
||||
text: 'Lucide Static',
|
||||
link: '/guide/packages/lucide-static',
|
||||
|
||||
@@ -3,12 +3,20 @@ import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
||||
import { search } from '../../../data/iconNodes'
|
||||
|
||||
const SearchIcon = createLucideIcon('search', search)
|
||||
|
||||
defineProps({
|
||||
shortcut: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="fake-input">
|
||||
<component :is="SearchIcon" class="search-icon"/>
|
||||
<slot/>
|
||||
<kbd v-if="shortcut" class="shortcut">{{ shortcut }}</kbd>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
@@ -33,4 +41,23 @@ const SearchIcon = createLucideIcon('search', search)
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-bg-alt);
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
margin-left: auto;
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
color: var(--vp-c-text-3);
|
||||
background: var(--vp-c-default-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
.shortcut {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,20 +6,35 @@ export default {
|
||||
export interface InputProps {
|
||||
type: string
|
||||
modelValue: string
|
||||
shortcut?: string
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, onMounted, nextTick, watch } from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<InputProps>(), {
|
||||
type: 'text'
|
||||
})
|
||||
|
||||
const input = ref()
|
||||
const wrapperEl = ref()
|
||||
const shortcutEl = ref()
|
||||
|
||||
defineEmits(['change', 'input', 'update:modelValue'])
|
||||
|
||||
const updateShortcutSpacing = () => {
|
||||
nextTick(() => {
|
||||
if (shortcutEl.value && wrapperEl.value) {
|
||||
const shortcutWidth = shortcutEl.value.offsetWidth
|
||||
wrapperEl.value.style.setProperty('--shortcut-width', `${shortcutWidth}px`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(updateShortcutSpacing)
|
||||
watch(() => props.shortcut, updateShortcutSpacing)
|
||||
|
||||
defineExpose({
|
||||
focus: () => {
|
||||
input.value.focus()
|
||||
@@ -28,17 +43,18 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="input-wrapper">
|
||||
<div class="input-wrapper" ref="wrapperEl">
|
||||
<slot name="icon" class="icon" />
|
||||
<input
|
||||
:type="type"
|
||||
class="input"
|
||||
:class="{'has-icon': $slots.icon}"
|
||||
:class="{'has-icon': $slots.icon, 'has-shortcut': shortcut}"
|
||||
ref="input"
|
||||
:value="modelValue"
|
||||
v-bind="$attrs"
|
||||
@input="$emit('update:modelValue', $event.target.value)"
|
||||
/>
|
||||
<kbd v-if="shortcut" class="shortcut" ref="shortcutEl">{{ shortcut }}</kbd>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -57,6 +73,10 @@ defineExpose({
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.input.has-shortcut {
|
||||
padding-right: calc(var(--shortcut-width, 40px) + 22px);
|
||||
}
|
||||
|
||||
.input:hover, .input:focus {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-bg-alt);
|
||||
@@ -66,7 +86,28 @@ defineExpose({
|
||||
padding-left: 52px;
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
color: var(--vp-c-text-3);
|
||||
background: var(--vp-c-default-soft);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
.shortcut {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -14,6 +14,7 @@ const SearchIcon = createLucideIcon('search', search)
|
||||
|
||||
interface Props {
|
||||
modelValue: string
|
||||
shortcut?: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
@@ -38,6 +39,8 @@ const value = computed({
|
||||
<Input
|
||||
ref="input"
|
||||
type="search"
|
||||
autofocus
|
||||
:shortcut="shortcut"
|
||||
v-bind="$attrs"
|
||||
v-model="value"
|
||||
class="input-wrapper"
|
||||
@@ -70,5 +73,4 @@ const value = computed({
|
||||
font-size: 14px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { Switch } from '@headlessui/vue'
|
||||
|
||||
const enabled = ref(false)
|
||||
defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Switch
|
||||
v-model="enabled"
|
||||
:model-value="modelValue"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
class="switch"
|
||||
:class="{ enabled }"
|
||||
:class="{ enabled: modelValue }"
|
||||
>
|
||||
<span class="thumb" />
|
||||
</Switch>
|
||||
|
||||
@@ -5,10 +5,15 @@ import LucideIcon from '../base/LucideIcon.vue'
|
||||
import { useRouter } from 'vitepress';
|
||||
import { random } from 'lodash-es'
|
||||
import FakeInput from '../base/FakeInput.vue'
|
||||
import useSearchShortcut from '../../utils/useSearchShortcut'
|
||||
|
||||
const { go } = useRouter()
|
||||
const intervalTime = shallowRef()
|
||||
|
||||
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
|
||||
go('/icons/?focus')
|
||||
})
|
||||
|
||||
const getInitialItems = () => data.icons.slice(0, 48)
|
||||
const items = ref(getInitialItems())
|
||||
let id = items.value.length + 1
|
||||
@@ -64,7 +69,11 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
<FakeInput @click="go('/icons/?focus')" class="search-box">
|
||||
<FakeInput
|
||||
@click="go('/icons/?focus')"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="search-box"
|
||||
>
|
||||
Search {{ data.iconsCount }} icons...
|
||||
</FakeInput>
|
||||
</div>
|
||||
|
||||
@@ -48,6 +48,7 @@ function resetStyle () {
|
||||
color.value = 'currentColor'
|
||||
strokeWidth.value = 2
|
||||
size.value = 24
|
||||
absoluteStrokeWidth.value = false
|
||||
}
|
||||
|
||||
watch(absoluteStrokeWidth, (enabled) => {
|
||||
|
||||
@@ -37,6 +37,12 @@ export default {
|
||||
logo: '/framework-logos/angular.svg',
|
||||
label: 'Lucide documentation for Angular',
|
||||
},
|
||||
{
|
||||
name: 'lucide-astro',
|
||||
logo: '/framework-logos/astro.svg',
|
||||
logoDark: '/framework-logos/astro-dark.svg',
|
||||
label: 'Lucide documentation for Astro',
|
||||
},
|
||||
{
|
||||
name: 'lucide-react-native',
|
||||
logo: '/framework-logos/react-native.svg',
|
||||
|
||||
@@ -13,7 +13,7 @@ const { go } = useRouter()
|
||||
<HomeSectionTitle>Available For:</HomeSectionTitle>
|
||||
<div class="packages-list">
|
||||
<a
|
||||
v-for="{ name, logo } in data.packages"
|
||||
v-for="{ name, logo, logoDark } in data.packages"
|
||||
:href="`/guide/packages/${name}`"
|
||||
class="package-logo"
|
||||
:aria-label="`Read more about: ${name} package`"
|
||||
@@ -21,10 +21,17 @@ const { go } = useRouter()
|
||||
>
|
||||
<img
|
||||
:src="logo"
|
||||
height="36"
|
||||
width="36"
|
||||
loading="lazy"
|
||||
:class="{ light: logoDark, 'image-logo': true }"
|
||||
:alt="`${name} logo`"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
<img
|
||||
v-if="logoDark"
|
||||
:src="logoDark"
|
||||
:alt="`${name} logo`"
|
||||
class="image-logo dark"
|
||||
loading="lazy"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
@@ -35,6 +42,13 @@ const { go } = useRouter()
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.image-logo {
|
||||
object-fit: contain;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.packages-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -57,4 +71,11 @@ const { go } = useRouter()
|
||||
.package-logo:hover {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
html.dark .image-logo.light {
|
||||
display: none;
|
||||
}
|
||||
html:not(.dark) .image-logo.dark {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -9,6 +9,8 @@ import {useData, useRouter} from 'vitepress';
|
||||
import { computed } from 'vue';
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||
import { diamond } from '../../../data/iconNodes'
|
||||
import deprecationReasonTemplate from '../../../../../tools/build-icons/utils/deprecationReasonTemplate.ts';
|
||||
|
||||
|
||||
const props = defineProps<{
|
||||
icon: IconEntity
|
||||
@@ -24,6 +26,15 @@ const tags = computed(() => {
|
||||
})
|
||||
|
||||
const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
|
||||
const deprecatedTitle = computed(() => {
|
||||
if (!props.icon.deprecationReason) return '';
|
||||
return deprecationReasonTemplate(props.icon.deprecationReason, {
|
||||
componentName: props.icon.name,
|
||||
iconName: props.icon.name,
|
||||
toBeRemovedInVersion: props.icon.toBeRemovedInVersion,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -36,6 +47,13 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
<DiamondIcon fill="currentColor" :size="12"/>
|
||||
{{ icon.externalLibrary }}
|
||||
</div>
|
||||
<Badge
|
||||
v-if="icon.deprecated"
|
||||
class="deprecated-badge"
|
||||
:title="deprecatedTitle"
|
||||
>
|
||||
Deprecated
|
||||
</Badge>
|
||||
</div>
|
||||
<div class="tags-scroller" v-if="tags.length">
|
||||
<p class="icon-tags horizontal-scroller">
|
||||
@@ -98,6 +116,16 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.deprecated-badge {
|
||||
background-color: var(--vp-c-brand-5);
|
||||
margin-left: 40px;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.deprecated-badge:hover {
|
||||
background-color: var(--vp-c-brand-2);
|
||||
}
|
||||
|
||||
.icon-tags {
|
||||
font-size: 16px;
|
||||
color: var(--vp-c-text-2);
|
||||
|
||||
@@ -178,6 +178,8 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
|
||||
stroke-width: var(--customize-strokeWidth, 2);
|
||||
width: calc(var(--customize-size, 24) * 1px);
|
||||
height: calc(var(--customize-size, 24) * 1px);
|
||||
max-width: 3rem;
|
||||
max-height: 3rem;
|
||||
}
|
||||
|
||||
html.absolute-stroke-width .lucide-icon.customizable {
|
||||
|
||||
@@ -26,10 +26,9 @@ const iconComponent = computed(() => {
|
||||
<component
|
||||
ref="previewIcon"
|
||||
:is="iconComponent"
|
||||
:width="size"
|
||||
:height="size"
|
||||
:stroke="color"
|
||||
:stroke-width="absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth"
|
||||
:size="size"
|
||||
:color="color"
|
||||
:strokeWidth="absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth"
|
||||
/>
|
||||
<svg class="icon-grid" :viewBox="`0 0 ${size} ${size}`" fill="none" stroke-width="0.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g :key="`grid-${i}`" v-for="(_, i) in gridLines">
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, defineAsyncComponent, onMounted, watch, watchEffect } from 'vue';
|
||||
import { ref, computed, defineAsyncComponent, onMounted } from 'vue';
|
||||
import type { IconEntity, Category } from '../../types';
|
||||
import useSearch from '../../composables/useSearch';
|
||||
import InputSearch from '../base/InputSearch.vue';
|
||||
import useSearchInput from '../../composables/useSearchInput';
|
||||
import useSearchShortcut from '../../utils/useSearchShortcut';
|
||||
import StickyBar from './StickyBar.vue';
|
||||
import IconsCategory from './IconsCategory.vue';
|
||||
import IconsCategory, { CategoryRow } from './IconsCategory.vue';
|
||||
import useFetchTags from '../../composables/useFetchTags';
|
||||
import useFetchCategories from '../../composables/useFetchCategories';
|
||||
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
|
||||
import chunkArray from '../../utils/chunkArray';
|
||||
import { CategoryRow } from './IconsCategory.vue';
|
||||
import useScrollToCategory from '../../composables/useScrollToCategory';
|
||||
import CarbonAdOverlay from './CarbonAdOverlay.vue';
|
||||
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
|
||||
|
||||
const ICON_SIZE = 56;
|
||||
const ICON_GRID_GAP = 8;
|
||||
@@ -27,6 +28,10 @@ const activeIconName = ref(null);
|
||||
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
|
||||
const isSearching = computed(() => !!searchQuery.value);
|
||||
|
||||
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
|
||||
searchInput.value?.focus();
|
||||
});
|
||||
|
||||
function setActiveIconName(name: string) {
|
||||
activeIconName.value = name;
|
||||
}
|
||||
@@ -35,10 +40,10 @@ const { execute: fetchTags, data: tags } = useFetchTags();
|
||||
const { execute: fetchCategories, data: categoriesMap } = useFetchCategories();
|
||||
|
||||
const overviewEl = ref<HTMLElement | null>(null);
|
||||
const { width: containerWidth } = useElementSize(overviewEl)
|
||||
const { width: containerWidth } = useElementSize(overviewEl);
|
||||
|
||||
const columnSize = computed(() => {
|
||||
return Math.floor((containerWidth.value) / ((ICON_SIZE + ICON_GRID_GAP)));
|
||||
return Math.floor(containerWidth.value / (ICON_SIZE + ICON_GRID_GAP));
|
||||
});
|
||||
|
||||
const mappedIcons = computed(() => {
|
||||
@@ -66,26 +71,27 @@ const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
|
||||
const categories = computed(() => {
|
||||
if (!props.categories?.length || !props.icons?.length) return [];
|
||||
|
||||
return props.categories
|
||||
.map(({ name, title }) => {
|
||||
const categoryIcons = props.icons.filter((icon) => {
|
||||
const iconCategories = icon?.externalLibrary ? icon.categories : props.iconCategories[icon.name]
|
||||
return props.categories.map(({ name, title }) => {
|
||||
const categoryIcons = props.icons.filter((icon) => {
|
||||
const iconCategories = icon?.externalLibrary
|
||||
? icon.categories
|
||||
: props.iconCategories[icon.name];
|
||||
|
||||
return iconCategories?.includes(name);
|
||||
});
|
||||
return iconCategories?.includes(name);
|
||||
});
|
||||
|
||||
const searchedCategoryIcons = isSearching
|
||||
? categoryIcons.filter((icon) =>
|
||||
searchResults.value.some((item) => item?.name === icon?.name)
|
||||
)
|
||||
: categoryIcons;
|
||||
const searchedCategoryIcons = isSearching
|
||||
? categoryIcons.filter((icon) =>
|
||||
searchResults.value.some((item) => item?.name === icon?.name),
|
||||
)
|
||||
: categoryIcons;
|
||||
|
||||
return {
|
||||
title,
|
||||
name,
|
||||
icons: searchedCategoryIcons,
|
||||
};
|
||||
})
|
||||
return {
|
||||
title,
|
||||
name,
|
||||
icons: searchedCategoryIcons,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const categoriesList = computed(() => {
|
||||
@@ -102,26 +108,24 @@ const categoriesList = computed(() => {
|
||||
return acc;
|
||||
}, []);
|
||||
});
|
||||
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
|
||||
|
||||
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
|
||||
categoriesList,
|
||||
{
|
||||
itemHeight: ICON_SIZE + ICON_GRID_GAP,
|
||||
overscan: 10
|
||||
},
|
||||
)
|
||||
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(categoriesList, {
|
||||
itemHeight: ICON_SIZE + ICON_GRID_GAP,
|
||||
overscan: 10,
|
||||
});
|
||||
|
||||
useScrollToCategory({
|
||||
categories,
|
||||
categoriesList,
|
||||
scrollTo,
|
||||
searchQueryDebounced,
|
||||
})
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
containerProps.ref.value = document.documentElement;
|
||||
useEventListener(window, 'scroll', containerProps.onScroll)
|
||||
})
|
||||
useEventListener(window, 'scroll', containerProps.onScroll);
|
||||
});
|
||||
|
||||
function onFocusSearchInput() {
|
||||
if (tags.value == null) {
|
||||
@@ -140,28 +144,27 @@ function handleCloseDrawer() {
|
||||
|
||||
window.history.pushState({}, '', '/icons/categories');
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
|
||||
console.log(props.icons.find((icon) => icon.name === 'burger'));
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="overviewEl" class="overview-container">
|
||||
<div
|
||||
ref="overviewEl"
|
||||
class="overview-container"
|
||||
>
|
||||
<StickyBar class="category-search">
|
||||
<InputSearch
|
||||
:placeholder="`Search ${icons.length} icons ...`"
|
||||
v-model="searchQuery"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
ref="searchInput"
|
||||
@focus="onFocusSearchInput"
|
||||
/>
|
||||
</StickyBar>
|
||||
<NoResults
|
||||
v-if="categories.length === 0"
|
||||
:searchQuery="searchQuery"
|
||||
v-if="searchPlaceholder.isNoResults"
|
||||
:searchQuery="searchPlaceholder.query"
|
||||
:isBrandSearch="searchPlaceholder.isBrand"
|
||||
@clear="searchQuery = ''"
|
||||
/>
|
||||
<div v-bind="wrapperProps">
|
||||
@@ -202,8 +205,4 @@ watchEffect(() => {
|
||||
.icons {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.overview-container {
|
||||
padding-bottom: 288px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,19 +2,30 @@
|
||||
import { ref, computed, defineAsyncComponent, onMounted, watch } from 'vue';
|
||||
import type { IconEntity } from '../../types';
|
||||
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
|
||||
import { useRoute } from 'vitepress';
|
||||
import IconGrid from './IconGrid.vue';
|
||||
import InputSearch from '../base/InputSearch.vue';
|
||||
import useSearch from '../../composables/useSearch';
|
||||
import useSearchInput from '../../composables/useSearchInput';
|
||||
import useSearchShortcut from '../../utils/useSearchShortcut';
|
||||
import StickyBar from './StickyBar.vue';
|
||||
import useFetchTags from '../../composables/useFetchTags';
|
||||
import useFetchCategories from '../../composables/useFetchCategories';
|
||||
import chunkArray from '../../utils/chunkArray';
|
||||
import CarbonAdOverlay from './CarbonAdOverlay.vue';
|
||||
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
|
||||
|
||||
const ICON_SIZE = 56;
|
||||
const ICON_GRID_GAP = 8;
|
||||
const DEFAULT_GRID_ITEMS = 10 * 160;
|
||||
|
||||
const initialGridItems = computed(() => {
|
||||
if (containerWidth.value === 0) return 120;
|
||||
|
||||
const itemsPerRow = columnSize.value || 10;
|
||||
const visibleRows = Math.ceil(window.innerHeight / (ICON_SIZE + ICON_GRID_GAP));
|
||||
|
||||
return Math.min(itemsPerRow * (visibleRows + 2), 200);
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
icons: IconEntity[];
|
||||
@@ -26,10 +37,10 @@ const { execute: fetchTags, data: tags } = useFetchTags();
|
||||
const { execute: fetchCategories, data: categories } = useFetchCategories();
|
||||
|
||||
const overviewEl = ref<HTMLElement | null>(null);
|
||||
const { width: containerWidth } = useElementSize(overviewEl)
|
||||
const { width: containerWidth } = useElementSize(overviewEl);
|
||||
|
||||
const columnSize = computed(() => {
|
||||
return Math.floor((containerWidth.value) / ((ICON_SIZE + ICON_GRID_GAP)));
|
||||
return Math.floor(containerWidth.value / (ICON_SIZE + ICON_GRID_GAP));
|
||||
});
|
||||
|
||||
const mappedIcons = computed(() => {
|
||||
@@ -50,30 +61,38 @@ const mappedIcons = computed(() => {
|
||||
});
|
||||
|
||||
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
|
||||
|
||||
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
|
||||
searchInput.value?.focus();
|
||||
});
|
||||
|
||||
const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
|
||||
{ name: 'name', weight: 3 },
|
||||
{ name: 'aliases', weight: 3 },
|
||||
{ name: 'tags', weight: 2 },
|
||||
{ name: 'categories', weight: 1 },
|
||||
]);
|
||||
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
|
||||
|
||||
const chunkedIcons = computed(() => {
|
||||
return chunkArray(searchResults.value, columnSize.value);
|
||||
});
|
||||
|
||||
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
|
||||
chunkedIcons,
|
||||
{
|
||||
itemHeight: ICON_SIZE + ICON_GRID_GAP,
|
||||
overscan: 10
|
||||
},
|
||||
)
|
||||
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(chunkedIcons, {
|
||||
itemHeight: ICON_SIZE + ICON_GRID_GAP,
|
||||
overscan: 10,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
containerProps.ref.value = document.documentElement;
|
||||
useEventListener(window, 'scroll', containerProps.onScroll)
|
||||
})
|
||||
useEventListener(window, 'scroll', containerProps.onScroll);
|
||||
|
||||
// Check if we should focus the search input from URL parameter
|
||||
const route = useRoute();
|
||||
if (route.data?.relativePath && window.location.search.includes('focus')) {
|
||||
searchInput.value?.focus();
|
||||
}
|
||||
});
|
||||
|
||||
function setActiveIconName(name: string) {
|
||||
activeIconName.value = name;
|
||||
@@ -93,8 +112,8 @@ const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
|
||||
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
|
||||
|
||||
watch(searchQueryDebounced, () => {
|
||||
scrollTo(0)
|
||||
})
|
||||
scrollTo(0);
|
||||
});
|
||||
|
||||
function handleCloseDrawer() {
|
||||
setActiveIconName('');
|
||||
@@ -104,26 +123,30 @@ function handleCloseDrawer() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="overviewEl" class="overview-container">
|
||||
<div
|
||||
ref="overviewEl"
|
||||
class="overview-container"
|
||||
>
|
||||
<StickyBar>
|
||||
<InputSearch
|
||||
:placeholder="`Search ${icons.length} icons ...`"
|
||||
v-model="searchQuery"
|
||||
ref="searchInput"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
@focus="onFocusSearchInput"
|
||||
/>
|
||||
</StickyBar>
|
||||
<NoResults
|
||||
v-if="list.length === 0 && searchQuery !== ''"
|
||||
:searchQuery="searchQuery"
|
||||
v-if="searchPlaceholder.isNoResults"
|
||||
:searchQuery="searchPlaceholder.query"
|
||||
:isBrandSearch="searchPlaceholder.isBrand"
|
||||
@clear="searchQuery = ''"
|
||||
/>
|
||||
<IconGrid
|
||||
v-else-if="list.length === 0"
|
||||
:key="index"
|
||||
overlayMode
|
||||
:icons="[...searchResults].splice(0, DEFAULT_GRID_ITEMS)"
|
||||
:icons="searchResults.slice(0, initialGridItems)"
|
||||
:activeIcon="activeIconName"
|
||||
@setActiveIcon="setActiveIconName"
|
||||
/>
|
||||
@@ -163,8 +186,4 @@ function handleCloseDrawer() {
|
||||
.input-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.overview-container {
|
||||
padding-bottom: 288px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,56 +1,218 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { bird, squirrel, rabbit } from '../../../data/iconNodes'
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
||||
import {useEventListener} from '@vueuse/core'
|
||||
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
|
||||
import { IconNode } from '../../types'
|
||||
import { ref, onMounted, computed, markRaw, shallowReadonly, watch } from 'vue';
|
||||
import {
|
||||
bird,
|
||||
squirrel,
|
||||
rabbit,
|
||||
ghost,
|
||||
castle,
|
||||
drama,
|
||||
dog,
|
||||
cat,
|
||||
wandSparkles,
|
||||
save,
|
||||
snowflake,
|
||||
cake,
|
||||
fish,
|
||||
turtle,
|
||||
rat,
|
||||
worm,
|
||||
testTubeDiagonal,
|
||||
sword,
|
||||
} from '../../../data/iconNodes';
|
||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
|
||||
import { IconNode } from '../../types';
|
||||
|
||||
defineProps<{
|
||||
searchQuery: string
|
||||
}>()
|
||||
const { searchQuery, isBrandSearch } = defineProps<{
|
||||
searchQuery: string;
|
||||
isBrandSearch: boolean;
|
||||
}>();
|
||||
|
||||
defineEmits(['clear'])
|
||||
defineEmits(['clear']);
|
||||
|
||||
const animalIcon = ref<HTMLElement>()
|
||||
const randomAnimal = computed<IconNode>(() => {
|
||||
return Math.random() > 0.5 ? squirrel : Math.random() > 0.5 ? rabbit : bird
|
||||
})
|
||||
const animalComponent = computed(() => createLucideIcon('animal', randomAnimal.value))
|
||||
const flip = ref(false)
|
||||
interface Placeholder {
|
||||
title: string;
|
||||
message: string;
|
||||
icon: IconNode;
|
||||
finePrint?: string;
|
||||
}
|
||||
|
||||
const brandPlaceholders: Placeholder[] = shallowReadonly([
|
||||
{
|
||||
title: 'Boooo! What a scary brand logo!',
|
||||
message:
|
||||
'[name] and its friends often haunt this search box, but you won’t ever find them here.',
|
||||
icon: markRaw(ghost),
|
||||
},
|
||||
{
|
||||
title: 'Thank You Mario!',
|
||||
message: 'But [name] is in another castle!',
|
||||
icon: markRaw(castle),
|
||||
},
|
||||
{
|
||||
title: '[name] did audition for our icon set',
|
||||
message: '...but didn’t make the callback.',
|
||||
icon: markRaw(drama),
|
||||
},
|
||||
{
|
||||
title: 'Such Search. Very [name].',
|
||||
message: 'Much not here. So Wow.',
|
||||
icon: markRaw(dog),
|
||||
},
|
||||
{
|
||||
title: 'I Can Has [name]?',
|
||||
message: 'No [name] for you in here.',
|
||||
icon: markRaw(cat),
|
||||
},
|
||||
{
|
||||
title: 'Loading [name]...',
|
||||
message: 'Fatal error: our cartridge contains only open-source pixels.',
|
||||
icon: markRaw(save),
|
||||
},
|
||||
{
|
||||
title: '[name] Shall Not Pass',
|
||||
message: 'Do not look to its coming at first light of any day.',
|
||||
icon: markRaw(wandSparkles),
|
||||
},
|
||||
{
|
||||
title: 'Winter is coming',
|
||||
message: 'But [name] sure isn’t.',
|
||||
icon: markRaw(snowflake),
|
||||
},
|
||||
{
|
||||
title: 'The cake is a lie',
|
||||
message: 'And so is the promise of an icon for [name] at Lucide.',
|
||||
icon: markRaw(cake),
|
||||
},
|
||||
{
|
||||
title: 'It’s not a bug',
|
||||
message: 'Having no [name] icon around is a feature.',
|
||||
icon: markRaw(worm),
|
||||
},
|
||||
{
|
||||
title: 'The lab exploded',
|
||||
message: 'We tried mixing [name] with open-source icons.',
|
||||
icon: markRaw(testTubeDiagonal),
|
||||
},
|
||||
{
|
||||
title: 'It’s Dangerous to Go Alone',
|
||||
message: 'Take this icon instead — it’s not [name], but it might help.',
|
||||
icon: markRaw(sword),
|
||||
},
|
||||
]);
|
||||
|
||||
const notFoundPlaceholders: Omit<Placeholder, 'title'>[] = shallowReadonly([
|
||||
{
|
||||
message: 'We’ve looked for this icon for a bird’s eye view, but could not find it.',
|
||||
icon: markRaw(bird),
|
||||
},
|
||||
{
|
||||
message: 'We checked every tree. Only acorns, no results.',
|
||||
icon: markRaw(squirrel),
|
||||
},
|
||||
{
|
||||
message: 'You’ve gone too deep into the rabbit hole — this icon doesn’t exist.',
|
||||
icon: markRaw(rabbit),
|
||||
},
|
||||
{
|
||||
message: 'This icon seems to have slipped through the net.',
|
||||
icon: markRaw(fish),
|
||||
},
|
||||
{
|
||||
message: 'This icon might exist in the future… but it hasn’t arrived yet.',
|
||||
icon: markRaw(turtle),
|
||||
},
|
||||
{
|
||||
message: 'Rats! This icon seems to have slipped through the cracks.',
|
||||
icon: markRaw(rat),
|
||||
},
|
||||
]);
|
||||
|
||||
function randomItem<T>(arr: T[]): T {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
const placeholderIcon = ref<HTMLElement>();
|
||||
const placeholder = ref<Placeholder>();
|
||||
|
||||
watch(
|
||||
() => isBrandSearch,
|
||||
() => {
|
||||
placeholder.value = isBrandSearch
|
||||
? {
|
||||
...randomItem(brandPlaceholders),
|
||||
finePrint:
|
||||
'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.',
|
||||
}
|
||||
: {
|
||||
title: `No results for “[name]”`,
|
||||
finePrint:
|
||||
'This icon doesn’t seem to exist… yet. Try searching similar terms, browsing existing requests, or opening a new one.',
|
||||
...randomItem(notFoundPlaceholders),
|
||||
};
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
const iconComponent = computed(() => createLucideIcon('placeholder', placeholder.value.icon));
|
||||
const flip = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
useEventListener(document, 'mousemove', (mouseEvent) => {
|
||||
const {width, height, x, y} = animalIcon.value.getBoundingClientRect()
|
||||
const { width, x } = placeholderIcon.value.getBoundingClientRect();
|
||||
|
||||
const centerX = (width / 2) + x
|
||||
|
||||
flip.value = mouseEvent.x < centerX
|
||||
})
|
||||
})
|
||||
const centerX = width / 2 + x;
|
||||
|
||||
flip.value = !isBrandSearch && mouseEvent.x < centerX;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="no-results">
|
||||
<component
|
||||
:is="animalComponent"
|
||||
class="animal-icon"
|
||||
ref="animalIcon"
|
||||
:is="iconComponent"
|
||||
class="placeholder-icon"
|
||||
ref="placeholderIcon"
|
||||
:class="{ flip }"
|
||||
:strokeWidth="1"
|
||||
/>
|
||||
<h2 class="no-results-text">
|
||||
No icons found for '{{ searchQuery }}'
|
||||
</h2>
|
||||
<h2 class="no-results-text">{{ placeholder.title.replace('[name]', searchQuery) }}</h2>
|
||||
<p class="no-results-message">
|
||||
{{ placeholder.message.replace('[name]', searchQuery) }}
|
||||
</p>
|
||||
<div class="divider"></div>
|
||||
<p
|
||||
v-if="placeholder.finePrint"
|
||||
class="no-results-fine-print"
|
||||
>
|
||||
{{ placeholder.finePrint }}
|
||||
</p>
|
||||
<VPButton
|
||||
text="Clear your search and try again"
|
||||
theme="alt"
|
||||
v-if="isBrandSearch"
|
||||
text="Head over to Simple Icons"
|
||||
theme="brand"
|
||||
:href="`https://simpleicons.org/?q=${searchQuery}`"
|
||||
target="_blank"
|
||||
/>
|
||||
<VPButton
|
||||
v-else
|
||||
text="Clear search & try again"
|
||||
theme="brand"
|
||||
@click="$emit('clear')"
|
||||
/>
|
||||
<span class="text-divider">or</span>
|
||||
<VPButton
|
||||
text="Search on Github issues"
|
||||
v-if="isBrandSearch"
|
||||
text="Read our statement on brand logos"
|
||||
theme="alt"
|
||||
href="https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md"
|
||||
target="_blank"
|
||||
/>
|
||||
<VPButton
|
||||
v-else
|
||||
text="Search GitHub issues"
|
||||
theme="alt"
|
||||
:href="`https://github.com/lucide-icons/lucide/issues?q=is%3Aopen+${searchQuery}`"
|
||||
target="_blank"
|
||||
@@ -63,33 +225,38 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding-block: 48px;
|
||||
}
|
||||
|
||||
.animal-icon {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
color: var(--vp-c-neutral);
|
||||
opacity: 0.8;
|
||||
margin-top: 72px;
|
||||
.placeholder-icon {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.animal-icon.flip {
|
||||
.placeholder-icon.flip {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.animal-icon {
|
||||
width: 240px;
|
||||
height: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.no-results-text {
|
||||
line-height: 40px;
|
||||
line-height: 1.35;
|
||||
font-size: 24px;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 8px;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.no-results-message {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.no-results-fine-print {
|
||||
max-inline-size: 60ch;
|
||||
font-size: 14px;
|
||||
margin-bottom: 32px;
|
||||
text-align: center;
|
||||
color: var(--vp-c-text-2);
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.text-divider {
|
||||
@@ -97,4 +264,10 @@ onMounted(() => {
|
||||
font-size: 16px;
|
||||
color: var(--vp-c-neutral);
|
||||
}
|
||||
.divider {
|
||||
margin: 24px auto 18px;
|
||||
width: 64px;
|
||||
height: 1px;
|
||||
background-color: var(--vp-c-divider);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -47,6 +47,7 @@ function resetStyle () {
|
||||
color.value = STYLE_DEFAULTS.color
|
||||
strokeWidth.value = STYLE_DEFAULTS.strokeWidth
|
||||
size.value = STYLE_DEFAULTS.size
|
||||
absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth
|
||||
}
|
||||
|
||||
watch(absoluteStrokeWidth, (enabled) => {
|
||||
@@ -59,9 +60,8 @@ const customizingActive = computed(() => {
|
||||
return color.value !== STYLE_DEFAULTS.color
|
||||
|| strokeWidth.value !== STYLE_DEFAULTS.strokeWidth
|
||||
|| size.value !== STYLE_DEFAULTS.size
|
||||
|| absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -110,7 +110,7 @@ const customizingActive = computed(() => {
|
||||
name="size"
|
||||
v-model="size"
|
||||
:min="16"
|
||||
:max="48"
|
||||
:max="256"
|
||||
:step="4"
|
||||
/>
|
||||
</InputField>
|
||||
@@ -120,8 +120,8 @@ const customizingActive = computed(() => {
|
||||
label="Absolute Stroke width"
|
||||
>
|
||||
<Switch
|
||||
id="size"
|
||||
name="size"
|
||||
id="absolute-stroke-width"
|
||||
name="absolute-stroke-width"
|
||||
v-model="absoluteStrokeWidth"
|
||||
/>
|
||||
</InputField>
|
||||
@@ -161,9 +161,4 @@ const customizingActive = computed(() => {
|
||||
.color-picker {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#absolute-stroke-width {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
import packageData from '../../../data/packageData.json';
|
||||
import packageDataList from '../../../data/packageData.json';
|
||||
import thirdPartyPackages from '../../../data/packageData.thirdParty.json';
|
||||
import fetchPackages from '../../../lib/fetchPackages';
|
||||
|
||||
export default {
|
||||
async load() {
|
||||
const packages = await fetchPackages();
|
||||
const packageJsonList = await fetchPackages();
|
||||
|
||||
return {
|
||||
packages: packages
|
||||
.filter((p) => p?.name != null && p.name in packageData)
|
||||
.map((pData) => ({
|
||||
...pData,
|
||||
...packageData[pData.name],
|
||||
documentation: `/guide/packages/${pData.name}`,
|
||||
source: `https://github.com/lucide-icons/lucide/tree/main/packages/${pData.name}`,
|
||||
icon: `/framework-logos/${packageData[pData.name].icon}.svg`,
|
||||
}))
|
||||
packages: packageJsonList
|
||||
.filter((pJson) => pJson?.name != null && pJson.name in packageDataList)
|
||||
.map((pJson) => {
|
||||
const packageData = packageDataList[pJson.name];
|
||||
return {
|
||||
...pJson,
|
||||
...packageData,
|
||||
documentation: `/guide/packages/${packageData.docsAlias ?? pJson.name}`,
|
||||
source: `https://github.com/lucide-icons/lucide/tree/main/packages/${packageData.packageDirname ?? pJson.name}`,
|
||||
icon: `/framework-logos/${packageData.icon}.svg`,
|
||||
iconDark: Boolean(packageData.iconDark)
|
||||
? `/framework-logos/${packageData.iconDark}.svg`
|
||||
: null,
|
||||
};
|
||||
})
|
||||
.sort((a, b) => a.order - b.order),
|
||||
thirdPartyPackages,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { ref, inject, Ref } from 'vue';
|
||||
|
||||
export const ICON_STYLE_CONTEXT = Symbol('size');
|
||||
export const ICON_STYLE_CONTEXT = Symbol('style');
|
||||
|
||||
interface IconSizeContext {
|
||||
size: Ref<number>;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
--vp-code-editor-string: #032f62;
|
||||
|
||||
--vp-c-text-4: rgba(60, 60, 67, 0.32);
|
||||
--vp-home-hero-name-color: var(--vp-c-text);
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -60,36 +61,15 @@
|
||||
.VPHomeHero .image-container {
|
||||
transform: none;
|
||||
width: 100%;
|
||||
/* padding: 0 24px; */
|
||||
}
|
||||
|
||||
/* .VPHomeHero .container {
|
||||
flex-direction: column-reverse;
|
||||
} */
|
||||
.VPHomeHero .container .main {
|
||||
/* flex:1; */
|
||||
flex-shirk: 0;
|
||||
}
|
||||
|
||||
.VPHomeHero .container .main h1.name {
|
||||
color: var(--vp-c-text);
|
||||
}
|
||||
.VPHomeHero .container .main h1.name .clip {
|
||||
color: inherit;
|
||||
-webkit-text-fill-color: unset;
|
||||
color: var(--vp-c-text);
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.VPHomeHero .container .main h1::first-line {
|
||||
.VPHomeHero .name::first-line {
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
/* */
|
||||
.VPHomeHero .container .image {
|
||||
margin: 0;
|
||||
order: 2;
|
||||
/* flex: 1; */
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
@@ -98,10 +78,6 @@
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.VPHomeHero .container .image-bg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.VPFeature .icon {
|
||||
background-color: var(--vp-c-bg);
|
||||
}
|
||||
@@ -115,12 +91,6 @@
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.VPHomeHero .container .main h1.name .clip {
|
||||
font-size: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.VPHomeHero .container .image {
|
||||
order: 1;
|
||||
@@ -140,18 +110,11 @@
|
||||
.VPHomeHero .container .image-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.VPHomeHero .container .main h1.name {
|
||||
}
|
||||
}
|
||||
|
||||
.VPNavBarHamburger .container > span {
|
||||
border-radius: 2px;
|
||||
}
|
||||
/*
|
||||
html:has(* .outline-link:target) {
|
||||
scroll-behavior: smooth;
|
||||
} */
|
||||
|
||||
.sp-wrapper + * {
|
||||
margin-top: 24px;
|
||||
@@ -183,7 +146,6 @@ html:has(* .outline-link:target) {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
/* box-sizing: content-box; */
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button:after {
|
||||
|
||||
@@ -6,6 +6,9 @@ export interface IconMetaData {
|
||||
categories: string[];
|
||||
contributors: string[];
|
||||
aliases?: string[];
|
||||
deprecated?: boolean;
|
||||
deprecationReason?: string;
|
||||
toBeRemovedInVersion?: string;
|
||||
}
|
||||
|
||||
export type ExternalLibs = 'lab';
|
||||
@@ -33,12 +36,19 @@ interface Shield {
|
||||
|
||||
export interface PackageItem {
|
||||
name: string;
|
||||
// set when the package's directory
|
||||
// name under the `packages/` directory
|
||||
// is diffrent from the package name
|
||||
packageDirname?: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
iconDark: string;
|
||||
shields: Shield[];
|
||||
source: string;
|
||||
documentation: string;
|
||||
// set when the docs page name is
|
||||
// diffrent from the package name
|
||||
docsAlias?: string;
|
||||
order?: number;
|
||||
private?: boolean;
|
||||
flutter?: object;
|
||||
|
||||
41
docs/.vitepress/theme/utils/useSearchPlaceholder.ts
Normal file
41
docs/.vitepress/theme/utils/useSearchPlaceholder.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ref, Ref, watch } from 'vue';
|
||||
import BRAND_STOPWORDS from '../../data/brandStopwords.json' with { type: 'json' };
|
||||
|
||||
export default function useSearchPlaceholder(
|
||||
searchQuery: Ref<string, string>,
|
||||
results: Ref<{ name: string }[]>,
|
||||
) {
|
||||
const state = ref({
|
||||
isNoResults: false,
|
||||
isBrand: false,
|
||||
query: '',
|
||||
});
|
||||
|
||||
watch(
|
||||
results,
|
||||
() => {
|
||||
const query = searchQuery.value;
|
||||
const searchResults = results.value;
|
||||
if (query.length > 0 && searchResults.length === 0) {
|
||||
for (const stopword of Object.keys(BRAND_STOPWORDS)) {
|
||||
if (stopword.startsWith(query)) {
|
||||
state.value = {
|
||||
isNoResults: true,
|
||||
isBrand: true,
|
||||
query: BRAND_STOPWORDS[stopword],
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
state.value = {
|
||||
isNoResults: query in BRAND_STOPWORDS && searchResults.length === 0 && query !== '',
|
||||
isBrand: query in BRAND_STOPWORDS,
|
||||
query: BRAND_STOPWORDS[query] ?? query,
|
||||
};
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
return state;
|
||||
}
|
||||
35
docs/.vitepress/theme/utils/useSearchShortcut.ts
Normal file
35
docs/.vitepress/theme/utils/useSearchShortcut.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
|
||||
/**
|
||||
* Composable for handling search keyboard shortcuts.
|
||||
* Listens for Cmd/Ctrl+K and "/" keys to trigger a search action.
|
||||
*
|
||||
* @param callback - Function to execute when shortcut is triggered
|
||||
* @returns Object containing the platform-specific shortcut display text
|
||||
*/
|
||||
export default function useSearchShortcut(callback: () => void) {
|
||||
const shortcutText = ref('');
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
// Check for Cmd+K (Mac), Ctrl+K (Windows/Linux), or forward slash
|
||||
if (((event.metaKey || event.ctrlKey) && event.key === 'k') || event.key === '/') {
|
||||
event.preventDefault();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// Detect platform and set appropriate keyboard shortcut for search focus
|
||||
const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
|
||||
shortcutText.value = isMac ? '⌘K' : 'Ctrl+K';
|
||||
|
||||
// Add keyboard shortcut listener
|
||||
window.addEventListener('keydown', handleKeydown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', handleKeydown);
|
||||
});
|
||||
|
||||
return { shortcutText };
|
||||
}
|
||||
@@ -139,22 +139,22 @@ contained icon.
|
||||
```tsx
|
||||
// Don't do this
|
||||
<button class="btn-icon">
|
||||
<Home/>
|
||||
<House/>
|
||||
</button>
|
||||
|
||||
// Don't do this either
|
||||
<button class="btn-icon">
|
||||
<Home aria-label="Home icon"/>
|
||||
<House aria-label="Home icon"/>
|
||||
</button>
|
||||
|
||||
// This works but might not be the best solution, see below
|
||||
<button aria-label="Go to home" class="btn-icon">
|
||||
<Home/>
|
||||
<House/>
|
||||
</button>
|
||||
|
||||
// Do this instead
|
||||
<button class="btn-icon">
|
||||
<Home/>
|
||||
<House/>
|
||||
<span class="visually-hidden">Go to home</span>
|
||||
</button>
|
||||
```
|
||||
@@ -167,6 +167,7 @@ Although you could provide accessible labels to your elements via the `aria-labe
|
||||
generally recommend avoiding this and instead suggest that you use your chosen CSS framework's "
|
||||
visually hidden" utility whenever possible. You can
|
||||
[read more about why `aria-label` might not be the best solution](https://gomakethings.com/revisting-aria-label-versus-a-visually-hidden-class/).
|
||||
|
||||
### Example - Radix UI
|
||||
|
||||
Use [Radix UI's built-in accessible icon utility component](https://www.radix-ui.com/primitives/docs/utilities/accessible-icon).
|
||||
@@ -179,6 +180,7 @@ import { AccessibleIcon } from '@radix-ui/react-accessible-icon';
|
||||
<ArrowRightIcon />
|
||||
</AccessibleIcon>
|
||||
```
|
||||
|
||||
### Example - Bootstrap
|
||||
|
||||
```html
|
||||
|
||||
@@ -7,9 +7,9 @@ Beside aliases names lucide also includes prefixed and suffixed names to use wit
|
||||
```tsx
|
||||
// These are all the same icon
|
||||
import {
|
||||
Home,
|
||||
HomeIcon,
|
||||
LucideHome,
|
||||
House,
|
||||
HouseIcon,
|
||||
LucideHouse,
|
||||
} from "lucide-react";
|
||||
```
|
||||
|
||||
|
||||
@@ -42,7 +42,11 @@ Here are rules that should be followed to keep quality and consistency when maki
|
||||
|
||||
### 8. Distinct elements must have **2 pixels of spacing between each other**
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 9. Icons should have a similar optical volume to `circle` and `square`.
|
||||
|
||||
@@ -142,7 +146,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 [SVGOMG](https://jakearchibald.github.io/svgomg/) to minify paths to 2 points of precision.
|
||||
We recommend to use [Lucide Studio](https://studio.lucide.dev/) to tidy paths to 3 points of precision.
|
||||
|
||||
### Allowed elements
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ nextPage:
|
||||
|
||||
# What is 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](/packages) to make it easier to use these icons in your project.
|
||||
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](/packages).
|
||||
|
||||
## Available Icons
|
||||
|
||||
@@ -29,7 +29,8 @@ 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/lucide-angular), [NodeJS](https://lucide.dev/guide/packages/lucide-static#nodejs) and [Flutter](https://lucide.dev/guide/packages/lucide-flutter).
|
||||
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/lucide-angular), [Astro](https://lucide.dev/guide/packages/lucide-astro), and [NodeJS](https://lucide.dev/guide/packages/lucide-static#nodejs).
|
||||
|
||||
## Community
|
||||
|
||||
If you have any questions about Lucide, feel free to reach out to the community. You can find them on [GitHub](https://github.com/lucide-icons/lucide) and [Discord](https://discord.gg/EH6nSts).
|
||||
|
||||
@@ -11,7 +11,7 @@ Implementation of the lucide icon library for web applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide
|
||||
pnpm add lucide
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -37,7 +37,7 @@ Implementation of the lucide icon library for React applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-react
|
||||
pnpm add lucide-react
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -64,7 +64,7 @@ Implementation of the lucide icon library for Vue applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-vue-next
|
||||
pnpm add lucide-vue-next
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -91,7 +91,7 @@ Implementation of the lucide icon library for Svelte applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-svelte
|
||||
pnpm add lucide-svelte
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -117,7 +117,7 @@ Implementation of the lucide icon library for Solid applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-solid
|
||||
pnpm add lucide-solid
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -143,7 +143,7 @@ Implementation of the lucide icon library for Angular applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-angular
|
||||
pnpm add lucide-angular
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -169,7 +169,7 @@ Implementation of the lucide icon library for preact applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-preact
|
||||
pnpm add lucide-preact
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -184,8 +184,37 @@ npm install lucide-preact
|
||||
bun add lucide-preact
|
||||
```
|
||||
|
||||
|
||||
:::
|
||||
|
||||
For more details, see the [documentation](packages/lucide-preact.md).
|
||||
|
||||
## Astro
|
||||
|
||||
Implementation of the lucide icon library for Astro applications.
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add @lucide/astro
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add @lucide/astro
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install @lucide/astro
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add @lucide/astro
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
For more details, see the [documentation](packages/lucide-astro.md).
|
||||
|
||||
## Static usage
|
||||
|
||||
Implementation of the lucide icon library for multiple usages that like to use: SVG files icons, SVG Sprite, Icon Fonts and static SVG strings export in Common JS modules (for NodeJS).
|
||||
@@ -193,7 +222,7 @@ Implementation of the lucide icon library for multiple usages that like to use:
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-static
|
||||
pnpm add lucide-static
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
# Lucide Angular
|
||||
|
||||
Implementation of the lucide icon library for Angular applications.
|
||||
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 install lucide-angular
|
||||
pnpm add lucide-angular
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -31,11 +38,11 @@ bun add lucide-angular
|
||||
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, Home, Menu, UserCheck } from 'lucide-angular';
|
||||
import { LucideAngularModule, File, House, Menu, UserCheck } from 'lucide-angular';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
LucideAngularModule.pick({File, Home, Menu, UserCheck})
|
||||
LucideAngularModule.pick({File, House, Menu, UserCheck})
|
||||
]
|
||||
})
|
||||
export class AppModule { }
|
||||
@@ -64,7 +71,7 @@ Within your templates you may now use one of the following component tags to ins
|
||||
|
||||
```html
|
||||
<lucide-angular name="file" class="my-icon"></lucide-angular>
|
||||
<lucide-icon name="home" class="my-icon"></lucide-icon>
|
||||
<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>
|
||||
```
|
||||
@@ -88,7 +95,7 @@ You can pass additional props to adjust the icon appearance.
|
||||
| `absoluteStrokeWidth` | *boolean* | false |
|
||||
|
||||
```html
|
||||
<i-lucide name="home" [size]="48" color="red" [strokeWidth]="1"></i-lucide>
|
||||
<i-lucide name="house" [size]="48" color="red" [strokeWidth]="1"></i-lucide>
|
||||
```
|
||||
|
||||
### Global configuration
|
||||
@@ -151,11 +158,11 @@ They can be used in the same way as the official icons.
|
||||
|
||||
```js
|
||||
import { LucideAngularModule } from 'lucide-angular';
|
||||
import { burger } from '@lucide/lab';
|
||||
import { coconut } from '@lucide/lab';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
LucideAngularModule.pick({ burger })
|
||||
LucideAngularModule.pick({ coconut })
|
||||
]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
||||
194
docs/guide/packages/lucide-astro.md
Normal file
194
docs/guide/packages/lucide-astro.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# Lucide Astro
|
||||
|
||||
Astro components for Lucide icons that work perfectly with Astro's island architecture and multi-framework support. Each icon is an Astro component that renders as an inline SVG, providing excellent performance for static sites and server-side rendering scenarios.
|
||||
|
||||
**What you can accomplish:**
|
||||
- Use icons as Astro components with zero JavaScript runtime overhead
|
||||
- Build fast, static websites with optimized SVG icons
|
||||
- Integrate seamlessly with Astro's component islands and partial hydration
|
||||
- Create multi-framework applications where icons work across different UI libraries
|
||||
- Optimize performance with direct icon imports and build-time rendering
|
||||
|
||||
## Installation
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add @lucide/astro
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add @lucide/astro
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install @lucide/astro
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add @lucide/astro
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## How to use
|
||||
|
||||
Lucide is built with ES Modules, so it's completely tree-shakable.
|
||||
|
||||
Each icon can be imported as an Astro component, which renders an inline SVG element. This way, only the icons that are imported into your project are included in the final bundle. The rest of the icons are tree-shaken away.
|
||||
|
||||
### Example
|
||||
|
||||
Default usage:
|
||||
|
||||
```astro
|
||||
---
|
||||
import { Skull } from '@lucide/astro';
|
||||
---
|
||||
|
||||
<Skull />
|
||||
```
|
||||
|
||||
Additional props can be passed to adjust the icon:
|
||||
|
||||
```astro
|
||||
---
|
||||
import { Camera } from '@lucide/astro';
|
||||
---
|
||||
|
||||
<Camera color="#ff3e98" />
|
||||
```
|
||||
|
||||
For faster builds and load times, you can import icons directly from the `@lucide/astro/icons` directory:
|
||||
|
||||
```astro
|
||||
---
|
||||
import CircleAlert from '@lucide/astro/icons/circle-alert';
|
||||
---
|
||||
|
||||
<CircleAlert color="#ff3e98" />
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| name | type | default |
|
||||
| --------------------- | --------- | ------------ |
|
||||
| `size` | _number_ | 24 |
|
||||
| `color` | _string_ | currentColor |
|
||||
| `stroke-width` | _number_ | 2 |
|
||||
| `absoluteStrokeWidth` | _boolean_ | false |
|
||||
|
||||
### Applying props
|
||||
|
||||
To customize the appearance of an icon, you can pass custom properties as props directly to the component. The component accepts all SVG attributes as props, which allows flexible styling of the SVG elements. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
|
||||
|
||||
```astro
|
||||
---
|
||||
import { Phone } from '@lucide/astro';
|
||||
---
|
||||
|
||||
<Phone fill="#333" />
|
||||
```
|
||||
|
||||
This results a filled phone icon.
|
||||
|
||||
## Types
|
||||
|
||||
The package includes type definitions for all icons. This is useful if you want to dynamically render icons.
|
||||
|
||||
### Example
|
||||
|
||||
```astro
|
||||
---
|
||||
import { House, Library, Cog, type Icon as IconType } from '@lucide/astro';
|
||||
|
||||
type MenuItem = {
|
||||
name: string;
|
||||
href: string;
|
||||
icon: typeof IconType;
|
||||
};
|
||||
|
||||
const menuItems: MenuItem[] = [
|
||||
{
|
||||
name: 'Home',
|
||||
href: '/',
|
||||
icon: House,
|
||||
},
|
||||
{
|
||||
name: 'Blog',
|
||||
href: '/blog',
|
||||
icon: Library,
|
||||
},
|
||||
{
|
||||
name: 'Projects',
|
||||
href: '/projects',
|
||||
icon: Cog,
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
{
|
||||
menuItems.map((item) => (
|
||||
<a href={item.href}>
|
||||
<item.icon />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
))
|
||||
}
|
||||
```
|
||||
|
||||
## With Lucide lab or custom icons
|
||||
|
||||
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||
|
||||
They can be used by using the `Icon` component.
|
||||
All props of the regular Lucide icons can be passed to adjust the icon appearance.
|
||||
|
||||
### Using the `Icon` component
|
||||
|
||||
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
|
||||
|
||||
```astro
|
||||
---
|
||||
import { Icon } from '@lucide/astro';
|
||||
import { burger, sausage } from '@lucide/lab';
|
||||
---
|
||||
|
||||
<Icon iconNode={burger} />
|
||||
<Icon iconNode={sausage} color="red"/>
|
||||
```
|
||||
|
||||
## One generic icon component
|
||||
|
||||
It is possible to create one generic icon component to load icons, but it is not recommended.
|
||||
|
||||
::: danger
|
||||
The example below imports all ES Modules, so exercise caution when using it. Importing all icons will significantly increase the build size of the application. This may be passable if you're doing SSG and SSR in server environments. However if you're doing SSR in serverless environments, it could negatively affect your app's performance, as a bigger bundle size will translate to an increase in cold starts.
|
||||
:::
|
||||
|
||||
### Icon Component Example
|
||||
|
||||
```astro
|
||||
---
|
||||
import { icons, type IconProps } from '@lucide/astro';
|
||||
|
||||
interface Props extends IconProps {
|
||||
name: keyof typeof icons;
|
||||
}
|
||||
|
||||
const { name, ...restProps } = Astro.props;
|
||||
const Icon = icons[name];
|
||||
---
|
||||
|
||||
<Icon {...restProps} />
|
||||
```
|
||||
|
||||
### Using the Icon Component
|
||||
|
||||
```astro
|
||||
---
|
||||
import LucideIcon from './LucideIcon.astro';
|
||||
---
|
||||
|
||||
<LucideIcon name="Menu" />
|
||||
```
|
||||
@@ -4,14 +4,21 @@ title: Lucide Preact
|
||||
|
||||
# Lucide Preact
|
||||
|
||||
Implementation of the lucide icon library for preact applications.
|
||||
Preact components for Lucide icons that provide React-like development experience with a smaller footprint. Each icon is a lightweight Preact component that renders as an inline SVG, perfect for applications that need React compatibility with minimal bundle size.
|
||||
|
||||
**What you can accomplish:**
|
||||
- Use icons as Preact components with React-like syntax and patterns
|
||||
- Build lightweight applications with Preact's smaller runtime
|
||||
- Create fast, responsive interfaces with minimal JavaScript overhead
|
||||
- Maintain React compatibility while reducing bundle size
|
||||
- Integrate with existing Preact applications and component libraries
|
||||
|
||||
## Installation
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-preact
|
||||
pnpm add lucide-preact
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -84,10 +91,10 @@ This creates a single icon based on the iconNode passed and renders a Lucide ico
|
||||
|
||||
```jsx
|
||||
import { Icon } from 'lucide-preact';
|
||||
import { burger } from '@lucide/lab';
|
||||
import { coconut } from '@lucide/lab';
|
||||
|
||||
const App = () => (
|
||||
<Icon iconNode={burger} />
|
||||
<Icon iconNode={coconut} />
|
||||
);
|
||||
```
|
||||
|
||||
@@ -119,7 +126,7 @@ export default Icon;
|
||||
import Icon from './Icon';
|
||||
|
||||
const App = () => {
|
||||
return <Icon name="home" />;
|
||||
return <Icon name="house" />;
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
# Lucide React Native
|
||||
|
||||
Implementation of the lucide icon library for React Native applications
|
||||
React Native components for Lucide icons that work seamlessly across iOS and Android platforms. Built on top of react-native-svg, each icon renders as a native SVG component, providing consistent visual appearance and performance across mobile devices.
|
||||
|
||||
**What you can accomplish:**
|
||||
- Use icons as React Native components with platform-consistent rendering
|
||||
- Build cross-platform mobile apps with scalable vector icons
|
||||
- Create responsive interfaces that adapt to different screen densities
|
||||
- Integrate with React Native's styling system and animation libraries
|
||||
- Maintain consistent icon appearance across iOS and Android platforms
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -9,7 +16,7 @@ First, ensure that you have `react-native-svg` (version between 12 and 15) insta
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-react-native
|
||||
pnpm add lucide-react-native
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -78,10 +85,10 @@ This creates a single icon based on the iconNode passed and renders a Lucide ico
|
||||
|
||||
```jsx
|
||||
import { Icon } from 'lucide-react-native';
|
||||
import { burger } from '@lucide/lab';
|
||||
import { coconut } from '@lucide/lab';
|
||||
|
||||
const App = () => (
|
||||
<Icon iconNode={burger} />
|
||||
<Icon iconNode={coconut} />
|
||||
);
|
||||
```
|
||||
|
||||
@@ -113,7 +120,7 @@ export default Icon;
|
||||
import Icon from './Icon';
|
||||
|
||||
const App = () => {
|
||||
return <Icon name="home" />;
|
||||
return <Icon name="house" />;
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
# Lucide React
|
||||
|
||||
Implementation of the lucide icon library for react applications
|
||||
React components for Lucide icons that integrate seamlessly into your React applications. Each icon is a fully-typed React component that renders as an optimized inline SVG, giving you the flexibility of components with the performance of vector graphics.
|
||||
|
||||
**What you can accomplish:**
|
||||
- Import icons as React components with full TypeScript support
|
||||
- Pass props to customize size, color, stroke width, and other SVG attributes
|
||||
- Use icons in JSX with the same ease as any other React component
|
||||
- Benefit from automatic tree-shaking to include only the icons you use
|
||||
- Create dynamic icon components that respond to state and user interactions
|
||||
|
||||
## Installation
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-react
|
||||
pnpm add lucide-react
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -78,17 +85,16 @@ This creates a single icon based on the iconNode passed and renders a Lucide ico
|
||||
|
||||
```jsx
|
||||
import { Icon } from 'lucide-react';
|
||||
import { burger } from '@lucide/lab';
|
||||
import { coconut } from '@lucide/lab';
|
||||
|
||||
const App = () => (
|
||||
<Icon iconNode={burger} />
|
||||
<Icon iconNode={coconut} />
|
||||
);
|
||||
```
|
||||
|
||||
## Dynamic Icon Component
|
||||
|
||||
It is possible to create one generic icon component to load icons, but it is not recommended.
|
||||
Since it is importing all icons during build. This increases build time and the different modules it will create.
|
||||
It is possible to create one generic icon component to load icons. But it is not recommended, since it is importing all icons during the build. This increases build time and the different modules it will create.
|
||||
|
||||
`DynamicIcon` is useful for applications that want to show icons dynamically by icon name. For example, when using a content management system with where icon names are stored in a database.
|
||||
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
# Lucide Solid
|
||||
|
||||
Implementation of the lucide icon library for solid applications.
|
||||
SolidJS components for Lucide icons that leverage Solid's fine-grained reactivity system. Each icon is a reactive Solid component that renders as an inline SVG, providing exceptional performance through Solid's compile-time optimizations and reactive primitives.
|
||||
|
||||
**What you can accomplish:**
|
||||
- Use icons as SolidJS components with fine-grained reactivity
|
||||
- Create highly performant interfaces with Solid's reactive system
|
||||
- Build dynamic icon components that respond to signals and stores
|
||||
- Integrate seamlessly with Solid's JSX and component patterns
|
||||
- Optimize performance with direct icon imports and minimal runtime overhead
|
||||
|
||||
## Installation
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-solid
|
||||
pnpm add lucide-solid
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -91,7 +98,7 @@ This creates a single icon based on the iconNode passed and renders a Lucide ico
|
||||
|
||||
```jsx
|
||||
import { Icon } from 'lucide-solid';
|
||||
import { burger, sausage } from '@lucide/lab';
|
||||
import { sausage } from '@lucide/lab';
|
||||
|
||||
const App = () => (
|
||||
<Icon iconNode={sausage} color="red"/>
|
||||
|
||||
@@ -1,28 +1,51 @@
|
||||
# Lucide Static
|
||||
|
||||
This package include the following lucide implementations:
|
||||
Static assets and utilities for Lucide icons that work without JavaScript frameworks. This package provides multiple formats including individual SVG files, SVG sprites, icon fonts, and Node.js utilities for server-side rendering and static site generation.
|
||||
|
||||
- All SVG files
|
||||
**What you can accomplish:**
|
||||
- Use individual SVG files as images or CSS background images
|
||||
- Implement icon fonts for CSS-based icon systems
|
||||
- Create SVG sprites for efficient icon loading in static sites
|
||||
- Import SVG strings in Node.js applications and server-side rendering
|
||||
- Build static websites and applications without JavaScript framework dependencies
|
||||
|
||||
This package includes the following implementations of Lucide icons:
|
||||
|
||||
- Individual SVG files
|
||||
- SVG sprite
|
||||
- Icon fonts
|
||||
- JavaScript library containing strings of SVGs.
|
||||
- Icon font files
|
||||
- A JavaScript library exporting SVG strings
|
||||
|
||||
## Why lucide-static?
|
||||
## Who is this for?
|
||||
|
||||
This package is suitable for specific use cases, for example if you want to use icon fonts, SVG sprites, normal SVGs or Common.js SVG strings in your javascript project.
|
||||
`lucide-static` is suitable for _very specific use cases_ where you want to use Lucide icons without relying on a JavaScript framework or component system. It's ideal for:
|
||||
|
||||
::: warning
|
||||
While they can be useful for prototyping, it is not recommended to use the SVG sprites or icon fonts provided by this package in production web apps as all the available icons are included in the app, hence increasing loading time and data usage. We recommend to use a bundler and tree-shaking to make sure only the icons you use are bundled with your app. Tree-shaking is only available in these packages: [lucide](lucide), [lucide-react](lucide-react), [lucide-vue](lucide-vue), [lucide-vue-next](lucide-vue-next), [lucide-angular](lucide-angular), [lucide-preact](lucide-preact)
|
||||
- Projects that use icon fonts with plain CSS or utility-first frameworks
|
||||
- Embedding raw SVG files or sprites directly in HTML
|
||||
- Using SVGs as CSS background images
|
||||
- Importing SVG strings into Node.js (CommonJS) environments
|
||||
|
||||
::: danger
|
||||
### Not recommended for production {#production-warning}
|
||||
|
||||
SVG sprites and icon fonts include **all icons**, which can significantly increase your app's bundle size and load time.
|
||||
|
||||
For production environments, we recommend using a bundler with tree-shaking support to include only the icons you actually use. Consider using:
|
||||
|
||||
- [lucide](lucide)
|
||||
- [lucide-react](lucide-react)
|
||||
- [lucide-vue](lucide-vue)
|
||||
- [lucide-vue-next](lucide-vue-next)
|
||||
- [lucide-angular](lucide-angular)
|
||||
- [lucide-preact](lucide-preact)
|
||||
:::
|
||||
|
||||
## Installation
|
||||
|
||||
## Package Managers
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-static
|
||||
pnpm add lucide-static
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -39,64 +62,86 @@ bun add lucide-static
|
||||
|
||||
:::
|
||||
|
||||
### CDN
|
||||
## SVG Files
|
||||
|
||||
```html
|
||||
<!-- SVG file for a single icon -->
|
||||
<img src="https://unpkg.com/lucide-static@latest/icons/house.svg" />
|
||||
You can use standalone SVG files or SVG sprites in several ways.
|
||||
|
||||
<!-- Icon font -->
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'LucideIcons';
|
||||
src: url(https://unpkg.com/lucide-static@latest/font/Lucide.ttf) format('truetype');
|
||||
}
|
||||
</style>
|
||||
```
|
||||
Check out our [codesandbox example](https://codesandbox.io/s/using-the-svg-sprite-lz1kk).
|
||||
|
||||
## Usage
|
||||
### SVG file as image
|
||||
|
||||
Check out the [codesandbox examples](https://codesandbox.io/s/using-the-svg-sprite-lz1kk).
|
||||
#### In HTML:
|
||||
|
||||
### SVG Files
|
||||
::: code-group
|
||||
|
||||
#### SVG file as image
|
||||
|
||||
To use it in for example html:
|
||||
|
||||
```html
|
||||
```html [Webpack]
|
||||
<!-- SVG file for a single icon -->
|
||||
<img src="~lucide-static/icons/house.svg" />
|
||||
```
|
||||
|
||||
```css
|
||||
```html [CDN]
|
||||
<!-- SVG file for a single icon -->
|
||||
<img src="https://unpkg.com/lucide-static@latest/icons/house.svg" />
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
#### In CSS:
|
||||
|
||||
::: code-group
|
||||
|
||||
```css [Webpack]
|
||||
.house-icon {
|
||||
background-image: url(~lucide-static/icons/house.svg);
|
||||
}
|
||||
```
|
||||
|
||||
Make sure you have the correct webpack loaders to make this work. [url-loader](https://v4.webpack.js.org/loaders/url-loader/)
|
||||
|
||||
#### SVG file as string
|
||||
|
||||
You can simply import each SVG by targeting `lucide-static/icons/{icon-name}.svg`.
|
||||
To use SVGs in your project you can for example use a [SVG loader](https://v4.webpack.js.org/loaders/svg-inline-loader/).
|
||||
|
||||
```js
|
||||
import arrowRightIcon from 'lucide-static/icons/arrow-right';
|
||||
|
||||
// return string of an SVG
|
||||
```css [CDN]
|
||||
.house-icon {
|
||||
background-image: url(https://unpkg.com/lucide-static@latest/icons/house.svg);
|
||||
}
|
||||
```
|
||||
|
||||
### SVG Sprite
|
||||
:::
|
||||
|
||||
You may need additional loader for this.
|
||||
Make sure you have the correct Webpack loader configured, such as [`url-loader`](https://v4.webpack.js.org/loaders/url-loader/).
|
||||
|
||||
### SVG file as string
|
||||
|
||||
To import an SVG as a string (e.g., for templating):
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [Webpack]
|
||||
import arrowRightIcon from 'lucide-static/icons/arrow-right';
|
||||
```
|
||||
|
||||
```js [Vite]
|
||||
import arrowRightIcon from 'lucide-static/icons/arrow-right.svg?raw';
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
You'll need an SVG loader like [`svg-inline-loader`](https://v4.webpack.js.org/loaders/svg-inline-loader/).
|
||||
|
||||
|
||||
### Using the SVG sprite
|
||||
|
||||
:::danger
|
||||
[Not intended for production use.](#production-warning)
|
||||
:::
|
||||
|
||||
You may also need an additional SVG loader to handle this.
|
||||
|
||||
#### Basic sprite usage (not production-optimized):
|
||||
|
||||
```html
|
||||
<!-- Icon Sprite, not recommended for production! -->
|
||||
<img src="lucide-static/sprite.svg#house" />
|
||||
```
|
||||
|
||||
<!-- or -->
|
||||
#### Inline usage:
|
||||
|
||||
```html
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
@@ -109,12 +154,13 @@ You may need additional loader for this.
|
||||
<use href="#alert-triangle" />
|
||||
</svg>
|
||||
|
||||
<svg>
|
||||
...sprite svg
|
||||
</svg>
|
||||
<!-- sprite SVG -->
|
||||
<svg>...</svg>
|
||||
```
|
||||
|
||||
If you'd prefer, you can use CSS to hold your base SVG properties
|
||||
#### Inline with CSS helper class
|
||||
|
||||
If you'd prefer, you can use CSS to hold your base SVG properties:
|
||||
|
||||
```css
|
||||
.lucide-icon {
|
||||
@@ -128,47 +174,79 @@ If you'd prefer, you can use CSS to hold your base SVG properties
|
||||
}
|
||||
```
|
||||
|
||||
and update the SVG as follows
|
||||
...and update the SVG as follows:
|
||||
|
||||
```xml
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="lucide-icon"
|
||||
>
|
||||
<use
|
||||
href="#alert-triangle"
|
||||
/>
|
||||
</svg>
|
||||
<svg>
|
||||
...sprite svg
|
||||
<use href="#triangle-alert" />
|
||||
</svg>
|
||||
|
||||
<!-- sprite SVG -->
|
||||
<svg>...</svg>
|
||||
```
|
||||
|
||||
### Icon Font
|
||||
## Icon font
|
||||
|
||||
```css
|
||||
:::danger
|
||||
[Not intended for production use.](#production-warning)
|
||||
:::
|
||||
|
||||
Lucide icons are also available as a web font. To use them, you first need to include the corresponding stylesheet:
|
||||
|
||||
::: code-group
|
||||
|
||||
```css [Vite]
|
||||
@import 'lucide-static/font/lucide.css';
|
||||
```
|
||||
|
||||
```css [Webpack]
|
||||
@import ('~lucide-static/font/lucide.css');
|
||||
```
|
||||
|
||||
```html [CDN]
|
||||
<link rel="stylesheet" href="https://unpkg.com/lucide-static@latest/font/lucide.css" />
|
||||
```
|
||||
|
||||
```html [Static asset]
|
||||
<link rel="stylesheet" href="/your/path/to/lucide.css" />
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Once included, use the format `icon-{kebab-case-name}`. You can copy icon names from the [Lucide Icons page](https://lucide.dev/icons).
|
||||
|
||||
```html
|
||||
<div class="icon-house"></div>
|
||||
```
|
||||
|
||||
### Node.js
|
||||
If you're not using a package manager, you can download the font files directly from the [latest release](https://github.com/lucide-icons/lucide/releases/latest).
|
||||
|
||||
To use lucide icons in your Nodejs project you can import each icon as:
|
||||
## Node.js
|
||||
|
||||
```js
|
||||
const { messageSquare } = require('lucide-static/lib');
|
||||
You can also import Lucide icons in Node.js projects:
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [ESM]
|
||||
import {MessageSquare} from 'lucide-static';
|
||||
```
|
||||
|
||||
> Note: Each icon name is in camelCase.
|
||||
```js [CommonJs]
|
||||
const {MessageSquare} = require('lucide-static');
|
||||
```
|
||||
|
||||
#### Example in node.js project
|
||||
:::
|
||||
|
||||
> Note: Each icon name is in PascalCase.
|
||||
|
||||
#### Express app example in Node.js
|
||||
|
||||
```js
|
||||
const express = require('express');
|
||||
const { messageSquare } = require('lucide-static/lib');
|
||||
import express from 'express';
|
||||
import {MessageSquare} from 'lucide-static';
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
@@ -181,7 +259,7 @@ app.get('/', (req, res) => {
|
||||
</head>
|
||||
<body>
|
||||
<h1>Lucide Icons</h1>
|
||||
<p>This is a lucide icon ${messageSquare}</p>
|
||||
<p>This is a Lucide icon ${MessageSquare}</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
# Lucide Svelte
|
||||
|
||||
Implementation of the lucide icon library for svelte applications.
|
||||
Svelte components for Lucide icons that work seamlessly with both Svelte 4 and Svelte 5. Each icon is a reactive Svelte component that renders as an inline SVG, providing excellent performance and integration with Svelte's reactive system and modern features.
|
||||
|
||||
**What you can accomplish:**
|
||||
- Use icons as Svelte components with full reactivity and TypeScript support
|
||||
- Bind icon properties to reactive variables and stores
|
||||
- Create dynamic icon systems that respond to application state
|
||||
- Build type-safe interfaces with comprehensive TypeScript definitions
|
||||
- Optimize bundle sizes with direct icon imports and tree-shaking
|
||||
|
||||
## Installation
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm add lucide-svelte
|
||||
pnpm add @lucide/svelte
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add lucide-svelte
|
||||
yarn add @lucide/svelte
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install lucide-svelte
|
||||
npm install @lucide/svelte
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add lucide-svelte
|
||||
bun add @lucide/svelte
|
||||
```
|
||||
|
||||
:::
|
||||
> `@lucide/svelte` is only for Svelte 5, for Svelte 4 use the `lucide-svelte` package.
|
||||
|
||||
## How to use
|
||||
|
||||
@@ -36,7 +43,7 @@ Default usage:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Skull } from 'lucide-svelte';
|
||||
import { Skull } from '@lucide/svelte';
|
||||
</script>
|
||||
|
||||
<Skull />
|
||||
@@ -46,17 +53,17 @@ Additional props can be passed to adjust the icon:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Camera } from 'lucide-svelte';
|
||||
import { Camera } from '@lucide/svelte';
|
||||
</script>
|
||||
|
||||
<Camera color="#ff3e98" />
|
||||
```
|
||||
|
||||
For faster builds and load times, you can import icons directly from the `lucide-svelte/icons` directory:
|
||||
For faster builds and load times, you can import icons directly from the `@lucide/svelte/icons` directory:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import CircleAlert from 'lucide-svelte/icons/circle-alert';
|
||||
import CircleAlert from '@lucide/svelte/icons/circle-alert';
|
||||
</script>
|
||||
|
||||
<CircleAlert color="#ff3e98" />
|
||||
@@ -77,7 +84,7 @@ To customize the appearance of an icon, you can pass custom properties as props
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Phone } from 'lucide-svelte';
|
||||
import { Phone } from '@lucide/svelte';
|
||||
</script>
|
||||
|
||||
<Phone fill="#333" />
|
||||
@@ -91,101 +98,140 @@ The package includes type definitions for all icons. This is useful if you want
|
||||
|
||||
### TypeScript Example
|
||||
|
||||
#### Svelte 4
|
||||
::: code-group
|
||||
|
||||
```svelte
|
||||
```svelte [Svelte 5]
|
||||
<script lang="ts">
|
||||
import { Home, Library, Cog, type Icon } from 'lucide-svelte';
|
||||
import type { ComponentType } from 'svelte';
|
||||
import { Home, Library, Cog, type Icon as IconType } from '@lucide/svelte';
|
||||
|
||||
type MenuItem = {
|
||||
name: string;
|
||||
href: string;
|
||||
icon: ComponentType<Icon>;
|
||||
};
|
||||
type MenuItem = {
|
||||
name: string;
|
||||
href: string;
|
||||
icon: typeof IconType;
|
||||
};
|
||||
|
||||
const menuItems: MenuItem[] = [
|
||||
{
|
||||
name: 'Home',
|
||||
href: '/',
|
||||
icon: Home
|
||||
},
|
||||
{
|
||||
name: 'Blog',
|
||||
href: '/blog',
|
||||
icon: Library
|
||||
},
|
||||
{
|
||||
name: 'Projects',
|
||||
href: '/projects',
|
||||
icon: Cog
|
||||
}
|
||||
];
|
||||
const menuItems: MenuItem[] = [
|
||||
{
|
||||
name: 'Home',
|
||||
href: '/',
|
||||
icon: Home
|
||||
},
|
||||
{
|
||||
name: 'Blog',
|
||||
href: '/blog',
|
||||
icon: Library
|
||||
},
|
||||
{
|
||||
name: 'Projects',
|
||||
href: '/projects',
|
||||
icon: Cog
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
{#each menuItems as item}
|
||||
<a href={item.href}>
|
||||
<svelte:component this={item.icon} />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
{@const Icon = item.icon}
|
||||
<a href={item.href}>
|
||||
<Icon />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
{/each}
|
||||
```
|
||||
|
||||
|
||||
#### Svelte 5
|
||||
Some changes are required since Svelte 5 [deprecates](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes-Component-typing-changes) the `ComponentType` typescript type.
|
||||
|
||||
```svelte
|
||||
```svelte [Svelte 4]
|
||||
<script lang="ts">
|
||||
import { Home, Library, Cog, type Icon as IconType } from 'lucide-svelte';
|
||||
import { Home, Library, Cog, type Icon } from '@lucide/svelte';
|
||||
import type { ComponentType } from 'svelte';
|
||||
|
||||
type MenuItem = {
|
||||
name: string;
|
||||
href: string;
|
||||
icon: typeof IconType;
|
||||
};
|
||||
type MenuItem = {
|
||||
name: string;
|
||||
href: string;
|
||||
icon: ComponentType<Icon>;
|
||||
};
|
||||
|
||||
const menuItems: MenuItem[] = [
|
||||
{
|
||||
name: 'Home',
|
||||
href: '/',
|
||||
icon: Home
|
||||
},
|
||||
{
|
||||
name: 'Blog',
|
||||
href: '/blog',
|
||||
icon: Library
|
||||
},
|
||||
{
|
||||
name: 'Projects',
|
||||
href: '/projects',
|
||||
icon: Cog
|
||||
}
|
||||
];
|
||||
const menuItems: MenuItem[] = [
|
||||
{
|
||||
name: 'Home',
|
||||
href: '/',
|
||||
icon: Home
|
||||
},
|
||||
{
|
||||
name: 'Blog',
|
||||
href: '/blog',
|
||||
icon: Library
|
||||
},
|
||||
{
|
||||
name: 'Projects',
|
||||
href: '/projects',
|
||||
icon: Cog
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
{#each menuItems as item}
|
||||
{@const Icon = item.icon}
|
||||
<a href={item.href}>
|
||||
<Icon />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
{@const Icon = item.icon}
|
||||
<a href={item.href}>
|
||||
<Icon />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
{/each}
|
||||
|
||||
```
|
||||
:::
|
||||
|
||||
### JSDoc Example
|
||||
|
||||
#### Svelte 4
|
||||
::: code-group
|
||||
|
||||
```svelte
|
||||
```svelte [Svelte 5]
|
||||
<script>
|
||||
import { Home, Library, Cog } from 'lucide-svelte';
|
||||
import { Home, Library, Cog } from '@lucide/svelte';
|
||||
|
||||
/**
|
||||
* @typedef {Object} MenuItem
|
||||
* @property {string} name
|
||||
* @property {string} href
|
||||
* @property {import('svelte').ComponentType<import('lucide-svelte').Icon>} icon
|
||||
* @property {typeof import('@lucide/svelte').Icon} icon
|
||||
*/
|
||||
|
||||
/** @type {MenuItem[]} */
|
||||
const menuItems = [
|
||||
{
|
||||
name: 'Home',
|
||||
href: '/',
|
||||
icon: Home
|
||||
},
|
||||
{
|
||||
name: 'Blog',
|
||||
href: '/blog',
|
||||
icon: Library
|
||||
},
|
||||
{
|
||||
name: 'Projects',
|
||||
href: '/projects',
|
||||
icon: Cog
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
{#each menuItems as item}
|
||||
{@const Icon = item.icon}
|
||||
<a href={item.href}>
|
||||
<Icon />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
{/each}
|
||||
```
|
||||
|
||||
```svelte [Svelte 4]
|
||||
<script>
|
||||
import { Home, Library, Cog } from '@lucide/svelte';
|
||||
|
||||
/**
|
||||
* @typedef {Object} MenuItem
|
||||
* @property {string} name
|
||||
* @property {string} href
|
||||
* @property {import('svelte').ComponentType<import('@lucide/svelte').Icon>} icon
|
||||
*/
|
||||
|
||||
/** @type {MenuItem[]} */
|
||||
@@ -209,56 +255,15 @@ Some changes are required since Svelte 5 [deprecates](https://svelte.dev/docs/sv
|
||||
</script>
|
||||
|
||||
{#each menuItems as item}
|
||||
{@const Icon = item.icon}
|
||||
<a href={item.href}>
|
||||
<svelte:component this={item.icon} />
|
||||
<Icon />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
{/each}
|
||||
```
|
||||
|
||||
|
||||
#### Svelte 5
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Home, Library, Cog } from 'lucide-svelte';
|
||||
|
||||
/**
|
||||
* @typedef {Object} MenuItem
|
||||
* @property {string} name
|
||||
* @property {string} href
|
||||
* @property {typeof import('lucide-svelte').Icon} icon
|
||||
*/
|
||||
|
||||
/** @type {MenuItem[]} */
|
||||
const menuItems = [
|
||||
{
|
||||
name: 'Home',
|
||||
href: '/',
|
||||
icon: Home
|
||||
},
|
||||
{
|
||||
name: 'Blog',
|
||||
href: '/blog',
|
||||
icon: Library
|
||||
},
|
||||
{
|
||||
name: 'Projects',
|
||||
href: '/projects',
|
||||
icon: Cog
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
{#each menuItems as item}
|
||||
{@const Icon = item.icon}
|
||||
<a href={item.href}>
|
||||
<Icon />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
{/each}
|
||||
|
||||
```
|
||||
:::
|
||||
|
||||
For more details about typing the `svelte:component` directive, see the [Svelte documentation](https://svelte.dev/docs/typescript#types-componenttype).
|
||||
|
||||
@@ -275,11 +280,11 @@ This creates a single icon based on the iconNode passed and renders a Lucide ico
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Icon } from 'lucide-svelte';
|
||||
import { burger, sausage } from '@lucide/lab';
|
||||
import { Icon } from '@lucide/svelte';
|
||||
import { pear, sausage } from '@lucide/lab';
|
||||
</script>
|
||||
|
||||
<Icon iconNode={burger} />
|
||||
<Icon iconNode={pear} />
|
||||
<Icon iconNode={sausage} color="red"/>
|
||||
```
|
||||
|
||||
@@ -293,29 +298,29 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
|
||||
|
||||
### Icon Component Example
|
||||
|
||||
#### Svelte 4
|
||||
::: code-group
|
||||
|
||||
```svelte
|
||||
```svelte [Svelte 5]
|
||||
<script>
|
||||
import * as icons from 'lucide-svelte';
|
||||
import * as icons from '@lucide/svelte';
|
||||
let { name, ...props } = $props();
|
||||
|
||||
const Icon = icons[name];
|
||||
</script>
|
||||
|
||||
<Icon {...props} />
|
||||
```
|
||||
|
||||
```svelte [Svelte 4]
|
||||
<script>
|
||||
import * as icons from '@lucide/svelte';
|
||||
export let name;
|
||||
</script>
|
||||
|
||||
<svelte:component this={icons[name]} {...$$props} />
|
||||
```
|
||||
|
||||
#### Svelte 5
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import * as icons from 'lucide-svelte';
|
||||
let { name } = $props();
|
||||
|
||||
const Icon = icons[name];
|
||||
</script>
|
||||
|
||||
<Icon {...props} />
|
||||
```
|
||||
:::
|
||||
|
||||
#### Using the Icon Component
|
||||
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
# Lucide Vue Next
|
||||
|
||||
Implementation of the lucide icon library for Vue 3 applications.
|
||||
Vue 3 components for Lucide icons that leverage the Composition API and modern Vue features. Each icon is a reactive Vue component that renders as an inline SVG, providing excellent performance and developer experience in Vue 3 applications.
|
||||
|
||||
**What you can accomplish:**
|
||||
- Use icons as Vue 3 components with full reactivity and TypeScript support
|
||||
- Bind icon properties to reactive data and computed values
|
||||
- Customize icons with props, slots, and Vue's powerful templating system
|
||||
- Integrate seamlessly with Vue 3's Composition API and script setup syntax
|
||||
- Build dynamic interfaces where icons respond to application state changes
|
||||
|
||||
## Installation
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-vue-next
|
||||
pnpm add lucide-vue-next
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -54,7 +61,7 @@ import { Camera } from 'lucide-vue-next';
|
||||
| `size` | *number* | 24 |
|
||||
| `color` | *string* | currentColor |
|
||||
| `stroke-width` | *number* | 2 |
|
||||
| `absolute-stroke-width` | *boolean* | false |
|
||||
| `absoluteStrokeWidth` | *boolean* | false |
|
||||
| `default-class` | *string* | lucide-icon |
|
||||
|
||||
### Applying props
|
||||
@@ -81,11 +88,11 @@ This creates a single icon based on the iconNode passed and renders a Lucide ico
|
||||
```vue
|
||||
<script setup>
|
||||
import { Icon } from 'lucide-vue-next';
|
||||
import { burger } from '@lucide/lab';
|
||||
import { baseball } from '@lucide/lab';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Icon :iconNode="burger" />
|
||||
<Icon :iconNode="baseball" />
|
||||
</template>
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
# Lucide Vue
|
||||
|
||||
Implementation of the lucide icon library for Vue applications.
|
||||
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
|
||||
- Build applications using Vue 2's familiar syntax and patterns
|
||||
- Bridge the gap while planning migration to Vue 3
|
||||
|
||||
::: danger
|
||||
This package is deprecated. Vue 2 is EOF See [Announcement](https://v2.vuejs.org/eol/). Migrate to Vue 3.
|
||||
@@ -11,7 +18,7 @@ This package is deprecated. Vue 2 is EOF See [Announcement](https://v2.vuejs.or
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-vue
|
||||
pnpm add lucide-vue
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -60,7 +67,7 @@ Additional props can be passed to adjust the icon:
|
||||
| `size` | *number* | 24 |
|
||||
| `color` | *string* | currentColor |
|
||||
| `stroke-width` | *number* | 2 |
|
||||
| `absolute-stroke-width` | *boolean* | false |
|
||||
| `absoluteStrokeWidth` | *boolean* | false |
|
||||
| `default-class` | *string* | lucide-icon |
|
||||
|
||||
### Applying props
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
# Lucide
|
||||
|
||||
Implementation of the lucide icon library for web applications.
|
||||
The core Lucide package for vanilla JavaScript applications. This package allows you to easily add scalable vector icons to any web project without framework dependencies. Perfect for static websites, legacy applications, or when you need lightweight icon integration with maximum browser compatibility.
|
||||
|
||||
**What you can accomplish:**
|
||||
- Add icons to HTML using simple data attributes
|
||||
- Dynamically create and insert SVG icons with JavaScript
|
||||
- Customize icon appearance with CSS classes and inline styles
|
||||
- Tree-shake unused icons to keep bundle sizes minimal
|
||||
- Use icons in any JavaScript environment or plain HTML
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -9,7 +16,7 @@ Implementation of the lucide icon library for web applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide
|
||||
pnpm add lucide
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -88,7 +95,12 @@ createIcons({
|
||||
|
||||
### Additional Options
|
||||
|
||||
In the `createIcons` function you can pass some extra parameters to adjust the `nameAttr` or add custom attributes like for example classes.
|
||||
In the `createIcons` function you can pass some extra parameters:
|
||||
|
||||
- you can pass `nameAttr` to adjust the attribute name to replace for
|
||||
- you can pass `attrs` to pass additional custom attributes, for instance CSS classes or stroke options.
|
||||
- you can pass `root` to provide a custom DOM element the icons should be replaced in (useful when manipulating small sections of a large DOM or elements in the shadow DOM)
|
||||
- you can pass `inTemplates: true` to also replace icons inside `<template>` tags.
|
||||
|
||||
Here is a full example:
|
||||
|
||||
@@ -101,7 +113,9 @@ createIcons({
|
||||
'stroke-width': 1,
|
||||
stroke: '#333'
|
||||
},
|
||||
nameAttr: 'data-lucide' // attribute for the icon name.
|
||||
nameAttr: 'data-lucide', // attribute for the icon name.
|
||||
root: element, // DOM element to replace icons in.
|
||||
inTemplates: true // Also replace icons inside <template> tags.
|
||||
});
|
||||
```
|
||||
|
||||
@@ -119,6 +133,34 @@ createIcons({
|
||||
});
|
||||
```
|
||||
|
||||
### Custom Document root
|
||||
|
||||
Apply icons in a custom root element, for instance a shadow DOM root.
|
||||
|
||||
```js
|
||||
import { createIcons } from 'lucide';
|
||||
|
||||
// Custom root element, for instance a shadow DOM root.
|
||||
const shadowRoot = element.attachShadow({ mode: 'open' });
|
||||
|
||||
createIcons({
|
||||
root: shadowRoot
|
||||
});
|
||||
```
|
||||
|
||||
### Apply icons inside `<template>` tags
|
||||
|
||||
By default icons inside `<template>` tags are not added.
|
||||
By setting the `inTemplates` option to `true`, icons inside templates will also be replaced.
|
||||
|
||||
```js
|
||||
import { createIcons } from 'lucide';
|
||||
|
||||
createIcons({
|
||||
inTemplates: true
|
||||
});
|
||||
```
|
||||
|
||||
### Custom Element binding
|
||||
|
||||
```js
|
||||
@@ -126,11 +168,23 @@ import { createElement, Menu } from 'lucide';
|
||||
|
||||
const menuIcon = createElement(Menu); // Returns HTMLElement (svg)
|
||||
|
||||
// set custom attributes with browser native functions
|
||||
menuIcon.setAttribute('stroke', '#333');
|
||||
menuIcon.classList.add('my-icon-class');
|
||||
// Append HTMLElement in the DOM
|
||||
const myApp = document.getElementById('app');
|
||||
myApp.appendChild(menuIcon);
|
||||
```
|
||||
|
||||
// Append HTMLElement in webpage
|
||||
#### Custom Element binding with custom attributes
|
||||
|
||||
```js
|
||||
import { createElement, Menu } from 'lucide';
|
||||
|
||||
const menuIcon = createElement(Menu, {
|
||||
class: ['my-custom-class', 'icon'],
|
||||
'stroke-width': 1,
|
||||
stroke: '#333'
|
||||
}); // Returns HTMLElement (svg)
|
||||
|
||||
// Append HTMLElement in the DOM
|
||||
const myApp = document.getElementById('app');
|
||||
myApp.appendChild(menuIcon);
|
||||
```
|
||||
@@ -141,11 +195,11 @@ myApp.appendChild(menuIcon);
|
||||
They can be used in the same way as the official icons.
|
||||
|
||||
```js
|
||||
import { burger } from '@lucide/lab';
|
||||
import { coconut } from '@lucide/lab';
|
||||
|
||||
createIcons({
|
||||
icons: {
|
||||
burger
|
||||
coconut
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user