Compare commits

...

10 Commits

Author SHA1 Message Date
Karsa
ae1ca07e36 fix(packages/angular-next): added angular-next package keeping original package intact 2025-12-17 21:46:02 +01:00
Karsa
818d99f41e Merge branch 'refs/heads/main' into package/angularv17 2025-12-17 10:48:40 +01:00
Karsa
a3e7e75b90 fix(packages/icons): finalize exportTemplate before migration to input signals & effect to build component data 2025-12-17 09:35:03 +01:00
taimar
3edcd9e0c3 fix and unify color-picker font-size (#3889) 2025-12-15 14:59:14 +01:00
Karsa
e851a03672 fix(packages/icons): trying some other variations 2025-12-15 11:53:37 +01:00
Karsa
0abfa2f0d5 Merge branch 'refs/heads/main' into package/angularv17
# Conflicts:
#	packages/lucide-angular/package.json
#	packages/lucide-angular/scripts/exportTemplate.mts
#	pnpm-lock.yaml
#	tools/build-icons/building/generateExportsFile.ts
#	tools/build-icons/building/generateIconFiles.ts
2025-12-15 10:05:13 +01:00
Jakob Guddas
0b8f99326c fix(icons): changed paint-bucket icon (#3880)
* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.json

* Updated icons/paint-bucket.json
2025-12-12 13:27:37 +01:00
Karsa
6c1e34df19 feat(packages): angular v17 dead end 2025-04-19 17:15:08 +02:00
Karsa
669f62bb64 Merge branch 'refs/heads/main' into package/icons 2025-04-19 12:09:52 +02:00
Karsa
708d5114d6 feat(packages): added lucide icons package skeleton 2025-04-01 17:25:10 +02:00
39 changed files with 7213 additions and 5151 deletions

View File

@@ -70,7 +70,7 @@ const value = computed({
color: var(--vp-c-text-2);
padding: 3px 8px 3px 3px;
height: auto;
font-size: 14px;
font-size: 13px;
text-align: left;
border: 1px solid transparent;
cursor: text;
@@ -90,7 +90,7 @@ const value = computed({
border: none;
background: transparent;
color: var(--vp-c-text-1);
font-size: 14px;
font-size: 13px;
text-align: left;
border-radius: 8px;
cursor: text;

View File

@@ -9,8 +9,8 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M19 12H2" />
<path d="M11 7 6 2" />
<path d="M18.992 12H2.041" />
<path d="M21.145 18.38A3.34 3.34 0 0 1 20 16.5a3.3 3.3 0 0 1-1.145 1.88c-.575.46-.855 1.02-.855 1.595A2 2 0 0 0 20 22a2 2 0 0 0 2-2.025c0-.58-.285-1.13-.855-1.595" />
<path d="m6 2 5 5" />
<path d="m8.5 4.5 2.148-2.148a1.205 1.205 0 0 1 1.704 0l7.296 7.296a1.205 1.205 0 0 1 0 1.704l-7.592 7.592a3.615 3.615 0 0 1-5.112 0l-3.888-3.888a3.615 3.615 0 0 1 0-5.112L5.67 7.33" />
</svg>

Before

Width:  |  Height:  |  Size: 613 B

After

Width:  |  Height:  |  Size: 622 B

View File

@@ -0,0 +1,73 @@
<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">
[![npm](https://img.shields.io/npm/v/lucide-angular?color=blue)](https://www.npmjs.com/package/lucide-angular)
![NPM Downloads](https://img.shields.io/npm/dw/lucide-angular)
[![GitHub](https://img.shields.io/github/license/lucide-icons/lucide)](https://lucide.dev/license)
</div>
<p align="center">
<a href="https://lucide.dev/guide/">About</a>
·
<a href="https://lucide.dev/icons/">Icons</a>
·
<a href="https://lucide.dev/guide/packages/lucide-angular">Documentation</a>
·
<a href="https://lucide.dev/license">License</a>
</p>
# Lucide Angular
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/lucide-angular)
## 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>

View File

@@ -0,0 +1,45 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"cli": {
"packageManager": "npm"
},
"newProjectRoot": "projects",
"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
}
}
}
}
}
}
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
"dest": "./dist",
"lib": {
"entryFile": "./src/public-api.ts"
}
}

View File

@@ -0,0 +1,56 @@
{
"name": "@lucide/angular",
"version": "0.0.1",
"scripts": {
"ng": "ng",
"watch": "ng build --watch --configuration development",
"prebuild": "pnpm clean && pnpm copy:license && pnpm build:icons",
"build": "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",
"build:ng": "ng build --configuration production",
"test": "ng test --no-watch",
"test:watch": "ng test",
"lint": "npx eslint 'src/**/*.{js,jsx,ts,tsx,html,css,scss}' --quiet --fix",
"e2e": "ng e2e",
"version": "pnpm version --git-tag-version=false"
},
"prettier": {
"printWidth": 100,
"singleQuote": true,
"overrides": [
{
"files": "*.html",
"options": {
"parser": "angular"
}
}
]
},
"private": true,
"devDependencies": {
"@angular/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:*",
"@vitest/browser-playwright": "^4.0.16",
"@vitest/coverage-v8": "^4.0.16",
"jsdom": "^27.1.0",
"ng-packagr": "^21.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"typescript": "~5.9.2",
"vitest": "^4.0.16"
},
"peerDependencies": {
"@angular/common": "13.x - 21.x",
"@angular/core": "13.x - 21.x"
}
}

View File

@@ -0,0 +1,68 @@
import base64SVG from '@lucide/build-icons/utils/base64SVG';
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
export default defineExportTemplate(async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
aliases = [],
toPascalCase,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
const angularComponentName = `Lucide${componentName}`;
const selectors = [`svg[lucide${toPascalCase(iconName)}]`];
const aliasComponentNames: string[] = [];
for (const alias of aliases) {
const aliasName = typeof alias === 'string' ? alias : alias.name;
const aliasComponentName = `Lucide${toPascalCase(aliasName)}`;
const aliasSelector = `svg[lucide${toPascalCase(aliasName)}]`;
if (!selectors.includes(aliasSelector)) {
selectors.push(aliasSelector);
}
if (aliasComponentName !== angularComponentName && !aliasComponentNames.includes(aliasComponentName)) {
aliasComponentNames.push(aliasComponentName);
}
}
return `\
import { LucideIconData } from '../types';
import { LucideIconBase } from '../lucide-icon-base';
import { Component, signal } from '@angular/core';
/**
* @component @name ${componentName}
* @description Lucide SVG icon component, renders SVG Element with children.
*
* @preview ![img](data:image/svg+xml;base64,${svgBase64}) - https://lucide.dev/icons/${iconName}
* @see https://lucide.dev/guide/packages/lucide-angular - Documentation
*
* @param {Object} props - Lucide icons props and any valid SVG attribute
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
@Component({
selector: '${selectors.join(', ')}',
templateUrl: '../lucide-icon.html',
standalone: true,
})
export class ${angularComponentName} extends LucideIconBase {
static iconData: LucideIconData = ${JSON.stringify(children)};
static iconName = '${iconName}';
override readonly icon = signal(${angularComponentName}.iconData);
override readonly name = signal(${angularComponentName}.iconName);
}
${aliasComponentNames.map((aliasComponentName) => {
return `
/**
* @deprecated
* @see ${angularComponentName}
*/
export const ${aliasComponentName} = ${angularComponentName};
`;
}).join(`\n\n`)}
`;
});

View 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',
};

View 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,
});
});
});
});

