mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-17 16:17:43 +01:00
Compare commits
136 Commits
remove-umd
...
update-sit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
797cf7667d | ||
|
|
979e07fbf9 | ||
|
|
2f5f78c1c2 | ||
|
|
634322e96c | ||
|
|
53ebc4c076 | ||
|
|
7d023209f0 | ||
|
|
d58a2e43c6 | ||
|
|
5ecf78bb8a | ||
|
|
f78061b488 | ||
|
|
aa8f74eb9e | ||
|
|
7327637532 | ||
|
|
076e0bbcd9 | ||
|
|
33bb95edcd | ||
|
|
4c8650a7bf | ||
|
|
ebf400befd | ||
|
|
0f6dc52a8a | ||
|
|
08bd4b33a0 | ||
|
|
83d5ccc3e8 | ||
|
|
a3886eb829 | ||
|
|
86957f0205 | ||
|
|
b23d4f110b | ||
|
|
465b98df74 | ||
|
|
d3b2255c0a | ||
|
|
a4beac39a3 | ||
|
|
53311befff | ||
|
|
156b3379e4 | ||
|
|
8dd82dd592 | ||
|
|
80d6f737e0 | ||
|
|
7d86d083bd | ||
|
|
a94044cc3a | ||
|
|
2c46ea9948 | ||
|
|
190e8372af | ||
|
|
5c523be780 | ||
|
|
d3826ce952 | ||
|
|
ee66dd258d | ||
|
|
265770857f | ||
|
|
1c20fc66d7 | ||
|
|
c34c5f2aea | ||
|
|
6432dddfe0 | ||
|
|
028fc12657 | ||
|
|
2e794a296a | ||
|
|
498b8390d7 | ||
|
|
528de5284b | ||
|
|
a19fb21c5d | ||
|
|
5770f62d02 | ||
|
|
be59e647c1 | ||
|
|
e560b8bf3a | ||
|
|
74d6fbc941 | ||
|
|
94e3f56166 | ||
|
|
a2a0821c66 | ||
|
|
b787576f4a | ||
|
|
375ad12aeb | ||
|
|
a432aeee69 | ||
|
|
b0ed2827f8 | ||
|
|
bfbd5d7bda | ||
|
|
1090cbc810 | ||
|
|
fa6e015520 | ||
|
|
1756617885 | ||
|
|
f5d0a2a220 | ||
|
|
be55088e75 | ||
|
|
32e93c043f | ||
|
|
920fa7888b | ||
|
|
95766b6682 | ||
|
|
db15bb85c0 | ||
|
|
b1675c4c33 | ||
|
|
84c210acf5 | ||
|
|
5e27d7d11a | ||
|
|
3a608bac58 | ||
|
|
bcebce5927 | ||
|
|
84346a148e | ||
|
|
fac9fa4efd | ||
|
|
cde1ebaa0b | ||
|
|
9f67c0943b | ||
|
|
999bca4575 | ||
|
|
3c7815c5c0 | ||
|
|
d7e7493bd6 | ||
|
|
f34e889831 | ||
|
|
7bb27142c8 | ||
|
|
2811d60184 | ||
|
|
730c24ec55 | ||
|
|
56ebfeb596 | ||
|
|
905d38186c | ||
|
|
65403c9298 | ||
|
|
8eb64f4574 | ||
|
|
d1dd94a717 | ||
|
|
758fa4b75f | ||
|
|
1cfb3ff70e | ||
|
|
7bbb1e1fea | ||
|
|
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 |
@@ -9,9 +9,3 @@ strikethrough
|
||||
touchpad
|
||||
ungroup
|
||||
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
|
||||
|
||||
6
.github/pull_request_template.md
vendored
6
.github/pull_request_template.md
vendored
@@ -25,10 +25,12 @@ Common scopes: icons, docs, studio, site, dev
|
||||
|
||||
### 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. -->
|
||||
|
||||
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -7,6 +7,10 @@ on:
|
||||
paths:
|
||||
- icons/**/*.svg
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)')
|
||||
@@ -15,9 +19,9 @@ jobs:
|
||||
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -68,7 +72,7 @@ jobs:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Semantic Release
|
||||
id: semantic
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check for blocked phrases in issue title
|
||||
run: |
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
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.
|
||||
|
||||
Read more about brand guideline rules at [We're not accepting new Brand icons #670](https://github.com/lucide-icons/lucide/issues/670).
|
||||
Read [our official statement about brand logos in Lucide](https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md).
|
||||
|
||||
Always happy to help on [Discord](https://discord.gg/EH6nSts)."
|
||||
gh issue lock ${{ github.event.issue.number }} --reason spam
|
||||
|
||||
2
.github/workflows/labeler.yml
vendored
2
.github/workflows/labeler.yml
vendored
@@ -9,5 +9,5 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/labeler@v5
|
||||
|
||||
4
.github/workflows/lint-code.yml
vendored
4
.github/workflows/lint-code.yml
vendored
@@ -9,9 +9,9 @@ jobs:
|
||||
lint-code:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
6
.github/workflows/linting-icons.yml
vendored
6
.github/workflows/linting-icons.yml
vendored
@@ -13,8 +13,8 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
- name: Get changed files
|
||||
@@ -34,6 +34,6 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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 '"
|
||||
|
||||
8
.github/workflows/lucide-angular.yml
vendored
8
.github/workflows/lucide-angular.yml
vendored
@@ -11,9 +11,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -27,9 +27,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
4
.github/workflows/lucide-astro.yml
vendored
4
.github/workflows/lucide-astro.yml
vendored
@@ -12,9 +12,9 @@ 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-file: 'package.json'
|
||||
cache: 'pnpm'
|
||||
|
||||
4
.github/workflows/lucide-font.yml
vendored
4
.github/workflows/lucide-font.yml
vendored
@@ -10,9 +10,9 @@ jobs:
|
||||
lucide-font:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
4
.github/workflows/lucide-preact.yml
vendored
4
.github/workflows/lucide-preact.yml
vendored
@@ -13,9 +13,9 @@ jobs:
|
||||
lucide-preact:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
8
.github/workflows/lucide-react-native.yml
vendored
8
.github/workflows/lucide-react-native.yml
vendored
@@ -13,9 +13,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -29,9 +29,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
8
.github/workflows/lucide-react.yml
vendored
8
.github/workflows/lucide-react.yml
vendored
@@ -14,9 +14,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -30,9 +30,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
4
.github/workflows/lucide-shared.yml
vendored
4
.github/workflows/lucide-shared.yml
vendored
@@ -10,9 +10,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
8
.github/workflows/lucide-solid.yml
vendored
8
.github/workflows/lucide-solid.yml
vendored
@@ -13,9 +13,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -29,9 +29,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
4
.github/workflows/lucide-static.yml
vendored
4
.github/workflows/lucide-static.yml
vendored
@@ -11,9 +11,9 @@ jobs:
|
||||
lucide-static:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
8
.github/workflows/lucide-svelte-5.yml
vendored
8
.github/workflows/lucide-svelte-5.yml
vendored
@@ -13,9 +13,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -29,9 +29,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
8
.github/workflows/lucide-svelte.yml
vendored
8
.github/workflows/lucide-svelte.yml
vendored
@@ -13,9 +13,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -29,9 +29,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
8
.github/workflows/lucide-vue-next.yml
vendored
8
.github/workflows/lucide-vue-next.yml
vendored
@@ -13,9 +13,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -29,9 +29,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
43
.github/workflows/lucide-vue.yml
vendored
Normal file
43
.github/workflows/lucide-vue.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Lucide Vue checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/lucide-vue/**
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter @lucide/vue build
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter @lucide/vue test
|
||||
8
.github/workflows/lucide.yml
vendored
8
.github/workflows/lucide.yml
vendored
@@ -12,9 +12,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -28,9 +28,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -14,8 +14,8 @@ jobs:
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
with:
|
||||
files: icons/*.svg
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- 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
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@ jobs:
|
||||
steps:
|
||||
# We checkout the main branch of main repository for security reasons.
|
||||
# This is to prevent the workflow from running on a forked repository.
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
repository: lucide-icons/lucide
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
29
.github/workflows/release.yml
vendored
29
.github/workflows/release.yml
vendored
@@ -18,9 +18,13 @@ on:
|
||||
description: Version
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: read
|
||||
|
||||
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 }}
|
||||
@@ -39,7 +43,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: pre-release
|
||||
permissions:
|
||||
id-token: write
|
||||
id-token: write # Required for OIDC
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -48,7 +53,6 @@ jobs:
|
||||
'lucide',
|
||||
'lucide-react',
|
||||
'lucide-react-native',
|
||||
'lucide-vue',
|
||||
'lucide-vue-next',
|
||||
'lucide-angular',
|
||||
'lucide-preact',
|
||||
@@ -56,11 +60,12 @@ jobs:
|
||||
'lucide-svelte',
|
||||
'@lucide/astro',
|
||||
'@lucide/svelte',
|
||||
'@lucide/vue',
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -90,12 +95,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [pre-release, lucide-font]
|
||||
permissions:
|
||||
id-token: write
|
||||
id-token: write # Required for OIDC
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -125,9 +132,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: pre-release
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -153,7 +160,7 @@ jobs:
|
||||
needs: [pre-release, lucide-font]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/download-artifact@v4
|
||||
- name: Zip font and icons
|
||||
run: |
|
||||
|
||||
5
.github/workflows/request-review.yml
vendored
5
.github/workflows/request-review.yml
vendored
@@ -2,7 +2,8 @@ name: 'Request Review'
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
paths: icons/*.svg
|
||||
paths:
|
||||
- icons/*.svg
|
||||
|
||||
jobs:
|
||||
request-review:
|
||||
@@ -12,7 +13,7 @@ jobs:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -2,5 +2,5 @@
|
||||
"cSpell.words": ["devs", "preact", "Preact"],
|
||||
"eslint.enable": true,
|
||||
"eslint.validate": ["javascript", "svg"],
|
||||
"svg.preview.background": "transparent"
|
||||
"svg.preview.background": "editor"
|
||||
}
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -57,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!
|
||||
@@ -84,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>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"$schema": "../category.schema.json",
|
||||
"title": "Brands",
|
||||
"icon": "facebook"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "../category.schema.json",
|
||||
"title": "Navigation",
|
||||
"title": "Navigation, Maps, and POIs",
|
||||
"icon": "compass"
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@ export default eventHandler((event) => {
|
||||
|
||||
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 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 = [];
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
import { defineConfig } from 'vitepress';
|
||||
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
|
||||
import container from 'markdown-it-container';
|
||||
import { renderSandbox } from 'vitepress-plugin-sandpack';
|
||||
import sidebar from './sidebar';
|
||||
import snackPlayer from './plugins/snackPlayer';
|
||||
|
||||
const title = 'Lucide';
|
||||
const socialTitle = 'Lucide Icons';
|
||||
@@ -13,6 +17,19 @@ export default defineConfig({
|
||||
cleanUrls: true,
|
||||
outDir: '.vercel/output/static',
|
||||
srcExclude: ['**/README.md'],
|
||||
markdown: {
|
||||
config(md) {
|
||||
md.use(groupIconMdPlugin);
|
||||
md.use(snackPlayer);
|
||||
md.use(container, 'sandbox', {
|
||||
render (tokens, idx) {
|
||||
console.log(tokens);
|
||||
|
||||
return renderSandbox(tokens, idx, 'sandbox');
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: [
|
||||
@@ -34,6 +51,9 @@ export default defineConfig({
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
groupIconVitePlugin()
|
||||
],
|
||||
},
|
||||
head: [
|
||||
[
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
"name": "arrows",
|
||||
"title": "Arrows"
|
||||
},
|
||||
{
|
||||
"name": "brands",
|
||||
"title": "Brands"
|
||||
},
|
||||
{
|
||||
"name": "buildings",
|
||||
"title": "Buildings"
|
||||
@@ -101,7 +97,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",
|
||||
|
||||
@@ -31,20 +31,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-vue-next": {
|
||||
"@lucide/vue": {
|
||||
"order": 2,
|
||||
"icon": "vue-next",
|
||||
"icon": "vue",
|
||||
"docsAlias": "lucide-vue",
|
||||
"packageDirname": "vue",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/v/lucide-vue-next",
|
||||
"href": "https://www.npmjs.com/package/lucide-vue-next"
|
||||
},
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/dw/lucide-vue-next",
|
||||
"href": "https://www.npmjs.com/package/lucide-vue-next"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-svelte": {
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
"documentation": "https://github.com/swisnl/nuxt-lucide-icons/blob/main/README.md"
|
||||
},
|
||||
{
|
||||
"name": "lucide-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"
|
||||
/>
|
||||
|
||||
@@ -457,18 +457,7 @@ const SvgPreview = React.forwardRef<
|
||||
<ColoredPath
|
||||
paths={paths}
|
||||
colors={[
|
||||
'#1982c4',
|
||||
'#4267AC',
|
||||
'#6a4c93',
|
||||
'#B55379',
|
||||
'#FF595E',
|
||||
'#FF7655',
|
||||
'#ff924c',
|
||||
'#FFAE43',
|
||||
'#ffca3a',
|
||||
'#C5CA30',
|
||||
'#8ac926',
|
||||
'#52A675',
|
||||
'##dfdfd6',
|
||||
]}
|
||||
/>
|
||||
<Radii
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { bundledLanguages, getHighlighter, type ThemeRegistration } from 'shikiji';
|
||||
import { bundledLanguages, createHighlighter, type ThemeRegistration } from 'shiki';
|
||||
|
||||
type CodeExampleType = {
|
||||
title: string;
|
||||
@@ -37,7 +37,7 @@ export default App;
|
||||
language: 'vue',
|
||||
title: 'Vue',
|
||||
code: `<script setup>
|
||||
import { $PascalCase } from 'lucide-vue-next';
|
||||
import { $PascalCase } from '@lucide/vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -112,7 +112,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;
|
||||
@@ -119,7 +119,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),
|
||||
});
|
||||
|
||||
110
docs/.vitepress/plugins/snackPlayer.ts
Normal file
110
docs/.vitepress/plugins/snackPlayer.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* SnackPlayer markdown-it plugin
|
||||
*
|
||||
* Converts fenced code blocks like:
|
||||
*
|
||||
* ```SnackPlayer name=My%20Example&description=Nice%20demo&ext=tsx
|
||||
* // code here
|
||||
* ```
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <div
|
||||
* class="snack-player"
|
||||
* data-snack-name="My Example"
|
||||
* ...
|
||||
* />
|
||||
*/
|
||||
|
||||
import type MarkdownIt from 'markdown-it';
|
||||
|
||||
type SnackParams = Record<string, string>;
|
||||
|
||||
function parseParams(paramString = ''): SnackParams {
|
||||
const params = Object.fromEntries(
|
||||
new URLSearchParams(paramString),
|
||||
) as SnackParams;
|
||||
|
||||
if (!params.platform) {
|
||||
params.platform = 'web';
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
export default function snackPlayerPlugin(md: MarkdownIt) {
|
||||
const escapeHtml = md.utils.escapeHtml;
|
||||
const defaultFence =
|
||||
md.renderer.rules.fence ||
|
||||
((tokens, idx, options, env, self) =>
|
||||
self.renderToken(tokens, idx, options));
|
||||
|
||||
md.renderer.rules.fence = (tokens, idx, options, env, self) => {
|
||||
const token = tokens[idx];
|
||||
const info = (token.info || '').trim();
|
||||
|
||||
if (!info) {
|
||||
return defaultFence(tokens, idx, options, env, self);
|
||||
}
|
||||
|
||||
const [lang, ...rest] = info.split(/\s+/);
|
||||
if (lang !== 'SnackPlayer') {
|
||||
return defaultFence(tokens, idx, options, env, self);
|
||||
}
|
||||
|
||||
const paramString = rest.join(' ');
|
||||
const params = parseParams(paramString);
|
||||
|
||||
// Gather necessary params
|
||||
const name = params.name
|
||||
? decodeURIComponent(params.name)
|
||||
: 'Example';
|
||||
const description = params.description
|
||||
? decodeURIComponent(params.description)
|
||||
: 'Example usage';
|
||||
const ext = params.ext ? decodeURIComponent(params.ext) : 'tsx';
|
||||
const filename = `App.${ext}`;
|
||||
|
||||
const files = encodeURIComponent(
|
||||
JSON.stringify({
|
||||
[filename]: {
|
||||
type: 'CODE',
|
||||
contents: token.content,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const dependencies =
|
||||
'react-native-safe-area-context' +
|
||||
(params.dependencies ? `,${params.dependencies}` : '');
|
||||
const platform = params.platform ?? 'web';
|
||||
const supportedPlatforms =
|
||||
params.supportedPlatforms ?? 'ios,android,web';
|
||||
const theme = params.theme ?? 'light';
|
||||
const preview = params.preview ?? 'true';
|
||||
const loading = params.loading ?? 'lazy';
|
||||
const deviceAppearance = params.deviceAppearance ?? 'dark';
|
||||
|
||||
// Build the HTML output (escaping where appropriate)
|
||||
return (
|
||||
`<SnackPlayer` +
|
||||
` class="snack-player"` +
|
||||
` data-snack-name="${escapeHtml(name)}"` +
|
||||
` data-snack-description="${escapeHtml(description)}"` +
|
||||
` data-snack-files="${files}"` +
|
||||
` data-snack-dependencies="${escapeHtml(dependencies)}"` +
|
||||
` data-snack-platform="${escapeHtml(platform)}"` +
|
||||
` data-snack-supported-platforms="${escapeHtml(
|
||||
supportedPlatforms,
|
||||
)}"` +
|
||||
// ` data-snack-theme="${escapeHtml(theme)}"` +
|
||||
` data-snack-preview="${escapeHtml(preview)}"` +
|
||||
` data-snack-loading="${escapeHtml(loading)}"` +
|
||||
` data-snack-device-appearance="${escapeHtml(
|
||||
deviceAppearance,
|
||||
)}"` +
|
||||
` data-snack-device-frame="false"` +
|
||||
`></SnackPlayer>`
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
import { DefaultTheme, UserConfig } from 'vitepress';
|
||||
|
||||
const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
|
||||
guide: [
|
||||
{
|
||||
text: 'Introduction',
|
||||
items: [
|
||||
{ text: 'What is lucide?', link: '/guide/' },
|
||||
{ text: 'Installation', link: '/guide/installation' },
|
||||
{ text: 'Comparison', link: '/guide/comparison' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
link: '/guide/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
link: '/guide/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
link: '/guide/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
// TODO: Add this section
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/advanced/accessibility',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/advanced/global-styling',
|
||||
},
|
||||
// {
|
||||
// text: 'Animations',
|
||||
// },
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/advanced/filled-icons',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/advanced/aliased-names',
|
||||
},
|
||||
// {
|
||||
// text: 'Combining icons',
|
||||
// },
|
||||
// {
|
||||
// text: 'Dynamic imports'
|
||||
// },
|
||||
// {
|
||||
// text: 'Auto importing'
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Packages',
|
||||
items: [
|
||||
{
|
||||
text: 'Lucide',
|
||||
link: '/guide/packages/lucide',
|
||||
},
|
||||
{
|
||||
text: 'Lucide React',
|
||||
link: '/guide/packages/lucide-react',
|
||||
},
|
||||
{
|
||||
text: 'Lucide Vue',
|
||||
link: '/guide/packages/lucide-vue-next',
|
||||
},
|
||||
{
|
||||
text: 'Lucide Svelte',
|
||||
link: '/guide/packages/lucide-svelte',
|
||||
},
|
||||
{
|
||||
text: 'Lucide Solid',
|
||||
link: '/guide/packages/lucide-solid',
|
||||
},
|
||||
{
|
||||
text: 'Lucide React Native',
|
||||
link: '/guide/packages/lucide-react-native',
|
||||
},
|
||||
{
|
||||
text: 'Lucide Angular',
|
||||
link: '/guide/packages/lucide-angular',
|
||||
},
|
||||
{
|
||||
text: 'Lucide Preact',
|
||||
link: '/guide/packages/lucide-preact',
|
||||
},
|
||||
{
|
||||
text: 'Lucide Astro',
|
||||
link: '/guide/packages/lucide-astro',
|
||||
},
|
||||
{
|
||||
text: 'Lucide Static',
|
||||
link: '/guide/packages/lucide-static',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Contributing',
|
||||
items: [
|
||||
{
|
||||
text: 'Icon Design Principles',
|
||||
link: '/guide/design/icon-design-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Illustrator',
|
||||
link: '/guide/design/illustrator-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in InkScape',
|
||||
link: '/guide/design/inkscape-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Figma',
|
||||
link: '/guide/design/figma-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Affinity Designer',
|
||||
link: '/guide/design/affinity-designer-guide',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
// This should be here to keep the sidebar shown on the icons page
|
||||
icons: [{ text: '', link: '/' }],
|
||||
};
|
||||
|
||||
export default sidebar;
|
||||
66
docs/.vitepress/sidebar/index.ts
Normal file
66
docs/.vitepress/sidebar/index.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { DefaultTheme, UserConfig } from 'vitepress';
|
||||
import { reactSidebar } from './react';
|
||||
import { vueSidebar } from './vue';
|
||||
import { svelteSidebar } from './svelte';
|
||||
import { lucideSidebar } from './lucide';
|
||||
import { solidSidebar } from './solid';
|
||||
import { preactSidebar } from './preact';
|
||||
import { reactNativeSidebar } from './react-native';
|
||||
|
||||
type Sidebar = UserConfig<DefaultTheme.Config>['themeConfig']['sidebar']
|
||||
|
||||
export const guideSidebarTop: DefaultTheme.SidebarItem[] = [
|
||||
{
|
||||
text: 'Introduction',
|
||||
items: [
|
||||
{ text: 'What is lucide?', link: '/guide/' },
|
||||
{ text: 'Installation', link: '/guide/installation' },
|
||||
{ text: 'Comparison', link: '/guide/comparison' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const sidebar: Sidebar = {
|
||||
'/guide': [{ text: '', link: '/' }],
|
||||
'/guide/lucide': lucideSidebar,
|
||||
'/guide/react': reactSidebar,
|
||||
'/guide/vue': vueSidebar,
|
||||
'/guide/svelte': svelteSidebar,
|
||||
'/guide/solid': solidSidebar,
|
||||
'/guide/preact': preactSidebar,
|
||||
'/guide/react-native/': reactNativeSidebar,
|
||||
'/resources': [
|
||||
{
|
||||
text: "Community",
|
||||
},
|
||||
{
|
||||
text: 'Designing icons',
|
||||
items: [
|
||||
{
|
||||
text: 'Icon Design Principles',
|
||||
link: '/guide/design/icon-design-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Illustrator',
|
||||
link: '/guide/design/illustrator-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in InkScape',
|
||||
link: '/guide/design/inkscape-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Figma',
|
||||
link: '/guide/design/figma-guide',
|
||||
},
|
||||
{
|
||||
text: 'Designing in Affinity Designer',
|
||||
link: '/guide/design/affinity-designer-guide',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
// This should be here to keep the sidebar shown on the icons page
|
||||
icons: [{ text: '', link: '/' }],
|
||||
};
|
||||
|
||||
export default sidebar;
|
||||
68
docs/.vitepress/sidebar/lucide.ts
Normal file
68
docs/.vitepress/sidebar/lucide.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { DefaultTheme } from "vitepress";
|
||||
|
||||
export const lucideSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/lucide/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/lucide/getting-started',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/lucide/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/lucide/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/lucide/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
// TODO: Add this section
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/lucide/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'With shadow DOM',
|
||||
link: '/guide/lucide/advanced/shadow-dom',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/lucide/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'With lucide lab',
|
||||
link: '/guide/lucide/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with lucide',
|
||||
},
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/lucide/advanced/filled-icons',
|
||||
desc: 'Using filled icons in lucide',
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
|
||||
97
docs/.vitepress/sidebar/preact.ts
Normal file
97
docs/.vitepress/sidebar/preact.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { DefaultTheme } from "vitepress";
|
||||
|
||||
export const preactSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/preact/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/preact/getting-started',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/preact/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/preact/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/preact/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/preact/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/preact/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/preact/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With lucide lab',
|
||||
link: '/guide/preact/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with lucide-preact',
|
||||
},
|
||||
// {
|
||||
// text: 'Animations',
|
||||
// link: '/guide/preact/advanced/animations',
|
||||
// desc: 'Add animations to your icons',
|
||||
// },
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/preact/advanced/filled-icons',
|
||||
desc: 'Using filled icons in lucide-preact',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/preact/advanced/aliased-names',
|
||||
desc: 'Using aliased icon names',
|
||||
},
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/preact/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
}
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
|
||||
82
docs/.vitepress/sidebar/react-native.ts
Normal file
82
docs/.vitepress/sidebar/react-native.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { DefaultTheme } from "vitepress";
|
||||
|
||||
export const reactNativeSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/react-native/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/react-native/getting-started',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/react-native/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/react-native/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/react-native/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/react-native/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/react-native/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With lucide lab',
|
||||
link: '/guide/react-native/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with lucide-react-native',
|
||||
},
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/react-native/advanced/filled-icons',
|
||||
desc: 'Using filled icons in lucide-react-native',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/react-native/advanced/aliased-names',
|
||||
desc: 'Using aliased icon names',
|
||||
},
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/react-native/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
}
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
|
||||
103
docs/.vitepress/sidebar/react.ts
Normal file
103
docs/.vitepress/sidebar/react.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { DefaultTheme } from "vitepress";
|
||||
|
||||
export const reactSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/react/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/react/getting-started',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/react/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/react/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/react/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/react/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/react/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/react/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With lucide lab',
|
||||
link: '/guide/react/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with lucide-react',
|
||||
},
|
||||
// {
|
||||
// text: 'Animations',
|
||||
// link: '/guide/react/advanced/animations',
|
||||
// desc: 'Add animations to your icons',
|
||||
// },
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/react/advanced/filled-icons',
|
||||
desc: 'Using filled icons in lucide-react',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/react/advanced/aliased-names',
|
||||
desc: 'Using aliased icon names',
|
||||
},
|
||||
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/react/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
{
|
||||
text: 'Dynamic icon component',
|
||||
link: '/guide/react/advanced/dynamic-icon-component.md',
|
||||
desc: 'Dynamically import icons as needed',
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
}
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
|
||||
98
docs/.vitepress/sidebar/solid.ts
Normal file
98
docs/.vitepress/sidebar/solid.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { DefaultTheme } from "vitepress";
|
||||
|
||||
export const solidSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/solid/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/solid/getting-started',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/solid/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/solid/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/solid/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/solid/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/solid/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/solid/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With lucide lab',
|
||||
link: '/guide/solid/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with lucide-solid',
|
||||
},
|
||||
// {
|
||||
// text: 'Animations',
|
||||
// link: '/guide/solid/advanced/animations',
|
||||
// desc: 'Add animations to your icons',
|
||||
// },
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/solid/advanced/filled-icons',
|
||||
desc: 'Using filled icons in lucide-solid',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/solid/advanced/aliased-names',
|
||||
desc: 'Using aliased icon names',
|
||||
},
|
||||
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/solid/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
}
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
|
||||
87
docs/.vitepress/sidebar/svelte.ts
Normal file
87
docs/.vitepress/sidebar/svelte.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { DefaultTheme } from "vitepress";
|
||||
|
||||
export const svelteSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/svelte/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/svelte/getting-started',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/svelte/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/svelte/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/svelte/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/svelte/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/svelte/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/svelte/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With lucide lab',
|
||||
link: '/guide/svelte/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with @lucide/svelte',
|
||||
},
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/svelte/advanced/filled-icons',
|
||||
desc: 'Using filled icons in @lucide/svelte',
|
||||
},
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/svelte/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
}
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
|
||||
92
docs/.vitepress/sidebar/vue.ts
Normal file
92
docs/.vitepress/sidebar/vue.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { DefaultTheme } from "vitepress";
|
||||
|
||||
export const vueSidebar = [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: 'Overview',
|
||||
link: '/guide/vue/',
|
||||
},
|
||||
{
|
||||
text: 'Getting started',
|
||||
link: '/guide/vue/getting-started',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Basics',
|
||||
items: [
|
||||
{
|
||||
text: 'Color',
|
||||
desc: 'Adjust the color of your icons',
|
||||
link: '/guide/vue/basics/color',
|
||||
},
|
||||
{
|
||||
text: 'Sizing',
|
||||
desc: 'Adjust the size of your icons',
|
||||
link: '/guide/vue/basics/sizing',
|
||||
},
|
||||
{
|
||||
text: 'Stroke width',
|
||||
desc: 'Adjust the stroke width of your icons',
|
||||
link: '/guide/vue/basics/stroke-width',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{
|
||||
text: 'Typescript',
|
||||
link: '/guide/vue/advanced/typescript',
|
||||
desc: 'All exported types and how to use them',
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
link: '/guide/vue/advanced/accessibility',
|
||||
desc: 'Making your icons accessible',
|
||||
},
|
||||
{
|
||||
text: 'Global styling',
|
||||
link: '/guide/vue/advanced/global-styling',
|
||||
desc: 'Apply global styles to all icons',
|
||||
},
|
||||
{
|
||||
text: 'With lucide lab',
|
||||
link: '/guide/vue/advanced/with-lucide-lab',
|
||||
desc: 'Using lucide-lab with @lucide/vue',
|
||||
},
|
||||
{
|
||||
text: 'Filled icons',
|
||||
link: '/guide/vue/advanced/filled-icons',
|
||||
desc: 'Using filled icons in @lucide/vue',
|
||||
},
|
||||
{
|
||||
text: 'Aliased Names',
|
||||
link: '/guide/vue/advanced/aliased-names',
|
||||
desc: 'Using aliased icon names',
|
||||
},
|
||||
|
||||
{
|
||||
text: 'Combining icons',
|
||||
link: '/guide/vue/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
{
|
||||
text: 'Accessibility in depth',
|
||||
link: '/guide/accessibility',
|
||||
desc: 'Accessibility best practices',
|
||||
},
|
||||
{
|
||||
text: 'VSCode',
|
||||
link: '/guide/vscode',
|
||||
desc: 'VSCode and Lucide',
|
||||
},
|
||||
],
|
||||
}
|
||||
] satisfies DefaultTheme.SidebarItem[] & { items: { desc?: string }[] }[];
|
||||
@@ -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>()
|
||||
@@ -39,6 +40,7 @@ const value = computed({
|
||||
ref="input"
|
||||
type="search"
|
||||
autofocus
|
||||
:shortcut="shortcut"
|
||||
v-bind="$attrs"
|
||||
v-model="value"
|
||||
class="input-wrapper"
|
||||
@@ -71,5 +73,4 @@ const value = computed({
|
||||
font-size: 14px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -5,8 +5,8 @@ export type IconNode = [elementName: string, attrs: Record<string, string>][]
|
||||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
tags: string[];
|
||||
categories: string[];
|
||||
tags?: string[];
|
||||
categories?: string[];
|
||||
// contributors: Contributor[];
|
||||
iconNode: IconNode;
|
||||
}>()
|
||||
|
||||
61
docs/.vitepress/theme/components/base/OverviewLink.vue
Normal file
61
docs/.vitepress/theme/components/base/OverviewLink.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
|
||||
|
||||
defineProps<{
|
||||
href?: string
|
||||
title?: string
|
||||
desc?: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPLink
|
||||
class="overview-link"
|
||||
:href="href"
|
||||
:aria-label="`${title} - ${desc}`"
|
||||
>
|
||||
<span class="title">
|
||||
{{ title }}
|
||||
</span>
|
||||
<span
|
||||
class="desc">
|
||||
{{ desc }}
|
||||
</span>
|
||||
</VPLink>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.overview-link {
|
||||
display: block;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
padding: 11px 16px 13px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: border-color 0.25s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.overview-link:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.desc {
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.title {
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-brand-1);
|
||||
transition: color 0.25s;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
</style>
|
||||
31
docs/.vitepress/theme/components/base/OverviewLinkGrid.vue
Normal file
31
docs/.vitepress/theme/components/base/OverviewLinkGrid.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="overview-link-grid">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.overview-link-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.overview-link-grid > * {
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.overview-link-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.overview-link-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
226
docs/.vitepress/theme/components/base/Select.vue
Normal file
226
docs/.vitepress/theme/components/base/Select.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
Listbox,
|
||||
ListboxLabel,
|
||||
ListboxButton,
|
||||
ListboxOptions,
|
||||
ListboxOption,
|
||||
} from '@headlessui/vue'
|
||||
import { CheckIcon, ChevronsUpDownIcon } from 'lucide-vue-next'
|
||||
|
||||
|
||||
defineProps<{
|
||||
id?: string
|
||||
items?: { name: string, icon: string }[]
|
||||
}>()
|
||||
|
||||
const selected = defineModel<{ name: string, icon: string }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Listbox v-model="selected">
|
||||
<div class="select-wrapper">
|
||||
<ListboxButton class="select-button" :id="id">
|
||||
<img
|
||||
:src="selected.icon"
|
||||
:class="{ 'select-item-icon': true }"
|
||||
:alt="`${selected.name} logo`"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span class="select-text">{{ selected.name }}</span>
|
||||
<span class="select-icon">
|
||||
<ChevronsUpDownIcon class="chevron-icon" aria-hidden="true" />
|
||||
</span>
|
||||
</ListboxButton>
|
||||
|
||||
<transition
|
||||
leave-active-class="transition-leave"
|
||||
leave-from-class="transition-leave-from"
|
||||
leave-to-class="transition-leave-to"
|
||||
>
|
||||
<ListboxOptions class="select-options">
|
||||
<ListboxOption
|
||||
v-slot="{ active, selected }"
|
||||
v-for="item in items"
|
||||
:key="item.name"
|
||||
:value="item"
|
||||
as="template"
|
||||
>
|
||||
<li :class="['select-option', { active, selected }]">
|
||||
<img
|
||||
:src="item.icon"
|
||||
:class="{ 'select-item-icon': true }"
|
||||
:alt="`${item.name} logo`"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span :class="['option-text', { selected }]">{{ item.name }}</span>
|
||||
<span v-if="selected" class="check-icon">
|
||||
<CheckIcon class="check" aria-hidden="true" />
|
||||
</span>
|
||||
</li>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.select-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.select-button {
|
||||
background: var(--vp-sidebar-input);
|
||||
border-radius: 8px;
|
||||
color: var(--vp-c-text-1);
|
||||
padding: 7px 14px;
|
||||
height: auto;
|
||||
font-size: 14px;
|
||||
border: 1px solid transparent;
|
||||
cursor: text;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.select-button:focus {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.select-text {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
}
|
||||
|
||||
.select-icon {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.select-item-icon {
|
||||
object-fit: contain;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.chevron-icon {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.transition-leave {
|
||||
transition: opacity 100ms ease-in;
|
||||
}
|
||||
|
||||
.transition-leave-from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.transition-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.select-options {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
min-width: 128px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background-color: var(--vp-c-bg-elv);
|
||||
box-shadow: var(--vp-shadow-3);
|
||||
transition: background-color 0.5s;
|
||||
max-height: calc(100vh - var(--vp-nav-height));
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
z-index: 90;
|
||||
right: 0;
|
||||
top: 44px;
|
||||
}
|
||||
|
||||
.select-option {
|
||||
position: relative;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
padding: 0px 4px;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
border-radius: 6px;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-1);
|
||||
white-space: nowrap;
|
||||
transition: background-color .25s,color .25s;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.select-option:hover, .select-option.active {
|
||||
color: var(--vp-c-brand);
|
||||
background-color: var(--vp-c-default-soft);
|
||||
}
|
||||
|
||||
.select-option:active {
|
||||
color: var(--vp-c-brand);
|
||||
background-color: var(--vp-c-bg-elv);
|
||||
}
|
||||
|
||||
.option-text {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.option-text.selected {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 0.75rem;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.check {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.select-button,
|
||||
.select-options {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
136
docs/.vitepress/theme/components/editors/Sandbox.vue
Normal file
136
docs/.vitepress/theme/components/editors/Sandbox.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<script setup lang="ts">
|
||||
import { getSandpackFiles, getCustomSetupFromProps, parsedBoolean, getSandpackOptions } from 'vitepress-plugin-sandpack';
|
||||
import { Sandpack, type SandpackFiles } from 'sandpack-vue3';
|
||||
import { computed, nextTick, onBeforeMount, onMounted, ref, useSlots, watch } from 'vue';
|
||||
import styles from './styles.css?raw'
|
||||
|
||||
import sandpackTheme from '../../sandpackTheme.json'
|
||||
import { sandboxProps } from 'vitepress-plugin-sandpack';
|
||||
|
||||
const props = defineProps({
|
||||
...sandboxProps,
|
||||
editorHeight: {
|
||||
type: [String, Number],
|
||||
default: undefined
|
||||
},
|
||||
editorWidthPercentage: {
|
||||
type: [String, Number],
|
||||
default: undefined
|
||||
},
|
||||
dependencies: {
|
||||
type: String,
|
||||
default: undefined
|
||||
}
|
||||
});
|
||||
|
||||
const files = ref<SandpackFiles>({});
|
||||
|
||||
const getOpt = (propName: string) => props[propName] ?? props?.options?.[propName];
|
||||
|
||||
const editorVisible = computed(() => parsedBoolean(getOpt('hideEditor')) ? 'none' : 'flex');
|
||||
|
||||
const previewHeight = computed(() => isNaN(Number(getOpt('previewHeight'))) ? undefined : Number(getOpt('previewHeight')));
|
||||
const previewHeightStyle =
|
||||
computed(() => previewHeight.value ? `${previewHeight.value}px` : 'var(--sp-layout-height)');
|
||||
|
||||
const coderHeight = computed(() => isNaN(Number(getOpt('coderHeight'))) ? undefined : Number(getOpt('coderHeight')));
|
||||
const coderHeightStyle =
|
||||
computed(() => coderHeight.value ? `${coderHeight.value}px` : 'var(--sp-layout-height)');
|
||||
|
||||
const slots = useSlots();
|
||||
const isDark = ref(false);
|
||||
|
||||
const resolveFiles = async () => {
|
||||
files.value = {
|
||||
...await getSandpackFiles(props, slots),
|
||||
'styles.css': {
|
||||
code: styles,
|
||||
hidden: true
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
watch(props, resolveFiles);
|
||||
|
||||
onBeforeMount(resolveFiles);
|
||||
|
||||
const dependencies = computed(() => {
|
||||
if (props.dependencies) {
|
||||
return props.dependencies.split(',').reduce((acc, dep) => {
|
||||
const [name, version] = dep.split(':').map(s => s.trim());
|
||||
acc[name] = version || 'latest';
|
||||
return acc;
|
||||
}, {} as Record<string, string>);
|
||||
}
|
||||
return undefined;
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sandpack :theme="sandpackTheme" :template="template" :rtl="parsedBoolean(rtl)" :files="files" :options="{
|
||||
...(getSandpackOptions(props) as any),
|
||||
editorWidthPercentage: getOpt('editorWidthPercentage') ? Number(getOpt('editorWidthPercentage')) : undefined,
|
||||
showConsoleButton: false,
|
||||
}" :customSetup='{
|
||||
dependencies: dependencies
|
||||
}' />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.sp-wrapper+* {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-layout {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs-scrollable-container {
|
||||
border-radius: 8px 8px 0 0;
|
||||
position: relative;
|
||||
|
||||
box-shadow: inset 0 -1px var(--vp-code-tab-divider);
|
||||
margin-bottom: 0px;
|
||||
margin-top: -1px;
|
||||
height: 48px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-preview-container {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button {
|
||||
padding: 0 12px;
|
||||
line-height: 48px;
|
||||
height: 48px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button:after {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
left: 8px;
|
||||
bottom: 0px;
|
||||
z-index: 1;
|
||||
height: 1px;
|
||||
content: '';
|
||||
background-color: transparent;
|
||||
transition: background-color 0.25s;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true'] {
|
||||
color: var(--vp-code-tab-active-text-color);
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true']:after {
|
||||
background-color: var(--vp-code-tab-active-bar-color);
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-button {
|
||||
color: var(--sp-colors-clickable);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
48
docs/.vitepress/theme/components/editors/SnackPlayer.vue
Normal file
48
docs/.vitepress/theme/components/editors/SnackPlayer.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import { useMutationObserver, useScriptTag } from '@vueuse/core';
|
||||
import { useData } from 'vitepress';
|
||||
import { onMounted, useTemplateRef, watchEffect } from 'vue';
|
||||
|
||||
const { isDark } = useData()
|
||||
const el = useTemplateRef('el')
|
||||
|
||||
useScriptTag('https://snack.expo.dev/embed.js')
|
||||
|
||||
watchEffect(() => {
|
||||
console.log(isDark.value);
|
||||
})
|
||||
|
||||
useMutationObserver(el, (mutations) => {
|
||||
const container = el.value;
|
||||
if (mutations[0]) {
|
||||
if ('ExpoSnack' in window) {
|
||||
window?.ExpoSnack?.remove(container);
|
||||
window?.ExpoSnack?.append(container);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
attributes: true,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const container = el.value;
|
||||
if ('ExpoSnack' in window) {
|
||||
window?.ExpoSnack?.append(container);
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-bind="$attrs" class="snack-player" ref="el" :data-snack-theme="isDark ? 'dark' : 'light'" />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.snack-player {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
height: 635px;
|
||||
margin-bottom: 24px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
50
docs/.vitepress/theme/components/editors/styles.css
Normal file
50
docs/.vitepress/theme/components/editors/styles.css
Normal file
@@ -0,0 +1,50 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: auto;
|
||||
-moz-font-smoothing: auto;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-smoothing: auto;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-smooth: always;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-touch-callout: none;
|
||||
background: #202127;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
padding: 10px 20px;
|
||||
line-height: 24px;
|
||||
gap: 8px;
|
||||
border-radius: 24px;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: #111;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #f56565;
|
||||
}
|
||||
|
||||
.app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 32px;
|
||||
}
|
||||
88
docs/.vitepress/theme/components/guide/FrameworkSelect.vue
Normal file
88
docs/.vitepress/theme/components/guide/FrameworkSelect.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<script setup lang="tsx">
|
||||
import VPSidebarGroup from 'vitepress/dist/client/theme-default/components/VPSidebarGroup.vue';
|
||||
import sidebar, { guideSidebarTop } from '../../../sidebar';
|
||||
import { useData, useRouter } from 'vitepress';
|
||||
import Select from '../base/Select.vue';
|
||||
import { computed, ref, watch, watchEffect } from 'vue';
|
||||
import { link, route } from '~/.vitepress/data/iconNodes';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
|
||||
const { page } = useData()
|
||||
const router = useRouter()
|
||||
|
||||
const frameworks = [
|
||||
{ name: 'Vanilla', icon: '/framework-logos/js.svg', route: '/guide/lucide' },
|
||||
{ name: 'React', icon: '/framework-logos/react.svg', route: '/guide/react' },
|
||||
{ name: 'Vue', icon: '/framework-logos/vue.svg', route: '/guide/vue' },
|
||||
{ name: 'Svelte', icon: '/framework-logos/svelte.svg', route: '/guide/svelte' },
|
||||
{ name: 'Solid', icon: '/framework-logos/solid.svg', route: '/guide/solid' },
|
||||
{ name: 'Angular', icon: '/framework-logos/angular.svg', route: '/guide/angular' },
|
||||
{ name: 'Preact', icon: '/framework-logos/preact.svg', route: '/guide/preact' },
|
||||
{ name: 'React Native', icon: '/framework-logos/react-native.svg', route: '/guide/react-native' },
|
||||
{ name: 'Astro', icon: '/framework-logos/astro-dark.svg', route: '/guide/astro' },
|
||||
]
|
||||
|
||||
const fallbackFramework = useLocalStorage('lucide-docs-fallback-framework', frameworks[1])
|
||||
|
||||
const selected = computed(() => {
|
||||
const current = frameworks.find(({ route }) => {
|
||||
return router.route.path.split('/').slice(0, 3).join('/') === route
|
||||
})
|
||||
|
||||
return current || fallbackFramework.value
|
||||
})
|
||||
|
||||
function onSelectFramework(item: { name: string, icon: string, route: string }) {
|
||||
fallbackFramework.value = item
|
||||
if (item.route !== router.route.path) {
|
||||
const likeRoute = router.route.path.replace(selected.value.route, item.route);
|
||||
|
||||
const hasRoute = sidebar[item.route]?.some(section =>
|
||||
section?.items?.some(({ link }) => link === likeRoute)
|
||||
);
|
||||
|
||||
if (hasRoute) {
|
||||
router.go(likeRoute)
|
||||
return;
|
||||
}
|
||||
|
||||
router.go(item.route)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPSidebarGroup :items="guideSidebarTop" v-if="page?.relativePath?.startsWith?.('guide')" />
|
||||
<div class="framework-select" v-if="page?.relativePath?.startsWith?.('guide')">
|
||||
<label for="framework-select">Framework</label>
|
||||
<Select id="framework-select" :items="frameworks" @update:model-value="onSelectFramework" v-model="selected" />
|
||||
</div>
|
||||
<VPSidebarGroup :key="selected.route"
|
||||
v-if="page?.relativePath?.startsWith?.('guide') && !page?.relativePath?.startsWith?.(selected.route.substring(1))"
|
||||
:items="sidebar[selected.route]" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.framework-select {
|
||||
font-size: 12px;
|
||||
transition: border-color 0.5s, background-color 0.5s ease;
|
||||
margin-bottom: 10px;
|
||||
position: sticky;
|
||||
top: -0.5px;
|
||||
z-index: 10;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
padding-top: 10px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
label {
|
||||
color: var(--vp-c-text-1);
|
||||
padding: 4px 0;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
transition: color 0.25s;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
51
docs/.vitepress/theme/components/home/HomeHeroAfter.vue
Normal file
51
docs/.vitepress/theme/components/home/HomeHeroAfter.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vitepress';
|
||||
import Badge from '../base/Badge.vue';
|
||||
import HomeContainer from './HomeContainer.vue';
|
||||
import { data } from './HomeHeroIconsCard.data'
|
||||
import FakeInput from '../base/FakeInput.vue'
|
||||
import { nextTick, provide } from 'vue'
|
||||
import useSearchShortcut from '../../utils/useSearchShortcut';
|
||||
|
||||
const { go } = useRouter()
|
||||
|
||||
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
|
||||
go('/icons/?focus')
|
||||
})
|
||||
|
||||
const enableTransitions = () =>
|
||||
'startViewTransition' in document &&
|
||||
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
|
||||
|
||||
|
||||
async function handleClick(event: MouseEvent) {
|
||||
if (!enableTransitions()) {
|
||||
go('/icons/?focus')
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await document.startViewTransition(async () => {
|
||||
await go('/icons/?focus');
|
||||
await nextTick()
|
||||
}).ready
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<FakeInput
|
||||
@click="go('/icons/?focus')"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="search-box"
|
||||
>
|
||||
Search {{ data.iconsCount }} icons...
|
||||
</FakeInput>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.search-box {
|
||||
view-transition-name: icons-search-box;
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
</style>
|
||||
428
docs/.vitepress/theme/components/home/HomeHeroIconsAnimation.vue
Normal file
428
docs/.vitepress/theme/components/home/HomeHeroIconsAnimation.vue
Normal file
@@ -0,0 +1,428 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, shallowRef, onBeforeUnmount, watchEffect, computed } from 'vue';
|
||||
import { data } from './HomeHeroIconsCard.data'
|
||||
import { useRouter } from 'vitepress';
|
||||
import { random } from 'lodash-es'
|
||||
import FakeInput from '../base/FakeInput.vue'
|
||||
import { motion, Variants, useScroll, useSpring, useTransform } from "motion-v"
|
||||
import LucideIcon from '../base/LucideIcon.vue'
|
||||
import { shrink } from '~/.vitepress/data/iconNodes';
|
||||
|
||||
const emit = defineEmits(['animation-complete'])
|
||||
|
||||
const MotionLucideIcon = motion.create(LucideIcon)
|
||||
|
||||
const COLUMNS = 8;
|
||||
const SIZE = 2;
|
||||
const GAP = 1;
|
||||
|
||||
const { scrollYProgress } = useScroll()
|
||||
const opacity = useTransform(() => (1 - scrollYProgress.get() * 8))
|
||||
|
||||
const icons = ref(data.icons.slice(0, 64).map((icon, index) => {
|
||||
const x = index % COLUMNS;
|
||||
const y = Math.floor(index / COLUMNS);
|
||||
|
||||
if (index === 0) {
|
||||
return {
|
||||
...icon,
|
||||
x: 9999,
|
||||
y: 9999,
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...icon,
|
||||
x: x * (SIZE + GAP) + 0.5,
|
||||
y: y * (SIZE + GAP) + 0.5
|
||||
}
|
||||
}))
|
||||
|
||||
const { go } = useRouter()
|
||||
const intervalTime = shallowRef()
|
||||
const showHandles = ref(true)
|
||||
const scaleDownVariants: Variants = {
|
||||
fullSize: {
|
||||
scale: 1
|
||||
},
|
||||
riseUp: {
|
||||
x: 0.5,
|
||||
y: -0.5,
|
||||
animationName: 'riseUp',
|
||||
scale: 1,
|
||||
transition: {
|
||||
delay: 0.5,
|
||||
duration: 1.5,
|
||||
ease: [0.22, 1, 0.36, 1]
|
||||
}
|
||||
},
|
||||
small: {
|
||||
x: -10.5,
|
||||
y: -10.5,
|
||||
scale: 0.1,
|
||||
animationName: 'small',
|
||||
transition: {
|
||||
delay: 1,
|
||||
duration: 1,
|
||||
ease: [0.22, 1, 0.36, 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
const scaleDownAnimation = ref('fullSize')
|
||||
|
||||
const iconGridAnimation = ref('initial')
|
||||
|
||||
const drawAnimation = ref('visible')
|
||||
const draw: Variants = {
|
||||
hidden: { pathLength: 0, opacity: 0 },
|
||||
visible: {
|
||||
animationName: 'visible',
|
||||
pathLength: 1,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
pathLength: { delay: 2.4, type: "spring", duration: 2.8, bounce: 0 },
|
||||
opacity: { delay: 2.4, duration: 0.1 },
|
||||
},
|
||||
},
|
||||
exit: (path) => ({
|
||||
animationName: 'exit',
|
||||
stroke: path ? 'var(--vp-c-text-1)' : 'var(--vp-c-brand)',
|
||||
pathLength: 1,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.8,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
const onAnimationComplete = (item) => {
|
||||
if (item.animationName === 'visible') {
|
||||
drawAnimation.value = 'exit'
|
||||
return
|
||||
}
|
||||
if (item.animationName === 'exit') {
|
||||
showHandles.value = false
|
||||
scaleDownAnimation.value = 'small'
|
||||
}
|
||||
if (item.animationName === 'small') {
|
||||
iconGridAnimation.value = 'showIcons'
|
||||
}
|
||||
if (item.animationName === 'riseUp') {
|
||||
scaleDownAnimation.value = 'small'
|
||||
}
|
||||
if (item.animationName === 'showIcons') {
|
||||
shrinkIconAnimation.value = 'shrinkIcons'
|
||||
}
|
||||
if (item.animationName === 'shrinkIcons') {
|
||||
iconGridAnimation.value = 'initial'
|
||||
|
||||
setTimeout(() => {
|
||||
emit('animation-complete')
|
||||
}, 2800)
|
||||
}
|
||||
}
|
||||
|
||||
const randomIndex = ref(
|
||||
Math.floor(Math.random() * 64)
|
||||
)
|
||||
|
||||
const iconAnimationVariants = {
|
||||
initial: {
|
||||
animationName: 'end',
|
||||
opacity: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
transition: { duration: 1, delay: 1, ease: 'easeInOut' }
|
||||
},
|
||||
showIcons: (index) => ({
|
||||
animationName: 'showIcons',
|
||||
opacity: [0, 1, 1],
|
||||
x: [0.5, 0, 0],
|
||||
y: [-0.5, 0, 0],
|
||||
strokeWidth: randomIndex.value === index ? [0, 2, 2] : undefined,
|
||||
transition: { delay: index * 0.023, duration: 1.6, ease: 'easeInOut' }
|
||||
}),
|
||||
}
|
||||
|
||||
const shrinkIconAnimation = ref('initial')
|
||||
|
||||
const shrinkIconVariants = {
|
||||
initial: { strokeWidth: 2 },
|
||||
shrinkIcons: (index) => ({
|
||||
animationName: 'shrinkIcons',
|
||||
opacity: 1,
|
||||
strokeWidth: 0,
|
||||
transition: { delay: 1.8, duration: 1.5, ease: 'easeInOut' }
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="home-hero-animation-container">
|
||||
<div class="home-hero-animation">
|
||||
<motion.svg xmlns="http://www.w3.org/2000/svg" viewBox="-12 -12 48 48" fill="none" overflow="auto"
|
||||
stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="hero-background"
|
||||
:style="{ opacity }">
|
||||
|
||||
<g class="svg-preview-grid-group" stroke-linecap="butt" stroke-width="0.1" stroke="#777"
|
||||
mask="url(#svg-preview-bounding-box-mask)" stroke-opacity="0.3">
|
||||
<path
|
||||
stroke-dasharray="0 0.1 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0 0.15"
|
||||
stroke-width="0.1"
|
||||
d="M1 0.1v23.8M2 0.1v23.8M4 0.1v23.8M5 0.1v23.8M7 0.1v23.8M8 0.1v23.8M10 0.1v23.8M11 0.1v23.8M13 0.1v23.8M14 0.1v23.8M16 0.1v23.8M17 0.1v23.8M19 0.1v23.8M20 0.1v23.8M22 0.1v23.8M23 0.1v23.8M0.1 1h23.8M0.1 2h23.8M0.1 4h23.8M0.1 5h23.8M0.1 7h23.8M0.1 8h23.8M0.1 10h23.8M0.1 11h23.8M0.1 13h23.8M0.1 14h23.8M0.1 16h23.8M0.1 17h23.8M0.1 19h23.8M0.1 20h23.8M0.1 22h23.8M0.1 23h23.8">
|
||||
</path>
|
||||
<path
|
||||
d="M3 0.1v23.8M6 0.1v23.8M9 0.1v23.8M12 0.1v23.8M15 0.1v23.8M18 0.1v23.8M21 0.1v23.8M0.1 3h23.8M0.1 6h23.8M0.1 9h23.8M0.1 12h23.8M0.1 15h23.8M0.1 18h23.8M0.1 21h23.8">
|
||||
</path>
|
||||
</g>
|
||||
<!-- <rect fill="red" x="0" y="0" width="24" height="24" fill-opacity="0.1" stroke="none" /> -->
|
||||
<motion.g initial="initial" :variants="shrinkIconVariants" :animate="shrinkIconAnimation"
|
||||
@animation-complete="onAnimationComplete">
|
||||
<MotionLucideIcon v-for="(icon, index) in icons" size="2" class="animated-icon" initial="initial"
|
||||
:key="icon.name" :variants="iconAnimationVariants" :animate="iconGridAnimation" :custom="index"
|
||||
strokeWidth="inherit" v-bind="icon" @animation-complete="onAnimationComplete" />
|
||||
<motion.g class="svg-preview-colored-path-group" :variants="scaleDownVariants" :animate="scaleDownAnimation"
|
||||
initial="hidden" @animation-complete="onAnimationComplete">
|
||||
<motion.path
|
||||
d="M14 12C14 9.79086 12.2091 8 10 8C7.79086 8 6 9.79086 6 12C6 16.4183 9.58172 20 14 20C18.4183 20 22 16.4183 22 12C22 8.446 20.455 5.25285 18 3.05557"
|
||||
:style="{ stroke: 'var(--vp-c-gray-1)' }" :animate="drawAnimation" initial="hidden" :variants="draw"
|
||||
:custom="1" @animation-complete="onAnimationComplete" />
|
||||
<motion.path
|
||||
d="M10 12C10 14.2091 11.7909 16 14 16C16.2091 16 18 14.2091 18 12C18 7.58172 14.4183 4 10 4C5.58172 4 2 7.58172 2 12C2 15.5841 3.57127 18.8012 6.06253 21"
|
||||
:style="{ stroke: 'var(--vp-c-gray-1)' }" :animate="drawAnimation" initial="hidden" :variants="draw"
|
||||
:custom="0" />
|
||||
</motion.g>
|
||||
</motion.g>
|
||||
<motion.g class="svg-preview-control-path-marker-group" stroke="#fff" stroke-width="0.125"
|
||||
:initial="{ opacity: 1 }" :animate="showHandles ? { opacity: 1 } : { opacity: 0 }"
|
||||
:transition="{ delay: 0, duration: 0.2 }">
|
||||
<motion.path
|
||||
d="M14 12C14 9.79086 12.2091 8 10 8C7.79086 8 6 9.79086 6 12C6 16.4183 9.58172 20 14 20C18.4183 20 22 16.4183 22 12C22 8.446 20.455 5.25285 18 3.05557"
|
||||
:initial="{ opacity: 0 }" :animate="{ opacity: 1 }" :transition="{ delay: 1.6, duration: 1.5 }" />
|
||||
<motion.path
|
||||
d="M10 12C10 14.2091 11.7909 16 14 16C16.2091 16 18 14.2091 18 12C18 7.58172 14.4183 4 10 4C5.58172 4 2 7.58172 2 12C2 15.5841 3.57127 18.8012 6.06253 21"
|
||||
:initial="{ opacity: 0 }" :animate="{ opacity: 1 }" :transition="{ delay: 1.6, duration: 1.5 }" />
|
||||
<motion.g :initial="{ opacity: 0 }" :animate="{ opacity: 1 }" :transition="{ delay: 0.2, duration: 0.3 }">
|
||||
<path
|
||||
d="M14 12h.01M10 8h.01M10 8h.01M6 12h.01M6 12h.01M14 20h.01M14 20h.01M22 12h.01M22 12h.01M18 3.05557h.01M10 12h.01M14 16h.01M14 16h.01M18 12h.01M18 12h.01M10 4h.01M10 4h.01M2 12h.01M2 12h.01M6.06253 21h.01">
|
||||
</path>
|
||||
</motion.g>
|
||||
|
||||
<motion.circle :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0, duration: 0.8 }" cx="14" cy="12" r="0.5" />
|
||||
<motion.circle :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0, duration: 0.8 }" cx="14" cy="12" r="0.5" />
|
||||
<motion.circle :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0, duration: 0.8 }" cx="18" cy="3.05557" r="0.5" />
|
||||
<motion.circle :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0, duration: 0.8 }" cx="10" cy="12" r="0.5" />
|
||||
<motion.circle :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0, duration: 0.8 }" cx="6.06253" cy="21" r="0.5" />
|
||||
</motion.g>
|
||||
<motion.g class="svg-preview-handles-group" stroke-width="0.12" stroke="#FFF" stroke-opacity="0.3"
|
||||
:initial="{ opacity: 1 }" :animate="showHandles ? { opacity: 1 } : { opacity: 0 }"
|
||||
:transition="{ delay: 0, duration: 0.6 }">
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.2, duration: 0.3 }">
|
||||
<path d="M14 12 14 9.79086"></path>
|
||||
<circle cy="9.79086" cx="14" r="0.25"></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.4, duration: 0.3 }">
|
||||
<path d="M10 8 12.2091 8"></path>
|
||||
<circle cy="8" cx="12.2091" r="0.25"></circle>
|
||||
<path d="M10 8 7.79086 8"></path>
|
||||
<circle cy="8" cx="7.79086" r="0.25"></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.6, duration: 0.3 }">
|
||||
<path d="M6 12 6 9.79086"></path>
|
||||
<circle cy="9.79086" cx="6" r="0.25"></circle>
|
||||
<path d="M6 12 6 16.4183"></path>
|
||||
<circle cy="16.4183" cx="6" r="0.25"></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.8, duration: 0.3 }">
|
||||
<path d="M14 20 9.58172 20"></path>
|
||||
<circle cy="20" cx="9.58172" r="0.25"></circle>
|
||||
<path d="M14 20 18.4183 20"></path>
|
||||
<circle cy="20" cx="18.4183" r="0.25"></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 1, duration: 0.3 }">
|
||||
<path d="M22 12 22 16.4183"></path>
|
||||
<circle cy="16.4183" cx="22" r="0.25"></circle>
|
||||
<path d="M22 12 22 8.446"></path>
|
||||
<circle cy="8.446" cx="22" r="0.25"></circle>
|
||||
</motion.g>
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 1.2, duration: 0.3 }">
|
||||
<path d="M18 3.05557 20.455 5.25285"></path>
|
||||
<circle cy="5.25285" cx="20.455" r="0.25"></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.2, duration: 0.3 }">
|
||||
<path d="M10 12 10 14.2091"></path>
|
||||
<circle cy="14.2091" cx="10" r="0.25"></circle>
|
||||
</motion.g>
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.4, duration: 0.3 }">
|
||||
<path d="M14 16 11.7909 16"></path>
|
||||
<circle cy="16" cx="11.7909" r="0.25"></circle>
|
||||
<path d="M14 16 16.2091 16"></path>
|
||||
<circle cy="16" cx="16.2091" r="0.25"></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.6, duration: 0.3 }">
|
||||
<path d="M18 12 18 14.2091"></path>
|
||||
<circle cy="14.2091" cx="18" r="0.25"></circle>
|
||||
<path d="M18 12 18 7.58172"></path>
|
||||
<circle cy="7.58172" cx="18" r="0.25"></circle>
|
||||
</motion.g>
|
||||
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 0.8, duration: 0.3 }">
|
||||
<path d="M10 4 14.4183 4"></path>
|
||||
<circle cy="4" cx="14.4183" r="0.25"></circle>
|
||||
<path d="M10 4 5.58172 4"></path>
|
||||
<circle cy="4" cx="5.58172" r="0.25"></circle>
|
||||
</motion.g>
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 1, duration: 0.3 }">
|
||||
<path d="M2 12 2 7.58172"></path>
|
||||
<circle cy="7.58172" cx="2" r="0.25"></circle>
|
||||
<path d="M2 12 2 15.5841"></path>
|
||||
<circle cy="15.5841" cx="2" r="0.25"></circle>
|
||||
</motion.g>
|
||||
<motion.g :initial="{ opacity: 0, scale: 0.2 }" :animate="{ opacity: 1, scale: 1 }"
|
||||
:transition="{ delay: 1.2, duration: 0.3 }">
|
||||
<path d="M6.06253 21 3.57127 18.8012"></path>
|
||||
<circle cy="18.8012" cx="3.57127" r="0.25"></circle>
|
||||
</motion.g>
|
||||
</motion.g>
|
||||
</motion.svg>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.home-hero-animation-container {
|
||||
margin: -48px -24px 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.home-hero-animation {
|
||||
height: 250px;
|
||||
width: 396px;
|
||||
overflow: hidden;
|
||||
margin: auto;
|
||||
margin-left: calc(((396px - 100vw) / 2)* -1);
|
||||
}
|
||||
|
||||
@media (min-width: 396px) {
|
||||
.home-hero-animation {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-background {
|
||||
transform: rotateX(-51deg) rotateZ(-43deg);
|
||||
transform-style: preserve-3d;
|
||||
will-change: transform, opacity;
|
||||
position: relative;
|
||||
top: -155px;
|
||||
left: -82px;
|
||||
width: 560px;
|
||||
height: 560px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.hero-background {
|
||||
width: 680px;
|
||||
height: 680px;
|
||||
|
||||
left: -100px;
|
||||
top: -188px;
|
||||
}
|
||||
|
||||
.home-hero-animation {
|
||||
height: 305px;
|
||||
width: 480px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.hero-background {
|
||||
width: 760px;
|
||||
height: 760px;
|
||||
|
||||
left: -110px;
|
||||
top: -200px;
|
||||
}
|
||||
|
||||
.home-hero-animation {
|
||||
height: 360px;
|
||||
width: 540px;
|
||||
}
|
||||
|
||||
.home-hero-animation-container {
|
||||
margin-top: -60px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.hero-background {
|
||||
top: -20vw;
|
||||
right: 20vw;
|
||||
width: 80vw;
|
||||
height: 80vw;
|
||||
}
|
||||
|
||||
.home-hero-animation {
|
||||
height: 415px;
|
||||
width: 620px;
|
||||
}
|
||||
|
||||
.home-hero-animation-container {
|
||||
margin: -48px -48px 0 -64px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1160px) {
|
||||
.home-hero-animation-container {
|
||||
margin-right: -64px;
|
||||
margin-bottom: -180px;
|
||||
}
|
||||
|
||||
.home-hero-animation {
|
||||
width: auto;
|
||||
height: calc(((1152px/2)));
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
.hero-background {
|
||||
top: -20vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.home-hero-animation-container {
|
||||
margin-right: calc(((((100vw - 1152px) / 2)) * -1) + 24px);
|
||||
margin-left: -128px
|
||||
}
|
||||
|
||||
.hero-background {
|
||||
width: 1024px;
|
||||
height: 1024px;
|
||||
top: -280px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,153 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, shallowRef, onBeforeUnmount} from 'vue';
|
||||
import { data } from './HomeHeroIconsCard.data'
|
||||
import LucideIcon from '../base/LucideIcon.vue'
|
||||
import { useRouter } from 'vitepress';
|
||||
import { random } from 'lodash-es'
|
||||
import FakeInput from '../base/FakeInput.vue'
|
||||
|
||||
const { go } = useRouter()
|
||||
const intervalTime = shallowRef()
|
||||
|
||||
const getInitialItems = () => data.icons.slice(0, 48)
|
||||
const items = ref(getInitialItems())
|
||||
let id = items.value.length + 1
|
||||
|
||||
function getRandomNewIcon() {
|
||||
const randomIndex = random(0, 200)
|
||||
const newRandomIcon = data.icons[randomIndex]
|
||||
|
||||
if (items.value.some((item) => item.name === newRandomIcon.name)) {
|
||||
return getRandomNewIcon()
|
||||
}
|
||||
|
||||
return newRandomIcon
|
||||
}
|
||||
|
||||
function insert() {
|
||||
const replaceIndex = random(0, 48)
|
||||
const newIcon = getRandomNewIcon()
|
||||
|
||||
items.value[replaceIndex] = newIcon
|
||||
}
|
||||
|
||||
function startInterval() {
|
||||
intervalTime.value = setInterval(() => {
|
||||
insert()
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
// TODO: Try maybe something else for better pref performance
|
||||
onMounted(() => {
|
||||
window.addEventListener('mousemove', startInterval, { once: true })
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(intervalTime.value)
|
||||
})
|
||||
import { ref } from 'vue';
|
||||
import HomeHeroIconsAnimation from './HomeHeroIconsAnimation.vue'
|
||||
|
||||
const animationRun = ref(1)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card-wrapper">
|
||||
<div class="icons-card">
|
||||
<div class="card-grid">
|
||||
<TransitionGroup name="list" mode="out-in">
|
||||
<div
|
||||
v-for="icon in items"
|
||||
:key="icon.name"
|
||||
class="random-icon"
|
||||
>
|
||||
<LucideIcon
|
||||
v-bind="icon"
|
||||
<HomeHeroIconsAnimation
|
||||
:key="animationRun"
|
||||
@animation-complete="animationRun++"
|
||||
/>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
<FakeInput @click="go('/icons/?focus')" class="search-box">
|
||||
Search {{ data.iconsCount }} icons...
|
||||
</FakeInput>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-wrapper {
|
||||
margin-left: auto;
|
||||
margin-bottom: auto;
|
||||
margin-top: 48px;
|
||||
}
|
||||
.icons-card {
|
||||
background: var(--vp-c-bg-alt);
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
height:100%;
|
||||
max-height: 220px;
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(36px, 1fr));
|
||||
grid-template-rows: repeat(auto-fill, minmax(36px, 1fr));
|
||||
width: 100%;
|
||||
height:100%;
|
||||
max-height: 168px;
|
||||
max-width: 512px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.list-enter-active {
|
||||
transition: all 0.5s cubic-bezier(.85,.85,.25,1.1);
|
||||
}
|
||||
|
||||
.list-enter-from,
|
||||
.list-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.01);
|
||||
}
|
||||
|
||||
.list-leave-active {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: -64px;
|
||||
}
|
||||
|
||||
.random-icon {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.search-box {
|
||||
top: unset;
|
||||
bottom: -24px;
|
||||
left: -24px;
|
||||
|
||||
box-shadow: var(--vp-shadow-3);
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.dark .search-box {
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
.card-wrapper {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import Badge from '../base/Badge.vue';
|
||||
import HomeContainer from './HomeContainer.vue';
|
||||
import { data } from './HomeHeroBefore.data'
|
||||
import { data } from './HomeHeroInfoBefore.data'
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<HomeContainer class="container">
|
||||
<Badge
|
||||
:href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`"
|
||||
>v{{ data.version }}</Badge>
|
||||
</HomeContainer>
|
||||
<Badge :href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`">v{{ data.version }}</Badge>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
margin-block: 0;;
|
||||
margin-block: 0;
|
||||
margin-top: 37px;
|
||||
margin-bottom: -96px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -34,9 +31,9 @@ import { data } from './HomeHeroBefore.data'
|
||||
.container {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -13,9 +13,9 @@ export default {
|
||||
label: 'Lucide documentation for React',
|
||||
},
|
||||
{
|
||||
name: 'lucide-vue-next',
|
||||
name: 'lucide-vue',
|
||||
logo: '/framework-logos/vue.svg',
|
||||
label: 'Lucide documentation for Vue 3',
|
||||
label: 'Lucide documentation for Vue',
|
||||
},
|
||||
{
|
||||
name: 'lucide-svelte',
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -29,7 +29,12 @@ const props = defineProps<{
|
||||
|
||||
const iconComponent = computed(() => {
|
||||
if (!props.name || !props.iconNode) return null;
|
||||
try {
|
||||
return createLucideIcon(props.name, props.iconNode);
|
||||
} catch (error) {
|
||||
console.warn(`Icon ${props.name} not found, using fallback`);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const CalendarIcon = createLucideIcon('calendar', Calendar.iconNode);
|
||||
@@ -61,7 +66,7 @@ const prettyName = props.name
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="showcase">
|
||||
<section class="showcase" v-if="iconComponent">
|
||||
<h2 class="title">See this icon in action</h2>
|
||||
<div class="showcase-grid">
|
||||
<div class="showcase-item column">
|
||||
|
||||
@@ -4,6 +4,7 @@ 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 useFetchTags from '../../composables/useFetchTags';
|
||||
@@ -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;
|
||||
}
|
||||
@@ -154,6 +159,7 @@ watchEffect(() => {
|
||||
<InputSearch
|
||||
:placeholder="`Search ${icons.length} icons ...`"
|
||||
v-model="searchQuery"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
ref="searchInput"
|
||||
@focus="onFocusSearchInput"
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, defineAsyncComponent, onMounted, watch } from 'vue';
|
||||
import { ref, computed, defineAsyncComponent, onMounted, onBeforeUnmount, 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';
|
||||
@@ -58,6 +60,11 @@ 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 },
|
||||
@@ -80,8 +87,13 @@ const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
|
||||
onMounted(() => {
|
||||
containerProps.ref.value = document.documentElement;
|
||||
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;
|
||||
@@ -118,6 +130,7 @@ function handleCloseDrawer() {
|
||||
:placeholder="`Search ${icons.length} icons ...`"
|
||||
v-model="searchQuery"
|
||||
ref="searchInput"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
@focus="onFocusSearchInput"
|
||||
/>
|
||||
@@ -169,6 +182,7 @@ function handleCloseDrawer() {
|
||||
|
||||
.input-wrapper {
|
||||
width: 100%;
|
||||
view-transition-name: icons-search-box;
|
||||
}
|
||||
|
||||
.overview-container {
|
||||
|
||||
@@ -110,7 +110,7 @@ const customizingActive = computed(() => {
|
||||
name="size"
|
||||
v-model="size"
|
||||
:min="16"
|
||||
:max="48"
|
||||
:max="256"
|
||||
:step="4"
|
||||
/>
|
||||
</InputField>
|
||||
|
||||
379
docs/.vitepress/theme/components/overrides/Hero.vue
Normal file
379
docs/.vitepress/theme/components/overrides/Hero.vue
Normal file
@@ -0,0 +1,379 @@
|
||||
<script setup lang="ts">
|
||||
import VPHero from 'vitepress/dist/client/theme-default/components/VPHero.vue'
|
||||
import { useData } from 'vitepress/dist/client/theme-default/composables/data'
|
||||
import FakeInput from '../base/FakeInput.vue';
|
||||
import { useRouter } from 'vitepress';
|
||||
import { data } from '../home/HomeHeroIconsCard.data'
|
||||
import { useScroll } from '@vueuse/core';
|
||||
import { computed } from 'vue';
|
||||
const { go } = useRouter()
|
||||
|
||||
const { frontmatter: fm } = useData()
|
||||
const { x, y, isScrolling, arrivedState, directions } = useScroll(window)
|
||||
|
||||
const opacity = computed(() => {
|
||||
if (y.value < 0) return 1
|
||||
if (y.value > 300) return 0
|
||||
return 1 - (y.value / 300)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPHero v-if="fm.hero" class="VPHomeHero" :name="fm.hero.name" :text="fm.hero.text" :tagline="fm.hero.tagline"
|
||||
:image="undefined" :actions="fm.hero.actions">
|
||||
<template #home-hero-image></template>
|
||||
<template #home-hero-actions-after>
|
||||
<FakeInput @click="go('/icons/?focus')" class="search-box">
|
||||
Search {{ data.iconsCount }} icons...
|
||||
</FakeInput>
|
||||
</template>
|
||||
|
||||
</VPHero>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-12 -12 48 48" fill="none" overflow="auto"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="hero-background" :style="{ opacity: opacity }">
|
||||
<g class="svg-preview-grid-group" stroke-linecap="butt" stroke-width="0.1" stroke="#777"
|
||||
stroke-opacity="0.3">
|
||||
<!-- <rect class="svg-preview-grid-rect" width="23.9" height="23.9" x="0.05" y="0.05" rx="1"></rect> -->
|
||||
<path
|
||||
stroke-dasharray="0 0.1 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0.1 0.15 0 0.15"
|
||||
stroke-width="0.1"
|
||||
d="M1 0.1v23.8M2 0.1v23.8M4 0.1v23.8M5 0.1v23.8M7 0.1v23.8M8 0.1v23.8M10 0.1v23.8M11 0.1v23.8M13 0.1v23.8M14 0.1v23.8M16 0.1v23.8M17 0.1v23.8M19 0.1v23.8M20 0.1v23.8M22 0.1v23.8M23 0.1v23.8M0.1 1h23.8M0.1 2h23.8M0.1 4h23.8M0.1 5h23.8M0.1 7h23.8M0.1 8h23.8M0.1 10h23.8M0.1 11h23.8M0.1 13h23.8M0.1 14h23.8M0.1 16h23.8M0.1 17h23.8M0.1 19h23.8M0.1 20h23.8M0.1 22h23.8M0.1 23h23.8">
|
||||
</path>
|
||||
<path
|
||||
d="M3 0.1v23.8M6 0.1v23.8M9 0.1v23.8M12 0.1v23.8M15 0.1v23.8M18 0.1v23.8M21 0.1v23.8M0.1 3h23.8M0.1 6h23.8M0.1 9h23.8M0.1 12h23.8M0.1 15h23.8M0.1 18h23.8M0.1 21h23.8">
|
||||
</path>
|
||||
</g>
|
||||
<g class="svg-preview-shadow-mask-group" stroke-width="4" stroke="#777" stroke-opacity="0.15">
|
||||
<mask id="svg-preview-shadow-mask-0" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M4.9 16.1h.01M4.9 1.9h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-1" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M7.8 4.7h.01M7 12.2h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-2" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M12 7h.01M14 9h.01M12 11h.01M10 9h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-3" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M16.2 4.8h.01M17 12.27h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-4" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M19.1 1.9h.01M19.1 16h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-5" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M9.5 18h.01M14.5 18h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-shadow-mask-6" maskUnits="userSpaceOnUse" stroke-opacity="1" stroke-width="4" stroke="#000">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#fff" stroke="none" rx="1"></rect>
|
||||
<path d="M8 22h.01M12 11h.01M16 22h.01"></path>
|
||||
</mask>
|
||||
</g>
|
||||
<!-- <g class="svg-preview-shadow-group" stroke-width="4" stroke="#777" stroke-opacity="0.15">
|
||||
<path mask="url(#svg-preview-shadow-mask-0)" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-1)" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-2)" d="M 12 7 A2 2 0 0 1 14 9"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-2)" d="M 14 9 A2 2 0 0 1 12 11"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-2)" d="M 12 11 A2 2 0 0 1 10 9"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-2)" d="M 10 9 A2 2 0 0 1 12 7"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-3)" d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-4)" d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-5)" d="M 9.5 18 L 14.5 18"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-6)" d="M 8 22 L 12 11"></path>
|
||||
<path mask="url(#svg-preview-shadow-mask-6)" d="M 12 11 L 16 22"></path>
|
||||
<path
|
||||
d="M4.9 16.1h.01M4.9 1.9h.01M7.8 4.7h.01M7 12.2h.01M12 7h.01M14 9h.01M12 11h.01M10 9h.01M16.2 4.8h.01M17 12.27h.01M19.1 1.9h.01M19.1 16h.01M9.5 18h.01M14.5 18h.01M8 22h.01M16 22h.01">
|
||||
</path>
|
||||
</g> -->
|
||||
<g>
|
||||
<defs xmlns="http://www.w3.org/2000/svg">
|
||||
<pattern id="backdrop-pattern-:R4:" width=".1" height=".1" patternUnits="userSpaceOnUse"
|
||||
patternTransform="rotate(45 50 50)">
|
||||
<line stroke="red" stroke-width="0.1" y2="1"></line>
|
||||
<line stroke="red" stroke-width="0.1" y2="1"></line>
|
||||
</pattern>
|
||||
</defs>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
</mask>
|
||||
<path d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
</mask>
|
||||
<path
|
||||
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18"
|
||||
stroke="url(#backdrop-pattern-:R4:)" stroke-width="4" stroke-opacity="0.75"
|
||||
mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-1" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
|
||||
</mask>
|
||||
<path
|
||||
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18"
|
||||
stroke="url(#backdrop-pattern-:R4:)" stroke-width="4" stroke-opacity="0.75"
|
||||
mask="url(#svg-preview-backdrop-mask-:R4:-1)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
</mask>
|
||||
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-1" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
|
||||
</mask>
|
||||
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-1)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-2" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white"
|
||||
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18">
|
||||
</path>
|
||||
</mask>
|
||||
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-2)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-0" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
</mask>
|
||||
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-0)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-1" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
|
||||
</mask>
|
||||
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-1)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-2" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white"
|
||||
d="M 12 7 A2 2 0 0 1 14 9 M 14 9 A2 2 0 0 1 12 11 M 12 11 A2 2 0 0 1 10 9 M 10 9 A2 2 0 0 1 12 7 M 8 22 L 12 11 M 12 11 L 16 22 M 9.5 18 L 14.5 18">
|
||||
</path>
|
||||
</mask>
|
||||
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-2)"></path>
|
||||
</g>
|
||||
<g stroke-width="4">
|
||||
<mask id="svg-preview-backdrop-mask-:R4:-3" maskUnits="userSpaceOnUse">
|
||||
<path stroke="white" d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27"></path>
|
||||
</mask>
|
||||
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="url(#backdrop-pattern-:R4:)" stroke-width="4"
|
||||
stroke-opacity="0.75" mask="url(#svg-preview-backdrop-mask-:R4:-3)"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g class="svg-preview-handles-group" stroke-width="0.12" stroke="#777" stroke-opacity="0.6">
|
||||
<path d="M4.9 16.1 1 12.2"></path>
|
||||
<circle cy="12.2" cx="1" r="0.25"></circle>
|
||||
<path d="M4.9 1.9 1 5.8"></path>
|
||||
<circle cy="5.8" cx="1" r="0.25"></circle>
|
||||
<path d="M16.2 4.8 18.2 6.8"></path>
|
||||
<circle cy="6.8" cx="18.2" r="0.25"></circle>
|
||||
<path d="M17 12.27 18.46 9.91"></path>
|
||||
<circle cy="9.91" cx="18.46" r="0.25"></circle>
|
||||
</g>
|
||||
<g class="svg-preview-colored-path-group">
|
||||
<path d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9" stroke="##dfdfd6"></path>
|
||||
<path d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2" stroke="##dfdfd6"></path>
|
||||
<path d="M 12 7 A2 2 0 0 1 14 9" stroke="##dfdfd6"></path>
|
||||
<path d="M 14 9 A2 2 0 0 1 12 11" stroke="##dfdfd6"></path>
|
||||
<path d="M 12 11 A2 2 0 0 1 10 9" stroke="##dfdfd6"></path>
|
||||
<path d="M 10 9 A2 2 0 0 1 12 7" stroke="##dfdfd6"></path>
|
||||
<path d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27" stroke="##dfdfd6"></path>
|
||||
<path d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16" stroke="##dfdfd6"></path>
|
||||
<path d="M 9.5 18 L 14.5 18" stroke="##dfdfd6"></path>
|
||||
<path d="M 8 22 L 12 11" stroke="##dfdfd6"></path>
|
||||
<path d="M 12 11 L 16 22" stroke="##dfdfd6"></path>
|
||||
</g>
|
||||
<g class="svg-preview-radii-group" stroke-width="0.12" stroke-dasharray="0 0.25 0.25" stroke="#777"
|
||||
stroke-opacity="0.3">
|
||||
<circle cx="9.518750780437157" cy="16.261333416579962" r="0.25"></circle>
|
||||
<circle cx="11.118750780437157" cy="1.261333416579964" r="0.25"></circle>
|
||||
<path
|
||||
d="M9.518750780437157 16.261333416579962L4.481249219562843 8.138666583420036L11.118750780437157 1.261333416579964">
|
||||
</path>
|
||||
<circle cx="4.481249219562843" cy="8.138666583420036" r="0.25"></circle>
|
||||
<path d="M7 12.2L12.217985863765243 8.963918492134958L7.8 4.7"></path>
|
||||
<circle cy="8.963918492134958" cx="12.217985863765243" r="0.25" stroke-dasharray="0" stroke="red"></circle>
|
||||
<circle cy="8.963918492134958" cx="12.217985863765243" r="6.14" stroke="red"></circle>
|
||||
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
|
||||
<circle cy="9" cx="12" r="2"></circle>
|
||||
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
|
||||
<circle cy="9" cx="12" r="2"></circle>
|
||||
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
|
||||
<circle cy="9" cx="12" r="2"></circle>
|
||||
<circle cy="9" cx="12" r="0.25" stroke-dasharray="0"></circle>
|
||||
<circle cy="9" cx="12" r="2"></circle>
|
||||
<circle cx="12.035530040810755" cy="23.05" r="0.25"></circle>
|
||||
<circle cx="12.035530040810755" cy="-5.1499999999999995" r="0.25"></circle>
|
||||
<path d="M12.035530040810755 23.05L26.164469959189248 8.95L12.035530040810755 -5.1499999999999995"></path>
|
||||
<circle cx="26.164469959189248" cy="8.95" r="0.25"></circle>
|
||||
<path d="M19.1 16L12.064440320770494 8.95L19.1 1.9"></path>
|
||||
<circle cy="8.95" cx="12.064440320770494" r="0.25" stroke-dasharray="0" stroke="red"></circle>
|
||||
<circle cy="8.95" cx="12.064440320770494" r="9.96" stroke="red"></circle>
|
||||
</g>
|
||||
<g class="svg-preview-control-path-marker-mask-group" stroke-width="1" stroke="#000">
|
||||
<mask id="svg-preview-control-path-marker-mask-0" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M4.9 16.1h.01"></path>
|
||||
<path d="M4.9 1.9h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-1" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M7.8 4.7h.01"></path>
|
||||
<path d="M7 12.2h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-6" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M16.2 4.8h.01"></path>
|
||||
<path d="M17 12.27h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-7" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M19.1 1.9h.01"></path>
|
||||
<path d="M19.1 16h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-8" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M9.5 18h.01"></path>
|
||||
<path d="M14.5 18h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-9" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M8 22h.01"></path>
|
||||
<path d="M12 11h.01"></path>
|
||||
</mask>
|
||||
<mask id="svg-preview-control-path-marker-mask-10" maskUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="#ccc" stroke="none" rx="1"></rect>
|
||||
<path d="M12 11h.01"></path>
|
||||
<path d="M16 22h.01"></path>
|
||||
</mask>
|
||||
</g>
|
||||
<g class="svg-preview-control-path-group" stroke="#fff" stroke-width="0.125">
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-0)" d="M 4.9 16.1 C1 12.2 1 5.8 4.9 1.9"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-1)" d="M 7.8 4.7 A6.14 6.14 0 0 0 7 12.2"></path>
|
||||
<path d="M 12 7 A2 2 0 0 1 14 9"></path>
|
||||
<path d="M 14 9 A2 2 0 0 1 12 11"></path>
|
||||
<path d="M 12 11 A2 2 0 0 1 10 9"></path>
|
||||
<path d="M 10 9 A2 2 0 0 1 12 7"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-6)" d="M 16.2 4.8 C18.2 6.8 18.46 9.91 17 12.27"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-7)" d="M 19.1 1.9 A9.96 9.96 0 0 1 19.1 16"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-8)" d="M 9.5 18 L 14.5 18"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-9)" d="M 8 22 L 12 11"></path>
|
||||
<path mask="url(#svg-preview-control-path-marker-mask-10)" d="M 12 11 L 16 22"></path>
|
||||
</g>
|
||||
<g class="svg-preview-control-path-marker-group" stroke="#fff" stroke-width="0.125">
|
||||
<path
|
||||
d="M4.9 16.1h.01M4.9 1.9h.01M7.8 4.7h.01M7 12.2h.01M16.2 4.8h.01M17 12.27h.01M19.1 1.9h.01M19.1 16h.01M9.5 18h.01M14.5 18h.01M8 22h.01M12 11h.01M12 11h.01M16 22h.01">
|
||||
</path>
|
||||
<circle cx="4.9" cy="16.1" r="0.5"></circle>
|
||||
<circle cx="4.9" cy="1.9" r="0.5"></circle>
|
||||
<circle cx="7.8" cy="4.7" r="0.5"></circle>
|
||||
<circle cx="7" cy="12.2" r="0.5"></circle>
|
||||
<circle cx="16.2" cy="4.8" r="0.5"></circle>
|
||||
<circle cx="17" cy="12.27" r="0.5"></circle>
|
||||
<circle cx="19.1" cy="1.9" r="0.5"></circle>
|
||||
<circle cx="19.1" cy="16" r="0.5"></circle>
|
||||
<circle cx="9.5" cy="18" r="0.5"></circle>
|
||||
<circle cx="14.5" cy="18" r="0.5"></circle>
|
||||
<circle cx="8" cy="22" r="0.5"></circle>
|
||||
<circle cx="16" cy="22" r="0.5"></circle>
|
||||
</g>
|
||||
<g class="svg-preview-handles-group" stroke-width="0.12" style="stroke: var(--vp-c-brand)" stroke-opacity="0.3">
|
||||
<path d="M4.9 16.1 1 12.2"></path>
|
||||
<circle cy="12.2" cx="1" r="0.25"></circle>
|
||||
<path d="M4.9 1.9 1 5.8"></path>
|
||||
<circle cy="5.8" cx="1" r="0.25"></circle>
|
||||
<path d="M16.2 4.8 18.2 6.8"></path>
|
||||
<circle cy="6.8" cx="18.2" r="0.25"></circle>
|
||||
<path d="M17 12.27 18.46 9.91"></path>
|
||||
<circle cy="9.91" cx="18.46" r="0.25"></circle>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
overflow: hidden;
|
||||
height: 60vh;
|
||||
display: flex;
|
||||
position: relative;
|
||||
/* align-items: center;
|
||||
justify-content: center; */
|
||||
}
|
||||
|
||||
.hero-background {
|
||||
transform: rotateX(-51deg) rotateZ(-43deg);
|
||||
transform-style: preserve-3d;
|
||||
position: fixed;
|
||||
top: -240px;
|
||||
right: -320px;
|
||||
width: 112vw;
|
||||
height: 112vh;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 3.2rem;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
/* .VPHomeHero .image {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.VPHomeHero .container {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.VPHomeHero .main {
|
||||
text-align: center;
|
||||
} */
|
||||
|
||||
/* .VPHomeHero .container .actions {
|
||||
justify-content: center;
|
||||
} */
|
||||
/*
|
||||
@media (min-width: 960px) {
|
||||
.VPHomeHero :deep(.actions) {
|
||||
justify-content: center;
|
||||
}
|
||||
} */
|
||||
|
||||
@media screen and (prefers-color-scheme: light) {
|
||||
.svg-preview-grid-rect { fill: none }
|
||||
}
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
.svg-preview-grid-rect { fill: none }
|
||||
.svg
|
||||
.svg-preview-grid-group,
|
||||
.svg-preview-radii-group,
|
||||
.svg-preview-shadow-mask-group,
|
||||
.svg-preview-shadow-group {
|
||||
stroke: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.search-box {
|
||||
/* width: calc(100vw - 272px); */
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
}
|
||||
</style>
|
||||
248
docs/.vitepress/theme/components/overrides/HeroMatrix.vue
Normal file
248
docs/.vitepress/theme/components/overrides/HeroMatrix.vue
Normal file
@@ -0,0 +1,248 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, type Component } from 'vue';
|
||||
import {
|
||||
heart,
|
||||
star,
|
||||
zap,
|
||||
code,
|
||||
feather,
|
||||
cloud,
|
||||
sun,
|
||||
moon,
|
||||
camera,
|
||||
music,
|
||||
video,
|
||||
globe,
|
||||
layers,
|
||||
package as packageIcon,
|
||||
compass,
|
||||
command,
|
||||
terminal,
|
||||
database,
|
||||
server,
|
||||
cpu,
|
||||
lock,
|
||||
key,
|
||||
shield,
|
||||
wifi,
|
||||
download,
|
||||
upload,
|
||||
search,
|
||||
settings,
|
||||
users,
|
||||
mail,
|
||||
bell,
|
||||
calendar,
|
||||
clock,
|
||||
gitBranch,
|
||||
funnel,
|
||||
bookmark,
|
||||
tag,
|
||||
sparkles,
|
||||
} from '../../../data/iconNodes';
|
||||
import createElement from 'lucide/src/createElement';
|
||||
import { IconNode } from 'lucide';
|
||||
|
||||
const icons: Component[] = [
|
||||
heart, star, zap, code, feather, cloud, sun, moon, camera, music,
|
||||
video, globe, layers, packageIcon, compass, command, terminal, database,
|
||||
server, cpu, lock, key, shield, wifi, download, upload, search,
|
||||
settings, users, mail, bell, calendar, clock, gitBranch, funnel,
|
||||
bookmark, tag, sparkles,
|
||||
];
|
||||
|
||||
const svgs = icons.map(icon => {
|
||||
const element = createElement(icon as IconNode, {
|
||||
stroke: 'white',
|
||||
opacity: '0.2',
|
||||
})
|
||||
|
||||
return element.outerHTML;
|
||||
});
|
||||
|
||||
const highlightedSvgs = icons.map(icon => {
|
||||
const element = createElement(icon as IconNode, {
|
||||
stroke: 'white',
|
||||
})
|
||||
|
||||
return element.outerHTML;
|
||||
});
|
||||
|
||||
const images = svgs.map(svg => {
|
||||
const img = new Image();
|
||||
img.src = `data:image/svg+xml;base64,${btoa(svg)}`;
|
||||
return img;
|
||||
});
|
||||
|
||||
const highlightedImages = highlightedSvgs.map(svg => {
|
||||
const img = new Image();
|
||||
img.src = `data:image/svg+xml;base64,${btoa(svg)}`;
|
||||
return img;
|
||||
});
|
||||
|
||||
const canvas = ref<HTMLCanvasElement | null>(null);
|
||||
|
||||
// Setting up the columns
|
||||
const fontSize = 16;
|
||||
const gap = 40;
|
||||
const gapY = 8;
|
||||
const intervalTime = ref<number | null>(null);
|
||||
|
||||
// Store individual drops with their positions
|
||||
let individualDrops: Array<{x: number, y: number, active: boolean}> = [];
|
||||
|
||||
// Random integer between min and max
|
||||
function randInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!canvas.value) return;
|
||||
|
||||
const ctx = canvas.value.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight * 0.6;
|
||||
canvas.value.width = width;
|
||||
canvas.value.height = height;
|
||||
|
||||
let columns = Math.floor(width / 120);
|
||||
|
||||
var rows = Math.floor(canvas.value.height / fontSize);
|
||||
var midStart = Math.floor(rows * 0.25);
|
||||
var midEnd = Math.floor(rows * 0.85);
|
||||
|
||||
let drops = Array.from({ length: columns }, () => randInt(midStart, midEnd));
|
||||
let fps, fpsInterval, startTime, now, then, elapsed;
|
||||
|
||||
// Add click event listener
|
||||
function handleCanvasClick(event: MouseEvent) {
|
||||
const rect = canvas.value?.getBoundingClientRect();
|
||||
if (!rect) return;
|
||||
|
||||
const x = event.clientX - rect.left;
|
||||
const y = event.clientY - rect.top;
|
||||
|
||||
individualDrops.push({
|
||||
x: x,
|
||||
y: y,
|
||||
active: true
|
||||
});
|
||||
}
|
||||
|
||||
canvas.value.addEventListener('click', handleCanvasClick);
|
||||
|
||||
function draw() {
|
||||
if (!ctx) return;
|
||||
|
||||
ctx.fillStyle = 'rgba(27, 27, 31, 0.50)';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
|
||||
|
||||
// Draw regular drops
|
||||
for (var i = drops.length; i > 0; i--) {
|
||||
var img = images[Math.floor(Math.random() * images.length)];
|
||||
ctx.drawImage(img, i * fontSize * 2 + gap, drops[i] * fontSize, (fontSize / 2) + gapY, (fontSize / 2) + gapY);
|
||||
|
||||
drops[i]++;
|
||||
if (Math.random() > .98) {
|
||||
drops[i] = randInt(midStart, midEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw and update individual drops
|
||||
individualDrops = individualDrops.filter(drop => {
|
||||
if (!drop.active) return false;
|
||||
|
||||
var img = highlightedImages[Math.floor(Math.random() * images.length)];
|
||||
ctx.drawImage(img, drop.x - fontSize/2, drop.y, fontSize, fontSize);
|
||||
|
||||
drop.y += fontSize
|
||||
|
||||
// Remove if off screen
|
||||
if (drop.y > height) {
|
||||
drop.active = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function startAnimating(fps) {
|
||||
fpsInterval = 1000 / fps;
|
||||
then = Date.now();
|
||||
startTime = then;
|
||||
animate();
|
||||
}
|
||||
function animate() {
|
||||
|
||||
// request another frame
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
// calc elapsed time since last loop
|
||||
|
||||
now = Date.now();
|
||||
elapsed = now - then;
|
||||
|
||||
// if enough time has elapsed, draw the next frame
|
||||
|
||||
if (elapsed > fpsInterval) {
|
||||
|
||||
// Get ready for next frame by setting then=now, but also adjust for your
|
||||
// specified fpsInterval not being a multiple of RAF's interval (16.7ms)
|
||||
then = now - (elapsed % fpsInterval);
|
||||
|
||||
// Put your drawing code here
|
||||
draw();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
startAnimating(12);
|
||||
|
||||
window.addEventListener('resize', function() {
|
||||
canvas.value.width = window.innerWidth;
|
||||
canvas.value.height = window.innerHeight;
|
||||
rows = Math.floor(canvas.value.height / fontSize);
|
||||
columns = Math.floor(canvas.value.width / fontSize);
|
||||
|
||||
var oldDrops = drops.slice();
|
||||
drops = [];
|
||||
for (var i = 0; i < columns; i++) {
|
||||
drops[i] = oldDrops[i % oldDrops.length] ?? randInt(midStart, midEnd);
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup event listener
|
||||
onBeforeUnmount(() => {
|
||||
canvas.value?.removeEventListener('click', handleCanvasClick);
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="hero">
|
||||
<canvas ref="canvas" class="hero-canvas" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.hero {
|
||||
overflow: hidden;
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.hero-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 60vh;
|
||||
filter: blur(0px);
|
||||
}
|
||||
</style>
|
||||
@@ -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>;
|
||||
|
||||
@@ -1,27 +1,36 @@
|
||||
import { h } from 'vue';
|
||||
import { h, nextTick, onMounted, watch } from 'vue';
|
||||
import DefaultTheme from 'vitepress/theme';
|
||||
import './style.css';
|
||||
import { Theme } from 'vitepress';
|
||||
import 'virtual:group-icons.css'
|
||||
import { Theme, useRouter } from 'vitepress';
|
||||
import IconsSidebarNavAfter from './layouts/IconsSidebarNavAfter.vue';
|
||||
import HomeHeroIconsCard from './components/home/HomeHeroIconsCard.vue';
|
||||
import HomeHeroBefore from './components/home/HomeHeroBefore.vue';
|
||||
import HomeHeroAfter from './components/home/HomeHeroAfter.vue';
|
||||
import HomeHeroInfoBefore from './components/home/HomeHeroInfoBefore.vue';
|
||||
import { ICON_STYLE_CONTEXT, iconStyleContext } from './composables/useIconStyle';
|
||||
import { CATEGORY_VIEW_CONTEXT, categoryViewContext } from './composables/useCategoryView';
|
||||
import { EXTERNAL_LIBS_CONTEXT, externalLibContext } from './composables/useExternalLibs';
|
||||
import FrameworkSelect from './components/guide/FrameworkSelect.vue';
|
||||
import SnackPlayer from './components/editors/SnackPlayer.vue';
|
||||
import Sandbox from './components/editors/Sandbox.vue';
|
||||
|
||||
const theme: Partial<Theme> = {
|
||||
extends: DefaultTheme,
|
||||
Layout() {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
'home-hero-before': () => h(HomeHeroBefore),
|
||||
'sidebar-nav-before': () => h(FrameworkSelect),
|
||||
'home-hero-info-before': () => h(HomeHeroInfoBefore),
|
||||
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
|
||||
'home-hero-image': () => h(HomeHeroIconsCard),
|
||||
'home-hero-actions-after': () => h(HomeHeroAfter),
|
||||
});
|
||||
},
|
||||
enhanceApp({ app }) {
|
||||
app.provide(ICON_STYLE_CONTEXT, iconStyleContext);
|
||||
app.provide(CATEGORY_VIEW_CONTEXT, categoryViewContext);
|
||||
app.provide(EXTERNAL_LIBS_CONTEXT, externalLibContext);
|
||||
app.component('SnackPlayer', SnackPlayer)
|
||||
app.component('Sandbox', Sandbox);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -26,6 +26,12 @@
|
||||
|
||||
--vp-c-text-4: rgba(60, 60, 67, 0.32);
|
||||
--vp-home-hero-name-color: var(--vp-c-text);
|
||||
|
||||
--vp-a11y-danger: var(--vp-c-danger-3);
|
||||
--vp-a11y-success: var(--vp-c-success-3);
|
||||
--vp-a11y-warning: #da9200;
|
||||
|
||||
--vp-sidebar-input: var(--vp-c-gray-3);
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -43,8 +49,23 @@
|
||||
--vp-code-editor-string: #9ecbff;
|
||||
|
||||
--vp-c-text-4: rgba(235, 235, 245, 0.16);
|
||||
|
||||
--vp-a11y-danger: var(--vp-c-danger-2);
|
||||
--vp-a11y-success: var(--vp-c-success-2);
|
||||
--vp-a11y-warning: var(--vp-c-warning-2);
|
||||
|
||||
--vp-sidebar-input: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
@view-transition {
|
||||
navigation: auto;
|
||||
}
|
||||
|
||||
/* ::view-transition-old(icons-search-box),
|
||||
::view-transition-new(icons-search-box) {
|
||||
animation: .3s transition 0s ease;
|
||||
} */
|
||||
|
||||
.VPNavBarTitle .logo {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
@@ -70,7 +91,7 @@
|
||||
.VPHomeHero .container .image {
|
||||
margin: 0;
|
||||
order: 2;
|
||||
margin-top: 32px;
|
||||
/* margin-top: 32px; */
|
||||
}
|
||||
|
||||
.VPHomeHero .container .image-container {
|
||||
@@ -91,6 +112,10 @@
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.VPHomeHero .container {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.VPHomeHero .container .image {
|
||||
order: 1;
|
||||
@@ -110,60 +135,12 @@
|
||||
.VPHomeHero .container .image-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.VPHomeHero .container {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.VPNavBarHamburger .container > span {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.sp-wrapper + * {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-layout {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs-scrollable-container {
|
||||
border-radius: 8px 8px 0 0;
|
||||
position: relative;
|
||||
|
||||
box-shadow: inset 0 -1px var(--vp-code-tab-divider);
|
||||
margin-bottom: 0px;
|
||||
margin-top: -1px;
|
||||
height: 48px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-preview-container {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button {
|
||||
padding: 0 12px;
|
||||
line-height: 48px;
|
||||
height: 48px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button:after {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
left: 8px;
|
||||
bottom: 0px;
|
||||
z-index: 1;
|
||||
height: 1px;
|
||||
content: '';
|
||||
background-color: transparent;
|
||||
transition: background-color 0.25s;
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true'] {
|
||||
color: var(--vp-code-tab-active-text-color);
|
||||
}
|
||||
|
||||
.sp-wrapper .sp-tabs .sp-tab-button[data-active='true']:after {
|
||||
background-color: var(--vp-code-tab-active-bar-color);
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
26
docs/.vitepress/vue-shim.d.ts
vendored
26
docs/.vitepress/vue-shim.d.ts
vendored
@@ -11,6 +11,12 @@ declare module '*.data.ts' {
|
||||
export { data };
|
||||
}
|
||||
|
||||
declare module '*.data' {
|
||||
const data: any;
|
||||
|
||||
export { data };
|
||||
}
|
||||
|
||||
declare module '*.wasm' {}
|
||||
|
||||
declare const resvg_wasm: RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
@@ -22,3 +28,23 @@ declare module 'node:module' {
|
||||
declare module '*.node.json' {
|
||||
export default IconNode;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
ExpoSnack?: {
|
||||
/**
|
||||
* Initialize all snack players on the page
|
||||
*/
|
||||
initialize(): void;
|
||||
/**
|
||||
* Remove a snack player container
|
||||
*/
|
||||
remove(container: Element): void;
|
||||
/**
|
||||
* Append/add a snack player container
|
||||
*/
|
||||
append(container: Element): void;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: Accessibility
|
||||
---
|
||||
|
||||
# Accessibility
|
||||
# Accessibility in dept
|
||||
|
||||
Icons are pictures that show what something means without using words. They can be very helpful
|
||||
because they can quickly give information.
|
||||
@@ -17,7 +17,7 @@ Icons are a helpful tool to improve perception, but they aren't a replacement fo
|
||||
In most cases, it is probably a good idea to also provide a textual representation of your icon's
|
||||
function.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/visible-labels.svg -->
|
||||
|
||||
## Contrast
|
||||
|
||||
@@ -27,21 +27,21 @@ with low vision or color vision deficiencies.
|
||||
We recommend
|
||||
following [WCAG 2.1 SC 1.4.3](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html).
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/contrast.svg -->
|
||||
|
||||
## Use of color
|
||||
|
||||
Avoid relying solely on color to convey meaning in icons, as some users may have color blindness.
|
||||
Instead, use additional visual cues like shape, shading or text.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/use-of-color.svg -->
|
||||
|
||||
## Interactivity
|
||||
|
||||
Ensure that interactive icons are accessible via keyboard navigation and provide clear feedback when
|
||||
activated.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/interactivity.svg -->
|
||||
|
||||
In most cases this is easily done by wrapping them in icon buttons.
|
||||
|
||||
@@ -50,7 +50,7 @@ In most cases this is easily done by wrapping them in icon buttons.
|
||||
Small targets can be difficult to click or touch, if your icon is interactive, we recommend that it
|
||||
should have a minimum target size of 44×44 pixels.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/target-size.svg -->
|
||||
|
||||
In practice, this doesn't necessarily mean that the icon itself should be this large, only its
|
||||
interactive wrapper element.
|
||||
@@ -60,14 +60,14 @@ interactive wrapper element.
|
||||
Icons should represent concepts or actions in a universally understandable way. Avoid using abstract
|
||||
or ambiguous, or culture-specific symbols that might confuse some users.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/meaningfulness.svg -->
|
||||
|
||||
## Consistency
|
||||
|
||||
Maintain consistency in icon design and usage across your interface to help users learn and
|
||||
understand their meanings more easily.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/consistency.svg -->
|
||||
|
||||
## Text Alternatives
|
||||
|
||||
@@ -89,7 +89,7 @@ In case some of your icons stand alone, and they serve a non-decorative function
|
||||
provide the appropriate accessible label for them.
|
||||
:::
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/alttext-standalone.svg -->
|
||||
|
||||
In general try to avoid using functional icons with no interactivity, we recommend that:
|
||||
|
||||
@@ -104,7 +104,8 @@ elements (badges, buttons, nav items etc.) only, _not_ the icons themselves.
|
||||
Do not provide an accessible label to icons when used on a button, as this label will be read out by
|
||||
screen readers, leading to nonsensical text.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/alttext-buttons.svg -->
|
||||
|
||||
|
||||
::: details Code examples
|
||||
|
||||
@@ -132,7 +133,7 @@ the close button of a dialog for example).
|
||||
As previously stated, you should provide your accessible label on the icon button itself, not the
|
||||
contained icon.
|
||||
|
||||

|
||||
<!--@include: ../../docs/images/a11y/alttext-iconbuttons.svg -->
|
||||
|
||||
::: details Code examples
|
||||
|
||||
@@ -215,3 +216,16 @@ We also recommend checking out the following resources about accessibility:
|
||||
- [A11yTalks](https://www.a11ytalks.com/)
|
||||
- [A11y automation tracker](https://a11y-automation.dev/)
|
||||
- [The A11Y Project](https://www.a11yproject.com/)
|
||||
|
||||
<style>
|
||||
svg.a11y-example {
|
||||
max-width: calc(100% + 48px);
|
||||
margin: 0 -24px;
|
||||
}
|
||||
@media (min-width: 480px) {
|
||||
svg.a11y-example {
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
BIN
docs/guide/images/vscode-hover.png
Normal file
BIN
docs/guide/images/vscode-hover.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
@@ -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
|
||||
|
||||
|
||||
@@ -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,25 +64,24 @@ Implementation of the lucide icon library for Vue applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-vue-next
|
||||
pnpm add @lucide/vue
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn add lucide-vue-next
|
||||
yarn add @lucide/vue
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm install lucide-vue-next
|
||||
npm install @lucide/vue
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun add lucide-vue-next
|
||||
bun add @lucide/vue
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
For more details, see the [documentation](packages/lucide-vue-next.md).
|
||||
For Vue 2 use the `lucide-vue` package.
|
||||
For more details, see the [documentation](packages/lucide-vue.md).
|
||||
|
||||
## Svelte
|
||||
|
||||
@@ -91,22 +90,22 @@ Implementation of the lucide icon library for Svelte applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install 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.
|
||||
|
||||
For more details, see the [documentation](packages/lucide-svelte.md).
|
||||
|
||||
@@ -117,7 +116,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 +142,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 +168,7 @@ Implementation of the lucide icon library for preact applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-preact
|
||||
pnpm add lucide-preact
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -196,7 +195,7 @@ Implementation of the lucide icon library for Astro applications.
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install @lucide/astro
|
||||
pnpm add @lucide/astro
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -222,7 +221,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]
|
||||
@@ -248,13 +247,3 @@ The lucide figma plugin.
|
||||
Visit [Figma community page](https://www.figma.com/community/plugin/939567362549682242/Lucide-Icons) to install the plugin.
|
||||
|
||||

|
||||
|
||||
## Flutter
|
||||
|
||||
Implementation of Lucide icon library for Flutter applications.
|
||||
|
||||
```bash
|
||||
flutter pub add lucide_icons
|
||||
```
|
||||
|
||||
For more details, see the [pub.dev](https://pub.dev/packages/lucide_icons).
|
||||
|
||||
@@ -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
|
||||
@@ -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]
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
# Lucide Astro
|
||||
|
||||
Implementation of the lucide icon library for Astro applications.
|
||||
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
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# Lucide Static
|
||||
|
||||
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.
|
||||
|
||||
**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
|
||||
@@ -21,14 +30,7 @@ This package includes the following implementations of Lucide icons:
|
||||
|
||||
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)
|
||||
For production environments, we recommend using a bundler with tree-shaking support to include only the icons you actually use. Consider using one of the framework-specific [packages](../../packages).
|
||||
:::
|
||||
|
||||
## Installation
|
||||
@@ -36,7 +38,7 @@ For production environments, we recommend using a bundler with tree-shaking supp
|
||||
::: code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm install lucide-static
|
||||
pnpm add lucide-static
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
@@ -101,12 +103,21 @@ Make sure you have the correct Webpack loader configured, such as [`url-loader`]
|
||||
|
||||
To import an SVG as a string (e.g., for templating):
|
||||
|
||||
```js
|
||||
::: 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
|
||||
@@ -208,19 +219,27 @@ If you're not using a package manager, you can download the font files directly
|
||||
|
||||
## Node.js
|
||||
|
||||
You can also import Lucide icons in Node.js (CommonJS) projects:
|
||||
You can also import Lucide icons in Node.js projects:
|
||||
|
||||
```js
|
||||
const {messageSquare} = require('lucide-static/lib');
|
||||
::: code-group
|
||||
|
||||
```js [ESM]
|
||||
import {MessageSquare} from 'lucide-static';
|
||||
```
|
||||
|
||||
> Note: Each icon name is in camelCase.
|
||||
```js [CommonJs]
|
||||
const {MessageSquare} = require('lucide-static');
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
> 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;
|
||||
|
||||
@@ -233,7 +252,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,6 +1,13 @@
|
||||
# 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
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user