mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-10 13:18:25 +00:00
Compare commits
64
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45b0e148db | ||
|
|
ee187700a6 | ||
|
|
2b9bbdf444 | ||
|
|
8a26c9eaa5 | ||
|
|
4b3f93d9d0 | ||
|
|
0c0e3ab3ae | ||
|
|
b8671171b5 | ||
|
|
3efde520fc | ||
|
|
805a5e8d32 | ||
|
|
dcc57e4748 | ||
|
|
b32203c2f0 | ||
|
|
5e87c4b846 | ||
|
|
12691c4568 | ||
|
|
98331e2870 | ||
|
|
c3f2266801 | ||
|
|
09aa9c5c62 | ||
|
|
00300d31bd | ||
|
|
9d9418a25d | ||
|
|
309f440724 | ||
|
|
05eacffce6 | ||
|
|
41991a82aa | ||
|
|
3cb1625e46 | ||
|
|
ec6d30d82b | ||
|
|
b25472d265 | ||
|
|
b8718e70f6 | ||
|
|
aace268b9b | ||
|
|
e8a2c60b75 | ||
|
|
5a92b9ba26 | ||
|
|
66d8f9fc39 | ||
|
|
e042fec3b5 | ||
|
|
c870b3c54a | ||
|
|
f53e5bfff0 | ||
|
|
f06ac67e33 | ||
|
|
b0b1a4f8ab | ||
|
|
3a37bfa5cd | ||
|
|
16fb55f52c | ||
|
|
951813ce76 | ||
|
|
ba6751ac45 | ||
|
|
99c1037550 | ||
|
|
e32a4a2c10 | ||
|
|
5d9cc1580a | ||
|
|
d9f72b2525 | ||
|
|
0d0408c43b | ||
|
|
55d30bda04 | ||
|
|
f90f10a24a | ||
|
|
f3e36fb0d7 | ||
|
|
4faa47c0b0 | ||
|
|
e593a44ad2 | ||
|
|
dba8fb7932 | ||
|
|
a87cecebe3 | ||
|
|
3b9ea6d087 | ||
|
|
076b52527f | ||
|
|
58ef6830c6 | ||
|
|
f032bed22e | ||
|
|
266fd1c717 | ||
|
|
357a50175e | ||
|
|
a50c54cfff | ||
|
|
bd95db1af8 | ||
|
|
6b474e16d4 | ||
|
|
566032a95d | ||
|
|
042bf77119 | ||
|
|
5b8975c9a3 | ||
|
|
7eb8afe2f8 | ||
|
|
08b2fd2455 |
@@ -13,7 +13,7 @@ The `contributors` property is a required array of GitHub usernames for the peop
|
||||
|
||||
The `tags` property is an array of strings that describe the icon and can be used for searching.
|
||||
Validate the tags against the `icon.schema.json` to ensure they are correctly formatted and adhere to the defined structure.
|
||||
Provide tag suggestions based on the name of the icon and the use cases provided in the PR description. Use the existing tags in the repository as a reference for consistency and to avoid duplicates. Don't suggest words like: 'icon' and preferably use single words. Tags should always be in lowercase, and may also contain spaces (e.g. `magnifying glass`). The name of icon should not be included in the tags, as it is already specified.
|
||||
Provide tag suggestions based on the name of the icon and the use cases provided in the PR description. Use the existing tags in the repository as a reference for consistency and to avoid duplicates. Don't suggest words like: 'icon' and preferably use single words. Tags should always be in lowercase, and may also contain spaces (e.g. `magnifying glass`). Don't include the full icon name, its space-separated form, or any individual part of its kebab-case name because those terms are already searchable. For example, don't suggest `mail-search`, `mail search`, `mail`, or `search` for the `mail-search` icon.
|
||||
|
||||
## Categories
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'packages/lucide-solid/*'
|
||||
- 'packages/solid/*'
|
||||
|
||||
# For changes in the Lucide astro package
|
||||
🚀 astro package:
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
|
||||
+58
-42
@@ -5,7 +5,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- icons/**/*.svg
|
||||
- icons/*.svg
|
||||
|
||||
schedule:
|
||||
- cron: '0 6 * * *'
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
@@ -26,39 +29,63 @@ on:
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check-dispatch-gate:
|
||||
if: github.repository == 'lucide-icons/lucide' &&
|
||||
startsWith(github.event.head_commit.message, 'feat(icons)') &&
|
||||
github.event_name != 'workflow_dispatch'
|
||||
release-gate:
|
||||
concurrency:
|
||||
group: ci-${{ github.event_name == 'schedule' && github.ref || 'release-gated' }}
|
||||
cancel-in-progress: true
|
||||
if: github.repository == 'lucide-icons/lucide' && github.event_name != 'workflow_dispatch'
|
||||
environment: ${{ github.event_name == 'schedule' && 'Icon Release' || 'Icon Release Gated' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Approve release
|
||||
run: echo 'Release approved'
|
||||
|
||||
prepare-release:
|
||||
needs: release-gate
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
contents: read # Required to check if a new release is needed
|
||||
outputs:
|
||||
RELEASE_ENVIRONMENT: ${{ steps.environment.outputs.RELEASE_ENVIRONMENT }}
|
||||
SHOULD_RELEASE: ${{ steps.changes.outputs.SHOULD_RELEASE }}
|
||||
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch tags
|
||||
run: git fetch --all --tags
|
||||
- name: Get latest tag
|
||||
id: latest-tag
|
||||
run: echo "LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check if latest tag was created today
|
||||
id: environment
|
||||
- name: Log latest tag
|
||||
run: echo '${{ steps.latest-tag.outputs.LATEST_TAG }}'
|
||||
|
||||
- name: Check for releasable changes
|
||||
id: changes
|
||||
env:
|
||||
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
|
||||
run: |
|
||||
LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)
|
||||
TAG_DATE=$(git log -1 --format=%cs "$LATEST_TAG")
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
|
||||
if [ "$TAG_DATE" == "$TODAY" ]; then
|
||||
echo "::warning::A release tag already exists for today ($TODAY): $LATEST_TAG. Manual approval will be required to proceed."
|
||||
echo "RELEASE_ENVIRONMENT=Icon Release Gated" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "RELEASE_ENVIRONMENT=Icon Release" >> $GITHUB_OUTPUT
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
echo "::error::No version tag found to release from."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Latest version tag: $LATEST_TAG"
|
||||
|
||||
CHANGES=$(git diff --name-only "$LATEST_TAG" HEAD -- ':(glob)icons/*.svg')
|
||||
|
||||
if [ -z "$CHANGES" ]; then
|
||||
echo "::notice::No icon SVGs changed since $LATEST_TAG. Skipping release."
|
||||
echo "SHOULD_RELEASE=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "$CHANGES"
|
||||
echo "SHOULD_RELEASE=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
create-release:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
needs: check-dispatch-gate
|
||||
environment: ${{ needs.check-dispatch-gate.outputs.RELEASE_ENVIRONMENT }}
|
||||
if: github.repository == 'lucide-icons/lucide' && needs.prepare-release.outputs.SHOULD_RELEASE == 'true'
|
||||
needs: prepare-release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # Required to create the release and tag
|
||||
@@ -67,7 +94,7 @@ jobs:
|
||||
RELEASE_URL: ${{ steps.create-release.outputs.url }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -76,37 +103,26 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Fetch tags
|
||||
run: git fetch --all --tags
|
||||
|
||||
- name: Get latest tag
|
||||
id: latest-tag
|
||||
run: echo "LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Log latest tag
|
||||
run: echo '${{ steps.latest-tag.outputs.LATEST_TAG }}'
|
||||
|
||||
- name: Check if we can patch
|
||||
run: pnpm semver $LATEST_TAG -i minor
|
||||
run: pnpm semver "$LATEST_TAG" -i minor
|
||||
env:
|
||||
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
|
||||
LATEST_TAG: ${{ needs.prepare-release.outputs.LATEST_TAG }}
|
||||
|
||||
- name: Create new version
|
||||
id: new-version
|
||||
run: echo "NEW_VERSION=$(pnpm semver $LATEST_TAG -i minor)" >> $GITHUB_OUTPUT
|
||||
run: echo "NEW_VERSION=$(pnpm semver "$LATEST_TAG" -i minor)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
|
||||
LATEST_TAG: ${{ needs.prepare-release.outputs.LATEST_TAG }}
|
||||
|
||||
- name: Check output
|
||||
run: |
|
||||
echo '${{ steps.new-version.outputs.NEW_VERSION }}'
|
||||
echo '${{ steps.change-log.outputs.CHANGE_LOG }}'
|
||||
run: echo '${{ steps.new-version.outputs.NEW_VERSION }}'
|
||||
|
||||
- name: Create Release
|
||||
id: create-release
|
||||
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
|
||||
with:
|
||||
tag_name: ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
target_commitish: ${{ github.sha }}
|
||||
name: Version ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
generate_release_notes: true
|
||||
|
||||
@@ -135,7 +151,7 @@ jobs:
|
||||
post-release:
|
||||
name: Trigger post-release automation
|
||||
if: >-
|
||||
always() &&
|
||||
!cancelled() &&
|
||||
github.repository == 'lucide-icons/lucide' &&
|
||||
(needs.start-release.result == 'success' || needs.dispatch-release.result == 'success')
|
||||
needs: [create-release, start-release, dispatch-release]
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Docs Checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- docs/**
|
||||
- .github/workflows/docs.yml
|
||||
- package.json
|
||||
- pnpm-workspace.yaml
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
# Release metadata is generated by diffing every release tag
|
||||
fetch-depth: 0
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build docs
|
||||
run: pnpm --filter @lucide/docs build
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
# Release metadata is generated by diffing every release tag
|
||||
fetch-depth: 0
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Generate docs data
|
||||
run: pnpm --filter @lucide/docs run "/^prebuild:.*/"
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter @lucide/docs typecheck
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
@@ -46,3 +46,19 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter @lucide/astro test
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter @lucide/astro typecheck
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -37,3 +37,19 @@ jobs:
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter build-font typecheck
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -29,3 +29,19 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter @lucide/lab build
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter @lucide/lab typecheck
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -31,3 +31,19 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter lucide-preact test
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter lucide-preact typecheck
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -47,3 +47,19 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter lucide-react-native test
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter lucide-react-native typecheck
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -70,3 +70,19 @@ jobs:
|
||||
|
||||
- name: Run integration checks
|
||||
run: pnpm test:integrations:react
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter lucide-react typecheck
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -27,4 +27,20 @@ jobs:
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter lucide-react test
|
||||
run: pnpm --filter @lucide/shared test
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter @lucide/shared typecheck
|
||||
|
||||
@@ -7,9 +7,12 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/lucide-solid/**
|
||||
- integrations/lucide-solid/**
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- package.json
|
||||
- pnpm-workspace.yaml
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
@@ -20,7 +23,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -36,7 +39,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -47,3 +50,38 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter lucide-solid test
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter lucide-solid typecheck
|
||||
|
||||
integration:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Install Chromium
|
||||
run: pnpm --filter @lucide/integration-solid-vite exec playwright install --with-deps chromium
|
||||
|
||||
- name: Run integration checks
|
||||
run: pnpm test:integrations:solid
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -26,3 +26,19 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter lucide-static build
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter lucide-static typecheck
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -61,5 +61,5 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Test
|
||||
- name: Typecheck
|
||||
run: pnpm --filter @lucide/svelte typecheck
|
||||
|
||||
@@ -10,6 +10,9 @@ on:
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- integrations/lucide-vue/**
|
||||
- package.json
|
||||
- pnpm-workspace.yaml
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
@@ -20,7 +23,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -36,7 +39,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -47,3 +50,38 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter @lucide/vue test
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter @lucide/vue typecheck
|
||||
|
||||
integration:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Install Chromium
|
||||
run: pnpm --filter @lucide/integration-vue-vite exec playwright install --with-deps chromium
|
||||
|
||||
- name: Run integration checks
|
||||
run: pnpm test:integrations:vue
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -46,3 +46,19 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter lucide test
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm --filter lucide typecheck
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
repository: lucide-icons/lucide
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0 # Required to diff the lab icons against the previous tag
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
|
||||
@@ -53,12 +53,13 @@ jobs:
|
||||
'@lucide/angular',
|
||||
'@lucide/astro',
|
||||
'@lucide/icons',
|
||||
'@lucide/solid',
|
||||
'@lucide/svelte',
|
||||
'@lucide/vue',
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -96,7 +97,7 @@ jobs:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
@@ -127,7 +128,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
name: Lucide Solid 2 Checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/solid/**
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- integrations/lucide-solid-2/**
|
||||
- package.json
|
||||
- pnpm-workspace.yaml
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter @lucide/solid build
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter @lucide/solid test
|
||||
|
||||
integration:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Install Chromium
|
||||
run: pnpm --filter @lucide/integration-solid-2-vite exec playwright install --with-deps chromium
|
||||
|
||||
- name: Run integration checks
|
||||
run: pnpm test:integrations:solid-2
|
||||
@@ -1,6 +1,7 @@
|
||||
.DS_Store
|
||||
.idea
|
||||
.next
|
||||
.astro
|
||||
.obsidian
|
||||
.now
|
||||
.cache
|
||||
@@ -18,6 +19,9 @@ stats
|
||||
integrations/**/.react-router
|
||||
integrations/**/.vitest-attachments
|
||||
integrations/**/__screenshots__
|
||||
integrations/**/.output
|
||||
integrations/**/.nitro
|
||||
integrations/**/.nuxt
|
||||
outlined
|
||||
lucide-font
|
||||
packages/**/src/icons/*.js
|
||||
|
||||
@@ -23,8 +23,12 @@ packages/svelte/.svelte-kit
|
||||
integrations/**/.react-router
|
||||
integrations/**/next-env.d.ts
|
||||
integrations/**/routeTree.gen.ts
|
||||
integrations/**/file-routes.d.ts
|
||||
integrations/**/.vitest-attachments
|
||||
integrations/**/__screenshots__
|
||||
integrations/**/.output
|
||||
integrations/**/.nitro
|
||||
integrations/**/.nuxt
|
||||
|
||||
# lucide-svelte
|
||||
packages/svelte/src/icons/*.svelte
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { eventHandler, setResponseHeader } from 'h3';
|
||||
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 }]) => {
|
||||
const newIconNode = iconNode.map(([name, { key, ...attrs }]) => {
|
||||
return [name, attrs];
|
||||
});
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ export default eventHandler((event) => {
|
||||
<Diff
|
||||
oldSrc={oldSrc}
|
||||
newSrc={newSrc}
|
||||
showGrid
|
||||
height={height}
|
||||
width={width}
|
||||
>
|
||||
|
||||
@@ -48,7 +48,6 @@ export default eventHandler((event) => {
|
||||
<Diff
|
||||
oldSrc={oldSrc}
|
||||
newSrc={newSrc}
|
||||
showGrid
|
||||
height={height}
|
||||
width={width}
|
||||
>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { eventHandler, getQuery, setResponseHeader } from 'h3';
|
||||
import iconNodes from '../../data/iconNodes';
|
||||
import { IconNodeWithKeys } from '../../theme/types';
|
||||
|
||||
export default eventHandler((event) => {
|
||||
const query = getQuery(event);
|
||||
@@ -19,7 +18,7 @@ export default eventHandler((event) => {
|
||||
return [name, iconNode];
|
||||
}
|
||||
|
||||
const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => {
|
||||
const newIconNode = iconNode.map(([name, { key, ...attrs }]) => {
|
||||
return [name, attrs];
|
||||
});
|
||||
|
||||
|
||||
@@ -18,11 +18,12 @@ export default eventHandler((event) => {
|
||||
return sendError(event, error);
|
||||
}
|
||||
|
||||
const width = getQuery(event).width || undefined;
|
||||
const height = getQuery(event).height || undefined;
|
||||
const color = getQuery(event).color || undefined;
|
||||
const strokeWidth = getQuery(event).strokeWidth || undefined;
|
||||
const background = getQuery(event).background || undefined;
|
||||
const query = getQuery<Record<string, string | undefined>>(event);
|
||||
const width = query.width || undefined;
|
||||
const height = query.height || undefined;
|
||||
const color = query.color || undefined;
|
||||
const strokeWidth = query.strokeWidth || undefined;
|
||||
const background = query.background || undefined;
|
||||
|
||||
const LucideIcon = createLucideIcon(params.iconName, iconNode);
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ const defaultSandpackCSS = await readFile(
|
||||
fileURLToPath(new URL('./theme/sandpack-default.css', import.meta.url)),
|
||||
'utf-8',
|
||||
);
|
||||
const iconStyleHeadScript = await readFile(
|
||||
fileURLToPath(new URL('./theme/iconStyleHead.js', import.meta.url)),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const title = 'Lucide';
|
||||
const socialTitle = 'Lucide Icons';
|
||||
@@ -84,7 +88,10 @@ export default defineConfig({
|
||||
}) as unknown as UserConfig['vite']['plugins'][0],
|
||||
],
|
||||
},
|
||||
head: getHeadConfig({ title, description, socialTitle }),
|
||||
head: [
|
||||
...getHeadConfig({ title, description, socialTitle }),
|
||||
['script', {}, iconStyleHeadScript],
|
||||
],
|
||||
transformPageData,
|
||||
themeConfig: {
|
||||
logo: {
|
||||
@@ -97,7 +104,7 @@ export default defineConfig({
|
||||
{
|
||||
text: 'Resources',
|
||||
items: [
|
||||
...resourcesSidebar[0].items,
|
||||
...resourcesSidebar[0].items.map(({ text, link }) => ({ text, link })),
|
||||
{ text: 'How to use icons', link: '/how-to/' },
|
||||
{ text: 'Contributing icons', link: '/contribute/icons/' },
|
||||
],
|
||||
|
||||
@@ -85,8 +85,26 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-react-native": {
|
||||
"@lucide/solid": {
|
||||
"order": 5,
|
||||
"icon": "solid",
|
||||
"docsAlias": "solid",
|
||||
"packageDirname": "solid",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/v/@lucide/solid",
|
||||
"href": "https://www.npmjs.com/package/@lucide/solid"
|
||||
},
|
||||
{
|
||||
"alt": "npm",
|
||||
"src": "https://img.shields.io/npm/dw/@lucide/solid",
|
||||
"href": "https://www.npmjs.com/package/@lucide/solid"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lucide-react-native": {
|
||||
"order": 6,
|
||||
"icon": "react-native",
|
||||
"docsAlias": "react-native",
|
||||
"shields": [
|
||||
@@ -103,7 +121,7 @@
|
||||
]
|
||||
},
|
||||
"@lucide/angular": {
|
||||
"order": 6,
|
||||
"order": 7,
|
||||
"icon": "angular",
|
||||
"docsAlias": "angular",
|
||||
"packageDirname": "angular",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { type JSX } from 'react';
|
||||
|
||||
interface BackdropProps {
|
||||
src: string;
|
||||
|
||||
@@ -397,10 +397,14 @@ interface SvgPreviewProps extends React.SVGProps<SVGSVGElement> {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const SvgPreview = (
|
||||
{ src, children, height = 24, width = 24, showGrid = false, ...props }: SvgPreviewProps,
|
||||
ref,
|
||||
) => {
|
||||
const SvgPreview = ({
|
||||
src,
|
||||
children,
|
||||
height = 24,
|
||||
width = 24,
|
||||
showGrid = false,
|
||||
...props
|
||||
}: SvgPreviewProps) => {
|
||||
const subGridSize =
|
||||
Math.max(height, width) % 3 === 0
|
||||
? Math.max(height, width) > 24
|
||||
@@ -413,7 +417,6 @@ const SvgPreview = (
|
||||
|
||||
return (
|
||||
<svg
|
||||
ref={ref}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={width}
|
||||
height={height}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { SVGProps } from 'react';
|
||||
import { getCommands } from './utils';
|
||||
|
||||
export type Point = { x: number; y: number };
|
||||
@@ -15,8 +14,8 @@ export type Path = {
|
||||
};
|
||||
|
||||
export type PathProps<
|
||||
RequiredProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
|
||||
NeverProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
|
||||
RequiredProps extends keyof React.SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
|
||||
NeverProps extends keyof React.SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
|
||||
> = Required<Pick<React.SVGProps<SVGElement & SVGRectElement & SVGCircleElement>, RequiredProps>> &
|
||||
Omit<
|
||||
React.SVGProps<SVGPathElement & SVGRectElement & SVGCircleElement>,
|
||||
|
||||
@@ -53,10 +53,7 @@ export const getCommands = (src: string) =>
|
||||
getNodes(src)
|
||||
.map(convertToPathNode)
|
||||
.flatMap(({ d, name }, idx) =>
|
||||
new SVGPathData(d)
|
||||
.toAbs()
|
||||
// @ts-expect-error `commands` is typed as a union of command types svgson does not narrow
|
||||
.commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })),
|
||||
new SVGPathData(d).toAbs().commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })),
|
||||
);
|
||||
|
||||
const getPaths = (src: string) => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Category, IconEntity } from '../theme/types';
|
||||
|
||||
const directory = path.join(process.cwd(), '../categories');
|
||||
|
||||
export function getAllCategoryFiles(): Category[] {
|
||||
export function getAllCategoryFiles(): Omit<Category, 'iconCount'>[] {
|
||||
const fileNames = fs.readdirSync(directory).filter((file) => path.extname(file) === '.json');
|
||||
|
||||
return fileNames.map((fileName) => {
|
||||
@@ -21,9 +21,9 @@ export function getAllCategoryFiles(): Category[] {
|
||||
}
|
||||
|
||||
export function mapCategoryIconCount(
|
||||
categories: Category[],
|
||||
categories: Omit<Category, 'iconCount'>[],
|
||||
icons: { categories: IconEntity['categories'] }[],
|
||||
) {
|
||||
): Category[] {
|
||||
return categories.map((category) => ({
|
||||
...category,
|
||||
iconCount: icons.reduce(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createLucideIcon } from 'lucide-react/src/lucide-react';
|
||||
import React from 'react';
|
||||
import createLucideIcon from 'lucide-react/src/createLucideIcon';
|
||||
import { type LucideProps, type IconNode } from 'lucide-react/src/types';
|
||||
import { IconEntity } from '../theme/types';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
|
||||
@@ -20,6 +20,8 @@ export async function getData(name: string) {
|
||||
const iconNode = iconNodes[name];
|
||||
|
||||
const releaseData = releaseMeta?.[name] ?? {
|
||||
fromFork: false,
|
||||
awaitingRelease: false,
|
||||
createdRelease: {
|
||||
version: '0.0.0',
|
||||
date: DATE_OF_FORK,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { dirname, isAbsolute, resolve } from 'node:path';
|
||||
import type MarkdownIt from 'markdown-it';
|
||||
import type { RenderRule } from 'markdown-it/lib/renderer.mjs';
|
||||
import type { MarkdownIt, RenderRule } from './types';
|
||||
import container from 'markdown-it-container';
|
||||
|
||||
type ContainerArgs = [typeof container, string, { render: RenderRule }];
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import type MarkdownIt from 'markdown-it';
|
||||
import type { RenderRule } from 'markdown-it/lib/renderer.mjs';
|
||||
import type Token from 'markdown-it/lib/token.mjs';
|
||||
import type { MarkdownIt, RenderRule, Token } from './types';
|
||||
import container from 'markdown-it-container';
|
||||
import sandpackTheme from '../theme/sandpackTheme.json';
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* />
|
||||
*/
|
||||
|
||||
import type MarkdownIt from 'markdown-it';
|
||||
import type { MarkdownIt } from './types';
|
||||
|
||||
type SnackParams = Record<string, string>;
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { MarkdownRenderer } from 'vitepress';
|
||||
|
||||
// VitePress 1.x renders with markdown-it 14, so derive the plugin types from its renderer
|
||||
// instead of the markdown-it 15 package installed in docs.
|
||||
export type MarkdownIt = MarkdownRenderer;
|
||||
export type RenderRule = NonNullable<MarkdownIt['renderer']['rules'][string]>;
|
||||
export type Token = Parameters<RenderRule>[0][number];
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import IconButton from './base/IconButton.vue';
|
||||
import Button from './base/Button.vue';
|
||||
import { x } from '../../data/iconNodes';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
@@ -65,16 +65,17 @@ function hidePopup() {
|
||||
}"
|
||||
class="ask-overlay"
|
||||
>
|
||||
<IconButton
|
||||
<Button
|
||||
@click="hidePopup"
|
||||
class="hide-button"
|
||||
>
|
||||
<Icon
|
||||
:iconNode="x"
|
||||
name="x"
|
||||
:size="20"
|
||||
absoluteStrokeWidth
|
||||
/>
|
||||
</IconButton>
|
||||
</Button>
|
||||
|
||||
<h2 class="title">
|
||||
We value your feedback!
|
||||
|
||||
+43
-3
@@ -1,7 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
href?: string;
|
||||
}>()
|
||||
|
||||
const isExternal = computed(() => {
|
||||
return props.href?.startsWith('http') ?? false
|
||||
})
|
||||
|
||||
const component = computed(() => {
|
||||
return props.href ? 'a' : 'button'
|
||||
})
|
||||
|
||||
const target = computed(() => {
|
||||
return isExternal.value ? '_blank' : undefined
|
||||
})
|
||||
|
||||
const rel = computed(() => {
|
||||
return isExternal.value ? 'noreferrer noopener' : undefined
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button v-bind="$attrs" class="icon-button">
|
||||
<component
|
||||
:is="component"
|
||||
v-bind="$attrs"
|
||||
:href="href"
|
||||
:target="target"
|
||||
:rel="rel"
|
||||
class="icon-button"
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -16,12 +47,21 @@
|
||||
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
|
||||
border-radius: 6px;
|
||||
background-color: var(--vp-c-bg-alt);
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
/* width: 56px;
|
||||
height: 56px; */
|
||||
font-size: 24px;
|
||||
gap: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
transition: color 0.1s, border-color 0.1s, background-color 0.1s;
|
||||
}
|
||||
|
||||
.icon-button span {
|
||||
padding: 0 4px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.icon-button:hover {
|
||||
border-color: var(--vp-button-alt-hover-border);
|
||||
color: var(--vp-button-alt-hover-text);
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -7,8 +6,6 @@ const props = defineProps<{
|
||||
id: string;
|
||||
}>();
|
||||
|
||||
const { isDark } = useData();
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
function limitHexInput(val: string) {
|
||||
@@ -22,7 +19,7 @@ function limitHexInput(val: string) {
|
||||
const value = computed({
|
||||
get: () => {
|
||||
if (props.modelValue == null || props.modelValue === 'currentColor') {
|
||||
return isDark.value ? '#ffffff' : '#000000';
|
||||
return null;
|
||||
}
|
||||
|
||||
return props.modelValue;
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface InputProps {
|
||||
import { ref, onMounted, nextTick, watch } from 'vue';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import { x } from '../../../data/iconNodes';
|
||||
import IconButton from './IconButton.vue';
|
||||
import Button from './Button.vue';
|
||||
|
||||
const props = withDefaults(defineProps<InputProps>(), {
|
||||
type: 'text',
|
||||
@@ -68,9 +68,9 @@ defineExpose({
|
||||
ref="input"
|
||||
:value="modelValue"
|
||||
v-bind="$attrs"
|
||||
@input="$emit('update:modelValue', $event.target.value)"
|
||||
@input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
|
||||
/>
|
||||
<IconButton
|
||||
<Button
|
||||
@click="onClear"
|
||||
v-if="type === 'search' && modelValue"
|
||||
class="clear-button"
|
||||
@@ -79,9 +79,10 @@ defineExpose({
|
||||
>
|
||||
<Icon
|
||||
:iconNode="x"
|
||||
name="x"
|
||||
:size="20"
|
||||
/>
|
||||
</IconButton>
|
||||
</Button>
|
||||
<kbd
|
||||
v-if="shortcut"
|
||||
class="shortcut"
|
||||
|
||||
@@ -46,6 +46,7 @@ const value = computed({
|
||||
<template #icon>
|
||||
<Icon
|
||||
:iconNode="search"
|
||||
name="search"
|
||||
class="search-icon"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -9,7 +9,8 @@ import RocketDuotoneIcon from '../home/RocketDuotoneIcon.ts';
|
||||
<p>
|
||||
<Icon
|
||||
:iconNode="RocketDuotoneIcon"
|
||||
size="18"
|
||||
name="rocket-duotone"
|
||||
:size="18"
|
||||
class="rocket-icon"
|
||||
/>
|
||||
<VPLink
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<script setup lang="ts">
|
||||
import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'
|
||||
|
||||
const props = defineProps<{
|
||||
tabs: string[];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TabGroup>
|
||||
<TabList class="tab-list">
|
||||
<template v-for="(tab, index) in props.tabs" :key="index">
|
||||
<Tab :id="`tab-${index}`" class="tab">{{ tab }}</Tab>
|
||||
</template>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<template v-for="(_, name) in $slots">
|
||||
<TabPanel>
|
||||
<slot :name="name" />
|
||||
</TabPanel>
|
||||
</template>
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.tab-list {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 32px;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: block;
|
||||
line-height: 32px;
|
||||
font-size: 16px;
|
||||
padding: 10px 12px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
transition: color 0.5s;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.tab[aria-selected="true"] {
|
||||
color: var(--vp-c-text-1);
|
||||
transition: color 0.25s;
|
||||
border-bottom: 2px solid var(--vp-c-brand);
|
||||
}
|
||||
|
||||
@media (min-width: 860px) {
|
||||
.tab-list {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.tab {
|
||||
font-size: 18px;
|
||||
padding: 10px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.tab {
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1152px) {
|
||||
.tab {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -34,7 +34,7 @@ const percentage = computed<string>(() => `${((Number(props.modelValue) - props.
|
||||
type="range"
|
||||
v-bind="$attrs"
|
||||
v-bind:value="modelValue"
|
||||
v-on:input="$emit('update:modelValue', Number($event.target.value))"
|
||||
v-on:input="$emit('update:modelValue', Number(($event.target as HTMLInputElement).value))"
|
||||
:min="min"
|
||||
:max="max"
|
||||
:step="step"
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { rotateCw } from '../../../data/iconNodes';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
import IconButton from './IconButton.vue';
|
||||
import Button from './Button.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<IconButton class="reset-button">
|
||||
<Button class="reset-button" aria-label="Reset style">
|
||||
<Icon
|
||||
:size="20"
|
||||
:iconNode="rotateCw"
|
||||
name="rotate-cw"
|
||||
/>
|
||||
</IconButton>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.reset-button {
|
||||
transition: ease-in-out 0.1s opacity;
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -28,6 +30,12 @@ import IconButton from './IconButton.vue';
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.reset-button:disabled {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* a rotate css animation keyframes */
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
|
||||
@@ -27,6 +27,7 @@ const selected = defineModel<{ name: string; value: string; icon?: string; iconD
|
||||
<slot name="end-icon">
|
||||
<Icon
|
||||
:iconNode="chevronsUpDown"
|
||||
name="chevrons-up-down"
|
||||
class="chevron-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
@@ -57,6 +58,7 @@ const selected = defineModel<{ name: string; value: string; icon?: string; iconD
|
||||
>
|
||||
<Icon
|
||||
:iconNode="check"
|
||||
name="check"
|
||||
class="check"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup>
|
||||
import { Switch } from '@headlessui/vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
modelValue: {
|
||||
@@ -9,14 +10,19 @@
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const isHydrated = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
isHydrated.value = true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Switch
|
||||
:model-value="modelValue"
|
||||
:model-value="isHydrated ? modelValue : false"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
class="switch"
|
||||
:class="{ enabled: modelValue }"
|
||||
:class="{ enabled: isHydrated && modelValue }"
|
||||
>
|
||||
<span class="thumb" />
|
||||
</Switch>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { type FunctionalComponent } from 'vue';
|
||||
import { type LucideProps } from '@lucide/vue';
|
||||
import { Sandpack } from 'sandpack-vue3';
|
||||
import { Sandpack, type SandpackProps } from 'sandpack-vue3';
|
||||
|
||||
defineProps<FunctionalComponent<LucideProps>>();
|
||||
const props = defineProps<SandpackProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sandpack />
|
||||
<Sandpack v-bind="props" />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -10,7 +10,14 @@ import { useLocalStorage } from '@vueuse/core';
|
||||
const { page } = useData();
|
||||
const router = useRouter();
|
||||
|
||||
const frameworks = [
|
||||
interface Framework {
|
||||
name: string;
|
||||
icon: string;
|
||||
iconDark?: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const frameworks: Framework[] = [
|
||||
{ name: 'Vanilla', icon: '/framework-logos/js.svg', value: '/guide/lucide/' },
|
||||
{ name: 'React', icon: '/framework-logos/react.svg', value: '/guide/react/' },
|
||||
{ name: 'Vue', icon: '/framework-logos/vue.svg', value: '/guide/vue/' },
|
||||
@@ -48,7 +55,7 @@ const selected = computed(() => {
|
||||
return current || (fallbackFramework.value?.value ? fallbackFramework.value : frameworks[0]);
|
||||
});
|
||||
|
||||
function onSelectFramework(item: { name: string; icon: string; iconDark?: string; value: string }) {
|
||||
function onSelectFramework(item: Framework) {
|
||||
fallbackFramework.value = item;
|
||||
if (item.value !== router.route.path) {
|
||||
const likeRoute = router.route.path.replace(selected.value.value, item.value);
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
import { ref, shallowRef } from 'vue';
|
||||
import { data } from './HomeHeroIconsCard.data';
|
||||
import { useRouter } from 'vitepress';
|
||||
import { motion, Variants, useScroll, useTransform } from 'motion-v';
|
||||
import { motion, useScroll, useTransform, type Options as MotionOptions } from 'motion-v';
|
||||
import LucideIcon from '../base/LucideIcon.vue';
|
||||
|
||||
// motion-v re-exports framer-motion's `Variants`, which doesn't match the `variants` prop of its components
|
||||
type Variants = MotionOptions['variants'];
|
||||
|
||||
const emit = defineEmits(['animation-complete']);
|
||||
|
||||
const MotionLucideIcon = motion.create(LucideIcon);
|
||||
@@ -129,7 +132,7 @@ const onAnimationComplete = (item) => {
|
||||
|
||||
const randomIndex = ref(Math.floor(Math.random() * 64));
|
||||
|
||||
const iconAnimationVariants = {
|
||||
const iconAnimationVariants: Variants = {
|
||||
initial: {
|
||||
animationName: 'end',
|
||||
opacity: 0,
|
||||
@@ -149,7 +152,7 @@ const iconAnimationVariants = {
|
||||
|
||||
const shrinkIconAnimation = ref('initial');
|
||||
|
||||
const shrinkIconVariants = {
|
||||
const shrinkIconVariants: Variants = {
|
||||
initial: { strokeWidth: 2 },
|
||||
shrinkIcons: (index) => ({
|
||||
animationName: 'shrinkIcons',
|
||||
|
||||
@@ -69,7 +69,7 @@ const pickIcons = [
|
||||
'stethoscope',
|
||||
];
|
||||
|
||||
export default {
|
||||
const loader = {
|
||||
async load() {
|
||||
const icons = Object.entries(iconNodes).map(([name, iconNode]) => ({ name, iconNode }));
|
||||
|
||||
@@ -96,3 +96,8 @@ export default {
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
declare const data: Awaited<ReturnType<typeof loader.load>>;
|
||||
|
||||
export { data };
|
||||
export default loader;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default {
|
||||
const loader = {
|
||||
async load() {
|
||||
const version = await fetch('https://api.github.com/repos/lucide-icons/lucide/releases/latest')
|
||||
.then((res) => {
|
||||
@@ -16,3 +16,8 @@ export default {
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
declare const data: Awaited<ReturnType<typeof loader.load>>;
|
||||
|
||||
export { data };
|
||||
export default loader;
|
||||
|
||||
@@ -15,10 +15,11 @@ import RocketDuotoneIcon from './RocketDuotoneIcon.ts';
|
||||
class="badge-special confetti-button animate"
|
||||
href="/guide/version-1"
|
||||
>
|
||||
<Icon :iconNode="RocketDuotoneIcon" />
|
||||
<Icon :iconNode="RocketDuotoneIcon" name="rocket-duotone" />
|
||||
Version 1
|
||||
<Icon
|
||||
:iconNode="moveRight"
|
||||
name="move-right"
|
||||
class=""
|
||||
/>
|
||||
</Badge>
|
||||
|
||||
@@ -1,59 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { syncRef, useCssVar } from '@vueuse/core'
|
||||
import HomeContainer from './HomeContainer.vue'
|
||||
import RangeSlider from '../base/RangeSlider.vue'
|
||||
import InputField from '../base/InputField.vue'
|
||||
import ColorPicker from '../base/ColorPicker.vue'
|
||||
import ResetButton from '../base/ResetButton.vue'
|
||||
import HomeIconCustomizerIcons from './HomeIconCustomizerIcons.vue'
|
||||
import Switch from '../base/Switch.vue'
|
||||
import { computed } from 'vue';
|
||||
import { useIconStyle } from '../../composables/useIconStyle';
|
||||
import HomeContainer from './HomeContainer.vue';
|
||||
import RangeSlider from '../base/RangeSlider.vue';
|
||||
import InputField from '../base/InputField.vue';
|
||||
import ColorPicker from '../base/ColorPicker.vue';
|
||||
import ResetButton from '../base/ResetButton.vue';
|
||||
import HomeIconCustomizerIcons from './HomeIconCustomizerIcons.vue';
|
||||
import Switch from '../base/Switch.vue';
|
||||
|
||||
const { color, strokeWidth, size, absoluteStrokeWidth, isCustomized, resetStyle } = useIconStyle();
|
||||
|
||||
const iconContainer = ref<HTMLElement | null>()
|
||||
const color = ref('currentColor')
|
||||
const strokeWidth = ref(2)
|
||||
const size = ref(24)
|
||||
const absoluteStrokeWidth = ref(false)
|
||||
|
||||
const colorCssVar = useCssVar(
|
||||
'--customize-color',
|
||||
iconContainer,
|
||||
{
|
||||
initialValue: 'default'
|
||||
}
|
||||
)
|
||||
|
||||
const strokeWidthCssVar = useCssVar(
|
||||
'--customize-strokeWidth',
|
||||
iconContainer,
|
||||
{
|
||||
initialValue: '2'
|
||||
}
|
||||
)
|
||||
|
||||
const sizeCssVar = useCssVar(
|
||||
'--customize-size',
|
||||
iconContainer,
|
||||
{
|
||||
initialValue: '24'
|
||||
}
|
||||
)
|
||||
|
||||
syncRef(color, colorCssVar)
|
||||
syncRef(strokeWidth, strokeWidthCssVar)
|
||||
syncRef(size, sizeCssVar)
|
||||
|
||||
function resetStyle () {
|
||||
color.value = 'currentColor'
|
||||
strokeWidth.value = 2
|
||||
size.value = 24
|
||||
absoluteStrokeWidth.value = false
|
||||
}
|
||||
|
||||
watch(absoluteStrokeWidth, (enabled) => {
|
||||
iconContainer.value?.classList.toggle('absolute-stroke-width', enabled)
|
||||
})
|
||||
const iconStyle = computed(() => ({
|
||||
'--home-icon-color': color.value,
|
||||
'--home-icon-stroke-width': String(strokeWidth.value),
|
||||
'--home-icon-size': String(size.value),
|
||||
}));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -62,7 +24,10 @@ watch(absoluteStrokeWidth, (enabled) => {
|
||||
<div class="card-column">
|
||||
<h2 class="title">
|
||||
Style as you please
|
||||
<ResetButton @click="resetStyle"></ResetButton>
|
||||
<ResetButton
|
||||
:disabled="!isCustomized"
|
||||
@click="resetStyle"
|
||||
/>
|
||||
</h2>
|
||||
<p class="copy">
|
||||
Lucide has a lot of customization options to match the icons with your UI.
|
||||
@@ -78,7 +43,10 @@ watch(absoluteStrokeWidth, (enabled) => {
|
||||
class="color-picker-field"
|
||||
>
|
||||
<template #display>
|
||||
<ColorPicker v-model="color" id="icon-color" />
|
||||
<ColorPicker
|
||||
v-model="color"
|
||||
id="icon-color-picker"
|
||||
/>
|
||||
</template>
|
||||
</InputField>
|
||||
|
||||
@@ -90,7 +58,7 @@ watch(absoluteStrokeWidth, (enabled) => {
|
||||
<span class="customize-label">{{ strokeWidth }}px</span>
|
||||
</template>
|
||||
<RangeSlider
|
||||
id="stroke-width"
|
||||
id="stroke-width-slider"
|
||||
name="stroke-width"
|
||||
v-model="strokeWidth"
|
||||
:min="1"
|
||||
@@ -107,7 +75,7 @@ watch(absoluteStrokeWidth, (enabled) => {
|
||||
<span class="customize-label">{{ size }}px</span>
|
||||
</template>
|
||||
<RangeSlider
|
||||
id="size"
|
||||
id="size-slider"
|
||||
name="size"
|
||||
v-model="size"
|
||||
:min="16"
|
||||
@@ -122,7 +90,7 @@ watch(absoluteStrokeWidth, (enabled) => {
|
||||
>
|
||||
<template #display>
|
||||
<Switch
|
||||
id="absolute-stroke-width"
|
||||
id="absolute-stroke-width-switch"
|
||||
name="absolute-stroke-width"
|
||||
v-model="absoluteStrokeWidth"
|
||||
/>
|
||||
@@ -131,8 +99,11 @@ watch(absoluteStrokeWidth, (enabled) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="icons-container card-column" ref="iconContainer">
|
||||
<HomeIconCustomizerIcons />
|
||||
<div class="icons-container card-column">
|
||||
<HomeIconCustomizerIcons
|
||||
:class="{ 'absolute-stroke-width': absoluteStrokeWidth }"
|
||||
:style="iconStyle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</HomeContainer>
|
||||
@@ -179,10 +150,6 @@ watch(absoluteStrokeWidth, (enabled) => {
|
||||
display: grid;
|
||||
grid-template-columns: 8fr 10fr;
|
||||
}
|
||||
/*
|
||||
.card-column {
|
||||
flex: 1;
|
||||
} */
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
@@ -191,7 +158,7 @@ watch(absoluteStrokeWidth, (enabled) => {
|
||||
}
|
||||
}
|
||||
|
||||
.color-picker-field:deep(.display-value) {
|
||||
width: 138px;
|
||||
.color-picker-field:deep(.icon-color-picker-input) {
|
||||
width: 116px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,29 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { data } from './HomeHeroIconsCard.data'
|
||||
import LucideIcon from '../base/LucideIcon.vue'
|
||||
import { vIntersectionObserver } from '@vueuse/components'
|
||||
import { data } from './HomeHeroIconsCard.data';
|
||||
import LucideIcon from '../base/LucideIcon.vue';
|
||||
import { vIntersectionObserver } from '@vueuse/components';
|
||||
|
||||
const getInitialItems = () => data.icons.slice(0, 64)
|
||||
const items = ref(getInitialItems())
|
||||
const showIcons = ref(false)
|
||||
const getInitialItems = () => data.icons.slice(0, 64);
|
||||
const items = ref(getInitialItems());
|
||||
const showIcons = ref(false);
|
||||
|
||||
// Added intersection observer to improve performance
|
||||
const onIntersectionObserver: IntersectionObserverCallback = ([{ isIntersecting }]) => {
|
||||
if (isIntersecting) {
|
||||
showIcons.value = true
|
||||
showIcons.value = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="icon-grid" v-intersection-observer="onIntersectionObserver">
|
||||
<div
|
||||
class="icon-grid"
|
||||
v-intersection-observer="onIntersectionObserver"
|
||||
>
|
||||
<template v-if="showIcons">
|
||||
<div
|
||||
v-for="icon in items"
|
||||
class="icon-grid-item"
|
||||
>
|
||||
<LucideIcon v-bind="icon" class="lucide-icon"/>
|
||||
>
|
||||
<LucideIcon
|
||||
v-bind="icon"
|
||||
class="lucide-icon"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -36,7 +42,7 @@ const onIntersectionObserver: IntersectionObserverCallback = ([{ isIntersecting
|
||||
grid-template-columns: repeat(auto-fill, minmax(68px, 1fr));
|
||||
grid-template-rows: repeat(auto-fill, minmax(68px, 1fr));
|
||||
width: 100%;
|
||||
height:100%;
|
||||
height: 100%;
|
||||
max-height: 360px;
|
||||
gap: 1px;
|
||||
overflow: hidden;
|
||||
@@ -65,13 +71,15 @@ const onIntersectionObserver: IntersectionObserverCallback = ([{ isIntersecting
|
||||
|
||||
.lucide-icon {
|
||||
will-change: width, height, stroke-width, stroke;
|
||||
color: var(--customize-color, currentColor);
|
||||
stroke-width: var(--customize-strokeWidth, 2);
|
||||
width: calc(var(--customize-size, 24) * 1px);
|
||||
height: calc(var(--customize-size, 24) * 1px);
|
||||
color: var(--home-icon-color, currentColor);
|
||||
stroke-width: var(--home-icon-stroke-width, 2);
|
||||
width: calc(var(--home-icon-size, 24) * 1px);
|
||||
height: calc(var(--home-icon-size, 24) * 1px);
|
||||
max-width: 3rem;
|
||||
max-height: 3rem;
|
||||
}
|
||||
|
||||
.icons-container.absolute-stroke-width .lucide-icon {
|
||||
stroke-width: calc(var(--customize-strokeWidth, 2) * 24 / var(--customize-size, 24));
|
||||
.icon-grid.absolute-stroke-width .lucide-icon {
|
||||
stroke-width: calc(var(--home-icon-stroke-width, 2) * 24 / var(--home-icon-size, 24));
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default {
|
||||
const loader = {
|
||||
async load() {
|
||||
return {
|
||||
packages: [
|
||||
@@ -61,3 +61,8 @@ export default {
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
declare const data: Awaited<ReturnType<typeof loader.load>>;
|
||||
|
||||
export { data };
|
||||
export default loader;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
headingLevel: 1 | 2 | 3 | 4 | 5 | 6,
|
||||
headingLevel?: 1 | 2 | 3 | 4 | 5 | 6,
|
||||
}>()
|
||||
|
||||
const headingElement = computed(() => `h${props.headingLevel ?? 2}`)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
const RocketDuotoneIcon = [
|
||||
import type { LucideIconNode } from '@lucide/vue/src/types';
|
||||
|
||||
const RocketDuotoneIcon: LucideIconNode[] = [
|
||||
[
|
||||
'path',
|
||||
{
|
||||
@@ -16,6 +18,6 @@ const RocketDuotoneIcon = [
|
||||
'clip-rule': 'evenodd',
|
||||
},
|
||||
],
|
||||
] as const;
|
||||
];
|
||||
|
||||
export default RocketDuotoneIcon;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress';
|
||||
import { useSessionStorage } from '@vueuse/core';
|
||||
import IconButton from '../base/IconButton.vue';
|
||||
import Button from '../base/Button.vue';
|
||||
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue';
|
||||
import { x } from '../../../data/iconNodes';
|
||||
import Icon from '@lucide/vue/src/Icon';
|
||||
@@ -40,16 +40,17 @@ onMounted(() => {
|
||||
class="floating-ad"
|
||||
v-if="theme.carbonAds"
|
||||
>
|
||||
<IconButton
|
||||
<Button
|
||||
@click="hideAd"
|
||||
class="hide-button"
|
||||
>
|
||||
<Icon
|
||||
:iconNode="x"
|
||||
name="x"
|
||||
:size="20"
|
||||
absoluteStrokeWidth
|
||||
/>
|
||||
</IconButton>
|
||||
</Button>
|
||||
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" overlay />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { getAllCategoryFiles, mapCategoryIconCount } from '../../../lib/categories';
|
||||
import iconsMetaData from '../../../data/iconMetaData';
|
||||
|
||||
export default {
|
||||
const loader = {
|
||||
async load() {
|
||||
let categories = getAllCategoryFiles();
|
||||
|
||||
categories = mapCategoryIconCount(categories, Object.values(iconsMetaData));
|
||||
const categories = mapCategoryIconCount(getAllCategoryFiles(), Object.values(iconsMetaData));
|
||||
|
||||
return {
|
||||
categories,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
declare const data: Awaited<ReturnType<typeof loader.load>>;
|
||||
|
||||
export { data };
|
||||
export default loader;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
|
||||
import { isActive } from 'vitepress/dist/client/shared'
|
||||
import { isActive } from 'vitepress/dist/client/shared.js'
|
||||
import { useActiveAnchor } from '../../composables/useActiveAnchor'
|
||||
import { data } from './CategoryList.data'
|
||||
import CategoryListItem from './CategoryListItem.vue'
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { pencilSparkles } from '../../../data/iconNodes';
|
||||
import { IconEntity } from '../../types';
|
||||
import getStudioLink from '../../utils/getStudioLink';
|
||||
import Button from '../base/Button.vue';
|
||||
import Icon from '@lucide/vue/src/Icon.ts';
|
||||
|
||||
defineProps<{
|
||||
icon: IconEntity
|
||||
}>()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button
|
||||
:href="getStudioLink(icon.name, icon.iconNode, 'icon-info')"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Icon :iconNode="pencilSparkles" name="pencil-sparkles" :size="24" />
|
||||
<span class="text">Edit in studio</span>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.text {
|
||||
margin: 0 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -30,10 +30,15 @@ const props = defineProps<{
|
||||
.contributors {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
align-items: start;
|
||||
/* justify-content: flex-end; */
|
||||
gap: 16px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.contributors .label {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.avatar-group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -16,19 +16,17 @@ const tags = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <PageContainer class="overview"> -->
|
||||
<IconPreview
|
||||
:name="$params.name"
|
||||
:iconNode="$params.iconNode"
|
||||
class="preview"
|
||||
customizable
|
||||
/>
|
||||
<div class="details">
|
||||
<h1 class="title">
|
||||
{{ $params.name }}
|
||||
</h1>
|
||||
</div>
|
||||
<!-- </PageContainer> -->
|
||||
<IconPreview
|
||||
:name="$params.name"
|
||||
:iconNode="$params.iconNode"
|
||||
class="preview"
|
||||
customizable
|
||||
/>
|
||||
<div class="details">
|
||||
<h1 class="title">
|
||||
{{ $params.name }}
|
||||
</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -6,7 +6,11 @@ import useConfetti from '../../composables/useConfetti';
|
||||
const { animate, confetti } = useConfetti();
|
||||
const slots = useSlots();
|
||||
|
||||
const copiedText = computed(() => slots.default?.()[0].children);
|
||||
const copiedText = computed(() => {
|
||||
const children = slots.default?.()[0].children;
|
||||
|
||||
return typeof children === 'string' ? children : '';
|
||||
});
|
||||
|
||||
function copyText() {
|
||||
navigator.clipboard.writeText(copiedText.value);
|
||||
@@ -25,6 +29,7 @@ function copyText() {
|
||||
<slot />
|
||||
<Icon
|
||||
:iconNode="copy"
|
||||
name="copy"
|
||||
:size="20"
|
||||
class="copy-icon"
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { IconEntity } from '../../types';
|
||||
import { computed } from 'vue';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import IconButton from '../base/IconButton.vue';
|
||||
import Button from '../base/Button.vue';
|
||||
import IconContributors from './IconContributors.vue';
|
||||
import IconPreview from './IconPreview.vue';
|
||||
import { x, expand } from '../../../data/iconNodes';
|
||||
@@ -12,6 +12,7 @@ import Badge from '../base/Badge.vue';
|
||||
import { computedAsync } from '@vueuse/core';
|
||||
import { satisfies } from 'semver';
|
||||
import { useExternalLibs } from '../../composables/useExternalLibs';
|
||||
import EditInStudioButton from './EditInStudioButton.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
iconName: string | null;
|
||||
@@ -70,12 +71,19 @@ const Expand = createLucideIcon('Expand', expand);
|
||||
<div class="overlay-panel">
|
||||
<nav class="overlay-menu">
|
||||
<Badge
|
||||
v-if="icon.createdRelease"
|
||||
v-if="icon.awaitingRelease"
|
||||
class="version awaiting-release"
|
||||
>Unreleased</Badge
|
||||
>
|
||||
<Badge
|
||||
v-else-if="icon.createdRelease"
|
||||
class="version"
|
||||
:href="releaseTagLink(icon.createdRelease.version)"
|
||||
>v{{ icon.createdRelease.version }}</Badge
|
||||
>
|
||||
<IconButton
|
||||
{{ icon.createdRelease.version }}
|
||||
</Badge>
|
||||
<EditInStudioButton :icon="icon"/>
|
||||
<Button
|
||||
@click="
|
||||
go(
|
||||
icon.externalLibrary
|
||||
@@ -85,10 +93,10 @@ const Expand = createLucideIcon('Expand', expand);
|
||||
"
|
||||
>
|
||||
<component :is="Expand" />
|
||||
</IconButton>
|
||||
<IconButton @click="onClose">
|
||||
</Button>
|
||||
<Button @click="onClose">
|
||||
<component :is="CloseIcon" />
|
||||
</IconButton>
|
||||
</Button>
|
||||
</nav>
|
||||
<IconPreview
|
||||
id="previewer"
|
||||
@@ -200,7 +208,14 @@ const Expand = createLucideIcon('Expand', expand);
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.version.awaiting-release {
|
||||
color: var(--vp-c-danger-3);
|
||||
}
|
||||
|
||||
.contributors {
|
||||
justify-content: flex-end;
|
||||
position: absolute;
|
||||
bottom: 24px;
|
||||
right: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,30 +10,36 @@ import { computed } from 'vue';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import { diamond } from '../../../data/iconNodes';
|
||||
import deprecationReasonTemplate from '../../../../../tools/build-icons/utils/deprecationReasonTemplate.ts';
|
||||
import type { IconDeprecationReason } from '../../../../../tools/build-icons/types.ts';
|
||||
import IconTags from "./IconTags.vue"
|
||||
import getStudioLink from '../../utils/getStudioLink';
|
||||
|
||||
const props = defineProps<{
|
||||
icon: IconEntity;
|
||||
popoverPosition?: 'top' | 'bottom';
|
||||
showEditButton?: boolean;
|
||||
}>();
|
||||
|
||||
const { go } = useRouter();
|
||||
const { page } = useData();
|
||||
|
||||
const tags = computed(() => {
|
||||
if (!props.icon || !props?.icon?.tags) return [];
|
||||
return props.icon.tags.join(' • ');
|
||||
});
|
||||
|
||||
const DiamondIcon = createLucideIcon('Diamond', diamond);
|
||||
|
||||
const deprecatedTitle = computed(() => {
|
||||
if (!props.icon.deprecationReason) return '';
|
||||
return deprecationReasonTemplate(props.icon.deprecationReason, {
|
||||
return deprecationReasonTemplate(props.icon.deprecationReason as IconDeprecationReason, {
|
||||
componentName: props.icon.name,
|
||||
iconName: props.icon.name,
|
||||
toBeRemovedInVersion: props.icon.toBeRemovedInVersion,
|
||||
});
|
||||
});
|
||||
|
||||
const iconPath = computed(() => {
|
||||
if (props.icon.externalLibrary) {
|
||||
return `icons/${props.icon.externalLibrary}/${props.icon.name}`;
|
||||
}
|
||||
|
||||
return `icons/${props.icon.name}`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -60,14 +66,7 @@ const deprecatedTitle = computed(() => {
|
||||
Deprecated
|
||||
</Badge>
|
||||
</div>
|
||||
<div
|
||||
class="tags-scroller"
|
||||
v-if="tags.length"
|
||||
>
|
||||
<p class="icon-tags horizontal-scroller">
|
||||
{{ tags }}
|
||||
</p>
|
||||
</div>
|
||||
<IconTags :tags="props.icon.tags" />
|
||||
<div class="group">
|
||||
<Badge
|
||||
v-for="category in icon.categories"
|
||||
@@ -80,26 +79,10 @@ const deprecatedTitle = computed(() => {
|
||||
|
||||
<div class="group buttons">
|
||||
<VPButton
|
||||
v-if="
|
||||
!page?.relativePath?.startsWith?.(
|
||||
icon.externalLibrary
|
||||
? `icons/${icon.externalLibrary}/${icon.name}`
|
||||
: `icons/${icon.name}`,
|
||||
)
|
||||
"
|
||||
:href="
|
||||
icon.externalLibrary
|
||||
? `/icons/${icon.externalLibrary}/${icon.name}`
|
||||
: `/icons/${icon.name}`
|
||||
"
|
||||
v-if="!page?.relativePath?.startsWith?.(iconPath)"
|
||||
:href="iconPath"
|
||||
text="See in action"
|
||||
@click="
|
||||
go(
|
||||
icon.externalLibrary
|
||||
? `/icons/${icon.externalLibrary}/${icon.name}`
|
||||
: `/icons/${icon.name}`,
|
||||
)
|
||||
"
|
||||
@click="go(iconPath)"
|
||||
/>
|
||||
<CopySVGButton
|
||||
:name="icon.name"
|
||||
@@ -115,6 +98,38 @@ const deprecatedTitle = computed(() => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.studio-edit-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 0 14px;
|
||||
min-height: 40px;
|
||||
border: 1px solid var(--vp-button-alt-border);
|
||||
border-radius: 999px;
|
||||
background-color: var(--vp-button-alt-bg);
|
||||
color: var(--vp-button-alt-text);
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
color 0.2s,
|
||||
background-color 0.2s,
|
||||
transform 0.2s;
|
||||
margin-left: 0;
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
.studio-edit-button:hover,
|
||||
.studio-edit-button:focus-visible {
|
||||
border-color: var(--vp-button-alt-hover-border);
|
||||
color: var(--vp-button-alt-hover-text);
|
||||
background-color: var(--vp-button-alt-hover-bg);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -156,66 +171,6 @@ const deprecatedTitle = computed(() => {
|
||||
background-color: var(--vp-c-brand-2);
|
||||
}
|
||||
|
||||
.icon-tags {
|
||||
font-size: 16px;
|
||||
color: var(--vp-c-text-2);
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
white-space: nowrap;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tags-scroller {
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
padding: 8px 0 16px;
|
||||
margin-bottom: 16px;
|
||||
margin-top: 8px;
|
||||
align-items: center;
|
||||
|
||||
--gradient-background: var(--tags-gradient-background, var(--vp-c-bg-elv));
|
||||
}
|
||||
.horizontal-scroller {
|
||||
overflow-x: scroll;
|
||||
/* Hide Scrollbar */
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
scrollbar-width: thin; /* can also be normal, or none, to not render scrollbar */
|
||||
scrollbar-color: var(--vp-c-text-4) transparent; /* foreground background */
|
||||
}
|
||||
.horizontal-scroller::-webkit-scrollbar {
|
||||
width: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.horizontal-scroller::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.horizontal-scroller::-webkit-scrollbar-thumb {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tags-scroller::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 32px;
|
||||
height: 100%;
|
||||
/* Background Gradient left to right */
|
||||
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, var(--gradient-background) 100%);
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { IconEntity } from '../../types';
|
||||
import { computed, ref } from 'vue';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import { useIconStyleContext } from '../../composables/useIconStyle';
|
||||
import { squarePen } from '../../../data/iconNodes';
|
||||
|
||||
const props = defineProps<{
|
||||
name: IconEntity['name'];
|
||||
@@ -11,6 +10,8 @@ const props = defineProps<{
|
||||
customizable?: boolean;
|
||||
}>();
|
||||
|
||||
const activeSize = ref('1000%');
|
||||
|
||||
const { size, color, strokeWidth, absoluteStrokeWidth } = useIconStyleContext();
|
||||
const previewIcon = ref();
|
||||
|
||||
@@ -20,31 +21,6 @@ const iconComponent = computed(() => {
|
||||
if (!props.name || !props.iconNode) return null;
|
||||
return createLucideIcon(props.name, props.iconNode);
|
||||
});
|
||||
|
||||
const SquarePenIcon = createLucideIcon('SquarePen', squarePen);
|
||||
|
||||
const innerSvg = computed(() => {
|
||||
if (!props.iconNode) return '';
|
||||
|
||||
const string = props.iconNode
|
||||
.map(([tag, attrs]) => {
|
||||
const attributes = Object.entries(attrs)
|
||||
.map(([key, value]) => `${key}="${value}"`)
|
||||
.join(' ');
|
||||
return `<${tag} ${attributes}/>`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
return string
|
||||
.replace(/>[\r\n ]+</g, '><')
|
||||
.replace(/(<.*?>)|\s+/g, (m, $1) => $1 || ' ')
|
||||
.trim();
|
||||
});
|
||||
|
||||
const base64InnerSvg = computed(() => {
|
||||
if (typeof window === 'undefined') return '';
|
||||
return btoa(innerSvg.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -55,6 +31,8 @@ const base64InnerSvg = computed(() => {
|
||||
:size="size"
|
||||
:color="color"
|
||||
:strokeWidth="absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth"
|
||||
class="preview-icon"
|
||||
:data-size="activeSize"
|
||||
/>
|
||||
<svg
|
||||
class="icon-grid"
|
||||
@@ -62,6 +40,7 @@ const base64InnerSvg = computed(() => {
|
||||
fill="none"
|
||||
stroke-width="0.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:class="{ show: activeSize === '1000%' }"
|
||||
>
|
||||
<g
|
||||
:key="`grid-${i}`"
|
||||
@@ -83,16 +62,17 @@ const base64InnerSvg = computed(() => {
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<a
|
||||
:href="`https://studio.lucide.dev/edit?value=${encodeURIComponent(base64InnerSvg)}&name=${name}&utm_source=lucide.dev&utm_medium=icon-detail-preview`"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="studio-button"
|
||||
title="Open in Lucide Studio"
|
||||
aria-label="Open in Lucide Studio"
|
||||
>
|
||||
<SquarePenIcon :size="14" />
|
||||
</a>
|
||||
<div class="size-buttons" :class="{ show: activeSize !== '1000%' }">
|
||||
<button class="size-button" @click="activeSize = '1000%'" :class="{ active: activeSize === '1000%' }">
|
||||
1000%
|
||||
</button>
|
||||
<button class="size-button" @click="activeSize = '200%'" :class="{ active: activeSize === '200%' }">
|
||||
200%
|
||||
</button>
|
||||
<button class="size-button" @click="activeSize = '100%'" :class="{ active: activeSize === '100%' }">
|
||||
100%
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -104,56 +84,87 @@ const base64InnerSvg = computed(() => {
|
||||
top: 0;
|
||||
left: 0;
|
||||
stroke: var(--vp-c-divider);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.icon-grid.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
height: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
position: relative;
|
||||
background: var(--vp-c-bg-alt);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.icon-container > :deep(svg:not(.icon-grid)) {
|
||||
|
||||
.icon-container:hover .size-buttons {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.icon-container > :deep(svg.preview-icon) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: var(--vp-c-neutral);
|
||||
opacity: 0.8;
|
||||
transition: all 0.3s;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.studio-button {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--vp-c-text-2);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s, color 0.2s, background-color 0.2s;
|
||||
z-index: 2;
|
||||
.icon-container > :deep(svg.preview-icon[data-size='1000%']) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.icon-container:hover .studio-button,
|
||||
.studio-button:focus-visible {
|
||||
opacity: 1;
|
||||
.icon-container > :deep(svg.preview-icon[data-size='200%']) {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.studio-button:hover,
|
||||
.studio-button:focus-visible {
|
||||
background: var(--vp-c-bg-soft);
|
||||
color: var(--vp-c-text-1);
|
||||
.icon-container > :deep(svg.preview-icon[data-size='100%']) {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.icon-component.customizable {
|
||||
will-change: width, height, stroke-width, stroke;
|
||||
/* color: var(--customize-color, currentColor);
|
||||
stroke-width: var(--customize-strokeWidth, 2); */
|
||||
}
|
||||
|
||||
.size-buttons {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
left: 50%;
|
||||
padding: 4px;
|
||||
border-radius: 12px;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
z-index: 99;
|
||||
background: var(--vp-code-tab-bg);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.size-buttons.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.size-button {
|
||||
color: var(--vp-code-tab-text-color);
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.size-button.active {
|
||||
background: var(--vp-c-neutral-inverse);
|
||||
box-shadow: var(--vp-shadow-1);
|
||||
border-radius: 4px;
|
||||
color: var(--vp-code-tab-active-text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -70,7 +70,6 @@ const prettyName = props.name
|
||||
class="showcase"
|
||||
v-if="iconComponent"
|
||||
>
|
||||
<h2 class="title">See this icon in action</h2>
|
||||
<div class="showcase-grid">
|
||||
<div class="showcase-item column">
|
||||
<div class="placeholder"></div>
|
||||
@@ -236,18 +235,17 @@ const prettyName = props.name
|
||||
.showcase-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 48px;
|
||||
margin-inline-start: 24px;
|
||||
margin-block-start: 48px;
|
||||
gap: 32px;
|
||||
background: var(--vp-code-block-bg);
|
||||
padding: 32px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.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);
|
||||
box-shadow: var(--vp-shadow-2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
const MAX_VISIBLE_TAGS = 5;
|
||||
|
||||
const props = defineProps<{
|
||||
tags?: string[];
|
||||
}>();
|
||||
|
||||
const tags = computed(() => props.tags ?? []);
|
||||
const expanded = ref(false);
|
||||
const hiddenCount = computed(() => Math.max(tags.value.length - MAX_VISIBLE_TAGS, 0));
|
||||
const showAll = computed(() => expanded.value || hiddenCount.value === 0);
|
||||
|
||||
// The overlay reuses this component when switching icons, so collapse again
|
||||
watch(tags, () => {
|
||||
expanded.value = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul
|
||||
v-if="tags.length"
|
||||
class="icon-tags"
|
||||
>
|
||||
<!-- v-show keeps hidden tags in the rendered HTML so crawlers can index them -->
|
||||
<li
|
||||
v-for="(tag, index) in tags"
|
||||
v-show="showAll || index < MAX_VISIBLE_TAGS"
|
||||
:key="tag"
|
||||
class="tag"
|
||||
>
|
||||
{{ tag }}
|
||||
</li>
|
||||
<li
|
||||
v-if="!showAll"
|
||||
class="more"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="more-button"
|
||||
:aria-label="`Show ${hiddenCount} more tags`"
|
||||
@click="expanded = true"
|
||||
>
|
||||
+{{ hiddenCount }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.icon-tags {
|
||||
font-size: 16px;
|
||||
color: var(--vp-c-text-2);
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
list-style-type: disc;
|
||||
gap: 4px 8px;
|
||||
/* Clip bullets hanging before tags that start a wrapped line, padding leaves room for the focus ring */
|
||||
overflow: hidden;
|
||||
padding: 4px;
|
||||
margin: 4px -4px 12px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
margin-top: 0;
|
||||
padding-left: 0px;
|
||||
padding-right: 12px;
|
||||
list-style-position: outside;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tag:first-child {
|
||||
list-style: none;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.more {
|
||||
margin-top: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.more-button {
|
||||
font: inherit;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 12px;
|
||||
padding: 0 8px;
|
||||
margin-left: -4px;
|
||||
border-radius: 16px;
|
||||
background-color: var(--vp-c-bg-alt);
|
||||
transition:
|
||||
color 0.25s,
|
||||
background-color 0.25s;
|
||||
}
|
||||
|
||||
.more-button:hover,
|
||||
.more-button:focus-visible {
|
||||
color: var(--vp-c-text-1);
|
||||
background-color: var(--vp-c-default-soft);
|
||||
}
|
||||
|
||||
.more-button:focus-visible {
|
||||
outline: 2px solid var(--vp-c-brand-1);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -163,7 +163,7 @@ const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay
|
||||
function handleCloseDrawer() {
|
||||
setActiveIconName('');
|
||||
|
||||
const url = new URL(window.location);
|
||||
const url = new URL(window.location.href);
|
||||
url.pathname = '/icons/categories';
|
||||
|
||||
if (searchQueryDebounced.value) {
|
||||
|
||||
@@ -35,7 +35,6 @@ const SORTING = [
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
const initialGridItems = computed(() => {
|
||||
if (containerWidth.value === 0) return 120;
|
||||
|
||||
@@ -72,6 +71,8 @@ const sortedIcons = computed(() => {
|
||||
return [...props.icons].sort((a, b) => (b.popularity || 0) - (a.popularity || 0));
|
||||
case 'release-date':
|
||||
return [...props.icons].sort((a, b) => {
|
||||
if (a.awaitingRelease !== b.awaitingRelease) return a.awaitingRelease ? -1 : 1;
|
||||
|
||||
const aDate = a.createdRelease?.date ? new Date(a.createdRelease.date).getTime() : 0;
|
||||
const bDate = b.createdRelease?.date ? new Date(b.createdRelease.date).getTime() : 0;
|
||||
return bDate - aDate;
|
||||
@@ -172,7 +173,7 @@ watch(searchQueryDebounced, () => {
|
||||
function handleCloseDrawer() {
|
||||
setActiveIconName('');
|
||||
|
||||
const url = new URL(window.location);
|
||||
const url = new URL(window.location.href);
|
||||
url.pathname = '/icons/';
|
||||
|
||||
if (searchQueryDebounced.value) {
|
||||
@@ -207,6 +208,7 @@ function handleCloseDrawer() {
|
||||
<template #start-icon>
|
||||
<Icon
|
||||
:iconNode="listSortDescending"
|
||||
name="list-sort-descending"
|
||||
class="chevron-icon"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
@@ -39,7 +39,7 @@ interface Placeholder {
|
||||
finePrint?: string;
|
||||
}
|
||||
|
||||
const brandPlaceholders: Placeholder[] = shallowReadonly([
|
||||
const brandPlaceholders: readonly Placeholder[] = shallowReadonly([
|
||||
{
|
||||
title: 'Boooo! What a scary brand logo!',
|
||||
message:
|
||||
@@ -103,7 +103,7 @@ const brandPlaceholders: Placeholder[] = shallowReadonly([
|
||||
},
|
||||
]);
|
||||
|
||||
const notFoundPlaceholders: Omit<Placeholder, 'title'>[] = shallowReadonly([
|
||||
const notFoundPlaceholders: readonly Omit<Placeholder, 'title'>[] = shallowReadonly([
|
||||
{
|
||||
message: 'We’ve looked for this icon for a bird’s eye view, but could not find it.',
|
||||
icon: markRaw(bird),
|
||||
@@ -130,7 +130,7 @@ const notFoundPlaceholders: Omit<Placeholder, 'title'>[] = shallowReadonly([
|
||||
},
|
||||
]);
|
||||
|
||||
function randomItem<T>(arr: T[]): T {
|
||||
function randomItem<T>(arr: readonly T[]): T {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ defineProps<{
|
||||
|
||||
<template>
|
||||
<section class="related-icons">
|
||||
<h2 class="title">More icons like this</h2>
|
||||
<IconGrid :icons="icons" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -1,73 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { shallowRef, type Ref, watch, computed } from 'vue';
|
||||
import { useCssVar, syncRef } from '@vueuse/core';
|
||||
import { STYLE_DEFAULTS, useIconStyleContext } from '../../composables/useIconStyle';
|
||||
import { useIconStyleContext } from '../../composables/useIconStyle';
|
||||
import RangeSlider from '../base/RangeSlider.vue';
|
||||
import InputField from '../base/InputField.vue';
|
||||
import ColorPicker from '../base/ColorPicker.vue';
|
||||
import ResetButton from '../base/ResetButton.vue';
|
||||
import Switch from '../base/Switch.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
rootEl?: Ref<HTMLElement>;
|
||||
}>();
|
||||
|
||||
const { color, strokeWidth, size, absoluteStrokeWidth } = useIconStyleContext();
|
||||
const documentRef = shallowRef<HTMLElement | undefined>(
|
||||
typeof document !== 'undefined' ? document?.documentElement : undefined,
|
||||
);
|
||||
|
||||
const colorCssVar = useCssVar('--customize-color', props.rootEl?.value ?? documentRef.value, {
|
||||
initialValue: `${STYLE_DEFAULTS.color}`,
|
||||
});
|
||||
|
||||
const strokeWidthCssVar = useCssVar(
|
||||
'--customize-strokeWidth',
|
||||
props.rootEl?.value ?? documentRef.value,
|
||||
{
|
||||
initialValue: `${STYLE_DEFAULTS.strokeWidth}`,
|
||||
},
|
||||
);
|
||||
|
||||
const sizeCssVar = useCssVar('--customize-size', props.rootEl?.value ?? documentRef.value, {
|
||||
initialValue: `${STYLE_DEFAULTS.size}`,
|
||||
});
|
||||
|
||||
syncRef(color, colorCssVar, { direction: 'ltr' });
|
||||
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr' });
|
||||
syncRef(size, sizeCssVar, { direction: 'ltr' });
|
||||
|
||||
function resetStyle() {
|
||||
color.value = STYLE_DEFAULTS.color;
|
||||
strokeWidth.value = STYLE_DEFAULTS.strokeWidth;
|
||||
size.value = STYLE_DEFAULTS.size;
|
||||
absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth;
|
||||
}
|
||||
|
||||
watch(absoluteStrokeWidth, (enabled) => {
|
||||
const htmlEl = document.documentElement;
|
||||
|
||||
htmlEl.classList.toggle('absolute-stroke-width', enabled);
|
||||
});
|
||||
|
||||
const customizingActive = computed(() => {
|
||||
return (
|
||||
color.value !== STYLE_DEFAULTS.color ||
|
||||
strokeWidth.value !== STYLE_DEFAULTS.strokeWidth ||
|
||||
size.value !== STYLE_DEFAULTS.size ||
|
||||
absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
|
||||
);
|
||||
});
|
||||
const { color, strokeWidth, size, absoluteStrokeWidth, isCustomized, resetStyle } =
|
||||
useIconStyleContext();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="customizer-card"
|
||||
:class="{ customized: customizingActive }"
|
||||
:class="{ customized: isCustomized }"
|
||||
>
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">Customizer</h2>
|
||||
<ResetButton @click="resetStyle"></ResetButton>
|
||||
<ResetButton
|
||||
:disabled="!isCustomized"
|
||||
@click="resetStyle"
|
||||
/>
|
||||
</div>
|
||||
<InputField
|
||||
id="icon-color"
|
||||
@@ -75,7 +28,7 @@ const customizingActive = computed(() => {
|
||||
>
|
||||
<ColorPicker
|
||||
v-model="color"
|
||||
id="icon-color"
|
||||
id="icon-color-picker"
|
||||
class="color-picker"
|
||||
/>
|
||||
</InputField>
|
||||
@@ -88,7 +41,7 @@ const customizingActive = computed(() => {
|
||||
<span class="customize-label">{{ strokeWidth }}px</span>
|
||||
</template>
|
||||
<RangeSlider
|
||||
id="stroke-width"
|
||||
id="stroke-width-slider"
|
||||
name="stroke-width"
|
||||
v-model="strokeWidth"
|
||||
:min="0.5"
|
||||
@@ -105,7 +58,7 @@ const customizingActive = computed(() => {
|
||||
<span class="customize-label">{{ size }}px</span>
|
||||
</template>
|
||||
<RangeSlider
|
||||
id="size"
|
||||
id="size-slider"
|
||||
name="size"
|
||||
v-model="size"
|
||||
:min="16"
|
||||
@@ -119,7 +72,7 @@ const customizingActive = computed(() => {
|
||||
label="Absolute stroke width"
|
||||
>
|
||||
<Switch
|
||||
id="absolute-stroke-width"
|
||||
id="absolute-stroke-width-switch"
|
||||
name="absolute-stroke-width"
|
||||
v-model="absoluteStrokeWidth"
|
||||
/>
|
||||
@@ -140,7 +93,6 @@ const customizingActive = computed(() => {
|
||||
color: var(--vp-c-text-1);
|
||||
line-height: 32px;
|
||||
font-size: 16px;
|
||||
/* margin-bottom: 12px; */
|
||||
}
|
||||
|
||||
.customizer-card {
|
||||
|
||||
@@ -61,6 +61,50 @@
|
||||
}
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-button) {
|
||||
padding: 11px 14px;
|
||||
background-color: var(--vp-c-bg-alt);
|
||||
color: var(--vp-c-text-2);
|
||||
flex-shrink: 0;
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-button):hover,
|
||||
.search-bar :deep(.select-button):focus {
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-bg-alt);
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-options) {
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-option) {
|
||||
padding-right: 36px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-icon) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-button) .select-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.search-bar :deep(.select-button) {
|
||||
padding: 11px 16px 11px 16px;
|
||||
}
|
||||
|
||||
.search-bar :deep(.select-button) .select-text {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.search-bar {
|
||||
padding-top: 32px;
|
||||
@@ -68,4 +112,7 @@
|
||||
top: calc(var(--vp-layout-top-height, 0px) + 64px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import VPHero from 'vitepress/dist/client/theme-default/components/VPHero.vue'
|
||||
import { useData } from 'vitepress/dist/client/theme-default/composables/data'
|
||||
import { useData } from 'vitepress/dist/client/theme-default/composables/data.js'
|
||||
import FakeInput from '../base/FakeInput.vue';
|
||||
import { useRouter } from 'vitepress';
|
||||
import { data } from '../home/HomeHeroIconsCard.data'
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
sparkles,
|
||||
} from '../../../data/iconNodes';
|
||||
import createElement from 'lucide/src/createElement';
|
||||
import { IconNode } from 'lucide';
|
||||
import { IconNode } from 'lucide/src/types';
|
||||
|
||||
const icons: Component[] = [
|
||||
heart, star, zap, code, feather, cloud, sun, moon, camera, music,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress';
|
||||
import { useSidebar } from 'vitepress/dist/client/theme-default/composables/sidebar';
|
||||
import { useSidebar } from 'vitepress/dist/client/theme-default/composables/sidebar.js';
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
import { computed } from 'vue';
|
||||
import SurveyAskOverlay from '../SurveyAskOverlay.vue';
|
||||
|
||||
@@ -2,7 +2,7 @@ import packageDataList from '../../../data/packageData.json';
|
||||
import thirdPartyPackages from '../../../data/packageData.thirdParty.json';
|
||||
import fetchPackages from '../../../lib/fetchPackages';
|
||||
|
||||
export default {
|
||||
const loader = {
|
||||
async load() {
|
||||
const packageJsonList = await fetchPackages();
|
||||
|
||||
@@ -29,3 +29,8 @@ export default {
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
declare const data: Awaited<ReturnType<typeof loader.load>>;
|
||||
|
||||
export { data };
|
||||
export default loader;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { onMounted, onUpdated, onUnmounted } from 'vue';
|
||||
import { throttleAndDebounce } from 'vitepress/dist/client/theme-default/support/utils';
|
||||
import { throttleAndDebounce } from 'vitepress/dist/client/theme-default/support/utils.js';
|
||||
|
||||
/*
|
||||
* This file is compied and adjusted from vitepress/dist/client/theme-default/composables/useActiveAnchor.ts
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { test } from 'node:test';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
const storedValues = new Map<string, string>([
|
||||
['icon-size', '240'],
|
||||
['icon-stroke-width', '1.5'],
|
||||
['icon-color', '#ff0000'],
|
||||
['icon-absolute-stroke-width', 'true'],
|
||||
]);
|
||||
const styleProperties = new Map<string, string>();
|
||||
const classes = new Set<string>();
|
||||
|
||||
class StorageMock {
|
||||
getItem(key: string) {
|
||||
return storedValues.get(key) ?? null;
|
||||
}
|
||||
|
||||
setItem(key: string, value: string) {
|
||||
storedValues.set(key, value);
|
||||
}
|
||||
|
||||
removeItem(key: string) {
|
||||
storedValues.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
class StorageEventMock {
|
||||
type: string;
|
||||
options: unknown;
|
||||
|
||||
constructor(type: string, options: unknown) {
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
const localStorage = new StorageMock();
|
||||
const document = {
|
||||
documentElement: {
|
||||
style: { setProperty: (key: string, value: string) => styleProperties.set(key, value) },
|
||||
classList: {
|
||||
toggle: (value: string, enabled: boolean) =>
|
||||
enabled ? classes.add(value) : classes.delete(value),
|
||||
},
|
||||
},
|
||||
};
|
||||
const window = {
|
||||
document,
|
||||
localStorage,
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
dispatchEvent() {},
|
||||
};
|
||||
|
||||
Object.assign(globalThis, {
|
||||
window,
|
||||
document,
|
||||
localStorage,
|
||||
Storage: StorageMock,
|
||||
StorageEvent: StorageEventMock,
|
||||
CSS: { supports: () => true },
|
||||
});
|
||||
|
||||
const { useIconStyle, usePersistedIconStyle } = await import('./useIconStyle.ts');
|
||||
|
||||
test('creates a non-persisted icon style from the defaults', () => {
|
||||
const style = useIconStyle();
|
||||
|
||||
assert.equal(style.size.value, 24);
|
||||
assert.equal(style.strokeWidth.value, 2);
|
||||
assert.equal(style.color.value, 'currentColor');
|
||||
assert.equal(style.absoluteStrokeWidth.value, false);
|
||||
|
||||
style.size.value = 48;
|
||||
|
||||
assert.equal(storedValues.get('icon-size'), '240');
|
||||
assert.equal(styleProperties.get('--customize-size'), '240');
|
||||
});
|
||||
|
||||
test('restores persisted settings and applies the shared CSS variables', () => {
|
||||
const style = usePersistedIconStyle();
|
||||
|
||||
assert.equal(style.size.value, 240);
|
||||
assert.equal(style.strokeWidth.value, 1.5);
|
||||
assert.equal(style.color.value, '#ff0000');
|
||||
assert.equal(style.absoluteStrokeWidth.value, true);
|
||||
assert.equal(style.isCustomized.value, true);
|
||||
assert.equal(styleProperties.get('--customize-size'), '240');
|
||||
assert.equal(styleProperties.get('--customize-strokeWidth'), '1.5');
|
||||
assert.equal(styleProperties.get('--customize-color'), '#ff0000');
|
||||
assert.ok(classes.has('absolute-stroke-width'));
|
||||
});
|
||||
|
||||
test('resetting updates refs, CSS variables, and persisted values', async () => {
|
||||
const style = usePersistedIconStyle();
|
||||
style.resetStyle();
|
||||
await nextTick();
|
||||
|
||||
assert.equal(style.size.value, 24);
|
||||
assert.equal(style.strokeWidth.value, 2);
|
||||
assert.equal(style.color.value, 'currentColor');
|
||||
assert.equal(style.absoluteStrokeWidth.value, false);
|
||||
assert.equal(style.isCustomized.value, false);
|
||||
assert.equal(styleProperties.get('--customize-size'), '24');
|
||||
assert.equal(styleProperties.get('--customize-strokeWidth'), '2');
|
||||
assert.equal(styleProperties.get('--customize-color'), 'currentColor');
|
||||
assert.equal(classes.has('absolute-stroke-width'), false);
|
||||
assert.equal(storedValues.get('icon-size'), '24');
|
||||
assert.equal(storedValues.get('icon-stroke-width'), '2');
|
||||
assert.equal(storedValues.get('icon-color'), 'currentColor');
|
||||
assert.equal(storedValues.get('icon-absolute-stroke-width'), 'false');
|
||||
});
|
||||
@@ -1,14 +1,19 @@
|
||||
import { ref, inject, Ref } from 'vue';
|
||||
import { computed, inject, ref, watch, type ComputedRef, type Ref } from 'vue';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
|
||||
export const ICON_STYLE_CONTEXT = Symbol('style');
|
||||
|
||||
interface IconSizeContext {
|
||||
interface IconStyleContext {
|
||||
size: Ref<number>;
|
||||
strokeWidth: Ref<number>;
|
||||
color: Ref<string>;
|
||||
absoluteStrokeWidth: Ref<boolean>;
|
||||
isCustomized: ComputedRef<boolean>;
|
||||
resetStyle: () => void;
|
||||
}
|
||||
|
||||
type IconStyle = Pick<IconStyleContext, 'size' | 'strokeWidth' | 'color' | 'absoluteStrokeWidth'>;
|
||||
|
||||
export const STYLE_DEFAULTS = {
|
||||
size: 24,
|
||||
strokeWidth: 2,
|
||||
@@ -16,15 +21,82 @@ export const STYLE_DEFAULTS = {
|
||||
absoluteStrokeWidth: false,
|
||||
};
|
||||
|
||||
export const iconStyleContext = {
|
||||
size: ref(24),
|
||||
strokeWidth: ref(2),
|
||||
color: ref('currentColor'),
|
||||
absoluteStrokeWidth: ref(false),
|
||||
const persistedIconStyle = {
|
||||
size: useLocalStorage('icon-size', STYLE_DEFAULTS.size, { initOnMounted: false }),
|
||||
strokeWidth: useLocalStorage('icon-stroke-width', STYLE_DEFAULTS.strokeWidth, {
|
||||
initOnMounted: false,
|
||||
}),
|
||||
color: useLocalStorage('icon-color', STYLE_DEFAULTS.color, { initOnMounted: false }),
|
||||
absoluteStrokeWidth: useLocalStorage(
|
||||
'icon-absolute-stroke-width',
|
||||
STYLE_DEFAULTS.absoluteStrokeWidth,
|
||||
{ initOnMounted: false },
|
||||
),
|
||||
};
|
||||
|
||||
export function useIconStyleContext(): IconSizeContext {
|
||||
const context = inject<IconSizeContext>(ICON_STYLE_CONTEXT);
|
||||
function createIconStyleContext(style: IconStyle): IconStyleContext {
|
||||
const isCustomized = computed(() => {
|
||||
return (
|
||||
style.color.value !== STYLE_DEFAULTS.color ||
|
||||
style.strokeWidth.value !== STYLE_DEFAULTS.strokeWidth ||
|
||||
style.size.value !== STYLE_DEFAULTS.size ||
|
||||
style.absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
|
||||
);
|
||||
});
|
||||
|
||||
function resetStyle() {
|
||||
style.color.value = STYLE_DEFAULTS.color;
|
||||
style.strokeWidth.value = STYLE_DEFAULTS.strokeWidth;
|
||||
style.size.value = STYLE_DEFAULTS.size;
|
||||
style.absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth;
|
||||
}
|
||||
|
||||
return {
|
||||
...style,
|
||||
isCustomized,
|
||||
resetStyle,
|
||||
};
|
||||
}
|
||||
|
||||
export const iconStyleContext = createIconStyleContext(persistedIconStyle);
|
||||
|
||||
export function useIconStyle() {
|
||||
return createIconStyleContext({
|
||||
size: ref(STYLE_DEFAULTS.size),
|
||||
strokeWidth: ref(STYLE_DEFAULTS.strokeWidth),
|
||||
color: ref(STYLE_DEFAULTS.color),
|
||||
absoluteStrokeWidth: ref(STYLE_DEFAULTS.absoluteStrokeWidth),
|
||||
});
|
||||
}
|
||||
|
||||
export function usePersistedIconStyle() {
|
||||
return iconStyleContext;
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
watch(
|
||||
[
|
||||
persistedIconStyle.size,
|
||||
persistedIconStyle.strokeWidth,
|
||||
persistedIconStyle.color,
|
||||
persistedIconStyle.absoluteStrokeWidth,
|
||||
],
|
||||
([size, strokeWidth, color, absoluteStrokeWidth]) => {
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--customize-size', String(Math.min(256, Math.max(16, size))));
|
||||
root.style.setProperty(
|
||||
'--customize-strokeWidth',
|
||||
String(Math.min(3, Math.max(0.5, strokeWidth))),
|
||||
);
|
||||
if (CSS.supports('color', color)) root.style.setProperty('--customize-color', color);
|
||||
root.classList.toggle('absolute-stroke-width', absoluteStrokeWidth);
|
||||
},
|
||||
{ immediate: true, flush: 'sync' },
|
||||
);
|
||||
}
|
||||
|
||||
export function useIconStyleContext(): IconStyleContext {
|
||||
const context = inject<IconStyleContext>(ICON_STYLE_CONTEXT);
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useIconStyleContext must be used with useIconStyleProvider');
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Fuse from 'fuse.js';
|
||||
import Fuse, { type FuseOptionKeyObject } from 'fuse.js';
|
||||
import { shallowRef, computed, Ref } from 'vue';
|
||||
|
||||
const useSearch = <T>(
|
||||
query: Ref<string>,
|
||||
collection: Ref<T[]>,
|
||||
keys: Fuse.FuseOptionKeyObject<T>[] = [],
|
||||
keys: FuseOptionKeyObject<T>[] = [],
|
||||
) => {
|
||||
const index = shallowRef(
|
||||
new Fuse(collection.value, {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Run before the body is painted: the generated HTML cannot know local preferences.
|
||||
(() => {
|
||||
try {
|
||||
const root = globalThis.document.documentElement;
|
||||
const size = globalThis.localStorage.getItem('icon-size');
|
||||
const stroke = globalThis.localStorage.getItem('icon-stroke-width');
|
||||
const color = globalThis.localStorage.getItem('icon-color');
|
||||
const absolute = globalThis.localStorage.getItem('icon-absolute-stroke-width');
|
||||
if ([size, stroke, color, absolute].every((value) => value === null)) return;
|
||||
if (size !== null && Number.isFinite(Number(size))) {
|
||||
root.style.setProperty('--customize-size', String(Math.min(256, Math.max(16, Number(size)))));
|
||||
}
|
||||
if (stroke !== null && Number.isFinite(Number(stroke)))
|
||||
root.style.setProperty(
|
||||
'--customize-strokeWidth',
|
||||
String(Math.min(3, Math.max(0.5, Number(stroke)))),
|
||||
);
|
||||
if (color !== null && globalThis.CSS.supports('color', color))
|
||||
root.style.setProperty('--customize-color', color);
|
||||
root.classList.toggle('absolute-stroke-width', absolute === 'true');
|
||||
} catch {
|
||||
// Storage can be unavailable, for example in private browsing mode.
|
||||
}
|
||||
})();
|
||||
@@ -152,6 +152,10 @@ html {
|
||||
.VPHomeHero .container {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.VPDoc:has(.vp-doc[class*=' _icons_']) {
|
||||
padding: 32px 32px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.VPNavBarHamburger .container > span {
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
export type IconNode = [elementName: string, attrs: Record<string, string>][];
|
||||
export type IconNodeWithKeys = [elementName: string, attrs: Record<string, string>, key: string][];
|
||||
|
||||
export interface Alias {
|
||||
name: string;
|
||||
deprecationReason: string;
|
||||
deprecated: boolean;
|
||||
}
|
||||
|
||||
export interface IconMetaData {
|
||||
tags: string[];
|
||||
categories: string[];
|
||||
contributors: string[];
|
||||
aliases?: string[];
|
||||
aliases?: Alias[];
|
||||
deprecated?: boolean;
|
||||
deprecationReason?: string;
|
||||
toBeRemovedInVersion?: string;
|
||||
@@ -18,6 +24,8 @@ export interface IconEntity extends IconMetaData {
|
||||
externalLibrary?: ExternalLibs;
|
||||
createdRelease?: Release;
|
||||
changedRelease?: Release;
|
||||
fromFork?: boolean;
|
||||
awaitingRelease?: boolean;
|
||||
popularity?: number;
|
||||
}
|
||||
|
||||
@@ -43,7 +51,8 @@ export interface PackageItem {
|
||||
packageDirname?: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
iconDark: string;
|
||||
iconDark?: string;
|
||||
iconClass?: string;
|
||||
shields: Shield[];
|
||||
source: string;
|
||||
documentation: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const chunkArray = <ArrayType>(stream: ArrayType, size: number) => {
|
||||
return stream.reduce<ArrayType[][]>(
|
||||
const chunkArray = <ItemType>(stream: ItemType[], size: number) => {
|
||||
return stream.reduce<ItemType[][]>(
|
||||
(chunks, item, idx, arr) =>
|
||||
idx % size == 0 ? [...chunks, arr.slice(idx, idx + size)] : chunks,
|
||||
[],
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { IconNode } from '../types';
|
||||
|
||||
function getInnerSvg(iconNode: IconNode) {
|
||||
return iconNode
|
||||
.map(([tag, attrs]) => {
|
||||
const attributes = Object.entries(attrs)
|
||||
.map(([key, value]) => `${key}="${value}"`)
|
||||
.join(' ');
|
||||
return `<${tag} ${attributes}/>`;
|
||||
})
|
||||
.join('')
|
||||
.replace(/>[\r\n ]+</g, '><')
|
||||
.replace(/(<.*?>)|\s+/g, (m, $1) => $1 || ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
export default function getStudioLink(
|
||||
name: string,
|
||||
iconNode: IconNode,
|
||||
utmMedium = 'icon-detail-preview',
|
||||
) {
|
||||
const base64InnerSvg = btoa(getInnerSvg(iconNode ?? []));
|
||||
|
||||
return `https://studio.lucide.dev/edit?value=${encodeURIComponent(base64InnerSvg)}&name=${name}&utm_source=lucide.dev&utm_medium=${utmMedium}`;
|
||||
}
|
||||
Vendored
+5
@@ -1,3 +1,4 @@
|
||||
/// <reference types="vitepress/client" />
|
||||
import { type IconNode } from '@lucide/vue/src/types';
|
||||
import Vue from 'vue';
|
||||
|
||||
@@ -31,7 +32,11 @@ declare module '*.node.json' {
|
||||
}
|
||||
|
||||
declare global {
|
||||
// Defined by VitePress at build time, used by its VPDocAsideCarbonAds component
|
||||
const __CARBON__: boolean;
|
||||
|
||||
interface Window {
|
||||
_carbonads?: unknown;
|
||||
ExpoSnack?: {
|
||||
/**
|
||||
* Initialize all snack players on the page
|
||||
|
||||
@@ -153,6 +153,7 @@ deno add lucide-solid
|
||||
```
|
||||
|
||||
:::
|
||||
> `@lucide/solid` is for Solid 2, for Solid 1 use the `lucide-solid` package. Solid 2 is still a release candidate.
|
||||
|
||||
For more details, see the [documentation](./solid/index.md).
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from "lucide-solid";
|
||||
```
|
||||
|
||||
### Turn off autocomplete in your IDE
|
||||
## Turn off autocomplete in your IDE
|
||||
|
||||
```json [.vscode/settings.json]
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user