View File

@@ -0,0 +1,38 @@
import { InjectionToken, Provider } from '@angular/core';
/**
* A configuration service for Lucide icon components.
*
* You can inject this service, typically in AppComponent, and customize its property values in
* order to provide default values for all the icons used in the application.
*/
export interface LucideConfig {
color: string;
size: number;
strokeWidth: number;
absoluteStrokeWidth: boolean;
}
export const lucideDefaultConfig: LucideConfig = {
color: 'currentColor',
size: 24,
strokeWidth: 2,
absoluteStrokeWidth: false,
};
export const LUCIDE_CONFIG = new InjectionToken<LucideConfig>(
'Lucide icon config',
{
factory: () => lucideDefaultConfig,
},
);
export function provideLucideConfig(config: Partial<LucideConfig>): Provider {
return {
provide: LUCIDE_CONFIG,
useValue: {
...lucideDefaultConfig,
...config,
},
};
}

View File

@@ -0,0 +1,114 @@
import {
Component,
computed,
effect,
ElementRef,
inject,
input,
Renderer2,
Signal,
} from '@angular/core';
import { LUCIDE_CONFIG } from './lucide-config';
import { LucideIconData, Nullable } from './types';
import defaultAttributes from './default-attributes';
import { formatFixed } from './utils/format-fixed';
import { toKebabCase } from './utils/to-kebab-case';
function transformNumericStringInput(
value: Nullable<string | number>,
defaultValue: number,
): number {
if (typeof value === 'string') {
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
return defaultValue;
}
return parsedValue;
}
return value ?? defaultValue;
}
/**
* @internal
*/
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'svg[lucideIcon]',
templateUrl: './lucide-icon.html',
host: {
...defaultAttributes,
class: 'lucide',
'[attr.width]': 'size().toString(10)',
'[attr.height]': 'size().toString(10)',
'[attr.stroke]': 'color()',
'[attr.stroke-width]': 'computedStrokeWidth()',
'[attr.aria-hidden]': 'ariaHidden()',
},
})
export abstract class LucideIconBase {
abstract icon: Signal<Nullable<LucideIconData>>;
abstract name: Signal<Nullable<string>>;
protected readonly iconConfig = inject(LUCIDE_CONFIG);
protected readonly elRef = inject(ElementRef);
protected readonly renderer = inject(Renderer2);
readonly title = input<Nullable<string>>();
readonly ariaHidden = computed(() => {
return !this.title();
});
readonly size = input(this.iconConfig.size, {
transform: (value: Nullable<string | number>) =>
transformNumericStringInput(value, this.iconConfig.size),
});
readonly color = input(this.iconConfig.color, {
transform: (value: Nullable<string>) => value ?? this.iconConfig.color,
});
readonly strokeWidth = input(this.iconConfig.strokeWidth, {
transform: (value: Nullable<string | number>) =>
transformNumericStringInput(value, this.iconConfig.strokeWidth),
});
readonly absoluteStrokeWidth = input(this.iconConfig.absoluteStrokeWidth, {
transform: (value: Nullable<boolean>) => value ?? this.iconConfig.absoluteStrokeWidth,
});
protected readonly computedStrokeWidth = computed(() => {
const strokeWidth = this.strokeWidth();
const size = this.size();
return this.absoluteStrokeWidth()
? formatFixed(strokeWidth / (size / 24))
: strokeWidth.toString(10);
});
constructor() {
effect((onCleanup) => {
const icon = this.icon();
if (icon) {
const elements = icon.map(([name, attrs]) => {
const element = this.renderer.createElement(name, 'http://www.w3.org/2000/svg');
for (const [name, value] of Object.entries(attrs)) {
this.renderer.setAttribute(
element,
name,
typeof value === 'number' ? value.toString(10) : value,
);
}
this.renderer.appendChild(this.elRef.nativeElement, element);
return element;
});
onCleanup(() => {
for (const element of elements) {
this.renderer.removeChild(this.elRef.nativeElement, element);
}
});
}
});
effect((onCleanup) => {
const name = this.name();
if (name) {
const cssClass = `lucide-${toKebabCase(name)}`;
this.renderer.addClass(this.elRef.nativeElement, cssClass);
onCleanup(() => {
this.renderer.removeClass(this.elRef.nativeElement, cssClass);
});
}
});
}
}

