chore(lucide-vue-next|lucide-svelte|lucide-angular): Remove deprecated packages (#4376)

* Remove deprecated packages

* Remove `lucide-angular` package

* Remove workflow

* Update lock file
This commit is contained in:
Eric Fennis
2026-05-15 15:03:42 +02:00
committed by GitHub
parent 2214caa407
commit 523026ac18
72 changed files with 99 additions and 10833 deletions

View File

@@ -1,44 +0,0 @@
name: Lucide Angular checks
on:
push:
branches:
- next
pull_request:
paths:
- packages/lucide-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

View File

@@ -1,46 +0,0 @@
name: Lucide Svelte checks
on:
push:
branches:
- next
pull_request:
paths:
- packages/lucide-svelte/**
- packages/shared/**
- tools/build-icons/**
- tools/rollup-plugins/**
- 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-svelte 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-svelte test

View File

@@ -1,46 +0,0 @@
name: Lucide Vue Next checks
on:
push:
branches:
- next
pull_request:
paths:
- packages/lucide-vue-next/**
- packages/shared/**
- tools/build-icons/**
- tools/rollup-plugins/**
- 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-vue-next 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-vue-next test

1
.gitignore vendored
View File

@@ -39,6 +39,7 @@ packages/**/LICENSE
categories.json
tags.json
.vercel
package-lock.json
# docs
docs/.vitepress/cache

View File

@@ -1,38 +0,0 @@
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: 'element',
prefix: ['lucide', 'i', 'span'],
style: 'kebab-case',
},
],
},
},
{
files: ['*.html'],
extends: ['plugin:@angular-eslint/template/recommended'],
rules: {},
},
],
};

View File

@@ -1,55 +0,0 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# Node
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
/.vscode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
.editorconfig
# Miscellaneous
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db
# npm-yarn
package-lock.json
src/createElement.js
# angular cache
.angular/cache

View File

@@ -1,87 +0,0 @@
<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)
[![License](https://img.shields.io/badge/license-ISC-green)](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/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)
## 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>
### Hero backers 🦸
<a href="https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme Open-source PDF generation library built with TypeScript and React." /></a>
<a href="https://www.paxhistoria.co/"><img src="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia An alternate history sandbox game" /></a>
### Backers ☕
<a href="https://www.fina.money/"><img src="https://lucide.dev/sponsors/fina-money.png" width="180" alt="Fina Money Modular Finance Tracker" /></a>
### Other contributors 💸
You can find all our past and non-recurring financial contributors at [our Open Collective page](https://opencollective.com/lucide-icons).

View File

@@ -1,48 +0,0 @@
{
"$schema": "../../node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "",
"projects": {
"lucide-angular": {
"projectType": "library",
"root": ".",
"sourceRoot": "src",
"prefix": "lucide",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js"
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
}
}
}
}
},
"cli": {
"packageManager": "pnpm"
},
"defaultProject": "lucide-angular"
}

View File

@@ -1,48 +0,0 @@
/* eslint-disable global-require, func-names */
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true, // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, '../../coverage/lucide-angular'),
subdir: '.',
reporters: [{ type: 'html' }, { type: 'text-summary' }],
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['ChromeHeadlessCI'],
customLaunchers: {
ChromeHeadlessCI: {
base: 'ChromeHeadless',
flags: ['--no-sandbox'],
},
},
singleRun: false,
restartOnFileChange: true,
});
};

View File

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

View File

@@ -1,81 +0,0 @@
{
"name": "lucide-angular",
"description": "A Lucide icon library package for Angular applications.",
"version": "0.0.1",
"author": "SMAH1",
"license": "ISC",
"homepage": "https://lucide.dev",
"bugs": "https://github.com/lucide-icons/lucide/issues",
"repository": {
"type": "git",
"url": "https://github.com/lucide-icons/lucide.git",
"directory": "packages/lucide-angular"
},
"publishConfig": {
"directory": "dist"
},
"keywords": [
"Lucide",
"Angular",
"Feather",
"Icons",
"Icon",
"SVG",
"Feather Icons",
"Fontawesome",
"Font Awesome"
],
"scripts": {
"build": "pnpm clean && pnpm copy:license && pnpm copy:utils &&pnpm build:icons && pnpm build:ng",
"copy:license": "cp ../../LICENSE ./LICENSE",
"copy:utils": "mkdir -p ./src/utils && cp -rf ../../packages/shared/src/utils/hasA11yProp.ts ./src/utils/",
"clean": "rm -rf dist && rm -rf ./src/icons/*.ts",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=lucide-icons.ts",
"build:ng": "ng build --configuration production",
"test": "pnpm copy:utils && ng test --no-watch --no-progress --browsers=ChromeHeadlessCI",
"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"
},
"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",
"@lucide/build-icons": "workspace:*",
"@types/jasmine": "~3.10.0",
"@types/node": "^12.11.1",
"@typescript-eslint/eslint-plugin": "5.48.2",
"@typescript-eslint/parser": "5.48.2",
"eslint": "^8.33.0",
"eslint-config-prettier": "^8.5.0",
"jasmine-core": "~4.0.0",
"jasmine-spec-reporter": "~7.0.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.1.0",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"ng-packagr": "^13.3.0",
"prettier": "^2.8.4",
"rxjs": "~7.5.0",
"ts-node": "~10.9.1",
"tslib": "^2.3.0",
"typescript": "~4.6.2",
"zone.js": "~0.11.4"
},
"peerDependencies": {
"@angular/common": "13.x - 21.x",
"@angular/core": "13.x - 21.x"
}
}

View File

@@ -1,33 +0,0 @@
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,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `\
import { LucideIconData } from './types';
/**
* @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
*
* @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
export default ${componentName};
`;
});

View File

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

View File

@@ -1,11 +0,0 @@
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

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

View File

