Compare commits
46 Commits
0.556.0
...
angular-pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4257c22f4e | ||
|
|
4ca96b7b9c | ||
|
|
e2c1696c46 | ||
|
|
e9bd52b3a4 | ||
|
|
19dd078a94 | ||
|
|
18b1b037aa | ||
|
|
d6149a2e1c | ||
|
|
dbf5328ee8 | ||
|
|
f6c141221b | ||
|
|
a784e9922c | ||
|
|
1a1843cb2f | ||
|
|
7ced22b514 | ||
|
|
1b72561da4 | ||
|
|
69bf052ee5 | ||
|
|
6b4075b89b | ||
|
|
7a68e10b12 | ||
|
|
a4531a9985 | ||
|
|
3edcd9e0c3 | ||
|
|
1075461aab | ||
|
|
0b8f99326c | ||
|
|
7abb61630e | ||
|
|
3b0d158ea1 | ||
|
|
124572c83b | ||
|
|
4fcfb6a4d1 | ||
|
|
0f732b411d | ||
|
|
ce09c31f08 | ||
|
|
c2b059fb60 | ||
|
|
b3c80d027a | ||
|
|
20f30bb5ea | ||
|
|
c47ae67a3b | ||
|
|
7866a5a5c6 | ||
|
|
92bc88b001 | ||
|
|
e75fbcdec4 | ||
|
|
4cef8283a7 | ||
|
|
330f4b37db | ||
|
|
fd31cb44a8 | ||
|
|
790d30dbfa | ||
|
|
e7c075785f | ||
|
|
6d4c91707d | ||
|
|
c0ea92ebe7 | ||
|
|
42dc5508dd | ||
|
|
4dda432471 | ||
|
|
0775d8647e | ||
|
|
83ef8fc98d | ||
|
|
5b56ef705d | ||
|
|
dafe529892 |
9
.github/ISSUE_TEMPLATE/02_bug_report.yml
vendored
@@ -13,16 +13,19 @@ body:
|
|||||||
description: Which Lucide packages are affected? You may select more than one.
|
description: Which Lucide packages are affected? You may select more than one.
|
||||||
options:
|
options:
|
||||||
- label: lucide
|
- label: lucide
|
||||||
- label: lucide-angular
|
- label: lucide-angular (old version)
|
||||||
|
- label: '@lucide/angular (new version)'
|
||||||
|
- label: '@lucide/astro'
|
||||||
- label: lucide-flutter
|
- label: lucide-flutter
|
||||||
- label: lucide-preact
|
- label: lucide-preact
|
||||||
- label: lucide-react
|
- label: lucide-react
|
||||||
- label: lucide-react-native
|
- label: lucide-react-native
|
||||||
- label: lucide-solid
|
- label: lucide-solid
|
||||||
- label: lucide-svelte
|
- label: lucide-static
|
||||||
|
- label: lucide-svelte (old version)
|
||||||
|
- label: '@lucide/svelte (new version)'
|
||||||
- label: lucide-vue
|
- label: lucide-vue
|
||||||
- label: lucide-vue-next
|
- label: lucide-vue-next
|
||||||
- label: lucide-astro
|
|
||||||
- label: Figma plugin
|
- label: Figma plugin
|
||||||
- label: source/main
|
- label: source/main
|
||||||
- label: other/not relevant
|
- label: other/not relevant
|
||||||
|
|||||||
10
.github/ISSUE_TEMPLATE/04_feature_request.yml
vendored
@@ -13,19 +13,23 @@ body:
|
|||||||
description: Which Lucide project do you wish this feature were added to? You may select more than one.
|
description: Which Lucide project do you wish this feature were added to? You may select more than one.
|
||||||
options:
|
options:
|
||||||
- label: lucide
|
- label: lucide
|
||||||
- label: lucide-angular
|
- label: lucide-angular (old version)
|
||||||
|
- label: '@lucide/angular (new version)'
|
||||||
|
- label: '@lucide/astro'
|
||||||
- label: lucide-flutter
|
- label: lucide-flutter
|
||||||
- label: lucide-preact
|
- label: lucide-preact
|
||||||
- label: lucide-react
|
- label: lucide-react
|
||||||
- label: lucide-react-native
|
- label: lucide-react-native
|
||||||
- label: lucide-solid
|
- label: lucide-solid
|
||||||
- label: lucide-svelte
|
- label: lucide-static
|
||||||
|
- label: lucide-svelte (old version)
|
||||||
|
- label: '@lucide/svelte (new version)'
|
||||||
- label: lucide-vue
|
- label: lucide-vue
|
||||||
- label: lucide-vue-next
|
- label: lucide-vue-next
|
||||||
- label: lucide-astro
|
|
||||||
- label: Figma plugin
|
- label: Figma plugin
|
||||||
- label: all JS packages
|
- label: all JS packages
|
||||||
- label: site
|
- label: site
|
||||||
|
- label: other/not relevant
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
1
.github/labeler.yml
vendored
@@ -59,6 +59,7 @@
|
|||||||
🅰️ angular package:
|
🅰️ angular package:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
|
- 'packages/angular/*'
|
||||||
- 'packages/lucide-angular/*'
|
- 'packages/lucide-angular/*'
|
||||||
|
|
||||||
# For changes in the lucide preact package
|
# For changes in the lucide preact package
|
||||||
|
|||||||
41
.github/workflows/angular.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: Lucide Angular checks
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- packages/angular/**
|
||||||
|
- tools/build-icons/**
|
||||||
|
- pnpm-lock.yaml
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
cache: 'pnpm'
|
||||||
|
node-version-file: 'package.json'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm --filter @lucide/angular build
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
cache: 'pnpm'
|
||||||
|
node-version-file: 'package.json'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: pnpm --filter @lucide/angular test
|
||||||
54
.github/workflows/ci.yml
vendored
@@ -36,25 +36,19 @@ jobs:
|
|||||||
id: latest-tag
|
id: latest-tag
|
||||||
run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT
|
run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Log latest tag
|
||||||
|
run: echo '${{ steps.latest-tag.outputs.LATEST_TAG }}'
|
||||||
|
|
||||||
- name: Check if we can patch
|
- name: Check if we can patch
|
||||||
run: .github/workflows/version-up.sh --minor
|
run: pnpm semver $LATEST_TAG -i minor
|
||||||
|
env:
|
||||||
|
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
|
||||||
|
|
||||||
- name: Create new version
|
- name: Create new version
|
||||||
id: new-version
|
id: new-version
|
||||||
run: echo "NEW_VERSION=$(.github/workflows/version-up.sh --minor)" >> $GITHUB_OUTPUT
|
run: echo "NEW_VERSION=$(pnpm semver $LATEST_TAG -i minor)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Create change log
|
|
||||||
id: change-log
|
|
||||||
run: |
|
|
||||||
CHANGE_LOG=$(pnpm run generate:changelog --old-tag=${{ steps.latest-tag.outputs.LATEST_TAG }})
|
|
||||||
CHANGE_LOG=$(tail -n +5 <<< $CHANGE_LOG)
|
|
||||||
echo $CHANGE_LOG
|
|
||||||
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
|
|
||||||
echo "CHANGE_LOG<<$EOF" >> $GITHUB_OUTPUT
|
|
||||||
echo "$CHANGE_LOG" >> $GITHUB_OUTPUT
|
|
||||||
echo "$EOF" >> $GITHUB_OUTPUT
|
|
||||||
env:
|
env:
|
||||||
GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
|
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
|
||||||
|
|
||||||
- name: Check output
|
- name: Check output
|
||||||
run: |
|
run: |
|
||||||
@@ -68,38 +62,6 @@ jobs:
|
|||||||
name: Version ${{ steps.new-version.outputs.NEW_VERSION }}
|
name: Version ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
|
|
||||||
test-semantic-release:
|
|
||||||
if: github.repository == 'lucide-icons/lucide'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Semantic Release
|
|
||||||
id: semantic
|
|
||||||
uses: cycjimmy/semantic-release-action@v4
|
|
||||||
with:
|
|
||||||
tag_format: ${version}
|
|
||||||
branches: |
|
|
||||||
['new-release-workflow']
|
|
||||||
extends: |
|
|
||||||
semantic-release-monorepo
|
|
||||||
extra_plugins: |
|
|
||||||
@semantic-release/github
|
|
||||||
@semantic-release/git
|
|
||||||
@semantic-release/release-notes-generator
|
|
||||||
conventional-changelog-conventionalcommits
|
|
||||||
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Log output
|
|
||||||
if: steps.semantic.outputs.new_release_published == 'true'
|
|
||||||
run: |
|
|
||||||
echo ${{ steps.semantic.outputs.new_release_version }}
|
|
||||||
echo ${{ steps.semantic.outputs.new_release_major_version }}
|
|
||||||
echo ${{ steps.semantic.outputs.new_release_minor_version }}
|
|
||||||
echo ${{ steps.semantic.outputs.new_release_patch_version }}
|
|
||||||
|
|
||||||
start-release:
|
start-release:
|
||||||
if: github.repository == 'lucide-icons/lucide'
|
if: github.repository == 'lucide-icons/lucide'
|
||||||
needs: create-release
|
needs: create-release
|
||||||
|
|||||||
13
.github/workflows/release.yml
vendored
@@ -20,7 +20,7 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write # Required for OIDC
|
id-token: write # Required for OIDC
|
||||||
contents: read
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-release:
|
pre-release:
|
||||||
@@ -58,6 +58,7 @@ jobs:
|
|||||||
'lucide-preact',
|
'lucide-preact',
|
||||||
'lucide-solid',
|
'lucide-solid',
|
||||||
'lucide-svelte',
|
'lucide-svelte',
|
||||||
|
'@lucide/angular',
|
||||||
'@lucide/astro',
|
'@lucide/astro',
|
||||||
'@lucide/svelte',
|
'@lucide/svelte',
|
||||||
]
|
]
|
||||||
@@ -72,9 +73,6 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Set Auth Token
|
|
||||||
run: npm config set //registry.npmjs.org/:_authToken ${{ inputs.NPM_TOKEN || secrets.NPM_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set new version
|
- name: Set new version
|
||||||
run: pnpm --filter ${{ matrix.package }} version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
|
run: pnpm --filter ${{ matrix.package }} version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
|
||||||
|
|
||||||
@@ -109,9 +107,6 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Set Auth Token
|
|
||||||
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set new version
|
- name: Set new version
|
||||||
run: pnpm --filter lucide-static version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
|
run: pnpm --filter lucide-static version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
|
||||||
|
|
||||||
@@ -157,7 +152,9 @@ jobs:
|
|||||||
if: github.repository == 'lucide-icons/lucide'
|
if: github.repository == 'lucide-icons/lucide'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [pre-release, lucide-font]
|
needs: [pre-release, lucide-font]
|
||||||
|
permissions:
|
||||||
|
id-token: write # Required for OIDC
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
|
|||||||
284
.github/workflows/version-up.sh
vendored
@@ -1,284 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
## Copyright (C) 2017, Oleksandr Kucherenko
|
|
||||||
## Last revisit: 2017-09-29
|
|
||||||
|
|
||||||
## get highest version tag for all branches
|
|
||||||
function highest_tag(){
|
|
||||||
local TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
|
|
||||||
echo "$TAG"
|
|
||||||
}
|
|
||||||
|
|
||||||
## extract current branch name
|
|
||||||
function current_branch(){
|
|
||||||
## expected: heads/{branch_name}
|
|
||||||
## expected: {branch_name}
|
|
||||||
local BRANCH=$(git rev-parse --abbrev-ref HEAD | cut -d"/" -f2)
|
|
||||||
echo "$BRANCH"
|
|
||||||
}
|
|
||||||
|
|
||||||
## get latest/head commit hash number
|
|
||||||
function head_hash(){
|
|
||||||
local COMMIT_HASH=$(git rev-parse --verify HEAD)
|
|
||||||
echo "$COMMIT_HASH"
|
|
||||||
}
|
|
||||||
|
|
||||||
## extract tag commit hash code, tag name provided by argument
|
|
||||||
function tag_hash(){
|
|
||||||
local TAG_HASH=$(git log -1 --format=format:"%H" $1 2>/dev/null | tail -n1)
|
|
||||||
echo "$TAG_HASH"
|
|
||||||
}
|
|
||||||
|
|
||||||
## get latest revision number
|
|
||||||
function latest_revision(){
|
|
||||||
local REV=$(git rev-list --count HEAD 2>/dev/null)
|
|
||||||
echo "$REV"
|
|
||||||
}
|
|
||||||
|
|
||||||
## parse last found tag, extract it PARTS
|
|
||||||
function parse_last(){
|
|
||||||
local position=$(($1-1))
|
|
||||||
|
|
||||||
# two parts found only
|
|
||||||
local SUBS=( ${PARTS[$position]//-/ } )
|
|
||||||
#echo ${SUBS[@]}, size: ${#SUBS}
|
|
||||||
|
|
||||||
# found NUMBER
|
|
||||||
PARTS[$position]=${SUBS[0]}
|
|
||||||
#echo ${PARTS[@]}
|
|
||||||
|
|
||||||
# found SUFFIX
|
|
||||||
if [[ ${#SUBS} -ge 1 ]]; then
|
|
||||||
PARTS[4]=${SUBS[1],,} #lowercase
|
|
||||||
#echo ${PARTS[@]}, ${SUBS[@]}
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
## increment REVISION part, don't touch STAGE
|
|
||||||
function increment_revision(){
|
|
||||||
PARTS[3]=$(( PARTS[3] + 1 ))
|
|
||||||
IS_DIRTY=1
|
|
||||||
}
|
|
||||||
|
|
||||||
## increment PATCH part, reset all other lower PARTS, don't touch STAGE
|
|
||||||
function increment_patch(){
|
|
||||||
PARTS[2]=$(( PARTS[2] + 1 ))
|
|
||||||
PARTS[3]=0
|
|
||||||
IS_DIRTY=1
|
|
||||||
}
|
|
||||||
|
|
||||||
## increment MINOR part, reset all other lower PARTS, don't touch STAGE
|
|
||||||
function increment_minor(){
|
|
||||||
PARTS[1]=$(( PARTS[1] + 1 ))
|
|
||||||
PARTS[2]=0
|
|
||||||
PARTS[3]=0
|
|
||||||
IS_DIRTY=1
|
|
||||||
}
|
|
||||||
|
|
||||||
## increment MAJOR part, reset all other lower PARTS, don't touch STAGE
|
|
||||||
function incremet_major(){
|
|
||||||
PARTS[0]="v$(( PARTS[0] + 1 ))"
|
|
||||||
PARTS[1]=0
|
|
||||||
PARTS[2]=0
|
|
||||||
PARTS[3]=0
|
|
||||||
IS_DIRTY=1
|
|
||||||
}
|
|
||||||
|
|
||||||
## increment the number only of last found PART: REVISION --> PATCH --> MINOR. don't touch STAGE
|
|
||||||
function increment_last_found(){
|
|
||||||
if [[ "${#PARTS[3]}" == 0 || "${PARTS[3]}" == "0" ]]; then
|
|
||||||
if [[ "${#PARTS[2]}" == 0 || "${PARTS[2]}" == "0" ]]; then
|
|
||||||
increment_minor
|
|
||||||
else
|
|
||||||
increment_patch
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
increment_revision
|
|
||||||
fi
|
|
||||||
|
|
||||||
# stage part is not EMPTY
|
|
||||||
if [[ "${#PARTS[4]}" != 0 ]]; then
|
|
||||||
IS_SHIFT=1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
## compose version from PARTS
|
|
||||||
function compose(){
|
|
||||||
MAJOR="${PARTS[0]}"
|
|
||||||
MINOR=".${PARTS[1]}"
|
|
||||||
PATCH=".${PARTS[2]}"
|
|
||||||
REVISION=".${PARTS[3]}"
|
|
||||||
SUFFIX="-${PARTS[4]}"
|
|
||||||
|
|
||||||
if [[ "${#PATCH}" == 1 ]]; then # if empty {PATCH}
|
|
||||||
PATCH=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${#REVISION}" == 1 ]]; then # if empty {REVISION}
|
|
||||||
REVISION=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${PARTS[3]}" == "0" ]]; then # if revision is ZERO
|
|
||||||
REVISION=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# shrink patch and revision
|
|
||||||
if [[ -z "${REVISION// }" ]]; then
|
|
||||||
if [[ "${PARTS[2]}" == "0" ]]; then
|
|
||||||
PATCH=".0"
|
|
||||||
fi
|
|
||||||
else # revision is not EMPTY
|
|
||||||
if [[ "${#PATCH}" == 0 ]]; then
|
|
||||||
PATCH=".0"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# remove suffix if we don't have a alpha/beta/rc
|
|
||||||
if [[ "${#SUFFIX}" == 1 ]]; then
|
|
||||||
SUFFIX=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
echo "${MAJOR}${MINOR}${PATCH}${REVISION}${SUFFIX}" #full format
|
|
||||||
}
|
|
||||||
|
|
||||||
# initial version used for repository without tags
|
|
||||||
INIT_VERSION=0.0.0.0-alpha
|
|
||||||
|
|
||||||
# do GIT data extracting
|
|
||||||
TAG=$(highest_tag)
|
|
||||||
REVISION=$(latest_revision)
|
|
||||||
BRANCH=$(current_branch)
|
|
||||||
TAG_HASH=$(tag_hash $TAG)
|
|
||||||
HEAD_HASH=$(head_hash)
|
|
||||||
|
|
||||||
# if tag and branch commit hashes are different, than print info about that
|
|
||||||
#echo $HEAD_HASH vs $TAG_HASH
|
|
||||||
if [[ "$@" == "" ]]; then
|
|
||||||
if [[ "$TAG_HASH" == "$HEAD_HASH" ]]; then
|
|
||||||
echo "Tag $TAG and HEAD are aligned. We will stay on the TAG version."
|
|
||||||
echo ""
|
|
||||||
NO_ARGS_VALUE='--stay'
|
|
||||||
else
|
|
||||||
PATTERN="^[0-9]+.[0-9]+(.[0-9]+)*(-(alpha|beta|rc))*$"
|
|
||||||
|
|
||||||
if [[ "$BRANCH" =~ $PATTERN ]]; then
|
|
||||||
echo "Detected version branch '$BRANCH'. We will auto-increment the last version PART."
|
|
||||||
echo ""
|
|
||||||
NO_ARGS_VALUE='--default'
|
|
||||||
else
|
|
||||||
echo "Detected branch name '$BRANCH' than does not match version pattern. We will increase MINOR."
|
|
||||||
echo ""
|
|
||||||
NO_ARGS_VALUE='--minor'
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
#
|
|
||||||
# {MAJOR}.{MINOR}[.{PATCH}[.{REVISION}][-(.*)]
|
|
||||||
#
|
|
||||||
# Suffix: alpha, beta, rc
|
|
||||||
# No Suffix --> {NEW_VERSION}-alpha
|
|
||||||
# alpha --> beta
|
|
||||||
# beta --> rc
|
|
||||||
# rc --> {VERSION}
|
|
||||||
#
|
|
||||||
PARTS=( ${TAG//./ } )
|
|
||||||
parse_last ${#PARTS[@]} # array size as argument
|
|
||||||
#echo ${PARTS[@]}
|
|
||||||
|
|
||||||
# if no parameters than emulate --default parameter
|
|
||||||
if [[ "$@" == "" ]]; then
|
|
||||||
set -- $NO_ARGS_VALUE
|
|
||||||
fi
|
|
||||||
|
|
||||||
# parse input parameters
|
|
||||||
for i in "$@"
|
|
||||||
do
|
|
||||||
key="$i"
|
|
||||||
|
|
||||||
case $key in
|
|
||||||
-a|--alpha) # switched to ALPHA
|
|
||||||
PARTS[4]="alpha"
|
|
||||||
IS_SHIFT=1
|
|
||||||
;;
|
|
||||||
-b|--beta) # switched to BETA
|
|
||||||
PARTS[4]="beta"
|
|
||||||
IS_SHIFT=1
|
|
||||||
;;
|
|
||||||
-c|--release-candidate) # switched to RC
|
|
||||||
PARTS[4]="rc"
|
|
||||||
IS_SHIFT=1
|
|
||||||
;;
|
|
||||||
-r|--release) # switched to RELEASE
|
|
||||||
PARTS[4]=""
|
|
||||||
IS_SHIFT=1
|
|
||||||
;;
|
|
||||||
-p|--patch) # increment of PATCH
|
|
||||||
increment_patch
|
|
||||||
;;
|
|
||||||
-e|--revision) # increment of REVISION
|
|
||||||
increment_revision
|
|
||||||
;;
|
|
||||||
-g|--git-revision) # use git revision number as a revision part§
|
|
||||||
PARTS[3]=$(( REVISION ))
|
|
||||||
IS_DIRTY=1
|
|
||||||
;;
|
|
||||||
-i|--minor) # increment of MINOR by default
|
|
||||||
increment_minor
|
|
||||||
;;
|
|
||||||
--default) # stay on the same stage, but increment only last found PART of version code
|
|
||||||
increment_last_found
|
|
||||||
;;
|
|
||||||
-m|--major) # increment of MAJOR
|
|
||||||
incremet_major
|
|
||||||
;;
|
|
||||||
-s|--stay) # extract version info
|
|
||||||
IS_DIRTY=1
|
|
||||||
NO_APPLY_MSG=1
|
|
||||||
;;
|
|
||||||
-t|--tag-only) # extract version info
|
|
||||||
TAG_ONLY=1
|
|
||||||
;;
|
|
||||||
--apply)
|
|
||||||
DO_APPLY=1
|
|
||||||
;;
|
|
||||||
-h|--help)
|
|
||||||
help
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# detected shift, but no increment
|
|
||||||
if [[ "$IS_SHIFT" == "1" ]]; then
|
|
||||||
# temporary disable stage shift
|
|
||||||
stage=${PARTS[4]}
|
|
||||||
PARTS[4]=''
|
|
||||||
|
|
||||||
# detect first run on repository, INIT_VERSION was used
|
|
||||||
if [[ "$(compose)" == "0.0" ]]; then
|
|
||||||
increment_minor
|
|
||||||
fi
|
|
||||||
|
|
||||||
PARTS[4]=$stage
|
|
||||||
fi
|
|
||||||
|
|
||||||
# no increment applied yet and no shift of state, do minor increase
|
|
||||||
if [[ "$IS_DIRTY$IS_SHIFT" == "" ]]; then
|
|
||||||
increment_minor
|
|
||||||
fi
|
|
||||||
|
|
||||||
compose
|
|
||||||
|
|
||||||
# is proposed tag in conflict with any other TAG
|
|
||||||
PROPOSED_HASH=$(tag_hash $(compose))
|
|
||||||
if [[ "${#PROPOSED_HASH}" -gt 0 && "$NO_APPLY_MSG" == "" ]]; then
|
|
||||||
echo -e "\033[31mERROR:\033[0m "
|
|
||||||
echo -e "\033[31mERROR:\033[0m Found conflict with existing tag \033[32m$(compose)\033[0m / $PROPOSED_HASH"
|
|
||||||
echo -e "\033[31mERROR:\033[0m Only manual resolving is possible now."
|
|
||||||
echo -e "\033[31mERROR:\033[0m "
|
|
||||||
echo -e "\033[31mERROR:\033[0m To Resolve try to add --revision or --patch modifier."
|
|
||||||
echo -e "\033[31mERROR:\033[0m "
|
|
||||||
echo ""
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -95,9 +95,25 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-angular": {
|
"@lucide/angular": {
|
||||||
"order": 6,
|
"order": 6,
|
||||||
"icon": "angular",
|
"icon": "angular",
|
||||||
|
"shields": [
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/v/@lucide/angular",
|
||||||
|
"href": "https://www.npmjs.com/package/@lucide/angular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alt": "npm",
|
||||||
|
"src": "https://img.shields.io/npm/dw/@lucide/angular",
|
||||||
|
"href": "https://www.npmjs.com/package/@lucide/angular"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lucide-angular": {
|
||||||
|
"order": 7,
|
||||||
|
"icon": "angular",
|
||||||
"shields": [
|
"shields": [
|
||||||
{
|
{
|
||||||
"alt": "npm",
|
"alt": "npm",
|
||||||
@@ -112,7 +128,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-preact": {
|
"lucide-preact": {
|
||||||
"order": 7,
|
"order": 8,
|
||||||
"icon": "preact",
|
"icon": "preact",
|
||||||
"shields": [
|
"shields": [
|
||||||
{
|
{
|
||||||
@@ -130,7 +146,7 @@
|
|||||||
"@lucide/astro": {
|
"@lucide/astro": {
|
||||||
"docsAlias": "lucide-astro",
|
"docsAlias": "lucide-astro",
|
||||||
"packageDirname": "astro",
|
"packageDirname": "astro",
|
||||||
"order": 8,
|
"order": 9,
|
||||||
"icon": "astro",
|
"icon": "astro",
|
||||||
"iconDark": "astro-dark",
|
"iconDark": "astro-dark",
|
||||||
"shields": [
|
"shields": [
|
||||||
@@ -147,7 +163,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-static": {
|
"lucide-static": {
|
||||||
"order": 9,
|
"order": 10,
|
||||||
"icon": "svg",
|
"icon": "svg",
|
||||||
"shields": [
|
"shields": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,14 +9,20 @@ type CodeExampleType = {
|
|||||||
const getIconCodes = (): CodeExampleType => {
|
const getIconCodes = (): CodeExampleType => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
language: 'js',
|
language: 'html',
|
||||||
title: 'Vanilla',
|
title: 'Vanilla',
|
||||||
code: `\
|
code: `\
|
||||||
import { createIcons, icons } from 'lucide';
|
<script>
|
||||||
|
import { createIcons, $CamelCase } from 'lucide';
|
||||||
|
|
||||||
createIcons({ icons });
|
createIcons({
|
||||||
|
icons: {
|
||||||
|
$CamelCase
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
document.body.append('<i data-lucide="$Name"></i>');\
|
<i data-lucide="$Name"></i>\
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ type CodeExampleType = {
|
|||||||
const getIconCodes = (): CodeExampleType => {
|
const getIconCodes = (): CodeExampleType => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
language: 'js',
|
language: 'html',
|
||||||
title: 'Vanilla',
|
title: 'Vanilla',
|
||||||
code: `\
|
code: `\
|
||||||
import { createIcons, icons } from 'lucide';
|
<script>
|
||||||
|
import { createIcons } from 'lucide';
|
||||||
import { $CamelCase } from '@lucide/lab';
|
import { $CamelCase } from '@lucide/lab';
|
||||||
|
|
||||||
createIcons({
|
createIcons({
|
||||||
@@ -21,8 +22,9 @@ createIcons({
|
|||||||
$CamelCase
|
$CamelCase
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
document.body.append('<i data-lucide="$Name"></i>');\
|
<i data-lucide="$Name"></i>\
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createLucideIcon } from 'lucide-react/src/lucide-react';
|
import { createLucideIcon } from 'lucide-react/src/lucide-react';
|
||||||
import { type LucideProps, type IconNode } from 'lucide-react/src/createLucideIcon';
|
import { type LucideProps, type IconNode } from 'lucide-react/src/types';
|
||||||
import { IconEntity } from '../theme/types';
|
import { IconEntity } from '../theme/types';
|
||||||
import { renderToStaticMarkup } from 'react-dom/server';
|
import { renderToStaticMarkup } from 'react-dom/server';
|
||||||
import { IconContent } from './generateZip';
|
import { IconContent } from './generateZip';
|
||||||
|
|||||||
@@ -69,39 +69,39 @@ const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
|
|||||||
link: '/guide/packages/lucide',
|
link: '/guide/packages/lucide',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide React',
|
text: 'React',
|
||||||
link: '/guide/packages/lucide-react',
|
link: '/guide/packages/lucide-react',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Vue',
|
text: 'Vue',
|
||||||
link: '/guide/packages/lucide-vue-next',
|
link: '/guide/packages/lucide-vue-next',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Svelte',
|
text: 'Svelte',
|
||||||
link: '/guide/packages/lucide-svelte',
|
link: '/guide/packages/lucide-svelte',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Solid',
|
text: 'Solid',
|
||||||
link: '/guide/packages/lucide-solid',
|
link: '/guide/packages/lucide-solid',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide React Native',
|
text: 'React Native',
|
||||||
link: '/guide/packages/lucide-react-native',
|
link: '/guide/packages/lucide-react-native',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Angular',
|
text: 'Angular',
|
||||||
link: '/guide/packages/lucide-angular',
|
link: '/guide/packages/angular',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Preact',
|
text: 'Preact',
|
||||||
link: '/guide/packages/lucide-preact',
|
link: '/guide/packages/lucide-preact',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Astro',
|
text: 'Astro',
|
||||||
link: '/guide/packages/lucide-astro',
|
link: '/guide/packages/lucide-astro',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Lucide Static',
|
text: 'Static',
|
||||||
link: '/guide/packages/lucide-static',
|
link: '/guide/packages/lucide-static',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,23 +1,31 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useData } from 'vitepress';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: string
|
modelValue: string;
|
||||||
id: string
|
id: string;
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const { isDark } = useData();
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get: () => props.modelValue,
|
get: () => {
|
||||||
set: (val) => emit('update:modelValue', val)
|
if (props.modelValue == null || props.modelValue === 'currentColor') {
|
||||||
})
|
return isDark.value ? '#ffffff' : '#000000';
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.modelValue;
|
||||||
|
},
|
||||||
|
set: (val) => emit('update:modelValue', val),
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="color-picker">
|
<div class="color-picker">
|
||||||
<div class="color-input-wrapper">
|
<div class="color-input-wrapper">
|
||||||
<!-- TODO: Add currentColor div if value is currentColor -->
|
|
||||||
<input
|
<input
|
||||||
type="color"
|
type="color"
|
||||||
:id="id"
|
:id="id"
|
||||||
@@ -33,6 +41,7 @@ const value = computed({
|
|||||||
class="color-input-text"
|
class="color-input-text"
|
||||||
aria-label="Color picker input"
|
aria-label="Color picker input"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
|
placeholder="[default]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -45,27 +54,33 @@ const value = computed({
|
|||||||
top: -5px;
|
top: -5px;
|
||||||
left: -5px;
|
left: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-input-wrapper {
|
.color-input-wrapper {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 12px;
|
border-radius: 4px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker {
|
.color-picker {
|
||||||
background: var(--color-picker-bg, var(--vp-c-bg-alt));
|
background: var(--color-picker-bg, var(--vp-c-bg-alt));
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
padding: 4px 8px;
|
padding: 3px 8px 3px 3px;
|
||||||
height: auto;
|
height: auto;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
cursor: text;
|
cursor: text;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
|
transition:
|
||||||
|
color 0.25s,
|
||||||
|
border-color 0.25s,
|
||||||
|
background-color 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-input-text {
|
.color-input-text {
|
||||||
@@ -75,19 +90,22 @@ const value = computed({
|
|||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
cursor: text;
|
cursor: text;
|
||||||
transition: border-color 0.25s, background 0.4s ease;
|
transition:
|
||||||
|
border-color 0.25s,
|
||||||
|
background 0.4s ease;
|
||||||
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker:hover, .color-picker:focus {
|
.color-picker:hover,
|
||||||
|
.color-picker:focus {
|
||||||
border-color: var(--vp-c-brand);
|
border-color: var(--vp-c-brand);
|
||||||
background: var(--vp-c-bg-alt);
|
background: var(--vp-c-bg-alt);
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-input[value="currentColor"] {
|
.color-input[value='currentColor'] {
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
import { search } from '../../../data/iconNodes'
|
import { search } from '../../../data/iconNodes';
|
||||||
|
|
||||||
const SearchIcon = createLucideIcon('search', search)
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
shortcut: {
|
shortcut: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false
|
required: false,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button class="fake-input">
|
<button class="fake-input">
|
||||||
<component :is="SearchIcon" class="search-icon"/>
|
<Icon
|
||||||
<slot/>
|
:iconNode="search"
|
||||||
<kbd v-if="shortcut" class="shortcut">{{ shortcut }}</kbd>
|
class="search-icon"
|
||||||
|
/>
|
||||||
|
<slot />
|
||||||
|
<kbd
|
||||||
|
v-if="shortcut"
|
||||||
|
class="shortcut"
|
||||||
|
>{{ shortcut }}</kbd
|
||||||
|
>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -34,10 +39,14 @@ defineProps({
|
|||||||
cursor: text;
|
cursor: text;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
|
transition:
|
||||||
|
color 0.25s,
|
||||||
|
border-color 0.25s,
|
||||||
|
background-color 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fake-input:hover, .fake-input:focus {
|
.fake-input:hover,
|
||||||
|
.fake-input:focus {
|
||||||
border-color: var(--vp-c-brand);
|
border-color: var(--vp-c-brand);
|
||||||
background: var(--vp-c-bg-alt);
|
background: var(--vp-c-bg-alt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
@@ -30,9 +29,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon-button:active {
|
.icon-button:active {
|
||||||
border-color: var(--vp-button-alt-active-border);
|
border-color: var(--vp-button-alt-active-border);
|
||||||
color: var(--vp-button-alt-active-text);
|
color: var(--vp-button-alt-active-text);
|
||||||
background-color: var(--vp-button-alt-active-bg);
|
background-color: var(--vp-button-alt-active-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-button.active {
|
.icon-button.active {
|
||||||
|
|||||||
@@ -1,60 +1,90 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface InputProps {
|
export interface InputProps {
|
||||||
type: string
|
type: string;
|
||||||
modelValue: string
|
modelValue: string;
|
||||||
shortcut?: string
|
shortcut?: string;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, nextTick, watch } from 'vue'
|
import { ref, onMounted, nextTick, watch } from 'vue';
|
||||||
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
|
import { x } from '../../../data/iconNodes';
|
||||||
|
import IconButton from './IconButton.vue';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<InputProps>(), {
|
const props = withDefaults(defineProps<InputProps>(), {
|
||||||
type: 'text'
|
type: 'text',
|
||||||
})
|
});
|
||||||
|
|
||||||
const input = ref()
|
const input = ref();
|
||||||
const wrapperEl = ref()
|
const wrapperEl = ref();
|
||||||
const shortcutEl = ref()
|
const shortcutEl = ref();
|
||||||
|
|
||||||
defineEmits(['change', 'input', 'update:modelValue'])
|
const emit = defineEmits(['change', 'input', 'update:modelValue']);
|
||||||
|
|
||||||
const updateShortcutSpacing = () => {
|
const updateShortcutSpacing = () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (shortcutEl.value && wrapperEl.value) {
|
if (shortcutEl.value && wrapperEl.value) {
|
||||||
const shortcutWidth = shortcutEl.value.offsetWidth
|
const shortcutWidth = shortcutEl.value.offsetWidth;
|
||||||
wrapperEl.value.style.setProperty('--shortcut-width', `${shortcutWidth}px`)
|
wrapperEl.value.style.setProperty('--shortcut-width', `${shortcutWidth}px`);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
onMounted(updateShortcutSpacing)
|
onMounted(updateShortcutSpacing);
|
||||||
watch(() => props.shortcut, updateShortcutSpacing)
|
watch(() => props.shortcut, updateShortcutSpacing);
|
||||||
|
|
||||||
|
function onClear() {
|
||||||
|
emit('update:modelValue', '');
|
||||||
|
input.value.focus();
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
focus: () => {
|
focus: () => {
|
||||||
input.value.focus()
|
input.value.focus();
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="input-wrapper" ref="wrapperEl">
|
<div
|
||||||
<slot name="icon" class="icon" />
|
class="input-wrapper"
|
||||||
|
ref="wrapperEl"
|
||||||
|
>
|
||||||
|
<slot
|
||||||
|
name="icon"
|
||||||
|
class="icon"
|
||||||
|
/>
|
||||||
<input
|
<input
|
||||||
:type="type"
|
:type="type"
|
||||||
class="input"
|
class="input"
|
||||||
:class="{'has-icon': $slots.icon, 'has-shortcut': shortcut}"
|
:class="{ 'has-icon': $slots.icon, 'has-shortcut': shortcut }"
|
||||||
ref="input"
|
ref="input"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@input="$emit('update:modelValue', $event.target.value)"
|
@input="$emit('update:modelValue', $event.target.value)"
|
||||||
/>
|
/>
|
||||||
<kbd v-if="shortcut" class="shortcut" ref="shortcutEl">{{ shortcut }}</kbd>
|
<IconButton
|
||||||
|
@click="onClear"
|
||||||
|
v-if="type === 'search' && modelValue"
|
||||||
|
class="clear-button"
|
||||||
|
aria-label="Clear input"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:iconNode="x"
|
||||||
|
:size="20"
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
<kbd
|
||||||
|
v-if="shortcut"
|
||||||
|
class="shortcut"
|
||||||
|
ref="shortcutEl"
|
||||||
|
>{{ shortcut }}</kbd
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -62,6 +92,7 @@ defineExpose({
|
|||||||
.input-wrapper {
|
.input-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
@@ -71,13 +102,18 @@ defineExpose({
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
background-color: var(--vp-c-bg-alt);
|
background-color: var(--vp-c-bg-alt);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
transition:
|
||||||
|
color 0.25s,
|
||||||
|
border-color 0.25s,
|
||||||
|
background-color 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input.has-shortcut {
|
.input.has-shortcut {
|
||||||
padding-right: calc(var(--shortcut-width, 40px) + 22px);
|
padding-right: calc(var(--shortcut-width, 40px) + 22px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input:hover, .input:focus {
|
.input:hover,
|
||||||
|
.input:focus {
|
||||||
border-color: var(--vp-c-brand);
|
border-color: var(--vp-c-brand);
|
||||||
background: var(--vp-c-bg-alt);
|
background: var(--vp-c-bg-alt);
|
||||||
}
|
}
|
||||||
@@ -86,6 +122,14 @@ defineExpose({
|
|||||||
padding-left: 52px;
|
padding-left: 52px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clear-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 56px;
|
||||||
|
top: 9px;
|
||||||
|
padding: 4px;
|
||||||
|
transition: background-color .25s;
|
||||||
|
}
|
||||||
|
|
||||||
.shortcut {
|
.shortcut {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
@@ -111,7 +155,7 @@ defineExpose({
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.input-wrapper svg {
|
.input-wrapper > svg {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 16px;
|
left: 16px;
|
||||||
top: 12px;
|
top: 12px;
|
||||||
|
|||||||
@@ -1,38 +1,36 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue';
|
||||||
import Input from './Input.vue'
|
import Input from './Input.vue';
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
import { search } from '../../../data/iconNodes'
|
import { search } from '../../../data/iconNodes';
|
||||||
|
|
||||||
const SearchIcon = createLucideIcon('search', search)
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
modelValue: string
|
modelValue: string;
|
||||||
shortcut?: string
|
shortcut?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
const input = ref()
|
const input = ref();
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
focus: () => {
|
focus: () => {
|
||||||
input.value.focus()
|
input.value.focus();
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get: () => props.modelValue,
|
get: () => props.modelValue,
|
||||||
set: (val) => emit('update:modelValue', val)
|
set: (val) => emit('update:modelValue', val),
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -46,7 +44,10 @@ const value = computed({
|
|||||||
class="input-wrapper"
|
class="input-wrapper"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<component :is="SearchIcon" class="search-icon" />
|
<Icon
|
||||||
|
:iconNode="search"
|
||||||
|
class="search-icon"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Input>
|
</Input>
|
||||||
</template>
|
</template>
|
||||||
@@ -62,7 +63,8 @@ const value = computed({
|
|||||||
background-color: var(--vp-c-bg-alt);
|
background-color: var(--vp-c-bg-alt);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input:hover, .input:focus {
|
.input:hover,
|
||||||
|
.input:focus {
|
||||||
border-color: var(--vp-c-brand);
|
border-color: var(--vp-c-brand);
|
||||||
background: var(--vp-c-bg-alt);
|
background: var(--vp-c-bg-alt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { rotateCw } from '../../../data/iconNodes'
|
import { rotateCw } from '../../../data/iconNodes';
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
import IconButton from "./IconButton.vue";
|
import IconButton from './IconButton.vue';
|
||||||
|
|
||||||
const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<IconButton class="reset-button">
|
<IconButton class="reset-button">
|
||||||
<RotateIcon :size="20"/>
|
<Icon
|
||||||
|
:size="20"
|
||||||
|
:iconNode="rotateCw"
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
|
|||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: rotate(359deg);
|
transform: rotate(359deg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,18 +22,18 @@ export default {
|
|||||||
logo: '/framework-logos/svelte.svg',
|
logo: '/framework-logos/svelte.svg',
|
||||||
label: 'Lucide documentation for Svelte',
|
label: 'Lucide documentation for Svelte',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'lucide-preact',
|
|
||||||
logo: '/framework-logos/preact.svg',
|
|
||||||
label: 'Lucide documentation for Preact',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'lucide-solid',
|
name: 'lucide-solid',
|
||||||
logo: '/framework-logos/solid.svg',
|
logo: '/framework-logos/solid.svg',
|
||||||
label: 'Lucide documentation for Solid',
|
label: 'Lucide documentation for Solid',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'lucide-angular',
|
name: 'lucide-preact',
|
||||||
|
logo: '/framework-logos/preact.svg',
|
||||||
|
label: 'Lucide documentation for Preact',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'angular',
|
||||||
logo: '/framework-logos/angular.svg',
|
logo: '/framework-logos/angular.svg',
|
||||||
label: 'Lucide documentation for Angular',
|
label: 'Lucide documentation for Angular',
|
||||||
},
|
},
|
||||||
@@ -48,11 +48,6 @@ export default {
|
|||||||
logo: '/framework-logos/react-native.svg',
|
logo: '/framework-logos/react-native.svg',
|
||||||
label: 'Lucide documentation for React Native',
|
label: 'Lucide documentation for React Native',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'lucide-flutter',
|
|
||||||
logo: '/framework-logos/flutter.svg',
|
|
||||||
label: 'Lucide documentation for Flutter',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,45 +2,48 @@
|
|||||||
import { useData } from 'vitepress';
|
import { useData } from 'vitepress';
|
||||||
import { useSessionStorage } from '@vueuse/core';
|
import { useSessionStorage } from '@vueuse/core';
|
||||||
import IconButton from '../base/IconButton.vue';
|
import IconButton from '../base/IconButton.vue';
|
||||||
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue'
|
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue';
|
||||||
import { x } from '../../../data/iconNodes'
|
import { x } from '../../../data/iconNodes';
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData();
|
||||||
const showAd = useSessionStorage('show-carbon-ads', true)
|
const showAd = useSessionStorage('show-carbon-ads', true);
|
||||||
const carbonLoaded = ref(true)
|
const carbonLoaded = ref(true);
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
drawerOpen: boolean
|
drawerOpen: boolean;
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const CloseIcon = createLucideIcon('Close', x)
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (window?._carbonads == null) {
|
if (window?._carbonads == null) {
|
||||||
carbonLoaded.value = false
|
carbonLoaded.value = false;
|
||||||
}
|
}
|
||||||
}, 5000)
|
}, 5000);
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="{
|
:class="{
|
||||||
'drawer-open': drawerOpen,
|
'drawer-open': drawerOpen,
|
||||||
'hide-ad': !(showAd && carbonLoaded)
|
'hide-ad': !(showAd && carbonLoaded),
|
||||||
}"
|
}"
|
||||||
class="floating-ad"
|
class="floating-ad"
|
||||||
v-if="theme.carbonAds"
|
v-if="theme.carbonAds"
|
||||||
>
|
>
|
||||||
<IconButton @click="showAd = false" class="hide-button">
|
<IconButton
|
||||||
<component :is="CloseIcon" :size="20" absoluteStrokeWidth />
|
@click="showAd = false"
|
||||||
|
class="hide-button"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:iconNode="x"
|
||||||
|
:size="20"
|
||||||
|
absoluteStrokeWidth
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<VPDocAsideCarbonAds
|
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" />
|
||||||
:carbon-ads="theme.carbonAds"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -51,7 +54,9 @@ onMounted(() => {
|
|||||||
bottom: 32px;
|
bottom: 32px;
|
||||||
width: 224px;
|
width: 224px;
|
||||||
right: 32px;
|
right: 32px;
|
||||||
transition: opacity 0.5s, transform 0.25s ease;
|
transition:
|
||||||
|
opacity 0.5s,
|
||||||
|
transform 0.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-ad.drawer-open {
|
.floating-ad.drawer-open {
|
||||||
@@ -67,8 +72,11 @@ onMounted(() => {
|
|||||||
transform: translateY(-288px) translateX(224px);
|
transform: translateY(-288px) translateX(224px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-ad.drawer-open, .floating-ad.hide-ad {
|
.floating-ad.drawer-open,
|
||||||
transition: opacity 0.25s, transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
|
.floating-ad.hide-ad {
|
||||||
|
transition:
|
||||||
|
opacity 0.25s,
|
||||||
|
transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1280px) {
|
@media (min-width: 1280px) {
|
||||||
|
|||||||
@@ -1,70 +1,68 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import ButtonMenu from '../base/ButtonMenu.vue'
|
import ButtonMenu from '../base/ButtonMenu.vue';
|
||||||
import { useIconStyleContext } from '../../composables/useIconStyle';
|
import { useIconStyleContext } from '../../composables/useIconStyle';
|
||||||
import useConfetti from '../../composables/useConfetti';
|
import useConfetti from '../../composables/useConfetti';
|
||||||
import getSVGIcon from '../../utils/getSVGIcon';
|
import getSVGIcon from '../../utils/getSVGIcon';
|
||||||
import downloadData from '../../utils/downloadData';
|
import downloadData from '../../utils/downloadData';
|
||||||
|
|
||||||
const downloadText = 'Download!'
|
const downloadText = 'Download!';
|
||||||
const copiedText = 'Copied!'
|
const copiedText = 'Copied!';
|
||||||
const confettiText = ref(copiedText)
|
const confettiText = ref(copiedText);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
name: string
|
name: string;
|
||||||
popoverPosition?: 'top' | 'bottom'
|
popoverPosition?: 'top' | 'bottom';
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const { size } = useIconStyleContext()
|
const { size } = useIconStyleContext();
|
||||||
|
|
||||||
const { animate, confetti } = useConfetti()
|
const { animate, confetti } = useConfetti();
|
||||||
|
|
||||||
function copySVG() {
|
function copySVG() {
|
||||||
confettiText.value = copiedText
|
confettiText.value = copiedText;
|
||||||
const svgString = getSVGIcon()
|
const svgString = getSVGIcon();
|
||||||
|
|
||||||
navigator.clipboard.writeText(svgString)
|
navigator.clipboard.writeText(svgString);
|
||||||
|
|
||||||
confetti()
|
confetti();
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyDataUrl() {
|
function copyDataUrl() {
|
||||||
confettiText.value = copiedText
|
confettiText.value = copiedText;
|
||||||
const svgString = getSVGIcon()
|
const svgString = getSVGIcon();
|
||||||
|
|
||||||
// Create SVG data url
|
// Create SVG data url
|
||||||
const dataUrl = `data:image/svg+xml;base64,${btoa(svgString)}`
|
const dataUrl = `data:image/svg+xml;base64,${btoa(svgString)}`;
|
||||||
navigator.clipboard.writeText(dataUrl)
|
navigator.clipboard.writeText(dataUrl);
|
||||||
|
|
||||||
confetti()
|
confetti();
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadSVG() {
|
function downloadSVG() {
|
||||||
confettiText.value = downloadText
|
confettiText.value = downloadText;
|
||||||
const svgString = getSVGIcon()
|
const svgString = getSVGIcon();
|
||||||
|
|
||||||
downloadData(`${props.name}.svg`, `data:image/svg+xml;base64,${btoa(svgString)}`)
|
downloadData(`${props.name}.svg`, `data:image/svg+xml;base64,${btoa(svgString)}`);
|
||||||
confetti()
|
confetti();
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadPNG() {
|
function downloadPNG() {
|
||||||
confettiText.value = downloadText
|
confettiText.value = downloadText;
|
||||||
const svgString = getSVGIcon()
|
const svgString = getSVGIcon();
|
||||||
|
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = size.value;
|
canvas.width = size.value;
|
||||||
canvas.height = size.value;
|
canvas.height = size.value;
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
image.src = `data:image/svg+xml;base64,${btoa(svgString)}`;
|
image.src = `data:image/svg+xml;base64,${btoa(svgString)}`;
|
||||||
image.onload = function() {
|
image.onload = function () {
|
||||||
ctx.drawImage(image, 0, 0);
|
ctx.drawImage(image, 0, 0);
|
||||||
downloadData(`${props.name}.png`, canvas.toDataURL('image/png'))
|
downloadData(`${props.name}.png`, canvas.toDataURL('image/png'));
|
||||||
confetti()
|
confetti();
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -75,10 +73,10 @@ function downloadPNG() {
|
|||||||
:data-confetti-text="confettiText"
|
:data-confetti-text="confettiText"
|
||||||
:popoverPosition="popoverPosition"
|
:popoverPosition="popoverPosition"
|
||||||
:options="[
|
:options="[
|
||||||
{ text: 'Copy SVG' , onClick: copySVG },
|
{ text: 'Copy SVG', onClick: copySVG },
|
||||||
{ text: 'Copy Data URL' , onClick: copyDataUrl },
|
{ text: 'Copy Data URL', onClick: copyDataUrl },
|
||||||
{ text: 'Download SVG' , onClick: downloadSVG },
|
{ text: 'Download SVG', onClick: downloadSVG },
|
||||||
{ text: 'Download PNG' , onClick: downloadPNG },
|
{ text: 'Download PNG', onClick: downloadPNG },
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,41 +1,44 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, useSlots } from 'vue';
|
import { computed, useSlots } from 'vue';
|
||||||
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
|
import { copy } from '../../../data/iconNodes';
|
||||||
import { copy } from '../../../data/iconNodes'
|
|
||||||
import useConfetti from '../../composables/useConfetti';
|
import useConfetti from '../../composables/useConfetti';
|
||||||
const { animate, confetti } = useConfetti()
|
import Icon from 'lucide-vue-next/src/Icon';
|
||||||
const slots = useSlots()
|
const { animate, confetti } = useConfetti();
|
||||||
|
const slots = useSlots();
|
||||||
|
|
||||||
const copiedText = computed(() => slots.default?.()[0].children)
|
const copiedText = computed(() => slots.default?.()[0].children);
|
||||||
|
|
||||||
function copyText() {
|
function copyText() {
|
||||||
navigator.clipboard.writeText(copiedText.value)
|
navigator.clipboard.writeText(copiedText.value);
|
||||||
|
|
||||||
confetti()
|
confetti();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Copy = createLucideIcon('ChevronUp', copy)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1
|
<h1
|
||||||
class="icon-name confetti-button"
|
class="icon-name confetti-button"
|
||||||
:class="{animate}"
|
:class="{ animate }"
|
||||||
data-confetti-text="Copied!"
|
data-confetti-text="Copied!"
|
||||||
@click="copyText"
|
@click="copyText"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
<Copy :size="20" class="copy-icon"/>
|
<Icon
|
||||||
|
:iconNode="copy"
|
||||||
|
:size="20"
|
||||||
|
class="copy-icon"
|
||||||
|
/>
|
||||||
</h1>
|
</h1>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import './confetti.css';
|
@import './confetti.css';
|
||||||
|
|
||||||
.icon-name {
|
.icon-name {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
transition: background ease-in .15s;;
|
transition: background ease-in 0.15s;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
width: auto;
|
width: auto;
|
||||||
@@ -48,7 +51,7 @@ const Copy = createLucideIcon('ChevronUp', copy)
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon-name:hover .copy-icon {
|
.icon-name:hover .copy-icon {
|
||||||
opacity: .9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-name:before,
|
.icon-name:before,
|
||||||
@@ -65,10 +68,10 @@ const Copy = createLucideIcon('ChevronUp', copy)
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
transition:ease .3s opacity;
|
transition: ease 0.3s opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-name:hover .copy-icon:hover {
|
.icon-name:hover .copy-icon:hover {
|
||||||
opacity: .6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,75 +1,72 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { shallowRef, type Ref, watch, computed } from 'vue'
|
import { shallowRef, type Ref, watch, computed } from 'vue';
|
||||||
import { useCssVar, syncRef } from '@vueuse/core'
|
import { useCssVar, syncRef } from '@vueuse/core';
|
||||||
import { STYLE_DEFAULTS, useIconStyleContext } from '../../composables/useIconStyle'
|
import { STYLE_DEFAULTS, useIconStyleContext } from '../../composables/useIconStyle';
|
||||||
import RangeSlider from '../base/RangeSlider.vue'
|
import RangeSlider from '../base/RangeSlider.vue';
|
||||||
import InputField from '../base/InputField.vue'
|
import InputField from '../base/InputField.vue';
|
||||||
import ColorPicker from '../base/ColorPicker.vue'
|
import ColorPicker from '../base/ColorPicker.vue';
|
||||||
import ResetButton from '../base/ResetButton.vue'
|
import ResetButton from '../base/ResetButton.vue';
|
||||||
import Switch from '../base/Switch.vue'
|
import Switch from '../base/Switch.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
rootEl?: Ref<HTMLElement>
|
rootEl?: Ref<HTMLElement>;
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const { color, strokeWidth, size, absoluteStrokeWidth } = useIconStyleContext()
|
const { color, strokeWidth, size, absoluteStrokeWidth } = useIconStyleContext();
|
||||||
const documentRef = shallowRef<HTMLElement | undefined>(typeof document !== 'undefined' ? document?.documentElement : undefined)
|
const documentRef = shallowRef<HTMLElement | undefined>(
|
||||||
|
typeof document !== 'undefined' ? document?.documentElement : undefined,
|
||||||
|
);
|
||||||
|
|
||||||
const colorCssVar = useCssVar(
|
const colorCssVar = useCssVar('--customize-color', props.rootEl?.value ?? documentRef.value, {
|
||||||
'--customize-color',
|
initialValue: `${STYLE_DEFAULTS.color}`,
|
||||||
props.rootEl?.value ?? documentRef.value,
|
});
|
||||||
{
|
|
||||||
initialValue: `${STYLE_DEFAULTS.color}`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const strokeWidthCssVar = useCssVar(
|
const strokeWidthCssVar = useCssVar(
|
||||||
'--customize-strokeWidth',
|
'--customize-strokeWidth',
|
||||||
props.rootEl?.value ?? documentRef.value,
|
props.rootEl?.value ?? documentRef.value,
|
||||||
{
|
{
|
||||||
initialValue: `${STYLE_DEFAULTS.strokeWidth}`
|
initialValue: `${STYLE_DEFAULTS.strokeWidth}`,
|
||||||
}
|
},
|
||||||
)
|
);
|
||||||
|
|
||||||
const sizeCssVar = useCssVar(
|
const sizeCssVar = useCssVar('--customize-size', props.rootEl?.value ?? documentRef.value, {
|
||||||
'--customize-size',
|
initialValue: `${STYLE_DEFAULTS.size}`,
|
||||||
props.rootEl?.value ?? documentRef.value,
|
});
|
||||||
{
|
|
||||||
initialValue: `${STYLE_DEFAULTS.size}`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
syncRef(color, colorCssVar, { direction: 'ltr' })
|
syncRef(color, colorCssVar, { direction: 'ltr' });
|
||||||
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr' })
|
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr' });
|
||||||
syncRef(size, sizeCssVar, { direction: 'ltr' })
|
syncRef(size, sizeCssVar, { direction: 'ltr' });
|
||||||
|
|
||||||
function resetStyle () {
|
function resetStyle() {
|
||||||
color.value = STYLE_DEFAULTS.color
|
color.value = STYLE_DEFAULTS.color;
|
||||||
strokeWidth.value = STYLE_DEFAULTS.strokeWidth
|
strokeWidth.value = STYLE_DEFAULTS.strokeWidth;
|
||||||
size.value = STYLE_DEFAULTS.size
|
size.value = STYLE_DEFAULTS.size;
|
||||||
absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth
|
absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(absoluteStrokeWidth, (enabled) => {
|
watch(absoluteStrokeWidth, (enabled) => {
|
||||||
const htmlEl = document.documentElement
|
const htmlEl = document.documentElement;
|
||||||
|
|
||||||
htmlEl.classList.toggle('absolute-stroke-width', enabled)
|
htmlEl.classList.toggle('absolute-stroke-width', enabled);
|
||||||
})
|
});
|
||||||
|
|
||||||
const customizingActive = computed(() => {
|
const customizingActive = computed(() => {
|
||||||
return color.value !== STYLE_DEFAULTS.color
|
return (
|
||||||
|| strokeWidth.value !== STYLE_DEFAULTS.strokeWidth
|
color.value !== STYLE_DEFAULTS.color ||
|
||||||
|| size.value !== STYLE_DEFAULTS.size
|
strokeWidth.value !== STYLE_DEFAULTS.strokeWidth ||
|
||||||
|| absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
|
size.value !== STYLE_DEFAULTS.size ||
|
||||||
})
|
absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
|
||||||
|
);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="customizer-card" :class="{ customized: customizingActive }">
|
<div
|
||||||
|
class="customizer-card"
|
||||||
|
:class="{ customized: customizingActive }"
|
||||||
|
>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h2 class="card-title">
|
<h2 class="card-title">Customizer</h2>
|
||||||
Customizer
|
|
||||||
</h2>
|
|
||||||
<ResetButton @click="resetStyle"></ResetButton>
|
<ResetButton @click="resetStyle"></ResetButton>
|
||||||
</div>
|
</div>
|
||||||
<InputField
|
<InputField
|
||||||
@@ -77,7 +74,11 @@ const customizingActive = computed(() => {
|
|||||||
label="Color"
|
label="Color"
|
||||||
>
|
>
|
||||||
<template #display>
|
<template #display>
|
||||||
<ColorPicker v-model="color" id="icon-color" class="color-picker"/>
|
<ColorPicker
|
||||||
|
v-model="color"
|
||||||
|
id="icon-color"
|
||||||
|
class="color-picker"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</InputField>
|
</InputField>
|
||||||
|
|
||||||
@@ -117,7 +118,7 @@ const customizingActive = computed(() => {
|
|||||||
|
|
||||||
<InputField
|
<InputField
|
||||||
id="absolute-stroke-width"
|
id="absolute-stroke-width"
|
||||||
label="Absolute Stroke width"
|
label="Absolute stroke width"
|
||||||
>
|
>
|
||||||
<Switch
|
<Switch
|
||||||
id="absolute-stroke-width"
|
id="absolute-stroke-width"
|
||||||
@@ -143,6 +144,7 @@ const customizingActive = computed(() => {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
/* margin-bottom: 12px; */
|
/* margin-bottom: 12px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.customizer-card {
|
.customizer-card {
|
||||||
background: var(--vp-c-bg);
|
background: var(--vp-c-bg);
|
||||||
padding: 12px 24px 24px;
|
padding: 12px 24px 24px;
|
||||||
@@ -151,7 +153,7 @@ const customizingActive = computed(() => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
transition: border-color .4s ease-in-out;
|
transition: border-color 0.4s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customizer-card.customized {
|
.customizer-card.customized {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default function useSearchPlaceholder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.value = {
|
state.value = {
|
||||||
isNoResults: query in BRAND_STOPWORDS || (searchResults.length === 0 && query !== ''),
|
isNoResults: query in BRAND_STOPWORDS && searchResults.length === 0 && query !== '',
|
||||||
isBrand: query in BRAND_STOPWORDS,
|
isBrand: query in BRAND_STOPWORDS,
|
||||||
query: BRAND_STOPWORDS[query] ?? query,
|
query: BRAND_STOPWORDS[query] ?? query,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { IconNode } from 'lucide-vue-next/src/createLucideIcon';
|
import { type IconNode } from 'lucide-vue-next/src/types';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
declare module '*.vue' {
|
declare module '*.vue' {
|
||||||
@@ -20,5 +20,6 @@ declare module 'node:module' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.node.json' {
|
declare module '*.node.json' {
|
||||||
export default IconNode;
|
const value: IconNode;
|
||||||
|
export default value;
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ However, not everyone can understand them easily. Read more about [how to use Lu
|
|||||||
|
|
||||||
## Official Packages
|
## Official Packages
|
||||||
|
|
||||||
Lucide's official packages are designed to work on different platforms, making it easier for users to integrate icons into their projects. The packages are available for various technologies, including [Web (Vanilla)](https://lucide.dev/guide/packages/lucide), [React](https://lucide.dev/guide/packages/lucide-react), [React Native](https://lucide.dev/guide/packages/lucide-react-native), [Vue](https://lucide.dev/guide/packages/lucide-vue), [Vue 3](https://lucide.dev/guide/packages/lucide-vue-next), [Svelte](https://lucide.dev/guide/packages/lucide-svelte), [Preact](https://lucide.dev/guide/packages/lucide-preact), [Solid](https://lucide.dev/guide/packages/lucide-solid), [Angular](https://lucide.dev/guide/packages/lucide-angular), [Astro](https://lucide.dev/guide/packages/lucide-astro), and [NodeJS](https://lucide.dev/guide/packages/lucide-static#nodejs).
|
Lucide's official packages are designed to work on different platforms, making it easier for users to integrate icons into their projects. The packages are available for various technologies, including [Web (Vanilla)](https://lucide.dev/guide/packages/lucide), [React](https://lucide.dev/guide/packages/lucide-react), [React Native](https://lucide.dev/guide/packages/lucide-react-native), [Vue](https://lucide.dev/guide/packages/lucide-vue), [Vue 3](https://lucide.dev/guide/packages/lucide-vue-next), [Svelte](https://lucide.dev/guide/packages/lucide-svelte), [Preact](https://lucide.dev/guide/packages/lucide-preact), [Solid](https://lucide.dev/guide/packages/lucide-solid), [Angular](https://lucide.dev/guide/packages/angular), [Astro](https://lucide.dev/guide/packages/lucide-astro), and [NodeJS](https://lucide.dev/guide/packages/lucide-static#nodejs).
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
|
|||||||
277
docs/guide/packages/angular.md
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
# `@lucide/angular`
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
This documentation is for `@lucide/angular`.
|
||||||
|
|
||||||
|
To learn about our legacy package for Angular, please refer to [`lucide-angular`](./lucide-angular).
|
||||||
|
:::
|
||||||
|
|
||||||
|
A standalone, signal-based, zoneless implementation of Lucide icons for Angular.
|
||||||
|
|
||||||
|
**What you can accomplish:**
|
||||||
|
- Use icons as standalone Angular components with full dependency injection support
|
||||||
|
- Configure icons globally through modern Angular providers
|
||||||
|
- Integrate with Angular's reactive forms and data binding
|
||||||
|
- Build scalable applications with tree-shaken icons and lazy loading support
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
This package requires Angular 17+ and uses standalone components, signals, and zoneless change detection.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
::: code-group
|
||||||
|
|
||||||
|
```sh [pnpm]
|
||||||
|
pnpm add @lucide/angular
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh [yarn]
|
||||||
|
yarn add @lucide/angular
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh [npm]
|
||||||
|
npm install @lucide/angular
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh [bun]
|
||||||
|
bun add @lucide/angular
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
### Standalone icons
|
||||||
|
|
||||||
|
Every icon can be imported as a ready-to-use standalone component:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svg lucideFileText></svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts{2,7}
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { LucideFileText } from '@lucide/angular';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-foobar',
|
||||||
|
templateUrl: './foobar.html',
|
||||||
|
imports: [LucideFileText],
|
||||||
|
})
|
||||||
|
export class Foobar { }
|
||||||
|
```
|
||||||
|
|
||||||
|
::: tip
|
||||||
|
Standalone icon components use the selector `svg[lucide{PascalCaseIconName}]`.
|
||||||
|
|
||||||
|
This ensures minimal bloating of the DOM and the ability to directly manipulate all attributes of the resulting SVG element.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Dynamic icon component
|
||||||
|
|
||||||
|
You may also use the dynamic `LucideIcon` component to dynamically render icons.
|
||||||
|
|
||||||
|
#### With tree-shaken imports
|
||||||
|
|
||||||
|
You may pass imported icons directly to the component:
|
||||||
|
|
||||||
|
```html{3}
|
||||||
|
@for (item of items) {
|
||||||
|
<a navbarItem [routerLink]="item.routerLink">
|
||||||
|
<svg [lucideIcon]="item.icon"></svg>
|
||||||
|
{{ item.title }}
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts{2,8,14,19}
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { LucideIcon, LucideHouse, LucideUsersRound } from '@lucide/angular';
|
||||||
|
import { NavbarItem, NavbarItemModel } from './navbar-item';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-navbar',
|
||||||
|
templateUrl: './navbar.html',
|
||||||
|
imports: [LucideIcon, NavbarItem],
|
||||||
|
})
|
||||||
|
export class Navbar {
|
||||||
|
readonly items: NavbarItemModel[] = [
|
||||||
|
{
|
||||||
|
title: 'Home',
|
||||||
|
icon: LucideHouse,
|
||||||
|
routerLink: [''],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Users',
|
||||||
|
icon: LucideUsersRound,
|
||||||
|
routerLink: ['admin/users'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### With icons provided via dependency injection
|
||||||
|
|
||||||
|
Alternatively, the component also accepts string inputs.
|
||||||
|
|
||||||
|
To use icons this way, first, you have to provide icons via `provideLucideIcons`:
|
||||||
|
|
||||||
|
:::code-group
|
||||||
|
```ts{7-10} [app.config.ts]
|
||||||
|
import { ApplicationConfig } from '@angular/core';
|
||||||
|
import { provideLucideIcons, LucideCircleCheck, LucideCircleX } from '@lucide/angular';
|
||||||
|
|
||||||
|
export const appConfig: ApplicationConfig = {
|
||||||
|
providers: [
|
||||||
|
// ...
|
||||||
|
provideLucideIcons([
|
||||||
|
LucideCircleCheck,
|
||||||
|
LucideCircleX,
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```html [foobar.html]
|
||||||
|
<svg lucideIcon="circle-check"></svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts{7} [foobar.ts]
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { LucideIcon } from '@lucide/angular';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-foobar',
|
||||||
|
templateUrl: './template-url',
|
||||||
|
imports: [LucideIcon],
|
||||||
|
})
|
||||||
|
export class Foobar { }
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: tip
|
||||||
|
For optimal bundle size, provide icons at the highest appropriate level in your application.
|
||||||
|
|
||||||
|
Providing all icons at the root level may increase your initial bundle size, while providing them at feature module level enables better code splitting.
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
While you may provide your icons at any level of the dependency injection tree, be aware that [Angular's DI system is hierarchical](https://angular.dev/guide/di/defining-dependency-providers#injector-hierarchy-in-angular): `LucideIcon` will only have access to the icons provided closest to it in the tree.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Accessible labels
|
||||||
|
|
||||||
|
You can use the `title` input property to set the [accessible name element](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/title) on the SVG:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svg lucideIcon="house" title="Go to dashboard"></svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
This will result in the following output:
|
||||||
|
|
||||||
|
```html{2}
|
||||||
|
<svg class="lucide lucide-house" ...>
|
||||||
|
<title>Go to dashboard</title>
|
||||||
|
<!-- SVG paths -->
|
||||||
|
</svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
You can pass additional props to adjust the icon appearance.
|
||||||
|
|
||||||
|
| name | type | default |
|
||||||
|
|-----------------------|-----------|--------------|
|
||||||
|
| `size` | *number* | 24 |
|
||||||
|
| `color` | *string* | currentColor |
|
||||||
|
| `strokeWidth` | *number* | 2 |
|
||||||
|
| `absoluteStrokeWidth` | *boolean* | false |
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svg lucideHouse size="48" color="red" strokeWidth="1"></svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Global configuration
|
||||||
|
|
||||||
|
You can use `provideLucideConfig` to configure the default property values as defined above:
|
||||||
|
|
||||||
|
```ts{2,7-9}
|
||||||
|
import { ApplicationConfig } from '@angular/core';
|
||||||
|
import { provideLucideConfig } from '@lucide/angular';
|
||||||
|
|
||||||
|
export const appConfig: ApplicationConfig = {
|
||||||
|
providers: [
|
||||||
|
// ...
|
||||||
|
provideLucideConfig({
|
||||||
|
strokeWidth: 1.5
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Styling via CSS
|
||||||
|
|
||||||
|
Icons can also be styled by using custom CSS classes:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svg lucideHousePlus class="my-icon"></svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
```css
|
||||||
|
svg.my-icon {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
stroke-width: 3;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## With Lucide lab or custom icons
|
||||||
|
|
||||||
|
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
|
||||||
|
|
||||||
|
While they aren't provided as standalone components, they can be still be passed to the `LucideIcon` component the same way as official icons:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Directly as LucideIconData: -->
|
||||||
|
<svg [lucideIcon]="CoconutIcon"></svg>
|
||||||
|
|
||||||
|
<!-- As a provided icon by name: -->
|
||||||
|
<svg lucideIcon="coconut"></svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts{2,6-7,11-12}
|
||||||
|
import { provideLucideIcons } from '@lucide/angular';
|
||||||
|
import { coconut } from '@lucide/lab';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: './foobar.html',
|
||||||
|
// For using by name via provider:
|
||||||
|
providers: [provideLucideIcons({ coconut })],
|
||||||
|
imports: [LucideIcon]
|
||||||
|
})
|
||||||
|
export class Foobar {
|
||||||
|
// For passing directly as LucideIconData:
|
||||||
|
readonly CoconutIcon = coconut;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### The icon is not being displayed
|
||||||
|
If using per-icon-components:
|
||||||
|
1. Ensure that the icon component is being imported, if using per-icon-components
|
||||||
|
2. Check that the icon name matches exactly (case-sensitive)
|
||||||
|
|
||||||
|
If using the dynamic component:
|
||||||
|
1. Ensure the icon is provided via `provideLucideIcons()` if using string names
|
||||||
|
2. Verify the icon is imported from `@lucide/angular` and not the legacy package
|
||||||
|
|
||||||
|
### TypeScript errors?
|
||||||
|
Make sure you're importing from `@lucide/angular` and not `lucide-angular`.
|
||||||
|
|
||||||
|
### Icons render with wrong defaults
|
||||||
|
Ensure `provideLucideConfig()` is used at the right level.
|
||||||
|
|
||||||
|
## Migration guide
|
||||||
|
Migrating from `lucide-angular`? Read our [comprehensive migration guide](https://github.com/lucide-icons/lucide/blob/main/packages/angular/MIGRATION.md).
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
# Lucide Angular
|
# Lucide Angular
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
This documentation if for our legacy package for Angular.
|
||||||
|
|
||||||
|
For our modern, standalone-first implementation, please refer to [`@lucide/angular`](./angular).
|
||||||
|
:::
|
||||||
|
|
||||||
Angular components and services for Lucide icons that integrate with Angular's dependency injection and component system. Provides both traditional module-based and modern standalone component approaches for maximum flexibility in Angular applications.
|
Angular components and services for Lucide icons that integrate with Angular's dependency injection and component system. Provides both traditional module-based and modern standalone component approaches for maximum flexibility in Angular applications.
|
||||||
|
|
||||||
**What you can accomplish:**
|
**What you can accomplish:**
|
||||||
|
|||||||
@@ -102,10 +102,16 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
|
|||||||
|
|
||||||
### Icon Component Example
|
### Icon Component Example
|
||||||
|
|
||||||
```jsx
|
```tsx
|
||||||
import { icons } from 'lucide-react-native';
|
import * as icons from 'lucide-react-native/icons';
|
||||||
|
|
||||||
const Icon = ({ name, color, size }) => {
|
interface IconProps {
|
||||||
|
name: keyof typeof icons;
|
||||||
|
color?: string;
|
||||||
|
size?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Icon = ({ name, color, size }: IconProps) => {
|
||||||
const LucideIcon = icons[name];
|
const LucideIcon = icons[name];
|
||||||
|
|
||||||
return <LucideIcon color={color} size={size} />;
|
return <LucideIcon color={color} size={size} />;
|
||||||
@@ -116,11 +122,11 @@ export default Icon;
|
|||||||
|
|
||||||
#### Using the Icon Component
|
#### Using the Icon Component
|
||||||
|
|
||||||
```jsx
|
```tsx
|
||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return <Icon name="house" />;
|
return <Icon name="House" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@@ -9,14 +9,14 @@
|
|||||||
"docs:build": "pnpm run /^prebuild:.*/ && vitepress build",
|
"docs:build": "pnpm run /^prebuild:.*/ && vitepress build",
|
||||||
"docs:preview": "vitepress preview",
|
"docs:preview": "vitepress preview",
|
||||||
"build:docs": "vitepress build",
|
"build:docs": "vitepress build",
|
||||||
"prebuild:iconNodes": "node --experimental-strip-types ./scripts/writeIconNodes.mjs",
|
"prebuild:iconNodes": "node ./scripts/writeIconNodes.mjs",
|
||||||
"prebuild:metaJson": "node --experimental-strip-types ./scripts/writeIconMetaIndex.mjs",
|
"prebuild:metaJson": "node ./scripts/writeIconMetaIndex.mjs",
|
||||||
"prebuild:releaseJson": "node --experimental-strip-types ./scripts/writeReleaseMetadata.mjs",
|
"prebuild:releaseJson": "node ./scripts/writeReleaseMetadata.mjs",
|
||||||
"prebuild:categoriesJson": "node --experimental-strip-types ./scripts/writeCategoriesMetadata.mjs",
|
"prebuild:categoriesJson": "node ./scripts/writeCategoriesMetadata.mjs",
|
||||||
"prebuild:relatedIcons": "node --experimental-strip-types ./scripts/writeIconRelatedIcons.mjs",
|
"prebuild:relatedIcons": "node ./scripts/writeIconRelatedIcons.mjs",
|
||||||
"prebuild:iconDetails": "node --experimental-strip-types ./scripts/writeIconDetails.mjs",
|
"prebuild:iconDetails": "node ./scripts/writeIconDetails.mjs",
|
||||||
"prebuild:brandStopwords": "node --experimental-strip-types ./scripts/writeBrandStopwords.mjs",
|
"prebuild:brandStopwords": "node ./scripts/writeBrandStopwords.mjs",
|
||||||
"postbuild:vercelJson": "node --experimental-strip-types ./scripts/writeVercelOutput.mjs",
|
"postbuild:vercelJson": "node ./scripts/writeVercelOutput.mjs",
|
||||||
"dev": "npx nitropack dev",
|
"dev": "npx nitropack dev",
|
||||||
"prebuild:api": "npx nitropack prepare",
|
"prebuild:api": "npx nitropack prepare",
|
||||||
"build:api": "npx nitropack build",
|
"build:api": "npx nitropack build",
|
||||||
|
|||||||
22
icons/balloon.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"peteruithoven"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"party",
|
||||||
|
"festival",
|
||||||
|
"congratulations",
|
||||||
|
"celebration",
|
||||||
|
"decoration",
|
||||||
|
"colorful",
|
||||||
|
"floating",
|
||||||
|
"fun",
|
||||||
|
"birthday",
|
||||||
|
"event",
|
||||||
|
"entertainment"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"emoji"
|
||||||
|
]
|
||||||
|
}
|
||||||
15
icons/balloon.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M12 16v1a2 2 0 0 0 2 2h1a2 2 0 0 1 2 2v1" />
|
||||||
|
<path d="M12 6a2 2 0 0 1 2 2" />
|
||||||
|
<path d="M18 8c0 4-3.5 8-6 8s-6-4-6-8a6 6 0 0 1 12 0" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 358 B |
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../icon.schema.json",
|
"$schema": "../icon.schema.json",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"karsa-mistmere"
|
"karsa-mistmere",
|
||||||
|
"jguddas"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"cleaning",
|
"cleaning",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path d="m16 22-1-4" />
|
<path d="m16 22-1-4" />
|
||||||
<path d="M19 13.99a1 1 0 0 0 1-1V12a2 2 0 0 0-2-2h-3a1 1 0 0 1-1-1V4a2 2 0 0 0-4 0v5a1 1 0 0 1-1 1H6a2 2 0 0 0-2 2v.99a1 1 0 0 0 1 1" />
|
<path d="M19 14a1 1 0 0 0 1-1v-1a2 2 0 0 0-2-2h-3a1 1 0 0 1-1-1V4a2 2 0 0 0-4 0v5a1 1 0 0 1-1 1H6a2 2 0 0 0-2 2v1a1 1 0 0 0 1 1" />
|
||||||
<path d="M5 14h14l1.973 6.767A1 1 0 0 1 20 22H4a1 1 0 0 1-.973-1.233z" />
|
<path d="M19 14H5l-1.973 6.767A1 1 0 0 0 4 22h16a1 1 0 0 0 .973-1.233z" />
|
||||||
<path d="m8 22 1-4" />
|
<path d="m8 22 1-4" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 474 B After Width: | Height: | Size: 470 B |
14
icons/cannabis-off.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"nickveles"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"cannabis",
|
||||||
|
"weed",
|
||||||
|
"leaf"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"nature"
|
||||||
|
]
|
||||||
|
}
|
||||||
18
icons/cannabis-off.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M12 22v-4c1.5 1.5 3.5 3 6 3 0-1.5-.5-3.5-2-5" />
|
||||||
|
<path d="M13.988 8.327C13.902 6.054 13.365 3.82 12 2a9.3 9.3 0 0 0-1.445 2.9" />
|
||||||
|
<path d="M17.375 11.725C18.882 10.53 21 7.841 21 6c-2.324 0-5.08 1.296-6.662 2.684" />
|
||||||
|
<path d="m2 2 20 20" />
|
||||||
|
<path d="M21.024 15.378A15 15 0 0 0 22 15c-.426-1.279-2.67-2.557-4.25-2.907" />
|
||||||
|
<path d="M6.995 6.992C5.714 6.4 4.29 6 3 6c0 2 2.5 5 4 6-1.5 0-4.5 1.5-5 3 3.5 1.5 6 1 6 1-1.5 1.5-2 3.5-2 5 2.5 0 4.5-1.5 6-3" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 681 B |
29
icons/circle-pile.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"colebemis",
|
||||||
|
"nathan-de-pachtere"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"off",
|
||||||
|
"zero",
|
||||||
|
"record",
|
||||||
|
"shape",
|
||||||
|
"circle-pile",
|
||||||
|
"circle",
|
||||||
|
"pile",
|
||||||
|
"stack",
|
||||||
|
"layer",
|
||||||
|
"structure",
|
||||||
|
"form",
|
||||||
|
"group",
|
||||||
|
"collection",
|
||||||
|
"stock",
|
||||||
|
"inventory",
|
||||||
|
"materials",
|
||||||
|
"warehouse"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"shapes"
|
||||||
|
]
|
||||||
|
}
|
||||||
9
icons/circle-pile.svg
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||||
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="19" r="2" />
|
||||||
|
<circle cx="12" cy="5" r="2" />
|
||||||
|
<circle cx="16" cy="12" r="2" />
|
||||||
|
<circle cx="20" cy="19" r="2" />
|
||||||
|
<circle cx="4" cy="19" r="2" />
|
||||||
|
<circle cx="8" cy="12" r="2" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 397 B |
33
icons/cloud-backup.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"ericfennis",
|
||||||
|
"jguddas",
|
||||||
|
"danielbayley",
|
||||||
|
"karsa-mistmere"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"storage",
|
||||||
|
"memory",
|
||||||
|
"bytes",
|
||||||
|
"servers",
|
||||||
|
"backup",
|
||||||
|
"timemachine",
|
||||||
|
"rotate",
|
||||||
|
"synchronize",
|
||||||
|
"synchronise",
|
||||||
|
"refresh",
|
||||||
|
"reconnect",
|
||||||
|
"transfer",
|
||||||
|
"data",
|
||||||
|
"security",
|
||||||
|
"upload",
|
||||||
|
"save",
|
||||||
|
"remote",
|
||||||
|
"safety"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"arrows",
|
||||||
|
"files"
|
||||||
|
]
|
||||||
|
}
|
||||||
15
icons/cloud-backup.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M21 15.251A4.5 4.5 0 0 0 17.5 8h-1.79A7 7 0 1 0 3 13.607" />
|
||||||
|
<path d="M7 11v4h4" />
|
||||||
|
<path d="M8 19a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5 4.82 4.82 0 0 0-3.41 1.41L7 15" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 393 B |
27
icons/cloud-sync.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"colebemis",
|
||||||
|
"csandman",
|
||||||
|
"ericfennis",
|
||||||
|
"karsa-mistmere"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"synchronize",
|
||||||
|
"synchronise",
|
||||||
|
"refresh",
|
||||||
|
"reconnect",
|
||||||
|
"transfer",
|
||||||
|
"backup",
|
||||||
|
"storage",
|
||||||
|
"upload",
|
||||||
|
"download",
|
||||||
|
"connection",
|
||||||
|
"network",
|
||||||
|
"data"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"arrows",
|
||||||
|
"files"
|
||||||
|
]
|
||||||
|
}
|
||||||
17
icons/cloud-sync.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="m17 18-1.535 1.605a5 5 0 0 1-8-1.5" />
|
||||||
|
<path d="M17 22v-4h-4" />
|
||||||
|
<path d="M20.996 15.251A4.5 4.5 0 0 0 17.495 8h-1.79a7 7 0 1 0-12.709 5.607" />
|
||||||
|
<path d="M7 10v4h4" />
|
||||||
|
<path d="m7 14 1.535-1.605a5 5 0 0 1 8 1.5" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 442 B |
28
icons/fishing-hook.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"7ender",
|
||||||
|
"jguddas",
|
||||||
|
"karsa-mistmere",
|
||||||
|
"jamiemlaw"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"sea",
|
||||||
|
"boating",
|
||||||
|
"angler",
|
||||||
|
"bait",
|
||||||
|
"reel",
|
||||||
|
"tackle",
|
||||||
|
"marine",
|
||||||
|
"outdoors",
|
||||||
|
"fish",
|
||||||
|
"fishing",
|
||||||
|
"hook",
|
||||||
|
"sports",
|
||||||
|
"travel"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"sports",
|
||||||
|
"travel"
|
||||||
|
]
|
||||||
|
}
|
||||||
15
icons/fishing-hook.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="m17.586 11.414-5.93 5.93a1 1 0 0 1-8-8l3.137-3.137a.707.707 0 0 1 1.207.5V10" />
|
||||||
|
<path d="M20.414 8.586 22 7" />
|
||||||
|
<circle cx="19" cy="10" r="2" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 369 B |
21
icons/hd.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"ahtohbi4",
|
||||||
|
"jamiemlaw",
|
||||||
|
"karsa-mistmere",
|
||||||
|
"jguddas"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"tv",
|
||||||
|
"resolution",
|
||||||
|
"video",
|
||||||
|
"high definition",
|
||||||
|
"720p",
|
||||||
|
"1080p"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"devices",
|
||||||
|
"multimedia"
|
||||||
|
]
|
||||||
|
}
|
||||||
17
icons/hd.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M10 12H6" />
|
||||||
|
<path d="M10 15V9" />
|
||||||
|
<path d="M14 14.5a.5.5 0 0 0 .5.5h1a2.5 2.5 0 0 0 2.5-2.5v-1A2.5 2.5 0 0 0 15.5 9h-1a.5.5 0 0 0-.5.5z" />
|
||||||
|
<path d="M6 15V9" />
|
||||||
|
<rect x="2" y="5" width="20" height="14" rx="2" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 440 B |
22
icons/layers-plus.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"juanisidoro",
|
||||||
|
"karsa-mistmere"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"stack",
|
||||||
|
"layers",
|
||||||
|
"add",
|
||||||
|
"new",
|
||||||
|
"increase",
|
||||||
|
"create",
|
||||||
|
"positive",
|
||||||
|
"copy",
|
||||||
|
"upgrade"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"design",
|
||||||
|
"layout"
|
||||||
|
]
|
||||||
|
}
|
||||||
17
icons/layers-plus.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 .83.18 2 2 0 0 0 .83-.18l8.58-3.9a1 1 0 0 0 0-1.831z" />
|
||||||
|
<path d="M16 17h6" />
|
||||||
|
<path d="M19 14v6" />
|
||||||
|
<path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 .825.178" />
|
||||||
|
<path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l2.116-.962" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 539 B |
@@ -9,13 +9,15 @@
|
|||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path d="M6 19v-3" />
|
<path d="M12 12v-2" />
|
||||||
<path d="M10 19v-3" />
|
<path d="M12 18v-2" />
|
||||||
<path d="M14 19v-3" />
|
<path d="M16 12v-2" />
|
||||||
<path d="M18 19v-3" />
|
<path d="M16 18v-2" />
|
||||||
<path d="M8 11V9" />
|
<path d="M2 11h1.5" />
|
||||||
<path d="M16 11V9" />
|
<path d="M20 18v-2" />
|
||||||
<path d="M12 11V9" />
|
<path d="M20.5 11H22" />
|
||||||
<path d="M2 15h20" />
|
<path d="M4 18v-2" />
|
||||||
<path d="M2 7a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v1.1a2 2 0 0 0 0 3.837V17a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-5.1a2 2 0 0 0 0-3.837Z" />
|
<path d="M8 12v-2" />
|
||||||
|
<path d="M8 18v-2" />
|
||||||
|
<rect x="2" y="6" width="20" height="10" rx="2" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 510 B |
@@ -9,15 +9,14 @@
|
|||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
|
<path d="M10 12h4" />
|
||||||
|
<path d="M10 17h4" />
|
||||||
|
<path d="M10 7h4" />
|
||||||
<path d="M18 12h2" />
|
<path d="M18 12h2" />
|
||||||
<path d="M18 16h2" />
|
<path d="M18 18h2" />
|
||||||
<path d="M18 20h2" />
|
<path d="M18 6h2" />
|
||||||
<path d="M18 4h2" />
|
|
||||||
<path d="M18 8h2" />
|
|
||||||
<path d="M4 12h2" />
|
<path d="M4 12h2" />
|
||||||
<path d="M4 16h2" />
|
<path d="M4 18h2" />
|
||||||
<path d="M4 20h2" />
|
<path d="M4 6h2" />
|
||||||
<path d="M4 4h2" />
|
<rect x="6" y="2" width="12" height="20" rx="2" />
|
||||||
<path d="M4 8h2" />
|
|
||||||
<path d="M8 2a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-1.5c-.276 0-.494.227-.562.495a2 2 0 0 1-3.876 0C9.994 2.227 9.776 2 9.5 2z" />
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 471 B |
@@ -9,8 +9,8 @@
|
|||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path d="m19 11-8-8-8.6 8.6a2 2 0 0 0 0 2.8l5.2 5.2c.8.8 2 .8 2.8 0L19 11Z" />
|
<path d="M11 7 6 2" />
|
||||||
<path d="m5 2 5 5" />
|
<path d="M18.992 12H2.041" />
|
||||||
<path d="M2 13h15" />
|
<path d="M21.145 18.38A3.34 3.34 0 0 1 20 16.5a3.3 3.3 0 0 1-1.145 1.88c-.575.46-.855 1.02-.855 1.595A2 2 0 0 0 20 22a2 2 0 0 0 2-2.025c0-.58-.285-1.13-.855-1.595" />
|
||||||
<path d="M22 20a2 2 0 1 1-4 0c0-1.6 1.7-2.4 2-4 .3 1.6 2 2.4 2 4Z" />
|
<path d="m8.5 4.5 2.148-2.148a1.205 1.205 0 0 1 1.704 0l7.296 7.296a1.205 1.205 0 0 1 0 1.704l-7.592 7.592a3.615 3.615 0 0 1-5.112 0l-3.888-3.888a3.615 3.615 0 0 1 0-5.112L5.67 7.33" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 622 B |
25
icons/search-alert.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"colebemis",
|
||||||
|
"ericfennis",
|
||||||
|
"jguddas",
|
||||||
|
"Veatec22"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"find",
|
||||||
|
"scan",
|
||||||
|
"magnifier",
|
||||||
|
"magnifying glass",
|
||||||
|
"stop",
|
||||||
|
"warning",
|
||||||
|
"alert",
|
||||||
|
"error",
|
||||||
|
"anomaly",
|
||||||
|
"lens"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"text",
|
||||||
|
"social"
|
||||||
|
]
|
||||||
|
}
|
||||||
16
icons/search-alert.svg
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.3-4.3" />
|
||||||
|
<path d="M11 7v4" />
|
||||||
|
<path d="M11 15h.01" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 321 B |
24
icons/stone.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"Alportan",
|
||||||
|
"karsa-mistmere"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"mineral",
|
||||||
|
"geology",
|
||||||
|
"nature",
|
||||||
|
"solid",
|
||||||
|
"pebble",
|
||||||
|
"crystal",
|
||||||
|
"ore",
|
||||||
|
"hard",
|
||||||
|
"coal",
|
||||||
|
"stone",
|
||||||
|
"rock",
|
||||||
|
"boulder"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"nature"
|
||||||
|
]
|
||||||
|
}
|
||||||
15
icons/stone.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M11.264 2.205A4 4 0 0 0 6.42 4.211l-4 8a4 4 0 0 0 1.359 5.117l6 4a4 4 0 0 0 4.438 0l6-4a4 4 0 0 0 1.576-4.592l-2-6a4 4 0 0 0-2.53-2.53z" />
|
||||||
|
<path d="M11.99 22 14 12l7.822 3.184" />
|
||||||
|
<path d="M14 12 8.47 2.302" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 435 B |
@@ -9,6 +9,6 @@
|
|||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path d="M17 14V2" />
|
|
||||||
<path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z" />
|
<path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z" />
|
||||||
|
<path d="M17 14V2" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 399 B After Width: | Height: | Size: 399 B |
@@ -9,6 +9,6 @@
|
|||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path d="M7 10v12" />
|
|
||||||
<path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z" />
|
<path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z" />
|
||||||
|
<path d="M7 10v12" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 400 B After Width: | Height: | Size: 400 B |
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../icon.schema.json",
|
"$schema": "../icon.schema.json",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"jguddas"
|
"jguddas",
|
||||||
|
"karsa-mistmere"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"plane",
|
"plane",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
>
|
>
|
||||||
<path d="M10.5 17h1.227a2 2 0 0 0 1.345-.52L18 12" />
|
<path d="M10.5 17h1.227a2 2 0 0 0 1.345-.52L18 12" />
|
||||||
<path d="m12 13.5 3.75.5" />
|
<path d="m12 13.5 3.75.5" />
|
||||||
<path d="m4.5 8 10.58-5.06a1 1 0 0 1 1.342.488L18.5 8" />
|
<path d="m3.173 8.18 11-5a2 2 0 0 1 2.647.993L18.56 8" />
|
||||||
<path d="M6 10V8" />
|
<path d="M6 10V8" />
|
||||||
<path d="M6 14v1" />
|
<path d="M6 14v1" />
|
||||||
<path d="M6 19v2" />
|
<path d="M6 19v2" />
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 477 B |
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../icon.schema.json",
|
"$schema": "../icon.schema.json",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"jguddas"
|
"jguddas",
|
||||||
|
"karsa-mistmere"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"trip",
|
"trip",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path d="m4.5 8 10.58-5.06a1 1 0 0 1 1.342.488L18.5 8" />
|
<path d="m3.173 8.18 11-5a2 2 0 0 1 2.647.993L18.56 8" />
|
||||||
<path d="M6 10V8" />
|
<path d="M6 10V8" />
|
||||||
<path d="M6 14v1" />
|
<path d="M6 14v1" />
|
||||||
<path d="M6 19v2" />
|
<path d="M6 19v2" />
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 390 B |
37
icons/toolbox.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"karsa-mistmere"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"toolkit",
|
||||||
|
"tools",
|
||||||
|
"trunk",
|
||||||
|
"chest",
|
||||||
|
"box",
|
||||||
|
"storage",
|
||||||
|
"utility",
|
||||||
|
"utilities",
|
||||||
|
"container",
|
||||||
|
"kit",
|
||||||
|
"set",
|
||||||
|
"repair",
|
||||||
|
"fix",
|
||||||
|
"service",
|
||||||
|
"maintenance",
|
||||||
|
"mechanic",
|
||||||
|
"workshop",
|
||||||
|
"construction",
|
||||||
|
"hardware",
|
||||||
|
"equipment",
|
||||||
|
"gear",
|
||||||
|
"handyman",
|
||||||
|
"engineering",
|
||||||
|
"craft",
|
||||||
|
"diy"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"tools",
|
||||||
|
"home"
|
||||||
|
]
|
||||||
|
}
|
||||||
17
icons/toolbox.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M16 12v4" />
|
||||||
|
<path d="M16 6a2 2 0 0 1 1.414.586l4 4A2 2 0 0 1 22 12v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 .586-1.414l4-4A2 2 0 0 1 8 6z" />
|
||||||
|
<path d="M16 6V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" />
|
||||||
|
<path d="M2 14h20" />
|
||||||
|
<path d="M8 12v4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 471 B |
@@ -7,8 +7,8 @@ const filenamesToAjvOption = (filenames) => filenames.map((filename) => `-d ${fi
|
|||||||
/** @satisfies {import('lint-staged').Config} */
|
/** @satisfies {import('lint-staged').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
'icons/*.svg': [
|
'icons/*.svg': [
|
||||||
'node ./scripts/optimizeStagedSvgs.mjs',
|
'node ./scripts/optimizeStagedSvgs.mts',
|
||||||
'node ./scripts/generateNextJSAliases.mjs',
|
'node ./scripts/generateNextJSAliases.mts',
|
||||||
],
|
],
|
||||||
'icons/*.json': (filenames) => [
|
'icons/*.json': (filenames) => [
|
||||||
`ajv --spec=draft2020 -s icon.schema.json ${filenamesToAjvOption(filenames)}`,
|
`ajv --spec=draft2020 -s icon.schema.json ${filenamesToAjvOption(filenames)}`,
|
||||||
|
|||||||
@@ -73,9 +73,9 @@
|
|||||||
"zod": "^3.25.67"
|
"zod": "^3.25.67"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=23.0.0"
|
"node": ">=24.11.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.23.0+sha512.21c4e5698002ade97e4efe8b8b4a89a8de3c85a37919f957e7a0f30f38fbc5bbdd05980ffe29179b2fb6e6e691242e098d945d1601772cad0fef5fb6411e2a4b",
|
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"packageExtensions": {
|
"packageExtensions": {
|
||||||
"vue-template-compiler": {
|
"vue-template-compiler": {
|
||||||
|
|||||||
38
packages/angular/.eslintrc.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts'],
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:@angular-eslint/recommended',
|
||||||
|
'plugin:@angular-eslint/template/process-inline-templates',
|
||||||
|
'prettier',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'@angular-eslint/directive-selector': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
type: 'attribute',
|
||||||
|
prefix: 'lucide',
|
||||||
|
style: 'camelCase',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@angular-eslint/component-selector': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
type: 'attribute',
|
||||||
|
prefix: ['lucide'],
|
||||||
|
style: 'camelCase',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.html'],
|
||||||
|
extends: ['plugin:@angular-eslint/template/recommended'],
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
4
packages/angular/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||||
|
"recommendations": ["angular.ng-template"]
|
||||||
|
}
|
||||||
20
packages/angular/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "ng serve",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "npm: start",
|
||||||
|
"url": "http://localhost:4200/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng test",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "npm: test",
|
||||||
|
"url": "http://localhost:9876/debug.html"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
42
packages/angular/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "start",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "typescript",
|
||||||
|
"pattern": "$tsc",
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": {
|
||||||
|
"regexp": "(.*?)"
|
||||||
|
},
|
||||||
|
"endsPattern": {
|
||||||
|
"regexp": "bundle generation complete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "test",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "typescript",
|
||||||
|
"pattern": "$tsc",
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": {
|
||||||
|
"regexp": "(.*?)"
|
||||||
|
},
|
||||||
|
"endsPattern": {
|
||||||
|
"regexp": "bundle generation complete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
184
packages/angular/MIGRATION.md
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
# Migrating from `lucide-angular` ⇒ `@lucide/angular`
|
||||||
|
|
||||||
|
## What changed
|
||||||
|
|
||||||
|
`@lucide/angular` moves from a module + single component based API to a more modern Angular approach:
|
||||||
|
|
||||||
|
- The library defines modern signal-based, standalone components, without zone.js based change detection.
|
||||||
|
- Icons are consumed as standalone imports (one component per icon).
|
||||||
|
- Dynamic icon registration is done via `provideLucideIcon()`, not using `NgModule`.
|
||||||
|
- Static icons use per-icon components for better tree-shaking.
|
||||||
|
- Dynamic icons still use a single dynamic component (`svg[lucideIcon]`).
|
||||||
|
- Global defaults are configured via `provideLucideConfig()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1 – Update dependencies
|
||||||
|
|
||||||
|
Remove `lucide-angular`, add `@lucide/angular`, see http://lucide.dev/guide/packages/angular#installation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 2 – Replace `LucideAngularModule.pick(...)` with `provideLucideIcons(...)`
|
||||||
|
|
||||||
|
> Notes:
|
||||||
|
> - Old imports like `AirVentIcon` / `AlarmClock` from `lucide-angular` should be replaced with the new per-icon exports `LucideAirVent` and `LucideAlarmClock`.
|
||||||
|
> - If you mostly used static icons, you may not need to provide them **at all**, please refer to Step 3.
|
||||||
|
|
||||||
|
### Before
|
||||||
|
|
||||||
|
#### NgModule based
|
||||||
|
```ts
|
||||||
|
import { LucideAngularModule, AirVent, AlarmClock } from 'lucide-angular';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
LucideAngularModule.pick({ AirVent, AlarmClock }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Standalone
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { ApplicationConfig } from '@angular/core';
|
||||||
|
|
||||||
|
export const appConfig: ApplicationConfig = {
|
||||||
|
providers: [
|
||||||
|
importProvidersFrom(LucideAngularModule.pick({ AirVent, AlarmClock })),
|
||||||
|
]
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### After
|
||||||
|
```ts
|
||||||
|
import { ApplicationConfig } from '@angular/core';
|
||||||
|
import { provideLucideIcons, LucideAirVent, LucideAlarmClock } from '@lucide/angular';
|
||||||
|
|
||||||
|
export const appConfig: ApplicationConfig = {
|
||||||
|
providers: [
|
||||||
|
// ...
|
||||||
|
provideLucideIcons([
|
||||||
|
LucideAirVent,
|
||||||
|
LucideAlarmClock,
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 3 – Replace `<lucide-angular>` / `<lucide-icon>` / `<i-lucide>` / `<span-lucide>`
|
||||||
|
|
||||||
|
The legacy package rendered everything through a single component. All of these selectors must be migrated to `<svg>` usage.
|
||||||
|
|
||||||
|
### A. Static icons by name
|
||||||
|
|
||||||
|
If the icon is known at build time, just use a static import:
|
||||||
|
|
||||||
|
#### Before
|
||||||
|
```html
|
||||||
|
<lucide-angular name="circle-check"></lucide-angular>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### After
|
||||||
|
```html
|
||||||
|
<svg lucideCircleCheck></svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
### B. Static icons with icon data binding
|
||||||
|
|
||||||
|
#### Before
|
||||||
|
```ts
|
||||||
|
import { CircleCheck } from 'lucide-angular';
|
||||||
|
```
|
||||||
|
|
||||||
|
```html
|
||||||
|
<lucide-icon [img]="CircleCheck"></lucide-icon>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### After
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { LucideCircleCheck } from '@lucide/angular';
|
||||||
|
```
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svg lucideCircleCheck></svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
...and import `LucideCircleCheck` from `@lucide/angular`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### C. Dynamic icons
|
||||||
|
|
||||||
|
If the icon varies at runtime, use the dynamic component:
|
||||||
|
|
||||||
|
#### Before
|
||||||
|
```html
|
||||||
|
<lucide-icon [name]="item.icon"></lucide-icon>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### After
|
||||||
|
```html
|
||||||
|
<svg [lucideIcon]="item.icon"></svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 4 – Replace `LucideIconConfig` with `provideLucideConfig()`
|
||||||
|
|
||||||
|
### Before
|
||||||
|
```ts
|
||||||
|
import { inject } from '@angular/core';
|
||||||
|
import { LucideIconConfig } from 'lucide-angular';
|
||||||
|
|
||||||
|
inject(LucideIconConfig).size = 12;
|
||||||
|
```
|
||||||
|
|
||||||
|
### After
|
||||||
|
```ts
|
||||||
|
import { provideLucideConfig } from '@lucide/angular';
|
||||||
|
|
||||||
|
providers: [
|
||||||
|
provideLucideConfig({ size: 12 }),
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Where to place it
|
||||||
|
|
||||||
|
- App-wide: `AppModule.providers` or `bootstrapApplication(...providers)`
|
||||||
|
- Feature-level: feature module providers
|
||||||
|
- Component-level (standalone): component `providers`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### The icon is not being displayed
|
||||||
|
If using per-icon-components:
|
||||||
|
1. Ensure that the icon component is being imported, if using per-icon-components
|
||||||
|
2. Check that the icon name matches exactly (case-sensitive)
|
||||||
|
|
||||||
|
If using the dynamic component:
|
||||||
|
1. Ensure the icon is provided via `provideLucideIcons()` if using string names
|
||||||
|
2. Verify the icon is imported from `@lucide/angular` and not the legacy package
|
||||||
|
|
||||||
|
### TypeScript errors?
|
||||||
|
Make sure you're importing from `@lucide/angular` and not `lucide-angular`.
|
||||||
|
|
||||||
|
### Icons render with wrong defaults
|
||||||
|
Ensure `provideLucideConfig()` is used at the right level.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TL;DR
|
||||||
|
- `LucideAngularModule` ⇒ static: removed; dynamic: `LucideIcon`
|
||||||
|
- `LucideAngularModule.pick(...)` ⇒ `provideLucideIcons(...)`
|
||||||
|
- `<lucide-angular name="foo-bar">` ⇒ `<svg lucideFooBar>`
|
||||||
|
- `<lucide-icon [name]="expr">` ⇒ `<svg [lucideIcon]="expr">`
|
||||||
|
- `<lucide-icon [img]="expr">` ⇒ `<svg [lucideIcon]="expr">`
|
||||||
|
- `LucideIconConfig` ⇒ `provideLucideConfig(...)`
|
||||||
77
packages/angular/README.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/lucide-icons/lucide">
|
||||||
|
<img src="https://lucide.dev/package-logos/lucide-angular.svg" alt="Lucide icon library for Angular applications." width="540">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
Lucide icon library for Angular applications.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
[](https://www.npmjs.com/package/lucide-angular)
|
||||||
|

|
||||||
|
[](https://lucide.dev/license)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://lucide.dev/guide/">About</a>
|
||||||
|
·
|
||||||
|
<a href="https://lucide.dev/icons/">Icons</a>
|
||||||
|
·
|
||||||
|
<a href="https://lucide.dev/guide/packages/lucide-angular">Documentation</a>
|
||||||
|
·
|
||||||
|
<a href="https://lucide.dev/license">License</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
# Lucide Angular
|
||||||
|
|
||||||
|
A standalone, signal based, zoneless implementation of the Lucide icon library for Angular applications.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm add @lucide/angular
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install @lucide/angular
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @lucide/angular
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun add @lucide/angular
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
For full documentation, visit [lucide.dev](https://lucide.dev/guide/packages/angular)
|
||||||
|
|
||||||
|
## Migration guide
|
||||||
|
|
||||||
|
Migrating from `lucide-angular`? Read our [comprehensive migration guide](./MIGRATION.md).
|
||||||
|
|
||||||
|
## Community
|
||||||
|
|
||||||
|
Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintainers and other users.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
|
||||||
|
|
||||||
|
## Sponsors
|
||||||
|
|
||||||
|
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
|
||||||
|
<img src="https://lucide.dev/vercel.svg" alt="Powered by Vercel" width="200" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
||||||
|
|
||||||
|
### Awesome backers 🍺
|
||||||
|
|
||||||
|
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
||||||
|
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
||||||
51
packages/angular/angular.json
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"cli": {
|
||||||
|
"packageManager": "pnpm"
|
||||||
|
},
|
||||||
|
"newProjectRoot": ".",
|
||||||
|
"projects": {
|
||||||
|
"@lucide/angular": {
|
||||||
|
"projectType": "library",
|
||||||
|
"root": ".",
|
||||||
|
"sourceRoot": "./src",
|
||||||
|
"prefix": "lib",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular/build:ng-packagr",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"tsConfig": "./tsconfig.lib.prod.json"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"tsConfig": "./tsconfig.lib.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular/build:unit-test",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": "./tsconfig.spec.json",
|
||||||
|
"coverage": true,
|
||||||
|
"coverageReporters": ["html", "lcov"],
|
||||||
|
"coverageExclude": ["src/icons/*"],
|
||||||
|
"coverageThresholds": {
|
||||||
|
"statements": 80,
|
||||||
|
"branches": 80,
|
||||||
|
"functions": 80,
|
||||||
|
"lines": 80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-eslint/builder:lint",
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
packages/angular/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
|
||||||
|
"dest": "./dist",
|
||||||
|
"lib": {
|
||||||
|
"entryFile": "./src/public-api.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
76
packages/angular/package.json
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"name": "@lucide/angular",
|
||||||
|
"description": "A Lucide icon library package for Angular applications.",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"author": "SMAH1",
|
||||||
|
"license": "ISC",
|
||||||
|
"homepage": "https://lucide.dev",
|
||||||
|
"bugs": "https://github.com/lucide-icons/lucide/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/lucide-icons/lucide.git",
|
||||||
|
"directory": "packages/lucide-angular"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"directory": "dist"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"watch": "ng build --watch --configuration development",
|
||||||
|
"prebuild": "pnpm clean && pnpm copy:license && pnpm build:icons",
|
||||||
|
"build": "pnpm prebuild && pnpm build:ng",
|
||||||
|
"copy:license": "cp ../../LICENSE ./LICENSE",
|
||||||
|
"clean": "rm -rf dist && rm -rf ./src/icons/*.ts",
|
||||||
|
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --iconFileExtension=.ts --exportFileName=lucide-angular.ts --useDefaultExports=0",
|
||||||
|
"build:ng": "ng build --configuration production",
|
||||||
|
"test": "pnpm prebuild && ng test --no-watch",
|
||||||
|
"test:watch": "ng test",
|
||||||
|
"lint": "npx eslint 'src/**/*.{js,jsx,ts,tsx,html,css,scss}' --quiet --fix",
|
||||||
|
"e2e": "ng e2e",
|
||||||
|
"version": "pnpm version --git-tag-version=false"
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"printWidth": 100,
|
||||||
|
"singleQuote": true,
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.html",
|
||||||
|
"options": {
|
||||||
|
"parser": "angular"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular-eslint/builder": "~21.1.0",
|
||||||
|
"@angular-eslint/eslint-plugin": "~21.1.0",
|
||||||
|
"@angular-eslint/eslint-plugin-template": "~21.1.0",
|
||||||
|
"@angular-eslint/schematics": "~21.1.0",
|
||||||
|
"@angular-eslint/template-parser": "~21.1.0",
|
||||||
|
"@angular/build": "^21.0.3",
|
||||||
|
"@angular/cli": "^21.0.3",
|
||||||
|
"@angular/common": "^21.0.0",
|
||||||
|
"@angular/compiler": "^21.0.0",
|
||||||
|
"@angular/compiler-cli": "^21.0.0",
|
||||||
|
"@angular/core": "^21.0.0",
|
||||||
|
"@angular/forms": "^21.0.0",
|
||||||
|
"@angular/platform-browser": "^21.0.0",
|
||||||
|
"@angular/router": "^21.0.0",
|
||||||
|
"@lucide/build-icons": "workspace:*",
|
||||||
|
"@lucide/helpers": "workspace:*",
|
||||||
|
"@vitest/browser-playwright": "^4.0.16",
|
||||||
|
"@vitest/coverage-v8": "^4.0.16",
|
||||||
|
"angular-eslint": "21.1.0",
|
||||||
|
"jsdom": "^27.1.0",
|
||||||
|
"ng-packagr": "^21.0.0",
|
||||||
|
"rxjs": "~7.8.0",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"typescript": "~5.9.2",
|
||||||
|
"vitest": "^4.0.16"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "17.x - 21.x",
|
||||||
|
"@angular/core": "17.x - 21.x"
|
||||||
|
}
|
||||||
|
}
|
||||||
68
packages/angular/scripts/exportTemplate.mts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import base64SVG from '@lucide/build-icons/utils/base64SVG';
|
||||||
|
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
|
||||||
|
import { toPascalCase } from '@lucide/helpers';
|
||||||
|
|
||||||
|
export default defineExportTemplate(async ({
|
||||||
|
componentName,
|
||||||
|
iconName,
|
||||||
|
children,
|
||||||
|
getSvg,
|
||||||
|
deprecated,
|
||||||
|
deprecationReason,
|
||||||
|
aliases = [],
|
||||||
|
}) => {
|
||||||
|
const svgContents = await getSvg();
|
||||||
|
const svgBase64 = base64SVG(svgContents);
|
||||||
|
const angularComponentName = `Lucide${componentName}`;
|
||||||
|
const selectors = [`svg[lucide${toPascalCase(iconName)}]`];
|
||||||
|
const aliasComponentNames: string[] = [];
|
||||||
|
for (const alias of aliases) {
|
||||||
|
const aliasName = typeof alias === 'string' ? alias : alias.name;
|
||||||
|
const aliasComponentName = `Lucide${toPascalCase(aliasName)}`;
|
||||||
|
const aliasSelector = `svg[lucide${toPascalCase(aliasName)}]`;
|
||||||
|
if (!selectors.includes(aliasSelector)) {
|
||||||
|
selectors.push(aliasSelector);
|
||||||
|
}
|
||||||
|
if (aliasComponentName !== angularComponentName && !aliasComponentNames.includes(aliasComponentName)) {
|
||||||
|
aliasComponentNames.push(aliasComponentName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `\
|
||||||
|
import { LucideIconData } from '../types';
|
||||||
|
import { LucideIconBase } from '../lucide-icon-base';
|
||||||
|
import { Component, signal } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @component @name ${componentName}
|
||||||
|
* @description Lucide SVG icon component, renders SVG Element with children.
|
||||||
|
*
|
||||||
|
* @preview  - https://lucide.dev/icons/${iconName}
|
||||||
|
* @see https://lucide.dev/guide/packages/lucide-angular - Documentation
|
||||||
|
*
|
||||||
|
* @param {Object} props - Lucide icons props and any valid SVG attribute
|
||||||
|
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: '${selectors.join(', ')}',
|
||||||
|
templateUrl: '../lucide-icon.html',
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
export class ${angularComponentName} extends LucideIconBase {
|
||||||
|
static readonly iconName = '${iconName}';
|
||||||
|
static readonly iconData: LucideIconData = ${JSON.stringify(children)};
|
||||||
|
protected override readonly iconName = signal(${angularComponentName}.iconName);
|
||||||
|
protected override readonly iconData = signal(${angularComponentName}.iconData);
|
||||||
|
}
|
||||||
|
|
||||||
|
${aliasComponentNames.map((aliasComponentName) => {
|
||||||
|
return `
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @see ${angularComponentName}
|
||||||
|
*/
|
||||||
|
export const ${aliasComponentName} = ${angularComponentName};
|
||||||
|
`;
|
||||||
|
}).join(`\n\n`)}
|
||||||
|
`;
|
||||||
|
});
|
||||||
11
packages/angular/src/default-attributes.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
xmlns: 'http://www.w3.org/2000/svg',
|
||||||
|
width: '24',
|
||||||
|
height: '24',
|
||||||
|
viewBox: '0 0 24 24',
|
||||||
|
fill: 'none',
|
||||||
|
stroke: 'currentColor',
|
||||||
|
'stroke-width': '2',
|
||||||
|
'stroke-linecap': 'round',
|
||||||
|
'stroke-linejoin': 'round',
|
||||||
|
};
|
||||||
25
packages/angular/src/lucide-config.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { LUCIDE_CONFIG, lucideDefaultConfig, provideLucideConfig } from './lucide-config';
|
||||||
|
|
||||||
|
describe('Lucide config', () => {
|
||||||
|
describe('LUCIDE_CONFIG', () => {
|
||||||
|
it('should use default', () => {
|
||||||
|
expect(TestBed.inject(LUCIDE_CONFIG)).toBe(lucideDefaultConfig);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('provideLucideConfig', () => {
|
||||||
|
it('should use defaults', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
provideLucideConfig({
|
||||||
|
size: 18,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(TestBed.inject(LUCIDE_CONFIG)).toEqual({
|
||||||
|
...lucideDefaultConfig,
|
||||||
|
size: 18,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
67
packages/angular/src/lucide-config.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { InjectionToken, Provider } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lucide icon configuration options.
|
||||||
|
*/
|
||||||
|
export interface LucideConfig {
|
||||||
|
/**
|
||||||
|
* Stroke color.
|
||||||
|
* @default currentColor
|
||||||
|
*/
|
||||||
|
color: string;
|
||||||
|
/**
|
||||||
|
* Width and height.
|
||||||
|
* @default 24
|
||||||
|
*/
|
||||||
|
size: number;
|
||||||
|
/**
|
||||||
|
* Stroke width
|
||||||
|
* @default 2
|
||||||
|
*/
|
||||||
|
strokeWidth: number;
|
||||||
|
/**
|
||||||
|
* Whether stroke width should be scaled to appear uniform regardless of icon size.
|
||||||
|
* @default false
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Use CSS to set on SVG paths instead:
|
||||||
|
* ```css
|
||||||
|
* .lucide * {
|
||||||
|
* vector-effect: non-scaling-stroke;
|
||||||
|
* }`
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
absoluteStrokeWidth: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default icon configuration options.
|
||||||
|
*/
|
||||||
|
export const lucideDefaultConfig: LucideConfig = {
|
||||||
|
color: 'currentColor',
|
||||||
|
size: 24,
|
||||||
|
strokeWidth: 2,
|
||||||
|
absoluteStrokeWidth: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection token for providing default configuration options.
|
||||||
|
*
|
||||||
|
* @internal Use {@link provideLucideConfig}
|
||||||
|
*/
|
||||||
|
export const LUCIDE_CONFIG = new InjectionToken<LucideConfig>('Lucide icon config', {
|
||||||
|
factory: () => lucideDefaultConfig,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider for default icon configuration options.
|
||||||
|
*/
|
||||||
|
export function provideLucideConfig(config: Partial<LucideConfig>): Provider {
|
||||||
|
return {
|
||||||
|
provide: LUCIDE_CONFIG,
|
||||||
|
useValue: {
|
||||||
|
...lucideDefaultConfig,
|
||||||
|
...config,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
149
packages/angular/src/lucide-icon-base.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
computed,
|
||||||
|
effect,
|
||||||
|
ElementRef,
|
||||||
|
inject,
|
||||||
|
input,
|
||||||
|
Renderer2,
|
||||||
|
Signal,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { LUCIDE_CONFIG } from './lucide-config';
|
||||||
|
import { LucideIconData, Nullable } from './types';
|
||||||
|
import defaultAttributes from './default-attributes';
|
||||||
|
import { formatFixed } from './utils/format-fixed';
|
||||||
|
import { toKebabCase } from './utils/to-kebab-case';
|
||||||
|
|
||||||
|
function transformNumericStringInput(
|
||||||
|
value: Nullable<string | number>,
|
||||||
|
defaultValue: number,
|
||||||
|
): number {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const parsedValue = parseInt(value, 10);
|
||||||
|
if (isNaN(parsedValue)) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return parsedValue;
|
||||||
|
}
|
||||||
|
return value ?? defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
// eslint-disable-next-line @angular-eslint/component-selector
|
||||||
|
selector: 'svg[lucideIcon]',
|
||||||
|
templateUrl: './lucide-icon.html',
|
||||||
|
host: {
|
||||||
|
...defaultAttributes,
|
||||||
|
class: 'lucide',
|
||||||
|
'[attr.width]': 'size().toString(10)',
|
||||||
|
'[attr.height]': 'size().toString(10)',
|
||||||
|
'[attr.stroke]': 'color()',
|
||||||
|
'[attr.stroke-width]': 'computedStrokeWidth()',
|
||||||
|
'[attr.aria-hidden]': 'ariaHidden()',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export abstract class LucideIconBase {
|
||||||
|
protected abstract readonly iconName: Signal<Nullable<string>>;
|
||||||
|
protected abstract readonly iconData: Signal<Nullable<LucideIconData>>;
|
||||||
|
protected readonly iconConfig = inject(LUCIDE_CONFIG);
|
||||||
|
protected readonly elRef = inject(ElementRef);
|
||||||
|
protected readonly renderer = inject(Renderer2);
|
||||||
|
protected readonly ariaHidden = computed(() => {
|
||||||
|
return !this.title();
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* An optional accessible label for the icon.
|
||||||
|
* - If provided, it will add the title as an [`<svg:title>` element](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/title).
|
||||||
|
* - If not provided, the component will add an `aria-hidden="true"` attribute automatically.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Please refer to our [Accessibility guide](https://lucide.dev/guide/advanced/accessibility) regarding this matter.
|
||||||
|
* Adding accessible labels to icons is normally not necessary:
|
||||||
|
* - If your icon is decorative (as most icons are) just leave it as hidden from screen readers.
|
||||||
|
* - If your icon is interactive, it should be contained within an interactive element (e.g. button), and you should probably set your accessible label on that element.
|
||||||
|
* - If your icon is functional (e.g. used in place of a label), feel free to use this property.
|
||||||
|
*/
|
||||||
|
readonly title = input<Nullable<string>>();
|
||||||
|
/**
|
||||||
|
* Width and height.
|
||||||
|
* @default 24
|
||||||
|
*/
|
||||||
|
readonly size = input(this.iconConfig.size, {
|
||||||
|
transform: (value: Nullable<string | number>) =>
|
||||||
|
transformNumericStringInput(value, this.iconConfig.size),
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* Stroke color.
|
||||||
|
* @default currentColor
|
||||||
|
*/
|
||||||
|
readonly color = input(this.iconConfig.color, {
|
||||||
|
transform: (value: Nullable<string>) => value ?? this.iconConfig.color,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* Stroke width
|
||||||
|
* @default 2
|
||||||
|
*/
|
||||||
|
readonly strokeWidth = input(this.iconConfig.strokeWidth, {
|
||||||
|
transform: (value: Nullable<string | number>) =>
|
||||||
|
transformNumericStringInput(value, this.iconConfig.strokeWidth),
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* Whether stroke width should be scaled to appear uniform regardless of icon size.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Use CSS to set on SVG paths instead:
|
||||||
|
* ```css
|
||||||
|
* .lucide * {
|
||||||
|
* vector-effect: non-scaling-stroke;
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
readonly absoluteStrokeWidth = input(this.iconConfig.absoluteStrokeWidth, {
|
||||||
|
transform: (value: Nullable<boolean>) => value ?? this.iconConfig.absoluteStrokeWidth,
|
||||||
|
});
|
||||||
|
protected readonly computedStrokeWidth = computed(() => {
|
||||||
|
const strokeWidth = this.strokeWidth();
|
||||||
|
const size = this.size();
|
||||||
|
return this.absoluteStrokeWidth()
|
||||||
|
? formatFixed(strokeWidth / (size / 24))
|
||||||
|
: strokeWidth.toString(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
effect((onCleanup) => {
|
||||||
|
const icon = this.iconData();
|
||||||
|
if (icon) {
|
||||||
|
const elements = icon.map(([name, attrs]) => {
|
||||||
|
const element = this.renderer.createElement(name, 'http://www.w3.org/2000/svg');
|
||||||
|
Object.entries(attrs).forEach(([name, value]) =>
|
||||||
|
this.renderer.setAttribute(
|
||||||
|
element,
|
||||||
|
name,
|
||||||
|
typeof value === 'number' ? value.toString(10) : value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
this.renderer.appendChild(this.elRef.nativeElement, element);
|
||||||
|
return element;
|
||||||
|
});
|
||||||
|
onCleanup(() => {
|
||||||
|
elements.forEach((element) =>
|
||||||
|
this.renderer.removeChild(this.elRef.nativeElement, element),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
effect((onCleanup) => {
|
||||||
|
const name = this.iconName();
|
||||||
|
if (name) {
|
||||||
|
const cssClass = `lucide-${toKebabCase(name)}`;
|
||||||
|
this.renderer.addClass(this.elRef.nativeElement, cssClass);
|
||||||
|
onCleanup(() => {
|
||||||
|
this.renderer.removeClass(this.elRef.nativeElement, cssClass);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
4
packages/angular/src/lucide-icon.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@if (title(); as titleValue) {
|
||||||
|
<title>{{ titleValue }}</title>
|
||||||
|
}
|
||||||
|
<ng-content />
|
||||||
243
packages/angular/src/lucide-icon.spec.ts
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
import { Component, input, inputBinding, signal, WritableSignal } from '@angular/core';
|
||||||
|
import { LucideIcon } from './lucide-icon';
|
||||||
|
import { LucideIconData, LucideIconInput } from './types';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { provideLucideIcons } from './lucide-icons';
|
||||||
|
import { LucideActivity } from './icons/activity';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `@if (icon(); as iconData) {
|
||||||
|
<svg [lucideIcon]="iconData">
|
||||||
|
<rect x="1" y="1" width="22" height="22" />
|
||||||
|
</svg>
|
||||||
|
}`,
|
||||||
|
imports: [LucideIcon],
|
||||||
|
})
|
||||||
|
class TestHostComponent {
|
||||||
|
readonly icon = input<LucideIconData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('LucideIcon', () => {
|
||||||
|
let component: LucideIcon;
|
||||||
|
let fixture: ComponentFixture<LucideIcon>;
|
||||||
|
let icon: WritableSignal<LucideIconInput | null | undefined>;
|
||||||
|
let name: WritableSignal<string | undefined>;
|
||||||
|
let title: WritableSignal<string | undefined>;
|
||||||
|
let color: WritableSignal<string | undefined>;
|
||||||
|
let size: WritableSignal<string | number | undefined>;
|
||||||
|
let strokeWidth: WritableSignal<string | number | undefined>;
|
||||||
|
let absoluteStrokeWidth: WritableSignal<boolean | undefined>;
|
||||||
|
const getSvgAttribute = (attr: string) => fixture.nativeElement.getAttribute(attr);
|
||||||
|
const testIcon: LucideIconData = [['polyline', { points: '1 1 22 22' }]];
|
||||||
|
const testIcon2: LucideIconData = [
|
||||||
|
['circle', { cx: 12, cy: 12, r: 8 }],
|
||||||
|
['polyline', { points: '1 1 22 22' }],
|
||||||
|
];
|
||||||
|
beforeEach(async () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [provideLucideIcons({ demo: testIcon })],
|
||||||
|
});
|
||||||
|
icon = signal('demo');
|
||||||
|
name = signal(undefined);
|
||||||
|
title = signal(undefined);
|
||||||
|
color = signal(undefined);
|
||||||
|
size = signal(undefined);
|
||||||
|
strokeWidth = signal(undefined);
|
||||||
|
absoluteStrokeWidth = signal(undefined);
|
||||||
|
fixture = TestBed.createComponent(LucideIcon, {
|
||||||
|
inferTagName: true,
|
||||||
|
bindings: [
|
||||||
|
inputBinding('lucideIcon', icon),
|
||||||
|
inputBinding('name', name),
|
||||||
|
inputBinding('title', title),
|
||||||
|
inputBinding('color', color),
|
||||||
|
inputBinding('size', size),
|
||||||
|
inputBinding('strokeWidth', strokeWidth),
|
||||||
|
inputBinding('absoluteStrokeWidth', absoluteStrokeWidth),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render children', () => {
|
||||||
|
icon.set(testIcon2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.innerHTML).toBe(
|
||||||
|
'<!--container--><circle cx="12" cy="12" r="8"></circle><polyline points="1 1 22 22"></polyline>',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove children on change', () => {
|
||||||
|
icon.set(null);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.innerHTML).toBe('<!--container-->');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('iconInput', () => {
|
||||||
|
it('should support LucideIconData input', () => {
|
||||||
|
icon.set(testIcon);
|
||||||
|
name.set('custom-name');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component['iconData']()).toBe(testIcon);
|
||||||
|
expect(component['iconName']()).toBe('custom-name');
|
||||||
|
expect(fixture.nativeElement.innerHTML).toBe(
|
||||||
|
'<!--container--><polyline points="1 1 22 22"></polyline>',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should support LucideIconComponentType input', () => {
|
||||||
|
icon.set(LucideActivity);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component['iconData']()).toBe(LucideActivity.iconData);
|
||||||
|
expect(component['iconName']()).toBe(LucideActivity.iconName);
|
||||||
|
});
|
||||||
|
it('should support string icon name', () => {
|
||||||
|
icon.set('demo');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component['iconData']()).toBe(testIcon);
|
||||||
|
expect(component['iconName']()).toBe('demo');
|
||||||
|
});
|
||||||
|
it('should throw error if no icon founds', () => {
|
||||||
|
icon.set('invalid');
|
||||||
|
expect(() => fixture.detectChanges()).toThrowError(`Unable to resolve icon 'invalid'`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('class', () => {
|
||||||
|
it('should add all classes', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('class')).toBe('lucide lucide-demo');
|
||||||
|
});
|
||||||
|
it('should add class from name, even if icon has name', () => {
|
||||||
|
icon.set(LucideActivity);
|
||||||
|
name.set('custom-name');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(getSvgAttribute('class')).toBe('lucide lucide-custom-name');
|
||||||
|
});
|
||||||
|
it('should add class icon if available', () => {
|
||||||
|
icon.set(LucideActivity);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(getSvgAttribute('class')).toBe('lucide lucide-activity');
|
||||||
|
});
|
||||||
|
it('should remove class on change', () => {
|
||||||
|
icon.set(null);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('class')).toBe('lucide');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('color', () => {
|
||||||
|
it('should default to currentColor', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke')).toBe('currentColor');
|
||||||
|
});
|
||||||
|
it('should set color', () => {
|
||||||
|
color.set('red');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke')).toBe('red');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('size', () => {
|
||||||
|
it('should default to 24', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('width')).toBe('24');
|
||||||
|
expect(getSvgAttribute('height')).toBe('24');
|
||||||
|
});
|
||||||
|
it('should set size', () => {
|
||||||
|
size.set(12);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('width')).toBe('12');
|
||||||
|
expect(getSvgAttribute('height')).toBe('12');
|
||||||
|
});
|
||||||
|
it('should allow string size', () => {
|
||||||
|
size.set('18');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('width')).toBe('18');
|
||||||
|
expect(getSvgAttribute('height')).toBe('18');
|
||||||
|
});
|
||||||
|
it('should use default on invalid string', () => {
|
||||||
|
size.set('large');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('width')).toBe('24');
|
||||||
|
expect(getSvgAttribute('height')).toBe('24');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('strokeWidth', () => {
|
||||||
|
it('should default to 2', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke-width')).toBe('2');
|
||||||
|
});
|
||||||
|
it('should set stroke width', () => {
|
||||||
|
strokeWidth.set(1.41);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke-width')).toBe('1.41');
|
||||||
|
});
|
||||||
|
it('should allow string stroke width', () => {
|
||||||
|
strokeWidth.set('1px');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke-width')).toBe('1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('absoluteStrokeWidth', () => {
|
||||||
|
it('should not adjust stroke width', () => {
|
||||||
|
strokeWidth.set(2);
|
||||||
|
size.set(12);
|
||||||
|
absoluteStrokeWidth.set(false);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke-width')).toBe('2');
|
||||||
|
});
|
||||||
|
it('should adjust stroke width', () => {
|
||||||
|
strokeWidth.set(2);
|
||||||
|
size.set(12);
|
||||||
|
absoluteStrokeWidth.set(true);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke-width')).toBe('4');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('title', () => {
|
||||||
|
it('should set title if provided', () => {
|
||||||
|
title.set('Foobar');
|
||||||
|
fixture.detectChanges();
|
||||||
|
const titleEl = fixture.debugElement.query(By.css('title')).nativeElement;
|
||||||
|
expect(titleEl).toBeDefined();
|
||||||
|
expect(titleEl.textContent).toBe('Foobar');
|
||||||
|
});
|
||||||
|
it('should not set aria-hidden when title is set', () => {
|
||||||
|
title.set('Foobar');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('aria-hidden')).toBeUndefined;
|
||||||
|
});
|
||||||
|
it('should set aria-hidden if no title is provided', () => {
|
||||||
|
title.set(undefined);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('aria-hidden')).toBeUndefined;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('content projection', () => {
|
||||||
|
it('should project content', () => {
|
||||||
|
const hostFixture = TestBed.createComponent(TestHostComponent);
|
||||||
|
hostFixture.componentRef.setInput('icon', testIcon);
|
||||||
|
hostFixture.detectChanges();
|
||||||
|
hostFixture.componentRef.setInput('icon', testIcon2);
|
||||||
|
hostFixture.detectChanges();
|
||||||
|
const rect = hostFixture.debugElement.query(By.css('rect')).nativeElement;
|
||||||
|
expect(rect).toBeInstanceOf(SVGElement);
|
||||||
|
expect(rect.outerHTML).toBe('<rect x="1" y="1" width="22" height="22"></rect>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
65
packages/angular/src/lucide-icon.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { Component, computed, inject, input } from '@angular/core';
|
||||||
|
import { isLucideIconComponent, isLucideIconData, LucideIconInput } from './types';
|
||||||
|
import { LucideIconBase } from './lucide-icon-base';
|
||||||
|
import { LUCIDE_ICONS } from './lucide-icons';
|
||||||
|
import { LucideIconData } from './types';
|
||||||
|
import { toKebabCase } from './utils/to-kebab-case';
|
||||||
|
|
||||||
|
interface LucideResolvedIcon {
|
||||||
|
name?: string | null;
|
||||||
|
data: LucideIconData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic icon component for rendering LucideIconData.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'svg[lucideIcon]',
|
||||||
|
templateUrl: './lucide-icon.html',
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
export class LucideIcon extends LucideIconBase {
|
||||||
|
protected readonly icons = inject(LUCIDE_ICONS);
|
||||||
|
readonly name = input<string | null>();
|
||||||
|
readonly iconInput = input.required<LucideIconInput | null>({
|
||||||
|
alias: 'lucideIcon',
|
||||||
|
});
|
||||||
|
readonly resolvedIcon = computed<LucideResolvedIcon | null>(() => {
|
||||||
|
return this.resolveIcon(this.name(), this.iconInput());
|
||||||
|
});
|
||||||
|
protected override readonly iconName = computed(() => {
|
||||||
|
return this.resolvedIcon()?.name;
|
||||||
|
});
|
||||||
|
protected override readonly iconData = computed(() => {
|
||||||
|
return this.resolvedIcon()?.data;
|
||||||
|
});
|
||||||
|
|
||||||
|
protected resolveIcon(
|
||||||
|
name: string | null | undefined,
|
||||||
|
icon: LucideIconInput | null | undefined,
|
||||||
|
): LucideResolvedIcon | null {
|
||||||
|
if (isLucideIconData(icon)) {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
data: icon,
|
||||||
|
};
|
||||||
|
} else if (isLucideIconComponent(icon)) {
|
||||||
|
return {
|
||||||
|
name: name ?? icon.iconName,
|
||||||
|
data: icon.iconData,
|
||||||
|
};
|
||||||
|
} else if (typeof icon === 'string') {
|
||||||
|
const name = toKebabCase(icon);
|
||||||
|
if (name in this.icons) {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
data: this.icons[name],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unable to resolve icon '${icon}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
packages/angular/src/lucide-icons.spec.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { LUCIDE_ICONS, provideLucideIcons } from './lucide-icons';
|
||||||
|
import { LucideIconData } from './types';
|
||||||
|
import { LucideActivity } from './icons/activity';
|
||||||
|
import { LucideCircle } from './icons/circle';
|
||||||
|
import { LucideSquareX } from './icons/square-x';
|
||||||
|
|
||||||
|
describe('Lucide icons', () => {
|
||||||
|
describe('LUCIDE_ICONS', () => {
|
||||||
|
it('should default to empty map', () => {
|
||||||
|
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('provideLucideIcons', () => {
|
||||||
|
const mockIcon: LucideIconData = [['polyline', { points: '1 1 22 22' }]];
|
||||||
|
const mockIcon2: LucideIconData = [['circle', { cx: 12, cy: 12, r: 8 }]];
|
||||||
|
it('should accept dictionary of icons', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
provideLucideIcons({
|
||||||
|
DemoIcon: mockIcon,
|
||||||
|
MockIcon: mockIcon2,
|
||||||
|
TestIcon: LucideActivity,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({
|
||||||
|
'demo-icon': mockIcon,
|
||||||
|
'mock-icon': mockIcon2,
|
||||||
|
[LucideActivity.iconName]: LucideActivity.iconData,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should accept list of icon components', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [provideLucideIcons([LucideActivity, LucideSquareX, LucideCircle])],
|
||||||
|
});
|
||||||
|
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({
|
||||||
|
[LucideActivity.iconName]: LucideActivity.iconData,
|
||||||
|
[LucideSquareX.iconName]: LucideSquareX.iconData,
|
||||||
|
[LucideCircle.iconName]: LucideCircle.iconData,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
64
packages/angular/src/lucide-icons.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { InjectionToken, Provider } from '@angular/core';
|
||||||
|
import { LucideIconData, LucideIcons } from './types';
|
||||||
|
import { isLucideIconComponent, LucideIconComponentType } from './types';
|
||||||
|
import { toKebabCase } from './utils/to-kebab-case';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection token for providing Lucide icons by name.
|
||||||
|
*
|
||||||
|
* @internal Use {@link provideLucideConfig}
|
||||||
|
*/
|
||||||
|
export const LUCIDE_ICONS = new InjectionToken<LucideIcons>('Lucide icons', {
|
||||||
|
factory: () => ({}),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide Lucide icons by name.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Warning! This provider will convert dictionary keys to lower-kebab-case.
|
||||||
|
*
|
||||||
|
* @param icons Either a dictionary of icons or a list of Angular icon components.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```ts
|
||||||
|
* import { provideLucideIcons, SquareCheck } from '@lucide/angular';
|
||||||
|
* import { MyCustomIcon } from './custom-icons/circle-check';
|
||||||
|
*
|
||||||
|
* providers: [
|
||||||
|
* provideLucideIcons({
|
||||||
|
* SquareCheck,
|
||||||
|
* MyCustomIcon, // LucideIconData
|
||||||
|
* }),
|
||||||
|
* ]
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <svg lucideIcon="my-custom-icon" />
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function provideLucideIcons(
|
||||||
|
icons: Record<string, LucideIconData | LucideIconComponentType> | Array<LucideIconComponentType>,
|
||||||
|
): Provider {
|
||||||
|
if (Array.isArray(icons)) {
|
||||||
|
return {
|
||||||
|
provide: LUCIDE_ICONS,
|
||||||
|
useValue: icons.reduce((acc, icon) => {
|
||||||
|
acc[toKebabCase(icon.iconName)] = icon.iconData;
|
||||||
|
return acc;
|
||||||
|
}, {} as LucideIcons),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
provide: LUCIDE_ICONS,
|
||||||
|
useValue: Object.entries(icons).reduce((acc, [name, icon]) => {
|
||||||
|
if (isLucideIconComponent(icon)) {
|
||||||
|
acc[icon.iconName] = icon.iconData;
|
||||||
|
} else {
|
||||||
|
acc[toKebabCase(name)] = icon;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {} as LucideIcons),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
8
packages/angular/src/public-api.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import * as icons from './icons/lucide-angular';
|
||||||
|
|
||||||
|
export * from './lucide-config';
|
||||||
|
export * from './lucide-icon';
|
||||||
|
export * from './lucide-icons';
|
||||||
|
export * from './types';
|
||||||
|
export * from './icons/lucide-angular';
|
||||||
|
export { icons };
|
||||||
47
packages/angular/src/types.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { Signal, Type } from '@angular/core';
|
||||||
|
|
||||||
|
type HtmlAttributes = { [key: string]: string | number };
|
||||||
|
export type LucideIconNode = readonly [string, HtmlAttributes];
|
||||||
|
export type LucideIconData = readonly LucideIconNode[];
|
||||||
|
export type LucideIcons = { [key: string]: LucideIconData };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Lucide icon component that has `iconName` and `iconData` signals inherited from `LucideIconBase` and respective static members accessible without instantiating the component.
|
||||||
|
*/
|
||||||
|
export type LucideIconComponentType = Type<{
|
||||||
|
title: Signal<Nullable<string>>;
|
||||||
|
size: Signal<Nullable<number>>;
|
||||||
|
color: Signal<Nullable<string>>;
|
||||||
|
strokeWidth: Signal<Nullable<number>>;
|
||||||
|
absoluteStrokeWidth: Signal<Nullable<boolean>>;
|
||||||
|
}> & {
|
||||||
|
iconName: string;
|
||||||
|
iconData: LucideIconData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard for {@link LucideIconData}
|
||||||
|
*/
|
||||||
|
export function isLucideIconData(icon: unknown): icon is LucideIconData {
|
||||||
|
return Array.isArray(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard for {@link LucideIconComponentType}
|
||||||
|
*/
|
||||||
|
export function isLucideIconComponent(icon: unknown): icon is LucideIconComponentType {
|
||||||
|
return (
|
||||||
|
icon instanceof Type &&
|
||||||
|
'iconData' in icon &&
|
||||||
|
Array.isArray(icon.iconData) &&
|
||||||
|
'iconName' in icon &&
|
||||||
|
typeof icon.iconName === 'string'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LucideIconInput = LucideIconComponentType | LucideIconData | string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export type Nullable<T> = T | null | undefined;
|
||||||
3
packages/angular/src/utils/format-fixed.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function formatFixed(number: number, decimals = 3): string {
|
||||||
|
return parseFloat(number.toFixed(decimals)).toString(10);
|
||||||
|
}
|
||||||
2
packages/angular/src/utils/to-kebab-case.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export const toKebabCase = (name: string) =>
|
||||||
|
name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||||
38
packages/angular/tsconfig.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||||
|
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@lucide/angular": ["./dist"],
|
||||||
|
},
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"declaration": false,
|
||||||
|
"downlevelIteration": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"importHelpers": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "preserve",
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
|
"strictInjectionParameters": true,
|
||||||
|
"strictInputAccessModifiers": true,
|
||||||
|
"strictTemplates": true,
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.lib.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
14
packages/angular/tsconfig.lib.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||||
|
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-tsc/lib",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts"],
|
||||||
|
"exclude": ["**/*.spec.ts"]
|
||||||
|
}
|
||||||
11
packages/angular/tsconfig.lib.prod.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||||
|
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.lib.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"declarationMap": false
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"compilationMode": "partial"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
packages/angular/tsconfig.spec.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||||
|
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-tsc/spec",
|
||||||
|
"types": ["vitest/globals"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.d.ts", "src/**/*.spec.ts"]
|
||||||
|
}
|
||||||
@@ -24,11 +24,23 @@
|
|||||||
"author": "Eric Fennis",
|
"author": "Eric Fennis",
|
||||||
"amdName": "lucide-react-native",
|
"amdName": "lucide-react-native",
|
||||||
"main": "dist/cjs/lucide-react-native.js",
|
"main": "dist/cjs/lucide-react-native.js",
|
||||||
"main:umd": "dist/umd/lucide-react-native.js",
|
|
||||||
"module": "dist/esm/lucide-react-native.js",
|
"module": "dist/esm/lucide-react-native.js",
|
||||||
"unpkg": "dist/umd/lucide-react-native.min.js",
|
|
||||||
"typings": "dist/lucide-react-native.d.ts",
|
"typings": "dist/lucide-react-native.d.ts",
|
||||||
"react-native": "dist/esm/lucide-react-native.js",
|
"react-native": "dist/esm/lucide-react-native.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/lucide-react-native.d.ts",
|
||||||
|
"import": "./dist/esm/lucide-react-native.js",
|
||||||
|
"browser": "./dist/esm/lucide-react-native.js",
|
||||||
|
"require": "./dist/cjs/lucide-react-native.js"
|
||||||
|
},
|
||||||
|
"./icons": {
|
||||||
|
"types": "./dist/icons.d.ts",
|
||||||
|
"import": "./dist/esm/icons/index.js",
|
||||||
|
"browser": "./dist/esm/icons/index.js",
|
||||||
|
"require": "./dist/cjs/icons/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import pkg from './package.json' with { type: 'json' };
|
|||||||
const packageName = 'LucideReact';
|
const packageName = 'LucideReact';
|
||||||
const outputFileName = 'lucide-react-native';
|
const outputFileName = 'lucide-react-native';
|
||||||
const outputDir = 'dist';
|
const outputDir = 'dist';
|
||||||
const inputs = ['src/lucide-react-native.ts'];
|
const inputs = ['src/lucide-react-native.ts', 'src/icons/index.ts'];
|
||||||
const bundles = [
|
const bundles = [
|
||||||
{
|
{
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
@@ -60,6 +60,16 @@ export default [
|
|||||||
],
|
],
|
||||||
plugins: [dts()],
|
plugins: [dts()],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: inputs[1],
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
file: `dist/icons.d.ts`,
|
||||||
|
format: 'es',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
plugins: [dts()],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
input: `src/${outputFileName}.suffixed.ts`,
|
input: `src/${outputFileName}.suffixed.ts`,
|
||||||
output: [
|
output: [
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
|
|||||||
absoluteStrokeWidth,
|
absoluteStrokeWidth,
|
||||||
children,
|
children,
|
||||||
iconNode,
|
iconNode,
|
||||||
|
className,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
@@ -46,6 +47,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
|
|||||||
{
|
{
|
||||||
ref,
|
ref,
|
||||||
...defaultAttributes,
|
...defaultAttributes,
|
||||||
|
className,
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
...customAttrs,
|
...customAttrs,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from './icons';
|
export * from './icons';
|
||||||
export * as icons from './icons';
|
|
||||||
export * from './aliases/prefixed';
|
export * from './aliases/prefixed';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from './icons';
|
export * from './icons';
|
||||||
export * as icons from './icons';
|
|
||||||
export * from './aliases/suffixed';
|
export * from './aliases/suffixed';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from './icons';
|
export * from './icons';
|
||||||
export * as icons from './icons';
|
|
||||||
export * from './aliases';
|
export * from './aliases';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|
||||||
|
|||||||