2
0
mirror of https://github.com/iconify/iconify.git synced 2025-01-08 15:54:09 +00:00

Clean up handling icon customisations and transformations

This commit is contained in:
Vjacheslav Trushkin 2022-06-20 23:43:01 +03:00
parent 7b4409665a
commit b2d3accf81
16 changed files with 200 additions and 170 deletions

View File

@ -7,7 +7,7 @@
"declaration": true, "declaration": true,
"sourceMap": false, "sourceMap": false,
"strict": true, "strict": true,
"types": ["node", "svelte"], "types": ["svelte"],
"moduleResolution": "node", "moduleResolution": "node",
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"esModuleInterop": true, "esModuleInterop": true,

View File

@ -31,8 +31,6 @@ export function buildIcon(
): IconifyIconBuildResult { ): IconifyIconBuildResult {
return iconToSVG( return iconToSVG(
{ ...defaultIconProps, ...icon }, { ...defaultIconProps, ...icon },
customisations mergeCustomisations(defaultIconCustomisations, customisations || {})
? mergeCustomisations(defaultIconCustomisations, customisations)
: defaultIconCustomisations
); );
} }

View File

@ -1,4 +1,3 @@
import { mergeIconTransformations } from '../icon/transformations';
import { import {
defaultIconSizeCustomisations, defaultIconSizeCustomisations,
FullIconCustomisations, FullIconCustomisations,
@ -7,29 +6,35 @@ import {
} from './defaults'; } from './defaults';
/** /**
* Convert IconifyIconCustomisations to FullIconCustomisations * Convert IconifyIconCustomisations to FullIconCustomisations, checking value types
*/ */
export function mergeCustomisations<T extends FullIconCustomisations>( export function mergeCustomisations<T extends FullIconCustomisations>(
defaults: T, defaults: T,
item: IconifyIconCustomisations, item: IconifyIconCustomisations
keepOtherProps = true
): T { ): T {
// Merge transformations // Copy default values
const result = mergeIconTransformations(defaults, item, keepOtherProps); const result = {
...defaults,
};
// Merge dimensions // Merge all properties
for (const key in defaultIconSizeCustomisations) { for (const key in item) {
const attr = key as keyof IconifyIconSizeCustomisations; const value = item[key as keyof IconifyIconCustomisations];
const value = item[attr];
const valueType = typeof value; const valueType = typeof value;
if (key in defaultIconSizeCustomisations) {
// Dimension
if ( if (
value === null || value === null ||
(value && (valueType === 'string' || valueType === 'number')) (value && (valueType === 'string' || valueType === 'number'))
) { ) {
result[attr] = value; result[key as keyof IconifyIconSizeCustomisations] =
} else { value as string;
(result as Record<string, unknown>)[attr] = defaults[attr]; }
} else if (valueType === typeof result[key as keyof T]) {
// Normalise rotation, copy everything else as is
(result as Record<string, unknown>)[key] =
key === 'rotate' ? (value as number) % 4 : value;
} }
} }

View File

@ -1,6 +1,5 @@
import type { IconifyJSON } from '@iconify/types'; import type { ExtendedIconifyIcon, IconifyJSON } from '@iconify/types';
import { defaultIconProps } from '../icon/defaults'; import { defaultIconProps, FullExtendedIconifyIcon } from '../icon/defaults';
import type { IconifyIcon, FullIconifyIcon } from '../icon/defaults';
import { mergeIconData } from '../icon/merge'; import { mergeIconData } from '../icon/merge';
import { getIconsTree } from './tree'; import { getIconsTree } from './tree';
@ -12,30 +11,29 @@ export function internalGetIconData(
name: string, name: string,
tree: string[], tree: string[],
full: true full: true
): FullIconifyIcon; ): FullExtendedIconifyIcon;
export function internalGetIconData( export function internalGetIconData(
data: IconifyJSON, data: IconifyJSON,
name: string, name: string,
tree: string[], tree: string[],
full: false full: false
): IconifyIcon; ): ExtendedIconifyIcon;
export function internalGetIconData( export function internalGetIconData(
data: IconifyJSON, data: IconifyJSON,
name: string, name: string,
tree: string[], tree: string[],
full: boolean full: boolean
): FullIconifyIcon | IconifyIcon { ): FullExtendedIconifyIcon | ExtendedIconifyIcon {
const icons = data.icons; const icons = data.icons;
const aliases = data.aliases || {}; const aliases = data.aliases || {};
let currentProps = {} as IconifyIcon; let currentProps = {} as ExtendedIconifyIcon;
// Parse parent item // Parse parent item
function parse(name: string) { function parse(name: string) {
currentProps = mergeIconData( currentProps = mergeIconData(
icons[name] || aliases[name], icons[name] || aliases[name],
currentProps, currentProps
false
); );
} }
@ -45,9 +43,8 @@ export function internalGetIconData(
// Add default values // Add default values
currentProps = mergeIconData( currentProps = mergeIconData(
data, data,
currentProps, currentProps
false ) as unknown as ExtendedIconifyIcon;
) as unknown as IconifyIcon;
// Return icon // Return icon
return full return full
@ -62,17 +59,17 @@ export function getIconData(
data: IconifyJSON, data: IconifyJSON,
name: string, name: string,
full: true full: true
): FullIconifyIcon | null; ): FullExtendedIconifyIcon | null;
export function getIconData( export function getIconData(
data: IconifyJSON, data: IconifyJSON,
name: string, name: string,
full: false full: false
): IconifyIcon | null; ): ExtendedIconifyIcon | null;
export function getIconData( export function getIconData(
data: IconifyJSON, data: IconifyJSON,
name: string, name: string,
full = false full = false
): FullIconifyIcon | IconifyIcon | null { ): FullExtendedIconifyIcon | ExtendedIconifyIcon | null {
if (data.icons[name]) { if (data.icons[name]) {
// Parse only icon // Parse only icon
return internalGetIconData(data, name, [], full as true); return internalGetIconData(data, name, [], full as true);

View File

@ -1,4 +1,4 @@
import type { IconifyAliases, IconifyJSON } from '@iconify/types'; import type { IconifyAliases, IconifyIcons, IconifyJSON } from '@iconify/types';
import { defaultIconDimensions } from '../icon/defaults'; import { defaultIconDimensions } from '../icon/defaults';
import { getIconsTree } from './tree'; import { getIconsTree } from './tree';
@ -17,7 +17,7 @@ export function getIcons(
names: string[], names: string[],
not_found?: boolean not_found?: boolean
): IconifyJSON | null { ): IconifyJSON | null {
const icons = Object.create(null) as IconifyJSON['icons']; const icons = Object.create(null) as IconifyIcons;
const aliases = Object.create(null) as IconifyAliases; const aliases = Object.create(null) as IconifyAliases;
const result: IconifyJSON = { const result: IconifyJSON = {
prefix: data.prefix, prefix: data.prefix,

View File

@ -1,5 +1,5 @@
import type { IconifyJSON } from '@iconify/types'; import type { IconifyJSON } from '@iconify/types';
import type { FullIconifyIcon } from '../icon/defaults'; import type { FullExtendedIconifyIcon } from '../icon/defaults';
import { internalGetIconData } from './get-icon'; import { internalGetIconData } from './get-icon';
import { getIconsTree } from './tree'; import { getIconsTree } from './tree';
@ -10,7 +10,7 @@ import { getIconsTree } from './tree';
*/ */
export type SplitIconSetCallback = ( export type SplitIconSetCallback = (
name: string, name: string,
data: FullIconifyIcon | null data: FullExtendedIconifyIcon | null
) => void; ) => void;
/** /**

View File

@ -43,7 +43,7 @@ export function getIconsTree(
} }
// Resolve only required icons // Resolve only required icons
(names || Object.keys(aliases).concat(Object.keys(icons))).forEach(resolve); (names || Object.keys(icons).concat(Object.keys(aliases))).forEach(resolve);
return resolved; return resolved;
} }

View File

@ -1,6 +1,9 @@
import type { IconifyJSON } from '@iconify/types'; import type { IconifyJSON } from '@iconify/types';
import { matchIconName } from '../icon/name'; import { matchIconName } from '../icon/name';
import { defaultIconDimensions, defaultIconProps } from '../icon/defaults'; import {
defaultIconDimensions,
defaultExtendedIconProps,
} from '../icon/defaults';
type PropsList = Record<string, unknown>; type PropsList = Record<string, unknown>;
@ -65,7 +68,10 @@ export function quicklyValidateIconSet(obj: unknown): IconifyJSON | null {
if ( if (
!name.match(matchIconName) || !name.match(matchIconName) ||
typeof icon.body !== 'string' || typeof icon.body !== 'string' ||
!checkOptionalProps(icon as unknown as PropsList, defaultIconProps) !checkOptionalProps(
icon as unknown as PropsList,
defaultExtendedIconProps
)
) { ) {
return null; return null;
} }
@ -80,7 +86,10 @@ export function quicklyValidateIconSet(obj: unknown): IconifyJSON | null {
!name.match(matchIconName) || !name.match(matchIconName) ||
typeof parent !== 'string' || typeof parent !== 'string' ||
(!icons[parent] && !aliases[parent]) || (!icons[parent] && !aliases[parent]) ||
!checkOptionalProps(icon as unknown as PropsList, defaultIconProps) !checkOptionalProps(
icon as unknown as PropsList,
defaultExtendedIconProps
)
) { ) {
return null; return null;
} }

View File

@ -4,7 +4,7 @@ import type {
IconifyOptional, IconifyOptional,
} from '@iconify/types'; } from '@iconify/types';
import { matchIconName } from '../icon/name'; import { matchIconName } from '../icon/name';
import { defaultIconProps } from '../icon/defaults'; import { defaultExtendedIconProps } from '../icon/defaults';
import { getIconsTree } from './tree'; import { getIconsTree } from './tree';
/** /**
@ -34,10 +34,9 @@ function validateIconProps(
continue; continue;
} }
const expectedType = const expectedType = typeof (
key === 'hidden' defaultExtendedIconProps as Record<string, unknown>
? 'boolean' )[attr];
: typeof (defaultIconProps as Record<string, unknown>)[attr];
if (expectedType !== 'undefined') { if (expectedType !== 'undefined') {
if (type !== expectedType) { if (type !== expectedType) {

View File

@ -3,12 +3,20 @@ import type {
IconifyTransformations, IconifyTransformations,
IconifyOptional, IconifyOptional,
IconifyIcon, IconifyIcon,
ExtendedIconifyIcon,
} from '@iconify/types'; } from '@iconify/types';
// Export icon and full icon types // Export icon and full icon types
export { IconifyIcon }; export { IconifyIcon };
export type FullIconifyIcon = Required<IconifyIcon>; export type FullIconifyIcon = Required<IconifyIcon>;
// Partial and full extended icon
export type PartialExtendedIconifyIcon = Partial<ExtendedIconifyIcon>;
type IconifyIconExtraProps = Omit<ExtendedIconifyIcon, keyof IconifyIcon>;
export type FullExtendedIconifyIcon = FullIconifyIcon & IconifyIconExtraProps;
/** /**
* Default values for dimensions * Default values for dimensions
*/ */
@ -38,3 +46,13 @@ export const defaultIconProps: Required<IconifyOptional> = Object.freeze({
...defaultIconDimensions, ...defaultIconDimensions,
...defaultIconTransformations, ...defaultIconTransformations,
}); });
/**
* Default values for all properties used in ExtendedIconifyIcon
*/
export const defaultExtendedIconProps: Required<FullExtendedIconifyIcon> =
Object.freeze({
...defaultIconProps,
body: '',
hidden: false,
});

View File

@ -1,43 +1,42 @@
import type { import type { IconifyTransformations } from '@iconify/types';
IconifyDimenisons, import {
IconifyIcon, defaultExtendedIconProps,
IconifyOptional, defaultIconTransformations,
IconifyTransformations, PartialExtendedIconifyIcon,
} from '@iconify/types'; } from './defaults';
import { defaultIconDimensions } from './defaults';
import { mergeIconTransformations } from './transformations'; import { mergeIconTransformations } from './transformations';
// Props to copy: all icon properties, except transformations
type PropsToCopy = Omit<IconifyIcon, keyof IconifyTransformations>;
const propsToMerge: Required<PropsToCopy> = {
...defaultIconDimensions,
body: '',
};
/** /**
* Merge icon and alias * Merge icon and alias
* *
* Can also be used to merge default values and icon * Can also be used to merge default values and icon
*/ */
export function mergeIconData<T extends IconifyOptional>( export function mergeIconData<T extends PartialExtendedIconifyIcon>(
parent: T, parent: T,
child: IconifyOptional | IconifyIcon, child: PartialExtendedIconifyIcon
keepOtherParentProps = true
): T { ): T {
// Merge transformations // Merge transformations and add defaults
const result = mergeIconTransformations( const result = mergeIconTransformations(parent, child);
parent,
child,
keepOtherParentProps
);
// Merge icon properties that aren't transformations // Merge icon properties that aren't transformations
for (const key in propsToMerge) { for (const key in defaultExtendedIconProps) {
const prop = key as keyof IconifyDimenisons; // Add default transformations if needed
if (child[prop] !== void 0) { if (
result[prop] = child[prop]; defaultIconTransformations[key as keyof IconifyTransformations] !==
} else if (parent[prop] !== void 0) { void 0
result[prop] = parent[prop]; ) {
if (
result[key as 'rotate'] === void 0 &&
parent[key as keyof T] !== void 0
) {
result[key as 'rotate'] =
defaultIconTransformations[key as 'rotate'];
}
// Not transformation
} else if (child[key as 'width'] !== void 0) {
result[key as 'width'] = child[key as 'width'];
} else if (parent[key as 'width'] !== void 0) {
result[key as 'width'] = parent[key as 'width'];
} }
} }

View File

@ -5,18 +5,18 @@ import type { IconifyTransformations } from '@iconify/types';
*/ */
export function mergeIconTransformations<T extends IconifyTransformations>( export function mergeIconTransformations<T extends IconifyTransformations>(
obj1: T, obj1: T,
obj2: IconifyTransformations, obj2: IconifyTransformations
keepOtherProps = true
): T { ): T {
const result = keepOtherProps ? { ...obj1 } : ({} as T); const result = {} as T;
if (obj1.hFlip || obj2.hFlip) { if (!obj1.hFlip !== !obj2.hFlip) {
result.hFlip = obj1.hFlip !== obj2.hFlip; result.hFlip = true;
} }
if (obj1.vFlip || obj2.vFlip) { if (!obj1.vFlip !== !obj2.vFlip) {
result.vFlip = obj1.vFlip !== obj2.vFlip; result.vFlip = true;
} }
if (obj1.rotate || obj2.rotate) { const rotate = ((obj1.rotate || 0) + (obj2.rotate || 0)) % 4;
result.rotate = ((obj1.rotate || 0) + (obj2.rotate || 0)) % 4; if (rotate) {
result.rotate = rotate;
} }
return result; return result;
} }

View File

@ -19,6 +19,7 @@ export {
// Icon data // Icon data
export { mergeIconData } from './icon/merge'; export { mergeIconData } from './icon/merge';
export { mergeIconTransformations } from './icon/transformations';
export { export {
defaultIconProps, defaultIconProps,
defaultIconDimensions, defaultIconDimensions,

View File

@ -160,7 +160,8 @@ export function iconToSVG(
const boxWidth = box.width; const boxWidth = box.width;
const boxHeight = box.height; const boxHeight = box.height;
let width, height; let width: string | number;
let height: string | number;
if (customisationsWidth === null) { if (customisationsWidth === null) {
// Width is not set: calculate width from height, default to '1em' // Width is not set: calculate width from height, default to '1em'
height = height =
@ -181,15 +182,11 @@ export function iconToSVG(
: customisationsHeight; : customisationsHeight;
} }
// Convert to string
width = typeof width === 'string' ? width : width.toString();
height = typeof height === 'string' ? height : height.toString();
// Result // Result
const result: IconifyIconBuildResult = { const result: IconifyIconBuildResult = {
attributes: { attributes: {
width, width: width.toString(),
height, height: height.toString(),
viewBox: viewBox:
box.left.toString() + box.left.toString() +
' ' + ' ' +

View File

@ -2,8 +2,7 @@ import type { IconifyIcon } from '@iconify/types';
import { mergeIconData } from '../lib/icon/merge'; import { mergeIconData } from '../lib/icon/merge';
describe('Testing merging icon data', () => { describe('Testing merging icon data', () => {
test('Test', () => { test('Nothing to merge', () => {
// Nothing to merge
const icon: IconifyIcon = { const icon: IconifyIcon = {
body: '<g />', body: '<g />',
}; };
@ -13,9 +12,10 @@ describe('Testing merging icon data', () => {
// Check hint manually: supposed to be IconifyIcon // Check hint manually: supposed to be IconifyIcon
const result = mergeIconData(icon, {}); const result = mergeIconData(icon, {});
expect(result).toEqual(expected); expect(result).toEqual(expected);
});
// TypeScript full icon test test('Full icons', () => {
const icon2: Required<IconifyIcon> = { const icon: Required<IconifyIcon> = {
body: '<g />', body: '<g />',
width: 24, width: 24,
height: 24, height: 24,
@ -25,7 +25,7 @@ describe('Testing merging icon data', () => {
hFlip: false, hFlip: false,
vFlip: false, vFlip: false,
}; };
const expected2: Required<IconifyIcon> = { const expected: Required<IconifyIcon> = {
body: '<g />', body: '<g />',
width: 24, width: 24,
height: 24, height: 24,
@ -36,9 +36,11 @@ describe('Testing merging icon data', () => {
vFlip: false, vFlip: false,
}; };
// Check hint manually: supposed to be Required<IconifyIcon> // Check hint manually: supposed to be Required<IconifyIcon>
const result2 = mergeIconData(icon2, {}); const result = mergeIconData(icon, {});
expect(result2).toEqual(expected2); expect(result).toEqual(expected);
});
test('Copy values', () => {
// Copy values // Copy values
expect( expect(
mergeIconData( mergeIconData(
@ -55,8 +57,9 @@ describe('Testing merging icon data', () => {
width: 24, width: 24,
height: 32, height: 32,
}); });
});
// Override values test('Override values', () => {
expect( expect(
mergeIconData( mergeIconData(
{ {
@ -74,4 +77,30 @@ describe('Testing merging icon data', () => {
height: 32, height: 32,
}); });
}); });
test('Override transformations', () => {
expect(
mergeIconData(
{
body: '<g />',
width: 24,
height: 24,
hFlip: true,
rotate: 3,
},
{
height: 32,
vFlip: true,
rotate: 2,
}
)
).toEqual({
body: '<g />',
width: 24,
height: 32,
hFlip: true,
vFlip: true,
rotate: 1,
});
});
}); });

View File

@ -4,7 +4,6 @@ import type { FullIconifyIcon } from '../lib/icon/defaults';
import { defaultIconProps } from '../lib/icon/defaults'; import { defaultIconProps } from '../lib/icon/defaults';
import type { FullIconCustomisations } from '../lib/customisations/defaults'; import type { FullIconCustomisations } from '../lib/customisations/defaults';
import { defaultIconCustomisations } from '../lib/customisations/defaults'; import { defaultIconCustomisations } from '../lib/customisations/defaults';
import { mergeCustomisations } from '../lib/customisations/merge';
import { iconToHTML } from '../lib/svg/html'; import { iconToHTML } from '../lib/svg/html';
describe('Testing iconToSVG', () => { describe('Testing iconToSVG', () => {
@ -31,12 +30,10 @@ describe('Testing iconToSVG', () => {
}); });
test('Auto size, body', () => { test('Auto size, body', () => {
const custom: FullIconCustomisations = mergeCustomisations( const custom: FullIconCustomisations = {
defaultIconCustomisations, ...defaultIconCustomisations,
{
height: 'auto', height: 'auto',
} };
);
const icon: FullIconifyIcon = { const icon: FullIconifyIcon = {
...defaultIconProps, ...defaultIconProps,
body: '<path d="" />', body: '<path d="" />',
@ -66,12 +63,10 @@ describe('Testing iconToSVG', () => {
}); });
test('Auto size, body', () => { test('Auto size, body', () => {
const custom: FullIconCustomisations = mergeCustomisations( const custom: FullIconCustomisations = {
defaultIconCustomisations, ...defaultIconCustomisations,
{
height: 'auto', height: 'auto',
} };
);
const icon: FullIconifyIcon = { const icon: FullIconifyIcon = {
...defaultIconProps, ...defaultIconProps,
body: '<path d="" />', body: '<path d="" />',
@ -90,12 +85,10 @@ describe('Testing iconToSVG', () => {
}); });
test('Custom size', () => { test('Custom size', () => {
const custom: FullIconCustomisations = mergeCustomisations( const custom: FullIconCustomisations = {
defaultIconCustomisations, ...defaultIconCustomisations,
{
height: 'auto', height: 'auto',
} };
);
const icon: FullIconifyIcon = { const icon: FullIconifyIcon = {
...defaultIconProps, ...defaultIconProps,
width: 20, width: 20,
@ -116,13 +109,11 @@ describe('Testing iconToSVG', () => {
}); });
test('Rotation', () => { test('Rotation', () => {
const custom: FullIconCustomisations = mergeCustomisations( const custom: FullIconCustomisations = {
defaultIconCustomisations, ...defaultIconCustomisations,
{
height: '40px', height: '40px',
rotate: 1, rotate: 1,
} };
);
const icon: FullIconifyIcon = { const icon: FullIconifyIcon = {
...defaultIconProps, ...defaultIconProps,
width: 20, width: 20,
@ -143,13 +134,11 @@ describe('Testing iconToSVG', () => {
}); });
test('Negative rotation', () => { test('Negative rotation', () => {
const custom: FullIconCustomisations = mergeCustomisations( const custom: FullIconCustomisations = {
defaultIconCustomisations, ...defaultIconCustomisations,
{
height: '40px', height: '40px',
rotate: -1, rotate: -1,
} };
);
const icon: FullIconifyIcon = { const icon: FullIconifyIcon = {
...defaultIconProps, ...defaultIconProps,
width: 20, width: 20,
@ -170,13 +159,11 @@ describe('Testing iconToSVG', () => {
}); });
test('Flip', () => { test('Flip', () => {
const custom: FullIconCustomisations = mergeCustomisations( const custom: FullIconCustomisations = {
defaultIconCustomisations, ...defaultIconCustomisations,
{
height: '32', height: '32',
hFlip: true, hFlip: true,
} };
);
const icon: FullIconifyIcon = { const icon: FullIconifyIcon = {
...defaultIconProps, ...defaultIconProps,
width: 20, width: 20,
@ -197,13 +184,11 @@ describe('Testing iconToSVG', () => {
}); });
test('Flip, rotation', () => { test('Flip, rotation', () => {
const custom: FullIconCustomisations = mergeCustomisations( const custom: FullIconCustomisations = {
defaultIconCustomisations, ...defaultIconCustomisations,
{
hFlip: true, hFlip: true,
rotate: 1, rotate: 1,
} };
);
const icon: FullIconifyIcon = { const icon: FullIconifyIcon = {
...defaultIconProps, ...defaultIconProps,
width: 20, width: 20,
@ -224,12 +209,10 @@ describe('Testing iconToSVG', () => {
}); });
test('Flip icon that is rotated by default', () => { test('Flip icon that is rotated by default', () => {
const custom: FullIconCustomisations = mergeCustomisations( const custom: FullIconCustomisations = {
defaultIconCustomisations, ...defaultIconCustomisations,
{
hFlip: true, hFlip: true,
} };
);
const icon: FullIconifyIcon = { const icon: FullIconifyIcon = {
...defaultIconProps, ...defaultIconProps,
width: 20, width: 20,
@ -254,16 +237,14 @@ describe('Testing iconToSVG', () => {
}); });
test('Flip and rotation canceling eachother', () => { test('Flip and rotation canceling eachother', () => {
const custom: FullIconCustomisations = mergeCustomisations( const custom: FullIconCustomisations = {
defaultIconCustomisations, ...defaultIconCustomisations,
{
width: '1em', width: '1em',
height: 'auto', height: 'auto',
hFlip: true, hFlip: true,
vFlip: true, vFlip: true,
rotate: 2, rotate: 2,
} };
);
const icon: FullIconifyIcon = { const icon: FullIconifyIcon = {
...defaultIconProps, ...defaultIconProps,
width: 20, width: 20,
@ -287,10 +268,7 @@ describe('Testing iconToSVG', () => {
const iconBody = const iconBody =
'<g stroke="currentColor" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" fill="none" fill-rule="evenodd"><path d="M40 64l48-48" class="animation-delay-0 animation-duration-10 animate-stroke stroke-length-102"/><path d="M40 64l48 48" class="animation-delay-0 animation-duration-10 animate-stroke stroke-length-102"/></g>'; '<g stroke="currentColor" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" fill="none" fill-rule="evenodd"><path d="M40 64l48-48" class="animation-delay-0 animation-duration-10 animate-stroke stroke-length-102"/><path d="M40 64l48 48" class="animation-delay-0 animation-duration-10 animate-stroke stroke-length-102"/></g>';
const custom: FullIconCustomisations = mergeCustomisations( const custom: FullIconCustomisations = defaultIconCustomisations;
defaultIconCustomisations,
{}
);
const icon: FullIconifyIcon = { const icon: FullIconifyIcon = {
...defaultIconProps, ...defaultIconProps,
body: iconBody, body: iconBody,