mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-05-18 12:24:52 +02:00
feat(packages/angular): add new @lucide/angular package (#3897)
* Add new lucide angular package * feat(packages/angular): added initial @lucide/angular package * feat(packages/angular): update readme * feat(packages/angular): update angular.json * docs(packages/angular): added (for now) full documentation for @lucide/angular * docs(packages/angular): added migration guide from lucide-angular * fix(github): fix package label syntax 😅 * fix(lint): fix linting issues * fix(github/angular): add prebuild stage * fix(github/angular): add prebuild stage & fix tests * fix(github/angular): fix LucideIconComponentType, update with _real_ public members * fix(github/angular): add prebuild to build step manually * fix(github/angular): downgrade vitest * fix(packages/angular): fix migration guide code example * fix(packages): add vitest + @vitest/* to pnpm overrides * fix(packages): update pnpm-lock with merged version * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix(packages): fix aria-hidden logic * fix(packages): update pnpm-lock * fix(packages): extract vitest and jsdom to root devDependencies * Fix copy utils script * Format code * feat(packages/angular): switched to self-describing IconData object from separate node+name – no more toKebabCase hackery feat(packages/angular): renamed LucideIconComponentType => LucideIcon, and LucideIcon => LucideDynamicIcon feat(packages/angular): added backwards compatible CSS class support feat(packages/angular): switched to vector-effect: non-scaling-stroke implementation from computed stroke width feat(packages/angular): rewrote icon provider to only accept a list of self-described icons – no more toKebabCase hackery & as an added bonus automatic backwards compatible alias support 🚀 feat(packages/angular): added legacy icon node helper function for passing legacy icons to providers test(packages/angular): added unit tests on LUCIDE_CONFIG provider usage * fix(packages/angular): fix linting issues * feat(packages/angular): extract createLucideIcon logic into helper function, refactor export template to use the iconData object as defined in ExportTemplate * Replace author * Remove private field * fix(packages/angular): remove createLucideIcon, it breaks the package :'( * fix(packages/angular): fix rendering order of child elements (_before_ projected content) * Format package.json * Update docs/guide/packages/angular.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/angular/MIGRATION.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Eric Fennis <eric.fennis@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -8,3 +8,4 @@ node_modules
|
|||||||
docs/images
|
docs/images
|
||||||
docs/**/examples/
|
docs/**/examples/
|
||||||
packages/lucide-react/dynamicIconImports.js
|
packages/lucide-react/dynamicIconImports.js
|
||||||
|
packages/angular/.angular
|
||||||
|
|||||||
8
.github/ISSUE_TEMPLATE/02_bug_report.yml
vendored
8
.github/ISSUE_TEMPLATE/02_bug_report.yml
vendored
@@ -13,13 +13,17 @@ 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: lucide-astro
|
||||||
|
|||||||
@@ -13,13 +13,17 @@ 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: lucide-astro
|
||||||
@@ -27,6 +31,7 @@ body:
|
|||||||
- 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
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
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
|
||||||
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -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',
|
||||||
'@lucide/icons',
|
'@lucide/icons',
|
||||||
|
|||||||
@@ -87,9 +87,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",
|
||||||
@@ -104,7 +120,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-preact": {
|
"lucide-preact": {
|
||||||
"order": 7,
|
"order": 8,
|
||||||
"icon": "preact",
|
"icon": "preact",
|
||||||
"shields": [
|
"shields": [
|
||||||
{
|
{
|
||||||
@@ -122,7 +138,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": [
|
||||||
@@ -139,7 +155,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lucide-static": {
|
"lucide-static": {
|
||||||
"order": 9,
|
"order": 10,
|
||||||
"icon": "svg",
|
"icon": "svg",
|
||||||
"shields": [
|
"shields": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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',
|
link: '/guide/packages/lucide-vue',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default {
|
|||||||
label: 'Lucide documentation for Preact',
|
label: 'Lucide documentation for Preact',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'lucide-angular',
|
name: 'angular',
|
||||||
logo: '/framework-logos/angular.svg',
|
logo: '/framework-logos/angular.svg',
|
||||||
label: 'Lucide documentation for Angular',
|
label: 'Lucide documentation for Angular',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
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 is 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:**
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -49,6 +49,8 @@
|
|||||||
"@types/yargs": "^17.0.33",
|
"@types/yargs": "^17.0.33",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
|
"@vitest/coverage-v8": "4.0.12",
|
||||||
|
"@vitest/ui": "4.0.12",
|
||||||
"ajv-cli": "^5.0.0",
|
"ajv-cli": "^5.0.0",
|
||||||
"dotenv": "^17.0.0",
|
"dotenv": "^17.0.0",
|
||||||
"eslint": "^8.57.1",
|
"eslint": "^8.57.1",
|
||||||
@@ -60,6 +62,7 @@
|
|||||||
"eslint-import-resolver-typescript": "^3.10.1",
|
"eslint-import-resolver-typescript": "^3.10.1",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
|
"jsdom": "^27.3.0",
|
||||||
"lint-staged": "^13.3.0",
|
"lint-staged": "^13.3.0",
|
||||||
"minimist": "^1.2.8",
|
"minimist": "^1.2.8",
|
||||||
"openai": "^5.8.1",
|
"openai": "^5.8.1",
|
||||||
@@ -70,6 +73,7 @@
|
|||||||
"simple-git": "^3.32.3",
|
"simple-git": "^3.32.3",
|
||||||
"svgo": "^3.3.2",
|
"svgo": "^3.3.2",
|
||||||
"svgson": "^5.3.1",
|
"svgson": "^5.3.1",
|
||||||
|
"vitest": "4.0.12",
|
||||||
"yargs": "^17.7.2",
|
"yargs": "^17.7.2",
|
||||||
"zod": "^3.25.67"
|
"zod": "^3.25.67"
|
||||||
},
|
},
|
||||||
@@ -86,13 +90,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"cross-spawn": "7.0.5",
|
|
||||||
"form-data": "^4.0.4",
|
|
||||||
"fast-json-patch": "^3.1.1",
|
|
||||||
"webpack-dev-middleware": "^5.3.4",
|
|
||||||
"semver": "^7.7.3",
|
|
||||||
"axios": "^1.12.0",
|
"axios": "^1.12.0",
|
||||||
"vite-prerender-plugin": "0.5.12"
|
"cross-spawn": "7.0.5",
|
||||||
|
"fast-json-patch": "^3.1.1",
|
||||||
|
"form-data": "^4.0.4",
|
||||||
|
"semver": "^7.7.3",
|
||||||
|
"vite-prerender-plugin": "0.5.12",
|
||||||
|
"webpack-dev-middleware": "^5.3.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
packages/angular/.eslintrc.js
Normal file
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
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
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
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
187
packages/angular/MIGRATION.md
Normal file
187
packages/angular/MIGRATION.md
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
# 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 `provideLucideIcons()`, 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 { BrowserModule, NgModule } from '@angular/core';
|
||||||
|
import { LucideAngularModule, AirVent, AlarmClock } from 'lucide-angular';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
LucideAngularModule.pick({ AirVent, AlarmClock }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Standalone
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { ApplicationConfig } from '@angular/core';
|
||||||
|
import { LucideAngularModule, AirVent, AlarmClock } from 'lucide-angular';
|
||||||
|
|
||||||
|
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
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/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
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
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
72
packages/angular/package.json
Normal file
72
packages/angular/package.json
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"name": "@lucide/angular",
|
||||||
|
"description": "A Lucide icon library package for Angular applications.",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"author": "karsa-mistmere",
|
||||||
|
"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/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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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.12",
|
||||||
|
"angular-eslint": "21.1.0",
|
||||||
|
"ng-packagr": "^21.0.0",
|
||||||
|
"rxjs": "~7.8.0",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"typescript": "~5.9.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "17.x - 21.x",
|
||||||
|
"@angular/core": "17.x - 21.x"
|
||||||
|
}
|
||||||
|
}
|
||||||
72
packages/angular/scripts/exportTemplate.mts
Normal file
72
packages/angular/scripts/exportTemplate.mts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
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,
|
||||||
|
getSvg,
|
||||||
|
deprecated,
|
||||||
|
deprecationReason,
|
||||||
|
iconData,
|
||||||
|
}) => {
|
||||||
|
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 iconData.aliases ?? []) {
|
||||||
|
const aliasComponentName = `Lucide${toPascalCase(alias)}`;
|
||||||
|
const aliasSelector = `svg[lucide${toPascalCase(alias)}]`;
|
||||||
|
if (!selectors.includes(aliasSelector)) {
|
||||||
|
selectors.push(aliasSelector);
|
||||||
|
}
|
||||||
|
if (aliasComponentName !== angularComponentName && !aliasComponentNames.includes(aliasComponentName)) {
|
||||||
|
aliasComponentNames.push(aliasComponentName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `\
|
||||||
|
import { LucideIconBase } from '../lucide-icon-base';
|
||||||
|
import { lucideIconTemplate } from '../lucide-icon-template';
|
||||||
|
import { LucideIconData } from '../types';
|
||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
ViewEncapsulation,
|
||||||
|
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/angular - Documentation
|
||||||
|
*
|
||||||
|
* @param {Object} props - Lucide icons props and any valid SVG attribute
|
||||||
|
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: '${selectors.join(', ')}',
|
||||||
|
template: lucideIconTemplate,
|
||||||
|
standalone: true,
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class ${angularComponentName} extends LucideIconBase {
|
||||||
|
static readonly icon: LucideIconData = ${JSON.stringify(iconData)};
|
||||||
|
protected override readonly icon = signal(${angularComponentName}.icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
${aliasComponentNames.map((aliasComponentName) => {
|
||||||
|
return `
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @see ${angularComponentName}
|
||||||
|
*/
|
||||||
|
export const ${aliasComponentName} = ${angularComponentName};
|
||||||
|
`;
|
||||||
|
}).join(`\n\n`)}
|
||||||
|
`;
|
||||||
|
});
|
||||||
11
packages/angular/src/default-attributes.ts
Normal file
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
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
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
132
packages/angular/src/lucide-dynamic-icon.spec.ts
Normal file
132
packages/angular/src/lucide-dynamic-icon.spec.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import { Component, input, inputBinding, signal, WritableSignal } from '@angular/core';
|
||||||
|
import { LucideDynamicIcon } from './lucide-dynamic-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: [LucideDynamicIcon],
|
||||||
|
})
|
||||||
|
class TestHostComponent {
|
||||||
|
readonly icon = input<LucideIconData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('LucideDynamicIcon', () => {
|
||||||
|
let component: LucideDynamicIcon;
|
||||||
|
let fixture: ComponentFixture<LucideDynamicIcon>;
|
||||||
|
let icon: WritableSignal<LucideIconInput | null | undefined>;
|
||||||
|
const getSvgAttribute = (attr: string) => fixture.nativeElement.getAttribute(attr);
|
||||||
|
const testIcon: LucideIconData = {
|
||||||
|
name: 'demo',
|
||||||
|
node: [['polyline', { points: '1 1 22 22' }]],
|
||||||
|
};
|
||||||
|
const testIcon2: LucideIconData = {
|
||||||
|
name: 'demo-other',
|
||||||
|
node: [
|
||||||
|
['circle', { cx: 12, cy: 12, r: 8 }],
|
||||||
|
['polyline', { points: '1 1 22 22' }],
|
||||||
|
],
|
||||||
|
aliases: ['demo-2'],
|
||||||
|
};
|
||||||
|
function createComponent() {
|
||||||
|
return TestBed.createComponent(LucideDynamicIcon, {
|
||||||
|
inferTagName: true,
|
||||||
|
bindings: [inputBinding('lucideIcon', icon)],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
beforeEach(async () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [provideLucideIcons(testIcon)],
|
||||||
|
});
|
||||||
|
icon = signal('demo');
|
||||||
|
fixture = createComponent();
|
||||||
|
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><!--ng-container-->',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove children on change', () => {
|
||||||
|
icon.set(null);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.innerHTML).toBe('<!--container--><!--ng-container-->');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('iconInput', () => {
|
||||||
|
it('should support LucideIconData input', () => {
|
||||||
|
icon.set(testIcon);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component['icon']()).toBe(testIcon);
|
||||||
|
expect(fixture.nativeElement.innerHTML).toBe(
|
||||||
|
'<!--container--><polyline points="1 1 22 22"></polyline><!--ng-container-->',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should support LucideIcon input', () => {
|
||||||
|
icon.set(LucideActivity);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component['icon']()).toBe(LucideActivity.icon);
|
||||||
|
});
|
||||||
|
it('should support string icon name', () => {
|
||||||
|
icon.set('demo');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component['icon']()).toBe(testIcon);
|
||||||
|
});
|
||||||
|
it('should throw error if no icon found', () => {
|
||||||
|
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 backwards compatible classes from aliases', () => {
|
||||||
|
icon.set(testIcon2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('class')).toBe('lucide lucide-demo-other lucide-demo-2');
|
||||||
|
});
|
||||||
|
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('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('svg :last-child')).nativeElement;
|
||||||
|
expect(rect).toBeInstanceOf(SVGElement);
|
||||||
|
expect(rect.outerHTML).toBe('<rect x="1" y="1" width="22" height="22"></rect>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
43
packages/angular/src/lucide-dynamic-icon.ts
Normal file
43
packages/angular/src/lucide-dynamic-icon.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
computed,
|
||||||
|
inject,
|
||||||
|
input,
|
||||||
|
ViewEncapsulation,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { isLucideIconComponent, isLucideIconData, LucideIconInput } from './types';
|
||||||
|
import { LucideIconBase } from './lucide-icon-base';
|
||||||
|
import { LUCIDE_ICONS } from './lucide-icons';
|
||||||
|
import { lucideIconTemplate } from './lucide-icon-template';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic icon component for rendering LucideIconData.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'svg[lucideIcon]',
|
||||||
|
template: lucideIconTemplate,
|
||||||
|
standalone: true,
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class LucideDynamicIcon extends LucideIconBase {
|
||||||
|
protected readonly icons = inject(LUCIDE_ICONS);
|
||||||
|
public readonly lucideIcon = input.required<LucideIconInput | null>();
|
||||||
|
|
||||||
|
protected override readonly icon = computed(() => {
|
||||||
|
const icon = this.lucideIcon();
|
||||||
|
if (isLucideIconData(icon)) {
|
||||||
|
return icon;
|
||||||
|
} else if (isLucideIconComponent(icon)) {
|
||||||
|
return icon.icon;
|
||||||
|
} else if (typeof icon === 'string') {
|
||||||
|
if (icon in this.icons) {
|
||||||
|
return this.icons[icon];
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unable to resolve icon '${icon}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
249
packages/angular/src/lucide-icon-base.spec.ts
Normal file
249
packages/angular/src/lucide-icon-base.spec.ts
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
inputBinding,
|
||||||
|
signal,
|
||||||
|
ViewEncapsulation,
|
||||||
|
WritableSignal,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { provideLucideConfig } from './lucide-config';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { lucideIconTemplate } from './lucide-icon-template';
|
||||||
|
import { LucideIconBase } from './lucide-icon-base';
|
||||||
|
import { LucideIconData } from './types';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'svg[lucideCircleCheck], svg[lucideCheckCircle2]',
|
||||||
|
template: lucideIconTemplate,
|
||||||
|
standalone: true,
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class LucideCircleCheck extends LucideIconBase {
|
||||||
|
static readonly icon: LucideIconData = {
|
||||||
|
name: 'circle-check',
|
||||||
|
size: 24,
|
||||||
|
node: [
|
||||||
|
['circle', { cx: '12', cy: '12', r: '10' }],
|
||||||
|
['path', { d: 'm9 12 2 2 4-4' }],
|
||||||
|
],
|
||||||
|
aliases: ['check-circle-2'],
|
||||||
|
};
|
||||||
|
protected override readonly icon = signal(LucideCircleCheck.icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('LucideIconBase', () => {
|
||||||
|
let component: LucideCircleCheck;
|
||||||
|
let fixture: ComponentFixture<LucideCircleCheck>;
|
||||||
|
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);
|
||||||
|
function createComponent() {
|
||||||
|
return TestBed.createComponent(LucideCircleCheck, {
|
||||||
|
inferTagName: true,
|
||||||
|
bindings: [
|
||||||
|
inputBinding('title', title),
|
||||||
|
inputBinding('color', color),
|
||||||
|
inputBinding('size', size),
|
||||||
|
inputBinding('strokeWidth', strokeWidth),
|
||||||
|
inputBinding('absoluteStrokeWidth', absoluteStrokeWidth),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
beforeEach(async () => {
|
||||||
|
title = signal(undefined);
|
||||||
|
color = signal(undefined);
|
||||||
|
size = signal(undefined);
|
||||||
|
strokeWidth = signal(undefined);
|
||||||
|
absoluteStrokeWidth = signal(undefined);
|
||||||
|
fixture = createComponent();
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render children', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.innerHTML).toBe(
|
||||||
|
'<!--container--><circle cx="12" cy="12" r="10"></circle><path d="m9 12 2 2 4-4"></path><!--ng-container-->',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('class', () => {
|
||||||
|
it('should add all classes', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('class')).toBe('lucide lucide-circle-check lucide-check-circle-2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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(true);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke-width')).toBe('2');
|
||||||
|
});
|
||||||
|
it('should not set vector-effect on children', () => {
|
||||||
|
absoluteStrokeWidth.set(false);
|
||||||
|
for (const child of fixture.nativeElement.children) {
|
||||||
|
expect(child.getAttribute('vector-effect')).toBeNull();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('should set vector-effect on children', () => {
|
||||||
|
absoluteStrokeWidth.set(true);
|
||||||
|
for (const child of fixture.nativeElement.children) {
|
||||||
|
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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')).toBe('false');
|
||||||
|
});
|
||||||
|
it('should set aria-hidden if no title is provided', () => {
|
||||||
|
title.set(undefined);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('aria-hidden')).toBe('true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('LUCIDE_CONFIG', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
provideLucideConfig({
|
||||||
|
color: 'red',
|
||||||
|
strokeWidth: 1,
|
||||||
|
size: 12,
|
||||||
|
absoluteStrokeWidth: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await TestBed.compileComponents();
|
||||||
|
fixture = createComponent();
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
describe('color', () => {
|
||||||
|
it('should use color from config', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke')).toBe('red');
|
||||||
|
});
|
||||||
|
it('should use override color from config', () => {
|
||||||
|
color.set('pink');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke')).toBe('pink');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('strokeWidth', () => {
|
||||||
|
it('should use stroke width from config', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke-width')).toBe('1');
|
||||||
|
});
|
||||||
|
it('should use override stroke width from config', () => {
|
||||||
|
strokeWidth.set(3);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('stroke-width')).toBe('3');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('size', () => {
|
||||||
|
it('should use size from config', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('width')).toBe('12');
|
||||||
|
expect(getSvgAttribute('height')).toBe('12');
|
||||||
|
});
|
||||||
|
it('should use override size from config', () => {
|
||||||
|
size.set('48');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getSvgAttribute('width')).toBe('48');
|
||||||
|
expect(getSvgAttribute('height')).toBe('48');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('absoluteStrokeWidth', () => {
|
||||||
|
it('should use absoluteStrokeWidth from config', () => {
|
||||||
|
for (const child of fixture.nativeElement.children) {
|
||||||
|
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('should override absoluteStrokeWidth', () => {
|
||||||
|
absoluteStrokeWidth.set(false);
|
||||||
|
for (const child of fixture.nativeElement.children) {
|
||||||
|
expect(child.getAttribute('vector-effect')).toBeNull();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
134
packages/angular/src/lucide-icon-base.ts
Normal file
134
packages/angular/src/lucide-icon-base.ts
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
effect,
|
||||||
|
ElementRef,
|
||||||
|
inject,
|
||||||
|
input,
|
||||||
|
Renderer2,
|
||||||
|
Signal,
|
||||||
|
viewChild,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { LUCIDE_CONFIG } from './lucide-config';
|
||||||
|
import { LucideIconData, Nullable } from './types';
|
||||||
|
import defaultAttributes from './default-attributes';
|
||||||
|
import { lucideIconTemplate } from './lucide-icon-template';
|
||||||
|
|
||||||
|
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]',
|
||||||
|
template: lucideIconTemplate,
|
||||||
|
host: {
|
||||||
|
...defaultAttributes,
|
||||||
|
class: 'lucide',
|
||||||
|
'[attr.width]': 'size().toString(10)',
|
||||||
|
'[attr.height]': 'size().toString(10)',
|
||||||
|
'[attr.stroke]': 'color()',
|
||||||
|
'[attr.stroke-width]': 'strokeWidth().toString(10)',
|
||||||
|
'[attr.aria-hidden]': '!title()',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export abstract class LucideIconBase {
|
||||||
|
protected abstract readonly icon: Signal<Nullable<LucideIconData>>;
|
||||||
|
protected readonly iconConfig = inject(LUCIDE_CONFIG);
|
||||||
|
protected readonly elRef = inject(ElementRef);
|
||||||
|
protected readonly renderer = inject(Renderer2);
|
||||||
|
protected readonly contentRef = viewChild.required<ElementRef>('contentRef');
|
||||||
|
/**
|
||||||
|
* 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),
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* If set to true, it adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements.
|
||||||
|
*/
|
||||||
|
readonly absoluteStrokeWidth = input(this.iconConfig.absoluteStrokeWidth, {
|
||||||
|
transform: (value: Nullable<boolean>) => value ?? this.iconConfig.absoluteStrokeWidth,
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
effect((onCleanup) => {
|
||||||
|
const icon = this.icon();
|
||||||
|
if (icon) {
|
||||||
|
const absoluteStrokeWidth = this.absoluteStrokeWidth();
|
||||||
|
const { name, node, aliases = [] } = icon;
|
||||||
|
const classes = [name, ...aliases].map((item) => `lucide-${item}`);
|
||||||
|
for (const cssClass of classes) {
|
||||||
|
this.renderer.addClass(this.elRef.nativeElement, cssClass);
|
||||||
|
}
|
||||||
|
const contentRef = this.contentRef();
|
||||||
|
const refChild = contentRef.nativeElement;
|
||||||
|
const elements = node.map(([name, attrs]) => {
|
||||||
|
const element = this.renderer.createElement(name, 'http://www.w3.org/2000/svg');
|
||||||
|
if (absoluteStrokeWidth) {
|
||||||
|
this.renderer.setAttribute(element, 'vector-effect', 'non-scaling-stroke');
|
||||||
|
}
|
||||||
|
Object.entries(attrs).forEach(([name, value]) =>
|
||||||
|
this.renderer.setAttribute(
|
||||||
|
element,
|
||||||
|
name,
|
||||||
|
typeof value === 'number' ? value.toString(10) : value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
this.renderer.insertBefore(this.elRef.nativeElement, element, refChild);
|
||||||
|
return element;
|
||||||
|
});
|
||||||
|
onCleanup(() => {
|
||||||
|
elements.forEach((element) =>
|
||||||
|
this.renderer.removeChild(this.elRef.nativeElement, element),
|
||||||
|
);
|
||||||
|
for (const cssClass of classes) {
|
||||||
|
this.renderer.removeClass(this.elRef.nativeElement, cssClass);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
10
packages/angular/src/lucide-icon-template.ts
Normal file
10
packages/angular/src/lucide-icon-template.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* The template of all Lucide icon components.
|
||||||
|
*/
|
||||||
|
export const lucideIconTemplate = `@if (title(); as titleValue) {
|
||||||
|
<title>{{ titleValue }}</title>
|
||||||
|
}
|
||||||
|
<ng-content select="title"></ng-content>
|
||||||
|
<ng-container #contentRef></ng-container>
|
||||||
|
<ng-content />`;
|
||||||
64
packages/angular/src/lucide-icons.spec.ts
Normal file
64
packages/angular/src/lucide-icons.spec.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import {
|
||||||
|
LUCIDE_ICONS,
|
||||||
|
lucideLegacyIcon,
|
||||||
|
lucideLegacyIconMap,
|
||||||
|
provideLucideIcons,
|
||||||
|
} from './lucide-icons';
|
||||||
|
import { LucideIconData } from './types';
|
||||||
|
import { LucideCircle } from './icons/circle';
|
||||||
|
|
||||||
|
describe('Lucide icons', () => {
|
||||||
|
describe('LUCIDE_ICONS', () => {
|
||||||
|
it('should default to empty map', () => {
|
||||||
|
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('provideLucideIcons', () => {
|
||||||
|
const mockIcon: LucideIconData = {
|
||||||
|
name: 'mock-icon',
|
||||||
|
node: [['polyline', { points: '1 1 22 22' }]],
|
||||||
|
};
|
||||||
|
const mockIcon2: LucideIconData = {
|
||||||
|
name: 'mock-icon-circle',
|
||||||
|
node: [['circle', { cx: 12, cy: 12, r: 8 }]],
|
||||||
|
aliases: ['mock-icon-2'],
|
||||||
|
};
|
||||||
|
const legacyIconNode: LucideIconData['node'] = [['circle', { cx: 12, cy: 12, r: 8 }]];
|
||||||
|
const legacyAlias = 'legacy-old-name';
|
||||||
|
const OtherLegacyIcon = legacyIconNode;
|
||||||
|
it('should accept list of icon object, icon components or legacy icons', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
provideLucideIcons(
|
||||||
|
mockIcon,
|
||||||
|
mockIcon2,
|
||||||
|
LucideCircle,
|
||||||
|
lucideLegacyIcon('legacy-icon', legacyIconNode, [legacyAlias]),
|
||||||
|
...lucideLegacyIconMap({ OtherLegacyIcon }),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const legacyIconData = {
|
||||||
|
name: 'legacy-icon',
|
||||||
|
node: legacyIconNode,
|
||||||
|
aliases: [legacyAlias],
|
||||||
|
};
|
||||||
|
const otherLegacyIconData = {
|
||||||
|
name: 'other-legacy-icon',
|
||||||
|
node: legacyIconNode,
|
||||||
|
aliases: ['OtherLegacyIcon'],
|
||||||
|
};
|
||||||
|
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({
|
||||||
|
'mock-icon': mockIcon,
|
||||||
|
'mock-icon-circle': mockIcon2,
|
||||||
|
'mock-icon-2': mockIcon2,
|
||||||
|
'legacy-icon': legacyIconData,
|
||||||
|
'legacy-old-name': legacyIconData,
|
||||||
|
'other-legacy-icon': otherLegacyIconData,
|
||||||
|
OtherLegacyIcon: otherLegacyIconData,
|
||||||
|
['circle']: LucideCircle.icon,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
105
packages/angular/src/lucide-icons.ts
Normal file
105
packages/angular/src/lucide-icons.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import { InjectionToken, Provider } from '@angular/core';
|
||||||
|
import { isLucideIconComponent, LucideIcon, LucideIconData, LucideIcons } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection token for providing Lucide icons by name.
|
||||||
|
*
|
||||||
|
* @internal Use {@link provideLucideIcons}
|
||||||
|
*/
|
||||||
|
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/my-custom-icon';
|
||||||
|
*
|
||||||
|
* providers: [
|
||||||
|
* provideLucideIcons({
|
||||||
|
* SquareCheck,
|
||||||
|
* MyCustomIcon, // LucideIconData
|
||||||
|
* }),
|
||||||
|
* ]
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <svg lucideIcon="my-custom-icon" />
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function provideLucideIcons(...icons: Array<LucideIcon | LucideIconData>): Provider {
|
||||||
|
return {
|
||||||
|
provide: LUCIDE_ICONS,
|
||||||
|
useValue: icons.reduce((acc, icon) => {
|
||||||
|
const iconData = isLucideIconComponent(icon) ? icon.icon : icon;
|
||||||
|
acc[iconData.name] = iconData;
|
||||||
|
for (const alias of iconData.aliases ?? []) {
|
||||||
|
acc[alias] = iconData;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {} as LucideIcons),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a legacy icon node to the new format, for custom icon (e.g. `@lucide/lab`) support.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```ts
|
||||||
|
* import { provideLucideIcons, lucideLegacyIcon } from '@lucide/angular';
|
||||||
|
* import { UserRoundX } from 'lucide-angular';
|
||||||
|
* import { burger } from '@lucide/lab';
|
||||||
|
*
|
||||||
|
* provideLucideIcons(
|
||||||
|
* lucideLegacyIcon('user-round-x', UserRoundX, ['user-circle-x']),
|
||||||
|
* lucideLegacyIcon('burger', burger, ['hamburger']),
|
||||||
|
* ),
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function lucideLegacyIcon(
|
||||||
|
name: string,
|
||||||
|
node: LucideIconData['node'],
|
||||||
|
aliases: string[] = [],
|
||||||
|
): LucideIconData {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
node,
|
||||||
|
aliases,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a map of legacy icon nodes to a list of icon data objects.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```ts
|
||||||
|
* import { provideLucideIcons, lucideLegacyIconMap, LucideCircle } from '@lucide/angular';
|
||||||
|
* import { UserRoundX } from 'lucide-angular';
|
||||||
|
* import { burger } from '@lucide/lab';
|
||||||
|
*
|
||||||
|
* provideLucideIcons(
|
||||||
|
* LucideCircle,
|
||||||
|
* ...lucideLegacyIconMap({ UserRoundX, burger }),
|
||||||
|
* ),
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function lucideLegacyIconMap(
|
||||||
|
icons: Record<string, LucideIconData['node']>,
|
||||||
|
): LucideIconData[] {
|
||||||
|
return Object.entries(icons).map(([pascalName, node]) => {
|
||||||
|
const name: string = pascalName.replaceAll(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
node,
|
||||||
|
aliases: [pascalName],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
10
packages/angular/src/public-api.ts
Normal file
10
packages/angular/src/public-api.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import * as icons from './icons/lucide-angular';
|
||||||
|
|
||||||
|
export * from './lucide-config';
|
||||||
|
export * from './lucide-dynamic-icon';
|
||||||
|
export * from './lucide-icon-base';
|
||||||
|
export * from './lucide-icon-template';
|
||||||
|
export * from './lucide-icons';
|
||||||
|
export * from './types';
|
||||||
|
export * from './icons/lucide-angular';
|
||||||
|
export { icons };
|
||||||
61
packages/angular/src/types.ts
Normal file
61
packages/angular/src/types.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { Signal, Type } from '@angular/core';
|
||||||
|
|
||||||
|
type HtmlAttributes = { [key: string]: string | number };
|
||||||
|
export type LucideIconNode = readonly [string, HtmlAttributes];
|
||||||
|
export type LucideIcons = { [key: string]: LucideIconData };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Lucide icon object that fully describes an icon to be displayed.
|
||||||
|
*/
|
||||||
|
export type LucideIconData = {
|
||||||
|
name: string;
|
||||||
|
size?: number;
|
||||||
|
node: LucideIconNode[];
|
||||||
|
aliases?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input signal map of Lucide icon components.
|
||||||
|
*/
|
||||||
|
interface LucideIconProps {
|
||||||
|
title: Signal<Nullable<string>>;
|
||||||
|
size: Signal<Nullable<number>>;
|
||||||
|
color: Signal<Nullable<string>>;
|
||||||
|
strokeWidth: Signal<Nullable<number>>;
|
||||||
|
absoluteStrokeWidth: Signal<Nullable<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Lucide icon component type that has `iconName` and `iconData` signals inherited from `LucideIconBase` and respective static members accessible without instantiating the component.
|
||||||
|
*/
|
||||||
|
export interface LucideIcon extends Type<LucideIconProps> {
|
||||||
|
icon: LucideIconData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard for {@link LucideIconData}
|
||||||
|
*/
|
||||||
|
export function isLucideIconData(icon: unknown): icon is LucideIconData {
|
||||||
|
return (
|
||||||
|
!!icon &&
|
||||||
|
typeof icon === 'object' &&
|
||||||
|
'name' in icon &&
|
||||||
|
typeof icon.name === 'string' &&
|
||||||
|
'node' in icon &&
|
||||||
|
Array.isArray(icon.node)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard for {@link LucideIcon}
|
||||||
|
*/
|
||||||
|
export function isLucideIconComponent(icon: unknown): icon is LucideIcon {
|
||||||
|
return icon instanceof Type && 'icon' in icon && isLucideIconData(icon.icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LucideIconInput = LucideIcon | LucideIconData | string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export type Nullable<T> = T | null | undefined;
|
||||||
38
packages/angular/tsconfig.json
Normal file
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
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
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
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"]
|
||||||
|
}
|
||||||
@@ -58,7 +58,6 @@
|
|||||||
"prettier": "^3.4.2",
|
"prettier": "^3.4.2",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^6.3.6",
|
"vite": "^6.3.6",
|
||||||
"vitest": "^4.0.12",
|
|
||||||
"astro": "^5.16.0"
|
"astro": "^5.16.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -52,8 +52,7 @@
|
|||||||
"rollup": "^4.59.0",
|
"rollup": "^4.59.0",
|
||||||
"rollup-plugin-dts": "^6.2.3",
|
"rollup-plugin-dts": "^6.2.3",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.2.4",
|
"vite": "^7.2.4"
|
||||||
"vitest": "^4.0.12"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"preact": "^10.27.2"
|
"preact": "^10.27.2"
|
||||||
|
|||||||
@@ -72,8 +72,7 @@
|
|||||||
"rollup": "^4.59.0",
|
"rollup": "^4.59.0",
|
||||||
"rollup-plugin-dts": "^6.2.3",
|
"rollup-plugin-dts": "^6.2.3",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.2.4",
|
"vite": "^7.2.4"
|
||||||
"vitest": "^4.0.12"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
|||||||
@@ -63,8 +63,7 @@
|
|||||||
"rollup-plugin-dts": "^6.2.3",
|
"rollup-plugin-dts": "^6.2.3",
|
||||||
"rollup-plugin-preserve-directives": "^0.4.0",
|
"rollup-plugin-preserve-directives": "^0.4.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.2.4",
|
"vite": "^7.2.4"
|
||||||
"vitest": "^4.0.12"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
|||||||
@@ -85,7 +85,6 @@
|
|||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.2.4",
|
"vite": "^7.2.4",
|
||||||
"vite-plugin-solid": "^2.11.6",
|
"vite-plugin-solid": "^2.11.6",
|
||||||
"vitest": "^4.0.12",
|
|
||||||
"esbuild": "^0.25.0"
|
"esbuild": "^0.25.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
"build": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons && pnpm build:package && pnpm build:license",
|
"build": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons && pnpm build:package && pnpm build:license",
|
||||||
"copy:license": "cp ../../LICENSE ./LICENSE",
|
"copy:license": "cp ../../LICENSE ./LICENSE",
|
||||||
"copy:utils": "mkdir -p ./src/utils && for f in hasA11yProp mergeClasses; do cp -f ../../packages/shared/src/utils/$f.ts ./src/utils/; done",
|
"copy:utils": "mkdir -p ./src/utils && for f in hasA11yProp mergeClasses; do cp -f ../../packages/shared/src/utils/$f.ts ./src/utils/; done",
|
||||||
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.svelte && rm -f index.js",
|
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.svelte && rm -rf ./src/icons/*.ts && rm -f index.js",
|
||||||
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --exportFileName=index.ts --iconFileExtension=.svelte --importImportFileExtension=.svelte --separateIconFileExport --separateIconFileExportExtension=.ts --withAliases --aliasesFileExtension=.ts --separateAliasesFile --separateAliasesFileExtension=.ts --aliasImportFileExtension=.js --pretty=false",
|
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --exportFileName=index.ts --iconFileExtension=.svelte --importImportFileExtension=.svelte --separateIconFileExport --separateIconFileExportExtension=.ts --withAliases --aliasesFileExtension=.ts --separateAliasesFile --separateAliasesFileExtension=.ts --aliasImportFileExtension=.js --pretty=false",
|
||||||
"build:package": "svelte-package --input ./src",
|
"build:package": "svelte-package --input ./src",
|
||||||
"build:license": "node ./scripts/appendBlockComments.mts",
|
"build:license": "node ./scripts/appendBlockComments.mts",
|
||||||
@@ -66,13 +66,11 @@
|
|||||||
"@testing-library/svelte": "^4.0.2",
|
"@testing-library/svelte": "^4.0.2",
|
||||||
"@tsconfig/svelte": "^5.0.0",
|
"@tsconfig/svelte": "^5.0.0",
|
||||||
"jest-serializer-html": "^7.1.0",
|
"jest-serializer-html": "^7.1.0",
|
||||||
"jsdom": "^20.0.3",
|
|
||||||
"svelte": "^4.2.19",
|
"svelte": "^4.2.19",
|
||||||
"svelte-check": "^3.4.4",
|
"svelte-check": "^3.4.4",
|
||||||
"svelte-preprocess": "^5.0.4",
|
"svelte-preprocess": "^5.0.4",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.2.4",
|
"vite": "^7.2.4"
|
||||||
"vitest": "^4.0.12"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"svelte": "^3 || ^4 || ^5.0.0-next.42"
|
"svelte": "^3 || ^4 || ^5.0.0-next.42"
|
||||||
|
|||||||
@@ -52,7 +52,6 @@
|
|||||||
"rollup": "^4.59.0",
|
"rollup": "^4.59.0",
|
||||||
"rollup-plugin-dts": "^6.2.3",
|
"rollup-plugin-dts": "^6.2.3",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.2.4",
|
"vite": "^7.2.4"
|
||||||
"vitest": "^4.0.12"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
"test:watch": "vitest watch"
|
"test:watch": "vitest watch"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^7.2.4",
|
"vite": "^7.2.4"
|
||||||
"vitest": "^4.0.12"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,8 +72,7 @@
|
|||||||
"svelte-check": "^4.3.4",
|
"svelte-check": "^4.3.4",
|
||||||
"svelte-preprocess": "^6.0.3",
|
"svelte-preprocess": "^6.0.3",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^6.3.6",
|
"vite": "^6.3.6"
|
||||||
"vitest": "^4.0.12"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"svelte": "^5"
|
"svelte": "^5"
|
||||||
|
|||||||
@@ -54,7 +54,6 @@
|
|||||||
"rollup": "^4.59.0",
|
"rollup": "^4.59.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.2.4",
|
"vite": "^7.2.4",
|
||||||
"vitest": "^4.0.12",
|
|
||||||
"vue": "^3.4.21"
|
"vue": "^3.4.21"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
5134
pnpm-lock.yaml
generated
5134
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ export default async function generateExportFile(
|
|||||||
iconNodes: Record<string, INode>,
|
iconNodes: Record<string, INode>,
|
||||||
exportModuleNameCasing: 'camel' | 'pascal',
|
exportModuleNameCasing: 'camel' | 'pascal',
|
||||||
iconFileExtension = '',
|
iconFileExtension = '',
|
||||||
|
useDefaultExports = true,
|
||||||
) {
|
) {
|
||||||
const fileName = path.basename(inputEntry);
|
const fileName = path.basename(inputEntry);
|
||||||
|
|
||||||
@@ -25,7 +26,9 @@ export default async function generateExportFile(
|
|||||||
} else if (exportModuleNameCasing === 'pascal') {
|
} else if (exportModuleNameCasing === 'pascal') {
|
||||||
componentName = toPascalCase(iconName);
|
componentName = toPascalCase(iconName);
|
||||||
}
|
}
|
||||||
const importString = `export { default as ${componentName} } from './${iconName}${iconFileExtension}';\n`;
|
const importString = `export ${
|
||||||
|
useDefaultExports ? `{ default as ${componentName} }` : `*`
|
||||||
|
} from './${iconName}${iconFileExtension}';\n`;
|
||||||
return appendFile(importString, fileName, outputDirectory);
|
return appendFile(importString, fileName, outputDirectory);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ function generateIconFiles({
|
|||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
trailingComma: 'all',
|
trailingComma: 'all',
|
||||||
printWidth: 100,
|
printWidth: 100,
|
||||||
parser: 'babel',
|
parser: iconFileExtension.endsWith('.ts') ? 'babel-ts' : 'babel',
|
||||||
})
|
})
|
||||||
: elementTemplate;
|
: elementTemplate;
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ interface CliArguments {
|
|||||||
separateIconFileExportExtension?: string;
|
separateIconFileExportExtension?: string;
|
||||||
aliasesFileExtension?: string;
|
aliasesFileExtension?: string;
|
||||||
aliasImportFileExtension?: string;
|
aliasImportFileExtension?: string;
|
||||||
|
useDefaultExports?: boolean;
|
||||||
pretty?: boolean;
|
pretty?: boolean;
|
||||||
output: string | undefined;
|
output: string | undefined;
|
||||||
}
|
}
|
||||||
@@ -62,6 +63,7 @@ const {
|
|||||||
separateIconFileExportExtension = undefined,
|
separateIconFileExportExtension = undefined,
|
||||||
aliasesFileExtension = '.js',
|
aliasesFileExtension = '.js',
|
||||||
aliasImportFileExtension = '',
|
aliasImportFileExtension = '',
|
||||||
|
useDefaultExports = true,
|
||||||
pretty = true,
|
pretty = true,
|
||||||
} = cliArguments;
|
} = cliArguments;
|
||||||
|
|
||||||
@@ -125,6 +127,7 @@ async function buildIcons() {
|
|||||||
icons,
|
icons,
|
||||||
exportModuleNameCasing,
|
exportModuleNameCasing,
|
||||||
importImportFileExtension,
|
importImportFileExtension,
|
||||||
|
useDefaultExports,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export interface ExportTemplate {
|
|||||||
getSvg: () => Promise<string>;
|
getSvg: () => Promise<string>;
|
||||||
deprecated: boolean;
|
deprecated: boolean;
|
||||||
deprecationReason: string;
|
deprecationReason: string;
|
||||||
aliases?: (string | AliasDeprecation)[];
|
|
||||||
iconData: IconData;
|
iconData: IconData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user