Lucide 0.15.0 (#272)
* add configs
* Add vue components
* Add documentation
* add alpha release version
* improve npm ignore files
* add tests
* Make style and class attrs work
* 📦 bump version
* Add Icon suffix for component names
* bump version
* Add icon component example
* remove space
* add new build strategy
* Write a better intro
* add other node design
* fix
* add new default template
* add tempalte
* improve code
* small improvements
* small improvements
* move files
* Connect lucide with lucide-react
* Add support for vue
* Add licenses to packages
* Fix tests
* refactor build scripts
* Minor code fixes
* update homepage readme
* Update footer text
* Add a better introduction to packages
* Split up in tempaltes
* Add new types build file
* Setup workflow file
* update readme
* update
* Fix build
* remove debug code
* Add check if svgs have duplicated children
* Add check if their are no children
* small fixes
* last fixes in the build
* Move script to packages folder
* Fix tests and add types for lucide
* Add rule to package.json
* add types in build
* add npm ignore
* update package.jsons
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
"name": "lucide-figma",
|
||||
"version": "0.13.0",
|
||||
"license": "ISC",
|
||||
"private": true,
|
||||
"main": "build/ui.js",
|
||||
"scripts": {
|
||||
"build": "webpack --mode=production",
|
||||
|
||||
15
packages/lucide-react/LICENSE
Normal file
15
packages/lucide-react/LICENSE
Normal file
@@ -0,0 +1,15 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, Lucide Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
@@ -1,6 +1,8 @@
|
||||
# Lucide React
|
||||
|
||||
Use the lucide icon library in you react app.
|
||||
Implementation of the lucide icon library for react applications.
|
||||
|
||||
> What is lucide? Read it [here](https://github.com/lucide-icons/lucide#what-is-lucide).
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
const mainConfig = require('../../babel.config');
|
||||
|
||||
module.exports = {
|
||||
presets: ['react-app'],
|
||||
env: mainConfig.env,
|
||||
};
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const srcDirectory = path.join(__dirname, "dist")
|
||||
|
||||
// Declare type definitions
|
||||
const typeDefinitions = `
|
||||
/// <reference types="react" />
|
||||
import { SVGAttributes } from 'react'
|
||||
|
||||
// Create interface extending SVGAttributes
|
||||
export interface LucideProps extends Partial<React.SVGProps<SVGSVGElement>> {
|
||||
color?: string
|
||||
size?: string | number
|
||||
stroke?: string | number
|
||||
strokeWidth?: string | number
|
||||
}
|
||||
|
||||
// Generated icons
|
||||
`
|
||||
|
||||
// Write type definitions to file
|
||||
fs.writeFileSync(
|
||||
path.join(srcDirectory, "index.d.ts"),
|
||||
typeDefinitions,
|
||||
"utf-8",
|
||||
)
|
||||
|
||||
const iconsFolder = "./build/icons"
|
||||
fs.readdir(iconsFolder, (err, files) => {
|
||||
|
||||
files.forEach(file => {
|
||||
|
||||
const fileName = file.replace(".js", "")
|
||||
|
||||
// Convert fileName to component name
|
||||
const componentName = fileName.split("-")
|
||||
.map(word => word[0].toUpperCase() + word.substr(1, word.length))
|
||||
.join("")
|
||||
|
||||
// Declare component type
|
||||
const exportTypeString = `export declare const ${componentName}: (props: LucideProps) => JSX.Element;\n`
|
||||
|
||||
// Add component to the types file
|
||||
fs.appendFileSync(
|
||||
path.join(srcDirectory, "index.d.ts"),
|
||||
exportTypeString,
|
||||
"utf-8",
|
||||
)
|
||||
|
||||
})
|
||||
|
||||
console.log("Generated index.d.ts file with", files.length, "icons")
|
||||
|
||||
})
|
||||
@@ -3,9 +3,6 @@ module.exports = {
|
||||
roots: ['<rootDir>/src/', '<rootDir>/tests/'],
|
||||
moduleFileExtensions: ['js'],
|
||||
transformIgnorePatterns: [`/node_modules`],
|
||||
moduleNameMapper: {
|
||||
'^@/(.*)$': '<rootDir>/src/$1',
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.js$': 'babel-jest',
|
||||
},
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
"main:umd": "dist/umd/lucide-react.js",
|
||||
"module": "dist/esm/lucide-react.js",
|
||||
"unpkg": "dist/umd/lucide-react.min.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"typings": "dist/lucide-react.d.ts",
|
||||
"scripts": {
|
||||
"build": "yarn clean && yarn build:move && yarn build:icons && yarn build:es && yarn build:types && yarn build:bundles",
|
||||
"clean": "rm -rf dist && rm -rf build",
|
||||
"build:move": "cp -av src build",
|
||||
"build:icons": "yarn --cwd ../../ build:icons --output=../packages/lucide-react/build --templateSrc=../packages/lucide-react/scripts/exportTemplate --camelizeAttrs --noDefaultAttrs --renderUniqueKey",
|
||||
"build:types": "node build-types.js",
|
||||
"build:es": "yarn --cwd ../../ babel packages/lucide-react/build -d packages/lucide-react/dist/esm",
|
||||
"build": "yarn clean && yarn build:icons && yarn build:es && yarn build:types && yarn build:bundles",
|
||||
"clean": "rm -rf dist && rm -rf ./src/icons/*.js",
|
||||
"build:icons": "yarn --cwd ../../ build:icons --output=../packages/lucide-react/src --templateSrc=../packages/lucide-react/scripts/exportTemplate --renderUniqueKey",
|
||||
"build:es": "yarn --cwd ../../ babel packages/lucide-react/src -d packages/lucide-react/dist/esm",
|
||||
"build:types": "yarn --cwd ../../ babel-node packages/lucide-react/scripts/buildTypes.js",
|
||||
"build:bundles": "yarn --cwd ../../ rollup -c packages/lucide-react/rollup.config.js",
|
||||
"test": "jest"
|
||||
},
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const plugins = require('../../rollup.plugins');
|
||||
const pkg = require('./package.json');
|
||||
import plugins from '../../rollup.plugins';
|
||||
import pkg from './package.json';
|
||||
|
||||
const packageName = 'LucideReact';
|
||||
const outputFileName = 'lucide-react';
|
||||
const rootDir = 'packages/lucide-react'; // It runs from the root
|
||||
const outputDir = `${rootDir}/dist`;
|
||||
const inputs = [`${rootDir}/build/lucide-react.js`];
|
||||
const inputs = [`${rootDir}/src/lucide-react.js`];
|
||||
const bundles = [
|
||||
{
|
||||
format: 'umd',
|
||||
@@ -30,7 +30,7 @@ const configs = bundles
|
||||
inputs.map(input => ({
|
||||
input,
|
||||
plugins: plugins(pkg, minify),
|
||||
external: ['react', 'prop-types'],
|
||||
external: ['react', 'prop-types', 'lucide'],
|
||||
output: {
|
||||
name: packageName,
|
||||
file: `${outputDir}/${format}/${outputFileName}${minify ? '.min' : ''}.js`,
|
||||
@@ -39,6 +39,7 @@ const configs = bundles
|
||||
globals: {
|
||||
react: 'react',
|
||||
'prop-types': 'PropTypes',
|
||||
lucide: 'lucide',
|
||||
},
|
||||
},
|
||||
})),
|
||||
|
||||
44
packages/lucide-react/scripts/buildTypes.js
Normal file
44
packages/lucide-react/scripts/buildTypes.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import path from 'path';
|
||||
import {
|
||||
writeFile,
|
||||
readSvgDirectory,
|
||||
resetFile,
|
||||
toPascalCase,
|
||||
appendFile,
|
||||
} from '../../../scripts/helpers';
|
||||
|
||||
const srcDirectory = path.join(__dirname, '../dist');
|
||||
|
||||
// Declare type definitions
|
||||
const typeDefinitions = `\
|
||||
/// <reference types="react" />
|
||||
import { SVGAttributes } from 'react'
|
||||
|
||||
// Create interface extending SVGAttributes
|
||||
export interface LucideProps extends Partial<React.SVGProps<SVGSVGElement>> {
|
||||
color?: string
|
||||
size?: string | number
|
||||
stroke?: string | number
|
||||
strokeWidth?: string | number
|
||||
}
|
||||
|
||||
// Generated icons
|
||||
`;
|
||||
|
||||
const ICONS_DIR = path.resolve(__dirname, '../../../icons');
|
||||
const TYPES_FILE = 'lucide-react.d.ts';
|
||||
|
||||
resetFile(TYPES_FILE, srcDirectory);
|
||||
writeFile(typeDefinitions, TYPES_FILE, srcDirectory);
|
||||
|
||||
const svgFiles = readSvgDirectory(ICONS_DIR);
|
||||
|
||||
svgFiles.forEach(svgFile => {
|
||||
const iconName = path.basename(svgFile, '.svg');
|
||||
const componentName = toPascalCase(iconName);
|
||||
|
||||
const exportTypeString = `export declare const ${componentName}: (props: LucideProps) => JSX.Element;\n`;
|
||||
appendFile(exportTypeString, TYPES_FILE, srcDirectory);
|
||||
});
|
||||
|
||||
console.log(`Generated ${TYPES_FILE} file with`, svgFiles.length, 'icons');
|
||||
@@ -1,8 +1,7 @@
|
||||
export default ({ componentName, node }) => `
|
||||
export default ({ componentName, children }) => `
|
||||
import createReactComponent from '../createReactComponent';
|
||||
import defaultAttributes from '../defaultAttributes';
|
||||
|
||||
const ${componentName} = createReactComponent('${componentName}', ['svg', defaultAttributes, ${node}]);
|
||||
const ${componentName} = createReactComponent('${componentName}', ${JSON.stringify(children)});
|
||||
|
||||
export default ${componentName};
|
||||
`;
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { forwardRef, createElement } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
|
||||
export default (iconName, [tag, attrs, children]) => {
|
||||
export default (iconName, iconNode) => {
|
||||
const Component = forwardRef(
|
||||
({ color = 'currentColor', size = 24, strokeWidth = 2, ...rest }, ref) =>
|
||||
createElement(
|
||||
tag,
|
||||
'svg',
|
||||
{
|
||||
ref,
|
||||
...attrs,
|
||||
...defaultAttributes,
|
||||
width: size,
|
||||
height: size,
|
||||
stroke: color,
|
||||
strokeWidth,
|
||||
...rest,
|
||||
},
|
||||
children.map(([childTag, childAttrs]) => createElement(childTag, childAttrs)),
|
||||
iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
1
packages/lucide-react/src/icons/.gitkeep
Normal file
1
packages/lucide-react/src/icons/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
Folder for generated icons
|
||||
@@ -1,5 +0,0 @@
|
||||
/*
|
||||
Icons exports.
|
||||
|
||||
Will be generated
|
||||
*/
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { Grid } from '..'
|
||||
import { Grid } from '../src/icons'
|
||||
|
||||
describe('Using lucide icon components', () => {
|
||||
it('should render an component', () => {
|
||||
|
||||
15
packages/lucide-vue/LICENSE
Normal file
15
packages/lucide-vue/LICENSE
Normal file
@@ -0,0 +1,15 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, Lucide Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
@@ -1,6 +1,8 @@
|
||||
# Lucide Vue
|
||||
|
||||
Use the lucide icon library in you Vue app.
|
||||
Implementation of the lucide icon library for Vue applications.
|
||||
|
||||
> What is lucide? Read it [here](https://github.com/lucide-icons/lucide#what-is-lucide).
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ module.exports = {
|
||||
'^.+\\.vue$': 'vue-jest',
|
||||
},
|
||||
transformIgnorePatterns: [`/node_modules`],
|
||||
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
|
||||
snapshotSerializers: ['jest-serializer-vue'],
|
||||
moduleNameMapper: {
|
||||
'^@/(.*)$': '<rootDir>/src/$1',
|
||||
},
|
||||
|
||||
@@ -21,19 +21,16 @@
|
||||
"vue": "^2.6.12"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn clean && yarn build:move && yarn build:icons && yarn build:es && yarn build:bundles",
|
||||
"clean": "rm -rf dist && rm -rf build",
|
||||
"build:move": "cp -av src build",
|
||||
"build:icons": "yarn --cwd ../../ build:icons --output=../packages/lucide-vue/build --templateSrc=../packages/lucide-vue/scripts/exportTemplate --noDefaultAttrs",
|
||||
"build:es": "yarn --cwd ../../ babel packages/lucide-vue/build -d packages/lucide-vue/dist/esm",
|
||||
"build": "yarn clean && yarn build:icons && yarn build:es && yarn build:bundles",
|
||||
"clean": "rm -rf dist && rm -rf ./src/icons/*.js",
|
||||
"build:icons": "yarn --cwd ../../ build:icons --output=../packages/lucide-vue/src --templateSrc=../packages/lucide-vue/scripts/exportTemplate",
|
||||
"build:es": "yarn --cwd ../../ babel packages/lucide-vue/src -d packages/lucide-vue/dist/esm",
|
||||
"build:bundles": "yarn --cwd ../../ rollup -c packages/lucide-vue/rollup.config.js",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watchAll"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/test-utils": "^1.1.2",
|
||||
"babel-jest": "^26.6.3",
|
||||
"jest": "^26.6.3",
|
||||
"jest-serializer-vue": "^2.0.2",
|
||||
"vue-jest": "^3.0.7",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const plugins = require('../../rollup.plugins');
|
||||
const pkg = require('./package.json');
|
||||
import plugins from '../../rollup.plugins';
|
||||
import pkg from './package.json';
|
||||
|
||||
const packageName = 'LucideVue';
|
||||
const outputFileName = 'lucide-vue';
|
||||
const rootDir = 'packages/lucide-vue'; // It runs from the root
|
||||
const outputDir = `${rootDir}/dist`;
|
||||
const inputs = [`${rootDir}/build/lucide-vue.js`];
|
||||
const inputs = [`${rootDir}/src/lucide-vue.js`];
|
||||
const bundles = [
|
||||
{
|
||||
format: 'umd',
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
export default ({ componentName, node }) => `
|
||||
export default ({ componentName, children }) => `
|
||||
import createVueComponent from '../createVueComponent';
|
||||
import defaultAttributes from '../defaultAttributes';
|
||||
|
||||
const ${componentName} = createVueComponent('${componentName}Icon', ['svg', defaultAttributes, ${node}]);
|
||||
const ${componentName} = createVueComponent('${componentName}Icon', ${JSON.stringify(children)});
|
||||
|
||||
export default ${componentName};
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export default (iconName, [tag, defaultAttrs, children]) => ({
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
|
||||
export default (iconName, iconNode) => ({
|
||||
name: iconName,
|
||||
functional: true,
|
||||
props: {
|
||||
@@ -27,13 +29,13 @@ export default (iconName, [tag, defaultAttrs, children]) => ({
|
||||
},
|
||||
) {
|
||||
return createElement(
|
||||
tag,
|
||||
'svg',
|
||||
{
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
class: [defaultClass, data.class, data.staticClass, data.attrs && data.attrs.class].filter(Boolean),
|
||||
style: [data.style, data.staticStyle, data.attrs && data.attrs.style].filter(Boolean),
|
||||
attrs: {
|
||||
...defaultAttrs,
|
||||
...defaultAttributes,
|
||||
width: size,
|
||||
height: size,
|
||||
stroke: color,
|
||||
@@ -41,7 +43,7 @@ export default (iconName, [tag, defaultAttrs, children]) => ({
|
||||
...data.attrs,
|
||||
},
|
||||
},
|
||||
children.map(([childTag, childAttrs]) => createElement(childTag, { attrs: childAttrs })),
|
||||
iconNode.map(([tag, attrs]) => createElement(tag, { attrs })),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
1
packages/lucide-vue/src/icons/.gitkeep
Normal file
1
packages/lucide-vue/src/icons/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
Folder for generated icons
|
||||
@@ -1,5 +0,0 @@
|
||||
/*
|
||||
Icons exports.
|
||||
|
||||
Will be generated
|
||||
*/
|
||||
@@ -1,5 +1,5 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { Smile } from '..'
|
||||
import { Smile } from '../src/icons'
|
||||
|
||||
describe('Using lucide icon components', () => {
|
||||
it('should render an component', () => {
|
||||
|
||||
1
packages/lucide/.gitignore
vendored
Normal file
1
packages/lucide/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
src/icons/*.js
|
||||
10
packages/lucide/.npmignore
Normal file
10
packages/lucide/.npmignore
Normal file
@@ -0,0 +1,10 @@
|
||||
stats
|
||||
node_modules
|
||||
tests
|
||||
scripts
|
||||
build
|
||||
src
|
||||
babel.config.js
|
||||
jest.config.js
|
||||
rollup.config.js
|
||||
yarn.error.log
|
||||
15
packages/lucide/LICENSE
Normal file
15
packages/lucide/LICENSE
Normal file
@@ -0,0 +1,15 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, Lucide Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
120
packages/lucide/README.md
Normal file
120
packages/lucide/README.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Lucide
|
||||
|
||||
Implementation of the lucide icon library for web applications.
|
||||
|
||||
## Installation
|
||||
|
||||
### Package Managers
|
||||
|
||||
``` bash
|
||||
npm install lucide
|
||||
#or
|
||||
yarn add lucide
|
||||
```
|
||||
|
||||
### CDN
|
||||
|
||||
``` html
|
||||
<!-- Development version -->
|
||||
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
|
||||
|
||||
<!-- Production version -->
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### With unpkg
|
||||
|
||||
Here is a complete example with unpkg
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<body>
|
||||
<i icon-name="volume-2" class="my-class"></i>
|
||||
<i icon-name="x"></i>
|
||||
<i icon-name="menu"></i>
|
||||
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
<script>
|
||||
lucide.createIcons();
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
### With ESModules
|
||||
|
||||
To reduce bundle size, lucide is built to be fully treeshakable.
|
||||
The `createIcons` function will search for HTMLElements with the attribute `icon-name` and replace it with the svg from the given icon name.
|
||||
|
||||
```html
|
||||
<!-- Your HTML file -->
|
||||
<i icon-name="menu"></i>
|
||||
```
|
||||
|
||||
```js
|
||||
import { createIcons, icons } from 'lucide';
|
||||
|
||||
// Caution, this will import all the icons and bundle them.
|
||||
createIcons({icons});
|
||||
|
||||
// Recommended way, to include only the icons you need.
|
||||
import { createIcons, Menu, ArrowRight, Globe } from 'lucide';
|
||||
|
||||
createIcons({
|
||||
icons: {
|
||||
Menu,
|
||||
ArrowRight,
|
||||
Globe,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Additional Options
|
||||
|
||||
In the `createIcons` function you can pass some extra parameters to adjust the `nameAttr` or add custom attributes like for example classes.
|
||||
|
||||
Here is a full example:
|
||||
|
||||
```js
|
||||
import { createIcons } from 'lucide';
|
||||
|
||||
createIcons({
|
||||
attrs: {
|
||||
class: ['my-custom-class', 'icon'],
|
||||
'stroke-width': 1,
|
||||
stroke: '#333',
|
||||
},
|
||||
nameAttr: 'icon-name', // attribute for the icon name.
|
||||
});
|
||||
```
|
||||
|
||||
#### Treeshake the library, only use the icons you use
|
||||
|
||||
```js
|
||||
import { createIcons, Menu, ArrowRight, Globe } from 'lucide';
|
||||
|
||||
createIcons({
|
||||
icons: {
|
||||
Menu,
|
||||
ArrowRight,
|
||||
Globe,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Custom Element binding
|
||||
|
||||
```js
|
||||
import { createElement, Menu } from 'lucide';
|
||||
|
||||
const menuIcon = createElement(Menu); // Returns HTMLElement (svg)
|
||||
|
||||
// set custom attributes with browser native functions
|
||||
menuIcon.setAttribute('stroke', '#333');
|
||||
menuIcon.classList.add('my-icon-class');
|
||||
|
||||
// Append HTMLElement in webpage
|
||||
const myApp = document.getElementById('app');
|
||||
myApp.appendChild(menuIcon);
|
||||
```
|
||||
14
packages/lucide/babel.config.js
Normal file
14
packages/lucide/babel.config.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const mainConfig = require('../../babel.config');
|
||||
|
||||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/env',
|
||||
{
|
||||
loose: true,
|
||||
modules: false,
|
||||
},
|
||||
],
|
||||
],
|
||||
env: mainConfig.env,
|
||||
};
|
||||
11
packages/lucide/jest.config.js
Normal file
11
packages/lucide/jest.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const esModules = ['lodash-es'].join('|');
|
||||
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
roots: ['<rootDir>/src/', '<rootDir>/tests/'],
|
||||
moduleFileExtensions: ['js'],
|
||||
transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
|
||||
transform: {
|
||||
'^.+\\.js$': 'babel-jest',
|
||||
},
|
||||
};
|
||||
30
packages/lucide/package.json
Normal file
30
packages/lucide/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "lucide",
|
||||
"description": "Lucide is a community-run fork of Feather Icons, open for anyone to contribute icons.",
|
||||
"version": "0.14.0",
|
||||
"license": "ISC",
|
||||
"homepage": "https://lucide.dev",
|
||||
"bugs": "https://github.com/lucide-icons/lucide/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lucide-icons/lucide.git",
|
||||
"directory": "packages/lucide"
|
||||
},
|
||||
"amdName": "lucide",
|
||||
"source": "build/lucide.js",
|
||||
"main": "dist/cjs/lucide.js",
|
||||
"main:umd": "dist/umd/lucide.js",
|
||||
"module": "dist/esm/lucide.js",
|
||||
"unpkg": "dist/umd/lucide.min.js",
|
||||
"typings": "dist/lucide.d.ts",
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "yarn clean && yarn build:icons && yarn build:es && yarn build:types && yarn build:bundles",
|
||||
"clean": "rm -rf dist && rm -rf ./src/icons/*.js",
|
||||
"build:icons": "yarn --cwd ../../ build:icons --output=../packages/lucide/src",
|
||||
"build:es": "babel src -d dist/esm",
|
||||
"build:types": "yarn --cwd ../../ babel-node packages/lucide/scripts/buildTypes.js",
|
||||
"build:bundles": "rollup -c rollup.config.js",
|
||||
"test": "jest"
|
||||
}
|
||||
}
|
||||
41
packages/lucide/rollup.config.js
Normal file
41
packages/lucide/rollup.config.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import plugins from '../../rollup.plugins';
|
||||
import pkg from './package.json';
|
||||
|
||||
const outputFileName = pkg.name;
|
||||
const outputDir = 'dist';
|
||||
const inputs = ['src/lucide.js'];
|
||||
const bundles = [
|
||||
{
|
||||
format: 'umd',
|
||||
inputs,
|
||||
outputDir,
|
||||
minify: true,
|
||||
},
|
||||
{
|
||||
format: 'umd',
|
||||
inputs,
|
||||
outputDir,
|
||||
},
|
||||
{
|
||||
format: 'cjs',
|
||||
inputs,
|
||||
outputDir,
|
||||
},
|
||||
];
|
||||
|
||||
const configs = bundles
|
||||
.map(({ inputs, outputDir, format, minify }) =>
|
||||
inputs.map(input => ({
|
||||
input,
|
||||
plugins: plugins(pkg, minify),
|
||||
output: {
|
||||
name: outputFileName,
|
||||
file: `${outputDir}/${format}/${outputFileName}${minify ? '.min' : ''}.js`,
|
||||
format,
|
||||
sourcemap: true,
|
||||
},
|
||||
})),
|
||||
)
|
||||
.flat();
|
||||
|
||||
export default configs;
|
||||
37
packages/lucide/scripts/buildTypes.js
Normal file
37
packages/lucide/scripts/buildTypes.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import path from 'path';
|
||||
|
||||
import { readSvgDirectory, resetFile, appendFile, toPascalCase } from '../../../scripts/helpers';
|
||||
|
||||
const TARGET_DIR = path.join(__dirname, '../dist');
|
||||
const ICONS_DIR = path.resolve(__dirname, '../../../icons');
|
||||
const TYPES_FILE_NAME = 'lucide.d.ts';
|
||||
|
||||
// Generates header of d.ts file include some types and functions
|
||||
const typeDefinitions = `\
|
||||
export type IconName = string;
|
||||
export type IconNode = readonly [tag: string, object:SVGProps<SVGSVGElement>, children:IconNode?];
|
||||
export type IconsObj = { [IconName]: IconNode }
|
||||
|
||||
export interface Attributes extends Partial<Props<Element>> {}
|
||||
|
||||
export function createElement(icon: IconNode): SVGSVGElement;
|
||||
export function createIcons({ icons: IconsObj, nameAttr: string = 'icon-name', attrs: Attributes = {} }): VoidFunction;
|
||||
|
||||
export declare const icons: IconsObj;
|
||||
|
||||
// Generated icons
|
||||
`;
|
||||
|
||||
resetFile(TYPES_FILE_NAME, TARGET_DIR);
|
||||
appendFile(typeDefinitions, TYPES_FILE_NAME, TARGET_DIR);
|
||||
|
||||
const svgFiles = readSvgDirectory(ICONS_DIR);
|
||||
|
||||
svgFiles.forEach(svgFile => {
|
||||
const nameSvg = path.basename(svgFile, '.svg');
|
||||
const namePascal = toPascalCase(nameSvg);
|
||||
|
||||
appendFile(`export declare const ${namePascal}: IconNode;\n`, TYPES_FILE_NAME, TARGET_DIR);
|
||||
});
|
||||
|
||||
console.log(`Generated ${TYPES_FILE_NAME} file with`, svgFiles.length, 'icons');
|
||||
31
packages/lucide/src/createElement.js
Normal file
31
packages/lucide/src/createElement.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Creates a new HTMLElement from icon node
|
||||
* @param {string} tag
|
||||
* @param {object} attrs
|
||||
* @param {array} children
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
const createElement = (tag, attrs, children = []) => {
|
||||
const element = document.createElementNS('http://www.w3.org/2000/svg', tag);
|
||||
|
||||
Object.keys(attrs).forEach(name => {
|
||||
element.setAttribute(name, attrs[name]);
|
||||
});
|
||||
|
||||
if (children.length) {
|
||||
children = children.forEach(child => {
|
||||
const childElement = createElement(...child);
|
||||
|
||||
element.appendChild(childElement);
|
||||
});
|
||||
}
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new HTMLElement from icon node
|
||||
* @param {[tag: string, attrs: object, children: array]} iconNode
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
export default ([tag, attrs, children]) => createElement(tag, attrs, children);
|
||||
11
packages/lucide/src/defaultAttributes.js
Normal file
11
packages/lucide/src/defaultAttributes.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export default {
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
width: 24,
|
||||
height: 24,
|
||||
viewBox: '0 0 24 24',
|
||||
fill: 'none',
|
||||
stroke: 'currentColor',
|
||||
'stroke-width': 2,
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-linejoin': 'round',
|
||||
};
|
||||
1
packages/lucide/src/icons/.gitkeep
Normal file
1
packages/lucide/src/icons/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
Folder for generated icons
|
||||
37
packages/lucide/src/lucide.js
Normal file
37
packages/lucide/src/lucide.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import replaceElement from './replaceElement';
|
||||
import * as allIcons from './icons';
|
||||
|
||||
/**
|
||||
* Replaces all elements with matching nameAttr with the defined icons
|
||||
* @param {{ icons?: object, nameAttr?: string, attrs?: object }} options
|
||||
*/
|
||||
const createIcons = ({ icons = {}, nameAttr = 'icon-name', attrs = {} } = {}) => {
|
||||
if (!Object.values(icons).length) {
|
||||
throw new Error(
|
||||
"Please provide an icons object.\nIf you want to use all the icons you can import it like:\n `import { createIcons, icons } from 'lucide';\nlucide.createIcons({icons});`",
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof document === 'undefined') {
|
||||
throw new Error('`createIcons()` only works in a browser environment.');
|
||||
}
|
||||
|
||||
const elementsToReplace = document.querySelectorAll(`[${nameAttr}]`);
|
||||
|
||||
Array.from(elementsToReplace).forEach(element =>
|
||||
replaceElement(element, { nameAttr, icons, attrs }),
|
||||
);
|
||||
};
|
||||
|
||||
export { createIcons };
|
||||
|
||||
/*
|
||||
Create Element function export.
|
||||
*/
|
||||
export { default as createElement } from './createElement';
|
||||
|
||||
/*
|
||||
Icons exports.
|
||||
*/
|
||||
export { allIcons as icons };
|
||||
export * from './icons';
|
||||
85
packages/lucide/src/replaceElement.js
Normal file
85
packages/lucide/src/replaceElement.js
Normal file
@@ -0,0 +1,85 @@
|
||||
import createElement from './createElement';
|
||||
|
||||
/**
|
||||
* Get the attributes of an HTML element.
|
||||
* @param {HTMLElement} element
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const getAttrs = element =>
|
||||
Array.from(element.attributes).reduce((attrs, attr) => {
|
||||
attrs[attr.name] = attr.value;
|
||||
return attrs;
|
||||
}, {});
|
||||
|
||||
/**
|
||||
* Gets the classNames of an attributes Object.
|
||||
* @param {Object} attrs
|
||||
* @returns {Array}
|
||||
*/
|
||||
export const getClassNames = attrs => {
|
||||
if (typeof attrs === 'string') return attrs;
|
||||
if (!attrs || !attrs.class) return '';
|
||||
if (attrs.class && typeof attrs.class === 'string') {
|
||||
return attrs.class.split(' ');
|
||||
}
|
||||
if (attrs.class && Array.isArray(attrs.class)) {
|
||||
return attrs.class;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Combines the classNames of array of classNames to a String
|
||||
* @param {array} arrayOfClassnames
|
||||
* @returns {string}
|
||||
*/
|
||||
export const combineClassNames = arrayOfClassnames => {
|
||||
const classNameArray = arrayOfClassnames.flatMap(getClassNames);
|
||||
|
||||
return classNameArray
|
||||
.map(classItem => classItem.trim())
|
||||
.filter(Boolean)
|
||||
.filter((value, index, self) => self.indexOf(value) === index)
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
const toPascalCase = string =>
|
||||
string.replace(/(\w)(\w*)(_|-|\s*)/g, (g0, g1, g2) => g1.toUpperCase() + g2.toLowerCase());
|
||||
|
||||
/**
|
||||
* ReplaceElement, replaces the given element with the created icon.
|
||||
* @param {HTMLElement} element
|
||||
* @param {{ nameAttr: string, icons: object, attrs: object }} options: { nameAttr, icons, attrs }
|
||||
* @returns {Function}
|
||||
*/
|
||||
export default (element, { nameAttr, icons, attrs }) => {
|
||||
const iconName = element.getAttribute(nameAttr);
|
||||
const ComponentName = toPascalCase(iconName);
|
||||
|
||||
const iconNode = icons[ComponentName];
|
||||
|
||||
if (!iconNode) {
|
||||
return console.warn(
|
||||
`${element.outerHTML} icon name was not found in the provided icons object.`,
|
||||
);
|
||||
}
|
||||
|
||||
const elementAttrs = getAttrs(element);
|
||||
const [tag, iconAttributes, children] = iconNode;
|
||||
|
||||
const iconAttrs = {
|
||||
...iconAttributes,
|
||||
'icon-name': iconName,
|
||||
...attrs,
|
||||
};
|
||||
|
||||
const classNames = combineClassNames(['lucide', elementAttrs, attrs]);
|
||||
|
||||
if (classNames) {
|
||||
iconAttrs.class = classNames;
|
||||
}
|
||||
|
||||
const svgElement = createElement([tag, iconAttrs, children]);
|
||||
|
||||
return element.parentNode.replaceChild(svgElement, element);
|
||||
};
|
||||
5
packages/lucide/tests/__snapshots__/lucide.spec.js.snap
Normal file
5
packages/lucide/tests/__snapshots__/lucide.spec.js.snap
Normal file
@@ -0,0 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`createIcons should add custom attributes 1`] = `"<svg xmlns=\\"http://www.w3.org/2000/svg\\" width=\\"24\\" height=\\"24\\" viewBox=\\"0 0 24 24\\" fill=\\"black\\" stroke=\\"currentColor\\" stroke-width=\\"2\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" icon-name=\\"volume-2\\" class=\\"lucide icon custom-class\\"><polygon points=\\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\\"></polygon><path d=\\"M19.07 4.93a10 10 0 010 14.14M15.54 8.46a5 5 0 010 7.07\\"></path></svg>"`;
|
||||
|
||||
exports[`createIcons should read elements from DOM and replace it with icons 1`] = `"<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\\" icon-name=\\"volume-2\\" class=\\"lucide\\"><polygon points=\\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\\"></polygon><path d=\\"M19.07 4.93a10 10 0 010 14.14M15.54 8.46a5 5 0 010 7.07\\"></path></svg>"`;
|
||||
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`combineClassNames should retuns a string of classNames 1`] = `"item item1 item2 item3 item4 item5 item6 item7 item8 item9"`;
|
||||
67
packages/lucide/tests/lucide.spec.js
Normal file
67
packages/lucide/tests/lucide.spec.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import * as icons from '../src/icons';
|
||||
import { createIcons } from '../src/lucide';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { parseSync, stringify } from 'svgson';
|
||||
|
||||
const ICONS_DIR = path.resolve(__dirname, '../../../icons');
|
||||
|
||||
const getOriginalSvg = (iconName) => {
|
||||
const svgContent = fs.readFileSync(path.join(ICONS_DIR, `${iconName}.svg`), 'utf8');
|
||||
const svgParsed = parseSync(svgContent);
|
||||
|
||||
svgParsed.attributes['icon-name'] = iconName;
|
||||
svgParsed.attributes['class'] = 'lucide';
|
||||
|
||||
return stringify(svgParsed, { selfClose: false });
|
||||
};
|
||||
|
||||
describe('createIcons', () => {
|
||||
it('should read elements from DOM and replace it with icons', () => {
|
||||
document.body.innerHTML = `<i icon-name="volume-2"></i>`;
|
||||
|
||||
createIcons({icons});
|
||||
|
||||
const svg = getOriginalSvg('volume-2');
|
||||
|
||||
expect(document.body.innerHTML).toBe(svg)
|
||||
expect(document.body.innerHTML).toMatchSnapshot()
|
||||
});
|
||||
|
||||
it('should customize the name attribute', () => {
|
||||
document.body.innerHTML = `<i custom-name="volume-2"></i>`;
|
||||
|
||||
createIcons({
|
||||
icons,
|
||||
nameAttr: 'custom-name'
|
||||
});
|
||||
|
||||
const hasSvg = !!document.querySelector('svg');
|
||||
|
||||
expect(hasSvg).toBeTruthy()
|
||||
});
|
||||
|
||||
it('should add custom attributes', () => {
|
||||
document.body.innerHTML = `<i icon-name="volume-2" class="lucide"></i>`;
|
||||
|
||||
const attrs = {
|
||||
class: 'lucide icon custom-class',
|
||||
fill: 'black',
|
||||
};
|
||||
|
||||
createIcons({ icons, attrs });
|
||||
|
||||
const element = document.querySelector('svg');
|
||||
const attributes = element.getAttributeNames();
|
||||
|
||||
const attributesAndValues = attributes.reduce((acc, item) => {
|
||||
acc[item] = element.getAttribute(item);
|
||||
|
||||
return acc;
|
||||
},{})
|
||||
|
||||
expect(document.body.innerHTML).toMatchSnapshot();
|
||||
|
||||
expect(attributesAndValues).toEqual(expect.objectContaining(attrs));
|
||||
});
|
||||
});
|
||||
63
packages/lucide/tests/replaceElement.spec.js
Normal file
63
packages/lucide/tests/replaceElement.spec.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { getAttrs, getClassNames, combineClassNames } from '../src/replaceElement';
|
||||
|
||||
describe('getAtts', () => {
|
||||
it('should returns attrbrutes of an element', () => {
|
||||
const element = {
|
||||
attributes: [
|
||||
{
|
||||
name: 'class',
|
||||
value: 'item1 item2 item4',
|
||||
},
|
||||
{
|
||||
name: 'date-name',
|
||||
value: 'volume',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const attrs = getAttrs(element);
|
||||
|
||||
expect(attrs.class).toBe(element.attributes[0].value);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getClassNames', () => {
|
||||
it('should returns an array when giving class property of string', () => {
|
||||
const elementAttrs = {
|
||||
class: 'item1 item2 item3'
|
||||
};
|
||||
|
||||
const attrs = getClassNames(elementAttrs);
|
||||
expect(JSON.stringify(attrs)).toBe(JSON.stringify(['item1','item2','item3']));
|
||||
});
|
||||
|
||||
it('should returns an array when givind class property with an array', () => {
|
||||
const elementAttrs = {
|
||||
class: ['item1','item2','item3']
|
||||
};
|
||||
|
||||
const attrs = getClassNames(elementAttrs);
|
||||
expect(JSON.stringify(attrs)).toBe(JSON.stringify(['item1','item2','item3']));
|
||||
});
|
||||
});
|
||||
|
||||
describe('combineClassNames', () => {
|
||||
it('should retuns a string of classNames', () => {
|
||||
const arrayOfClassnames = [
|
||||
'item',
|
||||
{
|
||||
class: ['item1','item2','item3']
|
||||
},
|
||||
{
|
||||
class: ['item4','item5','item6']
|
||||
},
|
||||
{
|
||||
class: ['item7','item8','item9']
|
||||
}
|
||||
];
|
||||
|
||||
const combinedClassNames = combineClassNames(arrayOfClassnames);
|
||||
|
||||
expect(combinedClassNames).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user