mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-13 13:48:26 +00:00
fix(lucide): Support for iconNodes from other libs like Lab (#2752)
* Add support for iconNodes * formatting
This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --iconFileExtension=.ts --withAliases --aliasNamesOnly --aliasesFileExtension=.ts --exportFileName=index.ts",
|
||||
"build:bundle": "rollup -c rollup.config.mjs",
|
||||
"test": "pnpm build:icons && vitest run",
|
||||
"test:watch": "vitest watch",
|
||||
"version": "pnpm version --git-tag-version=false"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -19,11 +19,7 @@ import type { IconNode } from '../types';
|
||||
* @returns {Array}
|
||||
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
|
||||
*/
|
||||
const ${componentName}: IconNode = [
|
||||
'svg',
|
||||
defaultAttributes,
|
||||
${JSON.stringify(children)}
|
||||
];
|
||||
const ${componentName}: IconNode = ${JSON.stringify(children)}
|
||||
|
||||
export default ${componentName};
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { IconNode, IconNodeChild, SVGProps } from './types';
|
||||
import { IconNode, SVGProps } from './types';
|
||||
|
||||
type CreateElementParams = [tag: string, attrs: SVGProps, children?: IconNode];
|
||||
|
||||
/**
|
||||
* Creates a new HTMLElement from icon node
|
||||
@@ -7,16 +9,16 @@ import { IconNode, IconNodeChild, SVGProps } from './types';
|
||||
* @param {array} children
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
const createElement = (tag: string, attrs: SVGProps, children: IconNodeChild[] = []) => {
|
||||
const createElement = ([tag, attrs, children]: CreateElementParams) => {
|
||||
const element = document.createElementNS('http://www.w3.org/2000/svg', tag);
|
||||
|
||||
Object.keys(attrs).forEach((name) => {
|
||||
element.setAttribute(name, String(attrs[name]));
|
||||
});
|
||||
|
||||
if (children.length) {
|
||||
if (children?.length) {
|
||||
children.forEach((child) => {
|
||||
const childElement = createElement(...child);
|
||||
const childElement = createElement(child);
|
||||
|
||||
element.appendChild(childElement);
|
||||
});
|
||||
@@ -25,9 +27,4 @@ const createElement = (tag: string, attrs: SVGProps, children: IconNodeChild[] =
|
||||
return element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new HTMLElement from icon node
|
||||
* @param {[tag: string, attrs: object, children: array]} iconNode
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
export default ([tag, attrs, children]: IconNode) => createElement(tag, attrs, children);
|
||||
export default createElement;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import createElement from './createElement';
|
||||
import defaultAttributes from './defaultAttributes';
|
||||
import { Icons } from './types';
|
||||
|
||||
export type CustomAttrs = { [attr: string]: any };
|
||||
@@ -19,7 +20,9 @@ export const getAttrs = (element: Element): Record<string, string> =>
|
||||
* @param {Object} attrs
|
||||
* @returns {Array}
|
||||
*/
|
||||
export const getClassNames = (attrs: Record<string, string> | string): string | string[] => {
|
||||
export const getClassNames = (
|
||||
attrs: Record<string, string | string[]> | string,
|
||||
): string | string[] => {
|
||||
if (typeof attrs === 'string') return attrs;
|
||||
if (!attrs || !attrs.class) return '';
|
||||
if (attrs.class && typeof attrs.class === 'string') {
|
||||
@@ -36,7 +39,9 @@ export const getClassNames = (attrs: Record<string, string> | string): string |
|
||||
* @param {array} arrayOfClassnames
|
||||
* @returns {string}
|
||||
*/
|
||||
export const combineClassNames = (arrayOfClassnames: (string | Record<string, string>)[]) => {
|
||||
export const combineClassNames = (
|
||||
arrayOfClassnames: (string | Record<string, string | string[]>)[],
|
||||
) => {
|
||||
const classNameArray = arrayOfClassnames.flatMap(getClassNames);
|
||||
|
||||
return classNameArray
|
||||
@@ -77,10 +82,9 @@ const replaceElement = (element: Element, { nameAttr, icons, attrs }: ReplaceEle
|
||||
}
|
||||
|
||||
const elementAttrs = getAttrs(element);
|
||||
const [tag, iconAttributes, children] = iconNode;
|
||||
|
||||
const iconAttrs = {
|
||||
...iconAttributes,
|
||||
...defaultAttributes,
|
||||
'data-lucide': iconName,
|
||||
...attrs,
|
||||
...elementAttrs,
|
||||
@@ -94,7 +98,7 @@ const replaceElement = (element: Element, { nameAttr, icons, attrs }: ReplaceEle
|
||||
});
|
||||
}
|
||||
|
||||
const svgElement = createElement([tag, iconAttrs, children]);
|
||||
const svgElement = createElement(['svg', iconAttrs, iconNode]);
|
||||
|
||||
return element.parentNode?.replaceChild(svgElement, element);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// className is not supported in svg elements
|
||||
export type SVGProps = Record<string, string | number>;
|
||||
|
||||
export type IconNodeChild = readonly [tag: string, attrs: SVGProps];
|
||||
export type IconNode = readonly [tag: string, attrs: SVGProps, children?: IconNodeChild[]];
|
||||
export type IconNode = [tag: string, attrs: SVGProps][];
|
||||
export type Icons = { [key: string]: IconNode };
|
||||
|
||||
@@ -6,7 +6,7 @@ import { parseSync, stringify } from 'svgson';
|
||||
|
||||
const ICONS_DIR = path.resolve(__dirname, '../../../icons');
|
||||
|
||||
const getOriginalSvg = (iconName, aliasName) => {
|
||||
const getOriginalSvg = (iconName: string, aliasName?: string) => {
|
||||
const svgContent = fs.readFileSync(path.join(ICONS_DIR, `${iconName}.svg`), 'utf8');
|
||||
const svgParsed = parseSync(svgContent);
|
||||
|
||||
@@ -51,14 +51,17 @@ describe('createIcons', () => {
|
||||
|
||||
createIcons({ icons, attrs });
|
||||
|
||||
const element = document.querySelector('svg');
|
||||
const element = document.querySelector('svg') as SVGSVGElement;
|
||||
const attributes = element.getAttributeNames();
|
||||
|
||||
const attributesAndValues = attributes.reduce((acc, item) => {
|
||||
acc[item] = element.getAttribute(item);
|
||||
const attributesAndValues = attributes.reduce(
|
||||
(acc, item) => {
|
||||
acc[item] = element.getAttribute(item);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string | null>,
|
||||
);
|
||||
|
||||
expect(document.body.innerHTML).toMatchSnapshot();
|
||||
|
||||
@@ -74,14 +77,17 @@ describe('createIcons', () => {
|
||||
|
||||
createIcons({ icons });
|
||||
|
||||
const element = document.querySelector('svg');
|
||||
const element = document.querySelector('svg') as SVGSVGElement;
|
||||
const attributes = element.getAttributeNames();
|
||||
|
||||
const attributesAndValues = attributes.reduce((acc, item) => {
|
||||
acc[item] = element.getAttribute(item);
|
||||
const attributesAndValues = attributes.reduce(
|
||||
(acc, item) => {
|
||||
acc[item] = element.getAttribute(item);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string | null>,
|
||||
);
|
||||
|
||||
expect(attributesAndValues).toEqual(expect.objectContaining(attrs));
|
||||
});
|
||||
+2
-2
@@ -16,7 +16,7 @@ describe('getAtts', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const attrs = getAttrs(element);
|
||||
const attrs = getAttrs(element as unknown as Element);
|
||||
|
||||
expect(attrs.class).toBe(element.attributes[0].value);
|
||||
});
|
||||
@@ -44,7 +44,7 @@ describe('getClassNames', () => {
|
||||
|
||||
describe('combineClassNames', () => {
|
||||
it('should returns a string of classNames', () => {
|
||||
const arrayOfClassnames = [
|
||||
const arrayOfClassnames: (string | Record<string, string[]>)[] = [
|
||||
'item',
|
||||
{
|
||||
class: ['item1', 'item2', 'item3'],
|
||||
Reference in New Issue
Block a user