Compare commits

..

6 Commits

Author SHA1 Message Date
Jakob Guddas
13aea2d3ac Update icons/barn.svg 2024-03-21 22:40:25 +01:00
Jakob Guddas
bd7ad68321 Update icons/barn.svg 2024-03-21 22:39:53 +01:00
Jakob Guddas
b708d7d761 Update icons/barn.json 2024-03-21 22:36:48 +01:00
Jakob Guddas
2cdea04562 Update icons/barn.svg 2024-03-21 22:36:33 +01:00
Jakob Guddas
cde043e2fc Merge branch 'main' into studio/barn 2024-03-21 22:33:11 +01:00
Jakob Guddas
912c81a8d8 chore: pull barn icon from lab branch 2024-03-21 22:28:22 +01:00
1378 changed files with 57814 additions and 32447 deletions

View File

@@ -8,8 +8,6 @@ squircle
strikethrough strikethrough
touchpad touchpad
ungroup ungroup
pilcrow
toc
# Brands # Brands
codepen codepen

View File

@@ -1,4 +1,4 @@
const DEFAULT_ATTRS = require('./tools/build-icons/render/default-attrs.json'); const DEFAULT_ATTRS = require('./scripts/render/default-attrs.json');
module.exports = { module.exports = {
root: true, root: true,
@@ -15,9 +15,7 @@ module.exports = {
'no-use-before-define': 'off', 'no-use-before-define': 'off',
'import/no-extraneous-dependencies': [ 'import/no-extraneous-dependencies': [
'error', 'error',
{ { devDependencies: ['**/*.test.js', '**/*.spec.js', './scripts/**'] },
devDependencies: ['**/*.test.js', '**/*.spec.js', '**/scripts/**'],
},
], ],
'import/extensions': [ 'import/extensions': [
'error', 'error',

View File

@@ -69,9 +69,6 @@ body:
- label: Windows - label: Windows
- label: Linux - label: Linux
- label: macOS - label: macOS
- label: ChromeOS
- label: iOS
- label: Android
- label: Other/not relevant - label: Other/not relevant
- type: textarea - type: textarea
id: description id: description

View File

@@ -30,9 +30,6 @@ body:
- label: Windows - label: Windows
- label: Linux - label: Linux
- label: macOS - label: macOS
- label: ChromeOS
- label: iOS
- label: Android
- label: Other/not relevant - label: Other/not relevant
- type: textarea - type: textarea
id: description id: description

View File

@@ -47,7 +47,7 @@
- [ ] I've made sure that the icons look sharp on low DPI displays. - [ ] I've made sure that the icons look sharp on low DPI displays.
- [ ] I've made sure that the icons look consistent with the icon set in size, optical volume and density. - [ ] I've made sure that the icons look consistent with the icon set in size, optical volume and density.
- [ ] I've made sure that the icons are visually centered. - [ ] I've made sure that the icons are visually centered.
- [ ] I've correctly optimized all icons to three points of precision. - [ ] I've correctly optimized all icons to two points of precision.
## Before Submitting <!-- For every PR! --> ## Before Submitting <!-- For every PR! -->
<!-- All of these requirements must be fulfilled. --> <!-- All of these requirements must be fulfilled. -->

View File

@@ -17,6 +17,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18

View File

@@ -1,35 +0,0 @@
name: Close Issue with Banned Phrases
on:
issues:
types: [opened]
jobs:
block_phrases:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Check for blocked phrases in issue title
run: |
ISSUE_TITLE=$(jq -r '.issue.title' "$GITHUB_EVENT_PATH")
BLOCKED_PHRASES=("twitter" "whatsapp" "logo" "google" "tiktok" "facebook" "slack" "discord" "bluesky")
# Check title and body for blocked phrases
for PHRASE in "${BLOCKED_PHRASES[@]}"
do
if echo "$ISSUE_TITLE" | grep -i "$PHRASE"; then
gh issue close ${{ github.event.issue.number }} --reason "not planned" --comment "This looks like a duplicate, use the [search](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+$PHRASE) to find similar issues.
Read more about brand guideline rules at [We're not accepting new Brand icons #670](https://github.com/lucide-icons/lucide/issues/670).
Always happy to help on [Discord](https://discord.gg/EH6nSts)."
gh issue lock ${{ github.event.issue.number }} --reason spam
exit 1
fi
done
env:
GH_TOKEN: ${{ github.token }}

View File

@@ -11,6 +11,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18

View File

@@ -8,11 +8,13 @@ on:
- pnpm-lock.yaml - pnpm-lock.yaml
jobs: jobs:
build: lucide-angular:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -24,18 +26,5 @@ jobs:
- name: Build - name: Build
run: pnpm --filter lucide-angular build run: pnpm --filter lucide-angular build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3.8.1
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test - name: Test
run: pnpm --filter lucide-angular test run: pnpm --filter lucide-angular test

View File

@@ -13,6 +13,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18

View File

@@ -4,7 +4,6 @@ on:
pull_request: pull_request:
paths: paths:
- packages/lucide-preact/** - packages/lucide-preact/**
- packages/shared/**
- tools/build-icons/** - tools/build-icons/**
- tools/rollup-plugins/** - tools/rollup-plugins/**
- pnpm-lock.yaml - pnpm-lock.yaml
@@ -15,6 +14,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -23,5 +24,8 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile
- name: Build
run: pnpm --filter lucide-preact build
- name: Test - name: Test
run: pnpm --filter lucide-preact test run: pnpm --filter lucide-preact test

View File

@@ -4,7 +4,6 @@ on:
pull_request: pull_request:
paths: paths:
- packages/lucide-react-native/** - packages/lucide-react-native/**
- packages/shared/**
- tools/build-icons/** - tools/build-icons/**
- tools/rollup-plugins/** - tools/rollup-plugins/**
- pnpm-lock.yaml - pnpm-lock.yaml
@@ -15,6 +14,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -23,5 +24,8 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile
- name: Build
run: pnpm --filter lucide-react-native build
- name: Test - name: Test
run: pnpm --filter lucide-react-native test run: pnpm --filter lucide-react-native test

View File

@@ -4,18 +4,19 @@ on:
pull_request: pull_request:
paths: paths:
- packages/lucide-react/** - packages/lucide-react/**
- packages/shared/**
- tools/build-icons/** - tools/build-icons/**
- tools/rollup-plugins/** - tools/rollup-plugins/**
- scripts/generateNextJSAliases.mjs - scripts/generateNextJSAliases.mjs
- pnpm-lock.yaml - pnpm-lock.yaml
jobs: jobs:
build: lucide-react:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -27,18 +28,5 @@ jobs:
- name: Build - name: Build
run: pnpm --filter lucide-react build run: pnpm --filter lucide-react build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3.8.1
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test - name: Test
run: pnpm --filter lucide-react test run: pnpm --filter lucide-react test

View File

@@ -1,24 +0,0 @@
name: Lucide Shared Checks
on:
pull_request:
paths:
- packages/shared/**
- pnpm-lock.yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3.8.1
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test
run: pnpm --filter lucide-react test

View File

@@ -4,17 +4,18 @@ on:
pull_request: pull_request:
paths: paths:
- packages/lucide-solid/** - packages/lucide-solid/**
- packages/shared/**
- tools/build-icons/** - tools/build-icons/**
- tools/rollup-plugins/** - tools/rollup-plugins/**
- pnpm-lock.yaml - pnpm-lock.yaml
jobs: jobs:
build: lucide-solid:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -26,18 +27,5 @@ jobs:
- name: Build - name: Build
run: pnpm --filter lucide-solid build run: pnpm --filter lucide-solid build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3.8.1
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test - name: Test
run: pnpm --filter lucide-solid test run: pnpm --filter lucide-solid test

View File

@@ -13,6 +13,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18

View File

@@ -4,17 +4,18 @@ on:
pull_request: pull_request:
paths: paths:
- packages/lucide-svelte/** - packages/lucide-svelte/**
- packages/shared/**
- tools/build-icons/** - tools/build-icons/**
- tools/rollup-plugins/** - tools/rollup-plugins/**
- pnpm-lock.yaml - pnpm-lock.yaml
jobs: jobs:
build: lucide-svelte:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -26,18 +27,5 @@ jobs:
- name: Build - name: Build
run: pnpm --filter lucide-svelte build run: pnpm --filter lucide-svelte build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3.8.1
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test - name: Test
run: pnpm --filter lucide-svelte test run: pnpm --filter lucide-svelte test

View File

@@ -4,17 +4,18 @@ on:
pull_request: pull_request:
paths: paths:
- packages/lucide-vue-next/** - packages/lucide-vue-next/**
- packages/shared/**
- tools/build-icons/** - tools/build-icons/**
- tools/rollup-plugins/** - tools/rollup-plugins/**
- pnpm-lock.yaml - pnpm-lock.yaml
jobs: jobs:
build: lucide-vue-next:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -26,18 +27,5 @@ jobs:
- name: Build - name: Build
run: pnpm --filter lucide-vue-next build run: pnpm --filter lucide-vue-next build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3.8.1
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test - name: Test
run: pnpm --filter lucide-vue-next test run: pnpm --filter lucide-vue-next test

View File

@@ -4,17 +4,18 @@ on:
pull_request: pull_request:
paths: paths:
- packages/lucide-vue/** - packages/lucide-vue/**
- packages/shared/**
- tools/build-icons/** - tools/build-icons/**
- tools/rollup-plugins/** - tools/rollup-plugins/**
- pnpm-lock.yaml - pnpm-lock.yaml
jobs: jobs:
build: lucide-vue:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -26,18 +27,5 @@ jobs:
- name: Build - name: Build
run: pnpm --filter lucide-vue build run: pnpm --filter lucide-vue build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3.8.1
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test - name: Test
run: pnpm --filter lucide-vue test run: pnpm --filter lucide-vue test

View File

@@ -9,11 +9,13 @@ on:
- pnpm-lock.yaml - pnpm-lock.yaml
jobs: jobs:
build: lucide:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -25,18 +27,5 @@ jobs:
- name: Build - name: Build
run: pnpm --filter lucide build run: pnpm --filter lucide build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3.8.1
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test - name: Test
run: pnpm --filter lucide test run: pnpm --filter lucide test

View File

@@ -3,7 +3,7 @@ name: Add Changed Icons comment
on: on:
pull_request_target: pull_request_target:
paths: paths:
- 'icons/*' - 'icons/*.svg'
branches: branches:
- main - main
- fix-icon-preview - fix-icon-preview
@@ -68,16 +68,6 @@ jobs:
# input: +++ b/icons/accessibility.json%0A@@ -2,0 +3 @@%0A+ "contributors": ["hi"],%0A@@ -13 +14 @@%0A+}%0A # 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 # output: ::$ANNOTATION_SEVERITY file=icons/accessibility.json,line=2,endLine=3,title=$ANNOTATION_TITLE::$ANNOTATION_DESCRIPTION%0A%0A+ "contributors": ["hi"],%0A@@ -13 +14 @@%0A+}%0A
lint-aliases:
name: Check Uniqueness of Aliases
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Check Uniqueness of Aliases
run: "! cat <(printf \"%s\\n\" icons/*.json | while read -r name; do basename \"$name\" .json; done) <(jq -cr 'select(.aliases) | .aliases[] | if type==\"string\" then . else .name end' icons/*.json) | sort | uniq -c | grep -ve '^\\s*1 '"
generate-changed-icons-comment: generate-changed-icons-comment:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
@@ -104,10 +94,6 @@ jobs:
comment-author: 'github-actions[bot]' comment-author: 'github-actions[bot]'
body-includes: Added or changed icons body-includes: Added or changed icons
- uses: actions/setup-node@v4
- name: Install svgson for code preview (safer and faster than installing all deps)
run: npm install svgson
- name: Generate comment markup - name: Generate comment markup
run: node ./scripts/generateChangedIconsCommentMarkup.mjs >> comment-markup.md run: node ./scripts/generateChangedIconsCommentMarkup.mjs >> comment-markup.md
id: comment-markup id: comment-markup

View File

@@ -56,6 +56,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -87,6 +89,8 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v3
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
@@ -117,6 +121,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18

9
.gitignore vendored
View File

@@ -16,17 +16,11 @@ outlined
packages/**/src/icons/*.js packages/**/src/icons/*.js
packages/**/src/icons/*.ts packages/**/src/icons/*.ts
packages/**/src/icons/*.tsx packages/**/src/icons/*.tsx
packages/**/src/aliases/*.ts
packages/**/src/aliases.ts packages/**/src/aliases.ts
!packages/**/src/aliases/index.ts
packages/**/src/dynamicIconImports.ts packages/**/src/dynamicIconImports.ts
packages/**/DynamicIcon.d.ts
packages/**/dynamicIconImports.js packages/**/dynamicIconImports.js
packages/**/dynamicIconImports.d.ts packages/**/dynamicIconImports.d.ts
packages/**/dynamicIconImports.js.map packages/**/dynamicIconImports.js.map
packages/**/dynamic.d.ts
packages/**/dynamic.mjs.map
packages/**/dynamic.mjs
packages/**/LICENSE packages/**/LICENSE
categories.json categories.json
tags.json tags.json
@@ -40,10 +34,7 @@ docs/.vitepress/data/iconNodes
docs/.vitepress/data/iconMetaData.ts docs/.vitepress/data/iconMetaData.ts
docs/.vitepress/data/releaseMetaData.json docs/.vitepress/data/releaseMetaData.json
docs/.vitepress/data/releaseMetaData docs/.vitepress/data/releaseMetaData
docs/.vitepress/data/categoriesData.json
docs/.vitepress/data/iconDetails docs/.vitepress/data/iconDetails
docs/.vitepress/data/relatedIcons.json docs/.vitepress/data/relatedIcons.json
docs/.vercel docs/.vercel
docs/.nitro docs/.nitro
.gitignore

View File

@@ -2,10 +2,6 @@ pnpm-lock.yaml
# docs examples # docs examples
docs/**/examples/ docs/**/examples/
docs/.vitepress/.temp
docs/.vitepress/cache
docs/.vitepress/data
docs/.nitro
# lucide-angular # lucide-angular
packages/lucide-angular/.angular/cache packages/lucide-angular/.angular/cache

View File

@@ -39,8 +39,6 @@ You can also [download an Adobe Illustrator template](https://github.com/lucide-
#### [Figma Guide](https://lucide.dev/docs/figma-guide) #### [Figma Guide](https://lucide.dev/docs/figma-guide)
#### [Affinity Designer Guide](https://lucide.dev/guide/design/affinity-designer-guide)
### Submitting Multiple Icons ### Submitting Multiple Icons
If you want submit multiple icons, please separate the icons and group them. That makes reviewing the icons easier and keep the thread clean and scoped. If you want submit multiple icons, please separate the icons and group them. That makes reviewing the icons easier and keep the thread clean and scoped.
@@ -72,7 +70,7 @@ pnpm install # Install dependencies, including the workspace packages
### Packages -> PNPM Workspaces ### Packages -> PNPM Workspaces
To distribute different packages we use [PNPM workspaces](https://pnpm.io/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/en/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](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. The configured directory for workspaces is the [packages](https://github.com/lucide-icons/lucide/tree/main/packages) directory, located in the root directory. There you will find all the current packages from lucide.
There are more workspaces defined, see [`pnpm-workspace.yaml`](https://github.com/lucide-icons/lucide/blob/main/pnpm-workspace.yaml). There are more workspaces defined, see [`pnpm-workspace.yaml`](https://github.com/lucide-icons/lucide/blob/main/pnpm-workspace.yaml).
@@ -127,7 +125,7 @@ When adding new features to for example the icon component for a framework. It i
### Local Testing ### Local Testing
To test changes in a local project, you can use `yarn link`, `npm link`, `bun link` or `pnpm link` to link the package. Before you do this make sure you builded the package first. To test changes in a local project, you can use `yarn link`, `npm link` or `pnpm link` to link the package. Before you do this make sure you builded the package first.
```sh ```sh
# in packages/lucide-react # in packages/lucide-react

275
README.md
View File

@@ -1,46 +1,185 @@
<p align="center"> <p align=center><img width="480" src="https://lucide.dev/lucide-logo-repo.svg" alt="Lucide Logo"></p>
<a href="https://github.com/lucide-icons/lucide#gh-light-mode-only">
<img src="https://lucide.dev/lucide-logo-repo.svg#gh-light-mode-only" alt="Lucide - Beautiful & consistent icon toolkit made by the community. Open-source project and a fork of Feather Icons." width="480">
</a>
<a href="https://github.com/lucide-icons/lucide#gh-dark-mode-only">
<img src="https://lucide.dev/lucide-logo-repo-dark.svg#gh-dark-mode-only" alt="Lucide - Beautiful & consistent icon toolkit made by the community. Open-source project and a fork of Feather Icons." width="480">
</a>
</p>
<p align="center"> <p align="center">
<a href="https://github.com/lucide-icons/lucide/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/lucide" alt="license"></a> <a href="https://github.com/lucide-icons/lucide/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/lucide" alt="license"></a>
<a href="https://www.figma.com/community/plugin/939567362549682242/Lucide-Icons"><img src="https://img.shields.io/badge/Figma-F24E1E?logo=figma&logoColor=white" alt="figma installs"></a> <a href="https://www.npmjs.com/package/lucide"><img src="https://img.shields.io/npm/v/lucide" alt="npm package"></a>
<a href="https://www.figma.com/community/plugin/939567362549682242/Lucide-Icons"><img src="https://img.shields.io/endpoint?logo=figma&label=installs&url=https://yuanqing.github.io/figma-plugins-stats/plugin/939567362549682242/installs.json" alt="figma installs"></a>
<a href="https://github.com/lucide-icons/lucide/actions/workflows/release.yml"><img src="https://github.com/lucide-icons/lucide/actions/workflows/release.yml/badge.svg" alt="build status"></a> <a href="https://github.com/lucide-icons/lucide/actions/workflows/release.yml"><img src="https://github.com/lucide-icons/lucide/actions/workflows/release.yml/badge.svg" alt="build status"></a>
<a href="https://discord.gg/EH6nSts"><img src="https://img.shields.io/discord/723074157486800936?label=chat&logo=discord&logoColor=%23ffffff&colorB=%237289DA" alt="discord chat"></a> <a href="https://discord.gg/EH6nSts"><img src="https://img.shields.io/discord/723074157486800936?label=chat&logo=discord&logoColor=%23ffffff&colorB=%237289DA" alt="discord chat"></a>
</p> </p>
<p align="center">
<a href="https://lucide.dev/icons/">Icons</a>
·
<a href="https://lucide.dev/guide/">Guide</a>
·
<a href="https://lucide.dev/packages">Packages</a>
·
<a href="https://lucide.dev/license">License</a>
·
<a href="https://lucide.dev/showcase">Showcase</a>
</p>
# Lucide # Lucide
Lucide is an open-source icon library that provides 1000+ vector (svg) files for displaying icons and symbols in digital and non-digital projects. The library aims to make it easier for designers and developers to incorporate icons into their projects by providing several official [packages](https://lucide.dev/packages) to make it easier to use these icons in your project. Community-run fork of [Feather Icons](https://github.com/feathericons/feather), open for anyone to contribute icons.
## Packages 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.
| Logo | Package | Version | Downloads | Links | 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!
| ---- | ------- | ------- | --------- | ----- |
| <img src="https://lucide.dev/framework-logos/js.svg" alt="JS logo" width="48"> | **`lucide`** | [![npm](https://img.shields.io/npm/v/lucide)](https://www.npmjs.com/package/lucide) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide) | [Docs](https://lucide.dev/guide/packages/lucide) · [Source](./packages/lucide) | ### Why choose Lucide over Feather Icons
| <img src="https://lucide.dev/framework-logos/react.svg" alt="React logo" width="48"> | **`lucide-react`** | [![npm](https://img.shields.io/npm/v/lucide-react)](https://www.npmjs.com/package/lucide-react) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-react) | [Docs](https://lucide.dev/guide/packages/lucide-react) · [Source](./packages/lucide-react) |
| <img src="https://lucide.dev/framework-logos/vue.svg" alt="Vue logo" width="48"> | **`lucide-vue-next`** | [![npm](https://img.shields.io/npm/v/lucide-vue-next)](https://www.npmjs.com/package/lucide-vue-next) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-vue-next) | [Docs](https://lucide.dev/guide/packages/lucide-vue-next) · [Source](./packages/lucide-vue-next) | - More icons to work with: Lucide already has hundreds of icons more than Feather does.
| <img src="https://lucide.dev/framework-logos/svelte.svg" alt="Svelte logo" width="48"> | **`lucide-svelte`** | [![npm](https://img.shields.io/npm/v/lucide-svelte)](https://www.npmjs.com/package/lucide-svelte) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-svelte) | [Docs](https://lucide.dev/guide/packages/lucide-svelte) · [Source](./packages/lucide-svelte) | - Official libraries and integrations with popular frameworks and design tools.
| <img src="https://lucide.dev/framework-logos/solid.svg" alt="Solid logo" width="48"> | **`lucide-solid`** | [![npm](https://img.shields.io/npm/v/lucide-solid)](https://www.npmjs.com/package/lucide-solid) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-solid) | [Docs](https://lucide.dev/guide/packages/lucide-solid) · [Source](./packages/lucide-solid) | - Well maintained code base.
| <img src="https://lucide.dev/framework-logos/preact.svg" alt="Preact logo" width="48"> | **`lucide-preact`** | [![npm](https://img.shields.io/npm/v/lucide-preact)](https://www.npmjs.com/package/lucide-preact) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-preact) | [Docs](https://lucide.dev/guide/packages/lucide-preact) · [Source](./packages/lucide-preact) | - Active community, regularly growing and improving the set.
| <img src="https://lucide.dev/framework-logos/react-native.svg" alt="React Native logo" width="48"> | **`lucide-react-native`** | [![npm](https://img.shields.io/npm/v/lucide-react-native)](https://www.npmjs.com/package/lucide-react-native) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-react-native) | [Docs](https://lucide.dev/guide/packages/lucide-react-native) · [Source](./packages/lucide-react-native) |
| <img src="https://lucide.dev/framework-logos/angular.svg" alt="Angular logo" width="48"> | **`lucide-angular`** | [![npm](https://img.shields.io/npm/v/lucide-angular)](https://www.npmjs.com/package/lucide-angular) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-angular) | [Docs](https://lucide.dev/guide/packages/lucide-angular) · [Source](./packages/lucide-angular) | ## Table of Contents
| <img src="https://lucide.dev/framework-logos/svg.svg" alt="SVG logo" width="48"> | **`lucide-static`** | [![npm](https://img.shields.io/npm/v/lucide-static)](https://www.npmjs.com/package/lucide-static) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-static) | [Docs](https://lucide.dev/guide/packages/lucide-static) · [Source](./packages/lucide-static) |
- [Usage](#usage)
- [Web](#web)
- [React](#react)
- [React Native](#react-native)
- [Vue 2](#vue-2)
- [Vue 3](#vue-3)
- [Angular](#angular)
- [Preact](#preact)
- [Static (svg sprite, font, icons ..)](#static-svg-sprite-font-icons-)
- [Figma](#figma)
- [Laravel](#laravel)
- [Svelte](#svelte)
- [Solid](#solid)
- [Hyva](#hyva)
- [Eleventy](#eleventy)
- [Contributing](#contributing)
- [Community](#community)
- [License](#license)
- [Credits](#credits)
- [Sponsors](#sponsors)
## Usage
At its core, Lucide is a collection of [SVG](https://svgontheweb.com/#svg) files. This means that you can use Lucide icons in all the same ways you can use SVGs (e.g. `img`, `background-image`, `inline`, `object`, `embed`, `iframe`). Here's a helpful article detailing the many ways SVGs can be used on the web: [SVG on the Web Implementation Options](https://svgontheweb.com/#implementation)
The following are additional ways you can use Lucide.
With the Javascript library you can easily incorporate the icon you want in your webpage.
### Web
Implementation of the lucide icon library for web applications.
```sh
npm install lucide
```
or
```sh
yarn add lucide
```
For more details, see the [documentation](https://github.com/lucide-icons/lucide/tree/main/packages/lucide#lucide).
### React
Implementation of the lucide icon library for react applications.
```sh
yarn add lucide-react
```
or
```sh
npm install lucide-react
```
For more details, see the [documentation](https://github.com/lucide-icons/lucide/tree/main/packages/lucide-react#lucide-react).
### React Native
Implementation of the lucide icon library for React Native applications.
```sh
yarn add lucide-react-native
```
or
```sh
npm install lucide-react-native
```
For more details, see the [documentation](https://github.com/lucide-icons/lucide/tree/main/packages/lucide-react-native#lucide-react-native).
### Vue 2
Implementation of the lucide icon library for vue applications.
```sh
yarn add lucide-vue
```
or
```sh
npm install lucide-vue
```
For more details, see the [documentation](https://github.com/lucide-icons/lucide/tree/main/packages/lucide-vue#lucide-vue).
### Vue 3
Implementation of the lucide icon library for vue applications.
```sh
yarn add lucide-vue-next
```
or
```sh
npm install lucide-vue-next
```
For more details, see the [documentation](https://github.com/lucide-icons/lucide/tree/main/packages/lucide-vue-next#lucide-vue-next).
### Angular
```sh
yarn add lucide-angular
```
or
```sh
npm install lucide-angular
```
For more details, see the [documentation](https://github.com/lucide-icons/lucide/tree/main/packages/lucide-angular#lucide-angular).
### Preact
Implementation of the lucide icon library for preact applications.
```sh
yarn add lucide-preact
```
or
```sh
npm install lucide-preact
```
For more details, see the [documentation](https://github.com/lucide-icons/lucide/tree/main/packages/lucide-preact#lucide-preact).
### Static (svg sprite, font, icons ..)
Assets:
[Font Files](https://github.com/lucide-icons/lucide/releases/latest)
[SVG Files](https://github.com/lucide-icons/lucide/releases/latest)
[SVG Sprite](https://cdn.jsdelivr.net/npm/lucide-static@latest/sprite.svg)
NPM package
```sh
yarn add lucide-static
```
or
```sh
npm install lucide-static
```
### Figma ### Figma
@@ -50,6 +189,68 @@ Visit [Figma community page](https://www.figma.com/community/plugin/939567362549
<img width="420" src="https://www.figma.com/community/plugin/939567362549682242/thumbnail" alt="Figma Lucide Cover"> <img width="420" src="https://www.figma.com/community/plugin/939567362549682242/thumbnail" alt="Figma Lucide Cover">
### Laravel
Implementation of Lucide icon's using `blade-icons` for Laravel based projects.
```sh
composer require mallardduck/blade-lucide-icons
```
For more details, see the [documentation](https://github.com/mallardduck/blade-lucide-icons/blob/main/README.md).
### Svelte
Implementation of the lucide icon library for Svelte applications.
```sh
yarn add lucide-svelte
```
or
```sh
npm install lucide-svelte
```
For more details, see the [documentation](https://github.com/lucide-icons/lucide/tree/main/packages/lucide-svelte#lucide-svelte).
### Solid
Implementation of the lucide icon library for solid applications.
```sh
yarn add lucide-solid
```
or
```sh
npm install lucide-solid
```
For more details, see the [documentation](https://github.com/lucide-icons/lucide/tree/main/packages/lucide-solid#lucide-solid).
### Hyva
Implementation of Lucide icon's using Hyvä's svg php viewmodal to render icons for Magento 2 Hyva theme based projects.
```sh
composer require siteation/magento2-hyva-icons-lucide
```
For more details, see the [documentation](https://github.com/Siteation/magento2-hyva-icons-lucide/blob/main/README.md).
### Eleventy
Using this plugin, Eleventy projects can incorporate Lucide icons. it makes it simple to use Lucide icons into your themes via shortcodes, improving your website's overall usability and visual appeal.
```sh
npm install @grimlink/eleventy-plugin-lucide-icons
```
For more details, see the [documentation](https://github.com/GrimLink/eleventy-plugin-lucide-icons/blob/main/README.md).
## Contributing ## Contributing
For more info on how to contribute please see the [contribution guidelines](https://github.com/lucide-icons/lucide/blob/main/CONTRIBUTING.md). For more info on how to contribute please see the [contribution guidelines](https://github.com/lucide-icons/lucide/blob/main/CONTRIBUTING.md).
@@ -62,7 +263,7 @@ Join the community on our [Discord](https://discord.gg/EH6nSts) server!
## License ## License
Lucide is totally free for commercial use and personal use, this software is licensed under the [ISC License](https://github.com/lucide-icons/lucide/blob/main/LICENSE). Lucide is totally free for commercial use and personally use, this software is licensed under the [ISC License](https://github.com/lucide-icons/lucide/blob/main/LICENSE).
## Credits ## Credits
@@ -73,13 +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> <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>
### Awesome backers 🍺
<a href="https://www.scipress.io?utm_source=lucide"><img src="docs/public/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
<a href="https://github.com/pdfme/pdfme"><img src="docs/public/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "../category.schema.json", "$schema": "../category.schema.json",
"title": "Charts", "title": "Charts",
"icon": "chart-pie" "icon": "pie-chart"
} }

5
categories/currency.json Normal file
View File

@@ -0,0 +1,5 @@
{
"$schema": "../category.schema.json",
"title": "Currency",
"icon": "dollar-sign"
}

View File

@@ -0,0 +1,5 @@
{
"$schema": "../category.schema.json",
"title": "Furniture",
"icon": "rocking-chair"
}

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "../category.schema.json", "$schema": "../category.schema.json",
"title": "Home", "title": "Home",
"icon": "house" "icon": "home"
} }

5
categories/maps.json Normal file
View File

@@ -0,0 +1,5 @@
{
"$schema": "../category.schema.json",
"title": "Maps",
"icon": "map"
}

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "../category.schema.json", "$schema": "../category.schema.json",
"title": "Mathematics", "title": "Maths",
"icon": "divide" "icon": "divide"
} }

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "../category.schema.json", "$schema": "../category.schema.json",
"title": "Finance", "title": "Money",
"icon": "piggy-bank" "icon": "piggy-bank"
} }

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "../category.schema.json", "$schema": "../category.schema.json",
"title": "Notification", "title": "Notifications",
"icon": "triangle-alert" "icon": "triangle-alert"
} }

View File

@@ -1,3 +1,4 @@
import { eventHandler, setResponseHeader } from 'h3';
import iconMetaData from '../../data/iconMetaData'; import iconMetaData from '../../data/iconMetaData';
export default eventHandler((event) => { export default eventHandler((event) => {

View File

@@ -1,40 +0,0 @@
import iconNodes from '../../data/iconNodes/index.ts';
import { IconNodeWithKeys } from '../../theme/types';
import iconMetaData from '../../data/iconMetaData';
import releaseMeta from '../../data/releaseMetaData.json';
import categories from '../../data/categoriesData.json';
const dataResponse = {
icons: Object.entries(iconNodes).reduce((acc, [name, iconNode]) => {
const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => {
return [name, attrs];
});
acc[name] = {
iconNode: newIconNode,
aliases: (iconMetaData[name]?.aliases ?? []).map((alias) =>
typeof alias === 'string' ? alias : alias.name,
),
tags: iconMetaData[name].tags ?? [],
categories: iconMetaData[name].categories ?? [],
...releaseMeta[name],
};
return acc;
}, {}),
aliases: Object.entries(iconNodes).reduce((acc, [name]) => {
for (const alias of iconMetaData[name]?.aliases ?? []) {
acc[typeof alias === 'string' ? alias : alias.name] = name;
}
return acc;
}, {}),
categories,
};
export default eventHandler((event) => {
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400');
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
return dataResponse;
});

View File

@@ -13,10 +13,7 @@ export default eventHandler((event) => {
const data = pathData.at(-1).slice(0, -4); const data = pathData.at(-1).slice(0, -4);
const [name] = pathData; const [name] = pathData;
const src = Buffer.from(data, 'base64') const src = Buffer.from(data, 'base64').toString('utf8');
.toString('utf8')
.replaceAll('\n', '')
.replace(/<svg[^>]*>|<\/svg>/g, '');
const children = []; const children = [];
@@ -28,18 +25,18 @@ export default eventHandler((event) => {
.map((_, idx, arr) => arr.slice(0, idx + 1).join('-')) .map((_, idx, arr) => arr.slice(0, idx + 1).join('-'))
.reverse() .reverse()
.find((groupName) => groupName in iconNodes); .find((groupName) => groupName in iconNodes);
if (!(name in iconNodes) && backdropName) { if (backdropName) {
const iconNode = iconNodes[backdropName]; const iconNode = iconNodes[backdropName];
const LucideIcon = createLucideIcon(backdropName, iconNode); const LucideIcon = createLucideIcon(backdropName, iconNode);
const svg = renderToStaticMarkup(createElement(LucideIcon)); const svg = renderToStaticMarkup(createElement(LucideIcon));
const backdropString = svg.replaceAll('\n', '').replace(/<svg[^>]*>|<\/svg>/g, ''); const backdropString = svg.replace(/<svg[^>]*>|<\/svg>/g, '');
children.push( children.push(
createElement(Backdrop, { createElement(Backdrop, {
backdropString, backdropString,
src, src,
color: '#777', color: name in iconNodes ? 'red' : '#777',
}), }),
); );
} }

View File

@@ -1,37 +0,0 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import { createElement } from 'react';
import Diff from '../../../lib/SvgPreview/Diff.tsx';
import iconNodes from '../../../data/iconNodes';
import createLucideIcon from 'lucide-react/src/createLucideIcon';
export default eventHandler((event) => {
const { params } = event.context;
const pathData = params.data.split('/');
const data = pathData.at(-1).slice(0, -4);
const [name] = pathData;
const newSrc = Buffer.from(data, 'base64')
.toString('utf8')
.replaceAll('\n', '')
.replace(/<svg[^>]*>|<\/svg>/g, '');
const children = [];
const oldSrc = iconNodes[name]
? renderToStaticMarkup(createElement(createLucideIcon(name, iconNodes[name])))
.replaceAll('\n', '')
.replace(/<svg[^>]*>|<\/svg>/g, '')
: '';
const svg = Buffer.from(
// We can't use jsx here, is not supported here by nitro.
renderToString(createElement(Diff, { oldSrc, newSrc, showGrid: true }, children)),
).toString('utf8');
defaultContentType(event, 'image/svg+xml');
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
return svg;
});

View File

@@ -1,3 +1,4 @@
import { eventHandler, setResponseHeader } from 'h3';
import iconMetaData from '../../data/iconMetaData'; import iconMetaData from '../../data/iconMetaData';
export default eventHandler((event) => { export default eventHandler((event) => {

View File

@@ -28,10 +28,6 @@ export default defineConfig({
new URL('./theme/components/overrides/VPFooter.vue', import.meta.url), new URL('./theme/components/overrides/VPFooter.vue', import.meta.url),
), ),
}, },
{
find: '~/.vitepress',
replacement: fileURLToPath(new URL('./', import.meta.url)),
},
], ],
}, },
}, },

View File

@@ -1,174 +0,0 @@
[
{
"name": "accessibility",
"title": "Accessibility"
},
{
"name": "account",
"title": "Accounts & access"
},
{
"name": "animals",
"title": "Animals"
},
{
"name": "arrows",
"title": "Arrows"
},
{
"name": "brands",
"title": "Brands"
},
{
"name": "buildings",
"title": "Buildings"
},
{
"name": "charts",
"title": "Charts"
},
{
"name": "communication",
"title": "Communication"
},
{
"name": "connectivity",
"title": "Connectivity"
},
{
"name": "cursors",
"title": "Cursors"
},
{
"name": "design",
"title": "Design"
},
{
"name": "development",
"title": "Coding & development"
},
{
"name": "devices",
"title": "Devices"
},
{
"name": "emoji",
"title": "Emoji"
},
{
"name": "files",
"title": "File icons"
},
{
"name": "finance",
"title": "Finance"
},
{
"name": "food-beverage",
"title": "Food & beverage"
},
{
"name": "gaming",
"title": "Gaming"
},
{
"name": "home",
"title": "Home"
},
{
"name": "layout",
"title": "Layout"
},
{
"name": "mail",
"title": "Mail"
},
{
"name": "math",
"title": "Mathematics"
},
{
"name": "medical",
"title": "Medical"
},
{
"name": "multimedia",
"title": "Multimedia"
},
{
"name": "nature",
"title": "Nature"
},
{
"name": "navigation",
"title": "Navigation"
},
{
"name": "notifications",
"title": "Notification"
},
{
"name": "people",
"title": "People"
},
{
"name": "photography",
"title": "Photography"
},
{
"name": "science",
"title": "Science"
},
{
"name": "seasons",
"title": "Seasons"
},
{
"name": "security",
"title": "Security"
},
{
"name": "shapes",
"title": "Shapes"
},
{
"name": "shopping",
"title": "Shopping"
},
{
"name": "social",
"title": "Social"
},
{
"name": "sports",
"title": "Sports"
},
{
"name": "sustainability",
"title": "Sustainability"
},
{
"name": "text",
"title": "Text formatting"
},
{
"name": "time",
"title": "Time & calendar"
},
{
"name": "tools",
"title": "Tools"
},
{
"name": "transportation",
"title": "Transportation"
},
{
"name": "travel",
"title": "Travel"
},
{
"name": "weather",
"title": "Weather"
}
]

View File

@@ -14,13 +14,5 @@
"light": "/library-logos/tamagui.svg", "light": "/library-logos/tamagui.svg",
"dark": "/library-logos/tamagui.svg" "dark": "/library-logos/tamagui.svg"
} }
},
{
"name": "Reflex",
"url": "https://reflex.dev",
"image": {
"light": "/library-logos/reflex-light.svg",
"dark": "/library-logos/reflex-dark.svg"
}
} }
] ]

View File

@@ -31,8 +31,24 @@
} }
] ]
}, },
"lucide-vue-next": { "lucide-vue": {
"order": 2, "order": 2,
"icon": "vue",
"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"
}
]
},
"lucide-vue-next": {
"order": 3,
"icon": "vue-next", "icon": "vue-next",
"shields": [ "shields": [
{ {
@@ -48,7 +64,7 @@
] ]
}, },
"lucide-svelte": { "lucide-svelte": {
"order": 3, "order": 4,
"icon": "svelte", "icon": "svelte",
"shields": [ "shields": [
{ {
@@ -79,8 +95,24 @@
} }
] ]
}, },
"lucide-react-native": { "lucide-preact": {
"order": 5, "order": 5,
"icon": "preact",
"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"
}
]
},
"lucide-react-native": {
"order": 6,
"icon": "react-native", "icon": "react-native",
"shields": [ "shields": [
{ {
@@ -96,7 +128,7 @@
] ]
}, },
"lucide-angular": { "lucide-angular": {
"order": 6, "order": 7,
"icon": "angular", "icon": "angular",
"shields": [ "shields": [
{ {
@@ -111,22 +143,6 @@
} }
] ]
}, },
"lucide-preact": {
"order": 7,
"icon": "preact",
"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"
}
]
},
"lucide-static": { "lucide-static": {
"order": 8, "order": 8,
"icon": "svg", "icon": "svg",
@@ -142,5 +158,16 @@
"href": "https://www.npmjs.com/package/lucide-static" "href": "https://www.npmjs.com/package/lucide-static"
} }
] ]
},
"lucide-flutter": {
"order": 9,
"icon": "flutter",
"shields": [
{
"alt": "flutter",
"src": "https://img.shields.io/pub/v/lucide_icons",
"href": "https://img.shields.io/pub/v/lucide_icons"
}
]
} }
} }

View File

@@ -76,24 +76,5 @@
], ],
"source": "https://github.com/swisnl/nuxt-lucide-icons", "source": "https://github.com/swisnl/nuxt-lucide-icons",
"documentation": "https://github.com/swisnl/nuxt-lucide-icons/blob/main/README.md" "documentation": "https://github.com/swisnl/nuxt-lucide-icons/blob/main/README.md"
},
{
"name": "lucide-lustre",
"description": "A library providing https://lucide.dev icons to lustre",
"icon": "/framework-logos/lustre.webp",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/hexpm/v/lucide_lustre",
"href": "https://hex.pm/packages/lucide_lustre"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/hexpm/dw/lucide_lustre",
"href": "https://hex.pm/packages/lucide_lustre"
}
],
"source": "https://github.com/dinkelspiel/lucide_lustre",
"documentation": "https://github.com/dinkelspiel/lucide_lustre/blob/master/README.md"
} }
] ]

View File

@@ -1,48 +0,0 @@
[
{
"name": "Eric Fennis",
"title": "Creator of Lucide & Software engineer @nedap",
"image": "https://github.com/ericfennis.png?size=192",
"sponsor": "https://github.com/sponsors/ericfennis",
"socialLinks": [
{
"icon": "github",
"link": "https://github.com/ericfennis"
},
{
"icon": "x",
"link": "https://x.com/ericfennis"
}
]
},
{
"name": "Karsa Rigó",
"title": "Maintainer of Lucide & Software engineer @sztaki",
"image": "https://github.com/karsa-mistmere.png?size=192",
"socialLinks": [
{
"icon": "github",
"link": "https://github.com/karsa-mistmere"
},
{
"icon": "linkedin",
"link": "https://www.linkedin.com/in/karsamistmere"
}
]
},
{
"name": "Jakob Guddas",
"title": "Maintainer of Lucide & Software engineer @LEGO",
"image": "https://github.com/jguddas.png?size=192",
"socialLinks": [
{
"icon": "github",
"link": "https://github.com/jguddas"
},
{
"icon": "linkedin",
"link": "https://www.linkedin.com/in/jguddas"
}
]
}
]

View File

@@ -3,22 +3,15 @@ import React from 'react';
interface BackdropProps { interface BackdropProps {
src: string; src: string;
color?: string; color?: string;
outline?: boolean;
backdropString: string; backdropString: string;
} }
const Backdrop = ({ const Backdrop = ({ src, color = 'red', backdropString }: BackdropProps): JSX.Element => {
src,
color = 'red',
outline = true,
backdropString,
}: BackdropProps): JSX.Element => {
const id = React.useId();
return ( return (
<> <>
<defs xmlns="http://www.w3.org/2000/svg"> <defs xmlns="http://www.w3.org/2000/svg">
<pattern <pattern
id={`pattern-${id}`} id="pattern"
width=".1" width=".1"
height=".1" height=".1"
patternUnits="userSpaceOnUse" patternUnits="userSpaceOnUse"
@@ -37,58 +30,69 @@ const Backdrop = ({
</pattern> </pattern>
</defs> </defs>
<mask <mask
id={`svg-preview-backdrop-mask-${id}`} id="svg-preview-backdrop-mask-outline"
maskUnits="userSpaceOnUse" maskUnits="userSpaceOnUse"
> >
<g <g
stroke="#fff" stroke="#fff"
dangerouslySetInnerHTML={{ __html: backdropString }} dangerouslySetInnerHTML={{ __html: backdropString }}
/> />
<g dangerouslySetInnerHTML={{ __html: src }} /> <g
dangerouslySetInnerHTML={{ __html: src }}
strokeWidth={2.05}
/>
</mask> </mask>
<mask <mask
id={`svg-preview-backdrop-mask-outline-${id}`} id="svg-preview-backdrop-mask-fill"
maskUnits="userSpaceOnUse" maskUnits="userSpaceOnUse"
> >
<rect <g
x="0" stroke="#fff"
y="0" dangerouslySetInnerHTML={{ __html: backdropString }}
width="24" />
height="24" <g
fill="#fff" dangerouslySetInnerHTML={{ __html: src }}
stroke="none" strokeWidth={2.05}
/> />
<g <g
strokeWidth={1.75} strokeWidth={1.75}
dangerouslySetInnerHTML={{ __html: backdropString }} dangerouslySetInnerHTML={{ __html: backdropString }}
/> />
</mask> </mask>
<g mask={`url(#svg-preview-backdrop-mask-${id})`}> <g
strokeWidth={2.25}
stroke="url(#pattern)"
mask={'url(#svg-preview-backdrop-mask-outline)'}
>
<rect <rect
x="0" x="0"
y="0" y="0"
width="24" width="24"
height="24" height="24"
fill="url(#pattern)"
opacity={0.5} opacity={0.5}
fill={`url(#pattern-${id})`}
stroke="none" stroke="none"
/> />
<g
stroke={color}
strokeWidth={2.25}
opacity={0.75}
dangerouslySetInnerHTML={{ __html: src }}
/>
{outline && (
<g
stroke={color}
strokeWidth={2.25}
opacity={0.75}
mask={`url(#svg-preview-backdrop-mask-outline-${id})`}
dangerouslySetInnerHTML={{ __html: backdropString }}
/>
)}
</g> </g>
<rect
x="0"
y="0"
width="24"
height="24"
fill="url(#pattern)"
stroke="none"
mask={'url(#svg-preview-backdrop-mask-fill)'}
/>
<rect
x="0"
y="0"
width="24"
height="24"
fill={color}
opacity={0.5}
stroke="none"
mask={'url(#svg-preview-backdrop-mask-fill)'}
/>
</> </>
); );
}; };

View File

@@ -1,71 +0,0 @@
import React from 'react';
import Backdrop from './Backdrop.tsx';
import { darkModeCss, Grid } from './index.tsx';
const SvgPreview = React.forwardRef<
SVGSVGElement,
{
oldSrc: string;
newSrc: string;
} & React.SVGProps<SVGSVGElement>
>(({ oldSrc, newSrc, children, ...props }, ref) => {
return (
<svg
ref={ref}
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<style>{darkModeCss}</style>
<Grid
strokeWidth={0.1}
stroke="#777"
strokeOpacity={0.3}
radius={1}
/>
<mask
id="gray"
maskUnits="userSpaceOnUse"
>
<rect
x="0"
y="0"
width="24"
height="24"
fill="#000"
stroke="none"
/>
<g
stroke="#fff"
dangerouslySetInnerHTML={{ __html: oldSrc }}
/>
</mask>
<Backdrop
src=""
outline={false}
backdropString={`<g mask="url('#gray')">${newSrc}</g>`}
color="#777"
/>
<Backdrop
src={oldSrc}
backdropString={newSrc}
color="lime"
/>
<Backdrop
src={newSrc}
backdropString={oldSrc}
color="red"
/>
{children}
</svg>
);
});
export default SvgPreview;

View File

@@ -2,23 +2,7 @@ import React from 'react';
import { PathProps, Path } from './types'; import { PathProps, Path } from './types';
import { getPaths, assert } from './utils'; import { getPaths, assert } from './utils';
export const darkModeCss = ` const Grid = ({
@media screen and (prefers-color-scheme: light) {
.svg-preview-grid-rect { fill: none }
}
@media screen and (prefers-color-scheme: dark) {
.svg-preview-grid-rect { fill: none }
.svg
.svg-preview-grid-group,
.svg-preview-radii-group,
.svg-preview-shadow-mask-group,
.svg-preview-shadow-group {
stroke: #fff;
}
}
`;
export const Grid = ({
radius, radius,
fill = '#fff', fill = '#fff',
...props ...props
@@ -317,6 +301,7 @@ const Handles = ({
'strokeWidth' | 'stroke' | 'strokeDasharray' | 'strokeOpacity', 'strokeWidth' | 'stroke' | 'strokeDasharray' | 'strokeOpacity',
any any
>) => { >) => {
console.log(paths);
return ( return (
<g <g
className="svg-preview-handles-group" className="svg-preview-handles-group"
@@ -355,6 +340,19 @@ const SvgPreview = React.forwardRef<
>(({ src, children, showGrid = false, ...props }, ref) => { >(({ src, children, showGrid = false, ...props }, ref) => {
const paths = typeof src === 'string' ? getPaths(src) : src; const paths = typeof src === 'string' ? getPaths(src) : src;
const darkModeCss = `@media screen and (prefers-color-scheme: light) {
.svg-preview-grid-rect { fill: none }
}
@media screen and (prefers-color-scheme: dark) {
.svg-preview-grid-rect { fill: none }
.svg
.svg-preview-grid-group,
.svg-preview-radii-group,
.svg-preview-shadow-mask-group,
.svg-preview-shadow-group {
stroke: #fff;
}
}`;
return ( return (
<svg <svg
ref={ref} ref={ref}

View File

@@ -1,161 +0,0 @@
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
import { getHighlighter } from 'shikiji';
type CodeExampleType = {
title: string;
language: string;
code: string;
}[];
const getIconCodes = (): CodeExampleType => {
return [
{
language: 'js',
title: 'Vanilla',
code: `\
import { createIcons, icons } from 'lucide';
import { $CamelCase } from '@lucide/lab';
createIcons({
icons: {
$CamelCase
}
});
document.body.append('<i data-lucide="$Name"></i>');\
`,
},
{
language: 'tsx',
title: 'React',
code: `import { Icon } from 'lucide-react';
import { $CamelCase } from '@lucide/lab';
const App = () => {
return (
<Icon iconNode={$CamelCase} />
);
};
export default App;
`,
},
{
language: 'vue',
title: 'Vue',
code: `<script setup>
import { Icon } from 'lucide-vue-next';
import { $CamelCase } from '@lucide/lab';
</script>
<template>
<Icon :iconNode="$CamelCase" />
</template>
`,
},
{
language: 'svelte',
title: 'Svelte',
code: `<script>
import { Icon } from 'lucide-svelte';
import { $CamelCase } from '@lucide/lab';
</script>
<Icon iconNode={$CamelCase} />
`,
},
{
language: 'tsx',
title: 'Preact',
code: `import { Icon } from 'lucide-preact';
import { $CamelCase } from '@lucide/lab';
const App = () => {
return (
<Icon iconNode={$CamelCase} />
);
};
export default App;
`,
},
{
language: 'tsx',
title: 'Solid',
code: `import { Icon } from 'lucide-solid';
import { $CamelCase } from '@lucide/lab';
const App = () => {
return (
<Icon iconNode={$CamelCase} />
);
};
export default App;
`,
},
{
language: 'tsx',
title: 'Angular',
code: `// app.module.ts
import { LucideAngularModule } from 'lucide-angular';
import { $CamelCase } from '@lucide/lab';
@NgModule({
imports: [
LucideAngularModule.pick({ $CamelCase })
],
})
// app.component.html
<lucide-icon name="$CamelCase"></lucide-icon>
`,
},
];
};
export type ThemeOptions =
| ThemeRegistration
| { light: ThemeRegistration; dark: ThemeRegistration };
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});
const highlightedCode = highlighter
.codeToHtml(code, {
lang,
themes: {
light: 'github-light',
dark: 'github-dark',
},
defaultColor: false,
})
.replace('shiki-themes', 'shiki-themes vp-code');
return `<div class="language-${lang} ${active ? 'active' : ''}">
<button title="Copy Code" class="copy"></button>
<span class="lang">${lang}</span>
${highlightedCode}
</div>`;
};
export default async function createCodeExamples() {
const codes = getIconCodes();
const codeExamplePromises = codes.map(async ({ title, language, code }, index) => {
const isFirst = index === 0;
const codeString = await highLightCode(code, language, isFirst);
return {
title,
language: language,
code: codeString,
};
});
return Promise.all(codeExamplePromises);
}

View File

@@ -1,32 +0,0 @@
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
import { getHighlighter } from 'shikiji';
export type ThemeOptions =
| ThemeRegistration
| { light: ThemeRegistration; dark: ThemeRegistration };
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});
const highlightedCode = highlighter
.codeToHtml(code, {
lang,
themes: {
light: 'github-light',
dark: 'github-dark',
},
defaultColor: false,
})
.replace('shiki-themes', 'shiki-themes vp-code');
return `<div class="language-${lang} ${active ? 'active' : ''}">
<button title="Copy Code" class="copy"></button>
<span class="lang">${lang}</span>
${highlightedCode}
</div>`;
};
export default highLightCode;

View File

@@ -1,5 +0,0 @@
export type CodeExampleType = {
title: string;
language: string;
code: string;
}[];

View File

@@ -10,24 +10,18 @@ type CodeExampleType = {
const getIconCodes = (): CodeExampleType => { const getIconCodes = (): CodeExampleType => {
return [ return [
{ {
language: 'js', language: 'html',
title: 'Vanilla', title: 'HTML',
code: `\ code: `<i data-lucide="Name"></i>`,
import { createIcons, icons } from 'lucide';
createIcons({ icons });
document.body.append('<i data-lucide="$Name"></i>');\
`,
}, },
{ {
language: 'tsx', language: 'tsx',
title: 'React', title: 'React',
code: `import { $PascalCase } from 'lucide-react'; code: `import { PascalCase } from 'lucide-react';
const App = () => { const App = () => {
return ( return (
<$PascalCase /> <PascalCase />
); );
}; };
@@ -38,11 +32,11 @@ export default App;
language: 'vue', language: 'vue',
title: 'Vue', title: 'Vue',
code: `<script setup> code: `<script setup>
import { $PascalCase } from 'lucide-vue-next'; import { PascalCase } from 'lucide-vue-next';
</script> </script>
<template> <template>
<$PascalCase /> <PascalCase />
</template> </template>
`, `,
}, },
@@ -50,20 +44,20 @@ import { $PascalCase } from 'lucide-vue-next';
language: 'svelte', language: 'svelte',
title: 'Svelte', title: 'Svelte',
code: `<script> code: `<script>
import { $PascalCase } from 'lucide-svelte'; import { PascalCase } from 'lucide-svelte';
</script> </script>
<$PascalCase /> <PascalCase />
`, `,
}, },
{ {
language: 'tsx', language: 'tsx',
title: 'Preact', title: 'Preact',
code: `import { $PascalCase } from 'lucide-preact'; code: `import { PascalCase } from 'lucide-preact';
const App = () => { const App = () => {
return ( return (
<$PascalCase /> <PascalCase />
); );
}; };
@@ -73,11 +67,11 @@ export default App;
{ {
language: 'tsx', language: 'tsx',
title: 'Solid', title: 'Solid',
code: `import { $PascalCase } from 'lucide-solid'; code: `import { PascalCase } from 'lucide-solid';
const App = () => { const App = () => {
return ( return (
<$PascalCase /> <PascalCase />
); );
}; };
@@ -88,16 +82,16 @@ export default App;
language: 'tsx', language: 'tsx',
title: 'Angular', title: 'Angular',
code: `// app.module.ts code: `// app.module.ts
import { LucideAngularModule, $PascalCase } from 'lucide-angular'; import { LucideAngularModule, PascalCase } from 'lucide-angular';
@NgModule({ @NgModule({
imports: [ imports: [
LucideAngularModule.pick({ $PascalCase }) LucideAngularModule.pick({ PascalCase })
], ],
}) })
// app.component.html // app.component.html
<lucide-icon name="$Name"></lucide-icon> <lucide-icon name="Name"></lucide-icon>
`, `,
}, },
{ {
@@ -107,7 +101,7 @@ import { LucideAngularModule, $PascalCase } from 'lucide-angular';
@import ('~lucide-static/font/Lucide.css'); @import ('~lucide-static/font/Lucide.css');
</style> </style>
<div class="icon-$Name"></div> <div class="icon-Name"></div>
`, `,
}, },
]; ];

View File

@@ -1,5 +1,6 @@
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 { PackageItem } from '../theme/types'; import { PackageItem } from '../theme/types';
const fileExist = (filePath) => const fileExist = (filePath) =>
@@ -26,6 +27,11 @@ const fetchPackages = async (): Promise<PackageItem[]> => {
return JSON.parse(await fs.readFile(jsonFilePath, 'utf-8')); return JSON.parse(await fs.readFile(jsonFilePath, 'utf-8'));
} }
const ymlFilePath = path.resolve(filePath, 'pubspec.yaml');
if (await fileExist(ymlFilePath)) {
return yaml.load(await fs.readFile(ymlFilePath, 'utf-8'));
}
return null; return null;
}), }),
); );

View File

@@ -31,10 +31,10 @@ const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
{ {
text: 'Advanced', text: 'Advanced',
items: [ items: [
{ // {
text: 'Accessibility', // text: 'Accessibility',
link: '/guide/advanced/accessibility', // link: '/guide/advanced/accessibility'
}, // },
{ {
text: 'Global styling', text: 'Global styling',
link: '/guide/advanced/global-styling', link: '/guide/advanced/global-styling',
@@ -46,10 +46,6 @@ const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
text: 'Filled icons', text: 'Filled icons',
link: '/guide/advanced/filled-icons', link: '/guide/advanced/filled-icons',
}, },
{
text: 'Aliased Names',
link: '/guide/advanced/aliased-names',
},
// { // {
// text: 'Combining icons', // text: 'Combining icons',
// }, // },
@@ -72,6 +68,10 @@ const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
text: 'Lucide React', text: 'Lucide React',
link: '/guide/packages/lucide-react', link: '/guide/packages/lucide-react',
}, },
{
text: 'Lucide React Native',
link: '/guide/packages/lucide-react-native',
},
{ {
text: 'Lucide Vue', text: 'Lucide Vue',
link: '/guide/packages/lucide-vue-next', link: '/guide/packages/lucide-vue-next',
@@ -85,17 +85,13 @@ const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
link: '/guide/packages/lucide-solid', link: '/guide/packages/lucide-solid',
}, },
{ {
text: 'Lucide React Native', text: 'Lucide Preact',
link: '/guide/packages/lucide-react-native', link: '/guide/packages/lucide-preact',
}, },
{ {
text: 'Lucide Angular', text: 'Lucide Angular',
link: '/guide/packages/lucide-angular', link: '/guide/packages/lucide-angular',
}, },
{
text: 'Lucide Preact',
link: '/guide/packages/lucide-preact',
},
{ {
text: 'Lucide Static', text: 'Lucide Static',
link: '/guide/packages/lucide-static', link: '/guide/packages/lucide-static',
@@ -121,10 +117,6 @@ const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
text: 'Designing in Figma', text: 'Designing in Figma',
link: '/guide/design/figma-guide', link: '/guide/design/figma-guide',
}, },
{
text: 'Designing in Affinity Designer',
link: '/guide/design/affinity-designer-guide',
},
], ],
}, },
], ],

View File

@@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
import { useRouter } from 'vitepress'; import { useRouter } from 'vitepress';
const { go } = useRouter() const { go } = useRouter()
@@ -7,16 +8,7 @@ const props = defineProps<{
href?: string href?: string
}>() }>()
const isExternal = computed(() => props.href?.startsWith('http') ?? false)
const component = computed(() => props.href ? 'a' : 'div') const component = computed(() => props.href ? 'a' : 'div')
const target = computed(() => isExternal.value ? '_blank' : undefined)
const rel = computed(() => isExternal.value ? 'noreferrer noopener' : undefined)
const onClick = computed(() => {
if(!props.href || isExternal) return
return go(props.href)
})
</script> </script>
<template> <template>
@@ -24,9 +16,7 @@ const onClick = computed(() => {
:is="component" :is="component"
:href="href" :href="href"
class="badge" class="badge"
:target="target" @click="props?.href ? go(href) : undefined"
:rel="rel"
@click="onClick"
> >
<slot/> <slot/>
</component> </component>

View File

@@ -1,11 +1,11 @@
<template> <template>
<div class="card-grid-flex"> <div class="grid">
<slot /> <slot />
</div> </div>
</template> </template>
<style> <style scoped>
.card-grid-flex { .grid {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-items: stretch; align-items: stretch;
@@ -15,20 +15,20 @@
margin: -8px; margin: -8px;
} }
.card-grid-flex > * { .grid > * {
flex-basis: 100%; flex-basis: 100%;
box-sizing: border-box; box-sizing: border-box;
padding: 8px; padding: 8px;
} }
@media (min-width: 960px) { @media (min-width: 960px) {
.card-grid-flex > * { .grid > * {
flex-basis: 50%; flex-basis: 50%;
} }
} }
@media (min-width: 1280px) { @media (min-width: 1280px) {
.card-grid-flex > * { .grid > * {
flex-basis: 33.33%; flex-basis: 33.33%;
} }
} }

View File

@@ -1,90 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps<{
label: string
id: string
value: string
modelValue: string | string[]
}>()
const emit = defineEmits(['change', 'input', 'update:modelValue'])
const model = computed({
get: () => {
if (Array.isArray(props.modelValue)) {
return props.modelValue.includes(props.value)
}
return props.modelValue === props.value
},
set: (value: string) => {
if (Array.isArray(props.modelValue)) {
const newValue = [...props.modelValue]
const index = newValue.indexOf(props.value)
if (value) {
if (index === -1) {
newValue.push(props.value)
}
} else {
if (index !== -1) {
newValue.splice(index, 1)
}
}
emit('update:modelValue', newValue)
} else {
emit('update:modelValue', value)
}
}
})
</script>
<template>
<div class="checkbox-wrapper">
<input
type="checkbox"
class="checkbox"
ref="input"
:id="id"
v-model="model"
v-bind="$attrs"
/>
<label :for="id" class="checkbox-label">
{{ label }}
</label>
</div>
</template>
<style scoped>
.checkbox-wrapper {
display: flex;
align-items: center;
gap: 8px;
}
.checkbox-label {
line-height: 20px;
font-size: 13px;
color: var(--vt-c-text-1);
transition: color .5s;
display: block;
}
.checkbox {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
cursor: pointer;
border: 1px solid var(--vp-input-border-color);
background-color: var(--vp-input-switch-bg-color);
border-radius: 4px;
}
.checkbox:checked {
border-color: transparent;
background: url("data:image/svg+xml,%3Csvg width='12px' height='12px' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='4' stroke-linecap='round' stroke-linejoin='round' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E")
center no-repeat var(--vp-c-brand);;
}
</style>

View File

@@ -24,10 +24,40 @@ const headingElement = computed(() => `h${props.headingLevel}`)
font-size: 32px; font-size: 32px;
font-weight: bold; font-weight: bold;
text-align: center; text-align: center;
margin-bottom: 36px; margin-bottom: 32px;
} }
section { section {
margin-bottom: 96px; 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> </style>

View File

@@ -42,7 +42,7 @@ onMounted(() => {
font-weight: 400; font-weight: 400;
background: var(--vp-c-brand-dark); background: var(--vp-c-brand-dark);
color: white; color: white;
z-index: 99; z-index: 10;
white-space: nowrap; white-space: nowrap;
padding: 2px 8px; padding: 2px 8px;
border-radius: 4px; border-radius: 4px;

View File

@@ -9,7 +9,7 @@ export default {
} }
return null; return null;
}) })
.then((res) => res?.tag_name); .then((res) => res.tag_name);
return { return {
version, version,

View File

@@ -8,6 +8,8 @@ import { data } from './HomeHeroBefore.data'
<HomeContainer class="container"> <HomeContainer class="container">
<Badge <Badge
:href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`" :href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`"
target="_blank"
rel="noreferrer noopener"
>v{{ data.version }}</Badge> >v{{ data.version }}</Badge>
</HomeContainer> </HomeContainer>
</template> </template>

View File

@@ -28,6 +28,8 @@ function insert() {
const replaceIndex = random(0, 48) const replaceIndex = random(0, 48)
const newIcon = getRandomNewIcon() const newIcon = getRandomNewIcon()
// items.value.splice(replaceIndex, 0, newIcon);
items.value[replaceIndex] = newIcon items.value[replaceIndex] = newIcon
} }
@@ -74,6 +76,7 @@ onBeforeUnmount(() => {
<style scoped> <style scoped>
.card-wrapper { .card-wrapper {
/* padding: 0 24px; */
margin-left: auto; margin-left: auto;
margin-bottom: auto; margin-bottom: auto;
margin-top: 48px; margin-top: 48px;
@@ -84,10 +87,13 @@ onBeforeUnmount(() => {
border-radius: 8px; border-radius: 8px;
width: 100%; width: 100%;
height:100%; height:100%;
/* box-shadow: var(--vp-shadow-2); */
max-height: 220px; max-height: 220px;
max-width: 560px; max-width: 560px;
margin: 0 auto; margin: 0 auto;
position: relative; position: relative;
/* max-height: 240px; */
/* margin-top: 96px; */
} }
.card-grid { .card-grid {
@@ -101,6 +107,7 @@ onBeforeUnmount(() => {
max-width: 512px; max-width: 512px;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
/* white-space: nowrap; */
} }
.list-enter-active { .list-enter-active {

View File

@@ -171,14 +171,16 @@ watch(absoluteStrokeWidth, (enabled) => {
margin-top: 32px; margin-top: 32px;
padding: 0; padding: 0;
background: none; background: none;
max-width: 280px;
} }
@media (min-width: 640px) { @media (min-width: 640px) {
.card { .card {
display: grid; display: grid;
grid-template-columns: 8fr 10fr; grid-template-columns: 8fr 10fr;
} }
/* /*
.card-column { .card-column {
flex: 1; flex: 1;
} */ } */

View File

@@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import HomeContainer from './HomeContainer.vue' import HomeContainer from './HomeContainer.vue'
import HomeSectionTitle from './HomeSectionTitle.vue'
import { useRouter } from 'vitepress'; import { useRouter } from 'vitepress';
import { data } from './HomePackagesSection.data' import { data } from './HomePackagesSection.data'
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'; import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
@@ -10,7 +9,7 @@ const { go } = useRouter()
<template> <template>
<HomeContainer> <HomeContainer>
<HomeSectionTitle>Available For:</HomeSectionTitle> <h2 class="section-title">Available For:</h2>
<div class="packages-list"> <div class="packages-list">
<a <a
v-for="{ name, logo } in data.packages" v-for="{ name, logo } in data.packages"
@@ -35,6 +34,14 @@ const { go } = useRouter()
</template> </template>
<style scoped> <style scoped>
.section-title {
color: var(--vp-c-text-2);
font-weight: 500;
line-height: 32px;
font-size: 16px;
text-align: center;
margin-bottom: 16px;
}
.packages-list { .packages-list {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;

View File

@@ -1,26 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps<{
headingLevel: 1 | 2 | 3 | 4 | 5 | 6,
}>()
const headingElement = computed(() => `h${props.headingLevel ?? 2}`)
</script>
<template>
<component :is="headingElement" class="section-title">
<slot />
</component>
</template>
<style scoped>
.section-title {
color: var(--vp-c-text-2);
font-weight: 500;
line-height: 32px;
font-size: 16px;
text-align: center;
margin-bottom: 16px;
}
</style>

View File

@@ -1,59 +0,0 @@
<script setup lang="ts">
import Card from '../base/Card.vue';
import HomeSectionTitle from './HomeSectionTitle.vue';
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
</script>
<template>
<HomeSectionTitle :headingLevel="3"> Sponsor the Lucide maintainers </HomeSectionTitle>
<Card class="sponsor-card">
<img
src="/company-logos/open-collective-light.svg"
alt="Open Collective logo"
class="logo light"
width="242"
height="42"
/>
<img
src="/company-logos/open-collective-dark.svg"
alt="Open Collective logo"
class="logo dark"
width="242"
height="42"
/>
<VPButton
href="https://opencollective.com/lucide-icons"
class="sponsor-button"
text="Become a sponsor"
/>
</Card>
</template>
<style scoped>
.sponsor-card {
margin: 0 auto;
max-width: 500px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
.sponsor-button {
margin: auto 0;
}
html.dark .logo.dark {
display: none;
}
html:not(.dark) .logo.light {
display: none;
}
@media (min-width: 640px) {
.sponsor-card {
flex-direction: row;
justify-content: space-between;
}
}
</style>

View File

@@ -1,91 +0,0 @@
<script setup lang="ts">
import { useData } from 'vitepress';
import HomeContainer from './HomeContainer.vue'
import GridSection from '../base/GridSection.vue'
import TeamMemberCard, { TeamMember } from './TeamMemberCard.vue'
import teamData from '../../../data/teamData.json'
import HomeSponsorCard from './HomeSponsorCard.vue';
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue'
const { theme } = useData()
</script>
<template>
<HomeContainer>
<GridSection
title="Meet the team"
:headingLevel="2"
class="team-members"
>
<TeamMemberCard
v-for="teamMember in (teamData as TeamMember[])"
v-bind="teamMember"
/>
</GridSection>
<HomeSponsorCard />
<VPDocAsideCarbonAds
:carbon-ads="theme.carbonAds"
class="ad-card"
/>
</HomeContainer>
</template>
<style scoped>
.team-members {
gap: 24px;
margin-top: 48px;
margin-bottom: 48px;
}
@media (min-width: 640px) {
.team-members :deep(.card-grid > *) {
flex-basis: 50%;
}
}
@media (min-width: 768px) {
.team-members :deep(.card-grid > *) {
flex-basis: 33.33%;
}
}
.ad-card {
margin: 32px auto 0;
width: 100%;;
max-width: 500px;
display: flex;
flex-direction: row;
justify-content: space-between;
display: none;
}
@media (min-width: 960px) {
.ad-card {
display: block;
}
}
.ad-card :deep(.VPCarbonAds) {
width: 100%;
text-align: left;
min-height: auto;
}
.ad-card :deep(.VPCarbonAds .carbon-wrap) {
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 24px;
}
.ad-card :deep(.VPCarbonAds .carbon-text) {
padding-top: 0;
}
.ad-card :deep(.VPCarbonAds .carbon-poweredby) {
text-align: right;
margin-top: -24px;
}
</style>

View File

@@ -1,92 +0,0 @@
<script lang="ts">
export interface TeamMember {
name: string
title: string
image: string
sponsor?: string
socialLinks: DefaultTheme.SocialLink[]
}
</script>
<script setup lang="ts">
import { DefaultTheme } from 'vitepress';
import Card from '../base/Card.vue'
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
import VPSocialLinks from 'vitepress/dist/client/theme-default/components/VPSocialLinks.vue'
defineProps<TeamMember>()
</script>
<template>
<div>
<Card class="member-card">
<img :src="image" :alt="name" class="member-image"/>
<h3 class="member-name">
{{name}}
</h3>
<p class="member-title">
{{title}}
</p>
<div class="member-links">
<VPButton
v-if="sponsor"
:href="sponsor"
text="Sponsor"
theme="sponsor"
class="sponsor-button"
size="medium"
/>
<VPSocialLinks
:links="socialLinks"
/>
</div>
</Card>
</div>
</template>
<style scoped>
.member-card {
flex-basis: 100%;
height:100%;
}
.member-image {
width: 64px;
height: 64px;
border-radius: 32px;
margin: 0 auto;
background-color: var(--vp-c-bg);
}
.member-name {
text-align: center;
margin-top: 16px;
font-size: 21px;
font-weight: 500;
color: var(--textColor);
}
.member-title {
flex-grow: 1;
padding-top: 8px;
line-height: 24px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-2);
text-align: center;
text-wrap: balance;
margin-bottom: 16px;;
}
.sponsor-button {
width: auto;
}
.member-links {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
}
</style>

View File

@@ -1,93 +0,0 @@
<script setup lang="ts">
import { useData } from 'vitepress';
import { useSessionStorage } from '@vueuse/core';
import IconButton from '../base/IconButton.vue';
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue'
import { x } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import { onMounted, ref } from 'vue';
const { theme } = useData()
const showAd = useSessionStorage('show-carbon-ads', true)
const carbonLoaded = ref(true)
defineProps<{
drawerOpen: boolean
}>()
const CloseIcon = createLucideIcon('Close', x)
onMounted(() => {
setTimeout(() => {
if (window?._carbonads == null) {
carbonLoaded.value = false
}
}, 5000)
})
</script>
<template>
<div
:class="{
'drawer-open': drawerOpen,
'hide-ad': !(showAd && carbonLoaded)
}"
class="floating-ad"
v-if="theme.carbonAds"
>
<IconButton @click="showAd = false" class="hide-button">
<component :is="CloseIcon" :size="20" absoluteStrokeWidth />
</IconButton>
<VPDocAsideCarbonAds
:carbon-ads="theme.carbonAds"
/>
</div>
</template>
<style scoped>
.floating-ad {
display: none;
position: fixed;
bottom: 32px;
width: 224px;
right: 32px;
transition: opacity 0.5s, transform 0.25s ease;
}
.floating-ad.drawer-open {
transform: translateY(-288px);
}
.floating-ad.hide-ad {
transform: translateX(224px);
opacity: 0;
}
.floating-ad.drawer-open.hide-ad {
transform: translateY(-288px) translateX(224px);
}
.floating-ad.drawer-open, .floating-ad.hide-ad {
transition: opacity 0.25s, transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
}
@media (min-width: 1280px) {
.floating-ad {
display: block;
}
}
@media (min-width: 1440px) {
.floating-ad {
right: calc(((100% - (var(--vp-layout-max-width) - var(--vp-sidebar-width))) - 272px) / 2);
}
}
.hide-button {
padding: 4px;
position: absolute;
top: 8px;
right: 8px;
background-color: transparent;
}
</style>

View File

@@ -6,7 +6,6 @@ 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 SidebarTitle from './SidebarTitle.vue'
import { useCategoryView } from '../../composables/useCategoryView' import { useCategoryView } from '../../composables/useCategoryView'
const { page } = useData() const { page } = useData()
@@ -47,13 +46,10 @@ watch(headers, () => {
<template> <template>
<div class="category-list" ref="container"> <div class="category-list" ref="container">
<SidebarTitle> <VPLink class="sidebar-title" href="/icons/" :class="{ 'active': overviewIsActive } ">
View
</SidebarTitle>
<VPLink class="sidebar-link sidebar-text" href="/icons/" :class="{ 'active': overviewIsActive } ">
All All
</VPLink> </VPLink>
<VPLink class="sidebar-link sidebar-text" href="/icons/categories" :class="{ 'active': categoriesIsActive } "> <VPLink class="sidebar-title" href="/icons/categories" :class="{ 'active': categoriesIsActive } ">
Categories Categories
</VPLink> </VPLink>
<div class="content"> <div class="content">
@@ -66,20 +62,17 @@ watch(headers, () => {
</template> </template>
<style scoped> <style scoped>
.sidebar-text { .sidebar-title {
font-weight: 500;
color: var(--vp-c-text-2);
margin-bottom: 6px;
line-height: 24px; line-height: 24px;
font-size: 14px; font-size: 14px;
display: block; display: block;
transition: color 0.25s; transition: color 0.25s;
padding: 4px 0;
} }
.sidebar-link { .sidebar-title:hover, .sidebar-title.active {
font-weight: 500;
color: var(--vp-c-text-2);
}
.sidebar-link:hover, .sidebar-link.active {
color: var(--vp-c-brand); color: var(--vp-c-brand);
} }
.content { .content {

View File

@@ -1,98 +1,92 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
import { toPascalCase } from '@lucide/shared'; 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';
import useConfetti from '../../composables/useConfetti'; import useConfetti from '../../composables/useConfetti';
const props = defineProps<{ const props = defineProps<{
name: string; name: string
popoverPosition?: 'top' | 'bottom'; popoverPosition?: 'top' | 'bottom'
}>(); }>()
const { size, color, strokeWidth, absoluteStrokeWidth } = useIconStyleContext(); const { size, color, strokeWidth, absoluteStrokeWidth } = useIconStyleContext()
const { animate, confetti } = useConfetti(); const { animate, confetti } = useConfetti()
const componentName = computed(() => { const componentName = computed(() => {
return (toPascalCase(props.name) as string).replace(/\s/g, ''); return startCase(camelCase(props.name)).replace(/\s/g, '')
}); })
function copyJSX() { function copyJSX() {
let attrs = ['']; let attrs = ['']
if (size.value && size.value !== 24) { if (size.value && size.value !== 24) {
attrs.push(`size={${size.value}}`); attrs.push(`size={${size.value}}`)
} }
if (color.value && color.value !== 'currentColor') { if (color.value && color.value !== 'currentColor') {
attrs.push(`color="${color.value}"`); attrs.push(`color="${color.value}"`)
} }
if (strokeWidth.value && strokeWidth.value !== 2) { if (strokeWidth.value && strokeWidth.value !== 2) {
attrs.push(`strokeWidth={${strokeWidth.value}}`); attrs.push(`strokeWidth={${strokeWidth.value}}`)
} }
if (absoluteStrokeWidth.value) { if (absoluteStrokeWidth.value) {
attrs.push(`absoluteStrokeWidth`); attrs.push(`absoluteStrokeWidth`)
} }
const code = `<${componentName.value}${attrs.join(' ')} />`; const code = `<${componentName.value}${attrs.join(' ')} />`
navigator.clipboard.writeText(code); navigator.clipboard.writeText(code)
}
function copyComponentName() {
const code = componentName.value;
navigator.clipboard.writeText(code);
} }
function copyVue() { function copyVue() {
let attrs = ['']; let attrs = ['']
if (size.value && size.value !== 24) { if (size.value && size.value !== 24) {
attrs.push(`:size="${size.value}"`); attrs.push(`:size="${size.value}"`)
} }
if (color.value && color.value !== 'currentColor') { if (color.value && color.value !== 'currentColor') {
attrs.push(`color="${color.value}"`); attrs.push(`color="${color.value}"`)
} }
if (strokeWidth.value && strokeWidth.value !== 2) { if (strokeWidth.value && strokeWidth.value !== 2) {
attrs.push(`:stroke-width="${strokeWidth.value}"`); attrs.push(`:stroke-width="${strokeWidth.value}"`)
} }
if (absoluteStrokeWidth.value) { if (absoluteStrokeWidth.value) {
attrs.push(`absoluteStrokeWidth`); attrs.push(`absoluteStrokeWidth`)
} }
const code = `<${componentName.value}${attrs.join(' ')} />`; const code = `<${componentName.value}${attrs.join(' ')} />`
navigator.clipboard.writeText(code); navigator.clipboard.writeText(code)
} }
function copyAngular() { function copyAngular() {
let attrs = ['']; let attrs = ['']
attrs.push(`name="${props.name}"`); attrs.push(`name="${props.name}"`)
if (size.value && size.value !== 24) { if (size.value && size.value !== 24) {
attrs.push(`[size]="${size.value}"`); attrs.push(`[size]="${size.value}"`)
} }
if (color.value && color.value !== 'currentColor') { if (color.value && color.value !== 'currentColor') {
attrs.push(`color="${color.value}"`); attrs.push(`color="${color.value}"`)
} }
if (strokeWidth.value && strokeWidth.value !== 2) { if (strokeWidth.value && strokeWidth.value !== 2) {
attrs.push(`[strokeWidth]="${strokeWidth.value}"`); attrs.push(`[strokeWidth]="${strokeWidth.value}"`)
} }
if (absoluteStrokeWidth.value) { if (absoluteStrokeWidth.value) {
attrs.push(`[absoluteStrokeWidth]="true"`); attrs.push(`[absoluteStrokeWidth]="true"`)
} }
const code = `<lucide-icon${attrs.join(' ')}></lucide-icon>`; const code = `<lucide-icon${attrs.join(' ')}></lucide-icon>`
navigator.clipboard.writeText(code); navigator.clipboard.writeText(code)
} }
</script> </script>
@@ -106,11 +100,10 @@ function copyAngular() {
data-confetti-text="Copied!" data-confetti-text="Copied!"
:popoverPosition="popoverPosition" :popoverPosition="popoverPosition"
:options="[ :options="[
{ text: 'Copy JSX', onClick: copyJSX }, { text: 'Copy JSX' , onClick: copyJSX },
{ text: 'Copy Component Name', onClick: copyComponentName }, { text: 'Copy Vue' , onClick: copyVue },
{ text: 'Copy Vue', onClick: copyVue }, { text: 'Copy Svelte' , onClick: copyJSX },
{ text: 'Copy Svelte', onClick: copyJSX }, { text: 'Copy Angular' , onClick: copyAngular },
{ text: 'Copy Angular', onClick: copyAngular },
]" ]"
/> />
</template> </template>

View File

@@ -11,31 +11,14 @@ 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'; import { satisfies } from 'semver';
import { useExternalLibs } from '../../composables/useExternalLibs';
const props = defineProps<{ const props = defineProps<{
iconName: string | null iconName: string
}>() }>()
const { externalIconNodes } = useExternalLibs()
const { go } = useRouter()
const icon = computedAsync<IconEntity | null>(async () => { const icon = computedAsync<IconEntity | null>(async () => {
if (props.iconName) { if (props.iconName) {
try { return (await import(`../../../data/iconDetails/${props.iconName}.ts`)).default as IconEntity
if (props.iconName.includes(':')) {
const [library, name] = props.iconName.split(':')
return externalIconNodes.value[library].find((icon) => icon.name === name)
} else {
return (await import(`../../../data/iconDetails/${props.iconName}.ts`)).default as IconEntity
}
} catch (err) {
if (!props.iconName.includes(':')) {
go(`/icons/${props.iconName}`)
}
}
} }
return null return null
}, null) }, null)
@@ -53,6 +36,8 @@ function onClose() {
emit('close') emit('close')
} }
const { go } = useRouter()
const CloseIcon = createLucideIcon('Close', x) const CloseIcon = createLucideIcon('Close', x)
const Expand = createLucideIcon('Expand', expand) const Expand = createLucideIcon('Expand', expand)
</script> </script>
@@ -66,8 +51,10 @@ const Expand = createLucideIcon('Expand', expand)
v-if="icon.createdRelease" v-if="icon.createdRelease"
class="version" class="version"
:href="releaseTagLink(icon.createdRelease.version)" :href="releaseTagLink(icon.createdRelease.version)"
target="_blank"
rel="noreferrer noopener"
>v{{ icon.createdRelease.version }}</Badge> >v{{ icon.createdRelease.version }}</Badge>
<IconButton @click="go(icon.externalLibrary ? `/icons/${icon.externalLibrary}/${icon.name}` : `/icons/${icon.name}`)"> <IconButton @click="go(`/icons/${icon.name}`)">
<component :is="Expand" /> <component :is="Expand" />
</IconButton> </IconButton>
<IconButton @click="onClose"> <IconButton @click="onClose">
@@ -157,11 +144,11 @@ const Expand = createLucideIcon('Expand', expand)
} }
.drawer-enter-active { .drawer-enter-active {
transition: opacity 0.5s, transform 0.25s ease; transition: all 0.2s cubic-bezier(.21,.8,.46,.9);
} }
.drawer-leave-active { .drawer-leave-active {
transition: opacity 0.25s ease, transform 1.6s ease-out; transition: all 0.4s cubic-bezier(1, 0.5, 0.8, 1);
} }
.drawer-enter-from, .drawer-enter-from,

View File

@@ -25,10 +25,8 @@ function setActiveIcon(name: string) {
:key="icon.name" :key="icon.name"
> >
<IconItem <IconItem
:iconNode="icon.iconNode" v-bind="icon"
:name="icon.name" @setActiveIcon="setActiveIcon(icon.name)"
:externalLibrary="icon.externalLibrary"
@setActiveIcon="setActiveIcon"
:active="activeIcon === icon.name" :active="activeIcon === icon.name"
customizable customizable
:overlayMode="overlayMode" :overlayMode="overlayMode"

View File

@@ -7,8 +7,6 @@ import CopyCodeButton from './CopyCodeButton.vue';
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'; import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
import {useData, useRouter} from 'vitepress'; import {useData, useRouter} from 'vitepress';
import { computed } from 'vue'; import { computed } from 'vue';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import { diamond } from '../../../data/iconNodes'
const props = defineProps<{ const props = defineProps<{
icon: IconEntity icon: IconEntity
@@ -22,21 +20,13 @@ const tags = computed(() => {
if (!props.icon || !props?.icon?.tags) return [] if (!props.icon || !props?.icon?.tags) return []
return props.icon.tags.join(' • ') return props.icon.tags.join(' • ')
}) })
const DiamondIcon = createLucideIcon('Diamond', diamond)
</script> </script>
<template> <template>
<div class="icon-info"> <div class="icon-info">
<div class="icon-name-wrapper"> <IconDetailName class="icon-name">
<IconDetailName class="icon-name"> {{ icon.name }}
{{ icon.name }} </IconDetailName>
</IconDetailName>
<div v-if="icon.externalLibrary" class="icon-external-lib">
<DiamondIcon fill="currentColor" :size="12"/>
{{ icon.externalLibrary }}
</div>
</div>
<div class="tags-scroller" v-if="tags.length"> <div class="tags-scroller" v-if="tags.length">
<p class="icon-tags horizontal-scroller"> <p class="icon-tags horizontal-scroller">
{{ tags }} {{ tags }}
@@ -54,10 +44,10 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
<div class="group buttons"> <div class="group buttons">
<VPButton <VPButton
v-if="!page?.relativePath?.startsWith?.(icon.externalLibrary ? `icons/${icon.externalLibrary}/${icon.name}`: `icons/${icon.name}`)" v-if="!page?.relativePath?.startsWith?.(`icons/${icon.name}`)"
:href="icon.externalLibrary ? `/icons/${icon.externalLibrary}/${icon.name}`: `/icons/${icon.name}`" :href="`/icons/${icon.name}`"
text="See in action" text="See in action"
@click="go(icon.externalLibrary ? `/icons/${icon.externalLibrary}/${icon.name}`: `/icons/${icon.name}`)" @click="go(`/icons/${icon.name}`)"
/> />
<CopySVGButton :name="icon.name" :popoverPosition="popoverPosition"/> <CopySVGButton :name="icon.name" :popoverPosition="popoverPosition"/>
<CopyCodeButton :name="icon.name" :popoverPosition="popoverPosition"/> <CopyCodeButton :name="icon.name" :popoverPosition="popoverPosition"/>
@@ -77,27 +67,9 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
text-transform: capitalize; text-transform: capitalize;
} }
.icon-name { .icon-name {
margin-right: -36px;
}
.icon-name-wrapper {
display: flex;
align-items: center;
gap: 2px;
margin-bottom: 4px; margin-bottom: 4px;
} }
.icon-external-lib {
color: var(--vp-c-brand-dark);
padding: 4px 12px;
font-size: 16px;
font-weight: 600;
line-height: 28px;
display: flex;
gap: 8px;
align-items: center;
}
.icon-tags { .icon-tags {
font-size: 16px; font-size: 16px;
color: var(--vp-c-text-2); color: var(--vp-c-text-2);

View File

@@ -6,7 +6,6 @@ import { useRouter } from 'vitepress';
import getSVGIcon from '../../utils/getSVGIcon'; import getSVGIcon from '../../utils/getSVGIcon';
import useConfetti from '../../composables/useConfetti'; import useConfetti from '../../composables/useConfetti';
import Tooltip from '../base/Tooltip.vue'; import Tooltip from '../base/Tooltip.vue';
import { diamond } from '../../../data/iconNodes'
const downloadText = 'Download!' const downloadText = 'Download!'
const copiedText = 'Copied!' const copiedText = 'Copied!'
@@ -17,7 +16,6 @@ const props = defineProps<{
name: string; name: string;
iconNode: IconNode; iconNode: IconNode;
active: boolean; active: boolean;
externalLibrary?: string;
customizable?: boolean; customizable?: boolean;
overlayMode?: boolean overlayMode?: boolean
hideIcon?: boolean hideIcon?: boolean
@@ -35,9 +33,8 @@ const icon = computed(() => {
return createLucideIcon(props.name, props.iconNode) return createLucideIcon(props.name, props.iconNode)
}) })
const href = computed(() => props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`)
async function navigateToIcon(event) { async function navigateToIcon(event) {
if (event.shiftKey) { if (event.shiftKey) {
event.preventDefault() event.preventDefault()
const svgString = getSVGIcon(event.target.firstChild, { const svgString = getSVGIcon(event.target.firstChild, {
@@ -53,16 +50,14 @@ async function navigateToIcon(event) {
if(props.overlayMode && showOverlay.value) { if(props.overlayMode && showOverlay.value) {
event.preventDefault() event.preventDefault()
window.history.pushState({}, '', `/icons/${props.name}`)
window.history.pushState({}, '', props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`) emit('setActiveIcon', props.name)
emit('setActiveIcon', props.externalLibrary ? `${props.externalLibrary}:${props.name}`: props.name) }
} else { else {
event.preventDefault() event.preventDefault()
go(props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`) go(`/icons/${props.name}`)
} }
} }
const DiamondIcon = createLucideIcon('Diamond', diamond)
</script> </script>
<template> <template>
@@ -72,7 +67,7 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
@click="navigateToIcon" @click="navigateToIcon"
:class="{ active, animate }" :class="{ active, animate }"
:aria-label="name" :aria-label="name"
:href="`/icons/${props.name}`"
:data-confetti-text="confettiText" :data-confetti-text="confettiText"
ref="ref" ref="ref"
> >
@@ -86,13 +81,6 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
}" }"
/> />
</KeepAlive> </KeepAlive>
<div
v-if="externalLibrary"
class="floating-diamond"
aria-hidden="true"
>
<DiamondIcon fill="currentColor" :size="8"/>
</div>
</a> </a>
</Tooltip> </Tooltip>
</template> </template>
@@ -101,7 +89,6 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
<style scoped> <style scoped>
.icon-button { .icon-button {
position: relative;
display: inline-block; display: inline-block;
border: 1px solid transparent; border: 1px solid transparent;
text-align: center; text-align: center;
@@ -118,13 +105,6 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
color: var(--vp-c-text-1); color: var(--vp-c-text-1);
} }
.floating-diamond {
position: absolute;
top: 4px;
right: 4px;
color: var(--vp-c-brand);
}
.confetti-button:before, .confetti-button:before,
.confetti-button:after { .confetti-button:after {
z-index: 100; z-index: 100;

View File

@@ -1,438 +0,0 @@
<script setup lang="ts">
import type { IconEntity } from '../../types';
import { computed } from 'vue';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon.ts';
import Calendar from '../../../data/iconDetails/calendar.ts';
import Clock from '../../../data/iconDetails/clock.ts';
import Bug from '../../../data/iconDetails/bug.ts';
import Rocket from '../../../data/iconDetails/rocket.ts';
import TriangleAlert from '../../../data/iconDetails/triangle-alert.ts';
import PartyPopper from '../../../data/iconDetails/party-popper.ts';
import Scissors from '../../../data/iconDetails/scissors.ts';
import Copy from '../../../data/iconDetails/copy.ts';
import Save from '../../../data/iconDetails/save.ts';
import Clipboard from '../../../data/iconDetails/clipboard.ts';
import MessageCircle from '../../../data/iconDetails/message-circle.ts';
import ThumbsDown from '../../../data/iconDetails/thumbs-down.ts';
import ThumbsUp from '../../../data/iconDetails/thumbs-up.ts';
import Heart from '../../../data/iconDetails/heart.ts';
import Folder from '../../../data/iconDetails/folder.ts';
import Files from '../../../data/iconDetails/files.ts';
import Plus from '../../../data/iconDetails/plus.ts';
import File from '../../../data/iconDetails/file.ts';
import FileText from '../../../data/iconDetails/file-text.ts';
const props = defineProps<{
name: IconEntity['name'];
iconNode: IconEntity['iconNode'];
}>();
const iconComponent = computed(() => {
if (!props.name || !props.iconNode) return null;
return createLucideIcon(props.name, props.iconNode);
});
const CalendarIcon = createLucideIcon('calendar', Calendar.iconNode);
const ClockIcon = createLucideIcon('clock', Clock.iconNode);
const BugIcon = createLucideIcon('bug', Bug.iconNode);
const RocketIcon = createLucideIcon('rocket', Rocket.iconNode);
const AlertTriangleIcon = createLucideIcon('alert-triangle', TriangleAlert.iconNode);
const ScissorsIcon = createLucideIcon('scissors', Scissors.iconNode);
const CopyIcon = createLucideIcon('copy', Copy.iconNode);
const SaveIcon = createLucideIcon('save', Save.iconNode);
const ClipboardIcon = createLucideIcon('clipboard', Clipboard.iconNode);
const PartyPopperIcon = createLucideIcon('party-popper', PartyPopper.iconNode);
const HeartIcon = createLucideIcon('heart', Heart.iconNode);
const ThumbsUpIcon = createLucideIcon('thumbs-up', ThumbsUp.iconNode);
const ThumbsDownIcon = createLucideIcon('thumbs-down', ThumbsDown.iconNode);
const MessageCircleIcon = createLucideIcon('message-circle', MessageCircle.iconNode);
const FolderIcon = createLucideIcon('folder.ts', Folder.iconNode);
const FilesIcon = createLucideIcon('files.ts', Files.iconNode);
const PlusIcon = createLucideIcon('plus.ts', Plus.iconNode);
const FileIcon = createLucideIcon('file.ts', File.iconNode);
const FileTextIcon = createLucideIcon('file-text.ts', FileText.iconNode);
const prettyName = props.name
.split('-')
.at(0)
.split('')
.map((character, index) => (index === 0 ? character.toUpperCase() : character))
.join('');
</script>
<template>
<section class="showcase">
<h2 class="title">See this icon in action</h2>
<div class="showcase-grid">
<div class="showcase-item column">
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder narrow"></div>
<div class="spacer"></div>
<div class="actions">
<button class="button button-brand">
<iconComponent />
{{ prettyName }}
</button>
<button class="button button-alt">Cancel</button>
</div>
</div>
<div class="showcase-item column">
<div class="placeholder narrow"></div>
<div class="input-wrapper">
<CalendarIcon v-if="name !== 'calendar'" />
<ClockIcon v-if="name == 'calendar'" />
<input
type="text"
v-if="name !== 'calendar'"
placeholder="Enter a date..."
/>
<input
type="text"
v-if="name == 'calendar'"
placeholder="Enter a time..."
/>
</div>
<div class="spacer"></div>
<div class="placeholder narrow"></div>
<div class="input-wrapper">
<iconComponent />
<input
type="text"
placeholder="Enter a value..."
/>
</div>
</div>
<div class="showcase-item column">
<div
class="row"
v-if="name !== 'bug'"
>
<div class="placeholder"></div>
<div class="badge badge-red">
<BugIcon :size="20" />
Bug
</div>
</div>
<div
class="row"
v-else
>
<div class="placeholder"></div>
<div class="badge badge-red">
<AlertTriangleIcon :size="20" />
Alert
</div>
</div>
<div class="row">
<div class="placeholder"></div>
<div class="badge badge-indigo">
<RocketIcon
:size="20"
v-if="name !== 'rocket'"
/>
<PartyPopperIcon
:size="20"
v-else
/>
Feature
</div>
</div>
<div class="row">
<div class="placeholder"></div>
<div class="badge badge-green">
<iconComponent :size="20" />
{{ prettyName }}
</div>
</div>
</div>
<div class="showcase-item column">
<button class="button button-alt button-square">
<FolderIcon v-if="name !== 'folder'" />
<FilesIcon v-else />
Documents
<PlusIcon class="ms-auto" />
</button>
<button class="button button-alt button-square">
<FileIcon v-if="name !== 'file'" />
<FileTextIcon v-else />
Readme
</button>
<button class="button button-alt button-square">
<iconComponent />
{{ prettyName }}
<span class="badge-notification ms-auto">12</span>
</button>
</div>
<div class="showcase-item column">
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder narrow"></div>
<div class="spacer"></div>
<div class="actions">
<div class="icon-counter">
<HeartIcon v-if="name !== 'heart'" />
<ThumbsUpIcon v-else />
112
</div>
<div class="spacer"></div>
<div class="icon-counter">
<MessageCircleIcon v-if="name !== 'message-circle'" />
<ThumbsDownIcon v-else />
8
</div>
<div class="spacer"></div>
<div class="icon-counter">
<iconComponent />
11
</div>
</div>
</div>
<div class="showcase-item">
<div class="column align-items-center">
<div class="actions justify-content-center">
<button class="button button-icon">
<CopyIcon v-if="name !== 'copy'" />
<SaveIcon v-else />
</button>
<button class="button button-icon">
<ScissorsIcon v-if="name !== 'scissors'" />
<SaveIcon v-else />
</button>
<button class="button button-icon">
<ClipboardIcon v-if="name !== 'clipboard'" />
<SaveIcon v-else />
</button>
<button class="button button-icon">
<iconComponent></iconComponent>
<span class="badge-notification">2</span>
</button>
</div>
<div class="spacer"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
</div>
</div>
</div>
</section>
</template>
<style scoped>
.showcase {
margin-bottom: 32px;
}
.showcase-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 48px;
margin-inline-start: 24px;
margin-block-start: 48px;
}
.showcase-item {
padding: 24px;
border-radius: 8px;
background-color: var(--vp-c-bg);
box-shadow:
var(--vp-shadow-4),
-24px -24px 0 var(--vp-c-bg-soft);
display: flex;
align-items: center;
justify-content: center;
min-height: 240px;
}
.spacer {
flex-basis: 0;
}
.button {
position: relative;
border-radius: 32px;
padding: 8px 16px;
display: flex;
flex-direction: row;
gap: 12px;
font-weight: 500;
transition:
color 0.25s,
border-color 0.25s,
background-color 0.25s;
border-color: var(--vp-button-alt-border);
color: var(--vp-button-alt-text);
background-color: var(--vp-button-alt-bg);
&:hover {
border-color: var(--vp-button-alt-hover-border);
color: var(--vp-button-alt-hover-text);
background-color: var(--vp-button-alt-hover-bg);
}
&.button-brand {
border-color: var(--vp-button-brand-border);
color: var(--vp-button-brand-text);
background-color: var(--vp-button-brand-bg);
&:hover {
border-color: var(--vp-button-brand-hover-border);
color: var(--vp-button-brand-hover-text);
background-color: var(--vp-button-brand-hover-bg);
}
}
&.button-icon {
padding: 12px;
}
&.button-square {
border-radius: 8px;
width: 100%;
}
}
.actions {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 12px;
}
.lucide {
flex-shrink: 0;
flex-grow: 0;
}
.placeholder {
display: block;
background-color: var(--vp-c-bg-soft);
border-radius: 32px;
height: 16px;
width: 100%;
&.narrow {
width: 33%;
}
}
.input-wrapper {
position: relative;
width: 100%;
.lucide {
position: absolute;
inset-inline-start: 12px;
inset-block: 50%;
translate: 0 -50%;
color: var(--vp-c-text-1);
opacity: 0.7;
}
input {
padding: 12px 20px;
padding-inline-start: 48px;
border-radius: 8px;
background-color: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
display: block;
width: 100%;
border: 2px solid transparent;
transition:
color 0.25s,
border-color 0.25s,
background-color 0.25s;
&:hover {
border-color: var(--vp-c-border);
}
&:focus {
border-color: var(--vp-c-brand);
}
}
&:focus-within {
.lucide {
opacity: 1;
}
}
}
.badge {
flex-shrink: 0;
display: flex;
flex-direction: row;
align-items: center;
gap: 6px;
padding: 4px 8px;
border-radius: 8px;
background-color: transparent;
overflow: hidden;
position: relative;
color: var(--badge-color);
&:before {
content: ' ';
inset: 0;
position: absolute;
background-color: var(--badge-color);
opacity: 0.1;
}
&.badge-indigo {
--badge-color: var(--vp-c-indigo-2);
}
&.badge-green {
--badge-color: var(--vp-c-green-2);
}
&.badge-red {
--badge-color: var(--vp-c-brand);
}
}
.badge-notification {
background-color: var(--vp-c-brand);
color: var(--vp-button-brand-text);
border-radius: 32px;
padding: 0 8px;
min-width: 24px;
min-height: 24px;
}
.button-icon {
.badge-notification {
position: absolute;
top: 0;
right: 0;
translate: 33% -33%;
}
}
.row {
display: flex;
flex-direction: row;
gap: 12px;
align-items: center;
width: 100%;
}
.icon-counter {
display: flex;
flex-direction: row;
gap: 6px;
color: var(--vp-c-text-2);
align-items: center;
}
.column {
display: flex;
flex-direction: column;
gap: 12px;
align-items: start;
width: 100%;
}
.align-items-center {
align-items: center;
}
.justify-content-center {
justify-content: center;
}
.ms-auto {
margin-inline-start: auto;
}
</style>

View File

@@ -19,7 +19,7 @@ export type CategoryRow = CategoryNameRow | CategoryIconsRow;
import IconGrid from './IconGrid.vue' import IconGrid from './IconGrid.vue'
defineProps<{ defineProps<{
activeIconName: string | null activeIconName: string
categoryRow: CategoryRow categoryRow: CategoryRow
}>() }>()

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, defineAsyncComponent, onMounted, watch, watchEffect } from 'vue'; import { ref, computed, defineAsyncComponent, onMounted } from 'vue';
import type { IconEntity, Category } from '../../types'; import 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';
@@ -12,7 +12,6 @@ import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
import chunkArray from '../../utils/chunkArray'; import chunkArray from '../../utils/chunkArray';
import { CategoryRow } from './IconsCategory.vue'; import { CategoryRow } from './IconsCategory.vue';
import useScrollToCategory from '../../composables/useScrollToCategory'; import useScrollToCategory from '../../composables/useScrollToCategory';
import CarbonAdOverlay from './CarbonAdOverlay.vue';
const ICON_SIZE = 56; const ICON_SIZE = 56;
const ICON_GRID_GAP = 8; const ICON_GRID_GAP = 8;
@@ -69,7 +68,7 @@ const categories = computed(() => {
return props.categories return props.categories
.map(({ name, title }) => { .map(({ name, title }) => {
const categoryIcons = props.icons.filter((icon) => { const categoryIcons = props.icons.filter((icon) => {
const iconCategories = icon?.externalLibrary ? icon.categories : props.iconCategories[icon.name] const iconCategories = props.iconCategories[icon.name];
return iconCategories?.includes(name); return iconCategories?.includes(name);
}); });
@@ -134,18 +133,6 @@ function onFocusSearchInput() {
const NoResults = defineAsyncComponent(() => import('./NoResults.vue')); const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue')); const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
function handleCloseDrawer() {
setActiveIconName('');
window.history.pushState({}, '', '/icons/categories');
}
watchEffect(() => {
console.log(props.icons.find((icon) => icon.name === 'burger'));
});
</script> </script>
<template> <template>
@@ -177,10 +164,8 @@ watchEffect(() => {
<IconDetailOverlay <IconDetailOverlay
v-if="activeIconName != null" v-if="activeIconName != null"
:iconName="activeIconName" :iconName="activeIconName"
@close="handleCloseDrawer" @close="setActiveIconName('')"
/> />
<CarbonAdOverlay :drawerOpen="!!activeIconName" />
</template> </template>
<style scoped> <style scoped>

View File

@@ -10,11 +10,9 @@ 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'; import chunkArray from '../../utils/chunkArray';
import CarbonAdOverlay from './CarbonAdOverlay.vue';
const ICON_SIZE = 56; const ICON_SIZE = 56;
const ICON_GRID_GAP = 8; const ICON_GRID_GAP = 8;
const DEFAULT_GRID_ITEMS = 10 * 160;
const props = defineProps<{ const props = defineProps<{
icons: IconEntity[]; icons: IconEntity[];
@@ -95,12 +93,6 @@ const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay
watch(searchQueryDebounced, () => { watch(searchQueryDebounced, () => {
scrollTo(0) scrollTo(0)
}) })
function handleCloseDrawer() {
setActiveIconName('');
window.history.pushState({}, '', '/icons/');
}
</script> </script>
<template> <template>
@@ -115,23 +107,11 @@ function handleCloseDrawer() {
/> />
</StickyBar> </StickyBar>
<NoResults <NoResults
v-if="list.length === 0 && searchQuery !== ''" v-if="list.length === 0"
:searchQuery="searchQuery" :searchQuery="searchQuery"
@clear="searchQuery = ''" @clear="searchQuery = ''"
/> />
<IconGrid <div v-bind="wrapperProps" class="icon">
v-else-if="list.length === 0"
:key="index"
overlayMode
:icons="[...searchResults].splice(0, DEFAULT_GRID_ITEMS)"
:activeIcon="activeIconName"
@setActiveIcon="setActiveIconName"
/>
<div
v-bind="wrapperProps"
class="icon"
v-else
>
<IconGrid <IconGrid
v-for="{ index, data: icons } in list" v-for="{ index, data: icons } in list"
:key="index" :key="index"
@@ -144,11 +124,10 @@ function handleCloseDrawer() {
</div> </div>
<IconDetailOverlay <IconDetailOverlay
v-if="activeIconName != null"
:iconName="activeIconName" :iconName="activeIconName"
@close="handleCloseDrawer" @close="setActiveIconName('')"
/> />
<CarbonAdOverlay :drawerOpen="!!activeIconName" />
</template> </template>
<style> <style>

View File

@@ -1,20 +1,29 @@
<script setup lang="ts"> <script setup lang="ts">
import type { IconEntity } from '../../types'; import type { IconEntity } from '../../types'
import IconGrid from './IconGrid.vue'; import IconGrid from './IconGrid.vue'
defineProps<{ defineProps<{
icons: IconEntity[]; icons: IconEntity[]
}>(); }>()
</script> </script>
<template> <template>
<section class="related-icons"> <section class="related-icons">
<h2 class="title">More icons like this</h2> <h2 class="title">
<IconGrid :icons="icons" /> Related Icons
</h2>
<IconGrid
:icons="icons"
/>
</section> </section>
</template> </template>
<style scoped> <style scoped>
.title {
margin-bottom: 24px;
font-size: 19px;
font-weight: 500;
}
.related-icons { .related-icons {
margin-bottom: 32px; margin-bottom: 32px;
} }

View File

@@ -1,47 +0,0 @@
<script setup lang="ts">
import Checkbox from '../base/Checkbox.vue'
import SidebarTitle from './SidebarTitle.vue'
import { useExternalLibs } from '../../composables/useExternalLibs';
import { ExternalLibs } from '../../types';
interface ExternalLibrary {
name: string;
value: ExternalLibs;
}
const externalLibraries: ExternalLibrary[] = [
{
name: 'Lab',
value: 'lab'
},
];
const { selectedLibs } = useExternalLibs();
</script>
<template>
<div class="external-library-select">
<SidebarTitle>
Include external libs
</SidebarTitle>
<ul>
<li
v-for="library in externalLibraries"
:key="library.name"
>
<Checkbox
:label="library.name"
:id="library.name"
v-model="selectedLibs"
:value="library.value"
/>
</li>
</ul>
</div>
</template>
<style scoped>
.external-library-select {
margin-bottom: 24px;
}
</style>

View File

@@ -1,19 +0,0 @@
<template>
<h2 class="sidebar-title sidebar-text">
<slot />
</h2>
</template>
<style scoped>
.sidebar-title {
font-weight: 700;
color: var(--vp-c-text-1);
}
.sidebar-text {
line-height: 24px;
font-size: 14px;
display: block;
transition: color 0.25s;
padding: 4px 0;
}
</style>

View File

@@ -23,8 +23,8 @@ const links = computed(() => [
href: `${githubLink.value}/releases` href: `${githubLink.value}/releases`
}, },
{ {
text: 'GitHub', text: 'Github',
href: `${githubLink.value}` href: `${githubLink.value}/issues`
}, },
{ {
text: 'Issues', text: 'Issues',

View File

@@ -5,10 +5,9 @@ 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 != null && p.name in packageData) .filter((p) => p.name in packageData)
.map((pData) => ({ .map((pData) => ({
...pData, ...pData,
...packageData[pData.name], ...packageData[pData.name],

View File

@@ -1,8 +1,7 @@
<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 GridSection from '../base/GridSection.vue'
import PackageListItem from "./PackageListItem.vue"; import PackageListItem from "./PackageListItem.vue";</script>
</script>
<template> <template>
<GridSection <GridSection
@@ -39,4 +38,33 @@ import PackageListItem from "./PackageListItem.vue";
.package-group { .package-group {
margin-bottom: 96px; 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> </style>

View File

@@ -19,7 +19,7 @@ const props = defineProps<{
<img v-if="packageData.iconDark" :src="packageData.iconDark" alt="" class="package-icon dark" :class="packageData.iconClass" /> <img v-if="packageData.iconDark" :src="packageData.iconDark" alt="" class="package-icon dark" :class="packageData.iconClass" />
</div> </div>
<div class="package-title"> <div class="package-title">
<h2 class="title">{{ props.packageData?.name }}</h2> <h2 class="title">{{ props.packageData.name }}</h2>
<a v-for="shield in props.packageData.shields" :href="shield.href" class="package-shield" rel="noreferrer noopener"> <a v-for="shield in props.packageData.shields" :href="shield.href" class="package-shield" rel="noreferrer noopener">
<img :src="shield.src" :alt="shield.href" /> <img :src="shield.src" :alt="shield.href" />
</a> </a>

View File

@@ -40,4 +40,33 @@ import ShowcaseListItem from "./ShowcaseListItem.vue";
.package-group { .package-group {
margin-bottom: 96px; 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> </style>

View File

@@ -1,57 +0,0 @@
import { ref, inject, Ref, watch } from 'vue';
import { ExternalLibs, IconEntity } from '../types';
export const EXTERNAL_LIBS_CONTEXT = Symbol('externalLibs');
type ExternalIconNodes = Partial<Record<ExternalLibs, IconEntity[]>>;
interface ExternalLibContext {
selectedLibs: Ref<[ExternalLibs]>;
externalIconNodes: Ref<ExternalIconNodes>;
}
export const externalLibContext = {
selectedLibs: ref([]),
externalIconNodes: ref({}),
};
const externalLibIconNodesAPI = {
lab: 'https://lab.lucide.dev/api/icon-details',
};
export function useExternalLibs(): ExternalLibContext {
const context = inject<ExternalLibContext>(EXTERNAL_LIBS_CONTEXT);
watch(context?.selectedLibs, async (selectedLibs) => {
const savedIconNodes = { ...context?.externalIconNodes.value };
const newExternalIconNodes: ExternalIconNodes = {};
try {
for (const lib of selectedLibs) {
if (savedIconNodes[lib]) {
newExternalIconNodes[lib] = savedIconNodes[lib];
} else {
const response = await fetch(externalLibIconNodesAPI[lib]);
const iconNodes = await response.json();
if (iconNodes) {
newExternalIconNodes[lib] = Object.values(iconNodes).map((iconEntity: IconEntity) => ({
...iconEntity,
externalLibrary: lib,
}));
}
}
}
context.externalIconNodes.value = newExternalIconNodes;
} catch (error) {
console.error(error);
}
});
if (!context) {
throw new Error('useExternalLibs must be used with externalLibs context');
}
return context;
}

View File

@@ -1,29 +0,0 @@
import { computed } from 'vue';
import { useExternalLibs } from '~/.vitepress/theme/composables/useExternalLibs';
import { IconEntity } from '../types';
const useIconsWithExternalLibs = (initialIcons?: IconEntity[]) => {
const { externalIconNodes } = useExternalLibs();
return computed(() => {
let icons = [];
if (initialIcons) {
icons = icons.concat(initialIcons);
}
const externalIconNodesArray = Object.values(externalIconNodes.value);
if (externalIconNodesArray?.length) {
externalIconNodesArray.forEach((iconNodes) => {
if (iconNodes?.length) {
icons = icons.concat(iconNodes);
}
});
}
return icons;
});
};
export default useIconsWithExternalLibs;

View File

@@ -7,7 +7,6 @@ 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';
import { EXTERNAL_LIBS_CONTEXT, externalLibContext } from './composables/useExternalLibs';
const theme: Partial<Theme> = { const theme: Partial<Theme> = {
extends: DefaultTheme, extends: DefaultTheme,
@@ -21,7 +20,6 @@ const theme: Partial<Theme> = {
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);
app.provide(EXTERNAL_LIBS_CONTEXT, externalLibContext);
}, },
}; };

View File

@@ -3,7 +3,6 @@ import { useData } from 'vitepress'
import CategoryList from '../components/icons/CategoryList.vue' import CategoryList from '../components/icons/CategoryList.vue'
import SidebarIconCustomizer from '../components/icons/SidebarIconCustomizer.vue' import SidebarIconCustomizer from '../components/icons/SidebarIconCustomizer.vue'
import ExternalLibrarySelect from '../components/icons/SidebarExternalLibrarySelect.vue'
const { page } = useData() const { page } = useData()
@@ -12,7 +11,6 @@ const { page } = useData()
<template> <template>
<div> <div>
<SidebarIconCustomizer v-if="page?.relativePath?.startsWith?.('icons')"/> <SidebarIconCustomizer v-if="page?.relativePath?.startsWith?.('icons')"/>
<ExternalLibrarySelect v-if="page?.relativePath?.startsWith?.('icons')"/>
<CategoryList v-if="page?.relativePath?.startsWith?.('icons')"/> <CategoryList v-if="page?.relativePath?.startsWith?.('icons')"/>
</div> </div>
</template> </template>

View File

@@ -1,18 +1,13 @@
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 IconMetaData { export interface IconEntity {
name: string;
tags: string[]; tags: string[];
categories: string[]; categories: string[];
contributors: string[]; contributors: string[];
aliases?: string[]; aliases?: string[];
}
export type ExternalLibs = 'lab';
export interface IconEntity extends IconMetaData {
name: string;
iconNode: IconNode; iconNode: IconNode;
externalLibrary?: ExternalLibs;
createdRelease?: Release; createdRelease?: Release;
changedRelease?: Release; changedRelease?: Release;
} }

View File

@@ -1,7 +1,5 @@
import { IconNode } from 'lucide-vue-next/src/createLucideIcon';
import Vue from 'vue';
declare module '*.vue' { declare module '*.vue' {
import Vue from 'vue';
export default Vue; export default Vue;
} }
@@ -18,7 +16,3 @@ declare const resvg_wasm: RequestInfo | URL | Response | BufferSource | WebAssem
declare module 'node:module' { declare module 'node:module' {
function createRequire(filename: string): NodeRequire; function createRequire(filename: string): NodeRequire;
} }
declare module '*.node.json' {
export default IconNode;
}

View File

@@ -1,215 +1,4 @@
---
title: Accessibility
---
# Accessibility # Accessibility
Icons are pictures that show what something means without using words. They can be very helpful <!-- Description how you should use svg icons keeping web accessible -->
because they can quickly give information. <!-- See @JanTrichter comment about some information to write this: https://github.com/lucide-icons/lucide/pull/1521#discussion_r1332141390 -->
However, not everyone can understand them easily. When using icons it is very important to consider
the following aspects of accessibility.
## Provide visible labels
Icons are a helpful tool to improve perception, but they aren't a replacement for text.
In most cases, it is probably a good idea to also provide a textual representation of your icon's
function.
![In short: Dont rely on communicating the function of elements by icons alone. Do also provide a written description of the your interactive elements. For example: write out "On this page" on your on-page navigation element.](../../images/a11y/visible-labels.svg?raw=true)
## Contrast
Ensure there's enough contrast between the icon and its background so that it's visible to people
with low vision or color vision deficiencies.
We recommend
following [WCAG 2.1 SC 1.4.3](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html).
![In short: use a contrast ratio of at least 4.5:1](../../images/a11y/contrast.svg?raw=true)
## Use of color
Avoid relying solely on color to convey meaning in icons, as some users may have color blindness.
Instead, use additional visual cues like shape, shading or text.
![For example: Dont mark state with color, mark it with distinct visuals.](../../images/a11y/use-of-color.svg?raw=true)
## Interactivity
Ensure that interactive icons are accessible via keyboard navigation and provide clear feedback when
activated.
![](../../images/a11y/interactivity.svg?raw=true)
In most cases this is easily done by wrapping them in icon buttons.
## Minimum target size
Small targets can be difficult to click or touch, if your icon is interactive, we recommend that it
should have a minimum target size of 44×44 pixels.
![](../../images/a11y/target-size.svg?raw=true)
In practice, this doesn't necessarily mean that the icon itself should be this large, only its
interactive wrapper element.
## Meaningfulness
Icons should represent concepts or actions in a universally understandable way. Avoid using abstract
or ambiguous, or culture-specific symbols that might confuse some users.
![For example: Use universally understandable symbols and don't base your choice of icon on puns.](../../images/a11y/meaningfulness.svg?raw=true)
## Consistency
Maintain consistency in icon design and usage across your interface to help users learn and
understand their meanings more easily.
![For example: Dont use the same icon for multiple distinct purposes or meanings. Dont use different icons for the same purpose or function.](../../images/a11y/consistency.svg?raw=true)
## Text Alternatives
You may have to provide text labels or alternative text descriptions for icons, especially for
screen readers used by people with visual impairments.
However: descriptions should only be provided to standalone icons that aren't purely decorative, as
providing accessible names to non-functional elements only increases clutter when using screen
readers.
### On standalone icons
Icons are usually very unlikely to stand on their own with no semantically meaningful wrapper
element. In most cases they will be part of a badge, button (including icon buttons), navigation
item or other interactive UI element.
::: warning
In case some of your icons stand alone, and they serve a non-decorative function, make sure to
provide the appropriate accessible label for them.
:::
![In short: provide accessible label for semantic icons, but not for decorative icons.](../../images/a11y/alttext-standalone.svg?raw=true)
In general try to avoid using functional icons with no interactivity, we recommend that:
1) you either add a visible description next to them, or
2) place them in badges, labels or on buttons, and at least add a tooltip to them.
In any such case, it is preferred that the accessible name be provided for these interactive
elements (badges, buttons, nav items etc.) only, _not_ the icons themselves.
### On buttons
Do not provide an accessible label to icons when used on a button, as this label will be read out by
screen readers, leading to nonsensical text.
![](../../images/a11y/alttext-buttons.svg?raw=true)
::: details Code examples
```tsx
// Don't do this
<button>
<Plus aria-label="Plus icon"/>
Add document
</button>
// Do this, just leave it
<button>
<Plus/>
Add document
</button>
```
:::
### On icon buttons
Icon buttons are buttons that do not contain any visible text apart from the icon itself (think of
the close button of a dialog for example).
As previously stated, you should provide your accessible label on the icon button itself, not the
contained icon.
![](../../images/a11y/alttext-iconbuttons.svg?raw=true)
::: details Code examples
```tsx
// Don't do this
<button class="btn-icon">
<Home/>
</button>
// Don't do this either
<button class="btn-icon">
<Home aria-label="Home icon"/>
</button>
// This works but might not be the best solution, see below
<button aria-label="Go to home" class="btn-icon">
<Home/>
</button>
// Do this instead
<button class="btn-icon">
<Home/>
<span class="visually-hidden">Go to home</span>
</button>
```
:::
## A note on `aria-label`
Although you could provide accessible labels to your elements via the `aria-label` attribute, we
generally recommend avoiding this and instead suggest that you use your chosen CSS framework's "
visually hidden" utility whenever possible. You can
[read more about why `aria-label` might not be the best solution](https://gomakethings.com/revisting-aria-label-versus-a-visually-hidden-class/).
### Example - Radix UI
Use [Radix UI's built-in accessible icon utility component](https://www.radix-ui.com/primitives/docs/utilities/accessible-icon).
```tsx
import { ArrowRightIcon } from 'lucide-react';
import { AccessibleIcon } from '@radix-ui/react-accessible-icon';
<AccessibleIcon label="Next item">
<ArrowRightIcon />
</AccessibleIcon>
```
### Example - Bootstrap
```html
<div>
<i data-lucide="phone" aria-hidden="true"></i>
<span class="visually-hidden">Phone number</span>
</div>
```
### Example - Tailwind CSS
```html
<div>
<i data-lucide="phone" aria-hidden="true"></i>
<span class="sr-only">Phone number</span>
</div>
```
If you're not sure, you may consider learning more
about [how to hide content.](https://www.a11yproject.com/posts/how-to-hide-content/)
## Further resources
We also recommend checking out the following resources about accessibility:
- [Web Content Accessibility Guidelines (WCAG) 2.1](https://www.w3.org/TR/WCAG21/)
- [Web Accessibility Initiative (WAI)](https://www.w3.org/WAI/)
- [Learn accessibility on web.dev](https://web.dev/learn/accessibility)
- [Inclusive Components](https://inclusive-components.design/)
- [A11yTalks](https://www.a11ytalks.com/)
- [A11y automation tracker](https://a11y-automation.dev/)
- [The A11Y Project](https://www.a11yproject.com/)

View File

@@ -1,93 +0,0 @@
# Aliased Names
Icons can have multiple names for the same icon. This is because we choose to rename some icons to make them more consistent with the rest of the icon set, or the name was not generic. For example, the `edit-2` icon is renamed to `pen` to make the name more generic, since it is just a pen icon.
Beside aliases names lucide also includes prefixed and suffixed names to use within your project. This is to prevent import name collisions with other libraries or your own code.
```tsx
// These are all the same icon
import {
Home,
HomeIcon,
LucideHome,
} from "lucide-react";
```
## Choosing import name style
To be consistent in your imports or want to change the autocompletion of Lucide icons in your IDE there an option to able the choose the import name style you want.
This can be done by creating a custom module declaration file to override the lucide imports and turning off the autocomplete in your IDE.
### Turn off autocomplete in your IDE
```json [.vscode/settings.json]
{
"typescript.preferences.autoImportFileExcludePatterns": [
"lucide-react", // or
"lucide-preact", // or
"lucide-react-native", // or
"lucide-vue-next",
]
}
```
### Create a custom module declaration file
Only available for `lucide-react`, `lucide-preact`, `lucide-react-native`, `lucide-vue-next` package.
This will enable you to choose the import name style you want to use in your project.
::: code-group
```ts [React]
declare module "lucide-react" {
// Prefixed import names
export * from "lucide-react/dist/lucide-react.prefixed";
// or
// Suffixed import names
export * from "lucide-react/dist/lucide-react.suffixed";
}
```
```ts [Vue]
declare module "lucide-vue-next" {
// Prefixed import names
export * from "lucide-vue-next/dist/lucide-vue-next.prefixed";
// or
// Suffixed import names
export * from "lucide-vue-next/dist/lucide-vue-next.suffixed";
}
```
```ts [Preact]
declare module "lucide-preact" {
// Prefixed import names
export * from "lucide-preact/dist/lucide-preact.prefixed";
// or
// Suffixed import names
export * from "lucide-preact/dist/lucide-preact.suffixed";
}
```
```ts [React Native]
declare module "lucide-react-native" {
// Prefixed import names
export * from "lucide-react-native/dist/lucide-react-native.prefixed";
// or
// Suffixed import names
export * from "lucide-react-native/dist/lucide-react-native.suffixed";
}
```
:::
Place this in your project root or in a folder where your tsconfig.json is located, or locate it in your defined type directory.
Easiest way is to create a `@types` folder in your project root and name the file `[package-name].d.ts`.
### Import name styles
| Import Style | Available imports | Declaration file import |
| ------------- | --------------------------- | ----------------------- |
| Default | Home, HomeIcon, LucideHome | |
| Prefixed | LucideHome | [package].prefixed |
| Suffixed | HomeIcon | [package].suffixed |

View File

@@ -1,18 +0,0 @@
---
title: Affinity Designer Template Guide
---
# Affinity Designer Template Guide
This guide describes how to use the Affinity Designer template for Lucide.
## General Workflow
>Attention: By default, Affinity Designer sets the unit for stroke to points. Make sure that it is set to pixel. To do this, open `Preferences > User Interface`. Under `Decimal Places for Unit Types`, uncheck `Show Lines in points`.
1. Download and open the [Affinity Designer template](https://github.com/lucide-icons/lucide/blob/main/docs/public/templates/affinity_designer.aftemplate).
2. Follow the [Icon Design Principles](icon-design-guide.md) while you use the template (to ensure integrity with the Lucide icon pack).
3. Export the file as SVG (`File > Export`). Make sure that _Rastering_ is set to _Nothing_, _Export text as curves_ is checked (hopefully, you won't need this), _Use hex colors_ is checked, and _Flatten transforms_ is checked.
![SVG export options in Affinity Designer](../../images/affinity-designer-export-options.png?raw=true)
4. Optimize the exported SVG file further with [SVGOMG](https://jakearchibald.github.io/svgomg/) or [`svgo`](https://github.com/svg/svgo) (using `svgo --multipass exported_icon.svg`).

View File

@@ -52,7 +52,7 @@ Here are rules that should be followed to keep quality and consistency when maki
![optical-volume-high](../../images/optical-volume-high.svg?raw=true "optical-volume-high") ![optical-volume-high](../../images/optical-volume-high.svg?raw=true "optical-volume-high")
**Tip:** place your icon next to the circle or square icon and blur them both; your icon should not feel much darker than the base shape. **Tip:** place your icon next to circle or square and blur them both; your icon should not feel much darker than the base shape.
### 10. Icons should be visually centered by their center of gravity. ### 10. Icons should be visually centered by their center of gravity.
@@ -76,7 +76,7 @@ Here are rules that should be followed to keep quality and consistency when maki
![curvature-uneven](../../images/curvature-uneven.svg?raw=true "curvature-uneven") ![curvature-uneven](../../images/curvature-uneven.svg?raw=true "curvature-uneven")
**Tip:** make sure to use arcs or quadratic curves. When using cubic curves control points should have mirrored angles for smooth curves. **Tip:** make sure to use arcs or quadratic curves, when using cubic curves control points should have mirrored angles for smooth curves.
### 13. Icons should aim to be pixel perfect so that they will be sharp on low DPI displays. ### 13. Icons should aim to be pixel perfect so that they will be sharp on low DPI displays.
@@ -109,8 +109,8 @@ Here are rules that should be followed to keep quality and consistency when maki
7. Icons depicting multiple elements (e.g. a person and a circle) of different sizes must list these elements in decreasing order of size.\ 7. Icons depicting multiple elements (e.g. a person and a circle) of different sizes must list these elements in decreasing order of size.\
For example: if the circle is bigger, it should be `circle-person`, if the person is bigger, it should be `person-circle`. For example: if the circle is bigger, it should be `circle-person`, if the person is bigger, it should be `person-circle`.
8. Icons depicting multiple elements of roughly equal sizes (e.g. a `ruler` and a `pencil`) must list these elements front to back in case one element is in front of the other, otherwise in English reading order (top to bottom, left to right).\ 8. Icons depicting multiple elements of roughly equal sizes (e.g. a `ruler` and a `pencil`) must list these elements in English reading order.\
For example: if the `pencil` is either in front of, above or left of `ruler`, it should be `pencil-ruler`, otherwise, it should be `ruler-pencil`. For example: if the `pencil` is either above or left of `ruler`, it should be `pencil-ruler`, otherwise, it should be `ruler-pencil`.
9. Icons depicting some sort of variation of an element must use the `[element]-[modifier]` naming scheme, with modifiers being applied to each element respectively.\ 9. Icons depicting some sort of variation of an element must use the `[element]-[modifier]` naming scheme, with modifiers being applied to each element respectively.\
For example: a dashed circle must be named `circle-dashed`, not `dashed-circle`, and in coordination with the previous guidelines, a dashed circle containing a broken heart would be named `circle-dashed-heart-broken`, due to the heart being smaller than the circle. For example: a dashed circle must be named `circle-dashed`, not `dashed-circle`, and in coordination with the previous guidelines, a dashed circle containing a broken heart would be named `circle-dashed-heart-broken`, due to the heart being smaller than the circle.
@@ -142,7 +142,7 @@ For each icon these attributes are applied, corresponding to the above rules.
### Minify paths ### Minify paths
The code of paths can sometimes get quite large. To reduce file size we like to minify the code. The code of paths can sometimes get quite large. To reduce file size we like to minify the code.
We recommend to use [SVGOMG](https://jakearchibald.github.io/svgomg/) to minify paths to 2 points of precision. We recommend to use the [SVGOMG](https://jakearchibald.github.io/svgomg/) to minify paths to 2 points of precision.
### Allowed elements ### Allowed elements

View File

@@ -12,8 +12,7 @@ Lucide is an open-source icon library that provides 1000+ vector (svg) files for
## Available Icons ## Available Icons
Lucide contains icons with different variants and states, allowing users to choose the most suitable icon for their needs. And if a desired icon isn't available yet, users can open a design request, and the Lucide community contributors will help provide new icons. With more icons to choose from, users have more options to work with in their projects. Lucide contains icons with different variants and states, allowing users to choose the most suitable icon for their needs. And if a desired icon isn't available yet, users can open a design request, and the Lucide community contributors will help provide new icons. With more icons to choose from, users have more options to work with in their projects.
Complete Set of Icons
### Complete Set of Icons
As new applications with specific features arise, Lucide aims to provide a complete set of icons for every project. The community follows a set of design rules when designing new icons. These rules maintain standards for the icons, such as recognizability, consistency in style, and readability at all sizes. While creativity is valued in new icons, recognizable design conventions are important to ensure that the icons are easily identifiable by users. As new applications with specific features arise, Lucide aims to provide a complete set of icons for every project. The community follows a set of design rules when designing new icons. These rules maintain standards for the icons, such as recognizability, consistency in style, and readability at all sizes. While creativity is valued in new icons, recognizable design conventions are important to ensure that the icons are easily identifiable by users.
@@ -21,15 +20,9 @@ As new applications with specific features arise, Lucide aims to provide a compl
In addition to design, code is also important. Assets like icons can significantly increase bandwidth usage in web projects. With the growing internet, Lucide has a responsibility to keep their assets as small as possible. To achieve this, Lucide uses SVG compression and specific code architecture for tree-shaking abilities. After tree-shaking, you only ship the icons you used, which helps to keep software distribution size to a minimum. In addition to design, code is also important. Assets like icons can significantly increase bandwidth usage in web projects. With the growing internet, Lucide has a responsibility to keep their assets as small as possible. To achieve this, Lucide uses SVG compression and specific code architecture for tree-shaking abilities. After tree-shaking, you only ship the icons you used, which helps to keep software distribution size to a minimum.
## Accessibility
Icons are pictures that show what something means without using words. They can be very helpful because they can quickly give information.
However, not everyone can understand them easily. Read more about [how to use Lucide in an accessible way](./advanced/accessibility.md).
## Official Packages ## Official Packages
Lucide's official packages are designed to work on different platforms, making it easier for users to integrate icons into their projects. The packages are available for various technologies, including [Web (Vanilla)](https://lucide.dev/guide/packages/lucide), [React](https://lucide.dev/guide/packages/lucide-react), [React Native](https://lucide.dev/guide/packages/lucide-react-native), [Vue](https://lucide.dev/guide/packages/lucide-vue), [Vue 3](https://lucide.dev/guide/packages/lucide-vue-next), [Svelte](https://lucide.dev/guide/packages/lucide-svelte), [Preact](https://lucide.dev/guide/packages/lucide-preact), [Solid](https://lucide.dev/guide/packages/lucide-solid), [Angular](https://lucide.dev/guide/packages/lucide-angular), [NodeJS](https://lucide.dev/guide/packages/lucide-static#nodejs) and [Flutter](https://lucide.dev/guide/packages/lucide-flutter). Lucide's official packages are designed to work on different platforms, making it easier for users to integrate icons into their projects. The packages are available for various technologies, including [Web (Vanilla)](https://lucide.dev/guide/packages/lucide), [React](https://lucide.dev/guide/packages/lucide-react), [React Native](https://lucide.dev/guide/packages/lucide-react-native), [Vue](https://lucide.dev/guide/packages/lucide-vue), [Vue 3](https://lucide.dev/guide/packages/lucide-vue-next), [Svelte](https://lucide.dev/guide/packages/lucide-svelte),[Preact](https://lucide.dev/guide/packages/lucide-preact), [Solid](https://lucide.dev/guide/packages/lucide-solid), [Angular](https://lucide.dev/guide/packages/lucide-angular), [NodeJS](https://lucide.dev/guide/packages/lucide-static#nodejs) and [Flutter](https://lucide.dev/guide/packages/lucide-flutter).
## Community ## Community
If you have any questions about Lucide, feel free to reach out to the community. You can find them on [GitHub](https://github.com/lucide-icons/lucide) and [Discord](https://discord.gg/EH6nSts). If you have any questions about Lucide, feel free to reach out to the community. You can find them on [GitHub](https://github.com/lucide-icons/lucide) and [Discord](https://discord.gg/EH6nSts).

View File

@@ -22,10 +22,6 @@ yarn add lucide
npm install lucide npm install lucide
``` ```
```sh [bun]
bun add lucide
```
::: :::
For more details, see the [documentation](packages/lucide.md). For more details, see the [documentation](packages/lucide.md).
@@ -48,10 +44,6 @@ yarn add lucide-react
npm install lucide-react npm install lucide-react
``` ```
```sh [bun]
bun add lucide-react
```
::: :::
For more details, see the [documentation](packages/lucide-react.md). For more details, see the [documentation](packages/lucide-react.md).
@@ -75,10 +67,6 @@ yarn add lucide-vue-next
npm install lucide-vue-next npm install lucide-vue-next
``` ```
```sh [bun]
bun add lucide-vue-next
```
::: :::
For more details, see the [documentation](packages/lucide-vue-next.md). For more details, see the [documentation](packages/lucide-vue-next.md).
@@ -102,10 +90,6 @@ yarn add lucide-svelte
npm install lucide-svelte npm install lucide-svelte
``` ```
```sh [bun]
bun add lucide-svelte
```
::: :::
For more details, see the [documentation](packages/lucide-svelte.md). For more details, see the [documentation](packages/lucide-svelte.md).
@@ -128,10 +112,6 @@ yarn add lucide-solid
npm install lucide-solid npm install lucide-solid
``` ```
```sh [bun]
bun add lucide-solid
```
::: :::
For more details, see the [documentation](packages/lucide-solid.md). For more details, see the [documentation](packages/lucide-solid.md).
@@ -154,10 +134,6 @@ yarn add lucide-angular
npm install lucide-angular npm install lucide-angular
``` ```
```sh [bun]
bun add lucide-angular
```
::: :::
For more details, see the [documentation](packages/lucide-angular.md). For more details, see the [documentation](packages/lucide-angular.md).
@@ -180,10 +156,6 @@ yarn add lucide-preact
npm install lucide-preact npm install lucide-preact
``` ```
```sh [bun]
bun add lucide-preact
```
::: :::
## Static usage ## Static usage
@@ -204,10 +176,6 @@ yarn add lucide-static
npm install lucide-static npm install lucide-static
``` ```
```sh [bun]
bun add lucide-static
```
::: :::
For more details, see the [documentation](packages/lucide-static.md). For more details, see the [documentation](packages/lucide-static.md).

View File

@@ -18,10 +18,6 @@ yarn add lucide-angular
npm install lucide-angular npm install lucide-angular
``` ```
```sh [bun]
bun add lucide-angular
```
::: :::
## How to use ## How to use
@@ -41,23 +37,6 @@ import { LucideAngularModule, File, Home, Menu, UserCheck } from 'lucide-angular
export class AppModule { } export class AppModule { }
``` ```
or using standalone version:
```js
import { Component } from '@angular/core';
import { LucideAngularModule, FileIcon } from 'lucide-angular';
@Component({
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
imports: [LucideAngularModule]
})
export class AppComponent {
readonly FileIcon = FileIcon;
}
```
### Step 2: Use the icons in templates ### Step 2: Use the icons in templates
Within your templates you may now use one of the following component tags to insert an icon: Within your templates you may now use one of the following component tags to insert an icon:
@@ -68,13 +47,6 @@ Within your templates you may now use one of the following component tags to ins
<i-lucide name="menu" class="my-icon"></i-lucide> <i-lucide name="menu" class="my-icon"></i-lucide>
<span-lucide name="user-check" class="my-icon"></span-lucide> <span-lucide name="user-check" class="my-icon"></span-lucide>
``` ```
for standalone
```html
<lucide-angular [img]="FileIcon" class="my-icon"></lucide-angular>
<lucide-icon [img]="FileIcon" class="my-icon"></lucide-icon>
<i-lucide [img]="FileIcon" class="my-icon"></i-lucide>
<span-lucide [img]="FileIcon" class="my-icon"></span-lucide>
```
### Props ### Props
@@ -143,20 +115,3 @@ import { icons } from 'lucide-angular';
LucideAngularModule.pick(icons) LucideAngularModule.pick(icons)
``` ```
## With Lucide lab or custom icons
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
They can be used in the same way as the official icons.
```js
import { LucideAngularModule } from 'lucide-angular';
import { burger } from '@lucide/lab';
@NgModule({
imports: [
LucideAngularModule.pick({ burger })
]
})
export class AppModule { }
```

Some files were not shown because too many files have changed in this diff Show More