@@ -1,13 +0,0 @@
type HtmlAttributes = { [key: string]: string | number };
export type LucideIconNode = readonly [string, HtmlAttributes];
export type LucideIconData = readonly LucideIconNode[];
export type LucideIcons = { [key: string]: LucideIconData };
/** @deprecated Use LucideIconData instead. Will be removed in v1.0. */
export type IconData = LucideIconData;
/** @deprecated Use LucideIconNode instead. Will be removed in v1.0. */
export type IconNode = LucideIconNode;
/** @deprecated Use LucideIcons instead. Will be removed in v1.0. */
export type Icons = LucideIcons;

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,151 +0,0 @@
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 getAttribute = (attr: string) =>
testHostFixture.nativeElement.querySelector('svg').getAttribute(attr);
const getRootAttribute = (attr: string) =>
testHostFixture.nativeElement.querySelector('i-lucide').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(getAttribute('class')).toBe('lucide lucide-demo my-icon');
});
it('should set color', () => {
const color = 'red';
testHostComponent.setColor(color);
testHostFixture.detectChanges();
expect(getAttribute('stroke')).toBe(color);
});
it('should set size', () => {
const size = 12;
testHostComponent.setSize(size);
testHostFixture.detectChanges();
expect(getAttribute('width')).toBe(size.toString(10));
});
it('should set stroke width', () => {
const strokeWidth = 1.41;
testHostComponent.setStrokeWidth(strokeWidth);
testHostFixture.detectChanges();
expect(getAttribute('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(getAttribute('stroke-width')).toBe(
formatFixed(strokeWidth / (size / defaultAttributes.height)),
);
});
it('should have aria-hidden prop when no aria prop is present', async () => {
testHostFixture.detectChanges();
expect(getRootAttribute('aria-hidden')).toBe('true');
});
it('should not have aria-hidden prop when aria prop is present', async () => {
const ariaLabel = 'Demo icon';
testHostComponent.setAriaLabel(ariaLabel);
testHostFixture.detectChanges();
expect(getRootAttribute('aria-label')).toBe(ariaLabel);
expect(getRootAttribute('aria-hidden')).toBeNull();
});
it('should not have aria-hidden prop when title prop is present', async () => {
const ariaLabel = 'Demo icon';
testHostComponent.setTitle(ariaLabel);
testHostFixture.detectChanges();
expect(getRootAttribute('title')).toBe(ariaLabel);
expect(getRootAttribute('aria-hidden')).toBeNull();
});
it('should never override aria-hidden prop', async () => {
testHostComponent.setAriaHidden(true);
testHostFixture.detectChanges();
expect(getRootAttribute('aria-hidden')).toBe('true');
});
@Component({
selector: 'lucide-spec-host-component',
template: `<i-lucide
name="demo"
class="my-icon"
[color]="color"
[size]="size"
[strokeWidth]="strokeWidth"
[absoluteStrokeWidth]="absoluteStrokeWidth"
[attr.aria-label]="ariaLabel"
[attr.title]="title"
[attr.aria-hidden]="ariaHidden"
>
</i-lucide>`,
})
class TestHostComponent {
color?: string;
size?: number;
strokeWidth?: number;
absoluteStrokeWidth = true;
ariaLabel?: string = undefined;
title?: string = undefined;
ariaHidden?: boolean = undefined;
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;
}
setAriaLabel(label: string): void {
this.ariaLabel = label;
}
setTitle(title: string): void {
this.title = title;
}
setAriaHidden(ariaHidden: boolean): void {
this.ariaHidden = ariaHidden;
}
}
});

View File

@@ -1,220 +0,0 @@
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';
import { hasA11yProp } from '../utils/hasA11yProp';
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;
const restAttributes = this.getRestAttributes();
if (!hasA11yProp(restAttributes)) {
this.renderer.setAttribute(this.elem.nativeElement, 'aria-hidden', 'true');
}
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 childElements = this.elem.nativeElement.childNodes;
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),
);
}
for (const child of childElements) {
this.renderer.removeChild(this.elem.nativeElement, child);
}
this.renderer.appendChild(this.elem.nativeElement, icoElement);
}
getRestAttributes(): Record<string, string> {
const restAttributeMap: NamedNodeMap = this.elem.nativeElement.attributes;
const restAttributes = Object.fromEntries(
Array.from(restAttributeMap).map((item) => [item.name, item.value]),
);
return restAttributes;
}
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;
}
}

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,16 +0,0 @@
import { Injectable } from '@angular/core';
import defaultAttributes from '../icons/constants/default-attributes';
/**
* 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.
*/
@Injectable({ providedIn: 'root' })
export class LucideIconConfig {
color: string = defaultAttributes.stroke;
size: number = defaultAttributes.width;
strokeWidth: number = defaultAttributes['stroke-width'];
absoluteStrokeWidth = false;
}

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 +0,0 @@
import * as icons from './icons/lucide-icons';
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 };

View File

@@ -1,28 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(
path: string,
deep?: boolean,
filter?: RegExp,
): {
<T>(id: string): T;
keys(): string[];
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -1,32 +0,0 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"paths": {
"lucide-angular": ["dist"],
},
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2017",
"module": "es2020",
"lib": ["es2020", "dom"],
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true,
},
}

View File

@@ -1,12 +0,0 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/lib",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": ["src/test.ts", "**/*.spec.ts"]
}

View File

@@ -1,10 +0,0 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

View File

@@ -1,10 +0,0 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": ["jasmine"]
},
"files": ["src/test.ts"],
"include": ["**/*.spec.ts", "**/*.d.ts"]
}

View File

