mirror of
https://github.com/iconify/iconify.git
synced 2025-01-08 15:54:09 +00:00
In IconifyJSON do not allow default transformations, remove inline from customisations, restructure utils
This commit is contained in:
parent
99ddeeae47
commit
ad29f6df20
7
packages/types/types.d.ts
vendored
7
packages/types/types.d.ts
vendored
@ -30,7 +30,6 @@ export interface IconifyDimenisons {
|
|||||||
* Used in:
|
* Used in:
|
||||||
* icon (as is)
|
* icon (as is)
|
||||||
* alias (merged with icon's properties)
|
* alias (merged with icon's properties)
|
||||||
* root of JSON file (default values)
|
|
||||||
*/
|
*/
|
||||||
export interface IconifyTransformations {
|
export interface IconifyTransformations {
|
||||||
// Number of 90 degrees rotations.
|
// Number of 90 degrees rotations.
|
||||||
@ -223,7 +222,7 @@ export interface IconifyMetaData {
|
|||||||
/**
|
/**
|
||||||
* JSON structure, contains only icon data
|
* JSON structure, contains only icon data
|
||||||
*/
|
*/
|
||||||
export interface IconifyJSONIconsData extends IconifyOptional {
|
export interface IconifyJSONIconsData extends IconifyDimenisons {
|
||||||
// Prefix for icons in JSON file, required.
|
// Prefix for icons in JSON file, required.
|
||||||
prefix: string;
|
prefix: string;
|
||||||
|
|
||||||
@ -236,8 +235,8 @@ export interface IconifyJSONIconsData extends IconifyOptional {
|
|||||||
// Optional aliases.
|
// Optional aliases.
|
||||||
aliases?: IconifyAliases;
|
aliases?: IconifyAliases;
|
||||||
|
|
||||||
// IconifyOptional properties that are used as default values for icons when icon is missing value.
|
// IconifyDimenisons properties that are used as default viewbox for icons when icon is missing value.
|
||||||
// If property exists in both icon and root, use value from icon.
|
// If viewbox exists in both icon and root, use value from icon.
|
||||||
// This is used to reduce duplication.
|
// This is used to reduce duplication.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,21 +55,17 @@
|
|||||||
"require": "./lib/customisations/bool.cjs",
|
"require": "./lib/customisations/bool.cjs",
|
||||||
"import": "./lib/customisations/bool.mjs"
|
"import": "./lib/customisations/bool.mjs"
|
||||||
},
|
},
|
||||||
"./lib/customisations/compare": {
|
"./lib/customisations/defaults": {
|
||||||
"require": "./lib/customisations/compare.cjs",
|
"require": "./lib/customisations/defaults.cjs",
|
||||||
"import": "./lib/customisations/compare.mjs"
|
"import": "./lib/customisations/defaults.mjs"
|
||||||
},
|
|
||||||
"./lib/customisations": {
|
|
||||||
"require": "./lib/customisations/index.cjs",
|
|
||||||
"import": "./lib/customisations/index.mjs"
|
|
||||||
},
|
},
|
||||||
"./lib/customisations/flip": {
|
"./lib/customisations/flip": {
|
||||||
"require": "./lib/customisations/flip.cjs",
|
"require": "./lib/customisations/flip.cjs",
|
||||||
"import": "./lib/customisations/flip.mjs"
|
"import": "./lib/customisations/flip.mjs"
|
||||||
},
|
},
|
||||||
"./lib/customisations/index": {
|
"./lib/customisations/merge": {
|
||||||
"require": "./lib/customisations/index.cjs",
|
"require": "./lib/customisations/merge.cjs",
|
||||||
"import": "./lib/customisations/index.mjs"
|
"import": "./lib/customisations/merge.mjs"
|
||||||
},
|
},
|
||||||
"./lib/customisations/rotate": {
|
"./lib/customisations/rotate": {
|
||||||
"require": "./lib/customisations/rotate.cjs",
|
"require": "./lib/customisations/rotate.cjs",
|
||||||
@ -107,13 +103,9 @@
|
|||||||
"require": "./lib/icon-set/validate-basic.cjs",
|
"require": "./lib/icon-set/validate-basic.cjs",
|
||||||
"import": "./lib/icon-set/validate-basic.mjs"
|
"import": "./lib/icon-set/validate-basic.mjs"
|
||||||
},
|
},
|
||||||
"./lib/icon": {
|
"./lib/icon/defaults": {
|
||||||
"require": "./lib/icon/index.cjs",
|
"require": "./lib/icon/defaults.cjs",
|
||||||
"import": "./lib/icon/index.mjs"
|
"import": "./lib/icon/defaults.mjs"
|
||||||
},
|
|
||||||
"./lib/icon/index": {
|
|
||||||
"require": "./lib/icon/index.cjs",
|
|
||||||
"import": "./lib/icon/index.mjs"
|
|
||||||
},
|
},
|
||||||
"./lib/icon/merge": {
|
"./lib/icon/merge": {
|
||||||
"require": "./lib/icon/merge.cjs",
|
"require": "./lib/icon/merge.cjs",
|
||||||
@ -123,6 +115,10 @@
|
|||||||
"require": "./lib/icon/name.cjs",
|
"require": "./lib/icon/name.cjs",
|
||||||
"import": "./lib/icon/name.mjs"
|
"import": "./lib/icon/name.mjs"
|
||||||
},
|
},
|
||||||
|
"./lib/icon/transformations": {
|
||||||
|
"require": "./lib/icon/transformations.cjs",
|
||||||
|
"import": "./lib/icon/transformations.mjs"
|
||||||
|
},
|
||||||
"./lib": {
|
"./lib": {
|
||||||
"require": "./lib/index.cjs",
|
"require": "./lib/index.cjs",
|
||||||
"import": "./lib/index.mjs"
|
"import": "./lib/index.mjs"
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
import type { FullIconCustomisations } from './index';
|
|
||||||
import { defaults } from './index';
|
|
||||||
|
|
||||||
// Get all keys
|
|
||||||
const allKeys: (keyof FullIconCustomisations)[] = Object.keys(
|
|
||||||
defaults
|
|
||||||
) as (keyof FullIconCustomisations)[];
|
|
||||||
|
|
||||||
// All keys without width/height
|
|
||||||
const filteredKeys = allKeys.filter(
|
|
||||||
(key) => key !== 'width' && key !== 'height'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare sets of cusotmisations, return false if they are different, true if the same
|
|
||||||
*
|
|
||||||
* If dimensions are derived from props1 or props2, do not compare them.
|
|
||||||
*/
|
|
||||||
export function compare(
|
|
||||||
item1: FullIconCustomisations,
|
|
||||||
item2: FullIconCustomisations,
|
|
||||||
compareDimensions = true
|
|
||||||
): boolean {
|
|
||||||
const keys = compareDimensions ? allKeys : filteredKeys;
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
const key = keys[i];
|
|
||||||
if (item1[key] !== item2[key]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
41
packages/utils/src/customisations/defaults.ts
Normal file
41
packages/utils/src/customisations/defaults.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import type { IconifyTransformations } from '@iconify/types';
|
||||||
|
import { defaultIconTransformations } from '../icon/defaults';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon size
|
||||||
|
*/
|
||||||
|
export type IconifyIconSize = null | string | number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dimensions
|
||||||
|
*/
|
||||||
|
export interface IconifyIconSizeCustomisations {
|
||||||
|
width?: IconifyIconSize;
|
||||||
|
height?: IconifyIconSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon customisations
|
||||||
|
*/
|
||||||
|
export interface IconifyIconCustomisations
|
||||||
|
extends IconifyTransformations,
|
||||||
|
IconifyIconSizeCustomisations {}
|
||||||
|
|
||||||
|
export type FullIconCustomisations = Required<IconifyIconCustomisations>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default icon customisations values
|
||||||
|
*/
|
||||||
|
export const defaultIconSizeCustomisations: Required<IconifyIconSizeCustomisations> =
|
||||||
|
Object.freeze({
|
||||||
|
width: null,
|
||||||
|
height: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const defaultIconCustomisations: FullIconCustomisations = Object.freeze({
|
||||||
|
// Dimensions
|
||||||
|
...defaultIconSizeCustomisations,
|
||||||
|
|
||||||
|
// Transformations
|
||||||
|
...defaultIconTransformations,
|
||||||
|
});
|
@ -1,4 +1,4 @@
|
|||||||
import type { IconifyIconCustomisations } from './index';
|
import type { IconifyIconCustomisations } from './defaults';
|
||||||
|
|
||||||
const separator = /[\s,]+/;
|
const separator = /[\s,]+/;
|
||||||
|
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
/**
|
|
||||||
* Icon size
|
|
||||||
*/
|
|
||||||
export type IconifyIconSize = null | string | number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Icon customisations
|
|
||||||
*/
|
|
||||||
export interface IconifyIconCustomisations {
|
|
||||||
// Display mode
|
|
||||||
inline?: boolean;
|
|
||||||
|
|
||||||
// Dimensions
|
|
||||||
width?: IconifyIconSize;
|
|
||||||
height?: IconifyIconSize;
|
|
||||||
|
|
||||||
// Transformations
|
|
||||||
hFlip?: boolean;
|
|
||||||
vFlip?: boolean;
|
|
||||||
rotate?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FullIconCustomisations = Required<IconifyIconCustomisations>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default icon customisations values
|
|
||||||
*/
|
|
||||||
export const defaults: FullIconCustomisations = Object.freeze({
|
|
||||||
// Display mode
|
|
||||||
inline: false,
|
|
||||||
|
|
||||||
// Dimensions
|
|
||||||
width: null,
|
|
||||||
height: null,
|
|
||||||
|
|
||||||
// Transformations
|
|
||||||
hFlip: false,
|
|
||||||
vFlip: false,
|
|
||||||
rotate: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TypeScript
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
function assertNever(v: never) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert IconifyIconCustomisations to FullIconCustomisations
|
|
||||||
*/
|
|
||||||
export function mergeCustomisations(
|
|
||||||
defaults: FullIconCustomisations,
|
|
||||||
item: IconifyIconCustomisations
|
|
||||||
): FullIconCustomisations {
|
|
||||||
const result: FullIconCustomisations = {} as FullIconCustomisations;
|
|
||||||
for (const key in defaults) {
|
|
||||||
const attr = key as keyof FullIconCustomisations;
|
|
||||||
|
|
||||||
// Copy old value
|
|
||||||
(result as Record<string, unknown>)[attr] = defaults[attr];
|
|
||||||
|
|
||||||
if (item[attr] === void 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate new value
|
|
||||||
const value = item[attr];
|
|
||||||
switch (attr) {
|
|
||||||
// Boolean attributes that override old value
|
|
||||||
case 'inline':
|
|
||||||
if (typeof value === 'boolean') {
|
|
||||||
result[attr] = value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Boolean attributes that are merged
|
|
||||||
case 'hFlip':
|
|
||||||
case 'vFlip':
|
|
||||||
if (value === true) {
|
|
||||||
result[attr] = !result[attr];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Non-empty string / non-zero number / null
|
|
||||||
case 'width':
|
|
||||||
case 'height':
|
|
||||||
if (
|
|
||||||
(typeof value === 'string' && value !== '') ||
|
|
||||||
(typeof value === 'number' && value) ||
|
|
||||||
value === null
|
|
||||||
) {
|
|
||||||
result[attr] = value as IconifyIconSize;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Rotation
|
|
||||||
case 'rotate':
|
|
||||||
if (typeof value === 'number') {
|
|
||||||
result[attr] += value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertNever(attr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
36
packages/utils/src/customisations/merge.ts
Normal file
36
packages/utils/src/customisations/merge.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { mergeIconTransformations } from '../icon/transformations';
|
||||||
|
import {
|
||||||
|
defaultIconSizeCustomisations,
|
||||||
|
FullIconCustomisations,
|
||||||
|
IconifyIconCustomisations,
|
||||||
|
IconifyIconSizeCustomisations,
|
||||||
|
} from './defaults';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert IconifyIconCustomisations to FullIconCustomisations
|
||||||
|
*/
|
||||||
|
export function mergeCustomisations(
|
||||||
|
defaults: FullIconCustomisations,
|
||||||
|
item: IconifyIconCustomisations
|
||||||
|
): FullIconCustomisations {
|
||||||
|
// Merge transformations
|
||||||
|
const result = mergeIconTransformations(defaults, item);
|
||||||
|
|
||||||
|
// Merge dimensions
|
||||||
|
for (const key in defaultIconSizeCustomisations) {
|
||||||
|
const attr = key as keyof IconifyIconSizeCustomisations;
|
||||||
|
|
||||||
|
const value = item[attr];
|
||||||
|
const valueType = typeof value;
|
||||||
|
if (
|
||||||
|
value === null ||
|
||||||
|
(value && (valueType === 'string' || valueType === 'number'))
|
||||||
|
) {
|
||||||
|
result[attr] = value;
|
||||||
|
} else {
|
||||||
|
(result as Record<string, unknown>)[attr] = defaults[attr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import type { IconifyJSON } from '@iconify/types';
|
import type { IconifyJSON } from '@iconify/types';
|
||||||
import { iconDefaults } from '../icon';
|
import { defaultIconDimensions } from '../icon/defaults';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expand minified icon set
|
* Expand minified icon set
|
||||||
@ -9,9 +9,12 @@ import { iconDefaults } from '../icon';
|
|||||||
export function expandIconSet(data: IconifyJSON): void {
|
export function expandIconSet(data: IconifyJSON): void {
|
||||||
const icons = Object.keys(data.icons);
|
const icons = Object.keys(data.icons);
|
||||||
|
|
||||||
(Object.keys(iconDefaults) as (keyof typeof iconDefaults)[]).forEach(
|
(
|
||||||
(prop) => {
|
Object.keys(
|
||||||
if (typeof data[prop] !== typeof iconDefaults[prop]) {
|
defaultIconDimensions
|
||||||
|
) as (keyof typeof defaultIconDimensions)[]
|
||||||
|
).forEach((prop) => {
|
||||||
|
if (typeof data[prop] !== typeof defaultIconDimensions[prop]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const value = data[prop];
|
const value = data[prop];
|
||||||
@ -19,11 +22,10 @@ export function expandIconSet(data: IconifyJSON): void {
|
|||||||
icons.forEach((name) => {
|
icons.forEach((name) => {
|
||||||
const item = data.icons[name];
|
const item = data.icons[name];
|
||||||
if (item[prop] === void 0) {
|
if (item[prop] === void 0) {
|
||||||
item[prop as 'height'] = value as number;
|
item[prop] = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
delete data[prop];
|
delete data[prop];
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { IconifyJSON, IconifyOptional } from '@iconify/types';
|
import type { IconifyDimenisons, IconifyJSON } from '@iconify/types';
|
||||||
import { fullIcon, IconifyIcon, iconDefaults } from '../icon';
|
import { defaultIconProps, defaultIconDimensions } from '../icon/defaults';
|
||||||
import type { FullIconifyIcon } from '../icon';
|
import type { IconifyIcon, FullIconifyIcon } from '../icon/defaults';
|
||||||
import { mergeIconData } from '../icon/merge';
|
import { mergeIconData } from '../icon/merge';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,17 +56,19 @@ export function getIconData(
|
|||||||
|
|
||||||
// Add default properties
|
// Add default properties
|
||||||
if (result) {
|
if (result) {
|
||||||
for (const key in iconDefaults) {
|
for (const key in defaultIconDimensions) {
|
||||||
if (
|
if (
|
||||||
result[key as keyof IconifyOptional] === void 0 &&
|
result[key as keyof IconifyDimenisons] === void 0 &&
|
||||||
data[key as keyof IconifyOptional] !== void 0
|
data[key as keyof IconifyDimenisons] !== void 0
|
||||||
) {
|
) {
|
||||||
(result as unknown as Record<string, unknown>)[key] =
|
(result as unknown as Record<string, unknown>)[key] =
|
||||||
data[key as keyof IconifyOptional];
|
data[key as keyof IconifyDimenisons];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return icon
|
// Return icon
|
||||||
return result && full ? fullIcon(result) : result;
|
return result && full
|
||||||
|
? Object.assign({}, defaultIconProps, result)
|
||||||
|
: result;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
import type { IconifyJSON } from '@iconify/types';
|
import type { IconifyJSON } from '@iconify/types';
|
||||||
import { iconDefaults } from '../icon';
|
import { defaultIconProps } from '../icon/defaults';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional properties that must be copied when copying icon set
|
* Optional properties that must be copied when copying icon set
|
||||||
*/
|
*/
|
||||||
export const propsToCopy = Object.keys(iconDefaults).concat([
|
export const propsToCopy = Object.keys(defaultIconProps).concat([
|
||||||
'provider',
|
'provider',
|
||||||
]) as (keyof IconifyJSON)[];
|
]) as (keyof IconifyJSON)[];
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
import type { IconifyJSON } from '@iconify/types';
|
import type { IconifyJSON } from '@iconify/types';
|
||||||
import { iconDefaults } from '../icon';
|
import { defaultIconDimensions } from '../icon/defaults';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minify icon set
|
* Minify icon set
|
||||||
*
|
*
|
||||||
* Function finds common values for few numeric properties, such as 'width' and 'height' (see iconDefaults keys for list of properties),
|
* Function finds common values for few numeric properties, such as 'width' and 'height' (see defaultIconDimensions keys for list of properties),
|
||||||
* removes entries from icons and sets default entry in root of icon set object.
|
* removes entries from icons and sets default entry in root of icon set object.
|
||||||
*
|
*
|
||||||
* For example, this:
|
* For example, this:
|
||||||
@ -45,13 +45,16 @@ import { iconDefaults } from '../icon';
|
|||||||
export function minifyIconSet(data: IconifyJSON): void {
|
export function minifyIconSet(data: IconifyJSON): void {
|
||||||
const icons = Object.keys(data.icons);
|
const icons = Object.keys(data.icons);
|
||||||
|
|
||||||
(Object.keys(iconDefaults) as (keyof typeof iconDefaults)[]).forEach(
|
(
|
||||||
(prop) => {
|
Object.keys(
|
||||||
|
defaultIconDimensions
|
||||||
|
) as (keyof typeof defaultIconDimensions)[]
|
||||||
|
).forEach((prop) => {
|
||||||
// Check for default value for property
|
// Check for default value for property
|
||||||
if (data[prop] === iconDefaults[prop]) {
|
if (data[prop] === defaultIconDimensions[prop]) {
|
||||||
delete data[prop];
|
delete data[prop];
|
||||||
}
|
}
|
||||||
const defaultValue = iconDefaults[prop];
|
const defaultValue = defaultIconDimensions[prop];
|
||||||
const propType = typeof defaultValue;
|
const propType = typeof defaultValue;
|
||||||
|
|
||||||
// Check for previously minified value
|
// Check for previously minified value
|
||||||
@ -72,7 +75,7 @@ export function minifyIconSet(data: IconifyJSON): void {
|
|||||||
} else if (hasMinifiedDefault) {
|
} else if (hasMinifiedDefault) {
|
||||||
value = data[prop]!;
|
value = data[prop]!;
|
||||||
} else {
|
} else {
|
||||||
value = iconDefaults[prop];
|
value = defaultIconDimensions[prop];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
@ -138,6 +141,5 @@ export function minifyIconSet(data: IconifyJSON): void {
|
|||||||
item[prop as 'height'] = value as number;
|
item[prop as 'height'] = value as number;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { IconifyJSON } from '@iconify/types';
|
import type { IconifyJSON } from '@iconify/types';
|
||||||
import type { FullIconifyIcon } from '../icon';
|
import type { FullIconifyIcon } from '../icon/defaults';
|
||||||
import { getIconData } from './get-icon';
|
import { getIconData } from './get-icon';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,18 +1,32 @@
|
|||||||
import type { IconifyJSON } from '@iconify/types';
|
import type { IconifyJSON } from '@iconify/types';
|
||||||
import { iconDefaults, matchName } from '../icon';
|
import { matchIconName } from '../icon/name';
|
||||||
|
import { defaultIconDimensions, defaultIconProps } from '../icon/defaults';
|
||||||
|
|
||||||
|
type PropsList = Record<string, unknown>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional properties
|
* Optional properties
|
||||||
*/
|
*/
|
||||||
const optionalProperties = {
|
const optionalPropertyDefaults = {
|
||||||
provider: 'string',
|
provider: '',
|
||||||
aliases: 'object',
|
aliases: {},
|
||||||
not_found: 'object',
|
not_found: {},
|
||||||
} as Record<string, string>;
|
...defaultIconDimensions,
|
||||||
|
} as PropsList;
|
||||||
|
|
||||||
for (const prop in iconDefaults) {
|
/**
|
||||||
optionalProperties[prop] =
|
* Check props
|
||||||
typeof iconDefaults[prop as keyof typeof iconDefaults];
|
*/
|
||||||
|
function checkOptionalProps(item: PropsList, defaults: PropsList): boolean {
|
||||||
|
for (const prop in defaults) {
|
||||||
|
if (
|
||||||
|
item[prop] !== void 0 &&
|
||||||
|
typeof item[prop] !== typeof defaults[prop]
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,59 +54,36 @@ export function quicklyValidateIconSet(obj: unknown): IconifyJSON | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for optional properties
|
// Check for optional properties
|
||||||
for (const prop in optionalProperties) {
|
if (!checkOptionalProps(obj as PropsList, optionalPropertyDefaults)) {
|
||||||
if (
|
|
||||||
(obj as Record<string, unknown>)[prop] !== void 0 &&
|
|
||||||
typeof (obj as Record<string, unknown>)[prop] !==
|
|
||||||
optionalProperties[prop]
|
|
||||||
) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check all icons
|
// Check all icons
|
||||||
const icons = data.icons;
|
const icons = data.icons;
|
||||||
for (const name in icons) {
|
for (const name in icons) {
|
||||||
const icon = icons[name];
|
const icon = icons[name];
|
||||||
if (!name.match(matchName) || typeof icon.body !== 'string') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const prop in iconDefaults) {
|
|
||||||
if (
|
if (
|
||||||
icon[prop as keyof typeof icon] !== void 0 &&
|
!name.match(matchIconName) ||
|
||||||
typeof icon[prop as keyof typeof icon] !==
|
typeof icon.body !== 'string' ||
|
||||||
typeof iconDefaults[prop as keyof typeof iconDefaults]
|
!checkOptionalProps(icon as unknown as PropsList, defaultIconProps)
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check all aliases
|
// Check all aliases
|
||||||
const aliases = data.aliases;
|
const aliases = data.aliases || {};
|
||||||
if (aliases) {
|
|
||||||
for (const name in aliases) {
|
for (const name in aliases) {
|
||||||
const icon = aliases[name];
|
const icon = aliases[name];
|
||||||
const parent = icon.parent;
|
const parent = icon.parent;
|
||||||
if (
|
if (
|
||||||
!name.match(matchName) ||
|
!name.match(matchIconName) ||
|
||||||
typeof parent !== 'string' ||
|
typeof parent !== 'string' ||
|
||||||
(!icons[parent] && !aliases[parent])
|
(!icons[parent] && !aliases[parent]) ||
|
||||||
|
!checkOptionalProps(icon as unknown as PropsList, defaultIconProps)
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const prop in iconDefaults) {
|
|
||||||
if (
|
|
||||||
icon[prop as keyof typeof icon] !== void 0 &&
|
|
||||||
typeof icon[prop as keyof typeof icon] !==
|
|
||||||
typeof iconDefaults[prop as keyof typeof iconDefaults]
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import type { IconifyJSON, IconifyOptional } from '@iconify/types';
|
import type { IconifyJSON, IconifyOptional } from '@iconify/types';
|
||||||
import { iconDefaults, matchName } from '../icon';
|
import { matchIconName } from '../icon/name';
|
||||||
|
import { defaultIconDimensions } from '../icon/defaults';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Match character
|
* Match character
|
||||||
@ -112,7 +113,7 @@ export function validateIconSet(
|
|||||||
data.prefix = options.prefix;
|
data.prefix = options.prefix;
|
||||||
} else if (
|
} else if (
|
||||||
typeof data.prefix !== 'string' ||
|
typeof data.prefix !== 'string' ||
|
||||||
!data.prefix.match(matchName)
|
!data.prefix.match(matchIconName)
|
||||||
) {
|
) {
|
||||||
throw new Error('Invalid prefix');
|
throw new Error('Invalid prefix');
|
||||||
}
|
}
|
||||||
@ -124,7 +125,7 @@ export function validateIconSet(
|
|||||||
const value = data.provider;
|
const value = data.provider;
|
||||||
if (
|
if (
|
||||||
typeof value !== 'string' ||
|
typeof value !== 'string' ||
|
||||||
(value !== '' && !value.match(matchName))
|
(value !== '' && !value.match(matchIconName))
|
||||||
) {
|
) {
|
||||||
if (fix) {
|
if (fix) {
|
||||||
delete data.provider;
|
delete data.provider;
|
||||||
@ -137,7 +138,7 @@ export function validateIconSet(
|
|||||||
// Validate all icons
|
// Validate all icons
|
||||||
const icons = data.icons;
|
const icons = data.icons;
|
||||||
Object.keys(icons).forEach((name) => {
|
Object.keys(icons).forEach((name) => {
|
||||||
if (!name.match(matchName)) {
|
if (!name.match(matchIconName)) {
|
||||||
if (fix) {
|
if (fix) {
|
||||||
delete icons[name];
|
delete icons[name];
|
||||||
return;
|
return;
|
||||||
@ -221,7 +222,7 @@ export function validateIconSet(
|
|||||||
item === null ||
|
item === null ||
|
||||||
typeof item.parent !== 'string' ||
|
typeof item.parent !== 'string' ||
|
||||||
// Check if name is valid
|
// Check if name is valid
|
||||||
!name.match(matchName)
|
!name.match(matchIconName)
|
||||||
) {
|
) {
|
||||||
if (fix) {
|
if (fix) {
|
||||||
delete aliases[name];
|
delete aliases[name];
|
||||||
@ -283,15 +284,17 @@ export function validateIconSet(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate all properties that can be optimised
|
// Validate all properties that can be optimised
|
||||||
(Object.keys(iconDefaults) as (keyof typeof iconDefaults)[]).forEach(
|
(
|
||||||
(prop) => {
|
Object.keys(
|
||||||
const expectedType = typeof iconDefaults[prop];
|
defaultIconDimensions
|
||||||
|
) as (keyof typeof defaultIconDimensions)[]
|
||||||
|
).forEach((prop) => {
|
||||||
|
const expectedType = typeof defaultIconDimensions[prop];
|
||||||
const actualType = typeof data[prop as keyof IconifyJSON];
|
const actualType = typeof data[prop as keyof IconifyJSON];
|
||||||
if (actualType !== 'undefined' && actualType !== expectedType) {
|
if (actualType !== 'undefined' && actualType !== expectedType) {
|
||||||
throw new Error(`Invalid value type for "${prop}"`);
|
throw new Error(`Invalid value type for "${prop}"`);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// Validate characters map
|
// Validate characters map
|
||||||
if (data.chars !== void 0) {
|
if (data.chars !== void 0) {
|
||||||
|
@ -9,11 +9,6 @@ import type {
|
|||||||
export { IconifyIcon };
|
export { IconifyIcon };
|
||||||
export type FullIconifyIcon = Required<IconifyIcon>;
|
export type FullIconifyIcon = Required<IconifyIcon>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Expression to test part of icon name.
|
|
||||||
*/
|
|
||||||
export const matchName = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default values for dimensions
|
* Default values for dimensions
|
||||||
*/
|
*/
|
||||||
@ -39,14 +34,7 @@ export const defaultIconTransformations: Required<IconifyTransformations> =
|
|||||||
/**
|
/**
|
||||||
* Default values for all optional IconifyIcon properties
|
* Default values for all optional IconifyIcon properties
|
||||||
*/
|
*/
|
||||||
export const iconDefaults: Required<IconifyOptional> = Object.freeze({
|
export const defaultIconProps: Required<IconifyOptional> = Object.freeze({
|
||||||
...defaultIconDimensions,
|
...defaultIconDimensions,
|
||||||
...defaultIconTransformations,
|
...defaultIconTransformations,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Add optional properties to icon
|
|
||||||
*/
|
|
||||||
export function fullIcon(data: IconifyIcon): FullIconifyIcon {
|
|
||||||
return { ...iconDefaults, ...data };
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
import type { IconifyOptional } from '@iconify/types';
|
import type { IconifyDimenisons, IconifyOptional } from '@iconify/types';
|
||||||
import { iconDefaults } from './index';
|
import { defaultIconDimensions } from './defaults';
|
||||||
|
import { mergeIconTransformations } from './transformations';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge icon and alias
|
* Merge icon and alias
|
||||||
@ -8,35 +9,16 @@ export function mergeIconData<T extends IconifyOptional>(
|
|||||||
icon: T,
|
icon: T,
|
||||||
alias: IconifyOptional
|
alias: IconifyOptional
|
||||||
): T {
|
): T {
|
||||||
const result = { ...icon };
|
// Merge transformations, while keeping other props
|
||||||
for (const key in iconDefaults) {
|
const result = mergeIconTransformations(icon, alias);
|
||||||
const prop = key as keyof IconifyOptional;
|
|
||||||
|
// Merge dimensions
|
||||||
|
for (const key in defaultIconDimensions) {
|
||||||
|
const prop = key as keyof IconifyDimenisons;
|
||||||
if (alias[prop] !== void 0) {
|
if (alias[prop] !== void 0) {
|
||||||
const value = alias[prop];
|
result[prop] = alias[prop];
|
||||||
|
|
||||||
if (result[prop] === void 0) {
|
|
||||||
// Missing value
|
|
||||||
(result as unknown as Record<string, unknown>)[prop] = value;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (prop) {
|
|
||||||
case 'rotate':
|
|
||||||
(result[prop] as number) =
|
|
||||||
((result[prop] as number) + (value as number)) % 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'hFlip':
|
|
||||||
case 'vFlip':
|
|
||||||
result[prop] = value !== result[prop];
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Overwrite value
|
|
||||||
(result as unknown as Record<string, unknown>)[prop] =
|
|
||||||
value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { matchName } from './index';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon name
|
* Icon name
|
||||||
*/
|
*/
|
||||||
@ -14,6 +12,11 @@ export interface IconifyIconName {
|
|||||||
*/
|
*/
|
||||||
export type IconifyIconSource = Omit<IconifyIconName, 'name'>;
|
export type IconifyIconSource = Omit<IconifyIconName, 'name'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expression to test part of icon name.
|
||||||
|
*/
|
||||||
|
export const matchIconName = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert string to Icon object.
|
* Convert string to Icon object.
|
||||||
*/
|
*/
|
||||||
@ -93,9 +96,9 @@ export const validateIcon = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return !!(
|
return !!(
|
||||||
(icon.provider === '' || icon.provider.match(matchName)) &&
|
(icon.provider === '' || icon.provider.match(matchIconName)) &&
|
||||||
((allowSimpleName && icon.prefix === '') ||
|
((allowSimpleName && icon.prefix === '') ||
|
||||||
icon.prefix.match(matchName)) &&
|
icon.prefix.match(matchIconName)) &&
|
||||||
icon.name.match(matchName)
|
icon.name.match(matchIconName)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
23
packages/utils/src/icon/transformations.ts
Normal file
23
packages/utils/src/icon/transformations.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import type { IconifyTransformations } from '@iconify/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge transformations. Also copies other properties from first parameter
|
||||||
|
*/
|
||||||
|
export function mergeIconTransformations<T extends IconifyTransformations>(
|
||||||
|
obj1: T,
|
||||||
|
obj2: IconifyTransformations
|
||||||
|
): T {
|
||||||
|
const result = {
|
||||||
|
...obj1,
|
||||||
|
};
|
||||||
|
if (obj2.hFlip) {
|
||||||
|
result.hFlip = !result.hFlip;
|
||||||
|
}
|
||||||
|
if (obj2.vFlip) {
|
||||||
|
result.vFlip = !result.vFlip;
|
||||||
|
}
|
||||||
|
if (obj2.rotate) {
|
||||||
|
result.rotate = ((result.rotate || 0) + obj2.rotate) % 4;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
// Customisations
|
// Customisations
|
||||||
export { compare as compareCustomisations } from './customisations/compare';
|
|
||||||
export {
|
export {
|
||||||
defaults as defaultCustomisations,
|
defaultIconCustomisations,
|
||||||
mergeCustomisations,
|
defaultIconSizeCustomisations,
|
||||||
} from './customisations/index';
|
} from './customisations/defaults';
|
||||||
|
export { mergeCustomisations } from './customisations/merge';
|
||||||
|
|
||||||
// Customisations: converting attributes in components
|
// Customisations: converting attributes in components
|
||||||
export { toBoolean } from './customisations/bool';
|
export { toBoolean } from './customisations/bool';
|
||||||
@ -11,17 +11,19 @@ export { flipFromString } from './customisations/flip';
|
|||||||
export { rotateFromString } from './customisations/rotate';
|
export { rotateFromString } from './customisations/rotate';
|
||||||
|
|
||||||
// Icon names
|
// Icon names
|
||||||
export { stringToIcon, validateIcon as validateIconName } from './icon/name';
|
export {
|
||||||
export { matchName as matchIconName } from './icon/index';
|
matchIconName,
|
||||||
|
stringToIcon,
|
||||||
|
validateIcon as validateIconName,
|
||||||
|
} from './icon/name';
|
||||||
|
|
||||||
// Icon data
|
// Icon data
|
||||||
export { mergeIconData } from './icon/merge';
|
export { mergeIconData } from './icon/merge';
|
||||||
export {
|
export {
|
||||||
iconDefaults as defaultIconData,
|
defaultIconProps,
|
||||||
fullIcon as fullIconData,
|
|
||||||
defaultIconDimensions,
|
defaultIconDimensions,
|
||||||
defaultIconTransformations,
|
defaultIconTransformations,
|
||||||
} from './icon/index';
|
} from './icon/defaults';
|
||||||
|
|
||||||
// Icon set functions
|
// Icon set functions
|
||||||
export { parseIconSet } from './icon-set/parse';
|
export { parseIconSet } from './icon-set/parse';
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import type { IconifyJSON } from '@iconify/types';
|
import type { IconifyJSON } from '@iconify/types';
|
||||||
import type { FullIconifyIcon } from '../icon';
|
import type { FullIconifyIcon } from '../icon/defaults';
|
||||||
import { iconToSVG } from '../svg/build';
|
import { iconToSVG } from '../svg/build';
|
||||||
import { getIconData } from '../icon-set/get-icon';
|
import { getIconData } from '../icon-set/get-icon';
|
||||||
import { mergeIconProps } from './utils';
|
import { mergeIconProps } from './utils';
|
||||||
import createDebugger from 'debug';
|
import createDebugger from 'debug';
|
||||||
import { defaults as DefaultIconCustomizations } from '../customisations';
|
import { defaultIconCustomisations } from '../customisations/defaults';
|
||||||
import type { IconifyLoaderOptions } from './types';
|
import type { IconifyLoaderOptions } from './types';
|
||||||
|
|
||||||
const debug = createDebugger('@iconify-loader:icon');
|
const debug = createDebugger('@iconify-loader:icon');
|
||||||
@ -21,7 +21,7 @@ export async function searchForIcon(
|
|||||||
iconData = getIconData(iconSet, id, true);
|
iconData = getIconData(iconSet, id, true);
|
||||||
if (iconData) {
|
if (iconData) {
|
||||||
debug(`${collection}:${id}`);
|
debug(`${collection}:${id}`);
|
||||||
let defaultCustomizations = { ...DefaultIconCustomizations };
|
let defaultCustomizations = { ...defaultIconCustomisations };
|
||||||
if (typeof customize === 'function')
|
if (typeof customize === 'function')
|
||||||
defaultCustomizations = customize(defaultCustomizations);
|
defaultCustomizations = customize(defaultCustomizations);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Awaitable } from '@antfu/utils';
|
import type { Awaitable } from '@antfu/utils';
|
||||||
import type { FullIconCustomisations } from '../customisations';
|
import type { FullIconCustomisations } from '../customisations/defaults';
|
||||||
import type { IconifyJSON } from '@iconify/types';
|
import type { IconifyJSON } from '@iconify/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
35
packages/utils/src/misc/objects.ts
Normal file
35
packages/utils/src/misc/objects.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Compares two objects, returns true if identical
|
||||||
|
*
|
||||||
|
* Reference object contains keys
|
||||||
|
*/
|
||||||
|
export function compareObjects<T extends Record<string, unknown>>(
|
||||||
|
obj1: T,
|
||||||
|
obj2: T,
|
||||||
|
ref: T = obj1
|
||||||
|
): boolean {
|
||||||
|
for (const key in ref) {
|
||||||
|
if (obj1[key] !== obj2[key]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Object.keys(obj1).length === Object.keys(obj2).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmerge objects, removing items that match in both objects
|
||||||
|
*/
|
||||||
|
export function unmergeObjects<T extends Record<string, unknown>>(
|
||||||
|
obj1: T,
|
||||||
|
obj2: T
|
||||||
|
): T {
|
||||||
|
const result = {
|
||||||
|
...obj1,
|
||||||
|
};
|
||||||
|
for (const key in obj2) {
|
||||||
|
if (result[key] === obj2[key]) {
|
||||||
|
delete result[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import type { FullIconifyIcon } from '../icon';
|
import type { FullIconifyIcon } from '../icon/defaults';
|
||||||
import type { FullIconCustomisations } from '../customisations';
|
import type { FullIconCustomisations } from '../customisations/defaults';
|
||||||
import { calculateSize } from './size';
|
import { calculateSize } from './size';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,10 +12,9 @@ export interface IconifyIconBuildResult {
|
|||||||
height: string;
|
height: string;
|
||||||
viewBox: string;
|
viewBox: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Content
|
// Content
|
||||||
body: string;
|
body: string;
|
||||||
// True if 'vertical-align: -0.125em' or equivalent should be added by implementation
|
|
||||||
inline?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -203,9 +202,5 @@ export function iconToSVG(
|
|||||||
body,
|
body,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (customisations.inline) {
|
|
||||||
result.inline = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
45
packages/utils/tests/merge-customisations-test.ts
Normal file
45
packages/utils/tests/merge-customisations-test.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { defaultIconCustomisations } from '../lib/customisations/defaults';
|
||||||
|
import { mergeCustomisations } from '../lib/customisations/merge';
|
||||||
|
|
||||||
|
describe('Testing mergeCustomisations', () => {
|
||||||
|
test('Empty', () => {
|
||||||
|
expect(mergeCustomisations(defaultIconCustomisations, {})).toEqual(
|
||||||
|
defaultIconCustomisations
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Flip', () => {
|
||||||
|
expect(
|
||||||
|
mergeCustomisations(defaultIconCustomisations, {
|
||||||
|
hFlip: true,
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
|
...defaultIconCustomisations,
|
||||||
|
hFlip: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Excessive rotation', () => {
|
||||||
|
expect(
|
||||||
|
mergeCustomisations(defaultIconCustomisations, {
|
||||||
|
rotate: 10,
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
|
...defaultIconCustomisations,
|
||||||
|
rotate: 2,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Dimensions', () => {
|
||||||
|
expect(
|
||||||
|
mergeCustomisations(defaultIconCustomisations, {
|
||||||
|
width: '1em',
|
||||||
|
height: 20,
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
|
...defaultIconCustomisations,
|
||||||
|
width: '1em',
|
||||||
|
height: 20,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,5 +1,5 @@
|
|||||||
import { parseIconSet } from '../lib/icon-set/parse';
|
import { parseIconSet } from '../lib/icon-set/parse';
|
||||||
import type { FullIconifyIcon } from '../lib/icon';
|
import type { FullIconifyIcon } from '../lib/icon/defaults';
|
||||||
|
|
||||||
describe('Testing parsing icon set', () => {
|
describe('Testing parsing icon set', () => {
|
||||||
test('Simple icon set', () => {
|
test('Simple icon set', () => {
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import type { IconifyIconBuildResult } from '../lib/svg/build';
|
import type { IconifyIconBuildResult } from '../lib/svg/build';
|
||||||
import { iconToSVG } from '../lib/svg/build';
|
import { iconToSVG } from '../lib/svg/build';
|
||||||
import type { FullIconifyIcon } from '../lib/icon';
|
import type { FullIconifyIcon } from '../lib/icon/defaults';
|
||||||
import { fullIcon, iconDefaults } from '../lib/icon';
|
import { defaultIconProps } from '../lib/icon/defaults';
|
||||||
import type { FullIconCustomisations } from '../lib/customisations';
|
import type { FullIconCustomisations } from '../lib/customisations/defaults';
|
||||||
import { defaults, mergeCustomisations } from '../lib/customisations';
|
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', () => {
|
||||||
test('Empty icon', () => {
|
test('Empty icon', () => {
|
||||||
const custom: FullIconCustomisations = defaults;
|
const custom: FullIconCustomisations = defaultIconCustomisations;
|
||||||
const icon: FullIconifyIcon = { ...iconDefaults, body: '' };
|
const icon: FullIconifyIcon = { ...defaultIconProps, body: '' };
|
||||||
const expected: IconifyIconBuildResult = {
|
const expected: IconifyIconBuildResult = {
|
||||||
attributes: {
|
attributes: {
|
||||||
width: '1em',
|
width: '1em',
|
||||||
@ -29,14 +30,17 @@ describe('Testing iconToSVG', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Auto size, inline, body', () => {
|
test('Auto size, body', () => {
|
||||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
const custom: FullIconCustomisations = mergeCustomisations(
|
||||||
inline: true,
|
defaultIconCustomisations,
|
||||||
|
{
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
});
|
}
|
||||||
const icon: FullIconifyIcon = fullIcon({
|
);
|
||||||
|
const icon: FullIconifyIcon = {
|
||||||
|
...defaultIconProps,
|
||||||
body: '<path d="" />',
|
body: '<path d="" />',
|
||||||
});
|
};
|
||||||
const expected: IconifyIconBuildResult = {
|
const expected: IconifyIconBuildResult = {
|
||||||
attributes: {
|
attributes: {
|
||||||
width: '16',
|
width: '16',
|
||||||
@ -44,7 +48,6 @@ describe('Testing iconToSVG', () => {
|
|||||||
viewBox: '0 0 16 16',
|
viewBox: '0 0 16 16',
|
||||||
},
|
},
|
||||||
body: '<path d="" />',
|
body: '<path d="" />',
|
||||||
inline: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = iconToSVG(icon, custom);
|
const result = iconToSVG(icon, custom);
|
||||||
@ -56,23 +59,23 @@ describe('Testing iconToSVG', () => {
|
|||||||
'role': 'img',
|
'role': 'img',
|
||||||
...result.attributes,
|
...result.attributes,
|
||||||
};
|
};
|
||||||
if (result.inline) {
|
|
||||||
htmlProps['style'] = 'vertical-align: -0.125em;';
|
|
||||||
}
|
|
||||||
const html = iconToHTML(result.body, htmlProps);
|
const html = iconToHTML(result.body, htmlProps);
|
||||||
expect(html).toBe(
|
expect(html).toBe(
|
||||||
'<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" width="16" height="16" viewBox="0 0 16 16" style="vertical-align: -0.125em;"><path d="" /></svg>'
|
'<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" width="16" height="16" viewBox="0 0 16 16"><path d="" /></svg>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Auto size, inline, body', () => {
|
test('Auto size, body', () => {
|
||||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
const custom: FullIconCustomisations = mergeCustomisations(
|
||||||
inline: true,
|
defaultIconCustomisations,
|
||||||
|
{
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
});
|
}
|
||||||
const icon: FullIconifyIcon = fullIcon({
|
);
|
||||||
|
const icon: FullIconifyIcon = {
|
||||||
|
...defaultIconProps,
|
||||||
body: '<path d="" />',
|
body: '<path d="" />',
|
||||||
});
|
};
|
||||||
const expected: IconifyIconBuildResult = {
|
const expected: IconifyIconBuildResult = {
|
||||||
attributes: {
|
attributes: {
|
||||||
width: '16',
|
width: '16',
|
||||||
@ -80,7 +83,6 @@ describe('Testing iconToSVG', () => {
|
|||||||
viewBox: '0 0 16 16',
|
viewBox: '0 0 16 16',
|
||||||
},
|
},
|
||||||
body: '<path d="" />',
|
body: '<path d="" />',
|
||||||
inline: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = iconToSVG(icon, custom);
|
const result = iconToSVG(icon, custom);
|
||||||
@ -88,14 +90,18 @@ describe('Testing iconToSVG', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Custom size', () => {
|
test('Custom size', () => {
|
||||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
const custom: FullIconCustomisations = mergeCustomisations(
|
||||||
|
defaultIconCustomisations,
|
||||||
|
{
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
});
|
}
|
||||||
const icon: FullIconifyIcon = fullIcon({
|
);
|
||||||
|
const icon: FullIconifyIcon = {
|
||||||
|
...defaultIconProps,
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 16,
|
height: 16,
|
||||||
body: '<path d="..." />',
|
body: '<path d="..." />',
|
||||||
});
|
};
|
||||||
const expected: IconifyIconBuildResult = {
|
const expected: IconifyIconBuildResult = {
|
||||||
attributes: {
|
attributes: {
|
||||||
width: '20',
|
width: '20',
|
||||||
@ -110,15 +116,19 @@ describe('Testing iconToSVG', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Rotation', () => {
|
test('Rotation', () => {
|
||||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
const custom: FullIconCustomisations = mergeCustomisations(
|
||||||
|
defaultIconCustomisations,
|
||||||
|
{
|
||||||
height: '40px',
|
height: '40px',
|
||||||
rotate: 1,
|
rotate: 1,
|
||||||
});
|
}
|
||||||
const icon: FullIconifyIcon = fullIcon({
|
);
|
||||||
|
const icon: FullIconifyIcon = {
|
||||||
|
...defaultIconProps,
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 16,
|
height: 16,
|
||||||
body: '<path d="..." />',
|
body: '<path d="..." />',
|
||||||
});
|
};
|
||||||
const expected: IconifyIconBuildResult = {
|
const expected: IconifyIconBuildResult = {
|
||||||
attributes: {
|
attributes: {
|
||||||
width: '32px',
|
width: '32px',
|
||||||
@ -133,15 +143,19 @@ describe('Testing iconToSVG', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Negative rotation', () => {
|
test('Negative rotation', () => {
|
||||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
const custom: FullIconCustomisations = mergeCustomisations(
|
||||||
|
defaultIconCustomisations,
|
||||||
|
{
|
||||||
height: '40px',
|
height: '40px',
|
||||||
rotate: -1,
|
rotate: -1,
|
||||||
});
|
}
|
||||||
const icon: FullIconifyIcon = fullIcon({
|
);
|
||||||
|
const icon: FullIconifyIcon = {
|
||||||
|
...defaultIconProps,
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 16,
|
height: 16,
|
||||||
body: '<path d="..." />',
|
body: '<path d="..." />',
|
||||||
});
|
};
|
||||||
const expected: IconifyIconBuildResult = {
|
const expected: IconifyIconBuildResult = {
|
||||||
attributes: {
|
attributes: {
|
||||||
width: '32px',
|
width: '32px',
|
||||||
@ -156,15 +170,19 @@ describe('Testing iconToSVG', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Flip', () => {
|
test('Flip', () => {
|
||||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
const custom: FullIconCustomisations = mergeCustomisations(
|
||||||
|
defaultIconCustomisations,
|
||||||
|
{
|
||||||
height: '32',
|
height: '32',
|
||||||
hFlip: true,
|
hFlip: true,
|
||||||
});
|
}
|
||||||
const icon: FullIconifyIcon = fullIcon({
|
);
|
||||||
|
const icon: FullIconifyIcon = {
|
||||||
|
...defaultIconProps,
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 16,
|
height: 16,
|
||||||
body: '<path d="..." />',
|
body: '<path d="..." />',
|
||||||
});
|
};
|
||||||
const expected: IconifyIconBuildResult = {
|
const expected: IconifyIconBuildResult = {
|
||||||
attributes: {
|
attributes: {
|
||||||
width: '40',
|
width: '40',
|
||||||
@ -179,15 +197,19 @@ describe('Testing iconToSVG', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Flip, rotation', () => {
|
test('Flip, rotation', () => {
|
||||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
const custom: FullIconCustomisations = mergeCustomisations(
|
||||||
|
defaultIconCustomisations,
|
||||||
|
{
|
||||||
hFlip: true,
|
hFlip: true,
|
||||||
rotate: 1,
|
rotate: 1,
|
||||||
});
|
}
|
||||||
const icon: FullIconifyIcon = fullIcon({
|
);
|
||||||
|
const icon: FullIconifyIcon = {
|
||||||
|
...defaultIconProps,
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 16,
|
height: 16,
|
||||||
body: '<path d="..." />',
|
body: '<path d="..." />',
|
||||||
});
|
};
|
||||||
const expected: IconifyIconBuildResult = {
|
const expected: IconifyIconBuildResult = {
|
||||||
attributes: {
|
attributes: {
|
||||||
width: '0.8em',
|
width: '0.8em',
|
||||||
@ -202,15 +224,19 @@ describe('Testing iconToSVG', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Flip icon that is rotated by default', () => {
|
test('Flip icon that is rotated by default', () => {
|
||||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
const custom: FullIconCustomisations = mergeCustomisations(
|
||||||
|
defaultIconCustomisations,
|
||||||
|
{
|
||||||
hFlip: true,
|
hFlip: true,
|
||||||
});
|
}
|
||||||
const icon: FullIconifyIcon = fullIcon({
|
);
|
||||||
|
const icon: FullIconifyIcon = {
|
||||||
|
...defaultIconProps,
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 16,
|
height: 16,
|
||||||
body: '<path d="..." />',
|
body: '<path d="..." />',
|
||||||
rotate: 1,
|
rotate: 1,
|
||||||
});
|
};
|
||||||
|
|
||||||
// Horizontally flipping icon that has 90 or 270 degrees rotation will result in vertical flip.
|
// Horizontally flipping icon that has 90 or 270 degrees rotation will result in vertical flip.
|
||||||
// Therefore result should be rotation + vertical flip to visually match horizontal flip on normal icon.
|
// Therefore result should be rotation + vertical flip to visually match horizontal flip on normal icon.
|
||||||
@ -228,18 +254,22 @@ describe('Testing iconToSVG', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Flip and rotation canceling eachother', () => {
|
test('Flip and rotation canceling eachother', () => {
|
||||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
const custom: FullIconCustomisations = mergeCustomisations(
|
||||||
|
defaultIconCustomisations,
|
||||||
|
{
|
||||||
width: '1em',
|
width: '1em',
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
hFlip: true,
|
hFlip: true,
|
||||||
vFlip: true,
|
vFlip: true,
|
||||||
rotate: 2,
|
rotate: 2,
|
||||||
});
|
}
|
||||||
const icon: FullIconifyIcon = fullIcon({
|
);
|
||||||
|
const icon: FullIconifyIcon = {
|
||||||
|
...defaultIconProps,
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 16,
|
height: 16,
|
||||||
body: '<path d="..." />',
|
body: '<path d="..." />',
|
||||||
});
|
};
|
||||||
const expected: IconifyIconBuildResult = {
|
const expected: IconifyIconBuildResult = {
|
||||||
attributes: {
|
attributes: {
|
||||||
width: '1em',
|
width: '1em',
|
||||||
@ -258,15 +288,16 @@ describe('Testing iconToSVG', () => {
|
|||||||
'<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 = mergeCustomisations(
|
||||||
defaults,
|
defaultIconCustomisations,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
const icon: FullIconifyIcon = fullIcon({
|
const icon: FullIconifyIcon = {
|
||||||
|
...defaultIconProps,
|
||||||
body: iconBody,
|
body: iconBody,
|
||||||
width: 128,
|
width: 128,
|
||||||
height: 128,
|
height: 128,
|
||||||
hFlip: true,
|
hFlip: true,
|
||||||
});
|
};
|
||||||
const expected: IconifyIconBuildResult = {
|
const expected: IconifyIconBuildResult = {
|
||||||
attributes: {
|
attributes: {
|
||||||
width: '1em',
|
width: '1em',
|
||||||
|
@ -115,7 +115,7 @@ describe('Testing validation', () => {
|
|||||||
},
|
},
|
||||||
height: 24,
|
height: 24,
|
||||||
// Object
|
// Object
|
||||||
rotate: {
|
width: {
|
||||||
foo: 1,
|
foo: 1,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -131,7 +131,7 @@ describe('Testing validation', () => {
|
|||||||
},
|
},
|
||||||
height: 24,
|
height: 24,
|
||||||
// Object
|
// Object
|
||||||
hFlip: null,
|
left: null,
|
||||||
})
|
})
|
||||||
).toBe(null);
|
).toBe(null);
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ describe('Testing validation', () => {
|
|||||||
},
|
},
|
||||||
height: 24,
|
height: 24,
|
||||||
// Object
|
// Object
|
||||||
rotate: {
|
width: {
|
||||||
foo: 1,
|
foo: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -253,7 +253,7 @@ describe('Testing validation', () => {
|
|||||||
},
|
},
|
||||||
height: 24,
|
height: 24,
|
||||||
// Object
|
// Object
|
||||||
hFlip: null,
|
left: null,
|
||||||
},
|
},
|
||||||
{ fix: true }
|
{ fix: true }
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user