View File

@@ -0,0 +1,4 @@
@if (title(); as titleValue) {
<title>{{ titleValue }}</title>
}
<ng-content />

View File

@@ -0,0 +1,243 @@
import { Component, input, inputBinding, signal, WritableSignal } from '@angular/core';
import { LucideIcon } from './lucide-icon';
import { LucideIconData, LucideIconInput } from './types';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideLucideIcons } from './lucide-icons';
import { LucideActivity } from './icons/activity';
import { By } from '@angular/platform-browser';
@Component({
template: `@if (icon(); as iconData) {
<svg [lucideIcon]="iconData">
<rect x="1" y="1" width="22" height="22" />
</svg>
}`,
imports: [LucideIcon],
})
class TestHostComponent {
readonly icon = input<LucideIconData>();
}
describe('LucideIcon', () => {
let component: LucideIcon;
let fixture: ComponentFixture<LucideIcon>;
let icon: WritableSignal<LucideIconInput | null | undefined>;
let name: WritableSignal<string | undefined>;
let title: WritableSignal<string | undefined>;
let color: WritableSignal<string | undefined>;
let size: WritableSignal<string | number | undefined>;
let strokeWidth: WritableSignal<string | number | undefined>;
let absoluteStrokeWidth: WritableSignal<boolean | undefined>;
const getSvgAttribute = (attr: string) => fixture.nativeElement.getAttribute(attr);
const testIcon: LucideIconData = [['polyline', { points: '1 1 22 22' }]];
const testIcon2: LucideIconData = [
['circle', { cx: 12, cy: 12, r: 8 }],
['polyline', { points: '1 1 22 22' }],
];
beforeEach(async () => {
TestBed.configureTestingModule({
providers: [provideLucideIcons({ demo: testIcon })],
});
icon = signal('demo');
name = signal(undefined);
title = signal(undefined);
color = signal(undefined);
size = signal(undefined);
strokeWidth = signal(undefined);
absoluteStrokeWidth = signal(undefined);
fixture = TestBed.createComponent(LucideIcon, {
inferTagName: true,
bindings: [
inputBinding('lucideIcon', icon),
inputBinding('name', name),
inputBinding('title', title),
inputBinding('color', color),
inputBinding('size', size),
inputBinding('strokeWidth', strokeWidth),
inputBinding('absoluteStrokeWidth', absoluteStrokeWidth),
],
});
component = fixture.componentInstance;
});
it('should create', () => {
fixture.detectChanges();
expect(component).toBeTruthy();
});
it('should render children', () => {
icon.set(testIcon2);
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toBe(
'<!--container--><circle cx="12" cy="12" r="8"></circle><polyline points="1 1 22 22"></polyline>',
);
});
it('should remove children on change', () => {
icon.set(null);
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toBe('<!--container-->');
});
describe('iconInput', () => {
it('should support LucideIconData input', () => {
icon.set(testIcon);
name.set('custom-name');
fixture.detectChanges();
expect(component.icon()).toBe(testIcon);
expect(component.name()).toBe('custom-name');
expect(fixture.nativeElement.innerHTML).toBe(
'<!--container--><polyline points="1 1 22 22"></polyline>',
);
});
it('should support LucideIconComponentType input', () => {
icon.set(LucideActivity);
fixture.detectChanges();
expect(component.icon()).toBe(LucideActivity.iconData);
expect(component.name()).toBe(LucideActivity.iconName);
});
it('should support string icon name', () => {
icon.set('demo');
fixture.detectChanges();
expect(component.icon()).toBe(testIcon);
expect(component.name()).toBe('demo');
});
it('should throw error if no icon founds', () => {
icon.set('invalid');
expect(() => fixture.detectChanges()).toThrowError(`Unable to resolve icon 'invalid'`);
});
});
describe('class', () => {
it('should add all classes', () => {
fixture.detectChanges();
expect(getSvgAttribute('class')).toBe('lucide lucide-demo');
});
it('should add class from name, even if icon has name', () => {
icon.set(LucideActivity);
name.set('custom-name');
fixture.detectChanges();
expect(getSvgAttribute('class')).toBe('lucide lucide-custom-name');
});
it('should add class icon if available', () => {
icon.set(LucideActivity);
fixture.detectChanges();
expect(getSvgAttribute('class')).toBe('lucide lucide-activity');
});
it('should remove class on change', () => {
icon.set(null);
fixture.detectChanges();
expect(getSvgAttribute('class')).toBe('lucide');
});
});
describe('color', () => {
it('should default to currentColor', () => {
fixture.detectChanges();
expect(getSvgAttribute('stroke')).toBe('currentColor');
});
it('should set color', () => {
color.set('red');
fixture.detectChanges();
expect(getSvgAttribute('stroke')).toBe('red');
});
});
describe('size', () => {
it('should default to 24', () => {
fixture.detectChanges();
expect(getSvgAttribute('width')).toBe('24');
expect(getSvgAttribute('height')).toBe('24');
});
it('should set size', () => {
size.set(12);
fixture.detectChanges();
expect(getSvgAttribute('width')).toBe('12');
expect(getSvgAttribute('height')).toBe('12');
});
it('should allow string size', () => {
size.set('18');
fixture.detectChanges();
expect(getSvgAttribute('width')).toBe('18');
expect(getSvgAttribute('height')).toBe('18');
});
it('should use default on invalid string', () => {
size.set('large');
fixture.detectChanges();
expect(getSvgAttribute('width')).toBe('24');
expect(getSvgAttribute('height')).toBe('24');
});
});
describe('strokeWidth', () => {
it('should default to 2', () => {
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('2');
});
it('should set stroke width', () => {
strokeWidth.set(1.41);
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('1.41');
});
it('should allow string stroke width', () => {
strokeWidth.set('1px');
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('1');
});
});
describe('absoluteStrokeWidth', () => {
it('should not adjust stroke width', () => {
strokeWidth.set(2);
size.set(12);
absoluteStrokeWidth.set(false);
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('2');
});
it('should adjust stroke width', () => {
strokeWidth.set(2);
size.set(12);
absoluteStrokeWidth.set(true);
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('4');
});
});
describe('title', () => {
it('should set title if provided', () => {
title.set('Foobar');
fixture.detectChanges();
const titleEl = fixture.debugElement.query(By.css('title')).nativeElement;
expect(titleEl).toBeDefined();
expect(titleEl.textContent).toBe('Foobar');
});
it('should not set aria-hidden when title is set', () => {
title.set('Foobar');
fixture.detectChanges();
expect(getSvgAttribute('aria-hidden')).toBeUndefined;
});
it('should set aria-hidden if no title is provided', () => {
title.set(undefined);
fixture.detectChanges();
expect(getSvgAttribute('aria-hidden')).toBeUndefined;
});
});
describe('content projection', () => {
it('should project content', () => {
const hostFixture = TestBed.createComponent(TestHostComponent);
hostFixture.componentRef.setInput('icon', testIcon);
hostFixture.detectChanges();
hostFixture.componentRef.setInput('icon', testIcon2);
hostFixture.detectChanges();
const rect = hostFixture.debugElement.query(By.css('rect')).nativeElement;
expect(rect).toBeInstanceOf(SVGElement);
expect(rect.outerHTML).toBe('<rect x="1" y="1" width="22" height="22"></rect>');
});
});
});