@@ -1,2 +0,0 @@
src/icons/*.svelte
.svelte-kit

View File

@@ -1,89 +0,0 @@
<p align="center">
<a href="https://github.com/lucide-icons/lucide">
<img src="https://lucide.dev/package-logos/lucide-svelte.svg" alt="Lucide icon library for Svelte applications." width="540">
</a>
</p>
<p align="center">
Lucide icon library for Svelte applications.
</p>
<div align="center">
[![npm](https://img.shields.io/npm/v/lucide-svelte?color=blue)](https://www.npmjs.com/package/lucide-svelte)
![NPM Downloads](https://img.shields.io/npm/dw/lucide-svelte)
[![License](https://img.shields.io/badge/license-ISC-green)](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/svelte">Documentation</a>
·
<a href="https://lucide.dev/license">License</a>
</p>
# Lucide Svelte
Implementation of the Lucide icon library for svelte applications.
## Installation
```sh
pnpm add lucide-svelte
```
```sh
npm install lucide-svelte
```
```sh
yarn add lucide-svelte
```
```sh
bun add lucide-svelte
```
> For Svelte 5, check out [`@lucide/svelte`](https://www.npmjs.com/package/@lucide/svelte) package.
## Documentation
For full documentation, visit [lucide.dev](https://lucide.dev/guide/packages/lucide-svelte)
## 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)
## 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>
### Hero backers 🦸
<a href="https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme Open-source PDF generation library built with TypeScript and React." /></a>
<a href="https://www.paxhistoria.co/"><img src="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia An alternate history sandbox game" /></a>
### Backers ☕
<a href="https://www.fina.money/"><img src="https://lucide.dev/sponsors/fina-money.png" width="180" alt="Fina Money Modular Finance Tracker" /></a>
### Other contributors 💸
You can find all our past and non-recurring financial contributors at [our Open Collective page](https://opencollective.com/lucide-icons).

View File

@@ -1,78 +0,0 @@
{
"name": "lucide-svelte",
"description": "A Lucide icon library package for Svelte applications.",
"version": "0.0.1",
"license": "ISC",
"homepage": "https://lucide.dev",
"bugs": "https://github.com/lucide-icons/lucide/issues",
"repository": {
"type": "git",
"url": "https://github.com/lucide-icons/lucide.git",
"directory": "packages/lucide-svelte"
},
"keywords": [
"Lucide",
"Svelte",
"Feather",
"Icons",
"Icon",
"SVG",
"Feather Icons",
"Fontawesome",
"Font Awesome"
],
"author": "Eric Fennis",
"type": "module",
"main": "dist/lucide-svelte.js",
"exports": {
".": {
"types": "./dist/lucide-svelte.d.ts",
"svelte": "./dist/lucide-svelte.js",
"default": "./dist/lucide-svelte.js"
},
"./icons": {
"types": "./dist/lucide-svelte.d.ts",
"svelte": "./dist/lucide-svelte.js"
},
"./icons/*": {
"types": "./dist/icons/*.svelte.d.ts",
"svelte": "./dist/icons/*.js",
"default": "./dist/icons/*.js"
}
},
"typings": "dist/lucide-svelte.d.ts",
"sideEffects": false,
"files": [
"dist"
],
"scripts": {
"build": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons && pnpm build:package && pnpm build: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",
"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:package": "svelte-package --input ./src",
"build:license": "node ./scripts/appendBlockComments.mts",
"test": "pnpm copy:license && pnpm copy:utils && pnpm build:icons && vitest run",
"test:watch": "vitest watch",
"version": "pnpm version --git-tag-version=false"
},
"devDependencies": {
"@lucide/build-icons": "workspace:*",
"@lucide/helpers": "workspace:*",
"@sveltejs/package": "^2.2.3",
"@sveltejs/vite-plugin-svelte": "^2.4.2",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/svelte": "^4.0.2",
"@tsconfig/svelte": "^5.0.0",
"jest-serializer-html": "^7.1.0",
"svelte": "^4.2.19",
"svelte-check": "^3.4.4",
"svelte-preprocess": "^5.0.4",
"typescript": "^5.8.3",
"vite": "^7.2.4"
},
"peerDependencies": {
"svelte": "^3 || ^4 || ^5.0.0-next.42"
}
}

View File

@@ -1,64 +0,0 @@
import { lstatSync } from 'fs';
import { readdir, readFile, writeFile } from 'fs/promises';
import path from 'path';
import { getCurrentDirPath } from '@lucide/helpers';
import { getJSBanner } from './license.mts';
const currentDir = await getCurrentDirPath(import.meta.url);
const targetDirectory = path.join(currentDir, '../dist');
const files = await readdir(targetDirectory, {
recursive: true,
encoding: 'utf-8',
});
// eslint-disable-next-line no-restricted-syntax
for (const file of files) {
const filepath = path.join(targetDirectory, file);
const filestat = lstatSync(filepath);
// eslint-disable-next-line no-continue
if (filestat.isFile() === false || filestat.isDirectory()) continue;
// eslint-disable-next-line no-await-in-loop
const contents = (await readFile(filepath, { encoding: 'utf-8' }) as unknown as string);
let newContents = contents;
const ext = path.extname(filepath);
let license;
if (/\.(js|mjs|cjs|ts)/.test(ext)) {
license = getJSBanner();
}
if (license) {
newContents = license + contents;
}
// Places icon block comment at the top of the Svelte component class
if (/icons\/(.*?)\.svelte\.d\.ts/.test(filepath)) {
const svelteFilepath = filepath.replace('.d.ts', '');
// eslint-disable-next-line no-await-in-loop
const svelteFileContents = (await readFile(svelteFilepath, { encoding: 'utf-8' }) as unknown as string);
const blockCommentRegex = /\/\*\*\n\s\*\s(@component\s@name)[\s\S]*?\*\//;
const blockCommentMatch = blockCommentRegex.exec(svelteFileContents);
if (blockCommentMatch !== null) {
const blockComment = blockCommentMatch[0];
const exportClassRegex = /export default class (\w+) extends SvelteComponentTyped<(.*?)> {/;
if (exportClassRegex.test(newContents)) {
newContents = newContents.replace(
exportClassRegex,
`${blockComment}\nexport default class $1 extends SvelteComponentTyped<$2> {`,
);
}
}
}
if (newContents !== contents) {
// eslint-disable-next-line no-await-in-loop
await writeFile(filepath, newContents, { encoding: 'utf-8' });
}
}

View File

@@ -1,43 +0,0 @@
import base64SVG from '@lucide/build-icons/utils/base64SVG';
import { getJSBanner } from './license.mts';
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
export default defineExportTemplate(async ({
iconName,
children,
componentName,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `\
<script lang="ts">
${getJSBanner()}
import Icon from '../Icon.svelte';
import type { IconNode, IconProps } from '../types.js';
type $$Props = IconProps;
const iconNode: IconNode = ${JSON.stringify(children)};
/**
* @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-svelte - Documentation
*
* @param {Object} props - Lucide icons props and any valid SVG attribute
* @returns {FunctionalComponent} Svelte component
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
</script>
<Icon name="${iconName}" {...$$props} iconNode={iconNode}>
<slot/>
</Icon>
`;
});

View File

@@ -1,13 +0,0 @@
import fs from 'fs';
import pkg from '../package.json' with { type: 'json' };
const license = fs.readFileSync('LICENSE', 'utf-8');
export function getJSBanner() {
return `/**
* @license ${pkg.name} v${pkg.version} - ${pkg.license}
*
* ${license.split('\n').join('\n * ')}
*/
`;
}

View File

@@ -1,40 +0,0 @@
<script lang="ts">
import defaultAttributes from './defaultAttributes'
import type { IconNode } from './types';
import { hasA11yProp } from './utils/hasA11yProp';
import { mergeClasses } from './utils/mergeClasses';
export let name: string | undefined = undefined
export let color = 'currentColor'
export let size: number | string = 24
export let strokeWidth: number | string = 2
export let absoluteStrokeWidth: boolean = false
export let iconNode: IconNode = []
</script>
<svg
{...defaultAttributes}
{...(!hasA11yProp($$restProps) ? {'aria-hidden': 'true'} : undefined)}
{...$$restProps}
width={size}
height={size}
stroke={color}
stroke-width={
absoluteStrokeWidth
? Number(strokeWidth) * 24 / Number(size)
: strokeWidth
}
class={
mergeClasses(
'lucide-icon',
'lucide',
name ? `lucide-${name}`: '',
$$props.class
)
}
>
{#each iconNode as [tag, attrs]}
<svelte:element this={tag} {...attrs}/>
{/each}
<slot />
</svg>

View File

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

View File

@@ -1,15 +0,0 @@
import type { Attrs } from './types.js';
const defaultAttributes: Attrs = {
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',
};
export default defaultAttributes;

View File

@@ -1 +0,0 @@
Folder for generated icons

View File

@@ -1,6 +0,0 @@
export * from './icons/index';
export * as icons from './icons/index';
export * from './aliases';
export { default as defaultAttributes } from './defaultAttributes';
export * from './types';
export { default as Icon } from './Icon.svelte';

View File

@@ -1,23 +0,0 @@
import type { SVGAttributes, SvelteHTMLElements } from 'svelte/elements';
export type Attrs = SVGAttributes<SVGSVGElement>;
export type IconNode = [elementName: keyof SvelteHTMLElements, attrs: Attrs][];
export interface IconProps extends Attrs {
name?: string;
color?: string;
size?: number | string;
strokeWidth?: number | string;
absoluteStrokeWidth?: boolean;
class?: string;
iconNode?: IconNode;
}
export type IconEvents = {
[evt: string]: CustomEvent<any>;
};
export type IconSlots = {
default: {};
};

View File

@@ -1,8 +0,0 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import sveltePreprocess from 'svelte-preprocess';
export default {
preprocess: sveltePreprocess({
typescript: true,
}),
};

View File

@@ -1,90 +0,0 @@
import { describe, it, expect } from 'vitest';
import { render } from '@testing-library/svelte';
import { Icon } from '../src/lucide-svelte';
import { airVent } from './testIconNodes';
describe('Using Icon Component', () => {
it('should render icon based on a iconNode', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
expect(container.firstChild).toBeDefined();
});
it('should render icon and match snapshot', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
expect(container.firstChild).toMatchSnapshot();
});
});
describe('Icon Component Accessibility', () => {
it('should have aria-hidden prop when no aria prop is present', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
expect(container.firstChild).toHaveAttribute('aria-hidden', 'true');
});
it('should not have aria-hidden prop when aria prop is present', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
'aria-label': 'Air conditioning',
},
});
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should not have aria-hidden prop when title prop is present', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
title: 'Air conditioning',
},
});
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should never override aria-hidden prop', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
'aria-hidden': 'false',
},
});
expect(container.firstChild).toHaveAttribute('aria-hidden', 'false');
});
});

View File

@@ -1,7 +0,0 @@
<script lang="ts">
import Smile from '../src/icons/smile.svelte'
</script>
<Smile>
<text>Test</text>
</Smile>

View File

@@ -1,35 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
<svg
aria-hidden="true"
class="lucide-icon lucide"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
/>
<path
d="M6 8h12"
/>
<path
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
/>
<path
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
/>
</svg>
`;

