mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-13 13:48:26 +00:00
fix(packages/angular): revert changes made to lucide-angular
This commit is contained in:
@@ -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": "~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",
|
||||
"@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",
|
||||
"@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": "^17.3.0",
|
||||
"ng-packagr": "^13.3.0",
|
||||
"prettier": "^2.8.4",
|
||||
"rxjs": "~6.5.3",
|
||||
"rxjs": "~7.5.0",
|
||||
"ts-node": "~10.9.1",
|
||||
"tslib": "^2.3.0",
|
||||
"typescript": "~5.4.5",
|
||||
"typescript": "~4.6.2",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -8,65 +8,26 @@ 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  - https://lucide.dev/icons/${iconName}
|
||||
* @see https://lucide.dev/guide/packages/lucide-angular - Documentation
|
||||
* @see https://lucide.dev/guide/packages/lucide-vue-next - Documentation
|
||||
*
|
||||
* @param {Object} props - Lucide icons props and any valid SVG attribute
|
||||
* @returns {FunctionalComponent} Vue component
|
||||
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
}
|
||||
const ${componentName}: LucideIconData = ${JSON.stringify(children)}; //eslint-disable-line no-shadow-restricted-names
|
||||
|
||||
${aliasComponentNames.map(([aliasComponentName]) => {
|
||||
return `
|
||||
/**
|
||||
* @deprecated
|
||||
* @see ${angularComponentName}
|
||||
*/
|
||||
export const ${aliasComponentName} = ${angularComponentName};
|
||||
`;
|
||||
}).join(`\n\n`)}
|
||||
export default ${componentName};
|
||||
`;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './aliases';
|
||||
export * from './prefixed';
|
||||
export * from './suffixed';
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"ngPackage": {
|
||||
"dest": "dist",
|
||||
"lib": {
|
||||
"entryFile": "../public-api.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** @deprecated Use the injection token LUCIDE_ICONS instead. Will be removed in v1.0. */
|
||||
export class Icons {
|
||||
constructor(private icons: object) {}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
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 defaultAttributes from '../icons/constants/default-attributes';
|
||||
import { LucideIcons } 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' }]],
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [LucideAngularComponent, TestHostComponent],
|
||||
imports: [LucideAngularModule.pick(testIcons)],
|
||||
}).compileComponents();
|
||||
testHostFixture = TestBed.createComponent(TestHostComponent);
|
||||
testHostComponent = testHostFixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
testHostFixture.detectChanges();
|
||||
expect(testHostComponent).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should add all classes', () => {
|
||||
testHostFixture.detectChanges();
|
||||
|
||||
expect(getSvgAttribute('class')).toBe('lucide lucide-demo my-icon');
|
||||
});
|
||||
|
||||
it('should set color', () => {
|
||||
const color = 'red';
|
||||
testHostComponent.setColor(color);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getSvgAttribute('stroke')).toBe(color);
|
||||
});
|
||||
|
||||
it('should set size', () => {
|
||||
const size = 12;
|
||||
testHostComponent.setSize(size);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getSvgAttribute('width')).toBe(size.toString(10));
|
||||
});
|
||||
|
||||
it('should set stroke width', () => {
|
||||
const strokeWidth = 1.41;
|
||||
testHostComponent.setStrokeWidth(strokeWidth);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getSvgAttribute('stroke-width')).toBe(strokeWidth.toString(10));
|
||||
});
|
||||
|
||||
it('should adjust stroke width', () => {
|
||||
const strokeWidth = 2;
|
||||
const size = 12;
|
||||
testHostComponent.setStrokeWidth(strokeWidth);
|
||||
testHostComponent.setSize(12);
|
||||
testHostComponent.setAbsoluteStrokeWidth(true);
|
||||
testHostFixture.detectChanges();
|
||||
expect(getSvgAttribute('stroke-width')).toBe(
|
||||
formatFixed(strokeWidth / (size / defaultAttributes.height)),
|
||||
);
|
||||
});
|
||||
|
||||
@Component({
|
||||
selector: 'lucide-spec-host-component',
|
||||
template: ` <i-lucide
|
||||
name="demo"
|
||||
class="my-icon"
|
||||
[color]="color"
|
||||
[size]="size"
|
||||
[strokeWidth]="strokeWidth"
|
||||
[absoluteStrokeWidth]="absoluteStrokeWidth"
|
||||
></i-lucide>`,
|
||||
})
|
||||
class TestHostComponent {
|
||||
color?: string;
|
||||
size?: number;
|
||||
strokeWidth?: number;
|
||||
absoluteStrokeWidth = true;
|
||||
|
||||
setColor(color: string): void {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
setSize(size: number): void {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
setStrokeWidth(strokeWidth: number): void {
|
||||
this.strokeWidth = strokeWidth;
|
||||
}
|
||||
|
||||
setAbsoluteStrokeWidth(absoluteStrokeWidth: boolean): void {
|
||||
this.absoluteStrokeWidth = absoluteStrokeWidth;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,202 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
Inject,
|
||||
Input,
|
||||
OnChanges,
|
||||
Renderer2,
|
||||
SimpleChange,
|
||||
} 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 {
|
||||
previousValue: T;
|
||||
currentValue: T;
|
||||
}
|
||||
|
||||
type SvgAttributes = { [key: string]: string | number };
|
||||
|
||||
type LucideAngularComponentChanges = {
|
||||
name?: TypedChange<string | LucideIconData>;
|
||||
img?: TypedChange<LucideIconData | undefined>;
|
||||
color?: TypedChange<string>;
|
||||
size?: TypedChange<number>;
|
||||
strokeWidth?: TypedChange<number>;
|
||||
absoluteStrokeWidth?: TypedChange<boolean>;
|
||||
class: TypedChange<string>;
|
||||
};
|
||||
|
||||
export function formatFixed(number: number, decimals = 3): string {
|
||||
return parseFloat(number.toFixed(decimals)).toString(10);
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'lucide-angular, lucide-icon, i-lucide, span-lucide',
|
||||
template: '<ng-content></ng-content>',
|
||||
})
|
||||
export class LucideAngularComponent implements OnChanges {
|
||||
@Input() class?: string;
|
||||
@Input() name?: string | LucideIconData;
|
||||
@Input() img?: LucideIconData;
|
||||
@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,
|
||||
) {
|
||||
this.defaultSize = defaultAttributes.height;
|
||||
}
|
||||
|
||||
_size?: number;
|
||||
|
||||
get size(): number {
|
||||
return this._size ?? this.iconConfig.size;
|
||||
}
|
||||
|
||||
@Input() set size(value: string | number | undefined) {
|
||||
if (value) {
|
||||
this._size = this.parseNumber(value);
|
||||
} else {
|
||||
delete this._size;
|
||||
}
|
||||
}
|
||||
|
||||
_strokeWidth?: number;
|
||||
|
||||
get strokeWidth(): number {
|
||||
return this._strokeWidth ?? this.iconConfig.strokeWidth;
|
||||
}
|
||||
|
||||
@Input() set strokeWidth(value: string | number | undefined) {
|
||||
if (value) {
|
||||
this._strokeWidth = this.parseNumber(value);
|
||||
} else {
|
||||
delete this._strokeWidth;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: LucideAngularComponentChanges): void {
|
||||
if (
|
||||
changes.name ||
|
||||
changes.img ||
|
||||
changes.color ||
|
||||
changes.size ||
|
||||
changes.absoluteStrokeWidth ||
|
||||
changes.strokeWidth ||
|
||||
changes.class
|
||||
) {
|
||||
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.`,
|
||||
);
|
||||
}
|
||||
} 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 {
|
||||
const attributes = {
|
||||
...defaultAttributes,
|
||||
width: this.size,
|
||||
height: this.size,
|
||||
stroke: this.color ?? this.iconConfig.color,
|
||||
'stroke-width': this.absoluteStrokeWidth
|
||||
? formatFixed(this.strokeWidth / (this.size / this.defaultSize))
|
||||
: this.strokeWidth.toString(10),
|
||||
};
|
||||
const icoElement = this.createElement(['svg', attributes, img]);
|
||||
icoElement.classList.add('lucide');
|
||||
if (typeof this.name === 'string') {
|
||||
icoElement.classList.add(`lucide-${this.name.replace('_', '-')}`);
|
||||
}
|
||||
if (this.class) {
|
||||
icoElement.classList.add(
|
||||
...this.class
|
||||
.split(/ /)
|
||||
.map((a) => a.trim())
|
||||
.filter((a) => a.length > 0),
|
||||
);
|
||||
}
|
||||
const childElements = this.elem.nativeElement.childNodes;
|
||||
for (const child of childElements) {
|
||||
this.renderer.removeChild(this.elem.nativeElement, child);
|
||||
}
|
||||
this.renderer.appendChild(this.elem.nativeElement, icoElement);
|
||||
}
|
||||
|
||||
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 {
|
||||
if (typeof value === 'string') {
|
||||
const parsedValue = parseInt(value, 10);
|
||||
if (isNaN(parsedValue)) {
|
||||
throw new Error(`${value} is not numeric.`);
|
||||
}
|
||||
return parsedValue;
|
||||
}
|
||||
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 [
|
||||
string,
|
||||
SvgAttributes,
|
||||
LucideIconData?,
|
||||
]) {
|
||||
const element = this.renderer.createElement(tag, 'http://www.w3.org/2000/svg');
|
||||
|
||||
Object.keys(attrs).forEach((name) => {
|
||||
const attrValue: string =
|
||||
typeof attrs[name] === 'string' ? (attrs[name] as string) : attrs[name].toString(10);
|
||||
this.renderer.setAttribute(element, name, attrValue);
|
||||
});
|
||||
|
||||
if (children.length) {
|
||||
children.forEach((child) => {
|
||||
const childElement = this.createElement(child);
|
||||
this.renderer.appendChild(element, childElement);
|
||||
});
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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]],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import * as icons from './icons/lucide-icons';
|
||||
|
||||
export * from './lib/lucide-icon.component';
|
||||
export * from './lib/lucide-angular.component';
|
||||
export * from './lib/lucide-angular.module';
|
||||
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 };
|
||||
|
||||
Reference in New Issue
Block a user