View File

@@ -0,0 +1,62 @@
import { Component, computed, inject, input } from '@angular/core';
import { isLucideIconComponent, isLucideIconData, LucideIconInput } from './types';
import { LucideIconBase } from './lucide-icon-base';
import { LUCIDE_ICONS } from './lucide-icons';
import { LucideIconData } from './types';
import { toKebabCase } from './utils/to-kebab-case';
interface LucideResolvedIcon {
name?: string | null;
data: LucideIconData;
}
@Component({
selector: 'svg[lucideIcon]',
templateUrl: './lucide-icon.html',
standalone: true,
})
export class LucideIcon extends LucideIconBase {
protected readonly icons = inject(LUCIDE_ICONS);
readonly nameInput = input<string | null>(null, { alias: 'name' });
readonly iconInput = input.required<LucideIconInput | null>({
alias: 'lucideIcon',
});
readonly resolvedIcon = computed<LucideResolvedIcon | null>(() => {
return this.resolveIcon(this.nameInput(), this.iconInput());
});
override readonly name = computed<string | null>(() => {
return this.resolvedIcon()?.name ?? null;
});
override readonly icon = computed<LucideIconData | null>(() => {
return this.resolvedIcon()?.data ?? null;
});
protected resolveIcon(
name: string | null | undefined,
icon: LucideIconInput | null | undefined,
): LucideResolvedIcon | null {
if (isLucideIconData(icon)) {
return {
name,
data: icon,
};
} else if (isLucideIconComponent(icon)) {
return {
name: name ?? icon.iconName,
data: icon.iconData,
};
} else if (typeof icon === 'string') {
const name = toKebabCase(icon);
if (name in this.icons) {
return {
name,
data: this.icons[name],
};
} else {
throw new Error(`Unable to resolve icon '${icon}'`);
}
}
return null;
}
}

View File

@@ -0,0 +1,44 @@
import { TestBed } from '@angular/core/testing';
import { LUCIDE_ICONS, provideLucideIcons } from './lucide-icons';
import { LucideIconData } from './types';
import { LucideActivity } from './icons/activity';
import { LucideCircle } from './icons/circle';
import { LucideSquareX } from './icons/square-x';
describe('Lucide icons', () => {
describe('LUCIDE_ICONS', () => {
it('should default to empty map', () => {
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({});
});
});
describe('provideLucideIcons', () => {
const mockIcon: LucideIconData = [['polyline', { points: '1 1 22 22' }]];
const mockIcon2: LucideIconData = [['circle', { cx: 12, cy: 12, r: 8 }]];
it('should accept dictionary of icons', () => {
TestBed.configureTestingModule({
providers: [
provideLucideIcons({
DemoIcon: mockIcon,
MockIcon: mockIcon2,
TestIcon: LucideActivity,
}),
],
});
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({
'demo-icon': mockIcon,
'mock-icon': mockIcon2,
[LucideActivity.iconName]: LucideActivity.iconData,
});
});
it('should accept list of icon components', () => {
TestBed.configureTestingModule({
providers: [provideLucideIcons([LucideActivity, LucideSquareX, LucideCircle])],
});
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({
[LucideActivity.iconName]: LucideActivity.iconData,
[LucideSquareX.iconName]: LucideSquareX.iconData,
[LucideCircle.iconName]: LucideCircle.iconData,
});
});
});
});