View File

@@ -1,214 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using lucide icon components > should add a class to the element 1`] = `
<svg
aria-hidden="true"
class="lucide-icon lucide lucide-smile my-icon"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M8 14s1.5 2 4 2 4-2 4-2"
/>
<line
x1="9"
x2="9.01"
y1="9"
y2="9"
/>
<line
x1="15"
x2="15.01"
y1="9"
y2="9"
/>
</svg>
`;
exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
<div>
<svg
aria-hidden="true"
class="lucide-icon lucide lucide-smile"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M8 14s1.5 2 4 2 4-2 4-2"
/>
<line
x1="9"
x2="9.01"
y1="9"
y2="9"
/>
<line
x1="15"
x2="15.01"
y1="9"
y2="9"
/>
</svg>
</div>
`;
exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
data-testid="smile-icon"
class="lucide-icon lucide lucide-smile"
>
<circle cx="12"
cy="12"
r="10"
>
</circle>
<path d="M8 14s1.5 2 4 2 4-2 4-2">
</path>
<line x1="9"
x2="9.01"
y1="9"
y2="9"
>
</line>
<line x1="15"
x2="15.01"
y1="9"
y2="9"
>
</line>
</svg>
`;
exports[`Using lucide icon components > should render an component 1`] = `
<div>
<svg
aria-hidden="true"
class="lucide-icon lucide lucide-smile"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M8 14s1.5 2 4 2 4-2 4-2"
/>
<line
x1="9"
x2="9.01"
y1="9"
y2="9"
/>
<line
x1="15"
x2="15.01"
y1="9"
y2="9"
/>
</svg>
</div>
`;
exports[`Using lucide icon components > should render an icon slot 1`] = `
<div>
<svg
aria-hidden="true"
class="lucide-icon lucide lucide-smile"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M8 14s1.5 2 4 2 4-2 4-2"
/>
<line
x1="9"
x2="9.01"
y1="9"
y2="9"
/>
<line
x1="15"
x2="15.01"
y1="9"
y2="9"
/>
<text>
Test
</text>
</svg>
</div>
`;

View File

@@ -1,92 +0,0 @@
import { describe, it, expect, afterEach } from 'vitest';
import { render, cleanup } from '@testing-library/svelte';
import { Smile, Pen, Edit2 } from '../src/lucide-svelte';
import TestSlots from './TestSlots.svelte';
describe('Using lucide icon components', () => {
afterEach(() => cleanup());
it('should render an component', () => {
const { container } = render(Smile);
expect(container).toMatchSnapshot();
});
it('should adjust the size, stroke color and stroke width', () => {
const { container } = render(Smile, {
props: {
size: 48,
color: 'red',
strokeWidth: 4,
},
});
expect(container).toMatchSnapshot();
});
it('should add a class to the element', () => {
const testClass = 'my-icon';
render(Smile, {
props: {
class: testClass,
},
});
const [icon] = document.getElementsByClassName(testClass);
expect(icon).toBeInTheDocument();
expect(icon).toMatchSnapshot();
expect(icon).toHaveClass(testClass);
expect(icon).toHaveClass('lucide');
expect(icon).toHaveClass('lucide-smile');
});
it('should add a style attribute to the element', () => {
render(Smile, {
props: {
style: 'position: absolute;',
},
});
const [icon] = document.getElementsByClassName('lucide');
expect(icon.getAttribute('style')).toContain('position: absolute');
});
it('should render an icon slot', () => {
const { container, getByText } = render(TestSlots);
const textElement = getByText('Test');
expect(textElement).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
it('should render the alias icon', () => {
const { container } = render(Pen);
const PenIconRenderedHTML = container.innerHTML;
cleanup();
const { container: Edit2Container } = render(Edit2);
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
});
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
const testId = 'smile-icon';
const { container, getByTestId } = render(Smile, {
'data-testid': testId,
color: 'red',
size: 48,
absoluteStrokeWidth: true,
});
const { attributes } = getByTestId(testId) as unknown as {
attributes: Record<string, { value: string }>;
};
expect(attributes.stroke.value).toBe('red');
expect(attributes.width.value).toBe('48');
expect(attributes.height.value).toBe('48');
expect(attributes['stroke-width'].value).toBe('1');
expect(container.innerHTML).toMatchSnapshot();
});
});

View File

@@ -1,5 +0,0 @@
import { expect } from 'vitest';
import '@testing-library/jest-dom/vitest';
import htmlSerializer from 'jest-serializer-html';
expect.addSnapshotSerializer(htmlSerializer);

View File

@@ -1,21 +0,0 @@
import type { IconNode } from '../src/lucide-svelte';
export const airVent: IconNode = [
[
'path',
{
d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2',
},
],
['path', { d: 'M6 8h12' }],
['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12' }],
['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5' }],
];
export const coffee: IconNode = [
['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1' }],
['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z' }],
['line', { x1: '6', x2: '6', y1: '2', y2: '4' }],
['line', { x1: '10', x2: '10', y1: '2', y2: '4' }],
['line', { x1: '14', x2: '14', y1: '2', y2: '4' }],
];

View File

@@ -1,14 +0,0 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"resolveJsonModule": true,
"allowJs": true,
"checkJs": true,
"isolatedModules": true,
"types": ["@testing-library/jest-dom"],
},
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte", "tests/**/*.ts"],
}

View File

@@ -1,16 +0,0 @@
import { defineConfig } from 'vitest/config';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [
svelte({
hot: false,
}),
],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './tests/setupVitest.ts',
alias: [{ find: /^svelte$/, replacement: 'svelte/internal' }],
},
});

View File

@@ -1,87 +0,0 @@
<p align="center">
<a href="https://github.com/lucide-icons/lucide">
<img src="https://lucide.dev/package-logos/lucide-vue.svg" alt="Lucide icon library for Vue applications." width="540">
</a>
</p>
<p align="center">
Lucide icon library for Vue applications.
</p>
<div align="center">
[![npm](https://img.shields.io/npm/v/lucide-vue-next?color=blue)](https://www.npmjs.com/package/lucide-vue-next)
![NPM Downloads](https://img.shields.io/npm/dw/lucide-vue-next)
[![License](https://img.shields.io/badge/license-ISC-green)](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/vue">Documentation</a>
·
<a href="https://lucide.dev/license">License</a>
</p>
# Lucide Vue
Implementation of the Lucide icon library for Vue 3 applications.
## Installation
```sh
pnpm add lucide-vue-next
```
```sh
npm install lucide-vue-next
```
```sh
yarn add lucide-vue-next
```
```sh
bun add lucide-vue-next
```
## Documentation
For full documentation, visit [lucide.dev](https://lucide.dev/guide/packages/lucide-vue-next)
## 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)
## 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>
### Hero backers 🦸
<a href="https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme Open-source PDF generation library built with TypeScript and React." /></a>
<a href="https://www.paxhistoria.co/"><img src="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia An alternate history sandbox game" /></a>
### Backers ☕
<a href="https://www.fina.money/"><img src="https://lucide.dev/sponsors/fina-money.png" width="180" alt="Fina Money Modular Finance Tracker" /></a>
### Other contributors 💸
You can find all our past and non-recurring financial contributors at [our Open Collective page](https://opencollective.com/lucide-icons).

View File

@@ -1,62 +0,0 @@
{
"name": "lucide-vue-next",
"version": "0.0.1",
"author": "Eric Fennis",
"description": "A Lucide icon library package for Vue 3 applications.",
"license": "ISC",
"homepage": "https://lucide.dev",
"bugs": "https://github.com/lucide-icons/lucide/issues",
"repository": {
"type": "git",
"url": "https://github.com/lucide-icons/lucide.git",
"directory": "packages/lucide-vue-next"
},
"keywords": [
"Lucide",
"Vue",
"Feather",
"Icons",
"Icon",
"SVG",
"Feather Icons",
"Fontawesome",
"Font Awesome"
],
"amdName": "lucide-vue-next",
"source": "build/lucide-vue-next.js",
"main": "dist/cjs/lucide-vue-next.js",
"module": "dist/esm/lucide-vue-next.js",
"typings": "dist/lucide-vue-next.d.ts",
"sideEffects": false,
"files": [
"dist",
"nuxt.js"
],
"scripts": {
"build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm build:bundles",
"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 --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts",
"build:bundles": "rollup -c ./rollup.config.mjs",
"test": "pnpm build:icons && vitest run",
"test:watch": "vitest watch",
"version": "pnpm version --git-tag-version=false"
},
"devDependencies": {
"@lucide/build-icons": "workspace:*",
"@lucide/rollup-plugins": "workspace:*",
"@lucide/shared": "workspace:*",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/vue": "^8.1.0",
"@vitejs/plugin-vue": "^6.0.2",
"@vue/test-utils": "2.4.6",
"rollup": "^4.59.0",
"rollup-plugin-dts": "^6.2.3",
"vite": "^7.2.4",
"vitest": "^4.0.12",
"vue": "^3.5.20"
},
"peerDependencies": {
"vue": ">=3.0.1"
}
}

View File

@@ -1,100 +0,0 @@
import plugins, { replace } from '@lucide/rollup-plugins';
import pkg from './package.json' with { type: 'json' };
import dts from 'rollup-plugin-dts';
const packageName = 'LucideVueNext';
const outputFileName = 'lucide-vue-next';
const outputDir = 'dist';
const inputs = ['src/lucide-vue-next.ts'];
const bundles = [
{
format: 'cjs',
inputs,
outputDir,
},
{
format: 'esm',
inputs,
outputDir,
preserveModules: true,
},
];
const configs = bundles
.map(({ inputs, outputDir, format, minify, preserveModules }) =>
inputs.map((input) => ({
input,
plugins: plugins({ pkg, minify }),
external: ['vue'],
output: {
name: packageName,
...(preserveModules
? {
dir: `${outputDir}/${format}`,
}
: {
file: `${outputDir}/${format}/${outputFileName}.js`,
}),
format,
preserveModules,
preserveModulesRoot: 'src',
sourcemap: true,
globals: {
vue: 'vue',
},
},
})),
)
.flat();
export default [
{
input: inputs[0],
output: [
{
file: `dist/${outputFileName}.d.ts`,
format: 'es',
},
],
plugins: [
dts({
compilerOptions: {
preserveSymlinks: false,
},
}),
],
},
{
input: `src/${outputFileName}.suffixed.ts`,
output: [
{
file: `dist/${outputFileName}.suffixed.d.ts`,
format: 'es',
},
],
plugins: [
dts({
compilerOptions: {
preserveSymlinks: false,
},
}),
],
},
{
input: `src/${outputFileName}.prefixed.ts`,
output: [
{
file: `dist/${outputFileName}.prefixed.d.ts`,
format: 'es',
},
],
plugins: [
dts({
compilerOptions: {
preserveSymlinks: false,
},
}),
],
},
...configs,
];

View File

@@ -1,36 +0,0 @@
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,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `
import createLucideIcon from '../createLucideIcon';
import { IconNode } from '../types';
export const __iconNode: IconNode = ${JSON.stringify(children)}
/**
* @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
*
* @param {Object} props - Lucide icons props and any valid SVG attribute
* @returns {FunctionalComponent} Vue component
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName} = createLucideIcon('${iconName}', __iconNode);
export default ${componentName};
`;
});

View File

@@ -1,63 +0,0 @@
import { type FunctionalComponent, h } from 'vue';
import {
mergeClasses,
toKebabCase,
toPascalCase,
isEmptyString,
hasA11yProp,
} from '@lucide/shared';
import defaultAttributes from './defaultAttributes';
import { IconNode, LucideProps } from './types';
interface IconProps {
iconNode: IconNode;
name: string;
as?: string | FunctionalComponent;
}
const Icon: FunctionalComponent<LucideProps & IconProps> = (
{
name,
iconNode,
absoluteStrokeWidth,
'absolute-stroke-width': absoluteStrokeWidthKebabCase,
strokeWidth,
'stroke-width': strokeWidthKebabCase,
as = 'svg',
size = defaultAttributes.width,
color = defaultAttributes.stroke,
...props
},
{ slots },
) => {
return h(
as,
{
...defaultAttributes,
...props,
width: size,
height: size,
stroke: color,
'stroke-width':
isEmptyString(absoluteStrokeWidth) ||
isEmptyString(absoluteStrokeWidthKebabCase) ||
absoluteStrokeWidth === true ||
absoluteStrokeWidthKebabCase === true
? (Number(strokeWidth || strokeWidthKebabCase || defaultAttributes['stroke-width']) *
24) /
Number(size)
: strokeWidth || strokeWidthKebabCase || defaultAttributes['stroke-width'],
class: mergeClasses(
'lucide',
props.class,
...(name
? [`lucide-${toKebabCase(toPascalCase(name))}-icon`, `lucide-${toKebabCase(name)}`]
: ['lucide-icon']),
),
...(!slots.default && !hasA11yProp(props) && { 'aria-hidden': 'true' }),
},
[...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])],
);
};
export default Icon;

View File

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

View File

@@ -1,36 +0,0 @@
import { h } from 'vue';
import type { FunctionalComponent } from 'vue';
import { IconNode, LucideProps } from './types';
import Icon from './Icon';
var showDeprecationWarning = true;
/**
* Create a Lucide icon component
* @param {string} iconName
* @param {array} iconNode
* @returns {FunctionalComponent} LucideIcon
*/
const createLucideIcon =
(iconName: string, iconNode: IconNode): FunctionalComponent<LucideProps> =>
(props, { slots, attrs }) => {
if (showDeprecationWarning) {
console.warn(
'[lucide-vue-nuxt]: This package is renamed to `@lucide/vue`. Please update your imports to avoid potential issues in the future.',
);
showDeprecationWarning = false;
}
return h(
Icon,
{
...attrs,
...props,
iconNode,
name: iconName,
},
slots,
);
};
export default createLucideIcon;

