mirror of
https://github.com/iconify/iconify.git
synced 2025-01-06 07:20:40 +00:00
Rewite icon set functions to allow deep aliases tree
This commit is contained in:
parent
e6336fe3da
commit
89a2def126
@ -11,10 +11,11 @@ import {
|
||||
*/
|
||||
export function mergeCustomisations<T extends FullIconCustomisations>(
|
||||
defaults: T,
|
||||
item: IconifyIconCustomisations
|
||||
item: IconifyIconCustomisations,
|
||||
keepOtherProps = true
|
||||
): T {
|
||||
// Merge transformations
|
||||
const result = mergeIconTransformations(defaults, item);
|
||||
const result = mergeIconTransformations(defaults, item, keepOtherProps);
|
||||
|
||||
// Merge dimensions
|
||||
for (const key in defaultIconSizeCustomisations) {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import type { IconifyDimenisons, IconifyJSON } from '@iconify/types';
|
||||
import { defaultIconProps, defaultIconDimensions } from '../icon/defaults';
|
||||
import type { IconifyJSON } from '@iconify/types';
|
||||
import { defaultIconProps } from '../icon/defaults';
|
||||
import type { IconifyIcon, FullIconifyIcon } from '../icon/defaults';
|
||||
import { mergeIconData } from '../icon/merge';
|
||||
import { getIconsTree } from './tree';
|
||||
|
||||
/**
|
||||
* Get data for icon
|
||||
@ -21,54 +22,43 @@ export function getIconData(
|
||||
name: string,
|
||||
full = false
|
||||
): FullIconifyIcon | IconifyIcon | null {
|
||||
function getIcon(name: string, iteration: number): IconifyIcon | null {
|
||||
if (data.icons[name] !== void 0) {
|
||||
// Return icon
|
||||
return Object.assign({}, data.icons[name]);
|
||||
}
|
||||
const icons = data.icons;
|
||||
const aliases = data.aliases || {};
|
||||
|
||||
// Check loop
|
||||
if (iteration > 5) {
|
||||
let currentProps = {} as IconifyIcon;
|
||||
|
||||
// Parse parent item
|
||||
function parse(name: string) {
|
||||
currentProps = mergeIconData(
|
||||
icons[name] || aliases[name],
|
||||
currentProps,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
const icon = icons[name];
|
||||
if (icon) {
|
||||
// Parse only icon
|
||||
parse(name);
|
||||
} else {
|
||||
// Resolve tree
|
||||
const tree = getIconsTree(data, [name])[name];
|
||||
if (!tree) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if alias exists
|
||||
const aliases = data.aliases;
|
||||
if (aliases && aliases[name] !== void 0) {
|
||||
const item = aliases[name];
|
||||
const result = getIcon(item.parent, iteration + 1);
|
||||
if (result) {
|
||||
return mergeIconData(result, item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if character exists
|
||||
const chars = data.chars;
|
||||
if (!iteration && chars && chars[name] !== void 0) {
|
||||
return getIcon(chars[name], iteration + 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
parse(name);
|
||||
tree.forEach(parse);
|
||||
}
|
||||
|
||||
const result = getIcon(name, 0);
|
||||
|
||||
// Add default properties
|
||||
if (result) {
|
||||
for (const key in defaultIconDimensions) {
|
||||
if (
|
||||
result[key as keyof IconifyDimenisons] === void 0 &&
|
||||
data[key as keyof IconifyDimenisons] !== void 0
|
||||
) {
|
||||
(result as unknown as Record<string, unknown>)[key] =
|
||||
data[key as keyof IconifyDimenisons];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add default values
|
||||
currentProps = mergeIconData(
|
||||
data,
|
||||
currentProps,
|
||||
false
|
||||
) as unknown as IconifyIcon;
|
||||
|
||||
// Return icon
|
||||
return result && full
|
||||
? Object.assign({}, defaultIconProps, result)
|
||||
: result;
|
||||
return full
|
||||
? Object.assign({}, defaultIconProps, currentProps)
|
||||
: currentProps;
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import type { IconifyJSON } from '@iconify/types';
|
||||
import { defaultIconProps } from '../icon/defaults';
|
||||
import type { IconifyAliases, IconifyJSON } from '@iconify/types';
|
||||
import { defaultIconDimensions } from '../icon/defaults';
|
||||
import { getIconsTree } from './tree';
|
||||
|
||||
/**
|
||||
* Optional properties that must be copied when copying icon set
|
||||
*/
|
||||
export const propsToCopy = Object.keys(defaultIconProps).concat([
|
||||
export const propsToCopy = Object.keys(defaultIconDimensions).concat([
|
||||
'provider',
|
||||
]) as (keyof IconifyJSON)[];
|
||||
|
||||
@ -14,61 +14,43 @@ export const propsToCopy = Object.keys(defaultIconProps).concat([
|
||||
*/
|
||||
export function getIcons(
|
||||
data: IconifyJSON,
|
||||
icons: string[],
|
||||
names: string[],
|
||||
not_found?: boolean
|
||||
): IconifyJSON | null {
|
||||
const icons = Object.create(null) as IconifyJSON['icons'];
|
||||
const aliases = Object.create(null) as IconifyAliases;
|
||||
const result: IconifyJSON = {
|
||||
prefix: data.prefix,
|
||||
icons: Object.create(null) as never,
|
||||
icons,
|
||||
};
|
||||
const tested: Set<string> = new Set();
|
||||
|
||||
const sourceIcons = data.icons;
|
||||
const sourceAliases = data.aliases || {};
|
||||
|
||||
const tree = getIconsTree(data, names);
|
||||
let empty = true;
|
||||
|
||||
function copy(name: string, iteration: number): boolean {
|
||||
if (iteration > 5 || tested.has(name)) {
|
||||
// Already copied or too much nesting
|
||||
return true;
|
||||
}
|
||||
tested.add(name);
|
||||
|
||||
// Check for icon
|
||||
if (data.icons[name] !== void 0) {
|
||||
// Copy all icons
|
||||
for (const name in tree) {
|
||||
if (!tree[name]) {
|
||||
// Failed
|
||||
if (not_found && names.indexOf(name) !== -1) {
|
||||
// Add to not_found
|
||||
(result.not_found || (result.not_found = [])).push(name);
|
||||
}
|
||||
} else if (sourceIcons[name]) {
|
||||
// Icon
|
||||
icons[name] = {
|
||||
...sourceIcons[name],
|
||||
};
|
||||
empty = false;
|
||||
result.icons[name] = { ...data.icons[name] };
|
||||
return true;
|
||||
} else {
|
||||
// Alias
|
||||
aliases[name] = {
|
||||
...sourceAliases[name],
|
||||
};
|
||||
result.aliases = aliases;
|
||||
}
|
||||
|
||||
// Check for alias
|
||||
const aliases = data.aliases;
|
||||
if (aliases && aliases[name] !== void 0) {
|
||||
const copied = copy(aliases[name].parent, iteration + 1);
|
||||
if (copied) {
|
||||
if (result.aliases === void 0) {
|
||||
result.aliases = Object.create(null) as never;
|
||||
}
|
||||
result.aliases[name] = { ...aliases[name] };
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
// Check for character, return as alias
|
||||
const chars = data.chars;
|
||||
if (chars && chars[name] !== void 0) {
|
||||
const parent = chars[name];
|
||||
const copied = copy(parent, iteration + 1);
|
||||
if (copied) {
|
||||
if (result.aliases === void 0) {
|
||||
result.aliases = Object.create(null) as never;
|
||||
}
|
||||
result.aliases[name] = {
|
||||
parent,
|
||||
};
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
// Not found
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy common properties
|
||||
@ -78,15 +60,5 @@ export function getIcons(
|
||||
}
|
||||
});
|
||||
|
||||
// Copy all icons
|
||||
icons.forEach((name) => {
|
||||
if (!copy(name, 0) && not_found === true) {
|
||||
if (result.not_found === void 0) {
|
||||
result.not_found = [];
|
||||
}
|
||||
result.not_found.push(name);
|
||||
}
|
||||
});
|
||||
|
||||
return empty && not_found !== true ? null : result;
|
||||
}
|
||||
|
@ -11,18 +11,20 @@ export type ParentIconsTree = Record<string, ParentIconsList | null>;
|
||||
*
|
||||
* Returns parent icon for each icon
|
||||
*/
|
||||
export function getIconsTree(data: IconifyJSON): ParentIconsTree {
|
||||
const resolved = Object.create(null) as ParentIconsTree;
|
||||
|
||||
// Add all icons
|
||||
for (const key in data.icons) {
|
||||
resolved[key] = [];
|
||||
}
|
||||
|
||||
// Add all aliases
|
||||
export function getIconsTree(
|
||||
data: IconifyJSON,
|
||||
names?: string[]
|
||||
): ParentIconsTree {
|
||||
const icons = data.icons;
|
||||
const aliases = data.aliases || {};
|
||||
|
||||
function resolveAlias(name: string): ParentIconsList | null {
|
||||
const resolved = Object.create(null) as ParentIconsTree;
|
||||
|
||||
function resolve(name: string): ParentIconsList | null {
|
||||
if (icons[name]) {
|
||||
return (resolved[name] = []);
|
||||
}
|
||||
|
||||
if (resolved[name] === void 0) {
|
||||
// Mark as failed if parent alias points to this icon to avoid infinite loop
|
||||
resolved[name] = null;
|
||||
@ -31,7 +33,7 @@ export function getIconsTree(data: IconifyJSON): ParentIconsTree {
|
||||
const parent = aliases[name] && aliases[name].parent;
|
||||
|
||||
// Get value for parent
|
||||
const value = parent && resolveAlias(parent);
|
||||
const value = parent && resolve(parent);
|
||||
if (value) {
|
||||
resolved[name] = [parent].concat(value);
|
||||
}
|
||||
@ -40,9 +42,8 @@ export function getIconsTree(data: IconifyJSON): ParentIconsTree {
|
||||
return resolved[name];
|
||||
}
|
||||
|
||||
for (const name in aliases) {
|
||||
resolveAlias(name);
|
||||
}
|
||||
// Resolve only required icons
|
||||
(names || Object.keys(aliases).concat(Object.keys(icons))).forEach(resolve);
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
@ -1,22 +1,43 @@
|
||||
import type { IconifyDimenisons, IconifyOptional } from '@iconify/types';
|
||||
import type {
|
||||
IconifyDimenisons,
|
||||
IconifyIcon,
|
||||
IconifyOptional,
|
||||
IconifyTransformations,
|
||||
} from '@iconify/types';
|
||||
import { defaultIconDimensions } from './defaults';
|
||||
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
|
||||
*
|
||||
* Can also be used to merge default values and icon
|
||||
*/
|
||||
export function mergeIconData<T extends IconifyOptional>(
|
||||
icon: T,
|
||||
alias: IconifyOptional
|
||||
parent: T,
|
||||
child: IconifyOptional | IconifyIcon,
|
||||
keepOtherParentProps = true
|
||||
): T {
|
||||
// Merge transformations, while keeping other props
|
||||
const result = mergeIconTransformations(icon, alias);
|
||||
// Merge transformations
|
||||
const result = mergeIconTransformations(
|
||||
parent,
|
||||
child,
|
||||
keepOtherParentProps
|
||||
);
|
||||
|
||||
// Merge dimensions
|
||||
for (const key in defaultIconDimensions) {
|
||||
// Merge icon properties that aren't transformations
|
||||
for (const key in propsToMerge) {
|
||||
const prop = key as keyof IconifyDimenisons;
|
||||
if (alias[prop] !== void 0) {
|
||||
result[prop] = alias[prop];
|
||||
if (child[prop] !== void 0) {
|
||||
result[prop] = child[prop];
|
||||
} else if (parent[prop] !== void 0) {
|
||||
result[prop] = parent[prop];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,22 @@
|
||||
import type { IconifyTransformations } from '@iconify/types';
|
||||
|
||||
/**
|
||||
* Merge transformations. Also copies other properties from first parameter
|
||||
* Merge transformations
|
||||
*/
|
||||
export function mergeIconTransformations<T extends IconifyTransformations>(
|
||||
obj1: T,
|
||||
obj2: IconifyTransformations
|
||||
obj2: IconifyTransformations,
|
||||
keepOtherProps = true
|
||||
): T {
|
||||
const result = {
|
||||
...obj1,
|
||||
};
|
||||
if (obj2.hFlip) {
|
||||
result.hFlip = !result.hFlip;
|
||||
const result = keepOtherProps ? { ...obj1 } : ({} as T);
|
||||
if (obj1.hFlip || obj2.hFlip) {
|
||||
result.hFlip = obj1.hFlip !== obj2.hFlip;
|
||||
}
|
||||
if (obj2.vFlip) {
|
||||
result.vFlip = !result.vFlip;
|
||||
if (obj1.vFlip || obj2.vFlip) {
|
||||
result.vFlip = obj1.vFlip !== obj2.vFlip;
|
||||
}
|
||||
if (obj2.rotate) {
|
||||
result.rotate = ((result.rotate || 0) + obj2.rotate) % 4;
|
||||
if (obj1.rotate || obj2.rotate) {
|
||||
result.rotate = ((obj1.rotate || 0) + (obj2.rotate || 0)) % 4;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -33,3 +33,20 @@ export function unmergeObjects<T extends Record<string, unknown>>(
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get common properties in 2 objects
|
||||
*/
|
||||
export function commonObjectProps<T extends Record<string, unknown>>(
|
||||
item: unknown,
|
||||
reference: T
|
||||
): Partial<T> {
|
||||
const result = {} as T;
|
||||
for (const key in reference) {
|
||||
const value = (item as T)[key];
|
||||
if (value !== void 0) {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -137,38 +137,10 @@ describe('Testing retrieving icons from icon set', () => {
|
||||
});
|
||||
|
||||
// Character
|
||||
expect(getIcons(data, ['f00'])).toEqual({
|
||||
prefix: 'foo',
|
||||
icons: {
|
||||
bar2: {
|
||||
body: '<g />',
|
||||
},
|
||||
},
|
||||
aliases: {
|
||||
f00: {
|
||||
parent: 'bar2',
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(getIcons(data, ['f00'])).toBeNull();
|
||||
|
||||
// Character that points to alias
|
||||
expect(getIcons(data, ['f02'])).toEqual({
|
||||
prefix: 'foo',
|
||||
icons: {
|
||||
bar: {
|
||||
body: '<g />',
|
||||
},
|
||||
},
|
||||
aliases: {
|
||||
f02: {
|
||||
parent: 'foo',
|
||||
},
|
||||
foo: {
|
||||
parent: 'bar',
|
||||
hFlip: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(getIcons(data, ['f02'])).toBeNull();
|
||||
|
||||
// Bad character
|
||||
expect(getIcons(data, ['f04'])).toBeNull();
|
||||
|
@ -173,6 +173,7 @@ describe('Testing parsing icon set', () => {
|
||||
'alias2z4',
|
||||
'alias2z5',
|
||||
'alias2z6',
|
||||
'alias2z7',
|
||||
];
|
||||
const namesCopy = names.slice(0);
|
||||
|
||||
@ -275,6 +276,17 @@ describe('Testing parsing icon set', () => {
|
||||
vFlip: true,
|
||||
rotate: 1,
|
||||
},
|
||||
alias2z7: {
|
||||
// alias of alias2z6
|
||||
body: '<path d="icon2" />',
|
||||
width: 21,
|
||||
height: 24,
|
||||
top: 0,
|
||||
left: 0,
|
||||
hFlip: false,
|
||||
vFlip: true,
|
||||
rotate: 1,
|
||||
},
|
||||
};
|
||||
|
||||
// Do stuff
|
||||
@ -333,7 +345,6 @@ describe('Testing parsing icon set', () => {
|
||||
},
|
||||
alias2z7: {
|
||||
// 7 parents: alias2z6, alias2z5, alias2z4, alias2z3, alias2z, alias2f, icon2
|
||||
// nesting is too deep and should not be parsed
|
||||
parent: 'alias2z6',
|
||||
},
|
||||
alias3: {
|
||||
|
Loading…
Reference in New Issue
Block a user