View File

@@ -0,0 +1,34 @@
import { InjectionToken, Provider } from '@angular/core';
import { LucideIconData, LucideIcons } from './types';
import { isLucideIconComponent, LucideIconComponentType } from './types';
import { toKebabCase } from './utils/to-kebab-case';
export const LUCIDE_ICONS = new InjectionToken<LucideIcons>('Lucide icons', {
factory: () => ({}),
});
export function provideLucideIcons(
icons: Record<string, LucideIconData | LucideIconComponentType> | Array<LucideIconComponentType>,
): Provider {
if (Array.isArray(icons)) {
return {
provide: LUCIDE_ICONS,
useValue: icons.reduce((acc, icon) => {
acc[toKebabCase(icon.iconName)] = icon.iconData;
return acc;
}, {} as LucideIcons),
};
} else {
return {
provide: LUCIDE_ICONS,
useValue: Object.entries(icons).reduce((acc, [name, icon]) => {
if (isLucideIconComponent(icon)) {
acc[icon.iconName] = icon.iconData;
} else {
acc[toKebabCase(name)] = icon;
}
return acc;
}, {} as LucideIcons),
};
}
}

View File

@@ -0,0 +1,7 @@
import * as icons from './icons/lucide-angular';
export * from './lucide-config';
export * from './lucide-icon';
export * from './lucide-icons';
export * from './types';
export { icons };

View File

@@ -0,0 +1,34 @@
import { InputSignal, Signal, Type } from '@angular/core';
type HtmlAttributes = { [key: string]: string | number };
export type LucideIconNode = readonly [string, HtmlAttributes];
export type LucideIconData = readonly LucideIconNode[];
export type LucideIcons = { [key: string]: LucideIconData };
export interface LucideIconComponent {
name: Signal<Nullable<string>>;
icon: Signal<Nullable<LucideIconData>>;
}
export type LucideIconComponentType = Type<LucideIconComponent> & {
iconName: string;
iconData: LucideIconData;
};
export function isLucideIconData(icon: unknown): icon is LucideIconData {
return Array.isArray(icon);
}
export function isLucideIconComponent(icon: unknown): icon is LucideIconComponentType {
return (
icon instanceof Type &&
'iconData' in icon &&
Array.isArray(icon.iconData) &&
'iconName' in icon &&
typeof icon.iconName === 'string'
);
}
export type LucideIconInput = LucideIconComponentType | LucideIconData | string;
export type Nullable<T> = T | null | undefined;

View File

@@ -0,0 +1,3 @@
export function formatFixed(number: number, decimals = 3): string {
return parseFloat(number.toFixed(decimals)).toString(10);
}

View File

@@ -0,0 +1,2 @@
export const toKebabCase = (name: string) =>
name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();

View 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,
"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"
}
]
}

View File

@@ -0,0 +1,18 @@
/* 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"
]
}

View 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"
}
}

View File

@@ -0,0 +1,15 @@
/* 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"
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "lucide-angular",
"description": "A Lucide icon library package for Angular applications.",
"name": "@lucide/angular",
"description": "A Lucide icon library package for Angular applications",
"version": "0.0.1",
"author": "SMAH1",
"license": "ISC",
@@ -38,19 +38,19 @@
"version": "pnpm version --git-tag-version=false"
},
"devDependencies": {
"@angular-devkit/build-angular": "~13.3.11",
"@angular-eslint/builder": "~13.0.0",
"@angular-eslint/eslint-plugin": "~13.0.0",
"@angular-eslint/eslint-plugin-template": "~13.0.0",
"@angular-eslint/schematics": "~13.0.0",
"@angular-eslint/template-parser": "~13.0.0",
"@angular/cli": "~13.3.11",
"@angular/common": "~13.3.0",
"@angular/compiler": "~13.3.0",
"@angular/compiler-cli": "~13.3.0",
"@angular/core": "~13.3.0",
"@angular/platform-browser": "~13.3.0",
"@angular/platform-browser-dynamic": "~13.3.0",
"@angular-devkit/build-angular": "~17.3.14",
"@angular-eslint/builder": "~17.5.3",
"@angular-eslint/eslint-plugin": "~17.5.3",
"@angular-eslint/eslint-plugin-template": "~17.5.3",
"@angular-eslint/schematics": "~17.5.3",
"@angular-eslint/template-parser": "~17.5.3",
"@angular/cli": "~17.3.14",
"@angular/common": "~17.3.12",
"@angular/compiler": "~17.3.12",
"@angular/compiler-cli": "~17.3.12",
"@angular/core": "~17.3.12",
"@angular/platform-browser": "~17.3.12",
"@angular/platform-browser-dynamic": "~17.3.12",
"@lucide/build-icons": "workspace:*",
"@types/jasmine": "~3.10.0",
"@types/node": "^12.11.1",
@@ -65,12 +65,12 @@
"karma-coverage": "~2.1.0",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"ng-packagr": "^13.3.0",
"ng-packagr": "^17.3.0",
"prettier": "^2.8.4",
"rxjs": "~7.5.0",
"rxjs": "~6.5.3",
"ts-node": "~10.9.1",
"tslib": "^2.3.0",
"typescript": "~4.6.2",
"typescript": "~5.4.5",
"zone.js": "~0.11.4"
},
"peerDependencies": {

View File

@@ -8,26 +8,65 @@ export default defineExportTemplate(async ({
getSvg,
deprecated,
deprecationReason,
aliases = [],
toPascalCase,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
const angularComponentName = `Lucide${componentName}`;
const selectors = [`svg[lucide${toPascalCase(iconName)}]`];
const aliasComponentNames: string[] = [];
for (const alias of aliases) {
const aliasName = typeof alias === 'string' ? alias : alias.name;
const aliasComponentName = `Lucide${toPascalCase(aliasName)}`;
const aliasSelector = `svg[lucide${toPascalCase(aliasName)}]`;
if (!selectors.includes(aliasSelector)) {
selectors.push(aliasSelector);
}
if (aliasComponentName !== angularComponentName && !aliasComponentNames.includes(aliasComponentName)) {
aliasComponentNames.push(aliasComponentName);
}
}
return `\
import { LucideIconData } from './types';
import { LucideIcon } from '../lib/lucide-icon.component';
import { Component } from '@angular/core';
/**
* @component @name ${componentName}
* @description Lucide SVG icon component, renders SVG Element with children.
*
* @preview ![img](data:image/svg+xml;base64,${svgBase64}) - https://lucide.dev/icons/${iconName}
* @see https://lucide.dev/guide/packages/lucide-vue-next - Documentation
* @see https://lucide.dev/guide/packages/lucide-angular - Documentation
*
* @param {Object} props - Lucide icons props and any valid SVG attribute
* @returns {FunctionalComponent} Vue component
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName}: LucideIconData = ${JSON.stringify(children)}; //eslint-disable-line no-shadow-restricted-names
@Component({
selector: '${selectors.join(', ')}',
template: '',
standalone: true,
})
export class ${angularComponentName} extends LucideIcon {
static iconData: LucideIconData = ${JSON.stringify(children)};
static iconName = '${iconName}';
override get icon() {
return ${angularComponentName}.iconData;
}
override get name() {
return ${angularComponentName}.iconName;
}
}
export default ${componentName};
${aliasComponentNames.map(([aliasComponentName]) => {
return `
/**
* @deprecated
* @see ${angularComponentName}
*/
export const ${aliasComponentName} = ${angularComponentName};
`;
}).join(`\n\n`)}
`;
});

View File

@@ -1,3 +0,0 @@
export * from './aliases';
export * from './prefixed';
export * from './suffixed';

View File

@@ -1,8 +0,0 @@
{
"ngPackage": {
"dest": "dist",
"lib": {
"entryFile": "../public-api.ts"
}
}
}

View File

@@ -1,4 +0,0 @@
/** @deprecated Use the injection token LUCIDE_ICONS instead. Will be removed in v1.0. */
export class Icons {
constructor(private icons: object) {}
}

View File

@@ -1,31 +0,0 @@
import { ModuleWithProviders, NgModule, Optional } from '@angular/core';
import { LucideAngularComponent } from './lucide-angular.component';
import { LucideIcons } from '../icons/types';
import { LUCIDE_ICONS, LucideIconProvider } from './lucide-icon.provider';
import { Icons } from './icons.provider';
const legacyIconProviderFactory = (icons?: LucideIcons) => {
return new LucideIconProvider(icons ?? {});
};
@NgModule({
declarations: [LucideAngularComponent],
imports: [],
exports: [LucideAngularComponent],
})
export class LucideAngularModule {
static pick(icons: LucideIcons): ModuleWithProviders<LucideAngularModule> {
return {
ngModule: LucideAngularModule,
providers: [
{ provide: LUCIDE_ICONS, multi: true, useValue: new LucideIconProvider(icons) },
{
provide: LUCIDE_ICONS,
multi: true,
useFactory: legacyIconProviderFactory,
deps: [[new Optional(), Icons]],
},
],
};
}
}

View File