View File

@@ -1,11 +0,0 @@
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

@@ -1 +0,0 @@
Folder for generated icons

View File

@@ -1,6 +0,0 @@
export * as icons from './icons';
export * from './aliases/prefixed';
export * from './types';
export { default as createLucideIcon } from './createLucideIcon';
export { default as Icon } from './Icon';

View File

@@ -1,6 +0,0 @@
export * as icons from './icons';
export * from './aliases/suffixed';
export * from './types';
export { default as createLucideIcon } from './createLucideIcon';
export { default as Icon } from './Icon';

View File

@@ -1,7 +0,0 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases';
export * from './types';
export { default as createLucideIcon } from './createLucideIcon';
export { default as Icon } from './Icon';

View File

@@ -1,14 +0,0 @@
import type { FunctionalComponent, SVGAttributes } from 'vue';
export interface LucideProps extends Partial<SVGAttributes> {
size?: 24 | number;
strokeWidth?: number | string;
absoluteStrokeWidth?: boolean;
'absolute-stroke-width'?: boolean;
}
export type IconNode = [elementName: string, attrs: Record<string, string>][];
export type LucideIcon = FunctionalComponent<LucideProps>;
// Legacy exports
export type SVGProps = LucideProps;

View File

@@ -1,110 +0,0 @@
import { describe, it, expect } from 'vitest';
import { render } from '@testing-library/vue';
import { airVent } from './testIconNodes';
import { Icon } from '../src/lucide-vue-next';
describe('Using Icon Component', () => {
it('should render icon based on a iconNode', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
expect(container.firstChild).toBeDefined();
});
it('should render icon and match snapshot', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
expect(container.firstChild).toMatchSnapshot();
});
});
describe('Icon Component Accessibility', () => {
it('should have aria-hidden prop when no aria prop is present', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
expect(container.firstChild).toHaveAttribute('aria-hidden', 'true');
});
it('should not have aria-hidden prop when aria prop is present', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
'aria-label': 'Air conditioning',
},
});
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should not have aria-hidden prop when title prop is present', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
title: 'Air conditioning',
},
});
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should not have aria-hidden prop when there are children that could be a <title> element', async () => {
const template = {
name: 'Stub',
template: `<title>Some title</title>`,
};
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
slots: {
default: template,
},
});
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
it('should never override aria-hidden prop', async () => {
const { container } = render(Icon, {
props: {
iconNode: airVent,
size: 48,
color: 'red',
absoluteStrokeWidth: true,
'aria-hidden': false,
},
});
expect(container.firstChild).toHaveAttribute('aria-hidden', 'false');
});
});

