mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-05-18 08:45:08 +02:00
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:
44
.github/workflows/lucide-angular.yml
vendored
44
.github/workflows/lucide-angular.yml
vendored
@@ -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
|
||||
46
.github/workflows/lucide-svelte.yml
vendored
46
.github/workflows/lucide-svelte.yml
vendored
@@ -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
|
||||
46
.github/workflows/lucide-vue-next.yml
vendored
46
.github/workflows/lucide-vue-next.yml
vendored
@@ -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
1
.gitignore
vendored
@@ -39,6 +39,7 @@ packages/**/LICENSE
|
||||
categories.json
|
||||
tags.json
|
||||
.vercel
|
||||
package-lock.json
|
||||
|
||||
# docs
|
||||
docs/.vitepress/cache
|
||||
|
||||
@@ -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: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
55
packages/lucide-angular/.gitignore
vendored
55
packages/lucide-angular/.gitignore
vendored
@@ -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
|
||||
@@ -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">
|
||||
|
||||
[](https://www.npmjs.com/package/lucide-angular)
|
||||

|
||||
[](https://lucide.dev/license)
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://lucide.dev/guide/">About</a>
|
||||
·
|
||||
<a href="https://lucide.dev/icons/">Icons</a>
|
||||
·
|
||||
<a href="https://lucide.dev/guide/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).
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "dist",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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  - 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};
|
||||
`;
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './aliases';
|
||||
export * from './prefixed';
|
||||
export * from './suffixed';
|
||||
@@ -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',
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"ngPackage": {
|
||||
"dest": "dist",
|
||||
"lib": {
|
||||
"entryFile": "../public-api.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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]],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 };
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
@@ -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"]
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"]
|
||||
}
|
||||
2
packages/lucide-svelte/.gitignore
vendored
2
packages/lucide-svelte/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
src/icons/*.svelte
|
||||
.svelte-kit
|
||||
@@ -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">
|
||||
|
||||
[](https://www.npmjs.com/package/lucide-svelte)
|
||||

|
||||
[](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).
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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' });
|
||||
}
|
||||
}
|
||||
@@ -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  - 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>
|
||||
`;
|
||||
});
|
||||
@@ -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 * ')}
|
||||
*/
|
||||
`;
|
||||
}
|
||||
@@ -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>
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './aliases';
|
||||
export * from './prefixed';
|
||||
export * from './suffixed';
|
||||
@@ -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;
|
||||
@@ -1 +0,0 @@
|
||||
Folder for generated icons
|
||||
@@ -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';
|
||||
@@ -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: {};
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import sveltePreprocess from 'svelte-preprocess';
|
||||
|
||||
export default {
|
||||
preprocess: sveltePreprocess({
|
||||
typescript: true,
|
||||
}),
|
||||
};
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Smile from '../src/icons/smile.svelte'
|
||||
</script>
|
||||
|
||||
<Smile>
|
||||
<text>Test</text>
|
||||
</Smile>
|
||||
@@ -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>
|
||||
`;
|
||||
@@ -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>
|
||||
`;
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -1,5 +0,0 @@
|
||||
import { expect } from 'vitest';
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
import htmlSerializer from 'jest-serializer-html';
|
||||
|
||||
expect.addSnapshotSerializer(htmlSerializer);
|
||||
@@ -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' }],
|
||||
];
|
||||
@@ -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"],
|
||||
}
|
||||
@@ -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' }],
|
||||
},
|
||||
});
|
||||
@@ -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">
|
||||
|
||||
[](https://www.npmjs.com/package/lucide-vue-next)
|
||||

|
||||
[](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).
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
@@ -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  - 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};
|
||||
`;
|
||||
});
|
||||
@@ -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;
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './aliases';
|
||||
export * from './prefixed';
|
||||
export * from './suffixed';
|
||||
@@ -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;
|
||||
@@ -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',
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
Folder for generated icons
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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>
|
||||
`;
|
||||
@@ -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>
|
||||
`;
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
@@ -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' }],
|
||||
];
|
||||
@@ -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"],
|
||||
},
|
||||
}
|
||||
@@ -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
7929
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user