@@ -1,23 +1,19 @@
import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LucideAngularModule } from './lucide-angular.module';
import { formatFixed, LucideAngularComponent } from './lucide-angular.component';
import { formatFixed, LucideIcon } from './lucide-icon.component';
import defaultAttributes from '../icons/constants/default-attributes';
import { LucideIcons } from '../icons/types';
import { LucideIconData } from '../icons/types';
describe('LucideAngularComponent', () => {
let testHostComponent: TestHostComponent;
let testHostFixture: ComponentFixture<TestHostComponent>;
const getSvgAttribute = (attr: string) =>
testHostFixture.nativeElement.querySelector('svg').getAttribute(attr);
const testIcons: LucideIcons = {
Demo: [['polyline', { points: '1 1 22 22' }]],
};
const testIcon: LucideIconData = [['polyline', { points: '1 1 22 22' }]];
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [LucideAngularComponent, TestHostComponent],
imports: [LucideAngularModule.pick(testIcons)],
declarations: [LucideIcon, TestHostComponent],
imports: [],
}).compileComponents();
testHostFixture = TestBed.createComponent(TestHostComponent);
testHostComponent = testHostFixture.componentInstance;
@@ -63,7 +59,7 @@ describe('LucideAngularComponent', () => {
testHostComponent.setAbsoluteStrokeWidth(true);
testHostFixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe(
formatFixed(strokeWidth / (size / defaultAttributes.height)),
formatFixed(strokeWidth / (size / defaultAttributes.height))
);
});
@@ -71,6 +67,7 @@ describe('LucideAngularComponent', () => {
selector: 'lucide-spec-host-component',
template: ` <i-lucide
name="demo"
[img]="testIcon"
class="my-icon"
[color]="color"
[size]="size"
@@ -83,6 +80,7 @@ describe('LucideAngularComponent', () => {
size?: number;
strokeWidth?: number;
absoluteStrokeWidth = true;
readonly testIcon = testIcon;
setColor(color: string): void {
this.color = color;

View File

@@ -5,12 +5,13 @@ import {
Inject,
Input,
OnChanges,
OnInit,
Renderer2,
SimpleChange,
Type,
} from '@angular/core';
import { LucideIconData } from '../icons/types';
import defaultAttributes from '../icons/constants/default-attributes';
import { LUCIDE_ICONS, LucideIconProviderInterface } from './lucide-icon.provider';
import { LucideIconConfig } from './lucide-icon.config';
interface TypedChange<T> extends SimpleChange {
@@ -22,7 +23,7 @@ type SvgAttributes = { [key: string]: string | number };
type LucideAngularComponentChanges = {
name?: TypedChange<string | LucideIconData>;
img?: TypedChange<LucideIconData | undefined>;
icon?: TypedChange<LucideIconData | undefined>;
color?: TypedChange<string>;
size?: TypedChange<number>;
strokeWidth?: TypedChange<number>;
@@ -34,24 +35,50 @@ export function formatFixed(number: number, decimals = 3): string {
return parseFloat(number.toFixed(decimals)).toString(10);
}
export type LucideIconComponentType = Type<LucideIcon> & { iconData: LucideIconData; name: string };
function isLucideIconComponent(icon: unknown): icon is LucideIconComponentType {
return (
icon instanceof Type &&
'iconData' in icon &&
Array.isArray(icon.iconData) &&
'iconName' in icon &&
typeof icon.iconName === 'string'
);
}
@Component({
selector: 'lucide-angular, lucide-icon, i-lucide, span-lucide',
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'svg[lucideIcon]',
template: '<ng-content></ng-content>',
standalone: true,
})
export class LucideAngularComponent implements OnChanges {
// eslint-disable-next-line @angular-eslint/component-class-suffix
export class LucideIcon implements OnInit, OnChanges {
@Input() class?: string;
@Input() name?: string | LucideIconData;
@Input() img?: LucideIconData;
_name?: string;
@Input() set name(name: string | undefined) {
this._name = name;
}
get name() {
return this._name;
}
_icon?: LucideIconData | LucideIconComponentType | null;
@Input('lucideIcon') set icon(icon: LucideIconData | LucideIconComponentType | null | undefined) {
this._icon = icon;
}
get icon() {
return this._icon;
}
@Input() color?: string;
@Input() absoluteStrokeWidth = false;
defaultSize: number;
constructor(
@Inject(ElementRef) private elem: ElementRef,
@Inject(Renderer2) private renderer: Renderer2,
@Inject(ChangeDetectorRef) private changeDetector: ChangeDetectorRef,
@Inject(LUCIDE_ICONS) private iconProviders: LucideIconProviderInterface[],
@Inject(LucideIconConfig) private iconConfig: LucideIconConfig,
@Inject(ElementRef) protected elem: ElementRef,
@Inject(Renderer2) protected renderer: Renderer2,
@Inject(ChangeDetectorRef) protected changeDetector: ChangeDetectorRef,
@Inject(LucideIconConfig) protected iconConfig: LucideIconConfig
) {
this.defaultSize = defaultAttributes.height;
}
@@ -84,38 +111,35 @@ export class LucideAngularComponent implements OnChanges {
}
}
ngOnInit() {
this.buildIcon();
}
ngOnChanges(changes: LucideAngularComponentChanges): void {
if (
changes.name ||
changes.img ||
changes.icon ||
changes.color ||
changes.size ||
changes.absoluteStrokeWidth ||
changes.strokeWidth ||
changes.class
) {
this.buildIcon();
}
this.changeDetector.markForCheck();
}
buildIcon(): void {
this.color = this.color ?? this.iconConfig.color;
this.size = this.parseNumber(this.size ?? this.iconConfig.size);
this.strokeWidth = this.parseNumber(this.strokeWidth ?? this.iconConfig.strokeWidth);
this.absoluteStrokeWidth = this.absoluteStrokeWidth ?? this.iconConfig.absoluteStrokeWidth;
const nameOrIcon = this.img ?? this.name;
if (typeof nameOrIcon === 'string') {
const icoOfName = this.getIcon(this.toPascalCase(nameOrIcon));
if (icoOfName) {
this.replaceElement(icoOfName);
} else {
throw new Error(
`The "${nameOrIcon}" icon has not been provided by any available icon providers.`,
);
console.log('Hello, my name is ', this.name, ' my icon is ', this.icon);
if (this.icon) {
this.replaceElement(isLucideIconComponent(this.icon) ? this.icon.iconData : this.icon);
}
} else if (Array.isArray(nameOrIcon)) {
this.replaceElement(nameOrIcon);
} else {
throw new Error(`No icon name or image has been provided.`);
}
}
this.changeDetector.markForCheck();
}
replaceElement(img: LucideIconData): void {
@@ -128,7 +152,10 @@ export class LucideAngularComponent implements OnChanges {
? formatFixed(this.strokeWidth / (this.size / this.defaultSize))
: this.strokeWidth.toString(10),
};
const icoElement = this.createElement(['svg', attributes, img]);
const icoElement = this.elem.nativeElement;
for (const [name, value] of Object.entries(attributes)) {
icoElement.setAttribute(name, value);
}
icoElement.classList.add('lucide');
if (typeof this.name === 'string') {
icoElement.classList.add(`lucide-${this.name.replace('_', '-')}`);
@@ -138,24 +165,19 @@ export class LucideAngularComponent implements OnChanges {
...this.class
.split(/ /)
.map((a) => a.trim())
.filter((a) => a.length > 0),
.filter((a) => a.length > 0)
);
}
const childElements = this.elem.nativeElement.childNodes;
for (const child of childElements) {
for (const child of icoElement.children) {
this.renderer.removeChild(this.elem.nativeElement, child);
}
this.renderer.appendChild(this.elem.nativeElement, icoElement);
for (const node of img) {
const childElement = this.createElement(node);
this.renderer.appendChild(icoElement, childElement);
}
}
toPascalCase(str: string): string {
return str.replace(
/(\w)([a-z0-9]*)(_|-|\s*)/g,
(g0, g1, g2) => g1.toUpperCase() + g2.toLowerCase(),
);
}
private parseNumber(value: string | number): number {
protected parseNumber(value: string | number): number {
if (typeof value === 'string') {
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
@@ -166,21 +188,10 @@ export class LucideAngularComponent implements OnChanges {
return value;
}
private getIcon(name: string): LucideIconData | null {
for (const iconProvider of Array.isArray(this.iconProviders)
? this.iconProviders
: [this.iconProviders]) {
if (iconProvider.hasIcon(name)) {
return iconProvider.getIcon(name);
}
}
return null;
}
private createElement([tag, attrs, children = []]: readonly [
protected createElement([tag, attrs, children = []]: readonly [
string,
SvgAttributes,
LucideIconData?,
LucideIconData?
]) {
const element = this.renderer.createElement(tag, 'http://www.w3.org/2000/svg');

View File

@@ -1,24 +0,0 @@
import { LucideIconData, LucideIcons } from '../icons/types';
import { InjectionToken } from '@angular/core';
export interface LucideIconProviderInterface {
hasIcon(name: string): boolean;
getIcon(name: string): LucideIconData | null;
}
export const LUCIDE_ICONS = new InjectionToken<LucideIconProviderInterface>('LucideIcons', {
factory: () => new LucideIconProvider({}),
});
export class LucideIconProvider implements LucideIconProviderInterface {
constructor(private icons: LucideIcons) {}
getIcon(name: string): LucideIconData | null {
return this.hasIcon(name) ? this.icons[name] : null;
}
hasIcon(name: string): boolean {
return typeof this.icons === 'object' && name in this.icons;
}
}

View File

@@ -1,10 +1,7 @@
import * as icons from './icons/lucide-icons';
export * from './lib/lucide-angular.component';
export * from './lib/lucide-angular.module';
export * from './lib/lucide-icon.component';
export * from './lib/lucide-icon.config';
export * from './lib/lucide-icon.provider';
export * from './icons/lucide-icons';
export * from './icons/types';
export * from './aliases';
export { icons };

11085
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@ export default async function generateExportFile(
} else if (exportModuleNameCasing === 'pascal') {
componentName = toPascalCase(iconName);
}
const importString = `export { default as ${componentName} } from './${iconName}${iconFileExtension}';\n`;
const importString = `export * from './${iconName}${iconFileExtension}';\n`;
return appendFile(importString, fileName, outputDirectory);
});

View File

@@ -48,7 +48,11 @@ function generateIconFiles({
]);
const getSvg = () => readSvg(`${iconName}.svg`, iconsDir);
const { deprecated = false, toBeRemovedInVersion = undefined } = iconMetaData[iconName];
const {
deprecated = false,
toBeRemovedInVersion = undefined,
aliases,
} = iconMetaData[iconName];
const deprecationReason = deprecated
? deprecationReasonTemplate(iconMetaData[iconName]?.deprecationReason ?? '', {
componentName,
@@ -64,6 +68,8 @@ function generateIconFiles({
getSvg,
deprecated,
deprecationReason,
aliases,
toPascalCase,
});
const output = pretty
@@ -71,7 +77,7 @@ function generateIconFiles({
singleQuote: true,
trailingComma: 'all',
printWidth: 100,
parser: 'babel',
parser: iconFileExtension.endsWith('.ts') ? 'babel-ts' : 'babel',
})
: elementTemplate;
@@ -81,7 +87,7 @@ function generateIconFiles({
const output = `export { default } from "./${iconName}${iconFileExtension}";\n`;
const location = path.join(
iconsDistDirectory,
`${iconName}${separateIconFileExportExtension ?? iconFileExtension}`,
`${iconName}${separateIconFileExportExtension ?? iconFileExtension}`
);
await fs.promises.writeFile(location, output, 'utf-8');

View File

@@ -13,6 +13,8 @@ export type TemplateFunction = (params: {
getSvg: () => Promise<string>;
deprecated?: boolean;
deprecationReason?: string;
aliases?: (string | AliasDeprecation)[];
toPascalCase: (value: string) => string;
}) => Promise<string>;
export type Path = string;

View File

@@ -7,6 +7,8 @@ export interface ExportTemplate {
getSvg: () => Promise<string>;
deprecated: boolean;
deprecationReason: string;
aliases: Array<string | { name: string }>;
toPascalCase: (value: string) => string;
}
export type TemplateFunction = (params: ExportTemplate) => Promise<string>;