View File

@@ -1,30 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using Icon Component > should render icon and match snapshot 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-icon"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
/>
<path
d="M6 8h12"
/>
<path
d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
/>
<path
d="M6.6 15.6A2 2 0 1 0 10 17v-5"
/>
</svg>
`;

View File

@@ -1,201 +0,0 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using lucide icon components > should add a class to the element 1`] = `
<div>
<svg
aria-hidden="true"
class="lucide my-icon lucide-smile-icon lucide-smile my-icon"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M8 14s1.5 2 4 2 4-2 4-2"
/>
<line
x1="9"
x2="9.01"
y1="9"
y2="9"
/>
<line
x1="15"
x2="15.01"
y1="9"
y2="9"
/>
</svg>
</div>
`;
exports[`Using lucide icon components > should add a style attribute to the element 1`] = `
<div>
<svg
aria-hidden="true"
class="lucide lucide-smile-icon lucide-smile"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
style="position: absolute;"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M8 14s1.5 2 4 2 4-2 4-2"
/>
<line
x1="9"
x2="9.01"
y1="9"
y2="9"
/>
<line
x1="15"
x2="15.01"
y1="9"
y2="9"
/>
</svg>
</div>
`;
exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
<div>
<svg
aria-hidden="true"
class="lucide lucide-smile-icon lucide-smile"
fill="none"
height="48"
stroke="red"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="4"
viewBox="0 0 24 24"
width="48"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M8 14s1.5 2 4 2 4-2 4-2"
/>
<line
x1="9"
x2="9.01"
y1="9"
y2="9"
/>
<line
x1="15"
x2="15.01"
y1="9"
y2="9"
/>
</svg>
</div>
`;
exports[`Using lucide icon components > should pass children to the icon slot 1`] = `
<div>
<svg
class="lucide lucide-smile-icon lucide-smile"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M8 14s1.5 2 4 2 4-2 4-2"
/>
<line
x1="9"
x2="9.01"
y1="9"
y2="9"
/>
<line
x1="15"
x2="15.01"
y1="9"
y2="9"
/>
<text>
Hello World
</text>
</svg>
</div>
`;
exports[`Using lucide icon components > should render an component 1`] = `
<div>
<svg
aria-hidden="true"
class="lucide lucide-smile-icon lucide-smile"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M8 14s1.5 2 4 2 4-2 4-2"
/>
<line
x1="9"
x2="9.01"
y1="9"
y2="9"
/>
<line
x1="15"
x2="15.01"
y1="9"
y2="9"
/>
</svg>
</div>
`;

View File

@@ -1,183 +0,0 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { render, fireEvent, cleanup } from '@testing-library/vue';
import { Smile, Edit2, Pen } from '../src/lucide-vue-next';
import defaultAttributes from '../src/defaultAttributes';
describe('Using lucide icon components', () => {
afterEach(() => cleanup());
it('should render an component', () => {
const { container } = render(Smile);
expect(container).toMatchSnapshot();
});
it('should render the icon with the default attributes', () => {
const { container } = render(Smile);
const SVGElement = container.firstElementChild;
expect(SVGElement).toHaveAttribute('xmlns', defaultAttributes.xmlns);
expect(SVGElement).toHaveAttribute('width', String(defaultAttributes.width));
expect(SVGElement).toHaveAttribute('height', String(defaultAttributes.height));
expect(SVGElement).toHaveAttribute('viewBox', defaultAttributes.viewBox);
expect(SVGElement).toHaveAttribute('fill', defaultAttributes.fill);
expect(SVGElement).toHaveAttribute('stroke', defaultAttributes.stroke);
expect(SVGElement).toHaveAttribute('stroke-width', String(defaultAttributes['stroke-width']));
expect(SVGElement).toHaveAttribute('stroke-linecap', defaultAttributes['stroke-linecap']);
expect(SVGElement).toHaveAttribute('stroke-linejoin', defaultAttributes['stroke-linejoin']);
});
it('should adjust the size, stroke color and stroke width', () => {
const { container } = render(Smile, {
props: {
size: 48,
color: 'red',
'stroke-width': 4,
},
});
const SVGElement = container.firstElementChild;
expect(SVGElement).toHaveAttribute('width', '48');
expect(SVGElement).toHaveAttribute('stroke', 'red');
expect(SVGElement).toHaveAttribute('stroke-width', '4');
expect(container).toMatchSnapshot();
});
it('should add a class to the element', () => {
const { container } = render(Smile, {
attrs: {
class: 'my-icon',
},
});
expect(container).toMatchSnapshot();
const icon = container.firstElementChild;
expect(icon).toHaveClass('my-icon');
expect(icon).toHaveClass('lucide');
expect(icon).toHaveClass('lucide-smile-icon');
});
it('should add a style attribute to the element', () => {
const { container } = render(Smile, {
attrs: {
style: 'position: absolute',
},
});
expect(container).toMatchSnapshot();
const icon = container.firstElementChild;
expect(icon).toHaveStyle({ position: 'absolute' });
});
it('should call the onClick event', async () => {
const onClick = vi.fn();
const { container } = render(Smile, {
attrs: {
onClick,
},
});
const icon = container.firstElementChild;
await fireEvent.click(icon);
expect(onClick).toHaveBeenCalled();
});
it('should pass children to the icon slot', () => {
const testText = 'Hello World';
const template = {
name: 'Stub',
template: `<text>${testText}</text>`,
};
const { getByText, container } = render(Smile, {
slots: {
default: template,
},
});
const textElement = getByText(testText);
expect(textElement).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
it('should render the alias icon', () => {
const { container } = render(Pen, {
props: {
size: 48,
color: 'red',
'stroke-width': 4,
},
});
const PenIconRenderedHTML = container.innerHTML;
cleanup();
const { container: Edit2Container } = render(Edit2, {
props: {
size: 48,
color: 'red',
'stroke-width': 4,
},
});
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
});
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
const { container } = render(Pen, {
props: {
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
const icon = container.firstElementChild;
expect(icon).toHaveAttribute('width', '48');
expect(icon).toHaveAttribute('stroke', 'red');
expect(icon).toHaveAttribute('stroke-width', '1');
});
it('should not scale the strokeWidth when absoluteStrokeWidth is as empty value attribute', () => {
const { container } = render(Pen, {
props: {
size: 48,
color: 'red',
absoluteStrokeWidth: '',
},
});
const icon = container.firstElementChild;
expect(icon).toHaveAttribute('width', '48');
expect(icon).toHaveAttribute('stroke', 'red');
expect(icon).toHaveAttribute('stroke-width', '1');
});
it('should not scale the strokeWidth when absoluteStrokeWidth is written in kebabCase', () => {
const { container } = render(Pen, {
props: {
size: 48,
color: 'red',
'stroke-width': '2',
'absolute-stroke-width': '',
},
});
const icon = container.firstElementChild;
expect(icon).toHaveAttribute('width', '48');
expect(icon).toHaveAttribute('stroke', 'red');
expect(icon).toHaveAttribute('stroke-width', '1');
});
});

View File

@@ -1 +0,0 @@
import '@testing-library/jest-dom/vitest';

View File

@@ -1,22 +0,0 @@
import { IconNode } from '../src/createLucideIcon';
export const airVent: IconNode = [
[
'path',
{
d: 'M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2',
key: 'larmp2',
},
],
['path', { d: 'M6 8h12', key: '6g4wlu' }],
['path', { d: 'M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12', key: '1bo8pg' }],
['path', { d: 'M6.6 15.6A2 2 0 1 0 10 17v-5', key: 't9h90c' }],
];
export const coffee: IconNode = [
['path', { d: 'M17 8h1a4 4 0 1 1 0 8h-1', key: 'jx4kbh' }],
['path', { d: 'M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z', key: '1bxrl0' }],
['line', { x1: '6', x2: '6', y1: '2', y2: '4', key: '1cr9l3' }],
['line', { x1: '10', x2: '10', y1: '2', y2: '4', key: '170wym' }],
['line', { x1: '14', x2: '14', y1: '2', y2: '4', key: '1c5f70' }],
];

View File

@@ -1,17 +0,0 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true,
"types": ["@testing-library/jest-dom"],
},
}

View File

@@ -1,11 +0,0 @@
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './tests/setupVitest.js',
},
});

7929
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff