mirror of
https://github.com/lucide-icons/lucide.git
synced 2025-12-19 05:09:20 +01:00
Compare commits
16 Commits
package/an
...
fix-stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5236cc3072 | ||
|
|
3c3b25202a | ||
|
|
69bf052ee5 | ||
|
|
4efc8e07a0 | ||
|
|
5be50c241c | ||
|
|
506f3652c3 | ||
|
|
6b4075b89b | ||
|
|
7a68e10b12 | ||
|
|
a4531a9985 | ||
|
|
23b85f7834 | ||
|
|
9392a8f84f | ||
|
|
115fb243af | ||
|
|
5408bc1d69 | ||
|
|
efa795aa4c | ||
|
|
ba46fcf4fc | ||
|
|
484984ad68 |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -11,6 +11,9 @@ permissions:
|
|||||||
id-token: write # Required for OIDC
|
id-token: write # Required for OIDC
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
create-release:
|
create-release:
|
||||||
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)')
|
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)')
|
||||||
|
|||||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -22,6 +22,9 @@ permissions:
|
|||||||
id-token: write # Required for OIDC
|
id-token: write # Required for OIDC
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-release:
|
pre-release:
|
||||||
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere", "jguddas"]', github.actor)
|
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere", "jguddas"]', github.actor)
|
||||||
@@ -135,11 +138,8 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Outline svg Icons
|
|
||||||
run: pnpm build:outline-icons
|
|
||||||
|
|
||||||
- name: Create font in ./lucide-font
|
- name: Create font in ./lucide-font
|
||||||
run: pnpm build:font
|
run: pnpm build:font --saveCodePoints
|
||||||
|
|
||||||
- name: 'Upload to Artifacts'
|
- name: 'Upload to Artifacts'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,6 +14,7 @@ coverage
|
|||||||
stats
|
stats
|
||||||
*.log
|
*.log
|
||||||
outlined
|
outlined
|
||||||
|
lucide-font
|
||||||
packages/**/src/icons/*.js
|
packages/**/src/icons/*.js
|
||||||
packages/**/src/icons/*.ts
|
packages/**/src/icons/*.ts
|
||||||
packages/**/src/icons/*.tsx
|
packages/**/src/icons/*.tsx
|
||||||
|
|||||||
@@ -102,10 +102,16 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
|
|||||||
|
|
||||||
### Icon Component Example
|
### Icon Component Example
|
||||||
|
|
||||||
```jsx
|
```tsx
|
||||||
import { icons } from 'lucide-react-native';
|
import * as icons from 'lucide-react-native/icons';
|
||||||
|
|
||||||
const Icon = ({ name, color, size }) => {
|
interface IconProps {
|
||||||
|
name: keyof typeof icons;
|
||||||
|
color?: string;
|
||||||
|
size?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Icon = ({ name, color, size }: IconProps) => {
|
||||||
const LucideIcon = icons[name];
|
const LucideIcon = icons[name];
|
||||||
|
|
||||||
return <LucideIcon color={color} size={size} />;
|
return <LucideIcon color={color} size={size} />;
|
||||||
@@ -116,11 +122,11 @@ export default Icon;
|
|||||||
|
|
||||||
#### Using the Icon Component
|
#### Using the Icon Component
|
||||||
|
|
||||||
```jsx
|
```tsx
|
||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return <Icon name="house" />;
|
return <Icon name="House" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
37
icons/toolbox.json
Normal file
37
icons/toolbox.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../icon.schema.json",
|
||||||
|
"contributors": [
|
||||||
|
"karsa-mistmere"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"toolkit",
|
||||||
|
"tools",
|
||||||
|
"trunk",
|
||||||
|
"chest",
|
||||||
|
"box",
|
||||||
|
"storage",
|
||||||
|
"utility",
|
||||||
|
"utilities",
|
||||||
|
"container",
|
||||||
|
"kit",
|
||||||
|
"set",
|
||||||
|
"repair",
|
||||||
|
"fix",
|
||||||
|
"service",
|
||||||
|
"maintenance",
|
||||||
|
"mechanic",
|
||||||
|
"workshop",
|
||||||
|
"construction",
|
||||||
|
"hardware",
|
||||||
|
"equipment",
|
||||||
|
"gear",
|
||||||
|
"handyman",
|
||||||
|
"engineering",
|
||||||
|
"craft",
|
||||||
|
"diy"
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"tools",
|
||||||
|
"home"
|
||||||
|
]
|
||||||
|
}
|
||||||
17
icons/toolbox.svg
Normal file
17
icons/toolbox.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<svg
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<path d="M16 12v4" />
|
||||||
|
<path d="M16 6a2 2 0 0 1 1.414.586l4 4A2 2 0 0 1 22 12v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 .586-1.414l4-4A2 2 0 0 1 8 6z" />
|
||||||
|
<path d="M16 6V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" />
|
||||||
|
<path d="M2 14h20" />
|
||||||
|
<path d="M8 12v4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 471 B |
@@ -16,7 +16,7 @@
|
|||||||
"lucide-svelte": "pnpm --filter lucide-svelte",
|
"lucide-svelte": "pnpm --filter lucide-svelte",
|
||||||
"lucide-static": "pnpm --filter lucide-static",
|
"lucide-static": "pnpm --filter lucide-static",
|
||||||
"build:outline-icons": "pnpm --filter outline-svg start",
|
"build:outline-icons": "pnpm --filter outline-svg start",
|
||||||
"build:font": "pnpm --filter docs prebuild:releaseJson && pnpm --filter build-font start",
|
"build:font": "pnpm --filter build-font start",
|
||||||
"optimize": "node ./scripts/optimizeSvgs.mts",
|
"optimize": "node ./scripts/optimizeSvgs.mts",
|
||||||
"addjsons": "node ./scripts/addMissingIconJsonFiles.mts",
|
"addjsons": "node ./scripts/addMissingIconJsonFiles.mts",
|
||||||
"checkIcons": "node ./scripts/checkIconsAndCategories.mts",
|
"checkIcons": "node ./scripts/checkIconsAndCategories.mts",
|
||||||
|
|||||||
@@ -1,73 +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/packages/lucide-angular">Documentation</a>
|
|
||||||
·
|
|
||||||
<a href="https://lucide.dev/license">License</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# Lucide Angular
|
|
||||||
|
|
||||||
Implementation of the lucide icon library for angular applications.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pnpm add lucide-angular
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install lucide-angular
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh
|
|
||||||
yarn add lucide-angular
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh
|
|
||||||
bun add lucide-angular
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
For full documentation, visit [lucide.dev](https://lucide.dev/guide/packages/lucide-angular)
|
|
||||||
|
|
||||||
## Community
|
|
||||||
|
|
||||||
Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintainers and other users.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
|
|
||||||
|
|
||||||
## Sponsors
|
|
||||||
|
|
||||||
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
|
|
||||||
<img src="https://lucide.dev/vercel.svg" alt="Powered by Vercel" width="200" />
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
|
|
||||||
|
|
||||||
### Awesome backers 🍺
|
|
||||||
|
|
||||||
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
|
|
||||||
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
||||||
"version": 1,
|
|
||||||
"cli": {
|
|
||||||
"packageManager": "npm"
|
|
||||||
},
|
|
||||||
"newProjectRoot": "projects",
|
|
||||||
"projects": {
|
|
||||||
"@lucide/angular": {
|
|
||||||
"projectType": "library",
|
|
||||||
"root": ".",
|
|
||||||
"sourceRoot": "./src",
|
|
||||||
"prefix": "lib",
|
|
||||||
"architect": {
|
|
||||||
"build": {
|
|
||||||
"builder": "@angular/build:ng-packagr",
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"tsConfig": "./tsconfig.lib.prod.json"
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"tsConfig": "./tsconfig.lib.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultConfiguration": "production"
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@angular/build:unit-test",
|
|
||||||
"options": {
|
|
||||||
"tsConfig": "./tsconfig.spec.json",
|
|
||||||
"coverage": true,
|
|
||||||
"coverageReporters": ["html", "lcov"],
|
|
||||||
"coverageExclude": ["src/icons/*"],
|
|
||||||
"coverageThresholds": {
|
|
||||||
"statements": 80,
|
|
||||||
"branches": 80,
|
|
||||||
"functions": 80,
|
|
||||||
"lines": 80
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
|
|
||||||
"dest": "./dist",
|
|
||||||
"lib": {
|
|
||||||
"entryFile": "./src/public-api.ts"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@lucide/angular",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"scripts": {
|
|
||||||
"ng": "ng",
|
|
||||||
"watch": "ng build --watch --configuration development",
|
|
||||||
"prebuild": "pnpm clean && pnpm copy:license && pnpm build:icons",
|
|
||||||
"build": "pnpm build:ng",
|
|
||||||
"copy:license": "cp ../../LICENSE ./LICENSE",
|
|
||||||
"clean": "rm -rf dist && rm -rf ./src/icons/*.ts",
|
|
||||||
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --iconFileExtension=.ts --exportFileName=lucide-angular.ts",
|
|
||||||
"build:ng": "ng build --configuration production",
|
|
||||||
"test": "ng test --no-watch",
|
|
||||||
"test:watch": "ng test",
|
|
||||||
"lint": "npx eslint 'src/**/*.{js,jsx,ts,tsx,html,css,scss}' --quiet --fix",
|
|
||||||
"e2e": "ng e2e",
|
|
||||||
"version": "pnpm version --git-tag-version=false"
|
|
||||||
},
|
|
||||||
"prettier": {
|
|
||||||
"printWidth": 100,
|
|
||||||
"singleQuote": true,
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "*.html",
|
|
||||||
"options": {
|
|
||||||
"parser": "angular"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"private": true,
|
|
||||||
"devDependencies": {
|
|
||||||
"@angular/build": "^21.0.3",
|
|
||||||
"@angular/cli": "^21.0.3",
|
|
||||||
"@angular/common": "^21.0.0",
|
|
||||||
"@angular/compiler": "^21.0.0",
|
|
||||||
"@angular/compiler-cli": "^21.0.0",
|
|
||||||
"@angular/core": "^21.0.0",
|
|
||||||
"@angular/forms": "^21.0.0",
|
|
||||||
"@angular/platform-browser": "^21.0.0",
|
|
||||||
"@angular/router": "^21.0.0",
|
|
||||||
"@lucide/build-icons": "workspace:*",
|
|
||||||
"@vitest/browser-playwright": "^4.0.16",
|
|
||||||
"@vitest/coverage-v8": "^4.0.16",
|
|
||||||
"jsdom": "^27.1.0",
|
|
||||||
"ng-packagr": "^21.0.0",
|
|
||||||
"rxjs": "~7.8.0",
|
|
||||||
"tslib": "^2.3.0",
|
|
||||||
"typescript": "~5.9.2",
|
|
||||||
"vitest": "^4.0.16"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@angular/common": "13.x - 21.x",
|
|
||||||
"@angular/core": "13.x - 21.x"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +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,
|
|
||||||
aliases = [],
|
|
||||||
toPascalCase,
|
|
||||||
}) => {
|
|
||||||
const svgContents = await getSvg();
|
|
||||||
const svgBase64 = base64SVG(svgContents);
|
|
||||||
const angularComponentName = `Lucide${componentName}`;
|
|
||||||
const selectors = [`svg[lucide${toPascalCase(iconName)}]`];
|
|
||||||
const aliasComponentNames: string[] = [];
|
|
||||||
for (const alias of aliases) {
|
|
||||||
const aliasName = typeof alias === 'string' ? alias : alias.name;
|
|
||||||
const aliasComponentName = `Lucide${toPascalCase(aliasName)}`;
|
|
||||||
const aliasSelector = `svg[lucide${toPascalCase(aliasName)}]`;
|
|
||||||
if (!selectors.includes(aliasSelector)) {
|
|
||||||
selectors.push(aliasSelector);
|
|
||||||
}
|
|
||||||
if (aliasComponentName !== angularComponentName && !aliasComponentNames.includes(aliasComponentName)) {
|
|
||||||
aliasComponentNames.push(aliasComponentName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return `\
|
|
||||||
import { LucideIconData } from '../types';
|
|
||||||
import { LucideIconBase } from '../lucide-icon-base';
|
|
||||||
import { Component, signal } from '@angular/core';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @component @name ${componentName}
|
|
||||||
* @description Lucide SVG icon component, renders SVG Element with children.
|
|
||||||
*
|
|
||||||
* @preview  - https://lucide.dev/icons/${iconName}
|
|
||||||
* @see https://lucide.dev/guide/packages/lucide-angular - Documentation
|
|
||||||
*
|
|
||||||
* @param {Object} props - Lucide icons props and any valid SVG attribute
|
|
||||||
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
|
|
||||||
*/
|
|
||||||
@Component({
|
|
||||||
selector: '${selectors.join(', ')}',
|
|
||||||
templateUrl: '../lucide-icon.html',
|
|
||||||
standalone: true,
|
|
||||||
})
|
|
||||||
export class ${angularComponentName} extends LucideIconBase {
|
|
||||||
static iconData: LucideIconData = ${JSON.stringify(children)};
|
|
||||||
static iconName = '${iconName}';
|
|
||||||
override readonly icon = signal(${angularComponentName}.iconData);
|
|
||||||
override readonly name = signal(${angularComponentName}.iconName);
|
|
||||||
}
|
|
||||||
|
|
||||||
${aliasComponentNames.map((aliasComponentName) => {
|
|
||||||
return `
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* @see ${angularComponentName}
|
|
||||||
*/
|
|
||||||
export const ${aliasComponentName} = ${angularComponentName};
|
|
||||||
`;
|
|
||||||
}).join(`\n\n`)}
|
|
||||||
`;
|
|
||||||
});
|
|
||||||
@@ -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,25 +0,0 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
import { LUCIDE_CONFIG, lucideDefaultConfig, provideLucideConfig } from './lucide-config';
|
|
||||||
|
|
||||||
describe('Lucide config', () => {
|
|
||||||
describe('LUCIDE_CONFIG', () => {
|
|
||||||
it('should use default', () => {
|
|
||||||
expect(TestBed.inject(LUCIDE_CONFIG)).toBe(lucideDefaultConfig);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('provideLucideConfig', () => {
|
|
||||||
it('should use defaults', () => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
provideLucideConfig({
|
|
||||||
size: 18,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
expect(TestBed.inject(LUCIDE_CONFIG)).toEqual({
|
|
||||||
...lucideDefaultConfig,
|
|
||||||
size: 18,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import { InjectionToken, Provider } from '@angular/core';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A configuration service for Lucide icon components.
|
|
||||||
*
|
|
||||||
* You can inject this service, typically in AppComponent, and customize its property values in
|
|
||||||
* order to provide default values for all the icons used in the application.
|
|
||||||
*/
|
|
||||||
export interface LucideConfig {
|
|
||||||
color: string;
|
|
||||||
size: number;
|
|
||||||
strokeWidth: number;
|
|
||||||
absoluteStrokeWidth: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const lucideDefaultConfig: LucideConfig = {
|
|
||||||
color: 'currentColor',
|
|
||||||
size: 24,
|
|
||||||
strokeWidth: 2,
|
|
||||||
absoluteStrokeWidth: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const LUCIDE_CONFIG = new InjectionToken<LucideConfig>(
|
|
||||||
'Lucide icon config',
|
|
||||||
{
|
|
||||||
factory: () => lucideDefaultConfig,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export function provideLucideConfig(config: Partial<LucideConfig>): Provider {
|
|
||||||
return {
|
|
||||||
provide: LUCIDE_CONFIG,
|
|
||||||
useValue: {
|
|
||||||
...lucideDefaultConfig,
|
|
||||||
...config,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
import {
|
|
||||||
Component,
|
|
||||||
computed,
|
|
||||||
effect,
|
|
||||||
ElementRef,
|
|
||||||
inject,
|
|
||||||
input,
|
|
||||||
Renderer2,
|
|
||||||
Signal,
|
|
||||||
} from '@angular/core';
|
|
||||||
import { LUCIDE_CONFIG } from './lucide-config';
|
|
||||||
import { LucideIconData, Nullable } from './types';
|
|
||||||
import defaultAttributes from './default-attributes';
|
|
||||||
import { formatFixed } from './utils/format-fixed';
|
|
||||||
import { toKebabCase } from './utils/to-kebab-case';
|
|
||||||
|
|
||||||
function transformNumericStringInput(
|
|
||||||
value: Nullable<string | number>,
|
|
||||||
defaultValue: number,
|
|
||||||
): number {
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
const parsedValue = parseInt(value, 10);
|
|
||||||
if (isNaN(parsedValue)) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
return parsedValue;
|
|
||||||
}
|
|
||||||
return value ?? defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
@Component({
|
|
||||||
// eslint-disable-next-line @angular-eslint/component-selector
|
|
||||||
selector: 'svg[lucideIcon]',
|
|
||||||
templateUrl: './lucide-icon.html',
|
|
||||||
host: {
|
|
||||||
...defaultAttributes,
|
|
||||||
class: 'lucide',
|
|
||||||
'[attr.width]': 'size().toString(10)',
|
|
||||||
'[attr.height]': 'size().toString(10)',
|
|
||||||
'[attr.stroke]': 'color()',
|
|
||||||
'[attr.stroke-width]': 'computedStrokeWidth()',
|
|
||||||
'[attr.aria-hidden]': 'ariaHidden()',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
export abstract class LucideIconBase {
|
|
||||||
abstract icon: Signal<Nullable<LucideIconData>>;
|
|
||||||
abstract name: Signal<Nullable<string>>;
|
|
||||||
protected readonly iconConfig = inject(LUCIDE_CONFIG);
|
|
||||||
protected readonly elRef = inject(ElementRef);
|
|
||||||
protected readonly renderer = inject(Renderer2);
|
|
||||||
readonly title = input<Nullable<string>>();
|
|
||||||
readonly ariaHidden = computed(() => {
|
|
||||||
return !this.title();
|
|
||||||
});
|
|
||||||
readonly size = input(this.iconConfig.size, {
|
|
||||||
transform: (value: Nullable<string | number>) =>
|
|
||||||
transformNumericStringInput(value, this.iconConfig.size),
|
|
||||||
});
|
|
||||||
readonly color = input(this.iconConfig.color, {
|
|
||||||
transform: (value: Nullable<string>) => value ?? this.iconConfig.color,
|
|
||||||
});
|
|
||||||
readonly strokeWidth = input(this.iconConfig.strokeWidth, {
|
|
||||||
transform: (value: Nullable<string | number>) =>
|
|
||||||
transformNumericStringInput(value, this.iconConfig.strokeWidth),
|
|
||||||
});
|
|
||||||
readonly absoluteStrokeWidth = input(this.iconConfig.absoluteStrokeWidth, {
|
|
||||||
transform: (value: Nullable<boolean>) => value ?? this.iconConfig.absoluteStrokeWidth,
|
|
||||||
});
|
|
||||||
protected readonly computedStrokeWidth = computed(() => {
|
|
||||||
const strokeWidth = this.strokeWidth();
|
|
||||||
const size = this.size();
|
|
||||||
return this.absoluteStrokeWidth()
|
|
||||||
? formatFixed(strokeWidth / (size / 24))
|
|
||||||
: strokeWidth.toString(10);
|
|
||||||
});
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
effect((onCleanup) => {
|
|
||||||
const icon = this.icon();
|
|
||||||
if (icon) {
|
|
||||||
const elements = icon.map(([name, attrs]) => {
|
|
||||||
const element = this.renderer.createElement(name, 'http://www.w3.org/2000/svg');
|
|
||||||
for (const [name, value] of Object.entries(attrs)) {
|
|
||||||
this.renderer.setAttribute(
|
|
||||||
element,
|
|
||||||
name,
|
|
||||||
typeof value === 'number' ? value.toString(10) : value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.renderer.appendChild(this.elRef.nativeElement, element);
|
|
||||||
return element;
|
|
||||||
});
|
|
||||||
onCleanup(() => {
|
|
||||||
for (const element of elements) {
|
|
||||||
this.renderer.removeChild(this.elRef.nativeElement, element);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
effect((onCleanup) => {
|
|
||||||
const name = this.name();
|
|
||||||
if (name) {
|
|
||||||
const cssClass = `lucide-${toKebabCase(name)}`;
|
|
||||||
this.renderer.addClass(this.elRef.nativeElement, cssClass);
|
|
||||||
onCleanup(() => {
|
|
||||||
this.renderer.removeClass(this.elRef.nativeElement, cssClass);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
@if (title(); as titleValue) {
|
|
||||||
<title>{{ titleValue }}</title>
|
|
||||||
}
|
|
||||||
<ng-content />
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
import { Component, input, inputBinding, signal, WritableSignal } from '@angular/core';
|
|
||||||
import { LucideIcon } from './lucide-icon';
|
|
||||||
import { LucideIconData, LucideIconInput } from './types';
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { provideLucideIcons } from './lucide-icons';
|
|
||||||
import { LucideActivity } from './icons/activity';
|
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
template: `@if (icon(); as iconData) {
|
|
||||||
<svg [lucideIcon]="iconData">
|
|
||||||
<rect x="1" y="1" width="22" height="22" />
|
|
||||||
</svg>
|
|
||||||
}`,
|
|
||||||
imports: [LucideIcon],
|
|
||||||
})
|
|
||||||
class TestHostComponent {
|
|
||||||
readonly icon = input<LucideIconData>();
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('LucideIcon', () => {
|
|
||||||
let component: LucideIcon;
|
|
||||||
let fixture: ComponentFixture<LucideIcon>;
|
|
||||||
let icon: WritableSignal<LucideIconInput | null | undefined>;
|
|
||||||
let name: WritableSignal<string | undefined>;
|
|
||||||
let title: WritableSignal<string | undefined>;
|
|
||||||
let color: WritableSignal<string | undefined>;
|
|
||||||
let size: WritableSignal<string | number | undefined>;
|
|
||||||
let strokeWidth: WritableSignal<string | number | undefined>;
|
|
||||||
let absoluteStrokeWidth: WritableSignal<boolean | undefined>;
|
|
||||||
const getSvgAttribute = (attr: string) => fixture.nativeElement.getAttribute(attr);
|
|
||||||
const testIcon: LucideIconData = [['polyline', { points: '1 1 22 22' }]];
|
|
||||||
const testIcon2: LucideIconData = [
|
|
||||||
['circle', { cx: 12, cy: 12, r: 8 }],
|
|
||||||
['polyline', { points: '1 1 22 22' }],
|
|
||||||
];
|
|
||||||
beforeEach(async () => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [provideLucideIcons({ demo: testIcon })],
|
|
||||||
});
|
|
||||||
icon = signal('demo');
|
|
||||||
name = signal(undefined);
|
|
||||||
title = signal(undefined);
|
|
||||||
color = signal(undefined);
|
|
||||||
size = signal(undefined);
|
|
||||||
strokeWidth = signal(undefined);
|
|
||||||
absoluteStrokeWidth = signal(undefined);
|
|
||||||
fixture = TestBed.createComponent(LucideIcon, {
|
|
||||||
inferTagName: true,
|
|
||||||
bindings: [
|
|
||||||
inputBinding('lucideIcon', icon),
|
|
||||||
inputBinding('name', name),
|
|
||||||
inputBinding('title', title),
|
|
||||||
inputBinding('color', color),
|
|
||||||
inputBinding('size', size),
|
|
||||||
inputBinding('strokeWidth', strokeWidth),
|
|
||||||
inputBinding('absoluteStrokeWidth', absoluteStrokeWidth),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render children', () => {
|
|
||||||
icon.set(testIcon2);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement.innerHTML).toBe(
|
|
||||||
'<!--container--><circle cx="12" cy="12" r="8"></circle><polyline points="1 1 22 22"></polyline>',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove children on change', () => {
|
|
||||||
icon.set(null);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement.innerHTML).toBe('<!--container-->');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('iconInput', () => {
|
|
||||||
it('should support LucideIconData input', () => {
|
|
||||||
icon.set(testIcon);
|
|
||||||
name.set('custom-name');
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(component.icon()).toBe(testIcon);
|
|
||||||
expect(component.name()).toBe('custom-name');
|
|
||||||
expect(fixture.nativeElement.innerHTML).toBe(
|
|
||||||
'<!--container--><polyline points="1 1 22 22"></polyline>',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should support LucideIconComponentType input', () => {
|
|
||||||
icon.set(LucideActivity);
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(component.icon()).toBe(LucideActivity.iconData);
|
|
||||||
expect(component.name()).toBe(LucideActivity.iconName);
|
|
||||||
});
|
|
||||||
it('should support string icon name', () => {
|
|
||||||
icon.set('demo');
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(component.icon()).toBe(testIcon);
|
|
||||||
expect(component.name()).toBe('demo');
|
|
||||||
});
|
|
||||||
it('should throw error if no icon founds', () => {
|
|
||||||
icon.set('invalid');
|
|
||||||
expect(() => fixture.detectChanges()).toThrowError(`Unable to resolve icon 'invalid'`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('class', () => {
|
|
||||||
it('should add all classes', () => {
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('class')).toBe('lucide lucide-demo');
|
|
||||||
});
|
|
||||||
it('should add class from name, even if icon has name', () => {
|
|
||||||
icon.set(LucideActivity);
|
|
||||||
name.set('custom-name');
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(getSvgAttribute('class')).toBe('lucide lucide-custom-name');
|
|
||||||
});
|
|
||||||
it('should add class icon if available', () => {
|
|
||||||
icon.set(LucideActivity);
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(getSvgAttribute('class')).toBe('lucide lucide-activity');
|
|
||||||
});
|
|
||||||
it('should remove class on change', () => {
|
|
||||||
icon.set(null);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('class')).toBe('lucide');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('color', () => {
|
|
||||||
it('should default to currentColor', () => {
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('stroke')).toBe('currentColor');
|
|
||||||
});
|
|
||||||
it('should set color', () => {
|
|
||||||
color.set('red');
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('stroke')).toBe('red');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('size', () => {
|
|
||||||
it('should default to 24', () => {
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('width')).toBe('24');
|
|
||||||
expect(getSvgAttribute('height')).toBe('24');
|
|
||||||
});
|
|
||||||
it('should set size', () => {
|
|
||||||
size.set(12);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('width')).toBe('12');
|
|
||||||
expect(getSvgAttribute('height')).toBe('12');
|
|
||||||
});
|
|
||||||
it('should allow string size', () => {
|
|
||||||
size.set('18');
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('width')).toBe('18');
|
|
||||||
expect(getSvgAttribute('height')).toBe('18');
|
|
||||||
});
|
|
||||||
it('should use default on invalid string', () => {
|
|
||||||
size.set('large');
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('width')).toBe('24');
|
|
||||||
expect(getSvgAttribute('height')).toBe('24');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('strokeWidth', () => {
|
|
||||||
it('should default to 2', () => {
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('stroke-width')).toBe('2');
|
|
||||||
});
|
|
||||||
it('should set stroke width', () => {
|
|
||||||
strokeWidth.set(1.41);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('stroke-width')).toBe('1.41');
|
|
||||||
});
|
|
||||||
it('should allow string stroke width', () => {
|
|
||||||
strokeWidth.set('1px');
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('stroke-width')).toBe('1');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('absoluteStrokeWidth', () => {
|
|
||||||
it('should not adjust stroke width', () => {
|
|
||||||
strokeWidth.set(2);
|
|
||||||
size.set(12);
|
|
||||||
absoluteStrokeWidth.set(false);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('stroke-width')).toBe('2');
|
|
||||||
});
|
|
||||||
it('should adjust stroke width', () => {
|
|
||||||
strokeWidth.set(2);
|
|
||||||
size.set(12);
|
|
||||||
absoluteStrokeWidth.set(true);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('stroke-width')).toBe('4');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('title', () => {
|
|
||||||
it('should set title if provided', () => {
|
|
||||||
title.set('Foobar');
|
|
||||||
fixture.detectChanges();
|
|
||||||
const titleEl = fixture.debugElement.query(By.css('title')).nativeElement;
|
|
||||||
expect(titleEl).toBeDefined();
|
|
||||||
expect(titleEl.textContent).toBe('Foobar');
|
|
||||||
});
|
|
||||||
it('should not set aria-hidden when title is set', () => {
|
|
||||||
title.set('Foobar');
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('aria-hidden')).toBeUndefined;
|
|
||||||
});
|
|
||||||
it('should set aria-hidden if no title is provided', () => {
|
|
||||||
title.set(undefined);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getSvgAttribute('aria-hidden')).toBeUndefined;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('content projection', () => {
|
|
||||||
it('should project content', () => {
|
|
||||||
const hostFixture = TestBed.createComponent(TestHostComponent);
|
|
||||||
hostFixture.componentRef.setInput('icon', testIcon);
|
|
||||||
hostFixture.detectChanges();
|
|
||||||
hostFixture.componentRef.setInput('icon', testIcon2);
|
|
||||||
hostFixture.detectChanges();
|
|
||||||
const rect = hostFixture.debugElement.query(By.css('rect')).nativeElement;
|
|
||||||
expect(rect).toBeInstanceOf(SVGElement);
|
|
||||||
expect(rect.outerHTML).toBe('<rect x="1" y="1" width="22" height="22"></rect>');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import { Component, computed, inject, input } from '@angular/core';
|
|
||||||
import { isLucideIconComponent, isLucideIconData, LucideIconInput } from './types';
|
|
||||||
import { LucideIconBase } from './lucide-icon-base';
|
|
||||||
import { LUCIDE_ICONS } from './lucide-icons';
|
|
||||||
import { LucideIconData } from './types';
|
|
||||||
import { toKebabCase } from './utils/to-kebab-case';
|
|
||||||
|
|
||||||
interface LucideResolvedIcon {
|
|
||||||
name?: string | null;
|
|
||||||
data: LucideIconData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'svg[lucideIcon]',
|
|
||||||
templateUrl: './lucide-icon.html',
|
|
||||||
standalone: true,
|
|
||||||
})
|
|
||||||
export class LucideIcon extends LucideIconBase {
|
|
||||||
protected readonly icons = inject(LUCIDE_ICONS);
|
|
||||||
readonly nameInput = input<string | null>(null, { alias: 'name' });
|
|
||||||
readonly iconInput = input.required<LucideIconInput | null>({
|
|
||||||
alias: 'lucideIcon',
|
|
||||||
});
|
|
||||||
readonly resolvedIcon = computed<LucideResolvedIcon | null>(() => {
|
|
||||||
return this.resolveIcon(this.nameInput(), this.iconInput());
|
|
||||||
});
|
|
||||||
override readonly name = computed<string | null>(() => {
|
|
||||||
return this.resolvedIcon()?.name ?? null;
|
|
||||||
});
|
|
||||||
override readonly icon = computed<LucideIconData | null>(() => {
|
|
||||||
return this.resolvedIcon()?.data ?? null;
|
|
||||||
});
|
|
||||||
|
|
||||||
protected resolveIcon(
|
|
||||||
name: string | null | undefined,
|
|
||||||
icon: LucideIconInput | null | undefined,
|
|
||||||
): LucideResolvedIcon | null {
|
|
||||||
if (isLucideIconData(icon)) {
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
data: icon,
|
|
||||||
};
|
|
||||||
} else if (isLucideIconComponent(icon)) {
|
|
||||||
return {
|
|
||||||
name: name ?? icon.iconName,
|
|
||||||
data: icon.iconData,
|
|
||||||
};
|
|
||||||
} else if (typeof icon === 'string') {
|
|
||||||
const name = toKebabCase(icon);
|
|
||||||
if (name in this.icons) {
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
data: this.icons[name],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
throw new Error(`Unable to resolve icon '${icon}'`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
import { LUCIDE_ICONS, provideLucideIcons } from './lucide-icons';
|
|
||||||
import { LucideIconData } from './types';
|
|
||||||
import { LucideActivity } from './icons/activity';
|
|
||||||
import { LucideCircle } from './icons/circle';
|
|
||||||
import { LucideSquareX } from './icons/square-x';
|
|
||||||
|
|
||||||
describe('Lucide icons', () => {
|
|
||||||
describe('LUCIDE_ICONS', () => {
|
|
||||||
it('should default to empty map', () => {
|
|
||||||
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('provideLucideIcons', () => {
|
|
||||||
const mockIcon: LucideIconData = [['polyline', { points: '1 1 22 22' }]];
|
|
||||||
const mockIcon2: LucideIconData = [['circle', { cx: 12, cy: 12, r: 8 }]];
|
|
||||||
it('should accept dictionary of icons', () => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
provideLucideIcons({
|
|
||||||
DemoIcon: mockIcon,
|
|
||||||
MockIcon: mockIcon2,
|
|
||||||
TestIcon: LucideActivity,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({
|
|
||||||
'demo-icon': mockIcon,
|
|
||||||
'mock-icon': mockIcon2,
|
|
||||||
[LucideActivity.iconName]: LucideActivity.iconData,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('should accept list of icon components', () => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [provideLucideIcons([LucideActivity, LucideSquareX, LucideCircle])],
|
|
||||||
});
|
|
||||||
expect(TestBed.inject(LUCIDE_ICONS)).toEqual({
|
|
||||||
[LucideActivity.iconName]: LucideActivity.iconData,
|
|
||||||
[LucideSquareX.iconName]: LucideSquareX.iconData,
|
|
||||||
[LucideCircle.iconName]: LucideCircle.iconData,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { InjectionToken, Provider } from '@angular/core';
|
|
||||||
import { LucideIconData, LucideIcons } from './types';
|
|
||||||
import { isLucideIconComponent, LucideIconComponentType } from './types';
|
|
||||||
import { toKebabCase } from './utils/to-kebab-case';
|
|
||||||
|
|
||||||
export const LUCIDE_ICONS = new InjectionToken<LucideIcons>('Lucide icons', {
|
|
||||||
factory: () => ({}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export function provideLucideIcons(
|
|
||||||
icons: Record<string, LucideIconData | LucideIconComponentType> | Array<LucideIconComponentType>,
|
|
||||||
): Provider {
|
|
||||||
if (Array.isArray(icons)) {
|
|
||||||
return {
|
|
||||||
provide: LUCIDE_ICONS,
|
|
||||||
useValue: icons.reduce((acc, icon) => {
|
|
||||||
acc[toKebabCase(icon.iconName)] = icon.iconData;
|
|
||||||
return acc;
|
|
||||||
}, {} as LucideIcons),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
provide: LUCIDE_ICONS,
|
|
||||||
useValue: Object.entries(icons).reduce((acc, [name, icon]) => {
|
|
||||||
if (isLucideIconComponent(icon)) {
|
|
||||||
acc[icon.iconName] = icon.iconData;
|
|
||||||
} else {
|
|
||||||
acc[toKebabCase(name)] = icon;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, {} as LucideIcons),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import * as icons from './icons/lucide-angular';
|
|
||||||
|
|
||||||
export * from './lucide-config';
|
|
||||||
export * from './lucide-icon';
|
|
||||||
export * from './lucide-icons';
|
|
||||||
export * from './types';
|
|
||||||
export { icons };
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { InputSignal, Signal, Type } from '@angular/core';
|
|
||||||
|
|
||||||
type HtmlAttributes = { [key: string]: string | number };
|
|
||||||
export type LucideIconNode = readonly [string, HtmlAttributes];
|
|
||||||
export type LucideIconData = readonly LucideIconNode[];
|
|
||||||
export type LucideIcons = { [key: string]: LucideIconData };
|
|
||||||
|
|
||||||
export interface LucideIconComponent {
|
|
||||||
name: Signal<Nullable<string>>;
|
|
||||||
icon: Signal<Nullable<LucideIconData>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type LucideIconComponentType = Type<LucideIconComponent> & {
|
|
||||||
iconName: string;
|
|
||||||
iconData: LucideIconData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function isLucideIconData(icon: unknown): icon is LucideIconData {
|
|
||||||
return Array.isArray(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isLucideIconComponent(icon: unknown): icon is LucideIconComponentType {
|
|
||||||
return (
|
|
||||||
icon instanceof Type &&
|
|
||||||
'iconData' in icon &&
|
|
||||||
Array.isArray(icon.iconData) &&
|
|
||||||
'iconName' in icon &&
|
|
||||||
typeof icon.iconName === 'string'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export type LucideIconInput = LucideIconComponentType | LucideIconData | string;
|
|
||||||
|
|
||||||
export type Nullable<T> = T | null | undefined;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export function formatFixed(number: number, decimals = 3): string {
|
|
||||||
return parseFloat(number.toFixed(decimals)).toString(10);
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export const toKebabCase = (name: string) =>
|
|
||||||
name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
|
||||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
|
||||||
{
|
|
||||||
"compileOnSave": false,
|
|
||||||
"compilerOptions": {
|
|
||||||
"paths": {
|
|
||||||
"@lucide/angular": [
|
|
||||||
"./dist"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"strict": true,
|
|
||||||
"noImplicitOverride": true,
|
|
||||||
"noPropertyAccessFromIndexSignature": true,
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"importHelpers": true,
|
|
||||||
"target": "ES2022",
|
|
||||||
"module": "preserve"
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"enableI18nLegacyMessageIdFormat": false,
|
|
||||||
"strictInjectionParameters": true,
|
|
||||||
"strictInputAccessModifiers": true,
|
|
||||||
"strictTemplates": true
|
|
||||||
},
|
|
||||||
"files": [],
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.lib.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.spec.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
|
||||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
|
||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "./out-tsc/lib",
|
|
||||||
"declaration": true,
|
|
||||||
"declarationMap": true,
|
|
||||||
"inlineSources": true,
|
|
||||||
"types": []
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src/**/*.ts"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"**/*.spec.ts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
|
||||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
|
||||||
{
|
|
||||||
"extends": "./tsconfig.lib.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"declarationMap": false
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"compilationMode": "partial"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
|
||||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
|
||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "./out-tsc/spec",
|
|
||||||
"types": [
|
|
||||||
"vitest/globals"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src/**/*.d.ts",
|
|
||||||
"src/**/*.spec.ts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lucide/angular",
|
"name": "lucide-angular",
|
||||||
"description": "A Lucide icon library package for Angular applications",
|
"description": "A Lucide icon library package for Angular applications.",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"author": "SMAH1",
|
"author": "SMAH1",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
@@ -38,19 +38,19 @@
|
|||||||
"version": "pnpm version --git-tag-version=false"
|
"version": "pnpm version --git-tag-version=false"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "~17.3.14",
|
"@angular-devkit/build-angular": "~13.3.11",
|
||||||
"@angular-eslint/builder": "~17.5.3",
|
"@angular-eslint/builder": "~13.0.0",
|
||||||
"@angular-eslint/eslint-plugin": "~17.5.3",
|
"@angular-eslint/eslint-plugin": "~13.0.0",
|
||||||
"@angular-eslint/eslint-plugin-template": "~17.5.3",
|
"@angular-eslint/eslint-plugin-template": "~13.0.0",
|
||||||
"@angular-eslint/schematics": "~17.5.3",
|
"@angular-eslint/schematics": "~13.0.0",
|
||||||
"@angular-eslint/template-parser": "~17.5.3",
|
"@angular-eslint/template-parser": "~13.0.0",
|
||||||
"@angular/cli": "~17.3.14",
|
"@angular/cli": "~13.3.11",
|
||||||
"@angular/common": "~17.3.12",
|
"@angular/common": "~13.3.0",
|
||||||
"@angular/compiler": "~17.3.12",
|
"@angular/compiler": "~13.3.0",
|
||||||
"@angular/compiler-cli": "~17.3.12",
|
"@angular/compiler-cli": "~13.3.0",
|
||||||
"@angular/core": "~17.3.12",
|
"@angular/core": "~13.3.0",
|
||||||
"@angular/platform-browser": "~17.3.12",
|
"@angular/platform-browser": "~13.3.0",
|
||||||
"@angular/platform-browser-dynamic": "~17.3.12",
|
"@angular/platform-browser-dynamic": "~13.3.0",
|
||||||
"@lucide/build-icons": "workspace:*",
|
"@lucide/build-icons": "workspace:*",
|
||||||
"@types/jasmine": "~3.10.0",
|
"@types/jasmine": "~3.10.0",
|
||||||
"@types/node": "^12.11.1",
|
"@types/node": "^12.11.1",
|
||||||
@@ -65,12 +65,12 @@
|
|||||||
"karma-coverage": "~2.1.0",
|
"karma-coverage": "~2.1.0",
|
||||||
"karma-jasmine": "~4.0.0",
|
"karma-jasmine": "~4.0.0",
|
||||||
"karma-jasmine-html-reporter": "~1.7.0",
|
"karma-jasmine-html-reporter": "~1.7.0",
|
||||||
"ng-packagr": "^17.3.0",
|
"ng-packagr": "^13.3.0",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.4",
|
||||||
"rxjs": "~6.5.3",
|
"rxjs": "~7.5.0",
|
||||||
"ts-node": "~10.9.1",
|
"ts-node": "~10.9.1",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"typescript": "~5.4.5",
|
"typescript": "~4.6.2",
|
||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -8,65 +8,26 @@ export default defineExportTemplate(async ({
|
|||||||
getSvg,
|
getSvg,
|
||||||
deprecated,
|
deprecated,
|
||||||
deprecationReason,
|
deprecationReason,
|
||||||
aliases = [],
|
|
||||||
toPascalCase,
|
|
||||||
}) => {
|
}) => {
|
||||||
const svgContents = await getSvg();
|
const svgContents = await getSvg();
|
||||||
const svgBase64 = base64SVG(svgContents);
|
const svgBase64 = base64SVG(svgContents);
|
||||||
const angularComponentName = `Lucide${componentName}`;
|
|
||||||
const selectors = [`svg[lucide${toPascalCase(iconName)}]`];
|
|
||||||
const aliasComponentNames: string[] = [];
|
|
||||||
for (const alias of aliases) {
|
|
||||||
const aliasName = typeof alias === 'string' ? alias : alias.name;
|
|
||||||
const aliasComponentName = `Lucide${toPascalCase(aliasName)}`;
|
|
||||||
const aliasSelector = `svg[lucide${toPascalCase(aliasName)}]`;
|
|
||||||
if (!selectors.includes(aliasSelector)) {
|
|
||||||
selectors.push(aliasSelector);
|
|
||||||
}
|
|
||||||
if (aliasComponentName !== angularComponentName && !aliasComponentNames.includes(aliasComponentName)) {
|
|
||||||
aliasComponentNames.push(aliasComponentName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return `\
|
return `\
|
||||||
import { LucideIconData } from './types';
|
import { LucideIconData } from './types';
|
||||||
import { LucideIcon } from '../lib/lucide-icon.component';
|
|
||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @component @name ${componentName}
|
* @component @name ${componentName}
|
||||||
* @description Lucide SVG icon component, renders SVG Element with children.
|
* @description Lucide SVG icon component, renders SVG Element with children.
|
||||||
*
|
*
|
||||||
* @preview  - https://lucide.dev/icons/${iconName}
|
* @preview  - https://lucide.dev/icons/${iconName}
|
||||||
* @see https://lucide.dev/guide/packages/lucide-angular - Documentation
|
* @see https://lucide.dev/guide/packages/lucide-vue-next - Documentation
|
||||||
*
|
*
|
||||||
* @param {Object} props - Lucide icons props and any valid SVG attribute
|
* @param {Object} props - Lucide icons props and any valid SVG attribute
|
||||||
|
* @returns {FunctionalComponent} Vue component
|
||||||
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
|
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
|
||||||
*/
|
*/
|
||||||
@Component({
|
const ${componentName}: LucideIconData = ${JSON.stringify(children)}; //eslint-disable-line no-shadow-restricted-names
|
||||||
selector: '${selectors.join(', ')}',
|
|
||||||
template: '',
|
|
||||||
standalone: true,
|
|
||||||
})
|
|
||||||
export class ${angularComponentName} extends LucideIcon {
|
|
||||||
static iconData: LucideIconData = ${JSON.stringify(children)};
|
|
||||||
static iconName = '${iconName}';
|
|
||||||
override get icon() {
|
|
||||||
return ${angularComponentName}.iconData;
|
|
||||||
}
|
|
||||||
override get name() {
|
|
||||||
return ${angularComponentName}.iconName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${aliasComponentNames.map(([aliasComponentName]) => {
|
export default ${componentName};
|
||||||
return `
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* @see ${angularComponentName}
|
|
||||||
*/
|
|
||||||
export const ${aliasComponentName} = ${angularComponentName};
|
|
||||||
`;
|
|
||||||
}).join(`\n\n`)}
|
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
|
|||||||
3
packages/lucide-angular/src/aliases/index.ts
Normal file
3
packages/lucide-angular/src/aliases/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './aliases';
|
||||||
|
export * from './prefixed';
|
||||||
|
export * from './suffixed';
|
||||||
8
packages/lucide-angular/src/icons/package.json
Normal file
8
packages/lucide-angular/src/icons/package.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"ngPackage": {
|
||||||
|
"dest": "dist",
|
||||||
|
"lib": {
|
||||||
|
"entryFile": "../public-api.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
packages/lucide-angular/src/lib/icons.provider.ts
Normal file
4
packages/lucide-angular/src/lib/icons.provider.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/** @deprecated Use the injection token LUCIDE_ICONS instead. Will be removed in v1.0. */
|
||||||
|
export class Icons {
|
||||||
|
constructor(private icons: object) {}
|
||||||
|
}
|
||||||
@@ -1,19 +1,23 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { formatFixed, LucideIcon } from './lucide-icon.component';
|
import { LucideAngularModule } from './lucide-angular.module';
|
||||||
|
import { formatFixed, LucideAngularComponent } from './lucide-angular.component';
|
||||||
import defaultAttributes from '../icons/constants/default-attributes';
|
import defaultAttributes from '../icons/constants/default-attributes';
|
||||||
import { LucideIconData } from '../icons/types';
|
import { LucideIcons } from '../icons/types';
|
||||||
|
|
||||||
describe('LucideAngularComponent', () => {
|
describe('LucideAngularComponent', () => {
|
||||||
let testHostComponent: TestHostComponent;
|
let testHostComponent: TestHostComponent;
|
||||||
let testHostFixture: ComponentFixture<TestHostComponent>;
|
let testHostFixture: ComponentFixture<TestHostComponent>;
|
||||||
const getSvgAttribute = (attr: string) =>
|
const getSvgAttribute = (attr: string) =>
|
||||||
testHostFixture.nativeElement.querySelector('svg').getAttribute(attr);
|
testHostFixture.nativeElement.querySelector('svg').getAttribute(attr);
|
||||||
const testIcon: LucideIconData = [['polyline', { points: '1 1 22 22' }]];
|
const testIcons: LucideIcons = {
|
||||||
|
Demo: [['polyline', { points: '1 1 22 22' }]],
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [LucideIcon, TestHostComponent],
|
declarations: [LucideAngularComponent, TestHostComponent],
|
||||||
imports: [],
|
imports: [LucideAngularModule.pick(testIcons)],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
testHostFixture = TestBed.createComponent(TestHostComponent);
|
testHostFixture = TestBed.createComponent(TestHostComponent);
|
||||||
testHostComponent = testHostFixture.componentInstance;
|
testHostComponent = testHostFixture.componentInstance;
|
||||||
@@ -59,7 +63,7 @@ describe('LucideAngularComponent', () => {
|
|||||||
testHostComponent.setAbsoluteStrokeWidth(true);
|
testHostComponent.setAbsoluteStrokeWidth(true);
|
||||||
testHostFixture.detectChanges();
|
testHostFixture.detectChanges();
|
||||||
expect(getSvgAttribute('stroke-width')).toBe(
|
expect(getSvgAttribute('stroke-width')).toBe(
|
||||||
formatFixed(strokeWidth / (size / defaultAttributes.height))
|
formatFixed(strokeWidth / (size / defaultAttributes.height)),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -67,7 +71,6 @@ describe('LucideAngularComponent', () => {
|
|||||||
selector: 'lucide-spec-host-component',
|
selector: 'lucide-spec-host-component',
|
||||||
template: ` <i-lucide
|
template: ` <i-lucide
|
||||||
name="demo"
|
name="demo"
|
||||||
[img]="testIcon"
|
|
||||||
class="my-icon"
|
class="my-icon"
|
||||||
[color]="color"
|
[color]="color"
|
||||||
[size]="size"
|
[size]="size"
|
||||||
@@ -80,7 +83,6 @@ describe('LucideAngularComponent', () => {
|
|||||||
size?: number;
|
size?: number;
|
||||||
strokeWidth?: number;
|
strokeWidth?: number;
|
||||||
absoluteStrokeWidth = true;
|
absoluteStrokeWidth = true;
|
||||||
readonly testIcon = testIcon;
|
|
||||||
|
|
||||||
setColor(color: string): void {
|
setColor(color: string): void {
|
||||||
this.color = color;
|
this.color = color;
|
||||||
@@ -5,13 +5,12 @@ import {
|
|||||||
Inject,
|
Inject,
|
||||||
Input,
|
Input,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
OnInit,
|
|
||||||
Renderer2,
|
Renderer2,
|
||||||
SimpleChange,
|
SimpleChange,
|
||||||
Type,
|
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { LucideIconData } from '../icons/types';
|
import { LucideIconData } from '../icons/types';
|
||||||
import defaultAttributes from '../icons/constants/default-attributes';
|
import defaultAttributes from '../icons/constants/default-attributes';
|
||||||
|
import { LUCIDE_ICONS, LucideIconProviderInterface } from './lucide-icon.provider';
|
||||||
import { LucideIconConfig } from './lucide-icon.config';
|
import { LucideIconConfig } from './lucide-icon.config';
|
||||||
|
|
||||||
interface TypedChange<T> extends SimpleChange {
|
interface TypedChange<T> extends SimpleChange {
|
||||||
@@ -23,7 +22,7 @@ type SvgAttributes = { [key: string]: string | number };
|
|||||||
|
|
||||||
type LucideAngularComponentChanges = {
|
type LucideAngularComponentChanges = {
|
||||||
name?: TypedChange<string | LucideIconData>;
|
name?: TypedChange<string | LucideIconData>;
|
||||||
icon?: TypedChange<LucideIconData | undefined>;
|
img?: TypedChange<LucideIconData | undefined>;
|
||||||
color?: TypedChange<string>;
|
color?: TypedChange<string>;
|
||||||
size?: TypedChange<number>;
|
size?: TypedChange<number>;
|
||||||
strokeWidth?: TypedChange<number>;
|
strokeWidth?: TypedChange<number>;
|
||||||
@@ -35,50 +34,24 @@ export function formatFixed(number: number, decimals = 3): string {
|
|||||||
return parseFloat(number.toFixed(decimals)).toString(10);
|
return parseFloat(number.toFixed(decimals)).toString(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LucideIconComponentType = Type<LucideIcon> & { iconData: LucideIconData; name: string };
|
|
||||||
|
|
||||||
function isLucideIconComponent(icon: unknown): icon is LucideIconComponentType {
|
|
||||||
return (
|
|
||||||
icon instanceof Type &&
|
|
||||||
'iconData' in icon &&
|
|
||||||
Array.isArray(icon.iconData) &&
|
|
||||||
'iconName' in icon &&
|
|
||||||
typeof icon.iconName === 'string'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// eslint-disable-next-line @angular-eslint/component-selector
|
selector: 'lucide-angular, lucide-icon, i-lucide, span-lucide',
|
||||||
selector: 'svg[lucideIcon]',
|
|
||||||
template: '<ng-content></ng-content>',
|
template: '<ng-content></ng-content>',
|
||||||
standalone: true,
|
|
||||||
})
|
})
|
||||||
// eslint-disable-next-line @angular-eslint/component-class-suffix
|
export class LucideAngularComponent implements OnChanges {
|
||||||
export class LucideIcon implements OnInit, OnChanges {
|
|
||||||
@Input() class?: string;
|
@Input() class?: string;
|
||||||
_name?: string;
|
@Input() name?: string | LucideIconData;
|
||||||
@Input() set name(name: string | undefined) {
|
@Input() img?: LucideIconData;
|
||||||
this._name = name;
|
|
||||||
}
|
|
||||||
get name() {
|
|
||||||
return this._name;
|
|
||||||
}
|
|
||||||
_icon?: LucideIconData | LucideIconComponentType | null;
|
|
||||||
@Input('lucideIcon') set icon(icon: LucideIconData | LucideIconComponentType | null | undefined) {
|
|
||||||
this._icon = icon;
|
|
||||||
}
|
|
||||||
get icon() {
|
|
||||||
return this._icon;
|
|
||||||
}
|
|
||||||
@Input() color?: string;
|
@Input() color?: string;
|
||||||
@Input() absoluteStrokeWidth = false;
|
@Input() absoluteStrokeWidth = false;
|
||||||
defaultSize: number;
|
defaultSize: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(ElementRef) protected elem: ElementRef,
|
@Inject(ElementRef) private elem: ElementRef,
|
||||||
@Inject(Renderer2) protected renderer: Renderer2,
|
@Inject(Renderer2) private renderer: Renderer2,
|
||||||
@Inject(ChangeDetectorRef) protected changeDetector: ChangeDetectorRef,
|
@Inject(ChangeDetectorRef) private changeDetector: ChangeDetectorRef,
|
||||||
@Inject(LucideIconConfig) protected iconConfig: LucideIconConfig
|
@Inject(LUCIDE_ICONS) private iconProviders: LucideIconProviderInterface[],
|
||||||
|
@Inject(LucideIconConfig) private iconConfig: LucideIconConfig,
|
||||||
) {
|
) {
|
||||||
this.defaultSize = defaultAttributes.height;
|
this.defaultSize = defaultAttributes.height;
|
||||||
}
|
}
|
||||||
@@ -111,37 +84,40 @@ export class LucideIcon implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.buildIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges(changes: LucideAngularComponentChanges): void {
|
ngOnChanges(changes: LucideAngularComponentChanges): void {
|
||||||
if (
|
if (
|
||||||
changes.name ||
|
changes.name ||
|
||||||
changes.icon ||
|
changes.img ||
|
||||||
changes.color ||
|
changes.color ||
|
||||||
changes.size ||
|
changes.size ||
|
||||||
changes.absoluteStrokeWidth ||
|
changes.absoluteStrokeWidth ||
|
||||||
changes.strokeWidth ||
|
changes.strokeWidth ||
|
||||||
changes.class
|
changes.class
|
||||||
) {
|
) {
|
||||||
this.buildIcon();
|
this.color = this.color ?? this.iconConfig.color;
|
||||||
|
this.size = this.parseNumber(this.size ?? this.iconConfig.size);
|
||||||
|
this.strokeWidth = this.parseNumber(this.strokeWidth ?? this.iconConfig.strokeWidth);
|
||||||
|
this.absoluteStrokeWidth = this.absoluteStrokeWidth ?? this.iconConfig.absoluteStrokeWidth;
|
||||||
|
const nameOrIcon = this.img ?? this.name;
|
||||||
|
if (typeof nameOrIcon === 'string') {
|
||||||
|
const icoOfName = this.getIcon(this.toPascalCase(nameOrIcon));
|
||||||
|
if (icoOfName) {
|
||||||
|
this.replaceElement(icoOfName);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`The "${nameOrIcon}" icon has not been provided by any available icon providers.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (Array.isArray(nameOrIcon)) {
|
||||||
|
this.replaceElement(nameOrIcon);
|
||||||
|
} else {
|
||||||
|
throw new Error(`No icon name or image has been provided.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changeDetector.markForCheck();
|
this.changeDetector.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
buildIcon(): void {
|
|
||||||
this.color = this.color ?? this.iconConfig.color;
|
|
||||||
this.size = this.parseNumber(this.size ?? this.iconConfig.size);
|
|
||||||
this.strokeWidth = this.parseNumber(this.strokeWidth ?? this.iconConfig.strokeWidth);
|
|
||||||
this.absoluteStrokeWidth = this.absoluteStrokeWidth ?? this.iconConfig.absoluteStrokeWidth;
|
|
||||||
console.log('Hello, my name is ', this.name, ' my icon is ', this.icon);
|
|
||||||
if (this.icon) {
|
|
||||||
this.replaceElement(isLucideIconComponent(this.icon) ? this.icon.iconData : this.icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceElement(img: LucideIconData): void {
|
replaceElement(img: LucideIconData): void {
|
||||||
const attributes = {
|
const attributes = {
|
||||||
...defaultAttributes,
|
...defaultAttributes,
|
||||||
@@ -152,10 +128,7 @@ export class LucideIcon implements OnInit, OnChanges {
|
|||||||
? formatFixed(this.strokeWidth / (this.size / this.defaultSize))
|
? formatFixed(this.strokeWidth / (this.size / this.defaultSize))
|
||||||
: this.strokeWidth.toString(10),
|
: this.strokeWidth.toString(10),
|
||||||
};
|
};
|
||||||
const icoElement = this.elem.nativeElement;
|
const icoElement = this.createElement(['svg', attributes, img]);
|
||||||
for (const [name, value] of Object.entries(attributes)) {
|
|
||||||
icoElement.setAttribute(name, value);
|
|
||||||
}
|
|
||||||
icoElement.classList.add('lucide');
|
icoElement.classList.add('lucide');
|
||||||
if (typeof this.name === 'string') {
|
if (typeof this.name === 'string') {
|
||||||
icoElement.classList.add(`lucide-${this.name.replace('_', '-')}`);
|
icoElement.classList.add(`lucide-${this.name.replace('_', '-')}`);
|
||||||
@@ -165,19 +138,24 @@ export class LucideIcon implements OnInit, OnChanges {
|
|||||||
...this.class
|
...this.class
|
||||||
.split(/ /)
|
.split(/ /)
|
||||||
.map((a) => a.trim())
|
.map((a) => a.trim())
|
||||||
.filter((a) => a.length > 0)
|
.filter((a) => a.length > 0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const child of icoElement.children) {
|
const childElements = this.elem.nativeElement.childNodes;
|
||||||
|
for (const child of childElements) {
|
||||||
this.renderer.removeChild(this.elem.nativeElement, child);
|
this.renderer.removeChild(this.elem.nativeElement, child);
|
||||||
}
|
}
|
||||||
for (const node of img) {
|
this.renderer.appendChild(this.elem.nativeElement, icoElement);
|
||||||
const childElement = this.createElement(node);
|
|
||||||
this.renderer.appendChild(icoElement, childElement);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected parseNumber(value: string | number): number {
|
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') {
|
if (typeof value === 'string') {
|
||||||
const parsedValue = parseInt(value, 10);
|
const parsedValue = parseInt(value, 10);
|
||||||
if (isNaN(parsedValue)) {
|
if (isNaN(parsedValue)) {
|
||||||
@@ -188,10 +166,21 @@ export class LucideIcon implements OnInit, OnChanges {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createElement([tag, attrs, children = []]: readonly [
|
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,
|
string,
|
||||||
SvgAttributes,
|
SvgAttributes,
|
||||||
LucideIconData?
|
LucideIconData?,
|
||||||
]) {
|
]) {
|
||||||
const element = this.renderer.createElement(tag, 'http://www.w3.org/2000/svg');
|
const element = this.renderer.createElement(tag, 'http://www.w3.org/2000/svg');
|
||||||
|
|
||||||
31
packages/lucide-angular/src/lib/lucide-angular.module.ts
Normal file
31
packages/lucide-angular/src/lib/lucide-angular.module.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { ModuleWithProviders, NgModule, Optional } from '@angular/core';
|
||||||
|
import { LucideAngularComponent } from './lucide-angular.component';
|
||||||
|
import { LucideIcons } from '../icons/types';
|
||||||
|
import { LUCIDE_ICONS, LucideIconProvider } from './lucide-icon.provider';
|
||||||
|
import { Icons } from './icons.provider';
|
||||||
|
|
||||||
|
const legacyIconProviderFactory = (icons?: LucideIcons) => {
|
||||||
|
return new LucideIconProvider(icons ?? {});
|
||||||
|
};
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [LucideAngularComponent],
|
||||||
|
imports: [],
|
||||||
|
exports: [LucideAngularComponent],
|
||||||
|
})
|
||||||
|
export class LucideAngularModule {
|
||||||
|
static pick(icons: LucideIcons): ModuleWithProviders<LucideAngularModule> {
|
||||||
|
return {
|
||||||
|
ngModule: LucideAngularModule,
|
||||||
|
providers: [
|
||||||
|
{ provide: LUCIDE_ICONS, multi: true, useValue: new LucideIconProvider(icons) },
|
||||||
|
{
|
||||||
|
provide: LUCIDE_ICONS,
|
||||||
|
multi: true,
|
||||||
|
useFactory: legacyIconProviderFactory,
|
||||||
|
deps: [[new Optional(), Icons]],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
24
packages/lucide-angular/src/lib/lucide-icon.provider.ts
Normal file
24
packages/lucide-angular/src/lib/lucide-icon.provider.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { LucideIconData, LucideIcons } from '../icons/types';
|
||||||
|
import { InjectionToken } from '@angular/core';
|
||||||
|
|
||||||
|
export interface LucideIconProviderInterface {
|
||||||
|
hasIcon(name: string): boolean;
|
||||||
|
|
||||||
|
getIcon(name: string): LucideIconData | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LUCIDE_ICONS = new InjectionToken<LucideIconProviderInterface>('LucideIcons', {
|
||||||
|
factory: () => new LucideIconProvider({}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export class LucideIconProvider implements LucideIconProviderInterface {
|
||||||
|
constructor(private icons: LucideIcons) {}
|
||||||
|
|
||||||
|
getIcon(name: string): LucideIconData | null {
|
||||||
|
return this.hasIcon(name) ? this.icons[name] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasIcon(name: string): boolean {
|
||||||
|
return typeof this.icons === 'object' && name in this.icons;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
import * as icons from './icons/lucide-icons';
|
import * as icons from './icons/lucide-icons';
|
||||||
|
|
||||||
export * from './lib/lucide-icon.component';
|
export * from './lib/lucide-angular.component';
|
||||||
|
export * from './lib/lucide-angular.module';
|
||||||
export * from './lib/lucide-icon.config';
|
export * from './lib/lucide-icon.config';
|
||||||
|
export * from './lib/lucide-icon.provider';
|
||||||
export * from './icons/lucide-icons';
|
export * from './icons/lucide-icons';
|
||||||
export * from './icons/types';
|
export * from './icons/types';
|
||||||
|
export * from './aliases';
|
||||||
export { icons };
|
export { icons };
|
||||||
|
|||||||
@@ -24,11 +24,23 @@
|
|||||||
"author": "Eric Fennis",
|
"author": "Eric Fennis",
|
||||||
"amdName": "lucide-react-native",
|
"amdName": "lucide-react-native",
|
||||||
"main": "dist/cjs/lucide-react-native.js",
|
"main": "dist/cjs/lucide-react-native.js",
|
||||||
"main:umd": "dist/umd/lucide-react-native.js",
|
|
||||||
"module": "dist/esm/lucide-react-native.js",
|
"module": "dist/esm/lucide-react-native.js",
|
||||||
"unpkg": "dist/umd/lucide-react-native.min.js",
|
|
||||||
"typings": "dist/lucide-react-native.d.ts",
|
"typings": "dist/lucide-react-native.d.ts",
|
||||||
"react-native": "dist/esm/lucide-react-native.js",
|
"react-native": "dist/esm/lucide-react-native.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/lucide-react-native.d.ts",
|
||||||
|
"import": "./dist/esm/lucide-react-native.js",
|
||||||
|
"browser": "./dist/esm/lucide-react-native.js",
|
||||||
|
"require": "./dist/cjs/lucide-react-native.js"
|
||||||
|
},
|
||||||
|
"./icons": {
|
||||||
|
"types": "./dist/icons.d.ts",
|
||||||
|
"import": "./dist/esm/icons/index.js",
|
||||||
|
"browser": "./dist/esm/icons/index.js",
|
||||||
|
"require": "./dist/cjs/icons/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import pkg from './package.json' with { type: 'json' };
|
|||||||
const packageName = 'LucideReact';
|
const packageName = 'LucideReact';
|
||||||
const outputFileName = 'lucide-react-native';
|
const outputFileName = 'lucide-react-native';
|
||||||
const outputDir = 'dist';
|
const outputDir = 'dist';
|
||||||
const inputs = ['src/lucide-react-native.ts'];
|
const inputs = ['src/lucide-react-native.ts', 'src/icons/index.ts'];
|
||||||
const bundles = [
|
const bundles = [
|
||||||
{
|
{
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
@@ -60,6 +60,16 @@ export default [
|
|||||||
],
|
],
|
||||||
plugins: [dts()],
|
plugins: [dts()],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: inputs[1],
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
file: `dist/icons.d.ts`,
|
||||||
|
format: 'es',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
plugins: [dts()],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
input: `src/${outputFileName}.suffixed.ts`,
|
input: `src/${outputFileName}.suffixed.ts`,
|
||||||
output: [
|
output: [
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
|
|||||||
absoluteStrokeWidth,
|
absoluteStrokeWidth,
|
||||||
children,
|
children,
|
||||||
iconNode,
|
iconNode,
|
||||||
|
className,
|
||||||
...rest
|
...rest
|
||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
@@ -46,6 +47,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
|
|||||||
{
|
{
|
||||||
ref,
|
ref,
|
||||||
...defaultAttributes,
|
...defaultAttributes,
|
||||||
|
className,
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
...customAttrs,
|
...customAttrs,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from './icons';
|
export * from './icons';
|
||||||
export * as icons from './icons';
|
|
||||||
export * from './aliases/prefixed';
|
export * from './aliases/prefixed';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from './icons';
|
export * from './icons';
|
||||||
export * as icons from './icons';
|
|
||||||
export * from './aliases/suffixed';
|
export * from './aliases/suffixed';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from './icons';
|
export * from './icons';
|
||||||
export * as icons from './icons';
|
|
||||||
export * from './aliases';
|
export * from './aliases';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|
||||||
|
|||||||
11106
pnpm-lock.yaml
generated
11106
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
1914
tools/build-font/codepoints.json
Normal file
1914
tools/build-font/codepoints.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,149 +0,0 @@
|
|||||||
import { readJson } from 'fs-extra/esm';
|
|
||||||
import svgtofont from 'svgtofont';
|
|
||||||
import getArgumentOptions from 'minimist';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
const fontName = 'lucide';
|
|
||||||
const classNamePrefix = 'icon';
|
|
||||||
const startUnicode = 57400;
|
|
||||||
|
|
||||||
const inputDir = path.join(process.cwd(), '../../', 'outlined');
|
|
||||||
const cliArguments = getArgumentOptions(process.argv.slice(2));
|
|
||||||
const { outputDir = 'lucide-font' } = cliArguments;
|
|
||||||
const targetDir = path.join(process.cwd(), '../../', outputDir);
|
|
||||||
const releaseMetaDataDir = path.join(process.cwd(), '../../', 'docs/.vitepress/data');
|
|
||||||
const releaseMetaDataPath = path.resolve(releaseMetaDataDir, 'releaseMetaData.json');
|
|
||||||
|
|
||||||
const releaseMetaData = convertReleaseMetaData(await getReleaseMetaData());
|
|
||||||
|
|
||||||
async function getReleaseMetaData() {
|
|
||||||
let releaseMetaData = {};
|
|
||||||
try {
|
|
||||||
releaseMetaData = await readJson(releaseMetaDataPath);
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error('Execution stopped because no release information was found.');
|
|
||||||
}
|
|
||||||
return releaseMetaData;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Releases = Record<string, ReleaseMetaData>;
|
|
||||||
|
|
||||||
type ReleaseMetaData = {
|
|
||||||
createdRelease: {
|
|
||||||
version: string;
|
|
||||||
date: string;
|
|
||||||
};
|
|
||||||
changedRelease: {
|
|
||||||
version: string;
|
|
||||||
date: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type ReleaseMetaDataWithName = ReleaseMetaData & {
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
function convertReleaseMetaData(releases: Releases) {
|
|
||||||
return Object.entries(releases)
|
|
||||||
.map(([key, data]) => ({
|
|
||||||
...data,
|
|
||||||
name: key,
|
|
||||||
}))
|
|
||||||
.sort((a, b) => sortMultiple(a, b, [sortByCreatedReleaseDate, sortByName]))
|
|
||||||
.map((value, index) => ({ ...value, index }))
|
|
||||||
.map((value, index) => ({
|
|
||||||
...value,
|
|
||||||
unicode: index + startUnicode,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
type CollatorFunction = (a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) => number;
|
|
||||||
|
|
||||||
function sortMultiple(
|
|
||||||
a: ReleaseMetaDataWithName,
|
|
||||||
b: ReleaseMetaDataWithName,
|
|
||||||
collators: CollatorFunction[] = [],
|
|
||||||
) {
|
|
||||||
const comparison = collators?.shift?.()?.(a, b) ?? 0;
|
|
||||||
if (comparison === 0 && collators.length > 0) return sortMultiple(a, b, collators);
|
|
||||||
return comparison;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortByCreatedReleaseDate(a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) {
|
|
||||||
const [dateA, dateB] = [a, b].map((value) => new Date(value.createdRelease.date).valueOf());
|
|
||||||
return Number(dateA > dateB) - Number(dateA < dateB);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortByName(a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) {
|
|
||||||
return new Intl.Collator('en-US').compare(a.name, b.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIconUnicode(name: string): [string, number] {
|
|
||||||
const { unicode } = releaseMetaData.find(({ name: iconName }) => iconName === name) ?? {
|
|
||||||
unicode: startUnicode,
|
|
||||||
};
|
|
||||||
return [String.fromCharCode(unicode), startUnicode];
|
|
||||||
}
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
console.time('Font generation');
|
|
||||||
try {
|
|
||||||
await svgtofont({
|
|
||||||
src: path.resolve(process.cwd(), inputDir),
|
|
||||||
dist: path.resolve(process.cwd(), targetDir),
|
|
||||||
// styleTemplates: path.resolve(process.cwd(), 'styles'), // Add different templates if needed
|
|
||||||
fontName,
|
|
||||||
classNamePrefix,
|
|
||||||
css: {
|
|
||||||
fontSize: 'inherit',
|
|
||||||
},
|
|
||||||
emptyDist: true,
|
|
||||||
useCSSVars: false,
|
|
||||||
outSVGReact: false,
|
|
||||||
outSVGPath: false,
|
|
||||||
svgicons2svgfont: {
|
|
||||||
fontHeight: 1000, // At least 1000 is recommended
|
|
||||||
normalize: false,
|
|
||||||
},
|
|
||||||
generateInfoData: true,
|
|
||||||
website: {
|
|
||||||
title: 'Lucide',
|
|
||||||
logo: undefined,
|
|
||||||
meta: {
|
|
||||||
description: 'Lucide icons as TTF/EOT/WOFF/WOFF2/SVG.',
|
|
||||||
keywords: 'Lucide,TTF,EOT,WOFF,WOFF2,SVG',
|
|
||||||
},
|
|
||||||
corners: {
|
|
||||||
url: 'https://github.com/lucide-icons/lucide',
|
|
||||||
width: 62, // default: 60
|
|
||||||
height: 62, // default: 60
|
|
||||||
bgColor: '#dc3545', // default: '#151513'
|
|
||||||
},
|
|
||||||
links: [
|
|
||||||
{
|
|
||||||
title: 'GitHub',
|
|
||||||
url: 'https://github.com/lucide-icons/lucide',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Feedback',
|
|
||||||
url: 'https://github.com/lucide-icons/lucide/issues',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Font Class',
|
|
||||||
url: 'index.html',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Unicode',
|
|
||||||
url: 'unicode.html',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
getIconUnicode,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
console.timeEnd('Font generation');
|
|
||||||
}
|
|
||||||
|
|
||||||
init();
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
"main": "main.ts",
|
"main": "main.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./main.ts"
|
"start": "node ./src/main.ts"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
@@ -14,9 +14,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"minimist": "^1.2.8",
|
"minimist": "^1.2.8",
|
||||||
|
"oslllo-svg-fixer": "^5.0.0",
|
||||||
"svgtofont": "^6.5.0"
|
"svgtofont": "^6.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@lucide/helpers": "workspace:*",
|
||||||
"@types/fs-extra": "^11.0.4",
|
"@types/fs-extra": "^11.0.4",
|
||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
"@types/node": "^22"
|
"@types/node": "^22"
|
||||||
|
|||||||
60
tools/build-font/src/allocateCodepoints.ts
Normal file
60
tools/build-font/src/allocateCodepoints.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { type IconAliases } from "@lucide/helpers";
|
||||||
|
import path from "path";
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import { cwd } from "process";
|
||||||
|
|
||||||
|
export type CodePoints = Record<string, number>;
|
||||||
|
|
||||||
|
async function getLatestCodePoints(): Promise<CodePoints> {
|
||||||
|
// This is for the first release where no codepoints.json exists yet
|
||||||
|
const codepointsContents = await fs.readFile(path.join(cwd(), 'codepoints.json'), 'utf-8')
|
||||||
|
|
||||||
|
return JSON.parse(codepointsContents) as CodePoints
|
||||||
|
|
||||||
|
// Next releases will use the codepoints.json from latest release in lucide-static.
|
||||||
|
// const codepointsContents = await fetch('https://unpkg.com/lucide-static@latest/font/codepoints.json')
|
||||||
|
// return codepointsContents.json() as Promise<CodePoints>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AllocateCodePointsOptions {
|
||||||
|
saveCodePoints?: boolean;
|
||||||
|
iconsWithAliases: IconAliases
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function allocateCodePoints({
|
||||||
|
saveCodePoints = false,
|
||||||
|
iconsWithAliases
|
||||||
|
}: AllocateCodePointsOptions): Promise<CodePoints> {
|
||||||
|
const baseCodePoints = await getLatestCodePoints()
|
||||||
|
|
||||||
|
const endCodePoint = Math.max(...Object.values(baseCodePoints))
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
iconsWithAliases.map(async ([iconName, aliases]) => {
|
||||||
|
if(!baseCodePoints[iconName]) {
|
||||||
|
console.log('Code point not found creating new one for', iconName);
|
||||||
|
baseCodePoints[iconName] = endCodePoint + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
aliases.forEach((alias, index) => {
|
||||||
|
if (baseCodePoints[alias]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Code point not found creating new one for');
|
||||||
|
|
||||||
|
baseCodePoints[alias] = endCodePoint + index + 1;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
if (saveCodePoints) {
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(cwd(), 'codepoints.json'),
|
||||||
|
JSON.stringify(baseCodePoints, null, 2),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseCodePoints;
|
||||||
|
}
|
||||||
86
tools/build-font/src/buildFont.ts
Normal file
86
tools/build-font/src/buildFont.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import svgtofont from 'svgtofont';
|
||||||
|
import { type CodePoints } from './allocateCodepoints.ts';
|
||||||
|
|
||||||
|
interface BuildFontOptions {
|
||||||
|
inputDir: string;
|
||||||
|
targetDir: string;
|
||||||
|
fontName: string;
|
||||||
|
classNamePrefix: string;
|
||||||
|
codePoints: CodePoints
|
||||||
|
startUnicode: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buildFont({
|
||||||
|
inputDir,
|
||||||
|
targetDir,
|
||||||
|
fontName,
|
||||||
|
classNamePrefix,
|
||||||
|
codePoints,
|
||||||
|
startUnicode
|
||||||
|
}: BuildFontOptions) {
|
||||||
|
console.time('Font generation');
|
||||||
|
try {
|
||||||
|
await svgtofont({
|
||||||
|
src: inputDir,
|
||||||
|
dist: targetDir,
|
||||||
|
fontName,
|
||||||
|
classNamePrefix,
|
||||||
|
css: {
|
||||||
|
fontSize: 'inherit',
|
||||||
|
},
|
||||||
|
emptyDist: true,
|
||||||
|
useCSSVars: false,
|
||||||
|
outSVGReact: false,
|
||||||
|
outSVGPath: false,
|
||||||
|
addLigatures: true,
|
||||||
|
svgicons2svgfont: {
|
||||||
|
fontHeight: 1000, // At least 1000 is recommended
|
||||||
|
normalize: false,
|
||||||
|
},
|
||||||
|
generateInfoData: true,
|
||||||
|
website: {
|
||||||
|
title: 'Lucide',
|
||||||
|
logo: undefined,
|
||||||
|
meta: {
|
||||||
|
description: 'Lucide icons as TTF/EOT/WOFF/WOFF2/SVG.',
|
||||||
|
keywords: 'Lucide,TTF,EOT,WOFF,WOFF2,SVG',
|
||||||
|
},
|
||||||
|
corners: {
|
||||||
|
url: 'https://github.com/lucide-icons/lucide',
|
||||||
|
width: 62, // default: 60
|
||||||
|
height: 62, // default: 60
|
||||||
|
bgColor: '#dc3545', // default: '#151513'
|
||||||
|
},
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
title: 'GitHub',
|
||||||
|
url: 'https://github.com/lucide-icons/lucide',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Feedback',
|
||||||
|
url: 'https://github.com/lucide-icons/lucide/issues',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Font Class',
|
||||||
|
url: 'index.html',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Unicode',
|
||||||
|
url: 'unicode.html',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
getIconUnicode: (name: string) => {
|
||||||
|
if (!codePoints[name]) {
|
||||||
|
throw new Error(`No codepoint found for icon: ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unicode = codePoints[name];
|
||||||
|
return [String.fromCharCode(unicode), startUnicode];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
console.timeEnd('Font generation');
|
||||||
|
}
|
||||||
15
tools/build-font/src/helpers.ts
Normal file
15
tools/build-font/src/helpers.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { type IconAliases } from "@lucide/helpers";
|
||||||
|
import { type CodePoints } from "./allocateCodepoints.ts";
|
||||||
|
|
||||||
|
export function hasMissingCodePoints(iconsWithAliases: IconAliases, codePoints: CodePoints): boolean {
|
||||||
|
return iconsWithAliases.map(([iconName, aliases]) => ([iconName, ...aliases]))
|
||||||
|
.flat()
|
||||||
|
.some(name => {
|
||||||
|
if (!codePoints?.[name]) {
|
||||||
|
console.log(`Missing code point for icon/alias: ${name}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
52
tools/build-font/src/main.ts
Normal file
52
tools/build-font/src/main.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import getArgumentOptions from 'minimist';
|
||||||
|
import path from 'path';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
|
import { getAllIconAliases } from '@lucide/helpers';
|
||||||
|
import { outlineSVG } from './outlineSVGs.ts';
|
||||||
|
import { allocateCodePoints } from './allocateCodepoints.ts';
|
||||||
|
import { buildFont } from './buildFont.ts';
|
||||||
|
import { hasMissingCodePoints } from './helpers.ts';
|
||||||
|
|
||||||
|
const fontName = 'lucide';
|
||||||
|
const classNamePrefix = 'icon';
|
||||||
|
const startUnicode = 57400;
|
||||||
|
const outputDir = 'lucide-font';
|
||||||
|
|
||||||
|
const {
|
||||||
|
saveCodePoints = false,
|
||||||
|
} = getArgumentOptions(process.argv.slice(2)) ?? {}
|
||||||
|
|
||||||
|
const repoRoot = path.join(process.cwd(), '../../')
|
||||||
|
const iconsDir = path.join(repoRoot, 'icons');
|
||||||
|
const outlinedDir = path.join(repoRoot, 'outlined');
|
||||||
|
const targetDir = path.join(repoRoot, outputDir);
|
||||||
|
|
||||||
|
const iconsWithAliases = await getAllIconAliases(iconsDir)
|
||||||
|
|
||||||
|
await outlineSVG({
|
||||||
|
iconsDir,
|
||||||
|
outlinedDir,
|
||||||
|
iconsWithAliases
|
||||||
|
});
|
||||||
|
|
||||||
|
const codePoints = await allocateCodePoints({
|
||||||
|
saveCodePoints,
|
||||||
|
iconsWithAliases
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (hasMissingCodePoints(iconsWithAliases, codePoints)) {
|
||||||
|
throw new Error('Some icons or aliases are missing code points. See log for details.');
|
||||||
|
}
|
||||||
|
|
||||||
|
await buildFont({
|
||||||
|
inputDir: outlinedDir,
|
||||||
|
targetDir,
|
||||||
|
fontName,
|
||||||
|
classNamePrefix,
|
||||||
|
codePoints,
|
||||||
|
startUnicode,
|
||||||
|
});
|
||||||
|
|
||||||
|
await fs.copyFile(path.join(process.cwd(), 'codepoints.json'), path.join(targetDir, 'codepoints.json'));
|
||||||
49
tools/build-font/src/outlineSVGs.ts
Normal file
49
tools/build-font/src/outlineSVGs.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import SVGFixer from 'oslllo-svg-fixer';
|
||||||
|
import { getAllIconAliases, type IconAliases } from '@lucide/helpers';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
interface OutlineSVGOptions {
|
||||||
|
iconsDir: string;
|
||||||
|
outlinedDir: string;
|
||||||
|
iconsWithAliases: IconAliases
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function outlineSVG({
|
||||||
|
iconsDir,
|
||||||
|
outlinedDir,
|
||||||
|
iconsWithAliases
|
||||||
|
}: OutlineSVGOptions) {
|
||||||
|
console.time('icon outliner');
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
await fs.mkdir(outlinedDir);
|
||||||
|
} catch (error) { } // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
await SVGFixer(iconsDir, outlinedDir, {
|
||||||
|
showProgressBar: true,
|
||||||
|
traceResolution: 800,
|
||||||
|
}).fix();
|
||||||
|
|
||||||
|
console.log('Duplicate icons with aliases..');
|
||||||
|
|
||||||
|
await Promise.all(iconsWithAliases.map(async ([iconName, aliases]) => {
|
||||||
|
const sourcePath = path.join(outlinedDir, `${iconName}.svg`);
|
||||||
|
|
||||||
|
await Promise.all(aliases.map(async (aliasName) => {
|
||||||
|
const destinationPath = path.join(outlinedDir, `${aliasName}.svg`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.copyFile(sourcePath, destinationPath);
|
||||||
|
console.log(`Copied ${iconName}.svg to ${aliasName}.svg`);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`Failed to copy ${sourcePath} to ${destinationPath}:`, err);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
|
||||||
|
console.timeEnd('icon outliner');
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ export * from './src/appendFile.ts';
|
|||||||
export * from './src/writeFile.ts';
|
export * from './src/writeFile.ts';
|
||||||
export * from './src/writeFileIfNotExists.ts';
|
export * from './src/writeFileIfNotExists.ts';
|
||||||
export * from './src/readAllMetadata.ts';
|
export * from './src/readAllMetadata.ts';
|
||||||
|
export * from './src/getAllIconAliases.ts';
|
||||||
export * from './src/readMetadata.ts';
|
export * from './src/readMetadata.ts';
|
||||||
export * from './src/readSvgDirectory.ts';
|
export * from './src/readSvgDirectory.ts';
|
||||||
export * from './src/readSvg.ts';
|
export * from './src/readSvg.ts';
|
||||||
|
|||||||
20
tools/build-helpers/src/getAllIconAliases.ts
Normal file
20
tools/build-helpers/src/getAllIconAliases.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { readAllMetadata } from "./readAllMetadata.ts";
|
||||||
|
|
||||||
|
export type IconAliases = [iconName: string, aliases: string[]][];
|
||||||
|
|
||||||
|
export const getAllIconAliases = async (iconsDir: string): Promise<IconAliases> => {
|
||||||
|
const metaDataFiles = await readAllMetadata(iconsDir)
|
||||||
|
|
||||||
|
return Object.entries(metaDataFiles).map(([iconName, metadata]) => {
|
||||||
|
const { aliases } = metadata;
|
||||||
|
|
||||||
|
if (!aliases?.length) return [iconName, []];
|
||||||
|
|
||||||
|
const aliasesNames = aliases.map(alias =>
|
||||||
|
typeof alias === 'string' ? alias : alias.name,
|
||||||
|
);
|
||||||
|
|
||||||
|
return [iconName, aliasesNames]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { readMetadata } from './readMetadata.ts';
|
import { readMetadata } from './readMetadata.ts';
|
||||||
|
import { type IconMetadata } from '../../build-icons/types.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads metadata from the icons/categories directories
|
* Reads metadata from the icons/categories directories
|
||||||
@@ -8,7 +9,7 @@ import { readMetadata } from './readMetadata.ts';
|
|||||||
* @param {string} directory
|
* @param {string} directory
|
||||||
* @returns {object} A map of icon or category metadata
|
* @returns {object} A map of icon or category metadata
|
||||||
*/
|
*/
|
||||||
export const readAllMetadata = async (directory: string): Promise<Record<string, unknown>> => {
|
export const readAllMetadata = async (directory: string): Promise<Record<string, IconMetadata>> => {
|
||||||
const directoryContent = await fs.readdir(directory);
|
const directoryContent = await fs.readdir(directory);
|
||||||
|
|
||||||
const metaDataPromises = directoryContent
|
const metaDataPromises = directoryContent
|
||||||
@@ -16,6 +17,7 @@ export const readAllMetadata = async (directory: string): Promise<Record<string,
|
|||||||
.map(async (file) => [path.basename(file, '.json'), await readMetadata(file, directory)]);
|
.map(async (file) => [path.basename(file, '.json'), await readMetadata(file, directory)]);
|
||||||
|
|
||||||
const metadata = await Promise.all(metaDataPromises);
|
const metadata = await Promise.all(metaDataPromises);
|
||||||
|
|
||||||
if (metadata.length === 0) {
|
if (metadata.length === 0) {
|
||||||
throw new Error(`No metadata files found in directory: ${directory}`);
|
throw new Error(`No metadata files found in directory: ${directory}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default async function generateExportFile(
|
|||||||
} else if (exportModuleNameCasing === 'pascal') {
|
} else if (exportModuleNameCasing === 'pascal') {
|
||||||
componentName = toPascalCase(iconName);
|
componentName = toPascalCase(iconName);
|
||||||
}
|
}
|
||||||
const importString = `export * from './${iconName}${iconFileExtension}';\n`;
|
const importString = `export { default as ${componentName} } from './${iconName}${iconFileExtension}';\n`;
|
||||||
return appendFile(importString, fileName, outputDirectory);
|
return appendFile(importString, fileName, outputDirectory);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -48,11 +48,7 @@ function generateIconFiles({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const getSvg = () => readSvg(`${iconName}.svg`, iconsDir);
|
const getSvg = () => readSvg(`${iconName}.svg`, iconsDir);
|
||||||
const {
|
const { deprecated = false, toBeRemovedInVersion = undefined } = iconMetaData[iconName];
|
||||||
deprecated = false,
|
|
||||||
toBeRemovedInVersion = undefined,
|
|
||||||
aliases,
|
|
||||||
} = iconMetaData[iconName];
|
|
||||||
const deprecationReason = deprecated
|
const deprecationReason = deprecated
|
||||||
? deprecationReasonTemplate(iconMetaData[iconName]?.deprecationReason ?? '', {
|
? deprecationReasonTemplate(iconMetaData[iconName]?.deprecationReason ?? '', {
|
||||||
componentName,
|
componentName,
|
||||||
@@ -68,8 +64,6 @@ function generateIconFiles({
|
|||||||
getSvg,
|
getSvg,
|
||||||
deprecated,
|
deprecated,
|
||||||
deprecationReason,
|
deprecationReason,
|
||||||
aliases,
|
|
||||||
toPascalCase,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const output = pretty
|
const output = pretty
|
||||||
@@ -77,7 +71,7 @@ function generateIconFiles({
|
|||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
trailingComma: 'all',
|
trailingComma: 'all',
|
||||||
printWidth: 100,
|
printWidth: 100,
|
||||||
parser: iconFileExtension.endsWith('.ts') ? 'babel-ts' : 'babel',
|
parser: 'babel',
|
||||||
})
|
})
|
||||||
: elementTemplate;
|
: elementTemplate;
|
||||||
|
|
||||||
@@ -87,7 +81,7 @@ function generateIconFiles({
|
|||||||
const output = `export { default } from "./${iconName}${iconFileExtension}";\n`;
|
const output = `export { default } from "./${iconName}${iconFileExtension}";\n`;
|
||||||
const location = path.join(
|
const location = path.join(
|
||||||
iconsDistDirectory,
|
iconsDistDirectory,
|
||||||
`${iconName}${separateIconFileExportExtension ?? iconFileExtension}`
|
`${iconName}${separateIconFileExportExtension ?? iconFileExtension}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
await fs.promises.writeFile(location, output, 'utf-8');
|
await fs.promises.writeFile(location, output, 'utf-8');
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ export type TemplateFunction = (params: {
|
|||||||
getSvg: () => Promise<string>;
|
getSvg: () => Promise<string>;
|
||||||
deprecated?: boolean;
|
deprecated?: boolean;
|
||||||
deprecationReason?: string;
|
deprecationReason?: string;
|
||||||
aliases?: (string | AliasDeprecation)[];
|
|
||||||
toPascalCase: (value: string) => string;
|
|
||||||
}) => Promise<string>;
|
}) => Promise<string>;
|
||||||
|
|
||||||
export type Path = string;
|
export type Path = string;
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ export interface ExportTemplate {
|
|||||||
getSvg: () => Promise<string>;
|
getSvg: () => Promise<string>;
|
||||||
deprecated: boolean;
|
deprecated: boolean;
|
||||||
deprecationReason: string;
|
deprecationReason: string;
|
||||||
aliases: Array<string | { name: string }>;
|
|
||||||
toPascalCase: (value: string) => string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TemplateFunction = (params: ExportTemplate) => Promise<string>;
|
export type TemplateFunction = (params: ExportTemplate) => Promise<string>;
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# @lucide/outline-svg
|
|
||||||
|
|
||||||
A internal used package to outline SVGs.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { promises as fs } from 'fs';
|
|
||||||
import SVGFixer from 'oslllo-svg-fixer';
|
|
||||||
import getArgumentOptions from 'minimist';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
const inputDir = path.join(process.cwd(), '../../icons');
|
|
||||||
const cliArguments = getArgumentOptions(process.argv.slice(2));
|
|
||||||
const { outputDir = 'outlined' } = cliArguments;
|
|
||||||
const targetDir = path.join(process.cwd(), '../../', outputDir);
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
console.time('icon outliner');
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
await fs.mkdir(targetDir);
|
|
||||||
} catch (error) {} // eslint-disable-line no-empty
|
|
||||||
|
|
||||||
await SVGFixer(inputDir, targetDir, {
|
|
||||||
showProgressBar: true,
|
|
||||||
traceResolution: 800,
|
|
||||||
}).fix();
|
|
||||||
|
|
||||||
console.timeEnd('icon outliner');
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init();
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@lucide/outline-svg",
|
|
||||||
"description": "A internal used package to outline SVGs.",
|
|
||||||
"private": true,
|
|
||||||
"version": "2.0.0",
|
|
||||||
"main": "main.ts",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"start": "node ./main.ts"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"minimist": "^1.2.8",
|
|
||||||
"oslllo-svg-fixer": "^5.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"strict": true,
|
|
||||||
"declaration": true,
|
|
||||||
"noEmitOnError": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"module": "ESNext",
|
|
||||||
"target": "ESNext",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"lib": ["esnext"],
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"outDir": "./dist",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user