mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-17 21:57:43 +01:00
Compare commits
226 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0aa132682 | ||
|
|
1a4dd64862 | ||
|
|
3481f70ad7 | ||
|
|
024f21e896 | ||
|
|
c30ccd472e | ||
|
|
28ec03ebc8 | ||
|
|
ab3a31367a | ||
|
|
148bae88d8 | ||
|
|
ca4a38ba85 | ||
|
|
f297765a13 | ||
|
|
4657ccbfff | ||
|
|
db222d4591 | ||
|
|
bafad1c625 | ||
|
|
7f1e95c6e7 | ||
|
|
ca7a87112c | ||
|
|
d542da0a1c | ||
|
|
6fa51d2a22 | ||
|
|
ca53b06af2 | ||
|
|
b5dc96d2c2 | ||
|
|
5e5fe0085f | ||
|
|
0c2a8d774f | ||
|
|
804906dcd8 | ||
|
|
d575743d3a | ||
|
|
42c1faed75 | ||
|
|
42b494f853 | ||
|
|
9bf8a653a3 | ||
|
|
01cff578e5 | ||
|
|
a0b1305258 | ||
|
|
0cfdfa2181 | ||
|
|
a2e8ea32d2 | ||
|
|
8a7e6ba343 | ||
|
|
7a9233f4a7 | ||
|
|
71aef25812 | ||
|
|
33189a81ac | ||
|
|
e3923f87c2 | ||
|
|
08c040a57d | ||
|
|
981c3309ce | ||
|
|
2e0af66d8a | ||
|
|
a789c91213 | ||
|
|
91c95600f3 | ||
|
|
99acf4102c | ||
|
|
86f2dc12f4 | ||
|
|
b9fdde2d09 | ||
|
|
5c494962e1 | ||
|
|
772c5be034 | ||
|
|
ee3483eb1b | ||
|
|
9182c51962 | ||
|
|
eb035fe370 | ||
|
|
b96e6acd2e | ||
|
|
7b62649c39 | ||
|
|
10aff6cf7b | ||
|
|
cfa8924025 | ||
|
|
713e9b8a09 | ||
|
|
8ab6f80e4f | ||
|
|
a5221c236a | ||
|
|
cdd32b5294 | ||
|
|
54c8d4078d | ||
|
|
3302870983 | ||
|
|
0f25ee86a0 | ||
|
|
28686b5bd5 | ||
|
|
8cc143915c | ||
|
|
06372db723 | ||
|
|
114fb08556 | ||
|
|
3bad7f6ced | ||
|
|
e450afe408 | ||
|
|
6fcfc820ca | ||
|
|
55f264bea6 | ||
|
|
675158df16 | ||
|
|
042393a931 | ||
|
|
551635003c | ||
|
|
5c7119f6d1 | ||
|
|
7bfdb2f54a | ||
|
|
e3abcdbcc6 | ||
|
|
62d350140e | ||
|
|
ad90ee7582 | ||
|
|
b0e825cb57 | ||
|
|
d4cbe899fb | ||
|
|
be3ccc8ece | ||
|
|
e56b09d400 | ||
|
|
7e936c8803 | ||
|
|
c9ffa4033f | ||
|
|
32a6187d69 | ||
|
|
2bf20db4bf | ||
|
|
4473df5ad5 | ||
|
|
bc5efd7f0d | ||
|
|
05f209cb04 | ||
|
|
ef89510a47 | ||
|
|
b72cea6399 | ||
|
|
d2152b3cdd | ||
|
|
ea43df6431 | ||
|
|
4042a366e8 | ||
|
|
5e0ba7e897 | ||
|
|
6b4f575992 | ||
|
|
92784de9c5 | ||
|
|
314ee3b255 | ||
|
|
ee7add11bf | ||
|
|
001d0a51fb | ||
|
|
fd4308e755 | ||
|
|
59893119b5 | ||
|
|
faff6a8269 | ||
|
|
3fe74beeb0 | ||
|
|
f8aafd63b9 | ||
|
|
f0422f415c | ||
|
|
14a196d89c | ||
|
|
87a89b30da | ||
|
|
3180910b23 | ||
|
|
46a4041cc7 | ||
|
|
e3166c65df | ||
|
|
d9d7e245a3 | ||
|
|
e19a9cc43a | ||
|
|
5ecfd61397 | ||
|
|
5366782da0 | ||
|
|
262f9e8865 | ||
|
|
7af26002a0 | ||
|
|
e71a776acc | ||
|
|
0a69fb9e52 | ||
|
|
3648e02992 | ||
|
|
64504da1cd | ||
|
|
e166ba4e80 | ||
|
|
1123214b13 | ||
|
|
4140ba5612 | ||
|
|
019f38281c | ||
|
|
633595d1c1 | ||
|
|
df58c3ff2f | ||
|
|
e7abba5273 | ||
|
|
2693da38d3 | ||
|
|
ab59a1ec36 | ||
|
|
315c020279 | ||
|
|
494f7953c8 | ||
|
|
a5ea3a5368 | ||
|
|
6486f76c55 | ||
|
|
e32db96f1f | ||
|
|
73f70c885a | ||
|
|
14c7ea8e13 | ||
|
|
9fc7653dc1 | ||
|
|
2b007e7962 | ||
|
|
bb1e470f3a | ||
|
|
4a1e6bbfa5 | ||
|
|
e5ee997566 | ||
|
|
27c0a136cd | ||
|
|
84b3c46b65 | ||
|
|
1a09e7fb39 | ||
|
|
75e9724072 | ||
|
|
fbaccc7d9f | ||
|
|
c24bc4d7ef | ||
|
|
5c8c8f4362 | ||
|
|
ffd2a62941 | ||
|
|
e77959e1a6 | ||
|
|
149ee36e61 | ||
|
|
88bf60b5d7 | ||
|
|
1e5df716a4 | ||
|
|
fc0ea760e5 | ||
|
|
c85275899a | ||
|
|
288edde1ea | ||
|
|
207ff6c487 | ||
|
|
a1a9a4d839 | ||
|
|
c5804ad9a5 | ||
|
|
c72fb4a28b | ||
|
|
da5ad547b5 | ||
|
|
66cfe527b3 | ||
|
|
d1866225ba | ||
|
|
372735999f | ||
|
|
f3a33346dd | ||
|
|
3373acf596 | ||
|
|
73d5bfc318 | ||
|
|
ddbc074aa3 | ||
|
|
8f4d6b1651 | ||
|
|
d8b08f8fda | ||
|
|
fddacb6260 | ||
|
|
3d0c8691c9 | ||
|
|
47998b05aa | ||
|
|
9a9e051343 | ||
|
|
6c6c8448fa | ||
|
|
49445aad3a | ||
|
|
70656eb4f0 | ||
|
|
930f862547 | ||
|
|
931b7f5376 | ||
|
|
81e44bdc40 | ||
|
|
e6e90944b9 | ||
|
|
f6fd369bfe | ||
|
|
bbf183fe48 | ||
|
|
4f5642b872 | ||
|
|
1dce6a141a | ||
|
|
dbfce919fc | ||
|
|
52adb78df8 | ||
|
|
c5cfbed28c | ||
|
|
1e4fd13852 | ||
|
|
0a0fd1cf6c | ||
|
|
487d03fc4d | ||
|
|
950160ad5a | ||
|
|
b6f5898aee | ||
|
|
a9c1dca801 | ||
|
|
c05c7e333f | ||
|
|
dff42fe326 | ||
|
|
c1f642e20f | ||
|
|
cf9565b69c | ||
|
|
4cc4468d2f | ||
|
|
fd9ab8f17a | ||
|
|
c9101f0f39 | ||
|
|
614ef1a1d5 | ||
|
|
1a441812ac | ||
|
|
4a33e90c65 | ||
|
|
062a64a078 | ||
|
|
95a1ea7255 | ||
|
|
a0a5bc8fc2 | ||
|
|
698eded89b | ||
|
|
a70b713572 | ||
|
|
34530ad805 | ||
|
|
f73aed151a | ||
|
|
2bd7748562 | ||
|
|
da8a6c5a1b | ||
|
|
5736028dfa | ||
|
|
45d2063340 | ||
|
|
f71d3ffd1d | ||
|
|
b8c3a5fa0b | ||
|
|
a4076db69b | ||
|
|
55cb681461 | ||
|
|
09d9bb747d | ||
|
|
42f9cdceca | ||
|
|
c652723b32 | ||
|
|
a44328d8be | ||
|
|
376568239f | ||
|
|
92d05b5fca | ||
|
|
27b5b7eaad | ||
|
|
4de1355e54 | ||
|
|
c8d94bf3e1 |
@@ -4,3 +4,7 @@ coverage
|
|||||||
lib
|
lib
|
||||||
tests
|
tests
|
||||||
node_modules
|
node_modules
|
||||||
|
.eslintrc.js
|
||||||
|
docs/images
|
||||||
|
docs/**/examples/
|
||||||
|
packages/lucide-react/dynamicIconImports.js
|
||||||
|
|||||||
51
.eslintrc.js
51
.eslintrc.js
@@ -1,22 +1,18 @@
|
|||||||
|
const DEFAULT_ATTRS = require('./scripts/render/default-attrs.json');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
root: true,
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
node: true,
|
node: true,
|
||||||
},
|
},
|
||||||
extends: ['airbnb-base', 'prettier'],
|
extends: ['airbnb-base', 'prettier'],
|
||||||
plugins: ['import', 'prettier'],
|
plugins: ['import', '@html-eslint'],
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': 'off',
|
'no-console': 'off',
|
||||||
'no-param-reassign': 'off',
|
'no-param-reassign': 'off',
|
||||||
'no-shadow': 'off',
|
'no-shadow': 'off',
|
||||||
'no-use-before-define': 'off',
|
'no-use-before-define': 'off',
|
||||||
'prettier/prettier': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'all',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'import/no-extraneous-dependencies': [
|
'import/no-extraneous-dependencies': [
|
||||||
'error',
|
'error',
|
||||||
{ devDependencies: ['**/*.test.js', '**/*.spec.js', './scripts/**'] },
|
{ devDependencies: ['**/*.test.js', '**/*.spec.js', './scripts/**'] },
|
||||||
@@ -33,8 +29,45 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
tsconfigRootDir: __dirname,
|
tsconfigRootDir: __dirname,
|
||||||
project: ['./site/tsconfig.json', './packages/*/tsconfig.json'],
|
project: ['./docs/tsconfig.json', './packages/*/tsconfig.json'],
|
||||||
ecmaVersion: 'latest',
|
ecmaVersion: 'latest',
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
},
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['./icons/*.svg'],
|
||||||
|
parser: '@html-eslint/parser',
|
||||||
|
rules: {
|
||||||
|
'@html-eslint/require-doctype': 'off',
|
||||||
|
'@html-eslint/no-duplicate-attrs': 'error',
|
||||||
|
'@html-eslint/no-inline-styles': 'error',
|
||||||
|
'@html-eslint/require-attrs': [
|
||||||
|
'error',
|
||||||
|
...Object.entries(DEFAULT_ATTRS).map(([attr, value]) => ({
|
||||||
|
tag: 'svg',
|
||||||
|
attr,
|
||||||
|
value: String(value),
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
'@html-eslint/indent': ['error', 2],
|
||||||
|
'@html-eslint/no-multiple-empty-lines': ['error', { max: 0 }],
|
||||||
|
'@html-eslint/no-extra-spacing-attrs': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBeforeSelfClose: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@html-eslint/require-closing-tags': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
selfClosing: 'always',
|
||||||
|
allowSelfClosingCustom: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@html-eslint/element-newline': 'error',
|
||||||
|
'@html-eslint/no-trailing-spaces': 'error',
|
||||||
|
'@html-eslint/quotes': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
14
.github/actions/build-and-test.yml
vendored
14
.github/actions/build-and-test.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
name: "Build and Test"
|
name: 'Build and Test'
|
||||||
description: "Builds and test a package"
|
description: 'Builds and test a package'
|
||||||
|
|
||||||
inputs:
|
inputs:
|
||||||
name:
|
name:
|
||||||
@@ -7,18 +7,18 @@ inputs:
|
|||||||
required: true
|
required: true
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
- uses: pnpm/action-setup@v2
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
id: pnpm-install
|
id: pnpm-install
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
|
|||||||
14
.github/actions/check-icons.yml
vendored
14
.github/actions/check-icons.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
name: "Check icons"
|
name: 'Check icons'
|
||||||
description: "Cross-checks icon and category references in JSON descriptors"
|
description: 'Cross-checks icon and category references in JSON descriptors'
|
||||||
|
|
||||||
inputs:
|
inputs:
|
||||||
name:
|
name:
|
||||||
@@ -7,18 +7,18 @@ inputs:
|
|||||||
required: true
|
required: true
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
- uses: pnpm/action-setup@v2
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
id: pnpm-install
|
id: pnpm-install
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
|
|||||||
66
.github/labeler.yml
vendored
66
.github/labeler.yml
vendored
@@ -1,62 +1,92 @@
|
|||||||
# For changed dependencies
|
# For changed dependencies
|
||||||
📦 dependencies:
|
📦 dependencies:
|
||||||
- pnpm-lock.yaml
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- pnpm-lock.yaml
|
||||||
|
|
||||||
# For changes in documentation
|
# For changes in documentation
|
||||||
📖 documentation:
|
📖 documentation:
|
||||||
- docs/*.md
|
- changed-files:
|
||||||
- docs/**/*.md
|
- any-glob-to-any-file:
|
||||||
|
- docs/*.md
|
||||||
|
- docs/**/*.md
|
||||||
|
|
||||||
# For changes in the site, but not markdown files
|
# For changes in the site, but not markdown files
|
||||||
🌍 site:
|
🌍 site:
|
||||||
- 'docs/**'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'docs/**'
|
||||||
|
|
||||||
# For changes in the metadata
|
# For changes in the metadata
|
||||||
🫧 metadata:
|
🫧 metadata:
|
||||||
- 'icons/*.json'
|
- changed-files:
|
||||||
- categories/*
|
- any-glob-to-any-file:
|
||||||
|
- 'icons/*.json'
|
||||||
|
- categories/*
|
||||||
|
|
||||||
# For changes or added icons
|
# For changes or added icons
|
||||||
🎨 icon:
|
🎨 icon:
|
||||||
- 'icons/*.svg'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'icons/*.svg'
|
||||||
|
|
||||||
# For changes in the lucide package
|
# For changes in the lucide package
|
||||||
🧳 lucide package:
|
🧳 lucide package:
|
||||||
- 'packages/lucide/*'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/lucide/*'
|
||||||
|
|
||||||
# For changes in the lucide React package
|
# For changes in the lucide React package
|
||||||
⚛️ react package:
|
⚛️ react package:
|
||||||
- 'packages/lucide-react/*'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/lucide-react/*'
|
||||||
|
|
||||||
# For changes in the lucide React Native package
|
# For changes in the lucide React Native package
|
||||||
⚛️ react native package:
|
⚛️ react native package:
|
||||||
- 'packages/lucide-react-native/*'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/lucide-react-native/*'
|
||||||
|
|
||||||
# For changes in the lucide vue packages
|
# For changes in the lucide vue packages
|
||||||
💎 vue package:
|
💎 vue package:
|
||||||
- 'packages/lucide-vue/*'
|
- changed-files:
|
||||||
- 'packages/lucide-vue-next/*'
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/lucide-vue/*'
|
||||||
|
- 'packages/lucide-vue-next/*'
|
||||||
|
|
||||||
# For changes in the lucide angular package
|
# For changes in the lucide angular package
|
||||||
🅰️ angular package:
|
🅰️ angular package:
|
||||||
- 'packages/lucide-angular/*'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/lucide-angular/*'
|
||||||
|
|
||||||
# For changes in the lucide preact package
|
# For changes in the lucide preact package
|
||||||
⚛️ preact package:
|
⚛️ preact package:
|
||||||
- 'packages/lucide-preact/*'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/lucide-preact/*'
|
||||||
|
|
||||||
# For changes in the lucide svelte package
|
# For changes in the lucide svelte package
|
||||||
🧣 svelte package:
|
🧣 svelte package:
|
||||||
- 'packages/lucide-svelte/*'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/lucide-svelte/*'
|
||||||
|
|
||||||
# For changes in the lucide solid package
|
# For changes in the lucide solid package
|
||||||
🪝 solid package:
|
🪝 solid package:
|
||||||
- 'packages/lucide-solid/*'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/lucide-solid/*'
|
||||||
|
|
||||||
# For changes in the lucide static package
|
# For changes in the lucide static package
|
||||||
🪨 static package:
|
🪨 static package:
|
||||||
- 'packages/lucide-static/*'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/lucide-static/*'
|
||||||
|
|
||||||
# For changes in the lucide flutter package
|
# For changes in the lucide flutter package
|
||||||
🏹 flutter package:
|
🏹 flutter package:
|
||||||
- 'packages/lucide-flutter/*'
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/lucide-flutter/*'
|
||||||
|
|||||||
33
.github/workflows/ci.yml
vendored
33
.github/workflows/ci.yml
vendored
@@ -15,33 +15,17 @@ jobs:
|
|||||||
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
|
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Fetch tags
|
- name: Fetch tags
|
||||||
run: git fetch --all --tags
|
run: git fetch --all --tags
|
||||||
@@ -50,9 +34,6 @@ jobs:
|
|||||||
id: latest-tag
|
id: latest-tag
|
||||||
run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT
|
run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
|
|
||||||
- name: Check if we can patch
|
- name: Check if we can patch
|
||||||
run: .github/workflows/version-up.sh --minor
|
run: .github/workflows/version-up.sh --minor
|
||||||
|
|
||||||
|
|||||||
7
.github/workflows/close-stale-prs.yml
vendored
7
.github/workflows/close-stale-prs.yml
vendored
@@ -1,18 +1,17 @@
|
|||||||
name: Close stale issues and PR
|
name: Close stale issues and PR
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "45 1 * * *"
|
- cron: '45 1 * * *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v3
|
- uses: actions/stale@v9
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
stale-pr-message: This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 5 days.
|
stale-pr-message: This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 5 days.
|
||||||
close-pr-message: This PR was closed because it has been stalled for 5 days with no activity.
|
close-pr-message: This PR was closed because it has been stalled for 5 days with no activity.
|
||||||
close-pr-label: 🧶 stale
|
close-pr-label: 🧶 stale
|
||||||
days-before-stale: 30
|
days-before-stale: 30
|
||||||
days-before-close: 5
|
days-before-close: -1
|
||||||
days-before-pr-close: -1
|
|
||||||
|
|||||||
6
.github/workflows/labeler.yml
vendored
6
.github/workflows/labeler.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
name: "Pull Request Labeler"
|
name: 'Pull Request Labeler'
|
||||||
on:
|
on:
|
||||||
- pull_request_target
|
- pull_request_target
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
triage:
|
triage:
|
||||||
@@ -9,4 +9,4 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v4
|
- uses: actions/labeler@v5
|
||||||
|
|||||||
25
.github/workflows/linting.yml
vendored
Normal file
25
.github/workflows/linting.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: Linting
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linting:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v2
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Run Linter
|
||||||
|
run: pnpm lint
|
||||||
30
.github/workflows/lucide-angular.yml
vendored
30
.github/workflows/lucide-angular.yml
vendored
@@ -11,33 +11,17 @@ jobs:
|
|||||||
lucide-angular:
|
lucide-angular:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm --filter lucide-angular build
|
run: pnpm --filter lucide-angular build
|
||||||
|
|||||||
42
.github/workflows/lucide-font.yml
vendored
42
.github/workflows/lucide-font.yml
vendored
@@ -10,47 +10,27 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
lucide-font:
|
lucide-font:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: ericfennis/lucide-font:latest
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3.4.1
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --filter outline-svg
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Outline svg Icons
|
- name: Outline svg Icons
|
||||||
run: pnpm build:outline-icons
|
run: pnpm build:outline-icons
|
||||||
|
|
||||||
- name: Create directory
|
- name: Create font in ./lucide-font
|
||||||
run: mkdir lucide-font
|
run: pnpm build:font
|
||||||
|
|
||||||
- name: Build font
|
- name: 'Upload to Artifacts'
|
||||||
run: fontcustom compile "./outlined" -h -n "lucide" -o ./lucide-font -F
|
uses: actions/upload-artifact@v3
|
||||||
|
|
||||||
- name: "Upload to Artifacts"
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
with:
|
||||||
name: lucide-font
|
name: lucide-font
|
||||||
path: lucide-font
|
path: lucide-font
|
||||||
|
|||||||
31
.github/workflows/lucide-preact.yml
vendored
31
.github/workflows/lucide-preact.yml
vendored
@@ -5,39 +5,24 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/lucide-preact/**
|
- packages/lucide-preact/**
|
||||||
- tools/build-icons/**
|
- tools/build-icons/**
|
||||||
|
- tools/rollup-plugins/**
|
||||||
- pnpm-lock.yaml
|
- pnpm-lock.yaml
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lucide-preact:
|
lucide-preact:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm --filter lucide-preact build
|
run: pnpm --filter lucide-preact build
|
||||||
|
|||||||
31
.github/workflows/lucide-react-native.yml
vendored
31
.github/workflows/lucide-react-native.yml
vendored
@@ -5,39 +5,24 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/lucide-react-native/**
|
- packages/lucide-react-native/**
|
||||||
- tools/build-icons/**
|
- tools/build-icons/**
|
||||||
|
- tools/rollup-plugins/**
|
||||||
- pnpm-lock.yaml
|
- pnpm-lock.yaml
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lucide-react-native:
|
lucide-react-native:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm --filter lucide-react-native build
|
run: pnpm --filter lucide-react-native build
|
||||||
|
|||||||
32
.github/workflows/lucide-react.yml
vendored
32
.github/workflows/lucide-react.yml
vendored
@@ -5,39 +5,25 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/lucide-react/**
|
- packages/lucide-react/**
|
||||||
- tools/build-icons/**
|
- tools/build-icons/**
|
||||||
|
- tools/rollup-plugins/**
|
||||||
|
- scripts/generateNextJSAliases.mjs
|
||||||
- pnpm-lock.yaml
|
- pnpm-lock.yaml
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lucide-react:
|
lucide-react:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm --filter lucide-react build
|
run: pnpm --filter lucide-react build
|
||||||
|
|||||||
31
.github/workflows/lucide-solid.yml
vendored
31
.github/workflows/lucide-solid.yml
vendored
@@ -5,39 +5,24 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/lucide-solid/**
|
- packages/lucide-solid/**
|
||||||
- tools/build-icons/**
|
- tools/build-icons/**
|
||||||
|
- tools/rollup-plugins/**
|
||||||
- pnpm-lock.yaml
|
- pnpm-lock.yaml
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lucide-solid:
|
lucide-solid:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm --filter lucide-solid build
|
run: pnpm --filter lucide-solid build
|
||||||
|
|||||||
30
.github/workflows/lucide-static.yml
vendored
30
.github/workflows/lucide-static.yml
vendored
@@ -11,33 +11,17 @@ jobs:
|
|||||||
lucide-static:
|
lucide-static:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm --filter lucide-static build
|
run: pnpm --filter lucide-static build
|
||||||
|
|||||||
31
.github/workflows/lucide-svelte.yml
vendored
31
.github/workflows/lucide-svelte.yml
vendored
@@ -5,39 +5,24 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/lucide-svelte/**
|
- packages/lucide-svelte/**
|
||||||
- tools/build-icons/**
|
- tools/build-icons/**
|
||||||
|
- tools/rollup-plugins/**
|
||||||
- pnpm-lock.yaml
|
- pnpm-lock.yaml
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lucide-svelte:
|
lucide-svelte:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm --filter lucide-svelte build
|
run: pnpm --filter lucide-svelte build
|
||||||
|
|||||||
31
.github/workflows/lucide-vue-next.yml
vendored
31
.github/workflows/lucide-vue-next.yml
vendored
@@ -5,39 +5,24 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/lucide-vue-next/**
|
- packages/lucide-vue-next/**
|
||||||
- tools/build-icons/**
|
- tools/build-icons/**
|
||||||
|
- tools/rollup-plugins/**
|
||||||
- pnpm-lock.yaml
|
- pnpm-lock.yaml
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lucide-vue-next:
|
lucide-vue-next:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm --filter lucide-vue-next build
|
run: pnpm --filter lucide-vue-next build
|
||||||
|
|||||||
31
.github/workflows/lucide-vue.yml
vendored
31
.github/workflows/lucide-vue.yml
vendored
@@ -5,39 +5,24 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/lucide-vue/**
|
- packages/lucide-vue/**
|
||||||
- tools/build-icons/**
|
- tools/build-icons/**
|
||||||
|
- tools/rollup-plugins/**
|
||||||
- pnpm-lock.yaml
|
- pnpm-lock.yaml
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lucide-vue:
|
lucide-vue:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm --filter lucide-vue build
|
run: pnpm --filter lucide-vue build
|
||||||
|
|||||||
31
.github/workflows/lucide.yml
vendored
31
.github/workflows/lucide.yml
vendored
@@ -5,39 +5,24 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- packages/lucide/**
|
- packages/lucide/**
|
||||||
- tools/build-icons/**
|
- tools/build-icons/**
|
||||||
|
- tools/rollup-plugins/**
|
||||||
- pnpm-lock.yaml
|
- pnpm-lock.yaml
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lucide:
|
lucide:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm --filter lucide build
|
run: pnpm --filter lucide build
|
||||||
|
|||||||
213
.github/workflows/pull-request.yml
vendored
213
.github/workflows/pull-request.yml
vendored
@@ -4,155 +4,106 @@ on:
|
|||||||
pull_request_target:
|
pull_request_target:
|
||||||
paths:
|
paths:
|
||||||
- 'icons/*.svg'
|
- 'icons/*.svg'
|
||||||
|
branches:
|
||||||
permissions:
|
- main
|
||||||
pull-requests: write
|
- fix-icon-preview
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
add-changed-icons-comment:
|
lint-filenames:
|
||||||
|
if: github.repository == 'lucide-icons/lucide'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||||
- name: Get changed files
|
- name: Get changed files
|
||||||
id: changed-files
|
id: changed-files
|
||||||
uses: tj-actions/changed-files@v35
|
uses: tj-actions/changed-files@v41
|
||||||
|
with:
|
||||||
|
files: icons/*
|
||||||
|
- name: Generate annotations
|
||||||
|
run: node ./scripts/lintFilenames.mjs
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||||
|
|
||||||
|
lint-contributors:
|
||||||
|
if: github.repository == 'lucide-icons/lucide'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||||
|
- name: Get changed files
|
||||||
|
id: changed-files
|
||||||
|
uses: tj-actions/changed-files@v41
|
||||||
|
with:
|
||||||
|
files: icons/*
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
- name: Install simple-git (safer and faster than installing all deps)
|
||||||
|
run: npm install simple-git
|
||||||
|
- name: Generate annotations
|
||||||
|
run: node ./scripts/updateContributors.mjs
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
FETCH_DEPTH: ${{ github.event.pull_request.commits }}
|
||||||
|
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||||
|
- name: Generate annotations
|
||||||
|
env:
|
||||||
|
ANNOTATION_SEVERITY: notice
|
||||||
|
ANNOTATION_TITLE: Contributors have changed!
|
||||||
|
ANNOTATION_DESCRIPTION: Don't add people who have only performed automatic optimizations.
|
||||||
|
run: |
|
||||||
|
git diff --unified=0 -- icons/*.json | # diff icon metadata (unified=0 gives the correct chunk line number)
|
||||||
|
perl -ne '/^(\+|- |@)/ && print' | # get chunks (lines that start with "+++", "@@", "+ ", "- ")
|
||||||
|
perl -pe 's/\n/%0A/' | # url encode line breaks (\n -> %0A)
|
||||||
|
perl -pe 's/%0A(\+\+\+ b\/)/\n\1/g' | # split chunks(one chunk per line)
|
||||||
|
perl -pe "s/\+\+\+ b\/([^@]*)%0A@@ -(\d+)[^\s]* \+(\d+)[^@]*@@(.*)/::$ANNOTATION_SEVERITY file=\1,line=\2,endLine=\3,title=$ANNOTATION_TITLE::$ANNOTATION_DESCRIPTION%0A\4/"
|
||||||
|
# Example for the previous substitution
|
||||||
|
# input: +++ b/icons/accessibility.json%0A@@ -2,0 +3 @@%0A+ "contributors": ["hi"],%0A@@ -13 +14 @@%0A+}%0A
|
||||||
|
# output: ::$ANNOTATION_SEVERITY file=icons/accessibility.json,line=2,endLine=3,title=$ANNOTATION_TITLE::$ANNOTATION_DESCRIPTION%0A%0A+ "contributors": ["hi"],%0A@@ -13 +14 @@%0A+}%0A
|
||||||
|
|
||||||
|
generate-changed-icons-comment:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||||
|
|
||||||
|
- name: Get changed files
|
||||||
|
id: changed-files
|
||||||
|
uses: tj-actions/changed-files@v41
|
||||||
with:
|
with:
|
||||||
files: icons/*.svg
|
files: icons/*.svg
|
||||||
- name: Generate 24px dpi preview
|
|
||||||
id: generate-24px-dpi-preview
|
|
||||||
run: |
|
|
||||||
delimiter="$(openssl rand -hex 8)"
|
|
||||||
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
|
|
||||||
cat "$file" | # get file content
|
|
||||||
tr '\n' ' ' | # remove line breaks
|
|
||||||
sed -e 's/<svg[^>]*>/<svg>/g' | # remove attributes from svg element
|
|
||||||
base64 -w 0 | # encode svg
|
|
||||||
sed "s|.*|<img title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/dpi/24/&.svg\"/> |"
|
|
||||||
done | tr '\n' ' ' >> $GITHUB_OUTPUT
|
|
||||||
echo >> $GITHUB_OUTPUT
|
|
||||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
- name: Generate cohesion check random
|
|
||||||
id: generate-cohesion-check-random
|
|
||||||
run: |
|
|
||||||
delimiter="$(openssl rand -hex 8)"
|
|
||||||
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
for file in $(printf "%s\\n" icons/*.svg | shuf | head -n$(awk -F' ' '{print NF}' <<< '${{ steps.changed-files.outputs.all_changed_files }}')); do
|
|
||||||
cat "$file" | # get file content
|
|
||||||
tr '\n' ' ' | # remove line breaks
|
|
||||||
sed -e 's/<svg[^>]*>/<svg>/g' | # remove attributes from svg element
|
|
||||||
base64 -w 0 | # encode svg
|
|
||||||
sed "s|.*|<img title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/stroke-width/2/&.svg\"/> |"
|
|
||||||
done | tr '\n' ' ' >> $GITHUB_OUTPUT
|
|
||||||
echo >> $GITHUB_OUTPUT
|
|
||||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
- name: Generate cohesion check squares
|
|
||||||
id: generate-cohesion-check-squares
|
|
||||||
run: |
|
|
||||||
delimiter="$(openssl rand -hex 8)"
|
|
||||||
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
for file in $(printf "%s\\n" icons/*square*.svg | shuf | head -n$(awk -F' ' '{print NF}' <<< '${{ steps.changed-files.outputs.all_changed_files }}')); do
|
|
||||||
cat "$file" | # get file content
|
|
||||||
tr '\n' ' ' | # remove line breaks
|
|
||||||
sed -e 's/<svg[^>]*>/<svg>/g' | # remove attributes from svg element
|
|
||||||
base64 -w 0 | # encode svg
|
|
||||||
sed "s|.*|<img title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/stroke-width/2/&.svg\"/> |"
|
|
||||||
done | tr '\n' ' ' >> $GITHUB_OUTPUT
|
|
||||||
echo >> $GITHUB_OUTPUT
|
|
||||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
- name: Generate 1px stroke-width
|
|
||||||
id: generate-1px-stroke-width
|
|
||||||
run: |
|
|
||||||
delimiter="$(openssl rand -hex 8)"
|
|
||||||
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
|
|
||||||
cat "$file" | # get file content
|
|
||||||
tr '\n' ' ' | # remove line breaks
|
|
||||||
sed -e 's/<svg[^>]*>/<svg>/g' | # remove attributes from svg element
|
|
||||||
base64 -w 0 | # encode svg
|
|
||||||
sed "s|.*|<img title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/stroke-width/1/&.svg\"/> |"
|
|
||||||
done | tr '\n' ' ' >> $GITHUB_OUTPUT
|
|
||||||
echo >> $GITHUB_OUTPUT
|
|
||||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
- name: Generate 2px stroke-width
|
|
||||||
id: generate-2px-stroke-width
|
|
||||||
run: |
|
|
||||||
delimiter="$(openssl rand -hex 8)"
|
|
||||||
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
|
|
||||||
cat "$file" | # get file content
|
|
||||||
tr '\n' ' ' | # remove line breaks
|
|
||||||
sed -e 's/<svg[^>]*>/<svg>/g' | # remove attributes from svg element
|
|
||||||
base64 -w 0 | # encode svg
|
|
||||||
sed "s|.*|<img title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/stroke-width/2/&.svg\"/> |"
|
|
||||||
done | tr '\n' ' ' >> $GITHUB_OUTPUT
|
|
||||||
echo >> $GITHUB_OUTPUT
|
|
||||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
- name: Generate 3px stroke-width
|
|
||||||
id: generate-3px-stroke-width
|
|
||||||
run: |
|
|
||||||
delimiter="$(openssl rand -hex 8)"
|
|
||||||
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
|
|
||||||
cat "$file" | # get file content
|
|
||||||
tr '\n' ' ' | # remove line breaks
|
|
||||||
sed -e 's/<svg[^>]*>/<svg>/g' | # remove attributes from svg element
|
|
||||||
base64 -w 0 | # encode svg
|
|
||||||
sed "s|.*|<img title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/stroke-width/3/&.svg\"/> |"
|
|
||||||
done | tr '\n' ' ' >> $GITHUB_OUTPUT
|
|
||||||
echo >> $GITHUB_OUTPUT
|
|
||||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
- name: Generate X-rays
|
|
||||||
id: generate-x-rays
|
|
||||||
run: |
|
|
||||||
delimiter="$(openssl rand -hex 8)"
|
|
||||||
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
|
|
||||||
cat "$file" | # get file content
|
|
||||||
tr '\n' ' ' | # remove line breaks
|
|
||||||
sed -e 's/<svg[^>]*>/<svg>/g' | # remove attributes from svg element
|
|
||||||
base64 -w 0 | # encode svg
|
|
||||||
sed "s|.*|<img width=\"400\" title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/$(basename ${file//\.svg/})/&.svg\"/> |"
|
|
||||||
done | tr '\n' ' ' >> $GITHUB_OUTPUT
|
|
||||||
echo >> $GITHUB_OUTPUT
|
|
||||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
- name: Find Comment
|
- name: Find Comment
|
||||||
uses: peter-evans/find-comment@v2
|
uses: peter-evans/find-comment@v2
|
||||||
id: fc
|
id: pr-comment
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
comment-author: 'github-actions[bot]'
|
comment-author: 'github-actions[bot]'
|
||||||
body-includes: Added or changed icons
|
body-includes: Added or changed icons
|
||||||
|
|
||||||
|
- name: Generate comment markup
|
||||||
|
run: node ./scripts/generateChangedIconsCommentMarkup.mjs >> comment-markup.md
|
||||||
|
id: comment-markup
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||||
|
|
||||||
- name: Create or update comment
|
- name: Create or update comment
|
||||||
uses: peter-evans/create-or-update-comment@v2
|
uses: peter-evans/create-or-update-comment@v3
|
||||||
with:
|
with:
|
||||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
comment-id: ${{ steps.pr-comment.outputs.comment-id }}
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
body: |
|
body-path: ./comment-markup.md
|
||||||
### Added or changed icons
|
|
||||||
${{ steps.generate-2px-stroke-width.outputs.body }}<br/>
|
|
||||||
<details>
|
|
||||||
<summary>Preview cohesion</summary>
|
|
||||||
${{ steps.generate-cohesion-check-squares.outputs.body }}<br/>
|
|
||||||
${{ steps.generate-2px-stroke-width.outputs.body }}<br/>
|
|
||||||
${{ steps.generate-cohesion-check-random.outputs.body }}<br/>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>Preview stroke widths</summary>
|
|
||||||
${{ steps.generate-1px-stroke-width.outputs.body }}<br/>
|
|
||||||
${{ steps.generate-2px-stroke-width.outputs.body }}<br/>
|
|
||||||
${{ steps.generate-3px-stroke-width.outputs.body }}<br/>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>DPI Preview (24px)</summary>
|
|
||||||
${{ steps.generate-24px-dpi-preview.outputs.body }}<br/>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>Icon X-rays</summary>
|
|
||||||
${{ steps.generate-x-rays.outputs.body }}
|
|
||||||
</details>
|
|
||||||
edit-mode: replace
|
edit-mode: replace
|
||||||
|
|||||||
114
.github/workflows/release.yml
vendored
114
.github/workflows/release.yml
vendored
@@ -39,8 +39,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: pre-release
|
needs: pre-release
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
package: [
|
package:
|
||||||
|
[
|
||||||
'lucide',
|
'lucide',
|
||||||
'lucide-react',
|
'lucide-react',
|
||||||
'lucide-react-native',
|
'lucide-react-native',
|
||||||
@@ -52,30 +54,14 @@ jobs:
|
|||||||
'lucide-svelte',
|
'lucide-svelte',
|
||||||
]
|
]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: pnpm/action-setup@v2
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
|
cache: 'pnpm'
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
|
||||||
version: 7
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
@@ -100,32 +86,15 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [pre-release, lucide-font]
|
needs: [pre-release, lucide-font]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/download-artifact@v2
|
- uses: actions/download-artifact@v3
|
||||||
|
- uses: pnpm/action-setup@v2
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
@@ -149,47 +118,27 @@ jobs:
|
|||||||
if: github.repository == 'lucide-icons/lucide'
|
if: github.repository == 'lucide-icons/lucide'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: pre-release
|
needs: pre-release
|
||||||
container: ericfennis/lucide-font:latest
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3.4.1
|
- uses: pnpm/action-setup@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
- uses: pnpm/action-setup@v2.0.1
|
|
||||||
name: Install pnpm
|
|
||||||
id: pnpm-install
|
|
||||||
with:
|
with:
|
||||||
version: 7
|
node-version: 18
|
||||||
run_install: false
|
cache: 'pnpm'
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-cache
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --filter outline-svg
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Outline svg Icons
|
- name: Outline svg Icons
|
||||||
run: pnpm build:outline-icons
|
run: pnpm build:outline-icons
|
||||||
|
|
||||||
- name: Create directory
|
- name: Create font in ./lucide-font
|
||||||
run: mkdir lucide-font
|
run: pnpm build:font
|
||||||
|
|
||||||
- name: Build font
|
- name: 'Upload to Artifacts'
|
||||||
run: fontcustom compile "./outlined" -h -n "lucide" -o ./lucide-font -F
|
uses: actions/upload-artifact@v3
|
||||||
|
|
||||||
- name: "Upload to Artifacts"
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
with:
|
||||||
name: lucide-font
|
name: lucide-font
|
||||||
path: lucide-font
|
path: lucide-font
|
||||||
@@ -197,14 +146,11 @@ jobs:
|
|||||||
post-release:
|
post-release:
|
||||||
if: github.repository == 'lucide-icons/lucide'
|
if: github.repository == 'lucide-icons/lucide'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [
|
needs: [pre-release, lucide-font]
|
||||||
pre-release,
|
|
||||||
lucide-font,
|
|
||||||
]
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/download-artifact@v2
|
- uses: actions/download-artifact@v3
|
||||||
- name: Zip font and icons
|
- name: Zip font and icons
|
||||||
run: |
|
run: |
|
||||||
zip -r lucide-font-${{ needs.pre-release.outputs.VERSION }}.zip lucide-font
|
zip -r lucide-font-${{ needs.pre-release.outputs.VERSION }}.zip lucide-font
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ stats
|
|||||||
node_modules
|
node_modules
|
||||||
tests
|
tests
|
||||||
scripts
|
scripts
|
||||||
site
|
|
||||||
src
|
src
|
||||||
build
|
build
|
||||||
babel.config.js
|
babel.config.js
|
||||||
|
|||||||
19
.prettierignore
Normal file
19
.prettierignore
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
# docs examples
|
||||||
|
docs/**/examples/
|
||||||
|
|
||||||
|
# lucide-angular
|
||||||
|
packages/lucide-angular/.angular/cache
|
||||||
|
|
||||||
|
# lucide-static
|
||||||
|
packages/lucide-static/icons
|
||||||
|
packages/lucide-static/lib
|
||||||
|
packages/lucide-static/sprite.svg
|
||||||
|
packages/lucide-static/tags.json
|
||||||
|
packages/lucide-static/icon-nodes.json
|
||||||
|
packages/lucide-static/font
|
||||||
|
|
||||||
|
# lucide-svelte
|
||||||
|
packages/lucide-svelte/src/icons/*.svelte
|
||||||
|
packages/lucide-svelte/.svelte-kit
|
||||||
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": ["devs", "preact", "Preact"],
|
||||||
"devs",
|
"eslint.enable": true,
|
||||||
"preact",
|
"eslint.validate": ["javascript", "svg"],
|
||||||
"Preact"
|
"svg.preview.background": "transparent"
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
2
.vscode/svg.code-snippets
vendored
2
.vscode/svg.code-snippets
vendored
@@ -49,7 +49,7 @@
|
|||||||
"circle",
|
"circle",
|
||||||
"<circle"
|
"<circle"
|
||||||
],
|
],
|
||||||
"body": "<circle cx=\"${2:12}\" cy=\"${3:$2}\" r=\"${1|10,2,.5|}\" />"
|
"body": "<circle cx=\"${2:12}\" cy=\"${3:$2}\" r=\"${1|10,2,.5\" fill=\"currentColor|}\" />"
|
||||||
},
|
},
|
||||||
"Ellipse": {
|
"Ellipse": {
|
||||||
"scope": "xml",
|
"scope": "xml",
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ Here you can find instructions on how to implement the guidelines with different
|
|||||||
|
|
||||||
#### [Adobe Illustrator Guide](https://lucide.dev/docs/illustrator-guide)
|
#### [Adobe Illustrator Guide](https://lucide.dev/docs/illustrator-guide)
|
||||||
|
|
||||||
You can also [download an Adobe Illustrator template](https://lucide.dev/templates/illustrator-template.ai).
|
You can also [download an Adobe Illustrator template](https://github.com/lucide-icons/lucide/blob/main/docs/public/templates/illustrator_template.ai).
|
||||||
|
|
||||||
#### [Inkscape Guide](https://lucide.dev/docs/inkscape-guide)
|
#### [Inkscape Guide](https://lucide.dev/docs/inkscape-guide)
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ Seperate them by two PRs; 'pr-01' `arrow`, `arrow-down` and 'pr-02' `bicycle`.
|
|||||||
|
|
||||||
Before creating an icon request, please search to see if someone has requested the icon already. If there is an open request, please add a :+1:.
|
Before creating an icon request, please search to see if someone has requested the icon already. If there is an open request, please add a :+1:.
|
||||||
|
|
||||||
If the icon has not already been requested, [create an issue](https://github.com/lucide-icons/lucide/issues/new?title=Icon%20Request:) with a title of `Icon request: <icon name>` and add as much information as possible.
|
If the icon has not already been requested, [create an icon request issue](https://github.com/lucide-icons/lucide/issues/new?assignees=&labels=%F0%9F%99%8C+icon+request&projects=&template=01_icon_request.yml) and add as much information as possible.
|
||||||
|
|
||||||
### Icon Requests from Feather
|
### Icon Requests from Feather
|
||||||
|
|
||||||
@@ -72,8 +72,8 @@ pnpm install # Install dependencies, including the workspace packages
|
|||||||
|
|
||||||
To distribute different packages we use PNPM workspaces. Before you start make sure you are familiar with this concept. The concept of working in workspaces is created by Yarn, they have a well written introduction: [yarn workspaces](https://classic.yarnpkg.com/lang/enhttps://lucide.dev/docs/workspaces).
|
To distribute different packages we use PNPM workspaces. Before you start make sure you are familiar with this concept. The concept of working in workspaces is created by Yarn, they have a well written introduction: [yarn workspaces](https://classic.yarnpkg.com/lang/enhttps://lucide.dev/docs/workspaces).
|
||||||
|
|
||||||
The configured directory for workspaces is the [packages](./packages) directory, located in the root directory. There you will find all the current packages from lucide.
|
The configured directory for workspaces is the [packages](https://github.com/lucide-icons/lucide/tree/main/packages) directory, located in the root directory. There you will find all the current packages from lucide.
|
||||||
There are more workspaces defined, see [`pnpm-workspace.yaml`](./pnpm-workspace.yaml).
|
There are more workspaces defined, see [`pnpm-workspace.yaml`](https://github.com/lucide-icons/lucide/blob/main/pnpm-workspace.yaml).
|
||||||
|
|
||||||
> Note: One package is not managed by pnpm: **lucide-flutter**, this package is written in Dart and used pub for publishing.
|
> Note: One package is not managed by pnpm: **lucide-flutter**, this package is written in Dart and used pub for publishing.
|
||||||
|
|
||||||
@@ -144,16 +144,19 @@ Root directories
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
lucide
|
lucide
|
||||||
|
|
|
||||||
├── docs
|
├── docs
|
||||||
|
│ ├── guide
|
||||||
├── icons
|
├── icons
|
||||||
├── packages
|
├── packages
|
||||||
├── scripts
|
└── scripts
|
||||||
└── site
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docs
|
### Docs
|
||||||
|
|
||||||
|
The lucide.dev website is using [vitepress](https://vitepress.dev/) to generate the static website. The markdown files are located in the docs directory.
|
||||||
|
|
||||||
|
### Guides
|
||||||
|
|
||||||
Detailed documentation about: installation, guides, packages, design guides etc.
|
Detailed documentation about: installation, guides, packages, design guides etc.
|
||||||
|
|
||||||
### Icons
|
### Icons
|
||||||
@@ -170,10 +173,6 @@ Includes all the (npm) packages of lucide.
|
|||||||
|
|
||||||
Includes usefully scripts to automate certain jobs. Big part of the scripts is the template generation, for example it generates icon components for all the packages. These scripts are usually executed from the "scripts" section in the package.json.
|
Includes usefully scripts to automate certain jobs. Big part of the scripts is the template generation, for example it generates icon components for all the packages. These scripts are usually executed from the "scripts" section in the package.json.
|
||||||
|
|
||||||
### site
|
|
||||||
|
|
||||||
The lucide.dev website is using [vitepress](https://vitepress.dev/) to generate the static website. The markdown files are located in the docs directory.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The documentation files are located in the [docs](https://github.com/lucide-icons/lucide/tree/main/docs) directory. All these markdown files will be loaded in the build of the lucide.dev website.
|
The documentation files are located in the [docs](https://github.com/lucide-icons/lucide/tree/main/docs) directory. All these markdown files will be loaded in the build of the lucide.dev website.
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
Community-run fork of [Feather Icons](https://github.com/feathericons/feather), open for anyone to contribute icons.
|
Community-run fork of [Feather Icons](https://github.com/feathericons/feather), open for anyone to contribute icons.
|
||||||
|
|
||||||
It began after growing disaffection with the [Feather Icons](https://github.com/feathericons/feather) project moderation. With over 300+ open issues and over 100+ open PRs, the Feather Icons project has been abandoned. This unfortunately means that hundreds of developers and designers wasted their time contributing to Feather Icons with no chance of PRs being accepted.
|
It began after growing dissatisfaction with the [Feather Icons](https://github.com/feathericons/feather) project moderation. With over 300+ open issues and over 100+ open PRs, the Feather Icons project has been abandoned. This unfortunately means that hundreds of developers and designers wasted their time contributing to Feather Icons with no chance of PRs being accepted.
|
||||||
|
|
||||||
Lucide is trying to expand the icon set as much as possible while staying faithful to the original simplistic design language. We do this as a community of devs and designers and hope that you'll join us!
|
Lucide is trying to expand the icon set as much as possible while staying faithful to the original simplistic design language. We do this as a community of devs and designers and hope that you'll join us!
|
||||||
|
|
||||||
### Why choose Lucide over Feather Icons
|
### Why choose Lucide over Feather Icons
|
||||||
|
|
||||||
- More icons to work with: Lucide already has hundreds of icons more than Feather does.
|
- More icons to work with: Lucide already has hundreds of icons more than Feather does.
|
||||||
- Official librairies and integrations with popular frameworks and design tools.
|
- Official libraries and integrations with popular frameworks and design tools.
|
||||||
- Well maintained code base.
|
- Well maintained code base.
|
||||||
- Active community, regularly growing and improving the set.
|
- Active community, regularly growing and improving the set.
|
||||||
|
|
||||||
@@ -35,7 +35,6 @@ Lucide is trying to expand the icon set as much as possible while staying faithf
|
|||||||
- [Static (svg sprite, font, icons ..)](#static-svg-sprite-font-icons-)
|
- [Static (svg sprite, font, icons ..)](#static-svg-sprite-font-icons-)
|
||||||
- [Figma](#figma)
|
- [Figma](#figma)
|
||||||
- [Laravel](#laravel)
|
- [Laravel](#laravel)
|
||||||
- [Flutter](#flutter)
|
|
||||||
- [Svelte](#svelte)
|
- [Svelte](#svelte)
|
||||||
- [Solid](#solid)
|
- [Solid](#solid)
|
||||||
- [Hyva](#hyva)
|
- [Hyva](#hyva)
|
||||||
@@ -200,16 +199,6 @@ composer require mallardduck/blade-lucide-icons
|
|||||||
|
|
||||||
For more details, see the [documentation](https://github.com/mallardduck/blade-lucide-icons/blob/main/README.md).
|
For more details, see the [documentation](https://github.com/mallardduck/blade-lucide-icons/blob/main/README.md).
|
||||||
|
|
||||||
### Flutter
|
|
||||||
|
|
||||||
Implementation of Lucide icon library for Flutter applications.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
flutter pub add lucide_icons
|
|
||||||
```
|
|
||||||
|
|
||||||
For more details, see the [pub.dev](https://pub.dev/packages/lucide_icons).
|
|
||||||
|
|
||||||
### Svelte
|
### Svelte
|
||||||
|
|
||||||
Implementation of the lucide icon library for Svelte applications.
|
Implementation of the lucide icon library for Svelte applications.
|
||||||
@@ -285,6 +274,9 @@ Thank you to all the people who contributed to Lucide!
|
|||||||
|
|
||||||
## Sponsors
|
## Sponsors
|
||||||
|
|
||||||
|
|
||||||
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
|
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
|
||||||
<img src="/docs/public/vercel.svg" alt="Powered by Vercel" width="200" />
|
<img src="docs/public/vercel.svg" alt="Powered by Vercel" width="200" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="docs/public/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../category.schema.json",
|
"$schema": "../category.schema.json",
|
||||||
"title": "File icons",
|
"title": "File icons",
|
||||||
"icon": "layout"
|
"icon": "panels-top-left"
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../category.schema.json",
|
"$schema": "../category.schema.json",
|
||||||
"title": "Layout",
|
"title": "Layout",
|
||||||
"icon": "layout"
|
"icon": "panels-top-left"
|
||||||
}
|
}
|
||||||
@@ -12,8 +12,10 @@
|
|||||||
},
|
},
|
||||||
"title": "Lucide Icons category schema",
|
"title": "Lucide Icons category schema",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["$schema", "icon", "title"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"title": {
|
"$schema": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
@@ -22,13 +24,12 @@
|
|||||||
"icon": {
|
"icon": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"weight": {
|
"weight": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
|
||||||
"title",
|
|
||||||
"icon"
|
|
||||||
],
|
|
||||||
"description": "A JSON Schema for categories defined by Lucide Icons."
|
"description": "A JSON Schema for categories defined by Lucide Icons."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { eventHandler, setResponseHeader } from 'h3'
|
import { eventHandler, setResponseHeader } from 'h3';
|
||||||
import iconMetaData from '../../data/iconMetaData'
|
import iconMetaData from '../../data/iconMetaData';
|
||||||
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400')
|
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400');
|
||||||
setResponseHeader(event, 'Access-Control-Allow-Origin', '*')
|
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
|
||||||
|
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(iconMetaData).map(([name, { categories }]) => [ name, categories ])
|
Object.entries(iconMetaData).map(([name, { categories }]) => [name, categories]),
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,41 +1,53 @@
|
|||||||
import { eventHandler, setResponseHeader, defaultContentType } from 'h3'
|
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
|
||||||
import { renderToString, renderToStaticMarkup } from 'react-dom/server'
|
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
|
||||||
import { createElement } from 'react'
|
import { createElement } from 'react';
|
||||||
import SvgPreview from '../../lib/SvgPreview/index.tsx';
|
import SvgPreview from '../../lib/SvgPreview/index.tsx';
|
||||||
import iconNodes from '../../data/iconNodes'
|
import iconNodes from '../../data/iconNodes';
|
||||||
import createLucideIcon from 'lucide-react/src/createLucideIcon'
|
import createLucideIcon from 'lucide-react/src/createLucideIcon';
|
||||||
import Backdrop from '../../lib/SvgPreview/Backdrop.tsx';
|
import Backdrop from '../../lib/SvgPreview/Backdrop.tsx';
|
||||||
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
const { params } = event.context
|
const { params } = event.context;
|
||||||
|
|
||||||
const [name, svgData] = params.data.split('/');
|
const pathData = params.data.split('/');
|
||||||
const data = svgData.slice(0, -4);
|
const data = pathData.at(-1).slice(0, -4);
|
||||||
|
const [name] = pathData;
|
||||||
|
|
||||||
const src = Buffer.from(data, 'base64').toString('utf8');
|
const src = Buffer.from(data, 'base64').toString('utf8');
|
||||||
|
|
||||||
const children = []
|
const children = [];
|
||||||
|
|
||||||
if (name in iconNodes) {
|
// Finds the longest matching icon to be use as the backdrop.
|
||||||
const iconNode = iconNodes[name]
|
// For `square-dashed-bottom-code` it suggests `square-dashed-bottom-code`.
|
||||||
|
// For `square-dashed-bottom-i-dont-exist` it suggests `square-dashed-bottom`.
|
||||||
|
const backdropName = name
|
||||||
|
.split('-')
|
||||||
|
.map((_, idx, arr) => arr.slice(0, idx + 1).join('-'))
|
||||||
|
.reverse()
|
||||||
|
.find((groupName) => groupName in iconNodes);
|
||||||
|
if (backdropName) {
|
||||||
|
const iconNode = iconNodes[backdropName];
|
||||||
|
|
||||||
const LucideIcon = createLucideIcon(name, iconNode)
|
const LucideIcon = createLucideIcon(backdropName, iconNode);
|
||||||
const svg = renderToStaticMarkup(createElement(LucideIcon))
|
const svg = renderToStaticMarkup(createElement(LucideIcon));
|
||||||
const backdropString = svg.replace(/<svg[^>]*>|<\/svg>/g, '');
|
const backdropString = svg.replace(/<svg[^>]*>|<\/svg>/g, '');
|
||||||
|
|
||||||
children.push(createElement(Backdrop, { backdropString, src }))
|
children.push(
|
||||||
|
createElement(Backdrop, {
|
||||||
|
backdropString,
|
||||||
|
src,
|
||||||
|
color: name in iconNodes ? 'red' : '#777',
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const svg = Buffer.from(
|
const svg = Buffer.from(
|
||||||
// We can't use jsx here, is not supported here by nitro.
|
// We can't use jsx here, is not supported here by nitro.
|
||||||
renderToString(createElement(SvgPreview, {src, showGrid: true}, children)).replace(
|
renderToString(createElement(SvgPreview, { src, showGrid: true }, children)),
|
||||||
/>/,
|
|
||||||
'><style>@media screen and (prefers-color-scheme: dark) { svg { stroke: #fff } }</style>'
|
|
||||||
)
|
|
||||||
).toString('utf8');
|
).toString('utf8');
|
||||||
|
|
||||||
defaultContentType(event, 'image/svg+xml')
|
defaultContentType(event, 'image/svg+xml');
|
||||||
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000')
|
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
|
||||||
|
|
||||||
return svg
|
return svg;
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default eventHandler(async (event) => {
|
|||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
`
|
`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const resvg = new Resvg(svg, { background: '#000' });
|
const resvg = new Resvg(svg, { background: '#000' });
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { eventHandler, setResponseHeader, defaultContentType } from 'h3'
|
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
|
||||||
import { renderToString } from 'react-dom/server'
|
import { renderToString } from 'react-dom/server';
|
||||||
import { createElement } from 'react'
|
import { createElement } from 'react';
|
||||||
import SvgPreview from '../../../lib/SvgPreview/index.tsx';
|
import SvgPreview from '../../../lib/SvgPreview/index.tsx';
|
||||||
import createLucideIcon, { IconNode } from 'lucide-react/src/createLucideIcon'
|
import createLucideIcon, { IconNode } from 'lucide-react/src/createLucideIcon';
|
||||||
import { parseSync } from 'svgson';
|
import { parseSync } from 'svgson';
|
||||||
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
const { params } = event.context
|
const { params } = event.context;
|
||||||
|
|
||||||
const [strokeWidth, svgData] = params.data.split('/');
|
const [strokeWidth, svgData] = params.data.split('/');
|
||||||
const data = svgData.slice(0, -4);
|
const data = svgData.slice(0, -4);
|
||||||
@@ -16,8 +16,8 @@ export default eventHandler((event) => {
|
|||||||
const Icon = createLucideIcon(
|
const Icon = createLucideIcon(
|
||||||
'icon',
|
'icon',
|
||||||
parseSync(src.includes('<svg') ? src : `<svg>${src}</svg>`).children.map(
|
parseSync(src.includes('<svg') ? src : `<svg>${src}</svg>`).children.map(
|
||||||
({ name, attributes }) => [name, attributes]
|
({ name, attributes }) => [name, attributes],
|
||||||
) as IconNode
|
) as IconNode,
|
||||||
);
|
);
|
||||||
|
|
||||||
const svg = Buffer.from(
|
const svg = Buffer.from(
|
||||||
@@ -33,12 +33,12 @@ export default eventHandler((event) => {
|
|||||||
@media screen and (prefers-color-scheme: dark) {
|
@media screen and (prefers-color-scheme: dark) {
|
||||||
svg { stroke: #fff; fill: transparent !important; }
|
svg { stroke: #fff; fill: transparent !important; }
|
||||||
}
|
}
|
||||||
</style>`
|
</style>`,
|
||||||
)
|
),
|
||||||
).toString('utf8');
|
).toString('utf8');
|
||||||
|
|
||||||
defaultContentType(event, 'image/svg+xml')
|
defaultContentType(event, 'image/svg+xml');
|
||||||
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000')
|
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
|
||||||
|
|
||||||
return svg
|
return svg;
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
import { eventHandler, getQuery, setResponseHeader } from 'h3'
|
import { eventHandler, getQuery, setResponseHeader } from 'h3';
|
||||||
import iconNodes from '../../data/iconNodes'
|
import iconNodes from '../../data/iconNodes';
|
||||||
import { IconNodeWithKeys } from '../../theme/types'
|
import { IconNodeWithKeys } from '../../theme/types';
|
||||||
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
const query = getQuery(event)
|
const query = getQuery(event);
|
||||||
|
|
||||||
const withUniqueKeys = query.withUniqueKeys === 'true'
|
const withUniqueKeys = query.withUniqueKeys === 'true';
|
||||||
|
|
||||||
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400')
|
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400');
|
||||||
setResponseHeader(event, 'Access-Control-Allow-Origin', '*')
|
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
|
||||||
|
|
||||||
if (withUniqueKeys) {
|
if (withUniqueKeys) {
|
||||||
return iconNodes
|
return iconNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.entries(iconNodes).reduce((acc, [name, iconNode]) => {
|
return Object.entries(iconNodes).reduce((acc, [name, iconNode]) => {
|
||||||
if (withUniqueKeys) {
|
if (withUniqueKeys) {
|
||||||
return [name, iconNode]
|
return [name, iconNode];
|
||||||
}
|
}
|
||||||
|
|
||||||
const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs}]) => {
|
const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => {
|
||||||
return [name, attrs]
|
return [name, attrs];
|
||||||
})
|
});
|
||||||
|
|
||||||
acc[name] = newIconNode
|
acc[name] = newIconNode;
|
||||||
|
|
||||||
return acc
|
return acc;
|
||||||
}, {})
|
}, {});
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import { eventHandler, getQuery, setResponseHeader, createError } from 'h3'
|
import { eventHandler, getQuery, setResponseHeader, createError } from 'h3';
|
||||||
import iconNodes from '../../data/iconNodes'
|
import iconNodes from '../../data/iconNodes';
|
||||||
import createLucideIcon from 'lucide-react/src/createLucideIcon'
|
import createLucideIcon from 'lucide-react/src/createLucideIcon';
|
||||||
import { renderToString } from 'react-dom/server'
|
import { renderToString } from 'react-dom/server';
|
||||||
import { createElement } from 'react'
|
import { createElement } from 'react';
|
||||||
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
const { params } = event.context
|
const { params } = event.context;
|
||||||
|
|
||||||
const iconNode = iconNodes[params.iconName]
|
const iconNode = iconNodes[params.iconName];
|
||||||
|
|
||||||
if (iconNode == null) {
|
if (iconNode == null) {
|
||||||
const error = createError({
|
const error = createError({
|
||||||
statusCode: 404,
|
statusCode: 404,
|
||||||
message: `Icon "${params.iconName}" not found`,
|
message: `Icon "${params.iconName}" not found`,
|
||||||
})
|
});
|
||||||
|
|
||||||
return sendError(event, error)
|
return sendError(event, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const width = getQuery(event).width || undefined
|
const width = getQuery(event).width || undefined;
|
||||||
const height = getQuery(event).height || undefined
|
const height = getQuery(event).height || undefined;
|
||||||
const color = getQuery(event).color || undefined
|
const color = getQuery(event).color || undefined;
|
||||||
const strokeWidth = getQuery(event).strokeWidth || undefined
|
const strokeWidth = getQuery(event).strokeWidth || undefined;
|
||||||
|
|
||||||
const LucideIcon = createLucideIcon(params.iconName, iconNode)
|
const LucideIcon = createLucideIcon(params.iconName, iconNode);
|
||||||
|
|
||||||
const svg = Buffer.from(
|
const svg = Buffer.from(
|
||||||
renderToString(
|
renderToString(
|
||||||
@@ -32,14 +32,13 @@ export default eventHandler((event) => {
|
|||||||
height,
|
height,
|
||||||
color: color ? `#${color}` : undefined,
|
color: color ? `#${color}` : undefined,
|
||||||
strokeWidth,
|
strokeWidth,
|
||||||
}
|
}),
|
||||||
))
|
),
|
||||||
).toString('utf8');
|
).toString('utf8');
|
||||||
|
|
||||||
defaultContentType(event, 'image/svg+xml')
|
defaultContentType(event, 'image/svg+xml');
|
||||||
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000')
|
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
|
||||||
setResponseHeader(event, 'Access-Control-Allow-Origin', '*')
|
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
|
||||||
|
|
||||||
return svg
|
return svg;
|
||||||
|
});
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { eventHandler, setResponseHeader } from 'h3'
|
import { eventHandler, setResponseHeader } from 'h3';
|
||||||
import iconMetaData from '../../data/iconMetaData'
|
import iconMetaData from '../../data/iconMetaData';
|
||||||
|
|
||||||
export default eventHandler((event) => {
|
export default eventHandler((event) => {
|
||||||
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400')
|
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400');
|
||||||
setResponseHeader(event, 'Access-Control-Allow-Origin', '*')
|
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
|
||||||
|
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(Object.entries(iconMetaData).map(([name, { tags }]) => [name, tags]));
|
||||||
Object.entries(iconMetaData).map(([name, { tags }]) => [ name, tags ])
|
});
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export default eventHandler(() => {
|
export default eventHandler(() => {
|
||||||
return { nitro: 'Is Awesome! asda' }
|
return { nitro: 'Is Awesome! asda' };
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { fileURLToPath, URL } from 'node:url'
|
import { fileURLToPath, URL } from 'node:url';
|
||||||
import { defineConfig } from 'vitepress'
|
import { defineConfig } from 'vitepress';
|
||||||
import sidebar from './sidebar';
|
import sidebar from './sidebar';
|
||||||
|
|
||||||
const title = "Lucide";
|
const title = 'Lucide';
|
||||||
const socialTitle = "Lucide Icons";
|
const socialTitle = 'Lucide Icons';
|
||||||
const description = "Beautiful & consistent icon toolkit made by the community."
|
const description = 'Beautiful & consistent icon toolkit made by the community.';
|
||||||
|
|
||||||
// https://vitepress.dev/reference/site-config
|
// https://vitepress.dev/reference/site-config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -12,113 +12,164 @@ export default defineConfig({
|
|||||||
description,
|
description,
|
||||||
cleanUrls: true,
|
cleanUrls: true,
|
||||||
outDir: '.vercel/output/static',
|
outDir: '.vercel/output/static',
|
||||||
|
srcExclude: ['**/README.md'],
|
||||||
vite: {
|
vite: {
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
find: /^.*\/VPIconAlignLeft\.vue$/,
|
find: /^.*\/VPIconAlignLeft\.vue$/,
|
||||||
replacement: fileURLToPath(
|
replacement: fileURLToPath(
|
||||||
new URL('./theme/components/overrides/VPIconAlignLeft.vue', import.meta.url)
|
new URL('./theme/components/overrides/VPIconAlignLeft.vue', import.meta.url),
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: /^.*\/VPFooter\.vue$/,
|
find: /^.*\/VPFooter\.vue$/,
|
||||||
replacement: fileURLToPath(
|
replacement: fileURLToPath(
|
||||||
new URL('./theme/components/overrides/VPFooter.vue', import.meta.url)
|
new URL('./theme/components/overrides/VPFooter.vue', import.meta.url),
|
||||||
)
|
),
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
head: [
|
head: [
|
||||||
[ 'script', {
|
[
|
||||||
|
'script',
|
||||||
|
{
|
||||||
src: 'https://analytics.lucide.dev/js/script.js',
|
src: 'https://analytics.lucide.dev/js/script.js',
|
||||||
'data-domain': 'lucide.dev',
|
'data-domain': 'lucide.dev',
|
||||||
defer: ''
|
defer: '',
|
||||||
}],
|
},
|
||||||
[ 'meta', {
|
],
|
||||||
property:"og:locale",
|
[
|
||||||
content:"en_US"
|
'meta',
|
||||||
}],
|
{
|
||||||
[ 'meta', {
|
property: 'og:locale',
|
||||||
property:"og:type",
|
content: 'en_US',
|
||||||
content:"website"
|
},
|
||||||
}],
|
],
|
||||||
[ 'meta', {
|
[
|
||||||
property:"og:site_name",
|
'meta',
|
||||||
|
{
|
||||||
|
property: 'og:type',
|
||||||
|
content: 'website',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta',
|
||||||
|
{
|
||||||
|
property: 'og:site_name',
|
||||||
content: title,
|
content: title,
|
||||||
}],
|
},
|
||||||
[ 'meta', {
|
],
|
||||||
property:"og:title",
|
[
|
||||||
|
'meta',
|
||||||
|
{
|
||||||
|
property: 'og:title',
|
||||||
content: socialTitle,
|
content: socialTitle,
|
||||||
}],
|
},
|
||||||
[ 'meta', {
|
],
|
||||||
property:"og:description",
|
[
|
||||||
content: description
|
'meta',
|
||||||
}],
|
{
|
||||||
[ 'meta', {
|
property: 'og:description',
|
||||||
property:"og:url",
|
content: description,
|
||||||
content:"https://lucide.dev"
|
},
|
||||||
}],
|
],
|
||||||
[ 'meta', {
|
[
|
||||||
property:"og:image",
|
'meta',
|
||||||
content: "https://lucide.dev/og.png"
|
{
|
||||||
}],
|
property: 'og:url',
|
||||||
[ 'meta', {
|
content: 'https://lucide.dev',
|
||||||
property:"og:image:width",
|
},
|
||||||
content:"1200"
|
],
|
||||||
}],
|
[
|
||||||
[ 'meta', {
|
'meta',
|
||||||
property:"og:image:height",
|
{
|
||||||
content:"630"
|
property: 'og:image',
|
||||||
}],
|
content: 'https://lucide.dev/og.png',
|
||||||
[ 'meta', {
|
},
|
||||||
property:"og:image:type",
|
],
|
||||||
content:"image/png"
|
[
|
||||||
}],
|
'meta',
|
||||||
[ 'meta', {
|
{
|
||||||
property:"twitter:card",
|
property: 'og:image:width',
|
||||||
content:"summary_large_image"
|
content: '1200',
|
||||||
}],
|
},
|
||||||
[ 'meta', {
|
],
|
||||||
property:"twitter:title",
|
[
|
||||||
|
'meta',
|
||||||
|
{
|
||||||
|
property: 'og:image:height',
|
||||||
|
content: '630',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta',
|
||||||
|
{
|
||||||
|
property: 'og:image:type',
|
||||||
|
content: 'image/png',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta',
|
||||||
|
{
|
||||||
|
property: 'twitter:card',
|
||||||
|
content: 'summary_large_image',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'meta',
|
||||||
|
{
|
||||||
|
property: 'twitter:title',
|
||||||
content: socialTitle,
|
content: socialTitle,
|
||||||
}],
|
},
|
||||||
[ 'meta', {
|
],
|
||||||
property:"twitter:description",
|
[
|
||||||
content: description
|
'meta',
|
||||||
}],
|
{
|
||||||
[ 'meta', {
|
property: 'twitter:description',
|
||||||
property:"twitter:image",
|
content: description,
|
||||||
content:"https://lucide.dev/og.png"
|
},
|
||||||
}],
|
],
|
||||||
|
[
|
||||||
|
'meta',
|
||||||
|
{
|
||||||
|
property: 'twitter:image',
|
||||||
|
content: 'https://lucide.dev/og.png',
|
||||||
|
},
|
||||||
|
],
|
||||||
],
|
],
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
// https://vitepress.dev/reference/default-theme-config
|
// https://vitepress.dev/reference/default-theme-config
|
||||||
logo: {
|
logo: {
|
||||||
light: '/logo.light.svg',
|
light: '/logo.light.svg',
|
||||||
dark: '/logo.dark.svg'
|
dark: '/logo.dark.svg',
|
||||||
},
|
},
|
||||||
nav: [
|
nav: [
|
||||||
{ text: 'Icons', link: '/icons/' },
|
{ text: 'Icons', link: '/icons/' },
|
||||||
{ text: 'Guide', link: '/guide/' },
|
{ text: 'Guide', link: '/guide/' },
|
||||||
{ text: 'Packages', link: '/packages' },
|
{ text: 'Packages', link: '/packages' },
|
||||||
|
{ text: 'Showcase', link: '/showcase' },
|
||||||
{ text: 'License', link: '/license' },
|
{ text: 'License', link: '/license' },
|
||||||
],
|
],
|
||||||
sidebar,
|
sidebar,
|
||||||
socialLinks: [
|
socialLinks: [
|
||||||
{ icon: 'github', link: 'https://github.com/lucide-icons/lucide' },
|
{ icon: 'github', link: 'https://github.com/lucide-icons/lucide' },
|
||||||
{ icon: 'discord', link: 'https://discord.gg/EH6nSts' }
|
{ icon: 'discord', link: 'https://discord.gg/EH6nSts' },
|
||||||
],
|
],
|
||||||
footer: {
|
footer: {
|
||||||
message: 'Released under the ISC License.',
|
message: 'Released under the ISC License.',
|
||||||
copyright: `Copyright © ${new Date().getFullYear()} Lucide Contributors`
|
copyright: `Copyright © ${new Date().getFullYear()} Lucide Contributors`,
|
||||||
},
|
},
|
||||||
editLink: {
|
editLink: {
|
||||||
pattern: 'https://github.com/lucide-icons/lucide/edit/main/docs/:path'
|
pattern: 'https://github.com/lucide-icons/lucide/edit/main/docs/:path',
|
||||||
|
},
|
||||||
|
carbonAds: {
|
||||||
|
code: 'CWYIC53U',
|
||||||
|
placement: 'lucidedev',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sitemap: {
|
sitemap: {
|
||||||
hostname: 'https://lucide.dev/'
|
hostname: 'https://lucide.dev/',
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|||||||
50
docs/.vitepress/data/companiesData.json
Normal file
50
docs/.vitepress/data/companiesData.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Vercel",
|
||||||
|
"url": "https://vercel.com",
|
||||||
|
"image": {
|
||||||
|
"light": "/company-logos/vercel-light.svg",
|
||||||
|
"dark": "/company-logos/vercel-dark.svg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Supabase",
|
||||||
|
"url": "https://supabase.com",
|
||||||
|
"image": {
|
||||||
|
"light": "/company-logos/supabase-light.svg",
|
||||||
|
"dark": "/company-logos/supabase-dark.svg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Obsidian",
|
||||||
|
"url": "https://obsidian.md",
|
||||||
|
"image": {
|
||||||
|
"light": "/company-logos/obsidian-light.svg",
|
||||||
|
"dark": "/company-logos/obsidian-dark.svg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Open Collective",
|
||||||
|
"url": "https://opencollective.com",
|
||||||
|
"image": {
|
||||||
|
"light": "/company-logos/open-collective-light.svg",
|
||||||
|
"dark": "/company-logos/open-collective-dark.svg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Super",
|
||||||
|
"url": "https://super.so",
|
||||||
|
"image": {
|
||||||
|
"light": "/company-logos/super-light.svg",
|
||||||
|
"dark": "/company-logos/super-dark.svg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Noodle",
|
||||||
|
"url": "https://noodle.run/",
|
||||||
|
"image": {
|
||||||
|
"light": "/company-logos/noodle-light.svg",
|
||||||
|
"dark": "/company-logos/noodle-dark.svg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
18
docs/.vitepress/data/componentLibrariesData.json
Normal file
18
docs/.vitepress/data/componentLibrariesData.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Shadcn/ui",
|
||||||
|
"url": "https://ui.shadcn.com/",
|
||||||
|
"image": {
|
||||||
|
"light": "/library-logos/shadcn-ui-light.svg",
|
||||||
|
"dark": "/library-logos/shadcn-ui-dark.svg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tamagui",
|
||||||
|
"url": "https://tamagui.dev/",
|
||||||
|
"image": {
|
||||||
|
"light": "/library-logos/tamagui.svg",
|
||||||
|
"dark": "/library-logos/tamagui.svg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -3,87 +3,171 @@
|
|||||||
"order": 0,
|
"order": 0,
|
||||||
"icon": "js",
|
"icon": "js",
|
||||||
"shields": [
|
"shields": [
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/v/lucide", "href": "https://www.npmjs.com/package/lucide" },
|
{
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide", "href": "https://www.npmjs.com/package/lucide" }
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/v/lucide",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/dw/lucide",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-react": {
|
"lucide-react": {
|
||||||
"order": 1,
|
"order": 1,
|
||||||
"icon": "react",
|
"icon": "react",
|
||||||
"shields": [
|
"shields": [
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-react", "href": "https://www.npmjs.com/package/lucide-react" },
|
{
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-react", "href": "https://www.npmjs.com/package/lucide-react" }
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/v/lucide-react",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-react"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/dw/lucide-react",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-react"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-vue": {
|
"lucide-vue": {
|
||||||
"order": 2,
|
"order": 2,
|
||||||
"icon": "vue",
|
"icon": "vue",
|
||||||
"shields": [
|
"shields": [
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-vue", "href": "https://www.npmjs.com/package/lucide-vue" },
|
{
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-vue", "href": "https://www.npmjs.com/package/lucide-vue" }
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/v/lucide-vue",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-vue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/dw/lucide-vue",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-vue"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-vue-next": {
|
"lucide-vue-next": {
|
||||||
"order": 3,
|
"order": 3,
|
||||||
"icon": "vue-next",
|
"icon": "vue-next",
|
||||||
"shields": [
|
"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" }
|
"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": {
|
"lucide-svelte": {
|
||||||
"order": 4,
|
"order": 4,
|
||||||
"icon": "svelte",
|
"icon": "svelte",
|
||||||
"shields": [
|
"shields": [
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-svelte", "href": "https://www.npmjs.com/package/lucide-svelte" },
|
{
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-svelte", "href": "https://www.npmjs.com/package/lucide-svelte" }
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/v/lucide-svelte",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-svelte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/dw/lucide-svelte",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-svelte"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-solid": {
|
"lucide-solid": {
|
||||||
"order": 4,
|
"order": 4,
|
||||||
"icon": "solid",
|
"icon": "solid",
|
||||||
"shields": [
|
"shields": [
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-solid", "href": "https://www.npmjs.com/package/lucide-solid" },
|
{
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-solid", "href": "https://www.npmjs.com/package/lucide-solid" }
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/v/lucide-solid",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-solid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/dw/lucide-solid",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-solid"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-preact": {
|
"lucide-preact": {
|
||||||
"order": 5,
|
"order": 5,
|
||||||
"icon": "preact",
|
"icon": "preact",
|
||||||
"shields": [
|
"shields": [
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-preact", "href": "https://www.npmjs.com/package/lucide-preact" },
|
{
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-preact", "href": "https://www.npmjs.com/package/lucide-preact" }
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/v/lucide-preact",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-preact"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/dw/lucide-preact",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-preact"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-react-native": {
|
"lucide-react-native": {
|
||||||
"order": 6,
|
"order": 6,
|
||||||
"icon": "react-native",
|
"icon": "react-native",
|
||||||
"shields": [
|
"shields": [
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-react-native", "href": "https://www.npmjs.com/package/lucide-react-native" },
|
{
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-react-native", "href": "https://www.npmjs.com/package/lucide-react-native" }
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/v/lucide-react-native",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-react-native"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/dw/lucide-react-native",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-react-native"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-angular": {
|
"lucide-angular": {
|
||||||
"order": 7,
|
"order": 7,
|
||||||
"icon": "angular",
|
"icon": "angular",
|
||||||
"shields": [
|
"shields": [
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-angular", "href": "https://www.npmjs.com/package/lucide-angular" },
|
{
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-angular", "href": "https://www.npmjs.com/package/lucide-angular" }
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/v/lucide-angular",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-angular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/dw/lucide-angular",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-angular"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-static": {
|
"lucide-static": {
|
||||||
"order": 8,
|
"order": 8,
|
||||||
"icon": "svg",
|
"icon": "svg",
|
||||||
"shields": [
|
"shields": [
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-static", "href": "https://www.npmjs.com/package/lucide-static" },
|
{
|
||||||
{ "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-static", "href": "https://www.npmjs.com/package/lucide-static" }
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/v/lucide-static",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-static"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/dw/lucide-static",
|
||||||
|
"href": "https://www.npmjs.com/package/lucide-static"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-flutter": {
|
"lucide-flutter": {
|
||||||
"order": 9,
|
"order": 9,
|
||||||
"icon": "flutter",
|
"icon": "flutter",
|
||||||
"shields": [
|
"shields": [
|
||||||
{ "alt": "flutter", "src": "https://img.shields.io/pub/v/lucide_icons", "href": "https://img.shields.io/pub/v/lucide_icons" }
|
{
|
||||||
|
"alt": "flutter",
|
||||||
|
"src": "https://img.shields.io/pub/v/lucide_icons",
|
||||||
|
"href": "https://img.shields.io/pub/v/lucide_icons"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
interface BackdropProps {
|
interface BackdropProps {
|
||||||
src: string
|
src: string;
|
||||||
backdropString: string
|
color?: string;
|
||||||
|
backdropString: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Backdrop = ({ src, backdropString }: BackdropProps): JSX.Element => {
|
const Backdrop = ({ src, color = 'red', backdropString }: BackdropProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<defs xmlns="http://www.w3.org/2000/svg">
|
<defs xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -16,18 +17,47 @@ const Backdrop = ({ src, backdropString }: BackdropProps): JSX.Element => {
|
|||||||
patternUnits="userSpaceOnUse"
|
patternUnits="userSpaceOnUse"
|
||||||
patternTransform="rotate(45 50 50)"
|
patternTransform="rotate(45 50 50)"
|
||||||
>
|
>
|
||||||
<line stroke="red" strokeWidth={0.1} y2={1} />
|
<line
|
||||||
<line stroke="red" strokeWidth={0.1} y2={1} />
|
stroke={color}
|
||||||
|
strokeWidth={0.1}
|
||||||
|
y2={1}
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth={0.1}
|
||||||
|
y2={1}
|
||||||
|
/>
|
||||||
</pattern>
|
</pattern>
|
||||||
</defs>
|
</defs>
|
||||||
<mask id="svg-preview-backdrop-mask-outline" maskUnits="userSpaceOnUse">
|
<mask
|
||||||
<g stroke="#fff" dangerouslySetInnerHTML={{ __html: backdropString }} />
|
id="svg-preview-backdrop-mask-outline"
|
||||||
<g dangerouslySetInnerHTML={{ __html: src }} strokeWidth={2.05} />
|
maskUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
stroke="#fff"
|
||||||
|
dangerouslySetInnerHTML={{ __html: backdropString }}
|
||||||
|
/>
|
||||||
|
<g
|
||||||
|
dangerouslySetInnerHTML={{ __html: src }}
|
||||||
|
strokeWidth={2.05}
|
||||||
|
/>
|
||||||
</mask>
|
</mask>
|
||||||
<mask id="svg-preview-backdrop-mask-fill" maskUnits="userSpaceOnUse">
|
<mask
|
||||||
<g stroke="#fff" dangerouslySetInnerHTML={{ __html: backdropString }} />
|
id="svg-preview-backdrop-mask-fill"
|
||||||
<g dangerouslySetInnerHTML={{ __html: src }} strokeWidth={2.05} />
|
maskUnits="userSpaceOnUse"
|
||||||
<g strokeWidth={1.75} dangerouslySetInnerHTML={{ __html: backdropString }} />
|
>
|
||||||
|
<g
|
||||||
|
stroke="#fff"
|
||||||
|
dangerouslySetInnerHTML={{ __html: backdropString }}
|
||||||
|
/>
|
||||||
|
<g
|
||||||
|
dangerouslySetInnerHTML={{ __html: src }}
|
||||||
|
strokeWidth={2.05}
|
||||||
|
/>
|
||||||
|
<g
|
||||||
|
strokeWidth={1.75}
|
||||||
|
dangerouslySetInnerHTML={{ __html: backdropString }}
|
||||||
|
/>
|
||||||
</mask>
|
</mask>
|
||||||
<g
|
<g
|
||||||
strokeWidth={2.25}
|
strokeWidth={2.25}
|
||||||
@@ -58,14 +88,13 @@ const Backdrop = ({ src, backdropString }: BackdropProps): JSX.Element => {
|
|||||||
y="0"
|
y="0"
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
fill="red"
|
fill={color}
|
||||||
opacity={0.5}
|
opacity={0.5}
|
||||||
stroke="none"
|
stroke="none"
|
||||||
mask={'url(#svg-preview-backdrop-mask-fill)'}
|
mask={'url(#svg-preview-backdrop-mask-fill)'}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
export default Backdrop;
|
export default Backdrop;
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ const Grid = ({
|
|||||||
strokeWidth: number;
|
strokeWidth: number;
|
||||||
radius: number;
|
radius: number;
|
||||||
} & PathProps<'stroke', 'strokeWidth'>) => (
|
} & PathProps<'stroke', 'strokeWidth'>) => (
|
||||||
<g className="svg-preview-grid-group" strokeLinecap="butt" {...props}>
|
<g
|
||||||
|
className="svg-preview-grid-group"
|
||||||
|
strokeLinecap="butt"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
<rect
|
<rect
|
||||||
className="svg-preview-grid-rect"
|
className="svg-preview-grid-rect"
|
||||||
width={24 - props.strokeWidth}
|
width={24 - props.strokeWidth}
|
||||||
@@ -21,11 +25,29 @@ const Grid = ({
|
|||||||
fill={fill}
|
fill={fill}
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
|
strokeDasharray={'0 0.1 ' + '0.1 0.15 '.repeat(11) + '0 0.15'}
|
||||||
|
strokeWidth={0.1}
|
||||||
d={
|
d={
|
||||||
props.d ||
|
props.d ||
|
||||||
new Array(Math.floor(24 - 1))
|
new Array(Math.floor(24 - 1))
|
||||||
.fill(null)
|
.fill(null)
|
||||||
.flatMap((_, i) => [
|
.map((_, i) => i)
|
||||||
|
.filter((i) => i % 3 !== 2)
|
||||||
|
.flatMap((i) => [
|
||||||
|
`M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`,
|
||||||
|
`M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`,
|
||||||
|
])
|
||||||
|
.join('')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d={
|
||||||
|
props.d ||
|
||||||
|
new Array(Math.floor(24 - 1))
|
||||||
|
.fill(null)
|
||||||
|
.map((_, i) => i)
|
||||||
|
.filter((i) => i % 3 === 2)
|
||||||
|
.flatMap((i) => [
|
||||||
`M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`,
|
`M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`,
|
||||||
`M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`,
|
`M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`,
|
||||||
])
|
])
|
||||||
@@ -44,15 +66,21 @@ const Shadow = ({
|
|||||||
paths: Path[];
|
paths: Path[];
|
||||||
} & PathProps<'stroke' | 'strokeWidth' | 'strokeOpacity', 'd'>) => {
|
} & PathProps<'stroke' | 'strokeWidth' | 'strokeOpacity', 'd'>) => {
|
||||||
const groupedPaths = Object.entries(
|
const groupedPaths = Object.entries(
|
||||||
paths.reduce((groups, val) => {
|
paths.reduce(
|
||||||
|
(groups, val) => {
|
||||||
const key = val.c.id;
|
const key = val.c.id;
|
||||||
groups[key] = [...(groups[key] || []), val];
|
groups[key] = [...(groups[key] || []), val];
|
||||||
return groups;
|
return groups;
|
||||||
}, {} as Record<number, Path[]>)
|
},
|
||||||
|
{} as Record<number, Path[]>,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<g className="svg-preview-shadow-mask-group" {...props}>
|
<g
|
||||||
|
className="svg-preview-shadow-mask-group"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
{groupedPaths.map(([id, paths]) => (
|
{groupedPaths.map(([id, paths]) => (
|
||||||
<mask
|
<mask
|
||||||
id={`svg-preview-shadow-mask-${id}`}
|
id={`svg-preview-shadow-mask-${id}`}
|
||||||
@@ -61,7 +89,15 @@ const Shadow = ({
|
|||||||
strokeWidth={props.strokeWidth}
|
strokeWidth={props.strokeWidth}
|
||||||
stroke="#000"
|
stroke="#000"
|
||||||
>
|
>
|
||||||
<rect x={0} y={0} width={24} height={24} fill="#fff" stroke="none" rx={radius} />
|
<rect
|
||||||
|
x={0}
|
||||||
|
y={0}
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
fill="#fff"
|
||||||
|
stroke="none"
|
||||||
|
rx={radius}
|
||||||
|
/>
|
||||||
<path
|
<path
|
||||||
d={paths
|
d={paths
|
||||||
.flatMap(({ prev, next }) => [
|
.flatMap(({ prev, next }) => [
|
||||||
@@ -74,9 +110,16 @@ const Shadow = ({
|
|||||||
</mask>
|
</mask>
|
||||||
))}
|
))}
|
||||||
</g>
|
</g>
|
||||||
<g className="svg-preview-shadow-group" {...props}>
|
<g
|
||||||
|
className="svg-preview-shadow-group"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
{paths.map(({ d, c: { id } }, i) => (
|
{paths.map(({ d, c: { id } }, i) => (
|
||||||
<path key={i} mask={`url(#svg-preview-shadow-mask-${id})`} d={d} />
|
<path
|
||||||
|
key={i}
|
||||||
|
mask={`url(#svg-preview-shadow-mask-${id})`}
|
||||||
|
d={d}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
<path
|
<path
|
||||||
d={paths
|
d={paths
|
||||||
@@ -94,9 +137,16 @@ const ColoredPath = ({
|
|||||||
paths,
|
paths,
|
||||||
...props
|
...props
|
||||||
}: { paths: Path[]; colors: string[] } & PathProps<never, 'd' | 'stroke'>) => (
|
}: { paths: Path[]; colors: string[] } & PathProps<never, 'd' | 'stroke'>) => (
|
||||||
<g className="svg-preview-colored-path-group" {...props}>
|
<g
|
||||||
|
className="svg-preview-colored-path-group"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
{paths.map(({ d, c }, i) => (
|
{paths.map(({ d, c }, i) => (
|
||||||
<path key={i} d={d} stroke={colors[(c.name === 'path' ? i : c.id) % colors.length]} />
|
<path
|
||||||
|
key={i}
|
||||||
|
d={d}
|
||||||
|
stroke={colors[(c.name === 'path' ? i : c.id) % colors.length]}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
@@ -138,7 +188,15 @@ const ControlPath = ({
|
|||||||
key={i}
|
key={i}
|
||||||
maskUnits="userSpaceOnUse"
|
maskUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<rect x="0" y="0" width="24" height="24" fill="#fff" stroke="none" rx={radius} />
|
<rect
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="#fff"
|
||||||
|
stroke="none"
|
||||||
|
rx={radius}
|
||||||
|
/>
|
||||||
<path d={`M${prev.x} ${prev.y}h.01`} />
|
<path d={`M${prev.x} ${prev.y}h.01`} />
|
||||||
<path d={`M${next.x} ${next.y}h.01`} />
|
<path d={`M${next.x} ${next.y}h.01`} />
|
||||||
</mask>
|
</mask>
|
||||||
@@ -146,7 +204,10 @@ const ControlPath = ({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</g>
|
</g>
|
||||||
<g className="svg-preview-control-path-group" {...props}>
|
<g
|
||||||
|
className="svg-preview-control-path-group"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
{controlPaths.map(({ d, showMarker }, i) => (
|
{controlPaths.map(({ d, showMarker }, i) => (
|
||||||
<path
|
<path
|
||||||
key={i}
|
key={i}
|
||||||
@@ -155,18 +216,33 @@ const ControlPath = ({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</g>
|
</g>
|
||||||
<g className="svg-preview-control-path-marker-group" {...props}>
|
<g
|
||||||
|
className="svg-preview-control-path-marker-group"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
<path
|
<path
|
||||||
d={controlPaths
|
d={controlPaths
|
||||||
.flatMap(({ prev, next, showMarker }) =>
|
.flatMap(({ prev, next, showMarker }) =>
|
||||||
showMarker ? [`M${prev.x} ${prev.y}h.01`, `M${next.x} ${next.y}h.01`] : []
|
showMarker ? [`M${prev.x} ${prev.y}h.01`, `M${next.x} ${next.y}h.01`] : [],
|
||||||
)
|
)
|
||||||
.join('')}
|
.join('')}
|
||||||
/>
|
/>
|
||||||
{controlPaths.map(({ d, prev, next, startMarker, endMarker }, i) => (
|
{controlPaths.map(({ d, prev, next, startMarker, endMarker }, i) => (
|
||||||
<React.Fragment key={i}>
|
<React.Fragment key={i}>
|
||||||
{startMarker && <circle cx={prev.x} cy={prev.y} r={pointSize / 2} />}
|
{startMarker && (
|
||||||
{endMarker && <circle cx={next.x} cy={next.y} r={pointSize / 2} />}
|
<circle
|
||||||
|
cx={prev.x}
|
||||||
|
cy={prev.y}
|
||||||
|
r={pointSize / 2}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{endMarker && (
|
||||||
|
<circle
|
||||||
|
cx={next.x}
|
||||||
|
cy={next.y}
|
||||||
|
r={pointSize / 2}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</g>
|
</g>
|
||||||
@@ -182,19 +258,75 @@ const Radii = ({
|
|||||||
any
|
any
|
||||||
>) => {
|
>) => {
|
||||||
return (
|
return (
|
||||||
<g className="svg-preview-radii-group" {...props}>
|
<g
|
||||||
{paths
|
className="svg-preview-radii-group"
|
||||||
.filter(({ circle }) => circle)
|
{...props}
|
||||||
.map(({ c, prev, next, circle: { x, y, r } }) =>
|
>
|
||||||
c.name === 'circle' ? (
|
{paths.map(
|
||||||
<path d={`M${x} ${y}h.01`} />
|
({ c, prev, next, circle }, i) =>
|
||||||
) : (
|
circle && (
|
||||||
<>
|
<React.Fragment key={i}>
|
||||||
<path d={`M${prev.x} ${prev.y} ${x} ${y} ${next.x} ${next.y}`} />
|
{c.name !== 'circle' && (
|
||||||
<circle cy={y} cx={x} r={r} />
|
<path d={`M${prev.x} ${prev.y} ${circle.x} ${circle.y} ${next.x} ${next.y}`} />
|
||||||
</>
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
|
<circle
|
||||||
|
cy={circle.y}
|
||||||
|
cx={circle.x}
|
||||||
|
r={0.25}
|
||||||
|
strokeDasharray="0"
|
||||||
|
stroke={
|
||||||
|
(Math.round(circle.x * 100) / 100) % 1 !== 0 ||
|
||||||
|
(Math.round(circle.y * 100) / 100) % 1 !== 0
|
||||||
|
? 'red'
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cy={circle.y}
|
||||||
|
cx={circle.x}
|
||||||
|
r={circle.r}
|
||||||
|
stroke={(Math.round(circle.r * 1000) / 1000) % 1 !== 0 ? 'red' : undefined}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Handles = ({
|
||||||
|
paths,
|
||||||
|
...props
|
||||||
|
}: { paths: Path[] } & PathProps<
|
||||||
|
'strokeWidth' | 'stroke' | 'strokeDasharray' | 'strokeOpacity',
|
||||||
|
any
|
||||||
|
>) => {
|
||||||
|
console.log(paths);
|
||||||
|
return (
|
||||||
|
<g
|
||||||
|
className="svg-preview-handles-group"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{paths.map(({ c, prev, next, cp1, cp2 }) => (
|
||||||
|
<>
|
||||||
|
{cp1 && <path d={`M${prev.x} ${prev.y} ${cp1.x} ${cp1.y}`} />}
|
||||||
|
{cp1 && (
|
||||||
|
<circle
|
||||||
|
cy={cp1.y}
|
||||||
|
cx={cp1.x}
|
||||||
|
r={0.25}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{cp2 && <path d={`M${next.x} ${next.y} ${cp2.x} ${cp2.y}`} />}
|
||||||
|
{cp2 && (
|
||||||
|
<circle
|
||||||
|
cy={cp2.y}
|
||||||
|
cx={cp2.x}
|
||||||
|
r={0.25}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -236,8 +368,27 @@ const SvgPreview = React.forwardRef<
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<style>{darkModeCss}</style>
|
<style>{darkModeCss}</style>
|
||||||
{showGrid && <Grid strokeWidth={0.1} stroke="#777" strokeOpacity={0.3} radius={1} />}
|
{showGrid && (
|
||||||
<Shadow paths={paths} strokeWidth={4} stroke="#777" radius={1} strokeOpacity={0.15} />
|
<Grid
|
||||||
|
strokeWidth={0.1}
|
||||||
|
stroke="#777"
|
||||||
|
strokeOpacity={0.3}
|
||||||
|
radius={1}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Shadow
|
||||||
|
paths={paths}
|
||||||
|
strokeWidth={4}
|
||||||
|
stroke="#777"
|
||||||
|
radius={1}
|
||||||
|
strokeOpacity={0.15}
|
||||||
|
/>
|
||||||
|
<Handles
|
||||||
|
paths={paths}
|
||||||
|
strokeWidth={0.12}
|
||||||
|
stroke="#777"
|
||||||
|
strokeOpacity={0.6}
|
||||||
|
/>
|
||||||
<ColoredPath
|
<ColoredPath
|
||||||
paths={paths}
|
paths={paths}
|
||||||
colors={[
|
colors={[
|
||||||
@@ -262,7 +413,19 @@ const SvgPreview = React.forwardRef<
|
|||||||
stroke="#777"
|
stroke="#777"
|
||||||
strokeOpacity={0.3}
|
strokeOpacity={0.3}
|
||||||
/>
|
/>
|
||||||
<ControlPath radius={1} paths={paths} pointSize={1} stroke="#fff" strokeWidth={0.125} />
|
<ControlPath
|
||||||
|
radius={1}
|
||||||
|
paths={paths}
|
||||||
|
pointSize={1}
|
||||||
|
stroke="#fff"
|
||||||
|
strokeWidth={0.125}
|
||||||
|
/>
|
||||||
|
<Handles
|
||||||
|
paths={paths}
|
||||||
|
strokeWidth={0.12}
|
||||||
|
stroke="#FFF"
|
||||||
|
strokeOpacity={0.3}
|
||||||
|
/>
|
||||||
{children}
|
{children}
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,13 +8,15 @@ export type Path = {
|
|||||||
prev: Point;
|
prev: Point;
|
||||||
next: Point;
|
next: Point;
|
||||||
isStart: boolean;
|
isStart: boolean;
|
||||||
circle: { x: number; y: number; r: number };
|
circle?: { x: number; y: number; r: number };
|
||||||
|
cp1?: Point;
|
||||||
|
cp2?: Point;
|
||||||
c: ReturnType<typeof getCommands>[number];
|
c: ReturnType<typeof getCommands>[number];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PathProps<
|
export type PathProps<
|
||||||
RequiredProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
|
RequiredProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
|
||||||
NeverProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>
|
NeverProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
|
||||||
> = Required<Pick<React.SVGProps<SVGElement & SVGRectElement & SVGCircleElement>, RequiredProps>> &
|
> = Required<Pick<React.SVGProps<SVGElement & SVGRectElement & SVGCircleElement>, RequiredProps>> &
|
||||||
Omit<
|
Omit<
|
||||||
React.SVGProps<SVGPathElement & SVGRectElement & SVGCircleElement>,
|
React.SVGProps<SVGPathElement & SVGRectElement & SVGCircleElement>,
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const getCommands = (src: string) =>
|
|||||||
getNodes(src)
|
getNodes(src)
|
||||||
.map(convertToPathNode)
|
.map(convertToPathNode)
|
||||||
.flatMap(({ d, name }, idx) =>
|
.flatMap(({ d, name }, idx) =>
|
||||||
new SVGPathData(d).toAbs().commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name }))
|
new SVGPathData(d).toAbs().commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getPaths = (src: string) => {
|
export const getPaths = (src: string) => {
|
||||||
@@ -60,10 +60,10 @@ export const getPaths = (src: string) => {
|
|||||||
let prev: Point | undefined = undefined;
|
let prev: Point | undefined = undefined;
|
||||||
let start: Point | undefined = undefined;
|
let start: Point | undefined = undefined;
|
||||||
const addPath = (
|
const addPath = (
|
||||||
c: typeof commands[number],
|
c: (typeof commands)[number],
|
||||||
next: Point,
|
next: Point,
|
||||||
d?: string,
|
d?: string,
|
||||||
circle?: Path['circle']
|
extras?: { circle?: Path['circle']; cp1?: Path['cp1']; cp2?: Path['cp2'] },
|
||||||
) => {
|
) => {
|
||||||
assert(prev);
|
assert(prev);
|
||||||
paths.push({
|
paths.push({
|
||||||
@@ -71,7 +71,7 @@ export const getPaths = (src: string) => {
|
|||||||
d: d || `M ${prev.x} ${prev.y} L ${next.x} ${next.y}`,
|
d: d || `M ${prev.x} ${prev.y} L ${next.x} ${next.y}`,
|
||||||
prev,
|
prev,
|
||||||
next,
|
next,
|
||||||
circle,
|
...extras,
|
||||||
isStart: start === prev,
|
isStart: start === prev,
|
||||||
});
|
});
|
||||||
prev = next;
|
prev = next;
|
||||||
@@ -110,7 +110,10 @@ export const getPaths = (src: string) => {
|
|||||||
}
|
}
|
||||||
case SVGPathData.CURVE_TO: {
|
case SVGPathData.CURVE_TO: {
|
||||||
assert(prev);
|
assert(prev);
|
||||||
addPath(c, c, `M ${prev.x} ${prev.y} ${encodeSVGPath(c)}`);
|
addPath(c, c, `M ${prev.x} ${prev.y} ${encodeSVGPath(c)}`, {
|
||||||
|
cp1: { x: c.x1, y: c.y1 },
|
||||||
|
cp2: { x: c.x2, y: c.y2 },
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SVGPathData.SMOOTH_CURVE_TO: {
|
case SVGPathData.SMOOTH_CURVE_TO: {
|
||||||
@@ -146,20 +149,27 @@ export const getPaths = (src: string) => {
|
|||||||
y1: prev.y - reflectedCp1.y,
|
y1: prev.y - reflectedCp1.y,
|
||||||
x2: c.x2,
|
x2: c.x2,
|
||||||
y2: c.y2,
|
y2: c.y2,
|
||||||
})}`
|
})}`,
|
||||||
|
{
|
||||||
|
cp1: { x: prev.x - reflectedCp1.x, y: prev.y - reflectedCp1.y },
|
||||||
|
cp2: { x: c.x2, y: c.y2 },
|
||||||
|
},
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SVGPathData.QUAD_TO: {
|
case SVGPathData.QUAD_TO: {
|
||||||
assert(prev);
|
assert(prev);
|
||||||
addPath(c, c, `M ${prev.x} ${prev.y} ${encodeSVGPath(c)}`);
|
addPath(c, c, `M ${prev.x} ${prev.y} ${encodeSVGPath(c)}`, {
|
||||||
|
cp1: { x: c.x1, y: c.y1 },
|
||||||
|
cp2: { x: c.x1, y: c.y1 },
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SVGPathData.SMOOTH_QUAD_TO: {
|
case SVGPathData.SMOOTH_QUAD_TO: {
|
||||||
assert(prev);
|
assert(prev);
|
||||||
const backTrackCP = (
|
const backTrackCP = (
|
||||||
index: number,
|
index: number,
|
||||||
currentPoint: { x: number; y: number }
|
currentPoint: { x: number; y: number },
|
||||||
): { x: number; y: number } => {
|
): { x: number; y: number } => {
|
||||||
const previousCommand = commands[index - 1];
|
const previousCommand = commands[index - 1];
|
||||||
if (!previousCommand) {
|
if (!previousCommand) {
|
||||||
@@ -197,7 +207,11 @@ export const getPaths = (src: string) => {
|
|||||||
y: c.y,
|
y: c.y,
|
||||||
x1: prevCP.x,
|
x1: prevCP.x,
|
||||||
y1: prevCP.y,
|
y1: prevCP.y,
|
||||||
})}`
|
})}`,
|
||||||
|
{
|
||||||
|
cp1: { x: prevCP.x, y: prevCP.y },
|
||||||
|
cp2: { x: prevCP.x, y: prevCP.y },
|
||||||
|
},
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -212,13 +226,13 @@ export const getPaths = (src: string) => {
|
|||||||
c.lArcFlag,
|
c.lArcFlag,
|
||||||
c.sweepFlag,
|
c.sweepFlag,
|
||||||
c.x,
|
c.x,
|
||||||
c.y
|
c.y,
|
||||||
);
|
);
|
||||||
addPath(
|
addPath(
|
||||||
c,
|
c,
|
||||||
c,
|
c,
|
||||||
`M ${prev.x} ${prev.y} A${c.rX} ${c.rY} ${c.xRot} ${c.lArcFlag} ${c.sweepFlag} ${c.x} ${c.y}`,
|
`M ${prev.x} ${prev.y} A${c.rX} ${c.rY} ${c.xRot} ${c.lArcFlag} ${c.sweepFlag} ${c.x} ${c.y}`,
|
||||||
c.rX === c.rY ? { ...center, r: c.rX } : undefined
|
{ circle: c.rX === c.rY ? { ...center, r: c.rX } : undefined },
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -239,7 +253,7 @@ export const arcEllipseCenter = (
|
|||||||
fa: number,
|
fa: number,
|
||||||
fs: number,
|
fs: number,
|
||||||
x2: number,
|
x2: number,
|
||||||
y2: number
|
y2: number,
|
||||||
) => {
|
) => {
|
||||||
const phi = (a * Math.PI) / 180;
|
const phi = (a * Math.PI) / 180;
|
||||||
|
|
||||||
@@ -266,7 +280,7 @@ export const arcEllipseCenter = (
|
|||||||
sign *
|
sign *
|
||||||
Math.sqrt(
|
Math.sqrt(
|
||||||
Math.max(rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p, 0) /
|
Math.max(rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p, 0) /
|
||||||
(rx * rx * y1p * y1p + ry * ry * x1p * x1p)
|
(rx * rx * y1p * y1p + ry * ry * x1p * x1p),
|
||||||
);
|
);
|
||||||
|
|
||||||
const V2 = [(rx * y1p) / ry, (-ry * x1p) / rx];
|
const V2 = [(rx * y1p) / ry, (-ry * x1p) / rx];
|
||||||
|
|||||||
@@ -1,28 +1,34 @@
|
|||||||
import fs from "fs";
|
import fs from 'fs';
|
||||||
import path from "path";
|
import path from 'path';
|
||||||
import {Category, IconEntity} from "../theme/types";
|
import { Category, IconEntity } from '../theme/types';
|
||||||
|
|
||||||
const directory = path.join(process.cwd(), "../categories");
|
const directory = path.join(process.cwd(), '../categories');
|
||||||
|
|
||||||
export function getAllCategoryFiles(): Category[] {
|
export function getAllCategoryFiles(): Category[] {
|
||||||
const fileNames = fs.readdirSync(directory).filter((file) => path.extname(file) === '.json');
|
const fileNames = fs.readdirSync(directory).filter((file) => path.extname(file) === '.json');
|
||||||
|
|
||||||
return fileNames.map((fileName) => {
|
return fileNames.map((fileName) => {
|
||||||
const name = path.basename(fileName, '.json')
|
const name = path.basename(fileName, '.json');
|
||||||
const fileContent = fs.readFileSync(path.join(directory, fileName), 'utf8')
|
const fileContent = fs.readFileSync(path.join(directory, fileName), 'utf8');
|
||||||
|
|
||||||
const parsedFileContent = JSON.parse(fileContent)
|
const parsedFileContent = JSON.parse(fileContent);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
title: parsedFileContent.title,
|
title: parsedFileContent.title,
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapCategoryIconCount(categories: Category[], icons: { categories: IconEntity['categories'] }[]) {
|
export function mapCategoryIconCount(
|
||||||
|
categories: Category[],
|
||||||
|
icons: { categories: IconEntity['categories'] }[],
|
||||||
|
) {
|
||||||
return categories.map((category) => ({
|
return categories.map((category) => ({
|
||||||
...category,
|
...category,
|
||||||
iconCount: icons.reduce((acc, curr) => (curr.categories.includes(category.name) ? ++acc : acc), 0)
|
iconCount: icons.reduce(
|
||||||
}))
|
(acc, curr) => (curr.categories.includes(category.name) ? ++acc : acc),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,22 @@
|
|||||||
import {
|
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
|
||||||
BUNDLED_LANGUAGES,
|
import { getHighlighter } from 'shikiji';
|
||||||
type IThemeRegistration
|
|
||||||
} from 'shiki'
|
|
||||||
import {
|
|
||||||
getHighlighter,
|
|
||||||
} from 'shiki-processor'
|
|
||||||
|
|
||||||
type CodeExampleType = {
|
type CodeExampleType = {
|
||||||
title: string,
|
title: string;
|
||||||
lang: string,
|
language: string;
|
||||||
codes: {
|
code: string;
|
||||||
language?: string,
|
}[];
|
||||||
code: string,
|
|
||||||
metastring?: string,
|
|
||||||
}[],
|
|
||||||
}[]
|
|
||||||
|
|
||||||
const getIconCodes = (): CodeExampleType => {
|
const getIconCodes = (): CodeExampleType => {
|
||||||
return [
|
return [
|
||||||
{
|
|
||||||
lang: 'html',
|
|
||||||
title: 'HTML',
|
|
||||||
codes: [
|
|
||||||
{
|
{
|
||||||
language: 'html',
|
language: 'html',
|
||||||
code: `<i data-lucide-name="Name"></i>
|
title: 'HTML',
|
||||||
`,
|
code: `<i data-lucide="Name"></i>`,
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lang: 'tsx',
|
|
||||||
title: 'React',
|
|
||||||
codes: [
|
|
||||||
{
|
{
|
||||||
language: 'tsx',
|
language: 'tsx',
|
||||||
|
title: 'React',
|
||||||
code: `import { PascalCase } from 'lucide-react';
|
code: `import { PascalCase } from 'lucide-react';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
@@ -45,16 +27,10 @@ const App = () => {
|
|||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
`,
|
`,
|
||||||
|
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lang: 'vue',
|
|
||||||
title: 'Vue 3',
|
|
||||||
codes: [
|
|
||||||
{
|
{
|
||||||
language: 'vue',
|
language: 'vue',
|
||||||
|
title: 'Vue',
|
||||||
code: `<script setup>
|
code: `<script setup>
|
||||||
import { PascalCase } from 'lucide-vue-next';
|
import { PascalCase } from 'lucide-vue-next';
|
||||||
</script>
|
</script>
|
||||||
@@ -63,32 +39,20 @@ export default App;
|
|||||||
<PascalCase />
|
<PascalCase />
|
||||||
</template>
|
</template>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lang: 'svelte',
|
|
||||||
title: 'Svelte',
|
|
||||||
codes: [
|
|
||||||
{
|
{
|
||||||
language: 'svelte',
|
language: 'svelte',
|
||||||
|
title: 'Svelte',
|
||||||
code: `<script>
|
code: `<script>
|
||||||
import { PascalCase } from 'lucide-svelte';
|
import { PascalCase } from 'lucide-svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<PascalCase />
|
<PascalCase />
|
||||||
`,
|
`,
|
||||||
|
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lang: 'preact',
|
|
||||||
title: 'Preact',
|
|
||||||
codes: [
|
|
||||||
{
|
{
|
||||||
language: 'tsx',
|
language: 'tsx',
|
||||||
|
title: 'Preact',
|
||||||
code: `import { PascalCase } from 'lucide-preact';
|
code: `import { PascalCase } from 'lucide-preact';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
@@ -99,16 +63,10 @@ const App = () => {
|
|||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
`,
|
`,
|
||||||
|
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lang: 'solid',
|
|
||||||
title: 'Solid',
|
|
||||||
codes: [
|
|
||||||
{
|
{
|
||||||
language: 'tsx',
|
language: 'tsx',
|
||||||
|
title: 'Solid',
|
||||||
code: `import { PascalCase } from 'lucide-solid';
|
code: `import { PascalCase } from 'lucide-solid';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
@@ -119,16 +77,10 @@ const App = () => {
|
|||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
`,
|
`,
|
||||||
|
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lang: 'angular',
|
|
||||||
title: 'Angular',
|
|
||||||
codes: [
|
|
||||||
{
|
{
|
||||||
language: 'tsx',
|
language: 'tsx',
|
||||||
|
title: 'Angular',
|
||||||
code: `// app.module.ts
|
code: `// app.module.ts
|
||||||
import { LucideAngularModule, PascalCase } from 'lucide-angular';
|
import { LucideAngularModule, PascalCase } from 'lucide-angular';
|
||||||
|
|
||||||
@@ -142,14 +94,9 @@ import { LucideAngularModule, PascalCase } from 'lucide-angular';
|
|||||||
<lucide-icon name="Name"></lucide-icon>
|
<lucide-icon name="Name"></lucide-icon>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lang: 'html',
|
|
||||||
title: 'Icon Font',
|
|
||||||
codes: [
|
|
||||||
{
|
{
|
||||||
language: 'html',
|
language: 'html',
|
||||||
|
title: 'Icon Font',
|
||||||
code: `<style>
|
code: `<style>
|
||||||
@import ('~lucide-static/font/Lucide.css');
|
@import ('~lucide-static/font/Lucide.css');
|
||||||
</style>
|
</style>
|
||||||
@@ -157,62 +104,51 @@ import { LucideAngularModule, PascalCase } from 'lucide-angular';
|
|||||||
<div class="icon-Name"></div>
|
<div class="icon-Name"></div>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
},
|
};
|
||||||
{
|
|
||||||
lang: 'dart',
|
|
||||||
title: 'Flutter',
|
|
||||||
codes: [
|
|
||||||
{
|
|
||||||
language: 'dart',
|
|
||||||
code: `Icon(LucideIcons.Name);
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ThemeOptions =
|
export type ThemeOptions =
|
||||||
| IThemeRegistration
|
| ThemeRegistration
|
||||||
| { light: IThemeRegistration; dark: IThemeRegistration }
|
| { light: ThemeRegistration; dark: ThemeRegistration };
|
||||||
|
|
||||||
const highLightCode = async (code: string, lang: string, active?: boolean) => {
|
const highLightCode = async (code: string, lang: string, active?: boolean) => {
|
||||||
const highlighter = await getHighlighter({
|
const highlighter = await getHighlighter({
|
||||||
themes: ['material-theme-palenight'],
|
themes: ['github-light', 'github-dark'],
|
||||||
langs: [...BUNDLED_LANGUAGES],
|
langs: Object.keys(bundledLanguages),
|
||||||
processors: []
|
});
|
||||||
})
|
|
||||||
|
|
||||||
const highlightedCode = highlighter.codeToHtml(code, {
|
const highlightedCode = highlighter
|
||||||
|
.codeToHtml(code, {
|
||||||
lang,
|
lang,
|
||||||
// lineOptions,
|
themes: {
|
||||||
theme: 'material-theme-palenight'
|
light: 'github-light',
|
||||||
}).replace('background-color: #292D3E', '')
|
dark: 'github-dark',
|
||||||
|
},
|
||||||
|
defaultColor: false,
|
||||||
|
})
|
||||||
|
.replace('shiki-themes', 'shiki-themes vp-code');
|
||||||
|
|
||||||
return `<div class="language-${lang} ${active ? 'active' : ''}">
|
return `<div class="language-${lang} ${active ? 'active' : ''}">
|
||||||
<button title="Copy Code" class="copy"></button>
|
<button title="Copy Code" class="copy"></button>
|
||||||
<span class="lang">${lang}</span>
|
<span class="lang">${lang}</span>
|
||||||
${highlightedCode}
|
${highlightedCode}
|
||||||
</div>`
|
</div>`;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
export default async function createCodeExamples() {
|
export default async function createCodeExamples() {
|
||||||
const codes = getIconCodes();
|
const codes = getIconCodes();
|
||||||
|
|
||||||
const codeExamplePromises = codes.map(async (codeTemplate, index) => {
|
const codeExamplePromises = codes.map(async ({ title, language, code }, index) => {
|
||||||
const { title, lang, codes } = codeTemplate;
|
|
||||||
const isFirst = index === 0;
|
const isFirst = index === 0;
|
||||||
|
|
||||||
const code = await highLightCode(codes[0].code, codes[0].language || lang, isFirst);
|
const codeString = await highLightCode(code, language, isFirst);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title,
|
title,
|
||||||
language: codes[0].language || lang,
|
language: language,
|
||||||
code,
|
code: codeString,
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
return Promise.all(codeExamplePromises);
|
return Promise.all(codeExamplePromises);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,42 @@
|
|||||||
import { promises as fs, constants } from 'fs';
|
import { promises as fs, constants } from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import yaml from 'js-yaml'
|
import yaml from 'js-yaml';
|
||||||
import { PackageItem } from '../theme/types';
|
import { PackageItem } from '../theme/types';
|
||||||
|
|
||||||
const fileExist = (filePath) => fs.access(filePath, constants.F_OK).then(() => true).catch(() => false)
|
const fileExist = (filePath) =>
|
||||||
|
fs
|
||||||
|
.access(filePath, constants.F_OK)
|
||||||
|
.then(() => true)
|
||||||
|
.catch(() => false);
|
||||||
|
|
||||||
const fetchPackages = async (): Promise<PackageItem[]> => {
|
const fetchPackages = async (): Promise<PackageItem[]> => {
|
||||||
const docsDir = path.resolve(process.cwd(), '../packages');
|
const docsDir = path.resolve(process.cwd(), '../packages');
|
||||||
const fileNames = await (await fs.readdir(docsDir)).map(filename => ({filename, directory: docsDir}))
|
const fileNames = await (
|
||||||
|
await fs.readdir(docsDir)
|
||||||
|
).map((filename) => ({ filename, directory: docsDir }));
|
||||||
|
|
||||||
const packageJsons = await Promise.all(fileNames.map( async ({filename, directory}) => {
|
const packageJsons = await Promise.all(
|
||||||
const filePath = path.resolve(directory, filename)
|
fileNames.map(async ({ filename, directory }) => {
|
||||||
|
const filePath = path.resolve(directory, filename);
|
||||||
const fileStat = await fs.lstat(filePath);
|
const fileStat = await fs.lstat(filePath);
|
||||||
|
|
||||||
if(!fileStat.isDirectory()) return null;
|
if (!fileStat.isDirectory()) return null;
|
||||||
|
|
||||||
const jsonFilePath = path.resolve(filePath, 'package.json')
|
const jsonFilePath = path.resolve(filePath, 'package.json');
|
||||||
if (await fileExist(jsonFilePath)) {
|
if (await fileExist(jsonFilePath)) {
|
||||||
return JSON.parse(
|
return JSON.parse(await fs.readFile(jsonFilePath, 'utf-8'));
|
||||||
await fs.readFile(jsonFilePath, 'utf-8')
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ymlFilePath = path.resolve(filePath, 'pubspec.yaml')
|
const ymlFilePath = path.resolve(filePath, 'pubspec.yaml');
|
||||||
if(await fileExist(ymlFilePath)) {
|
if (await fileExist(ymlFilePath)) {
|
||||||
return yaml.load(
|
return yaml.load(await fs.readFile(ymlFilePath, 'utf-8'));
|
||||||
await fs.readFile(ymlFilePath, 'utf-8')
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
return packageJsons;
|
||||||
}))
|
};
|
||||||
|
|
||||||
return packageJsons
|
|
||||||
}
|
|
||||||
|
|
||||||
export default fetchPackages;
|
export default fetchPackages;
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
export type IconContent = [icon: string, src:string];
|
export type IconContent = [icon: string, src: string];
|
||||||
|
|
||||||
async function generateZip(icons: IconContent[]) {
|
async function generateZip(icons: IconContent[]) {
|
||||||
const JSZip = (await import('jszip')).default
|
const JSZip = (await import('jszip')).default;
|
||||||
|
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
|
|
||||||
const addingZipPromises = icons.map(([name, src]) =>
|
const addingZipPromises = icons.map(([name, src]) => zip.file(`${name}.svg`, src));
|
||||||
zip.file(`${name}.svg`, src),
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all(addingZipPromises)
|
await Promise.all(addingZipPromises);
|
||||||
|
|
||||||
return zip.generateAsync({ type: 'blob' });
|
return zip.generateAsync({ type: 'blob' });
|
||||||
}
|
}
|
||||||
|
|
||||||
export default generateZip
|
export default generateZip;
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import { createLucideIcon } from "lucide-react/src/lucide-react"
|
import { createLucideIcon } from 'lucide-react/src/lucide-react';
|
||||||
import { type LucideProps, type IconNode } from "lucide-react/src/createLucideIcon"
|
import { type LucideProps, type IconNode } from 'lucide-react/src/createLucideIcon';
|
||||||
import { IconEntity } from "../theme/types"
|
import { IconEntity } from '../theme/types';
|
||||||
import { renderToStaticMarkup } from 'react-dom/server';
|
import { renderToStaticMarkup } from 'react-dom/server';
|
||||||
import { IconContent } from "./generateZip";
|
import { IconContent } from './generateZip';
|
||||||
|
|
||||||
const getFallbackZip = (icons: IconEntity[], params: LucideProps) => {
|
const getFallbackZip = (icons: IconEntity[], params: LucideProps) => {
|
||||||
return icons
|
return icons.map<IconContent>((icon) => {
|
||||||
.map<IconContent>((icon) => {
|
const Icon = createLucideIcon(icon.name, icon.iconNode as IconNode);
|
||||||
const Icon = createLucideIcon(icon.name, icon.iconNode as IconNode)
|
const src = renderToStaticMarkup(<Icon {...params} />);
|
||||||
const src = renderToStaticMarkup(<Icon {...params} />)
|
return [icon.name, src];
|
||||||
return [icon.name, src]
|
});
|
||||||
})
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
export default getFallbackZip;
|
||||||
export default getFallbackZip
|
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
import fs from "fs";
|
import fs from 'fs';
|
||||||
import path from "path";
|
import path from 'path';
|
||||||
import { IconNodeWithKeys } from "../theme/types";
|
import { IconNodeWithKeys } from '../theme/types';
|
||||||
import iconNodes from '../data/iconNodes'
|
import iconNodes from '../data/iconNodes';
|
||||||
import releaseMeta from "../data/releaseMetaData.json";
|
import releaseMeta from '../data/releaseMetaData.json';
|
||||||
|
|
||||||
const DATE_OF_FORK = '2020-06-08T16:39:52+0100';
|
const DATE_OF_FORK = '2020-06-08T16:39:52+0100';
|
||||||
|
|
||||||
const directory = path.join(process.cwd(), "../icons");
|
const directory = path.join(process.cwd(), '../icons');
|
||||||
|
|
||||||
export interface GetDataOptions {
|
export interface GetDataOptions {
|
||||||
withChildKeys?: boolean
|
withChildKeys?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getData(name: string) {
|
export async function getData(name: string) {
|
||||||
const jsonPath = path.join(directory, `${name}.json`);
|
const jsonPath = path.join(directory, `${name}.json`);
|
||||||
const jsonContent = fs.readFileSync(jsonPath, "utf8");
|
const jsonContent = fs.readFileSync(jsonPath, 'utf8');
|
||||||
const { tags, categories, contributors } = JSON.parse(jsonContent);
|
const { tags, categories, contributors } = JSON.parse(jsonContent);
|
||||||
|
|
||||||
const iconNode = iconNodes[name]
|
const iconNode = iconNodes[name];
|
||||||
|
|
||||||
const releaseData = releaseMeta?.[name] ?? {
|
const releaseData = releaseMeta?.[name] ?? {
|
||||||
"createdRelease": {
|
createdRelease: {
|
||||||
"version": "0.0.0",
|
version: '0.0.0',
|
||||||
"date": DATE_OF_FORK
|
date: DATE_OF_FORK,
|
||||||
},
|
},
|
||||||
"changedRelease": {
|
changedRelease: {
|
||||||
"version": "0.0.0",
|
version: '0.0.0',
|
||||||
"date": DATE_OF_FORK
|
date: DATE_OF_FORK,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
@@ -36,11 +36,11 @@ export async function getData(name: string) {
|
|||||||
categories,
|
categories,
|
||||||
iconNode,
|
iconNode,
|
||||||
contributors,
|
contributors,
|
||||||
...releaseData
|
...releaseData,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAllData(): Promise<{ name: string, iconNode: IconNodeWithKeys}[]> {
|
export async function getAllData(): Promise<{ name: string; iconNode: IconNodeWithKeys }[]> {
|
||||||
const names = Object.keys(iconNodes);
|
const names = Object.keys(iconNodes);
|
||||||
|
|
||||||
return Promise.all(names.map((name) => getData(name)));
|
return Promise.all(names.map((name) => getData(name)));
|
||||||
|
|||||||
@@ -1,118 +1,127 @@
|
|||||||
import { DefaultTheme, UserConfig } from "vitepress"
|
import { DefaultTheme, UserConfig } from 'vitepress';
|
||||||
|
|
||||||
const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
|
const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
|
||||||
'guide':[
|
guide: [
|
||||||
{
|
{
|
||||||
text: 'Introduction',
|
text: 'Introduction',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'What is lucide?', link: '/guide/' },
|
{ text: 'What is lucide?', link: '/guide/' },
|
||||||
{ text: 'Installation', link: '/guide/installation' },
|
{ text: 'Installation', link: '/guide/installation' },
|
||||||
{ text: 'Comparison', link: '/guide/comparison' }
|
{ text: 'Comparison', link: '/guide/comparison' },
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// text: 'Using Icons',
|
text: 'Basics',
|
||||||
// items: [
|
items: [
|
||||||
// {
|
{
|
||||||
// text: 'How to use icons',
|
text: 'Color',
|
||||||
// link: 'how-to-use-icons'
|
link: '/guide/basics/color',
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// text: 'Styling icons',
|
text: 'Sizing',
|
||||||
// link: 'styling-icons'
|
link: '/guide/basics/sizing',
|
||||||
// },
|
},
|
||||||
|
{
|
||||||
|
text: 'Stroke width',
|
||||||
|
link: '/guide/basics/stroke-width',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// TODO: Add this section
|
||||||
|
{
|
||||||
|
text: 'Advanced',
|
||||||
|
items: [
|
||||||
// {
|
// {
|
||||||
// text: 'Accessibility',
|
// text: 'Accessibility',
|
||||||
// link: 'accessibility'
|
// link: '/guide/advanced/accessibility'
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
text: 'Global styling',
|
||||||
|
link: '/guide/advanced/global-styling',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// text: 'Animations',
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
text: 'Filled icons',
|
||||||
|
link: '/guide/advanced/filled-icons',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// text: 'Combining icons',
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// text: 'What should I use',
|
// text: 'Dynamic imports'
|
||||||
// link: 'what-should-i-use'
|
|
||||||
// },
|
// },
|
||||||
|
// {
|
||||||
// ]
|
// text: 'Auto importing'
|
||||||
// },
|
// },
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: 'Packages',
|
text: 'Packages',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
text: 'Lucide',
|
text: 'Lucide',
|
||||||
link: '/guide/packages/lucide'
|
link: '/guide/packages/lucide',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide React',
|
text: 'Lucide React',
|
||||||
link: '/guide/packages/lucide-react'
|
link: '/guide/packages/lucide-react',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide React Native',
|
text: 'Lucide React Native',
|
||||||
link: '/guide/packages/lucide-react-native'
|
link: '/guide/packages/lucide-react-native',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Vue',
|
text: 'Lucide Vue',
|
||||||
link: '/guide/packages/lucide-vue'
|
link: '/guide/packages/lucide-vue-next',
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Lucide Vue Next (Vue 3)',
|
|
||||||
link: '/guide/packages/lucide-vue-next'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Svelte',
|
text: 'Lucide Svelte',
|
||||||
link: '/guide/packages/lucide-svelte'
|
link: '/guide/packages/lucide-svelte',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Solid',
|
text: 'Lucide Solid',
|
||||||
link: '/guide/packages/lucide-solid'
|
link: '/guide/packages/lucide-solid',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Preact',
|
text: 'Lucide Preact',
|
||||||
link: '/guide/packages/lucide-preact'
|
link: '/guide/packages/lucide-preact',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Angular',
|
text: 'Lucide Angular',
|
||||||
link: '/guide/packages/lucide-angular'
|
link: '/guide/packages/lucide-angular',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Static',
|
text: 'Lucide Static',
|
||||||
link: '/guide/packages/lucide-static'
|
link: '/guide/packages/lucide-static',
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
text: 'Lucide Flutter',
|
|
||||||
link: '/guide/packages/lucide-flutter'
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Contributing',
|
text: 'Contributing',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
text: 'Icon Design Principles',
|
text: 'Icon Design Principles',
|
||||||
link: '/guide/design/icon-design-guide'
|
link: '/guide/design/icon-design-guide',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Designing in Illustrator',
|
text: 'Designing in Illustrator',
|
||||||
link: '/guide/design/illustrator-guide'
|
link: '/guide/design/illustrator-guide',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Designing in InkScape',
|
text: 'Designing in InkScape',
|
||||||
link: '/guide/design/inkscape-guide'
|
link: '/guide/design/inkscape-guide',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Designing in Figma',
|
text: 'Designing in Figma',
|
||||||
link: '/guide/design/figma-guide'
|
link: '/guide/design/figma-guide',
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'icons': [
|
},
|
||||||
{ text: '', link: '/' },
|
|
||||||
// { text: 'Categorized', link: '/icons/categorized' },
|
|
||||||
// {
|
|
||||||
// text: 'Categories',
|
|
||||||
// items: [
|
|
||||||
// ...(getAllCategoryFiles().map((category) => ({ text: category, link: `/icons/category/${category}` })))
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
],
|
],
|
||||||
}
|
// This should be here to keep the sidebar shown on the icons page
|
||||||
|
icons: [{ text: '', link: '/' }],
|
||||||
|
};
|
||||||
|
|
||||||
export default sidebar
|
export default sidebar;
|
||||||
|
|||||||
@@ -7,5 +7,6 @@
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.container {
|
.container {
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
|
padding-top: 33px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ const props = defineProps<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const component = computed(() => props.href ? 'a' : 'div')
|
const component = computed(() => props.href ? 'a' : 'div')
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -25,7 +23,7 @@ const component = computed(() => props.href ? 'a' : 'div')
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.badge, a.badge {
|
.badge, a.badge, .vp-doc a.badge {
|
||||||
display: block;
|
display: block;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -36,6 +34,7 @@ const component = computed(() => props.href ? 'a' : 'div')
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: var(--vp-c-bg-alt);
|
background-color: var(--vp-c-bg-alt);
|
||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
|
text-decoration: none;
|
||||||
/* width: 56px;
|
/* width: 56px;
|
||||||
height: 56px; */
|
height: 56px; */
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -59,4 +58,6 @@ const component = computed(() => props.href ? 'a' : 'div')
|
|||||||
/* color: var(--vp-button-alt-active-text);
|
/* color: var(--vp-button-alt-active-text);
|
||||||
background-color: var(--vp-button-alt-active-bg); */
|
background-color: var(--vp-button-alt-active-bg); */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -94,8 +94,7 @@ const ChevronUp = createLucideIcon('ChevronUp', chevronUp)
|
|||||||
.menu {
|
.menu {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.menu-items {
|
ul.menu-items {
|
||||||
--menu-offset: 44px;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -103,7 +102,7 @@ const ChevronUp = createLucideIcon('ChevronUp', chevronUp)
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
min-width: 128px;
|
min-width: 128px;
|
||||||
border: 1px solid var(--vp-c-divider);
|
border: 1px solid var(--vp-c-divider);
|
||||||
background-color: var(--vp-c-bg);
|
background-color: var(--vp-c-bg-elv);
|
||||||
box-shadow: var(--vp-shadow-3);
|
box-shadow: var(--vp-shadow-3);
|
||||||
transition: background-color 0.5s;
|
transition: background-color 0.5s;
|
||||||
max-height: calc(100vh - var(--vp-nav-height));
|
max-height: calc(100vh - var(--vp-nav-height));
|
||||||
@@ -124,11 +123,12 @@ const ChevronUp = createLucideIcon('ChevronUp', chevronUp)
|
|||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
transition: background-color .25s,color .25s;
|
transition: background-color .25s,color .25s;
|
||||||
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item:hover {
|
.menu-item:hover {
|
||||||
color: var(--vp-c-brand);
|
color: var(--vp-c-brand);
|
||||||
background-color: var(--vp-c-bg-elv-mute);
|
background-color: var(--vp-c-default-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item:active {
|
.menu-item:active {
|
||||||
@@ -170,11 +170,11 @@ const ChevronUp = createLucideIcon('ChevronUp', chevronUp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-items.bottom {
|
.menu-items.bottom {
|
||||||
top: var(--menu-offset);
|
top: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-items.top {
|
.menu-items.top {
|
||||||
bottom: var(--menu-offset);
|
bottom: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.arrow-up-button.top::before {
|
.arrow-up-button.top::before {
|
||||||
|
|||||||
56
docs/.vitepress/theme/components/base/Card.vue
Normal file
56
docs/.vitepress/theme/components/base/Card.vue
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
href?: string;
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const isExternal = computed(() => {
|
||||||
|
return props.href?.startsWith('http') ?? false
|
||||||
|
})
|
||||||
|
|
||||||
|
const component = computed(() => {
|
||||||
|
return props.href ? 'a' : 'div'
|
||||||
|
})
|
||||||
|
|
||||||
|
const rel = computed(() => {
|
||||||
|
return isExternal.value ? 'noreferrer noopener' : undefined
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="component"
|
||||||
|
:href="href"
|
||||||
|
:rel="rel"
|
||||||
|
class="card"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.card {
|
||||||
|
border: 1px solid var(--vp-c-bg-soft);
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: var(--vp-c-bg-soft);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 24px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card[href] {
|
||||||
|
display: block;
|
||||||
|
border: 1px solid var(--vp-c-bg-soft);
|
||||||
|
border-radius: 12px;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--vp-c-bg-soft);
|
||||||
|
transition: border-color .25s,background-color .25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card[href]:hover {
|
||||||
|
border-color: var(--vp-c-brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
36
docs/.vitepress/theme/components/base/CardGrid.vue
Normal file
36
docs/.vitepress/theme/components/base/CardGrid.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div class="grid">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: space-evenly;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid > * {
|
||||||
|
flex-basis: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 960px) {
|
||||||
|
.grid > * {
|
||||||
|
flex-basis: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.grid > * {
|
||||||
|
flex-basis: 33.33%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, defineProps, onMounted } from 'vue'
|
import { computed, onMounted } from 'vue'
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
groups: string[] | undefined,
|
groups: string[] | undefined,
|
||||||
groupName: string,
|
groupName: string,
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const value = computed({
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.color-picker {
|
.color-picker {
|
||||||
background: var(--color-picker-bg, var(--vp-c-bg-soft));
|
background: var(--color-picker-bg, var(--vp-c-bg-alt));
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
@@ -71,10 +71,10 @@ const value = computed({
|
|||||||
.color-input-text {
|
.color-input-text {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 0 8px;
|
padding: 0 0 0 8px;
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-1);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const SearchIcon = createLucideIcon('search', search)
|
|||||||
background: var(--vp-c-bg-soft);
|
background: var(--vp-c-bg-soft);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
padding: 12px 16px;
|
padding: 11px 16px;
|
||||||
height: auto;
|
height: auto;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
/* box-shadow: var(--vp-shadow-4), var(--vp-shadow-2); */
|
/* box-shadow: var(--vp-shadow-4), var(--vp-shadow-2); */
|
||||||
|
|||||||
63
docs/.vitepress/theme/components/base/GridSection.vue
Normal file
63
docs/.vitepress/theme/components/base/GridSection.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import CardGrid from './CardGrid.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
title: string,
|
||||||
|
headingLevel: 1 | 2 | 3 | 4 | 5 | 6,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const headingElement = computed(() => `h${props.headingLevel}`)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section>
|
||||||
|
<component :is="headingElement" class="name">{{ title }}</component>
|
||||||
|
<CardGrid>
|
||||||
|
<slot />
|
||||||
|
</CardGrid>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.name {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
margin-bottom: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: space-evenly;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid > * {
|
||||||
|
flex-basis: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 960px) {
|
||||||
|
.grid > * {
|
||||||
|
flex-basis: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.grid > * {
|
||||||
|
flex-basis: 33.33%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -49,7 +49,7 @@ const percentage = computed<string>(() => `${((Number(props.modelValue) - props.
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
line-height: 10px;
|
line-height: 10px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
--bar-color: var(--slider-bar-color, var(--vp-c-bg-soft));
|
--bar-color: var(--slider-bar-color, var(--vp-input-switch-bg-color));
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider:hover input{
|
.slider:hover input{
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
export default {
|
export default {
|
||||||
async load() {
|
async load() {
|
||||||
const version = await fetch('https://api.github.com/repos/lucide-icons/lucide/releases/latest').then(res => {
|
const version = await fetch('https://api.github.com/repos/lucide-icons/lucide/releases/latest')
|
||||||
|
.then((res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const releaseData = res.json() as Promise<{ tag_name: string }>
|
const releaseData = res.json() as Promise<{ tag_name: string }>;
|
||||||
|
|
||||||
return releaseData
|
return releaseData;
|
||||||
}
|
}
|
||||||
return null
|
return null;
|
||||||
}).then(res => res.tag_name)
|
})
|
||||||
|
.then((res) => res.tag_name);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
version
|
version,
|
||||||
}
|
};
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { data } from './HomeHeroBefore.data'
|
|||||||
:href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`"
|
:href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
>{{ data.version }}</Badge>
|
>v{{ data.version }}</Badge>
|
||||||
</HomeContainer>
|
</HomeContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import iconNodes from '../../../data/iconNodes'
|
import iconNodes from '../../../data/iconNodes';
|
||||||
|
|
||||||
const getRandomItem = <Item>(items: Item[]): Item => items[Math.floor(Math.random()*items.length)];
|
const getRandomItem = <Item>(items: Item[]): Item =>
|
||||||
|
items[Math.floor(Math.random() * items.length)];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async load() {
|
async load() {
|
||||||
const icons = Object.entries(iconNodes).map(([name, iconNode]) => ({ name, iconNode }))
|
const icons = Object.entries(iconNodes).map(([name, iconNode]) => ({ name, iconNode }));
|
||||||
|
|
||||||
const randomIcons = Array.from({ length: 200 }, () => getRandomItem(icons))
|
const randomIcons = Array.from({ length: 200 }, () => getRandomItem(icons));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
icons: randomIcons,
|
icons: randomIcons,
|
||||||
iconsCount: icons.length,
|
iconsCount: icons.length,
|
||||||
}
|
};
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ watch(absoluteStrokeWidth, (enabled) => {
|
|||||||
Lucide has a lot of customization options to match the icons with your UI.
|
Lucide has a lot of customization options to match the icons with your UI.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="customizer">
|
<div
|
||||||
|
class="customizer"
|
||||||
|
style="--color-picker-bg: var(--vp-input-switch-bg-color)"
|
||||||
|
>
|
||||||
<InputField
|
<InputField
|
||||||
id="icon-color"
|
id="icon-color"
|
||||||
label="Color"
|
label="Color"
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default {
|
|||||||
logo: '/framework-logos/flutter.svg',
|
logo: '/framework-logos/flutter.svg',
|
||||||
label: 'Lucide documentation for Flutter',
|
label: 'Lucide documentation for Flutter',
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
}
|
};
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import { getAllData } from '../../../lib/icons';
|
import { getAllData } from '../../../lib/icons';
|
||||||
import { getAllCategoryFiles, mapCategoryIconCount } from '../../../lib/categories';
|
import { getAllCategoryFiles, mapCategoryIconCount } from '../../../lib/categories';
|
||||||
import iconsMetaData from '../../../data/iconMetaData'
|
import iconsMetaData from '../../../data/iconMetaData';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async load() {
|
async load() {
|
||||||
let categories = getAllCategoryFiles()
|
let categories = getAllCategoryFiles();
|
||||||
|
|
||||||
categories = mapCategoryIconCount(categories, Object.values(iconsMetaData))
|
categories = mapCategoryIconCount(categories, Object.values(iconsMetaData));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
categories,
|
categories,
|
||||||
}
|
};
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { useData } from 'vitepress'
|
import { useData } from 'vitepress'
|
||||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
|
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
|
||||||
import { isActive } from 'vitepress/dist/client/shared'
|
import { isActive } from 'vitepress/dist/client/shared'
|
||||||
import { useActiveAnchor } from '../../composables/useActiveAnchor'
|
import { useActiveAnchor } from '../../composables/useActiveAnchor'
|
||||||
import { data } from './CategoryList.data'
|
import { data } from './CategoryList.data'
|
||||||
import CategoryListItem from './CategoryListItem.vue'
|
import CategoryListItem from './CategoryListItem.vue'
|
||||||
|
import { useCategoryView } from '../../composables/useCategoryView'
|
||||||
|
|
||||||
const { page } = useData()
|
const { page } = useData()
|
||||||
|
const { categoryCounts } = useCategoryView();
|
||||||
|
|
||||||
const categoriesIsActive = computed(() => {
|
const categoriesIsActive = computed(() => {
|
||||||
return isActive(page.value.relativePath, '/icons/categories');
|
return isActive(page.value.relativePath, '/icons/categories');
|
||||||
@@ -25,14 +27,21 @@ const headers = computed(() => {
|
|||||||
level: 2,
|
level: 2,
|
||||||
link: `${linkPrefix}#${name}`,
|
link: `${linkPrefix}#${name}`,
|
||||||
title,
|
title,
|
||||||
iconCount
|
iconCount: categoryCounts.value[name] ?? iconCount,
|
||||||
|
name
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
const container = ref()
|
const container = ref()
|
||||||
const marker = ref()
|
const marker = ref()
|
||||||
|
|
||||||
useActiveAnchor(container, marker)
|
const { setActiveLinkDebounced } = useActiveAnchor(container, marker)
|
||||||
|
|
||||||
|
watch(headers, () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setActiveLinkDebounced()
|
||||||
|
}, 200)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,47 +1,48 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted } from 'vue'
|
import { useCategoryView } from '../../composables/useCategoryView';
|
||||||
import { useCategoryView } from '../../composables/useCategoryView'
|
|
||||||
|
|
||||||
interface Header {
|
interface Header {
|
||||||
level: number
|
level: number;
|
||||||
title: string
|
title: string;
|
||||||
slug: string
|
slug: string;
|
||||||
iconCount: number
|
iconCount: number;
|
||||||
link: string
|
link: string;
|
||||||
children: Header[]
|
name: string;
|
||||||
|
children: Header[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type MenuItem = Omit<Header, 'slug' | 'children'> & {
|
type MenuItem = Omit<Header, 'slug' | 'children'> & {
|
||||||
children?: MenuItem[]
|
children?: MenuItem[];
|
||||||
}
|
};
|
||||||
|
|
||||||
const props = defineProps<{
|
defineProps<{
|
||||||
headers: MenuItem[]
|
headers: MenuItem[];
|
||||||
root?: boolean
|
root?: boolean;
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const { selectedCategory } = useCategoryView()
|
const { selectedCategory } = useCategoryView();
|
||||||
|
|
||||||
function onClick(event: Event) {
|
function onClick(categoryName: string) {
|
||||||
const target = (event.target as HTMLElement).nodeName === 'span' ? (event.target as HTMLElement).parentNode : event.target as HTMLElement
|
selectedCategory.value = categoryName;
|
||||||
const id = '#' + (target as HTMLAnchorElement).href!.split('#')[1]
|
|
||||||
const decodedId = decodeURIComponent(id)
|
|
||||||
|
|
||||||
selectedCategory.value = decodedId.replace('#', '')
|
const heading = document.querySelector<HTMLAnchorElement>(categoryName);
|
||||||
|
heading?.focus();
|
||||||
|
|
||||||
const heading = document.querySelector<HTMLAnchorElement>(decodedId)
|
window.history.pushState({}, '', `/icons/categories#${categoryName}`)
|
||||||
heading?.focus()
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ul :class="root ? 'root' : 'nested'">
|
<ul :class="root ? 'root' : 'nested'">
|
||||||
<li v-for="{ children, link, title, iconCount } in headers">
|
<li v-for="{ children, link, title, iconCount, name } in headers">
|
||||||
<a
|
<a
|
||||||
class="outline-link"
|
class="outline-link"
|
||||||
:href="link"
|
:href="link"
|
||||||
@click="onClick"
|
@click="onClick(name)"
|
||||||
:title="title"
|
:title="title"
|
||||||
|
:class="{
|
||||||
|
inactive: iconCount === 0,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
@@ -82,6 +83,10 @@ function onClick(event: Event) {
|
|||||||
transition: color 0.25s;
|
transition: color 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.outline-link.inactive {
|
||||||
|
color: var(--vp-c-text-4);
|
||||||
|
}
|
||||||
|
|
||||||
.outline-link.nested {
|
.outline-link.nested {
|
||||||
padding-left: 13px;
|
padding-left: 13px;
|
||||||
}
|
}
|
||||||
@@ -92,4 +97,8 @@ function onClick(event: Event) {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.outline-link.inactive .icon-count {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { startCase, camelCase } from 'lodash-es'
|
import { startCase, camelCase } from 'lodash-es'
|
||||||
import ButtonMenu from '../base/ButtonMenu.vue'
|
import ButtonMenu from '../base/ButtonMenu.vue'
|
||||||
import { useIconStyleContext } from '../../composables/useIconStyle';
|
import { useIconStyleContext } from '../../composables/useIconStyle';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { IconEntity } from '../../types'
|
import type { IconEntity } from '../../types'
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed } from 'vue'
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||||
import IconButton from '../base/IconButton.vue';
|
import IconButton from '../base/IconButton.vue';
|
||||||
import IconContributors from './IconContributors.vue';
|
import IconContributors from './IconContributors.vue';
|
||||||
@@ -10,6 +10,7 @@ import { useRouter } from 'vitepress';
|
|||||||
import IconInfo from './IconInfo.vue';
|
import IconInfo from './IconInfo.vue';
|
||||||
import Badge from '../base/Badge.vue';
|
import Badge from '../base/Badge.vue';
|
||||||
import { computedAsync } from '@vueuse/core';
|
import { computedAsync } from '@vueuse/core';
|
||||||
|
import { satisfies } from 'semver';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
iconName: string
|
iconName: string
|
||||||
@@ -25,6 +26,12 @@ const icon = computedAsync<IconEntity | null>(async () => {
|
|||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(['close'])
|
||||||
const isOpen = computed(() => !!icon.value)
|
const isOpen = computed(() => !!icon.value)
|
||||||
|
|
||||||
|
function releaseTagLink(version) {
|
||||||
|
const shouldAddV = satisfies(version, `<0.266.0`)
|
||||||
|
|
||||||
|
return `https://github.com/lucide-icons/lucide/releases/tag/${shouldAddV ? 'v' : ''}${version}`
|
||||||
|
}
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
@@ -43,7 +50,7 @@ const Expand = createLucideIcon('Expand', expand)
|
|||||||
<Badge
|
<Badge
|
||||||
v-if="icon.createdRelease"
|
v-if="icon.createdRelease"
|
||||||
class="version"
|
class="version"
|
||||||
:href="`https://github.com/lucide-icons/lucide/releases/tag/v${icon.createdRelease.version}`"
|
:href="releaseTagLink(icon.createdRelease.version)"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
>v{{ icon.createdRelease.version }}</Badge>
|
>v{{ icon.createdRelease.version }}</Badge>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import IconItem from './IconItem.vue'
|
|||||||
|
|
||||||
const emit = defineEmits(['setActiveIcon'])
|
const emit = defineEmits(['setActiveIcon'])
|
||||||
|
|
||||||
const props = defineProps<{
|
defineProps<{
|
||||||
icons: IconEntity[]
|
icons: IconEntity[]
|
||||||
activeIcon?: string
|
activeIcon?: string
|
||||||
overlayMode?: boolean
|
overlayMode?: boolean
|
||||||
@@ -19,7 +19,11 @@ function setActiveIcon(name: string) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
<div class="icon" v-for="icon in icons" :key="icon.name">
|
<div
|
||||||
|
class="icon"
|
||||||
|
v-for="icon in icons"
|
||||||
|
:key="icon.name"
|
||||||
|
>
|
||||||
<IconItem
|
<IconItem
|
||||||
v-bind="icon"
|
v-bind="icon"
|
||||||
@setActiveIcon="setActiveIcon(icon.name)"
|
@setActiveIcon="setActiveIcon(icon.name)"
|
||||||
@@ -36,7 +40,6 @@ function setActiveIcon(name: string) {
|
|||||||
.icons {
|
.icons {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
|
||||||
/* padding: 32px 32px 96px; */
|
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
||||||
import { useMediaQuery } from '@vueuse/core';
|
import { useMediaQuery } from '@vueuse/core';
|
||||||
import { useRouter } from 'vitepress';
|
import { useRouter } from 'vitepress';
|
||||||
@@ -27,7 +28,10 @@ const showOverlay = useMediaQuery('(min-width: 860px)');
|
|||||||
const { animate, confetti, confettiText } = useConfetti()
|
const { animate, confetti, confettiText } = useConfetti()
|
||||||
|
|
||||||
|
|
||||||
const icon = createLucideIcon(props.name, props.iconNode)
|
const icon = computed(() => {
|
||||||
|
if (!props.name || !props.iconNode) return null
|
||||||
|
return createLucideIcon(props.name, props.iconNode)
|
||||||
|
})
|
||||||
|
|
||||||
async function navigateToIcon(event) {
|
async function navigateToIcon(event) {
|
||||||
|
|
||||||
|
|||||||
@@ -1,56 +1,53 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { IconEntity } from '../../types';
|
||||||
|
|
||||||
|
type CategoryNameRow = {
|
||||||
|
type: 'category';
|
||||||
|
title: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CategoryIconsRow = {
|
||||||
|
type: 'icons';
|
||||||
|
icons: IconEntity[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CategoryRow = CategoryNameRow | CategoryIconsRow;
|
||||||
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
|
||||||
import { Category } from '../../types';
|
|
||||||
import IconGrid from './IconGrid.vue'
|
import IconGrid from './IconGrid.vue'
|
||||||
import { vIntersectionObserver } from '@vueuse/components'
|
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
activeIconName: string
|
activeIconName: string
|
||||||
category: Category
|
categoryRow: CategoryRow
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
|
||||||
const emit = defineEmits(['setActiveIcon'])
|
const emit = defineEmits(['setActiveIcon'])
|
||||||
|
|
||||||
const showIcons = ref(false)
|
|
||||||
|
|
||||||
// Added intersection observer to improve performance
|
|
||||||
const onIntersectionObserver: IntersectionObserverCallback = ([{ isIntersecting }]) => {
|
|
||||||
showIcons.value = isIntersecting
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section
|
<h2
|
||||||
class="category"
|
v-if="categoryRow.type === 'category'"
|
||||||
:key="category.name"
|
class="title"
|
||||||
:id="category.name"
|
|
||||||
v-intersection-observer="onIntersectionObserver"
|
|
||||||
>
|
>
|
||||||
<h2 class="title" >
|
<a class="header-anchor" :href="`#${categoryRow.name}`" :aria-label="`Permalink to "${categoryRow.title}"`">​</a>
|
||||||
<a class="header-anchor" :href="`#${category.name}`" :aria-label="`Permalink to "${category.title}"`">​</a>
|
{{ categoryRow.title }}
|
||||||
{{ category.title }}
|
|
||||||
</h2>
|
</h2>
|
||||||
<IconGrid
|
<IconGrid
|
||||||
|
v-else-if="categoryRow.type === 'icons'"
|
||||||
:activeIcon="activeIconName"
|
:activeIcon="activeIconName"
|
||||||
:icons="category.icons"
|
:icons="categoryRow.icons"
|
||||||
@setActiveIcon="$event => $emit('setActiveIcon', $event)"
|
@setActiveIcon="$event => $emit('setActiveIcon', $event)"
|
||||||
overlayMode
|
overlayMode
|
||||||
:hideIcons="!showIcons"
|
|
||||||
/>
|
/>
|
||||||
</section>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.title {
|
.title {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 8px;
|
||||||
font-size: 19px;
|
font-size: 19px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding-top: 86px;
|
padding: 24px 0 8px;
|
||||||
/* scroll-padding-top: 240px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.category {
|
|
||||||
margin-bottom: calc(-86px + 32px);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,66 +1,82 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, defineAsyncComponent } from 'vue'
|
import { ref, computed, defineAsyncComponent, onMounted } from 'vue';
|
||||||
import type { IconEntity, Category } from '../../types'
|
import type { IconEntity, Category } from '../../types';
|
||||||
import useSearch from '../../composables/useSearch'
|
import useSearch from '../../composables/useSearch';
|
||||||
import InputSearch from '../base/InputSearch.vue'
|
import InputSearch from '../base/InputSearch.vue';
|
||||||
import useSearchInput from '../../composables/useSearchInput'
|
import useSearchInput from '../../composables/useSearchInput';
|
||||||
import StickyBar from './StickyBar.vue'
|
import StickyBar from './StickyBar.vue';
|
||||||
import IconsCategory from './IconsCategory.vue'
|
import IconsCategory from './IconsCategory.vue';
|
||||||
import { useFetch } from '@vueuse/core'
|
import useFetchTags from '../../composables/useFetchTags';
|
||||||
import useFetchTags from '../../composables/useFetchTags'
|
import useFetchCategories from '../../composables/useFetchCategories';
|
||||||
import useFetchCategories from '../../composables/useFetchCategories'
|
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
|
||||||
|
import chunkArray from '../../utils/chunkArray';
|
||||||
|
import { CategoryRow } from './IconsCategory.vue';
|
||||||
|
import useScrollToCategory from '../../composables/useScrollToCategory';
|
||||||
|
|
||||||
|
const ICON_SIZE = 56;
|
||||||
|
const ICON_GRID_GAP = 8;
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
icons: IconEntity[]
|
icons: IconEntity[];
|
||||||
categories: Category[]
|
categories: Category[];
|
||||||
iconCategories: Record<string, string[]>
|
iconCategories: Record<string, string[]>;
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const activeIconName = ref(null)
|
const activeIconName = ref(null);
|
||||||
const { searchInput, searchQuery, searchQueryThrottled } = useSearchInput()
|
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
|
||||||
|
const isSearching = computed(() => !!searchQuery.value);
|
||||||
const isSearching = computed(() => !!searchQuery.value)
|
|
||||||
|
|
||||||
function setActiveIconName(name: string) {
|
function setActiveIconName(name: string) {
|
||||||
activeIconName.value = name
|
activeIconName.value = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { execute: fetchTags, data: tags } = useFetchTags()
|
const { execute: fetchTags, data: tags } = useFetchTags();
|
||||||
const { execute: fetchCategories, data: categoriesMap } = useFetchCategories()
|
const { execute: fetchCategories, data: categoriesMap } = useFetchCategories();
|
||||||
|
|
||||||
|
const overviewEl = ref<HTMLElement | null>(null);
|
||||||
|
const { width: containerWidth } = useElementSize(overviewEl)
|
||||||
|
|
||||||
|
const columnSize = computed(() => {
|
||||||
|
return Math.floor((containerWidth.value) / ((ICON_SIZE + ICON_GRID_GAP)));
|
||||||
|
});
|
||||||
|
|
||||||
const mappedIcons = computed(() => {
|
const mappedIcons = computed(() => {
|
||||||
if(tags.value == null) {
|
if (tags.value == null) {
|
||||||
return props.icons
|
return props.icons;
|
||||||
}
|
}
|
||||||
return props.icons.map((icon) => {
|
return props.icons.map((icon) => {
|
||||||
const iconTags = tags.value[icon.name]
|
const iconTags = tags.value[icon.name];
|
||||||
const iconCategories = categoriesMap.value?.[icon.name] ?? []
|
const iconCategories = categoriesMap.value?.[icon.name] ?? [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...icon,
|
...icon,
|
||||||
tags: iconTags,
|
tags: iconTags,
|
||||||
categories: iconCategories,
|
categories: iconCategories,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
const searchResults = useSearch(searchQuery, mappedIcons, [
|
const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
|
||||||
{ name: 'name', weight: 2 },
|
{ name: 'name', weight: 3 },
|
||||||
{ name: 'tags', weight: 1 },
|
{ name: 'aliases', weight: 3 },
|
||||||
])
|
{ name: 'tags', weight: 2 },
|
||||||
|
]);
|
||||||
|
|
||||||
const categories = computed(() => {
|
const categories = computed(() => {
|
||||||
if( !props.categories?.length || !props.icons?.length ) return []
|
if (!props.categories?.length || !props.icons?.length) return [];
|
||||||
|
|
||||||
return props.categories.map(({ name, title }) => {
|
return props.categories
|
||||||
|
.map(({ name, title }) => {
|
||||||
const categoryIcons = props.icons.filter((icon) => {
|
const categoryIcons = props.icons.filter((icon) => {
|
||||||
const iconCategories = props.iconCategories[icon.name]
|
const iconCategories = props.iconCategories[icon.name];
|
||||||
|
|
||||||
return iconCategories?.includes(name)
|
return iconCategories?.includes(name);
|
||||||
})
|
});
|
||||||
|
|
||||||
const searchedCategoryIcons = isSearching
|
const searchedCategoryIcons = isSearching
|
||||||
? categoryIcons.filter(icon => searchResults.value.some((item) => item?.name === icon?.name))
|
? categoryIcons.filter((icon) =>
|
||||||
|
searchResults.value.some((item) => item?.name === icon?.name)
|
||||||
|
)
|
||||||
: categoryIcons;
|
: categoryIcons;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -69,29 +85,59 @@ const categories = computed(() => {
|
|||||||
icons: searchedCategoryIcons,
|
icons: searchedCategoryIcons,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const categoriesList = computed(() => {
|
||||||
|
return categories.value
|
||||||
.filter(({ icons }) => icons.length)
|
.filter(({ icons }) => icons.length)
|
||||||
|
.reduce<CategoryRow[]>((acc, category) => {
|
||||||
|
acc.push({ type: 'category', title: category.title, name: category.name });
|
||||||
|
|
||||||
|
const categoryIcons = chunkArray(category.icons, columnSize.value);
|
||||||
|
categoryIcons.forEach((icons) => {
|
||||||
|
acc.push({ type: 'icons', icons });
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
|
||||||
|
categoriesList,
|
||||||
|
{
|
||||||
|
itemHeight: ICON_SIZE + ICON_GRID_GAP,
|
||||||
|
overscan: 10
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
useScrollToCategory({
|
||||||
|
categories,
|
||||||
|
categoriesList,
|
||||||
|
scrollTo,
|
||||||
|
searchQueryDebounced,
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
containerProps.ref.value = document.documentElement;
|
||||||
|
useEventListener(window, 'scroll', containerProps.onScroll)
|
||||||
})
|
})
|
||||||
|
|
||||||
function onFocusSearchInput() {
|
function onFocusSearchInput() {
|
||||||
if (tags.value == null) {
|
if (tags.value == null) {
|
||||||
fetchTags()
|
fetchTags();
|
||||||
}
|
}
|
||||||
if (categoriesMap.value == null) {
|
if (categoriesMap.value == null) {
|
||||||
fetchCategories()
|
fetchCategories();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NoResults = defineAsyncComponent(() =>
|
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
|
||||||
import('./NoResults.vue')
|
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
|
||||||
)
|
|
||||||
|
|
||||||
const IconDetailOverlay = defineAsyncComponent(() =>
|
|
||||||
import('./IconDetailOverlay.vue')
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<StickyBar class="search-bar category-search">
|
<div ref="overviewEl" class="overview-container">
|
||||||
|
<StickyBar class="category-search">
|
||||||
<InputSearch
|
<InputSearch
|
||||||
:placeholder="`Search ${icons.length} icons ...`"
|
:placeholder="`Search ${icons.length} icons ...`"
|
||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
@@ -105,13 +151,16 @@ const IconDetailOverlay = defineAsyncComponent(() =>
|
|||||||
:searchQuery="searchQuery"
|
:searchQuery="searchQuery"
|
||||||
@clear="searchQuery = ''"
|
@clear="searchQuery = ''"
|
||||||
/>
|
/>
|
||||||
|
<div v-bind="wrapperProps">
|
||||||
<IconsCategory
|
<IconsCategory
|
||||||
v-for="category in categories"
|
v-for="{ index, data } in list"
|
||||||
:key="category.name"
|
:categoryRow="data"
|
||||||
:category="category"
|
|
||||||
:activeIconName="activeIconName"
|
:activeIconName="activeIconName"
|
||||||
@setActiveIcon="setActiveIconName"
|
@setActiveIcon="setActiveIconName"
|
||||||
|
:key="index"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<IconDetailOverlay
|
<IconDetailOverlay
|
||||||
v-if="activeIconName != null"
|
v-if="activeIconName != null"
|
||||||
:iconName="activeIconName"
|
:iconName="activeIconName"
|
||||||
@@ -125,6 +174,21 @@ const IconDetailOverlay = defineAsyncComponent(() =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-bar.category-search {
|
.search-bar.category-search {
|
||||||
margin-bottom: -54px;
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 19px;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 24px 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icons {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-container {
|
||||||
|
padding-bottom: 288px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,109 +1,102 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch, defineAsyncComponent } from 'vue'
|
import { ref, computed, defineAsyncComponent, onMounted, watch } from 'vue';
|
||||||
import type { IconEntity } from '../../types'
|
import type { IconEntity } from '../../types';
|
||||||
import { useMediaQuery, useOffsetPagination } from '@vueuse/core'
|
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
|
||||||
import IconGrid from './IconGrid.vue'
|
import IconGrid from './IconGrid.vue';
|
||||||
import InputSearch from '../base/InputSearch.vue'
|
import InputSearch from '../base/InputSearch.vue';
|
||||||
import useSearch from '../../composables/useSearch'
|
import useSearch from '../../composables/useSearch';
|
||||||
import EndOfPage from '../base/EndOfPage.vue'
|
import useSearchInput from '../../composables/useSearchInput';
|
||||||
import useSearchInput from '../../composables/useSearchInput'
|
import StickyBar from './StickyBar.vue';
|
||||||
import StickyBar from './StickyBar.vue'
|
import useFetchTags from '../../composables/useFetchTags';
|
||||||
import useFetchTags from '../../composables/useFetchTags'
|
import useFetchCategories from '../../composables/useFetchCategories';
|
||||||
import useFetchCategories from '../../composables/useFetchCategories'
|
import chunkArray from '../../utils/chunkArray';
|
||||||
|
|
||||||
|
const ICON_SIZE = 56;
|
||||||
|
const ICON_GRID_GAP = 8;
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
icons: IconEntity[]
|
icons: IconEntity[];
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const activeIconName = ref(null)
|
const activeIconName = ref(null);
|
||||||
|
|
||||||
const isExtraLargeScreen = useMediaQuery('(min-width: 1440px)');
|
const { execute: fetchTags, data: tags } = useFetchTags();
|
||||||
const isLargeScreen = useMediaQuery('(min-width: 1280px)');
|
const { execute: fetchCategories, data: categories } = useFetchCategories();
|
||||||
const isMediumScreen = useMediaQuery('(min-width: 960px)');
|
|
||||||
const isSmallScreen = useMediaQuery('(min-width: 640px)');
|
|
||||||
|
|
||||||
const pageSize = computed(() => {
|
const overviewEl = ref<HTMLElement | null>(null);
|
||||||
if(isExtraLargeScreen.value) {
|
const { width: containerWidth } = useElementSize(overviewEl)
|
||||||
return 16 * 20;
|
|
||||||
}
|
|
||||||
if(isLargeScreen.value) {
|
|
||||||
return 16 * 12;
|
|
||||||
}
|
|
||||||
if(isMediumScreen.value) {
|
|
||||||
return 13 * 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isSmallScreen.value) {
|
const columnSize = computed(() => {
|
||||||
return 10 * 10;
|
return Math.floor((containerWidth.value) / ((ICON_SIZE + ICON_GRID_GAP)));
|
||||||
}
|
});
|
||||||
|
|
||||||
return 10 * 5;
|
|
||||||
})
|
|
||||||
|
|
||||||
const { execute: fetchTags, data: tags } = useFetchTags()
|
|
||||||
const { execute: fetchCategories, data: categories } = useFetchCategories()
|
|
||||||
|
|
||||||
const mappedIcons = computed(() => {
|
const mappedIcons = computed(() => {
|
||||||
if(tags.value == null) {
|
if (tags.value == null) {
|
||||||
return props.icons
|
return props.icons;
|
||||||
}
|
}
|
||||||
|
|
||||||
return props.icons.map((icon) => {
|
return props.icons.map((icon) => {
|
||||||
const iconTags = tags.value[icon.name]
|
const iconTags = tags.value[icon.name];
|
||||||
const iconCategories = categories.value?.[icon.name] ?? []
|
const iconCategories = categories.value?.[icon.name] ?? [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...icon,
|
...icon,
|
||||||
tags: iconTags,
|
tags: iconTags,
|
||||||
categories: iconCategories,
|
categories: iconCategories,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
const { searchInput, searchQuery, searchQueryThrottled } = useSearchInput()
|
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
|
||||||
const searchResults = useSearch(searchQueryThrottled, mappedIcons, [
|
const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
|
||||||
{ name: 'name', weight: 3 },
|
{ name: 'name', weight: 3 },
|
||||||
|
{ name: 'aliases', weight: 3 },
|
||||||
{ name: 'tags', weight: 2 },
|
{ name: 'tags', weight: 2 },
|
||||||
{ name: 'categories', weight: 1 },
|
{ name: 'categories', weight: 1 },
|
||||||
])
|
]);
|
||||||
|
|
||||||
const { next, currentPage } = useOffsetPagination( { pageSize })
|
const chunkedIcons = computed(() => {
|
||||||
|
return chunkArray(searchResults.value, columnSize.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
|
||||||
|
chunkedIcons,
|
||||||
|
{
|
||||||
|
itemHeight: ICON_SIZE + ICON_GRID_GAP,
|
||||||
|
overscan: 10
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
const paginatedIcons = computed(() => {
|
onMounted(() => {
|
||||||
const end = pageSize.value * currentPage.value
|
containerProps.ref.value = document.documentElement;
|
||||||
|
useEventListener(window, 'scroll', containerProps.onScroll)
|
||||||
return searchResults.value.slice(0, end)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
function setActiveIconName(name: string) {
|
function setActiveIconName(name: string) {
|
||||||
activeIconName.value = name
|
activeIconName.value = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(searchQueryThrottled, (searchString) => {
|
|
||||||
currentPage.value = 1
|
|
||||||
})
|
|
||||||
|
|
||||||
function onFocusSearchInput() {
|
function onFocusSearchInput() {
|
||||||
if (tags.value == null) {
|
if (tags.value == null) {
|
||||||
fetchTags()
|
fetchTags();
|
||||||
}
|
}
|
||||||
if (categories.value == null) {
|
if (categories.value == null) {
|
||||||
fetchCategories()
|
fetchCategories();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NoResults = defineAsyncComponent(() =>
|
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
|
||||||
import('./NoResults.vue')
|
|
||||||
)
|
|
||||||
|
|
||||||
const IconDetailOverlay = defineAsyncComponent(() =>
|
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
|
||||||
import('./IconDetailOverlay.vue')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
watch(searchQueryDebounced, () => {
|
||||||
|
scrollTo(0)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div ref="overviewEl" class="overview-container">
|
||||||
<StickyBar>
|
<StickyBar>
|
||||||
<InputSearch
|
<InputSearch
|
||||||
:placeholder="`Search ${icons.length} icons ...`"
|
:placeholder="`Search ${icons.length} icons ...`"
|
||||||
@@ -114,17 +107,22 @@ const IconDetailOverlay = defineAsyncComponent(() =>
|
|||||||
/>
|
/>
|
||||||
</StickyBar>
|
</StickyBar>
|
||||||
<NoResults
|
<NoResults
|
||||||
v-if="paginatedIcons.length === 0"
|
v-if="list.length === 0"
|
||||||
:searchQuery="searchQuery"
|
:searchQuery="searchQuery"
|
||||||
@clear="searchQuery = ''"
|
@clear="searchQuery = ''"
|
||||||
/>
|
/>
|
||||||
|
<div v-bind="wrapperProps" class="icon">
|
||||||
<IconGrid
|
<IconGrid
|
||||||
|
v-for="{ index, data: icons } in list"
|
||||||
|
:key="index"
|
||||||
overlayMode
|
overlayMode
|
||||||
|
:icons="icons"
|
||||||
:activeIcon="activeIconName"
|
:activeIcon="activeIconName"
|
||||||
:icons="paginatedIcons"
|
|
||||||
@setActiveIcon="setActiveIconName"
|
@setActiveIcon="setActiveIconName"
|
||||||
/>
|
/>
|
||||||
<EndOfPage @end-of-page="next" class="bottom-page"/>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<IconDetailOverlay
|
<IconDetailOverlay
|
||||||
v-if="activeIconName != null"
|
v-if="activeIconName != null"
|
||||||
:iconName="activeIconName"
|
:iconName="activeIconName"
|
||||||
@@ -134,10 +132,7 @@ const IconDetailOverlay = defineAsyncComponent(() =>
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.icons {
|
.icons {
|
||||||
display: grid;
|
margin-bottom: 8px;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
|
|
||||||
gap: 8px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@@ -148,7 +143,7 @@ const IconDetailOverlay = defineAsyncComponent(() =>
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-page {
|
.overview-container {
|
||||||
height: 288px;
|
padding-bottom: 288px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {ref} from 'vue'
|
import { ref, onMounted, computed } from 'vue'
|
||||||
import {bird} from '../../../data/iconNodes'
|
import { bird, squirrel, rabbit } from '../../../data/iconNodes'
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
||||||
import {useEventListener} from '@vueuse/core'
|
import {useEventListener} from '@vueuse/core'
|
||||||
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
|
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
|
||||||
|
import { IconNode } from '../../types'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
searchQuery: string
|
searchQuery: string
|
||||||
@@ -11,29 +12,45 @@ defineProps<{
|
|||||||
|
|
||||||
defineEmits(['clear'])
|
defineEmits(['clear'])
|
||||||
|
|
||||||
const birdIcon = ref<HTMLElement>()
|
const animalIcon = ref<HTMLElement>()
|
||||||
const Bird = createLucideIcon('bird', bird)
|
const randomAnimal = computed<IconNode>(() => {
|
||||||
|
return Math.random() > 0.5 ? squirrel : Math.random() > 0.5 ? rabbit : bird
|
||||||
|
})
|
||||||
|
const animalComponent = computed(() => createLucideIcon('animal', randomAnimal.value))
|
||||||
const flip = ref(false)
|
const flip = ref(false)
|
||||||
|
|
||||||
useEventListener(document, 'mousemove', (mouseEvent) => {
|
onMounted(() => {
|
||||||
const {width, height, x, y} = birdIcon.value.getBoundingClientRect()
|
useEventListener(document, 'mousemove', (mouseEvent) => {
|
||||||
|
const {width, height, x, y} = animalIcon.value.getBoundingClientRect()
|
||||||
|
|
||||||
const centerX = (width / 2) + x
|
const centerX = (width / 2) + x
|
||||||
|
|
||||||
flip.value = mouseEvent.x < centerX
|
flip.value = mouseEvent.x < centerX
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="no-results">
|
<div class="no-results">
|
||||||
<Bird class="bird-icon" ref="birdIcon" :class="{ flip }" :strokeWidth="1"/>
|
<component
|
||||||
|
:is="animalComponent"
|
||||||
|
class="animal-icon"
|
||||||
|
ref="animalIcon"
|
||||||
|
:class="{ flip }"
|
||||||
|
:strokeWidth="1"
|
||||||
|
/>
|
||||||
<h2 class="no-results-text">
|
<h2 class="no-results-text">
|
||||||
No icons found for '{{ searchQuery }}'
|
No icons found for '{{ searchQuery }}'
|
||||||
</h2>
|
</h2>
|
||||||
<VPButton text="Clear your search and try again" theme="alt" @click="$emit('clear')"/>
|
<VPButton
|
||||||
|
text="Clear your search and try again"
|
||||||
|
theme="alt"
|
||||||
|
@click="$emit('clear')"
|
||||||
|
/>
|
||||||
<span class="text-divider">or</span>
|
<span class="text-divider">or</span>
|
||||||
<VPButton text="Search on Github issues"
|
<VPButton
|
||||||
|
text="Search on Github issues"
|
||||||
theme="alt"
|
theme="alt"
|
||||||
:href="`https://github.com/lucide-icons/lucide/issues?q=is%3Aopen+${searchQuery}`"
|
:href="`https://github.com/lucide-icons/lucide/issues?q=is%3Aopen+${searchQuery}`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -48,7 +65,7 @@ useEventListener(document, 'mousemove', (mouseEvent) => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bird-icon {
|
.animal-icon {
|
||||||
width: 160px;
|
width: 160px;
|
||||||
height: 160px;
|
height: 160px;
|
||||||
color: var(--vp-c-neutral);
|
color: var(--vp-c-neutral);
|
||||||
@@ -56,12 +73,12 @@ useEventListener(document, 'mousemove', (mouseEvent) => {
|
|||||||
margin-top: 72px;
|
margin-top: 72px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bird-icon.flip {
|
.animal-icon.flip {
|
||||||
transform: rotateY(180deg);
|
transform: rotateY(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
@media (min-width: 960px) {
|
||||||
.bird-icon {
|
.animal-icon {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
height: 240px;
|
height: 240px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
.confetti-button:before,
|
.confetti-button:before,
|
||||||
.confetti-button:after {
|
.confetti-button:after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: "";
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
width: 140%;
|
width: 140%;
|
||||||
max-width: 160px;
|
max-width: 160px;
|
||||||
@@ -41,8 +41,16 @@
|
|||||||
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%),
|
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%),
|
||||||
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%),
|
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%),
|
||||||
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%);
|
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%);
|
||||||
background-size: 10% 10%, 20% 20%, 15% 15%, 20% 20%, 18% 18%, 10% 10%, 15% 15%,
|
background-size:
|
||||||
10% 10%, 18% 18%;
|
10% 10%,
|
||||||
|
20% 20%,
|
||||||
|
15% 15%,
|
||||||
|
20% 20%,
|
||||||
|
18% 18%,
|
||||||
|
10% 10%,
|
||||||
|
15% 15%,
|
||||||
|
10% 10%,
|
||||||
|
18% 18%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confetti-button:after {
|
.confetti-button:after {
|
||||||
@@ -55,7 +63,14 @@
|
|||||||
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%),
|
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%),
|
||||||
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%),
|
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%),
|
||||||
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%);
|
radial-gradient(circle, var(--confetti-color) 20%, transparent 20%);
|
||||||
background-size: 15% 15%, 20% 20%, 18% 18%, 20% 20%, 15% 15%, 10% 10%, 20% 20%;
|
background-size:
|
||||||
|
15% 15%,
|
||||||
|
20% 20%,
|
||||||
|
18% 18%,
|
||||||
|
20% 20%,
|
||||||
|
15% 15%,
|
||||||
|
10% 10%,
|
||||||
|
20% 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confetti-button.animate:before {
|
.confetti-button.animate:before {
|
||||||
@@ -70,35 +85,89 @@
|
|||||||
@keyframes topBubbles {
|
@keyframes topBubbles {
|
||||||
0% {
|
0% {
|
||||||
color: rgb(var(--text-color) / 0);
|
color: rgb(var(--text-color) / 0);
|
||||||
background-position: 5% 90%, 10% 90%, 10% 90%, 15% 90%, 25% 90%, 25% 90%,
|
background-position:
|
||||||
40% 90%, 55% 90%, 70% 90%;
|
5% 90%,
|
||||||
|
10% 90%,
|
||||||
|
10% 90%,
|
||||||
|
15% 90%,
|
||||||
|
25% 90%,
|
||||||
|
25% 90%,
|
||||||
|
40% 90%,
|
||||||
|
55% 90%,
|
||||||
|
70% 90%;
|
||||||
}
|
}
|
||||||
30% {
|
30% {
|
||||||
color: rgb(var(--text-color) / 1);
|
color: rgb(var(--text-color) / 1);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
background-position: 0% 80%, 0% 20%, 10% 40%, 20% 0%, 30% 30%, 22% 50%,
|
background-position:
|
||||||
50% 50%, 65% 20%, 90% 30%;
|
0% 80%,
|
||||||
|
0% 20%,
|
||||||
|
10% 40%,
|
||||||
|
20% 0%,
|
||||||
|
30% 30%,
|
||||||
|
22% 50%,
|
||||||
|
50% 50%,
|
||||||
|
65% 20%,
|
||||||
|
90% 30%;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
background-position: 0% 70%, 0% 10%, 10% 30%, 20% -10%, 30% 20%, 22% 40%,
|
background-position:
|
||||||
50% 40%, 65% 10%, 90% 20%;
|
0% 70%,
|
||||||
background-size: 0% 0%, 0% 0%, 0% 0%, 0% 0%, 0% 0%, 0% 0%;
|
0% 10%,
|
||||||
|
10% 30%,
|
||||||
|
20% -10%,
|
||||||
|
30% 20%,
|
||||||
|
22% 40%,
|
||||||
|
50% 40%,
|
||||||
|
65% 10%,
|
||||||
|
90% 20%;
|
||||||
|
background-size:
|
||||||
|
0% 0%,
|
||||||
|
0% 0%,
|
||||||
|
0% 0%,
|
||||||
|
0% 0%,
|
||||||
|
0% 0%,
|
||||||
|
0% 0%;
|
||||||
color: rgb(var(--text-color) / 0);
|
color: rgb(var(--text-color) / 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@keyframes bottomBubbles {
|
@keyframes bottomBubbles {
|
||||||
0% {
|
0% {
|
||||||
background-position: 10% -10%, 30% 10%, 55% -10%, 70% -10%, 85% -10%,
|
background-position:
|
||||||
70% -10%, 70% 0%;
|
10% -10%,
|
||||||
|
30% 10%,
|
||||||
|
55% -10%,
|
||||||
|
70% -10%,
|
||||||
|
85% -10%,
|
||||||
|
70% -10%,
|
||||||
|
70% 0%;
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
background-position: 0% 80%, 20% 80%, 45% 60%, 60% 100%, 75% 70%, 95% 60%,
|
background-position:
|
||||||
|
0% 80%,
|
||||||
|
20% 80%,
|
||||||
|
45% 60%,
|
||||||
|
60% 100%,
|
||||||
|
75% 70%,
|
||||||
|
95% 60%,
|
||||||
105% 0%;
|
105% 0%;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
background-position: 0% 90%, 20% 90%, 45% 70%, 60% 110%, 75% 80%, 95% 70%,
|
background-position:
|
||||||
|
0% 90%,
|
||||||
|
20% 90%,
|
||||||
|
45% 70%,
|
||||||
|
60% 110%,
|
||||||
|
75% 80%,
|
||||||
|
95% 70%,
|
||||||
110% 10%;
|
110% 10%;
|
||||||
background-size: 0% 0%, 0% 0%, 0% 0%, 0% 0%, 0% 0%, 0% 0%;
|
background-size:
|
||||||
|
0% 0%,
|
||||||
|
0% 0%,
|
||||||
|
0% 0%,
|
||||||
|
0% 0%,
|
||||||
|
0% 0%,
|
||||||
|
0% 0%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,15 +36,22 @@ const links = computed(() => [
|
|||||||
<template>
|
<template>
|
||||||
<footer v-if="theme.footer" class="VPFooter" :class="{ 'has-sidebar': hasSidebar }">
|
<footer v-if="theme.footer" class="VPFooter" :class="{ 'has-sidebar': hasSidebar }">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p v-if="theme.footer.message" class="message" v-html="theme.footer.message"></p>
|
<div class="sponsors">
|
||||||
<p v-if="theme.footer.copyright" class="copyright" v-html="theme.footer.copyright"></p>
|
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss" rel="noreferrer noopener">
|
||||||
|
<img src="/vercel.svg" alt="Powered by Vercel" width="200" />
|
||||||
|
</a>
|
||||||
|
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge" rel="noreferrer noopener">
|
||||||
|
<img src="/digitalocean.svg" alt="Digital Ocean" width="200" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<VPLink v-for="link in links" :href="link.href" :key="link.text" :rel="link.href.startsWith('http') ? 'noreferrer noopener': undefined">
|
<VPLink v-for="link in links" :href="link.href" :key="link.text" :rel="link.href.startsWith('http') ? 'noreferrer noopener': undefined">
|
||||||
{{ link.text }}
|
{{ link.text }}
|
||||||
</VPLink>
|
</VPLink>
|
||||||
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss" rel="noreferrer noopener">
|
</div>
|
||||||
<img src="/vercel.svg" alt="Powered by Vercel" width="200" />
|
<div>
|
||||||
</a>
|
<p v-if="theme.footer.message" class="message" v-html="theme.footer.message"></p>
|
||||||
|
<p v-if="theme.footer.copyright" class="copyright" v-html="theme.footer.copyright"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
@@ -67,7 +74,7 @@ const links = computed(() => [
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: var(--vp-layout-max-width);
|
max-width: calc(var(--vp-layout-max-width) - 64px);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -85,7 +92,7 @@ const links = computed(() => [
|
|||||||
.message { order: 2; }
|
.message { order: 2; }
|
||||||
.copyright { order: 1; }
|
.copyright { order: 1; }
|
||||||
|
|
||||||
.links {
|
.links, .sponsors {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 32px;
|
gap: 32px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -102,7 +109,7 @@ const links = computed(() => [
|
|||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
}
|
}
|
||||||
.links {
|
.links {
|
||||||
margin-left: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
import packageData from '../../../data/packageData.json';
|
import packageData from '../../../data/packageData.json';
|
||||||
import thirdPartyPackages from '../../../data/packageData.thirdParty.json';
|
import thirdPartyPackages from '../../../data/packageData.thirdParty.json';
|
||||||
import fetchPackages from "../../../lib/fetchPackages";
|
import fetchPackages from '../../../lib/fetchPackages';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async load() {
|
async load() {
|
||||||
const packages = await fetchPackages();
|
const packages = await fetchPackages();
|
||||||
return {
|
return {
|
||||||
packages: packages
|
packages: packages
|
||||||
.filter(p => p.name in packageData)
|
.filter((p) => p.name in packageData)
|
||||||
.map((pData) => ({
|
.map((pData) => ({
|
||||||
...pData,
|
...pData,
|
||||||
...packageData[pData.name],
|
...packageData[pData.name],
|
||||||
documentation: `/guide/packages/${pData.name}`,
|
documentation: `/guide/packages/${pData.name}`,
|
||||||
source: `https://github.com/lucide-icons/lucide/tree/main/packages/${pData.name}`,
|
source: `https://github.com/lucide-icons/lucide/tree/main/packages/${pData.name}`,
|
||||||
icon: `/framework-logos/${packageData[pData.name].icon}.svg`,
|
icon: `/framework-logos/${packageData[pData.name].icon}.svg`,
|
||||||
})).sort((a, b) => a.order - b.order),
|
}))
|
||||||
|
.sort((a, b) => a.order - b.order),
|
||||||
thirdPartyPackages,
|
thirdPartyPackages,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,24 +1,30 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {data} from './PackageList.data'
|
import {data} from './PackageList.data'
|
||||||
|
import GridSection from '../base/GridSection.vue'
|
||||||
import PackageListItem from "./PackageListItem.vue";</script>
|
import PackageListItem from "./PackageListItem.vue";</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="package-group">
|
<GridSection
|
||||||
<h1 class="name">Packages</h1>
|
title="Packages"
|
||||||
<div class="grid package-list" ref="container">
|
:headingLevel="1"
|
||||||
<div v-for="packageData in data.packages" class="item">
|
class="package-group"
|
||||||
<PackageListItem :packageData="packageData"/>
|
>
|
||||||
</div>
|
<PackageListItem
|
||||||
</div>
|
v-for="packageData in data.packages"
|
||||||
</section>
|
:packageData="packageData"
|
||||||
<section class="package-group">
|
/>
|
||||||
<h2 class="name">Third-party packages</h2>
|
</GridSection>
|
||||||
<div class="grid package-list" ref="container">
|
|
||||||
<div v-for="packageData in data.thirdPartyPackages" class="item">
|
<GridSection
|
||||||
<PackageListItem :packageData="packageData"/>
|
title="Third-party packages"
|
||||||
</div>
|
:headingLevel="2"
|
||||||
</div>
|
class="package-group"
|
||||||
</section>
|
>
|
||||||
|
<PackageListItem
|
||||||
|
v-for="packageData in data.thirdPartyPackages"
|
||||||
|
:packageData="packageData"
|
||||||
|
/>
|
||||||
|
</GridSection>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -26,7 +32,7 @@ import PackageListItem from "./PackageListItem.vue";</script>
|
|||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.package-group {
|
.package-group {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { useRouter } from 'vitepress';
|
import { useRouter } from 'vitepress';
|
||||||
import {PackageItem} from "../../types";
|
import {PackageItem} from "../../types";
|
||||||
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
|
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
|
||||||
|
import Card from '../base/Card.vue'
|
||||||
|
|
||||||
const { go } = useRouter()
|
const { go } = useRouter()
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -10,7 +11,8 @@ const props = defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<article class="package">
|
<div>
|
||||||
|
<Card class="package">
|
||||||
<header class="package-header">
|
<header class="package-header">
|
||||||
<div class="package-icon-well">
|
<div class="package-icon-well">
|
||||||
<img :src="packageData.icon" alt="" class="package-icon" :class="{[packageData.iconClass]: true, light: packageData.iconDark}" />
|
<img :src="packageData.icon" alt="" class="package-icon" :class="{[packageData.iconClass]: true, light: packageData.iconDark}" />
|
||||||
@@ -31,27 +33,18 @@ const props = defineProps<{
|
|||||||
:href="packageData.documentation"
|
:href="packageData.documentation"
|
||||||
text="Guide"
|
text="Guide"
|
||||||
theme="brand"
|
theme="brand"
|
||||||
@click="go(packageData.documentation)"
|
|
||||||
/>
|
/>
|
||||||
<VPButton
|
<VPButton
|
||||||
:href="packageData.source"
|
:href="packageData.source"
|
||||||
text="Source"
|
text="Source"
|
||||||
theme="alt"
|
theme="alt"
|
||||||
@click="go(packageData.source)"
|
|
||||||
/>
|
/>
|
||||||
</footer>
|
</footer>
|
||||||
</article>
|
</Card>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.package {
|
|
||||||
border: 1px solid var(--vp-c-bg-soft);
|
|
||||||
border-radius: 12px;
|
|
||||||
background-color: var(--vp-c-bg-soft);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
.package {
|
.package {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
72
docs/.vitepress/theme/components/showcase/ShowcaseList.vue
Normal file
72
docs/.vitepress/theme/components/showcase/ShowcaseList.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import companies from '../../../data/companiesData.json'
|
||||||
|
import componentLibraries from '../../../data/componentLibrariesData.json'
|
||||||
|
import GridSection from '../base/GridSection.vue'
|
||||||
|
import ShowcaseListItem from "./ShowcaseListItem.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<GridSection
|
||||||
|
title="Used by"
|
||||||
|
:headingLevel="1"
|
||||||
|
class="package-group"
|
||||||
|
>
|
||||||
|
<ShowcaseListItem
|
||||||
|
v-for="company in companies"
|
||||||
|
:showcaseItem="company"
|
||||||
|
/>
|
||||||
|
</GridSection>
|
||||||
|
|
||||||
|
<GridSection
|
||||||
|
title="Used in"
|
||||||
|
:headingLevel="1"
|
||||||
|
class="package-group"
|
||||||
|
>
|
||||||
|
<ShowcaseListItem
|
||||||
|
v-for="componentLibrary in componentLibraries"
|
||||||
|
:showcaseItem="componentLibrary"
|
||||||
|
/>
|
||||||
|
</GridSection>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.name {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-group {
|
||||||
|
margin-bottom: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: space-evenly;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid > * {
|
||||||
|
flex-basis: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 960px) {
|
||||||
|
.grid > * {
|
||||||
|
flex-basis: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.grid > * {
|
||||||
|
flex-basis: 33.33%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useRouter } from 'vitepress';
|
||||||
|
import { ShowcaseItem } from "../../types";
|
||||||
|
import Card from '../base/Card.vue'
|
||||||
|
|
||||||
|
const { go } = useRouter()
|
||||||
|
defineProps<{
|
||||||
|
showcaseItem: ShowcaseItem,
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Card
|
||||||
|
class="company"
|
||||||
|
:href="showcaseItem.url"
|
||||||
|
:aria-label="showcaseItem.name"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="showcaseItem.image.light"
|
||||||
|
class="logo light"
|
||||||
|
:alt="`${showcaseItem.name} logo`"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
:src="showcaseItem.image.dark"
|
||||||
|
class="logo dark"
|
||||||
|
:alt="`${showcaseItem.name} logo`"
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.company {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
padding: 48px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height:64px;
|
||||||
|
width: 240px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .logo.dark {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
html:not(.dark) .logo.light {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
import { onMounted, onUpdated, onUnmounted } from 'vue';
|
import { onMounted, onUpdated, onUnmounted } from 'vue';
|
||||||
import { throttleAndDebounce } from 'vitepress/dist/client/theme-default/support/utils'
|
import { throttleAndDebounce } from 'vitepress/dist/client/theme-default/support/utils';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is compied and adjusted from vitepress/dist/client/theme-default/composables/useActiveAnchor.ts
|
* This file is compied and adjusted from vitepress/dist/client/theme-default/composables/useActiveAnchor.ts
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function useActiveAnchor(container, marker) {
|
export function useActiveAnchor(container, marker) {
|
||||||
const onScroll = throttleAndDebounce(setActiveLink, 100);
|
const setActiveLinkDebounced = throttleAndDebounce(setActiveLink, 100);
|
||||||
let prevActiveLink = null;
|
let prevActiveLink = null;
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
requestAnimationFrame(setActiveLink);
|
requestAnimationFrame(setActiveLink);
|
||||||
window.addEventListener('scroll', onScroll);
|
window.addEventListener('scroll', setActiveLinkDebounced);
|
||||||
});
|
});
|
||||||
onUpdated(() => {
|
onUpdated(() => {
|
||||||
// sidebar update means a route change
|
// sidebar update means a route change
|
||||||
activateLink(location.hash);
|
activateLink(location.hash);
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener('scroll', onScroll);
|
window.removeEventListener('scroll', setActiveLinkDebounced);
|
||||||
});
|
});
|
||||||
function setActiveLink() {
|
function setActiveLink() {
|
||||||
const links = [].slice.call(container.value.querySelectorAll('.outline-link'));
|
const links = [].slice.call(container.value.querySelectorAll('.outline-link'));
|
||||||
@@ -59,15 +59,18 @@ export function useActiveAnchor(container, marker) {
|
|||||||
activeLink.classList.add('active');
|
activeLink.classList.add('active');
|
||||||
marker.value.style.top = activeLink.offsetTop + 5 + 'px';
|
marker.value.style.top = activeLink.offsetTop + 5 + 'px';
|
||||||
marker.value.style.opacity = '1';
|
marker.value.style.opacity = '1';
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
marker.value.style.top = '33px';
|
marker.value.style.top = '33px';
|
||||||
marker.value.style.opacity = '0';
|
marker.value.style.opacity = '0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
setActiveLinkDebounced,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const PAGE_OFFSET = 64;
|
const PAGE_OFFSET = 128;
|
||||||
|
|
||||||
function getAnchorTop(anchor) {
|
function getAnchorTop(anchor) {
|
||||||
return anchor.parentElement.offsetTop - PAGE_OFFSET;
|
return anchor.parentElement.offsetTop - PAGE_OFFSET;
|
||||||
@@ -75,7 +78,7 @@ function getAnchorTop(anchor) {
|
|||||||
function isAnchorActive(index, anchor, nextAnchor) {
|
function isAnchorActive(index, anchor, nextAnchor) {
|
||||||
const scrollTop = window.scrollY;
|
const scrollTop = window.scrollY;
|
||||||
if (index === 0 && scrollTop === 0) {
|
if (index === 0 && scrollTop === 0) {
|
||||||
return [true, null];
|
return [true, anchor.hash];
|
||||||
}
|
}
|
||||||
if (scrollTop < getAnchorTop(anchor)) {
|
if (scrollTop < getAnchorTop(anchor)) {
|
||||||
return [false, null];
|
return [false, null];
|
||||||
|
|||||||
@@ -1,25 +1,38 @@
|
|||||||
import {
|
import { useRoute } from 'vitepress';
|
||||||
ref, inject, Ref
|
import { ref, inject, Ref, onMounted, watch } from 'vue';
|
||||||
} from 'vue';
|
|
||||||
|
|
||||||
export const CATEGORY_VIEW_CONTEXT = Symbol('categoryView');
|
export const CATEGORY_VIEW_CONTEXT = Symbol('categoryView');
|
||||||
|
|
||||||
interface CategoryViewContext {
|
interface CategoryViewContext {
|
||||||
selectedCategory: Ref<string>
|
selectedCategory: Ref<string>;
|
||||||
categoryCounts: Ref<Record<string, number>>
|
categoryCounts: Ref<Record<string, number>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const categoryViewContext = {
|
export const categoryViewContext = {
|
||||||
selectedCategory: ref(''),
|
selectedCategory: ref(),
|
||||||
categoryCounts: ref({}),
|
categoryCounts: ref({}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useCategoryView(): CategoryViewContext {
|
export function useCategoryView(): CategoryViewContext {
|
||||||
const context = inject<CategoryViewContext>(CATEGORY_VIEW_CONTEXT);
|
const context = inject<CategoryViewContext>(CATEGORY_VIEW_CONTEXT);
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error('useCategoryView must be used with categoryView context');
|
throw new Error('useCategoryView must be used with categoryView context');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (window.location.hash) {
|
||||||
|
context.selectedCategory.value = decodeURIComponent(window.location.hash.slice(1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(route, (currentRoute) => {
|
||||||
|
if (currentRoute.path !== '/icons/categories') {
|
||||||
|
context.selectedCategory.value = '';
|
||||||
|
context.categoryCounts.value = {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ref } from "vue";
|
import { ref } from 'vue';
|
||||||
|
|
||||||
export default function useConfetti() {
|
export default function useConfetti() {
|
||||||
const animate = ref(false)
|
const animate = ref(false);
|
||||||
const confettiText = ref('confetti!')
|
const confettiText = ref('confetti!');
|
||||||
|
|
||||||
function confetti() {
|
function confetti() {
|
||||||
animate.value = true;
|
animate.value = true;
|
||||||
@@ -15,6 +15,6 @@ export default function useConfetti() {
|
|||||||
return {
|
return {
|
||||||
animate,
|
animate,
|
||||||
confetti,
|
confetti,
|
||||||
confettiText
|
confettiText,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useFetch } from "@vueuse/core"
|
import { useFetch } from '@vueuse/core';
|
||||||
|
|
||||||
const useFetchCategories = () => useFetch<Record<string, string[]>>(
|
const useFetchCategories = () =>
|
||||||
|
useFetch<Record<string, string[]>>(
|
||||||
`${import.meta.env.DEV ? 'http://localhost:3000' : ''}/api/categories`,
|
`${import.meta.env.DEV ? 'http://localhost:3000' : ''}/api/categories`,
|
||||||
{
|
{
|
||||||
immediate:
|
immediate:
|
||||||
typeof window !== 'undefined'
|
typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('search'),
|
||||||
&& new URLSearchParams(window.location.search).has('search'),
|
},
|
||||||
}
|
).json();
|
||||||
).json()
|
|
||||||
|
|
||||||
export default useFetchCategories
|
export default useFetchCategories;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useFetch } from "@vueuse/core"
|
import { useFetch } from '@vueuse/core';
|
||||||
|
|
||||||
const useFetchTags = () => useFetch<Record<string, string[]>>(
|
const useFetchTags = () =>
|
||||||
|
useFetch<Record<string, string[]>>(
|
||||||
`${import.meta.env.DEV ? 'http://localhost:3000' : ''}/api/tags`,
|
`${import.meta.env.DEV ? 'http://localhost:3000' : ''}/api/tags`,
|
||||||
{
|
{
|
||||||
immediate:
|
immediate:
|
||||||
typeof window !== 'undefined'
|
typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('search'),
|
||||||
&& new URLSearchParams(window.location.search).has('search'),
|
},
|
||||||
}
|
).json();
|
||||||
).json()
|
|
||||||
|
|
||||||
export default useFetchTags
|
export default useFetchTags;
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
import {
|
import { ref, inject, Ref } from 'vue';
|
||||||
ref, inject, Ref
|
|
||||||
} from 'vue';
|
|
||||||
|
|
||||||
export const ICON_STYLE_CONTEXT = Symbol('size');
|
export const ICON_STYLE_CONTEXT = Symbol('size');
|
||||||
|
|
||||||
interface IconSizeContext {
|
interface IconSizeContext {
|
||||||
size: Ref<number>
|
size: Ref<number>;
|
||||||
strokeWidth: Ref<number>
|
strokeWidth: Ref<number>;
|
||||||
color: Ref<string>
|
color: Ref<string>;
|
||||||
absoluteStrokeWidth: Ref<boolean>
|
absoluteStrokeWidth: Ref<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const STYLE_DEFAULTS = {
|
export const STYLE_DEFAULTS = {
|
||||||
@@ -27,7 +25,7 @@ export const iconStyleContext = {
|
|||||||
absoluteStrokeWidth: ref(false),
|
absoluteStrokeWidth: ref(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useIconStyleContext(): IconSizeContext{
|
export function useIconStyleContext(): IconSizeContext {
|
||||||
const context = inject<IconSizeContext>(ICON_STYLE_CONTEXT);
|
const context = inject<IconSizeContext>(ICON_STYLE_CONTEXT);
|
||||||
|
|
||||||
if (!context) {
|
if (!context) {
|
||||||
|
|||||||
52
docs/.vitepress/theme/composables/useScrollToCategory.ts
Normal file
52
docs/.vitepress/theme/composables/useScrollToCategory.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { Ref, onMounted, watch } from 'vue';
|
||||||
|
import { CategoryRow } from '../components/icons/IconsCategory.vue';
|
||||||
|
import { Category } from '../types';
|
||||||
|
import { useCategoryView } from './useCategoryView';
|
||||||
|
|
||||||
|
interface UseScrollToCategory {
|
||||||
|
categories: Ref<Pick<Category, 'name' | 'icons'>[]>;
|
||||||
|
categoriesList: Ref<CategoryRow[]>;
|
||||||
|
scrollTo: (index: number) => void;
|
||||||
|
searchQueryDebounced: Ref<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useScrollToCategory({
|
||||||
|
categories,
|
||||||
|
categoriesList,
|
||||||
|
scrollTo,
|
||||||
|
searchQueryDebounced,
|
||||||
|
}: UseScrollToCategory) {
|
||||||
|
const { selectedCategory, categoryCounts } = useCategoryView();
|
||||||
|
|
||||||
|
function scrollToSelectedCategory(selectedCategory: string) {
|
||||||
|
const category = categories.value.find((category) => category.name === selectedCategory);
|
||||||
|
|
||||||
|
if (category != null) {
|
||||||
|
const categoryRowIndex = categoriesList.value.findIndex(
|
||||||
|
(row) => row.type === 'category' && row.name === selectedCategory,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (categoryRowIndex !== -1) {
|
||||||
|
setTimeout(() => {
|
||||||
|
scrollTo(categoryRowIndex);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(selectedCategory, scrollToSelectedCategory);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
scrollToSelectedCategory(selectedCategory.value);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(searchQueryDebounced, () => {
|
||||||
|
scrollTo(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(categories, (items) => {
|
||||||
|
categoryCounts.value = Object.fromEntries(items.map(({ name, icons }) => [name, icons.length]));
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
import Fuse from 'fuse.js';
|
import Fuse from 'fuse.js';
|
||||||
import { shallowRef, computed, Ref } from 'vue';
|
import { shallowRef, computed, Ref } from 'vue';
|
||||||
|
|
||||||
const useSearch = <T>(query: Ref<string>, collection: Ref<T[]>, keys: Fuse.FuseOptionKey<T>[] = []) => {
|
const useSearch = <T>(
|
||||||
|
query: Ref<string>,
|
||||||
|
collection: Ref<T[]>,
|
||||||
|
keys: Fuse.FuseOptionKeyObject<T>[] = [],
|
||||||
|
) => {
|
||||||
const index = shallowRef(
|
const index = shallowRef(
|
||||||
new Fuse(collection.value, {
|
new Fuse(collection.value, {
|
||||||
threshold: 0.2,
|
threshold: 0.2,
|
||||||
keys,
|
keys,
|
||||||
})
|
}),
|
||||||
)
|
);
|
||||||
|
|
||||||
const results = computed(() => {
|
const results = computed(() => {
|
||||||
index.value.setCollection(collection.value);
|
index.value.setCollection(collection.value);
|
||||||
@@ -16,6 +20,16 @@ const useSearch = <T>(query: Ref<string>, collection: Ref<T[]>, keys: Fuse.FuseO
|
|||||||
return index.value.search(query.value).map((result) => result.item);
|
return index.value.search(query.value).map((result) => result.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keys.length !== 0) {
|
||||||
|
const mainKey = keys[0].name;
|
||||||
|
|
||||||
|
return collection.value.sort((a, b) => {
|
||||||
|
const aString = a[mainKey as keyof T] as string;
|
||||||
|
const bString = b[mainKey as keyof T] as string;
|
||||||
|
return aString.localeCompare(bString);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return collection.value;
|
return collection.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +1,41 @@
|
|||||||
import { refThrottled } from '@vueuse/core';
|
import { useDebounce } from '@vueuse/core';
|
||||||
import { nextTick, onMounted, ref, watch } from 'vue';
|
import { nextTick, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
const useSearchInput = () => {
|
const useSearchInput = () => {
|
||||||
const searchInput = ref()
|
const searchInput = ref();
|
||||||
const searchQuery = ref(
|
const searchQuery = ref<string>('');
|
||||||
typeof window === 'undefined'
|
const searchQueryDebounced = useDebounce<string>(searchQuery, 200);
|
||||||
? ''
|
|
||||||
: (
|
|
||||||
new URLSearchParams(window.location.search).get('search')
|
|
||||||
|| ''
|
|
||||||
)
|
|
||||||
)
|
|
||||||
const searchQueryThrottled = refThrottled(searchQuery, 400)
|
|
||||||
|
|
||||||
watch(searchQueryThrottled, (searchString) => {
|
watch(searchQueryDebounced, (searchString) => {
|
||||||
const newUrl = new URL(window.location.href);
|
const newUrl = new URL(window.location.href);
|
||||||
|
|
||||||
if(searchString === '') {
|
if (searchString === '') {
|
||||||
newUrl.searchParams.delete('search');
|
newUrl.searchParams.delete('search');
|
||||||
} else {
|
} else {
|
||||||
newUrl.searchParams.set('search', searchString);
|
newUrl.searchParams.set('search', searchString);
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
window.history.replaceState({}, '', newUrl)
|
window.history.replaceState({}, '', newUrl);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
if(searchParams.has('focus')) {
|
|
||||||
searchInput.value.focus()
|
if (searchParams.has('search')) {
|
||||||
|
searchQuery.value = searchParams.get('search');
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
if (searchParams.has('focus')) {
|
||||||
|
searchInput.value.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
searchInput,
|
searchInput,
|
||||||
searchQuery,
|
searchQuery,
|
||||||
searchQueryThrottled
|
searchQueryDebounced,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { h } from 'vue'
|
import { h } from 'vue';
|
||||||
import DefaultTheme from 'vitepress/theme'
|
import DefaultTheme from 'vitepress/theme';
|
||||||
import './style.css'
|
import './style.css';
|
||||||
import { Theme } from 'vitepress'
|
import { Theme } from 'vitepress';
|
||||||
import IconsSidebarNavAfter from './layouts/IconsSidebarNavAfter.vue'
|
import IconsSidebarNavAfter from './layouts/IconsSidebarNavAfter.vue';
|
||||||
import HomeHeroIconsCard from './components/home/HomeHeroIconsCard.vue'
|
import HomeHeroIconsCard from './components/home/HomeHeroIconsCard.vue';
|
||||||
import HomeHeroBefore from "./components/home/HomeHeroBefore.vue";
|
import HomeHeroBefore from './components/home/HomeHeroBefore.vue';
|
||||||
import { ICON_STYLE_CONTEXT, iconStyleContext } from './composables/useIconStyle'
|
import { ICON_STYLE_CONTEXT, iconStyleContext } from './composables/useIconStyle';
|
||||||
import { CATEGORY_VIEW_CONTEXT, categoryViewContext } from './composables/useCategoryView'
|
import { CATEGORY_VIEW_CONTEXT, categoryViewContext } from './composables/useCategoryView';
|
||||||
|
|
||||||
const theme: Partial<Theme> = {
|
const theme: Partial<Theme> = {
|
||||||
extends: DefaultTheme,
|
extends: DefaultTheme,
|
||||||
@@ -15,12 +15,12 @@ const theme: Partial<Theme> = {
|
|||||||
'home-hero-before': () => h(HomeHeroBefore),
|
'home-hero-before': () => h(HomeHeroBefore),
|
||||||
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
|
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
|
||||||
'home-hero-image': () => h(HomeHeroIconsCard),
|
'home-hero-image': () => h(HomeHeroIconsCard),
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
enhanceApp({ app }) {
|
enhanceApp({ app }) {
|
||||||
app.provide(ICON_STYLE_CONTEXT, iconStyleContext)
|
app.provide(ICON_STYLE_CONTEXT, iconStyleContext);
|
||||||
app.provide(CATEGORY_VIEW_CONTEXT, categoryViewContext)
|
app.provide(CATEGORY_VIEW_CONTEXT, categoryViewContext);
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export default theme
|
export default theme;
|
||||||
|
|||||||
34
docs/.vitepress/theme/sandpackTheme.json
Normal file
34
docs/.vitepress/theme/sandpackTheme.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"colors": {
|
||||||
|
"surface1": "var(--vp-code-block-bg)",
|
||||||
|
"surface2": "var(--vp-code-block-bg)",
|
||||||
|
"surface3": "var(--vp-code-line-highlight-color)",
|
||||||
|
"clickable": "var(--vp-c-text-2)",
|
||||||
|
"base": "#323232",
|
||||||
|
"disabled": "#C5C5C5",
|
||||||
|
"hover": "var(--vp-c-brand)",
|
||||||
|
"accent": "var(--vp-c-brand)",
|
||||||
|
"error": "var(--vp-c-red)",
|
||||||
|
"errorSurface": "#ffeceb"
|
||||||
|
},
|
||||||
|
"syntax": {
|
||||||
|
"plain": "var(--vp-code-editor-plain)",
|
||||||
|
"comment": {
|
||||||
|
"color": "var(--vp-code-editor-comment)",
|
||||||
|
"fontStyle": "italic"
|
||||||
|
},
|
||||||
|
"keyword": "var(--vp-code-editor-keyword)",
|
||||||
|
"tag": "var(--vp-code-editor-tag)",
|
||||||
|
"punctuation": "var(--vp-code-editor-punctuation)",
|
||||||
|
"definition": "var(--vp-code-editor-definition)",
|
||||||
|
"property": "var(--vp-code-editor-property)",
|
||||||
|
"static": "var(--vp-code-editor-static)",
|
||||||
|
"string": "var(--vp-code-editor-string)"
|
||||||
|
},
|
||||||
|
"font": {
|
||||||
|
"body": "var(--vp-font-family-base)",
|
||||||
|
"mono": "var(--vp-font-family-mono)",
|
||||||
|
"size": "var(--vp-code-font-size)",
|
||||||
|
"lineHeight": "var(--vp-code-line-height)"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,47 @@
|
|||||||
:root {
|
:root {
|
||||||
--vp-c-brand: #F56565;
|
--vp-c-brand: #f56565;
|
||||||
--vp-c-brand-light: #F67373;
|
--vp-c-brand-light: #f67373;
|
||||||
--vp-c-brand-lighter: #F89191;
|
--vp-c-brand-lighter: #f89191;
|
||||||
--vp-c-brand-dark: #DC5A5A;
|
--vp-c-brand-dark: #dc5a5a;
|
||||||
--vp-c-brand-darker: #C45050;
|
--vp-c-brand-darker: #c45050;
|
||||||
|
|
||||||
|
--vp-c-brand-1: #f67373;
|
||||||
|
--vp-c-brand-2: #ff7070;
|
||||||
|
--vp-c-brand-3: #f56565;
|
||||||
|
--vp-c-brand-4: #dc5a5a;
|
||||||
|
--vp-c-brand-5: #c45050;
|
||||||
|
|
||||||
--vp-c-bg-alt-up: #fff;
|
--vp-c-bg-alt-up: #fff;
|
||||||
--vp-c-bg-alt-down: #fff;
|
--vp-c-bg-alt-down: #fff;
|
||||||
|
|
||||||
|
--vp-code-editor-plain: #24292e;
|
||||||
|
--vp-code-editor-comment: #6a737d;
|
||||||
|
--vp-code-editor-keyword: #d73a49;
|
||||||
|
--vp-code-editor-tag: #22863a;
|
||||||
|
--vp-code-editor-punctuation: #24292e;
|
||||||
|
--vp-code-editor-definition: #6f42c1;
|
||||||
|
--vp-code-editor-property: #005cc5;
|
||||||
|
--vp-code-editor-static: #f78c6c;
|
||||||
|
--vp-code-editor-string: #032f62;
|
||||||
|
|
||||||
|
--vp-c-text-4: rgba(60, 60, 67, 0.32);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--vp-c-bg-alt-up: #1B1B1D;
|
--vp-c-bg-alt-up: #1b1b1d;
|
||||||
--vp-c-bg-alt-down: #0F0F10;
|
--vp-c-bg-alt-down: #0f0f10;
|
||||||
|
|
||||||
|
--vp-code-editor-plain: #e1e4e8;
|
||||||
|
--vp-code-editor-comment: #6a737d;
|
||||||
|
--vp-code-editor-keyword: #f97583;
|
||||||
|
--vp-code-editor-tag: #85e89d;
|
||||||
|
--vp-code-editor-punctuation: #9ecbff;
|
||||||
|
--vp-code-editor-definition: #b392f0;
|
||||||
|
--vp-code-editor-property: #79b8ff;
|
||||||
|
--vp-code-editor-static: #f78c6c;
|
||||||
|
--vp-code-editor-string: #9ecbff;
|
||||||
|
|
||||||
|
--vp-c-text-4: rgba(235, 235, 245, 0.16);
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPNavBarTitle .logo {
|
.VPNavBarTitle .logo {
|
||||||
@@ -43,7 +73,6 @@
|
|||||||
|
|
||||||
.VPHomeHero .container .main h1.name {
|
.VPHomeHero .container .main h1.name {
|
||||||
color: var(--vp-c-text);
|
color: var(--vp-c-text);
|
||||||
|
|
||||||
}
|
}
|
||||||
.VPHomeHero .container .main h1.name .clip {
|
.VPHomeHero .container .main h1.name .clip {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@@ -56,7 +85,6 @@
|
|||||||
color: var(--vp-c-brand);
|
color: var(--vp-c-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
.VPHomeHero .container .image {
|
.VPHomeHero .container .image {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -75,14 +103,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.VPFeature .icon {
|
.VPFeature .icon {
|
||||||
background-color: var(--vp-c-bg);;
|
background-color: var(--vp-c-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vp-doc[class*=" _icons_"] > div {
|
.vp-doc[class*=' _icons_'] > div {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPDoc:has(.vp-doc[class*=" _icons_"]) > .container > .content{
|
.VPDoc:has(.vp-doc[class*=' _icons_']) > .container > .content {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
@@ -94,7 +122,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
@media (min-width: 960px) {
|
||||||
|
|
||||||
.VPHomeHero .container .image {
|
.VPHomeHero .container .image {
|
||||||
order: 1;
|
order: 1;
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
@@ -115,7 +142,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.VPHomeHero .container .main h1.name {
|
.VPHomeHero .container .main h1.name {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,3 +152,56 @@
|
|||||||
html:has(* .outline-link:target) {
|
html:has(* .outline-link:target) {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
.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;
|
||||||
|
/* box-sizing: content-box; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export type IconNode = [elementName: string, attrs: Record<string, string>][]
|
export type IconNode = [elementName: string, attrs: Record<string, string>][];
|
||||||
export type IconNodeWithKeys = [elementName: string, attrs: Record<string, string>, key: string][]
|
export type IconNodeWithKeys = [elementName: string, attrs: Record<string, string>, key: string][];
|
||||||
|
|
||||||
export interface IconEntity {
|
export interface IconEntity {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -13,34 +13,44 @@ export interface IconEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Category {
|
export interface Category {
|
||||||
name: string
|
name: string;
|
||||||
title: string
|
title: string;
|
||||||
icon?: string
|
icon?: string;
|
||||||
iconCount: number
|
iconCount: number;
|
||||||
icons?: IconEntity[]
|
icons?: IconEntity[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Shield {
|
interface Shield {
|
||||||
alt: string
|
alt: string;
|
||||||
src: string
|
src: string;
|
||||||
href: string
|
href: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PackageItem {
|
export interface PackageItem {
|
||||||
name: string
|
name: string;
|
||||||
description: string
|
description: string;
|
||||||
icon: string
|
icon: string;
|
||||||
iconDark: string
|
iconDark: string;
|
||||||
shields: Shield[]
|
shields: Shield[];
|
||||||
source: string
|
source: string;
|
||||||
documentation: string
|
documentation: string;
|
||||||
order?: number
|
order?: number;
|
||||||
private?: boolean
|
private?: boolean;
|
||||||
flutter?: object
|
flutter?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface Release {
|
export interface Release {
|
||||||
version: string
|
version: string;
|
||||||
date: string
|
date: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ShowcaseItemImage {
|
||||||
|
light: string;
|
||||||
|
dark: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShowcaseItem {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
image: ShowcaseItemImage;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user