mirror of
https://github.com/iconify/iconify.git
synced 2025-01-06 07:20:40 +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:
|
||||
* icon (as is)
|
||||
* alias (merged with icon's properties)
|
||||
* root of JSON file (default values)
|
||||
*/
|
||||
export interface IconifyTransformations {
|
||||
// Number of 90 degrees rotations.
|
||||
@ -223,7 +222,7 @@ export interface IconifyMetaData {
|
||||
/**
|
||||
* JSON structure, contains only icon data
|
||||
*/
|
||||
export interface IconifyJSONIconsData extends IconifyOptional {
|
||||
export interface IconifyJSONIconsData extends IconifyDimenisons {
|
||||
// Prefix for icons in JSON file, required.
|
||||
prefix: string;
|
||||
|
||||
@ -236,8 +235,8 @@ export interface IconifyJSONIconsData extends IconifyOptional {
|
||||
// Optional aliases.
|
||||
aliases?: IconifyAliases;
|
||||
|
||||
// IconifyOptional properties that are used as default values for icons when icon is missing value.
|
||||
// If property exists in both icon and root, use value from icon.
|
||||
// IconifyDimenisons properties that are used as default viewbox for icons when icon is missing value.
|
||||
// If viewbox exists in both icon and root, use value from icon.
|
||||
// This is used to reduce duplication.
|
||||
}
|
||||
|
||||
|
@ -55,21 +55,17 @@
|
||||
"require": "./lib/customisations/bool.cjs",
|
||||
"import": "./lib/customisations/bool.mjs"
|
||||
},
|
||||
"./lib/customisations/compare": {
|
||||
"require": "./lib/customisations/compare.cjs",
|
||||
"import": "./lib/customisations/compare.mjs"
|
||||
},
|
||||
"./lib/customisations": {
|
||||
"require": "./lib/customisations/index.cjs",
|
||||
"import": "./lib/customisations/index.mjs"
|
||||
"./lib/customisations/defaults": {
|
||||
"require": "./lib/customisations/defaults.cjs",
|
||||
"import": "./lib/customisations/defaults.mjs"
|
||||
},
|
||||
"./lib/customisations/flip": {
|
||||
"require": "./lib/customisations/flip.cjs",
|
||||
"import": "./lib/customisations/flip.mjs"
|
||||
},
|
||||
"./lib/customisations/index": {
|
||||
"require": "./lib/customisations/index.cjs",
|
||||
"import": "./lib/customisations/index.mjs"
|
||||
"./lib/customisations/merge": {
|
||||
"require": "./lib/customisations/merge.cjs",
|
||||
"import": "./lib/customisations/merge.mjs"
|
||||
},
|
||||
"./lib/customisations/rotate": {
|
||||
"require": "./lib/customisations/rotate.cjs",
|
||||
@ -107,13 +103,9 @@
|
||||
"require": "./lib/icon-set/validate-basic.cjs",
|
||||
"import": "./lib/icon-set/validate-basic.mjs"
|
||||
},
|
||||
"./lib/icon": {
|
||||
"require": "./lib/icon/index.cjs",
|
||||
"import": "./lib/icon/index.mjs"
|
||||
},
|
||||
"./lib/icon/index": {
|
||||
"require": "./lib/icon/index.cjs",
|
||||
"import": "./lib/icon/index.mjs"
|
||||
"./lib/icon/defaults": {
|
||||
"require": "./lib/icon/defaults.cjs",
|
||||
"import": "./lib/icon/defaults.mjs"
|
||||
},
|
||||
"./lib/icon/merge": {
|
||||
"require": "./lib/icon/merge.cjs",
|
||||
@ -123,6 +115,10 @@
|
||||
"require": "./lib/icon/name.cjs",
|
||||
"import": "./lib/icon/name.mjs"
|
||||
},
|
||||
"./lib/icon/transformations": {
|
||||
"require": "./lib/icon/transformations.cjs",
|
||||
"import": "./lib/icon/transformations.mjs"
|
||||
},
|
||||
"./lib": {
|
||||
"require": "./lib/index.cjs",
|
||||
"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,]+/;
|
||||
|
||||
|
@ -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 { iconDefaults } from '../icon';
|
||||
import { defaultIconDimensions } from '../icon/defaults';
|
||||
|
||||
/**
|
||||
* Expand minified icon set
|
||||
@ -9,21 +9,23 @@ import { iconDefaults } from '../icon';
|
||||
export function expandIconSet(data: IconifyJSON): void {
|
||||
const icons = Object.keys(data.icons);
|
||||
|
||||
(Object.keys(iconDefaults) as (keyof typeof iconDefaults)[]).forEach(
|
||||
(prop) => {
|
||||
if (typeof data[prop] !== typeof iconDefaults[prop]) {
|
||||
return;
|
||||
}
|
||||
const value = data[prop];
|
||||
|
||||
icons.forEach((name) => {
|
||||
const item = data.icons[name];
|
||||
if (item[prop] === void 0) {
|
||||
item[prop as 'height'] = value as number;
|
||||
}
|
||||
});
|
||||
|
||||
delete data[prop];
|
||||
(
|
||||
Object.keys(
|
||||
defaultIconDimensions
|
||||
) as (keyof typeof defaultIconDimensions)[]
|
||||
).forEach((prop) => {
|
||||
if (typeof data[prop] !== typeof defaultIconDimensions[prop]) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
const value = data[prop];
|
||||
|
||||
icons.forEach((name) => {
|
||||
const item = data.icons[name];
|
||||
if (item[prop] === void 0) {
|
||||
item[prop] = value;
|
||||
}
|
||||
});
|
||||
|
||||
delete data[prop];
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { IconifyJSON, IconifyOptional } from '@iconify/types';
|
||||
import { fullIcon, IconifyIcon, iconDefaults } from '../icon';
|
||||
import type { FullIconifyIcon } from '../icon';
|
||||
import type { IconifyDimenisons, IconifyJSON } from '@iconify/types';
|
||||
import { defaultIconProps, defaultIconDimensions } from '../icon/defaults';
|
||||
import type { IconifyIcon, FullIconifyIcon } from '../icon/defaults';
|
||||
import { mergeIconData } from '../icon/merge';
|
||||
|
||||
/**
|
||||
@ -56,17 +56,19 @@ export function getIconData(
|
||||
|
||||
// Add default properties
|
||||
if (result) {
|
||||
for (const key in iconDefaults) {
|
||||
for (const key in defaultIconDimensions) {
|
||||
if (
|
||||
result[key as keyof IconifyOptional] === void 0 &&
|
||||
data[key as keyof IconifyOptional] !== void 0
|
||||
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 IconifyOptional];
|
||||
data[key as keyof IconifyDimenisons];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 */
|
||||
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
|
||||
*/
|
||||
export const propsToCopy = Object.keys(iconDefaults).concat([
|
||||
export const propsToCopy = Object.keys(defaultIconProps).concat([
|
||||
'provider',
|
||||
]) as (keyof IconifyJSON)[];
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import type { IconifyJSON } from '@iconify/types';
|
||||
import { iconDefaults } from '../icon';
|
||||
import { defaultIconDimensions } from '../icon/defaults';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* For example, this:
|
||||
@ -45,99 +45,101 @@ import { iconDefaults } from '../icon';
|
||||
export function minifyIconSet(data: IconifyJSON): void {
|
||||
const icons = Object.keys(data.icons);
|
||||
|
||||
(Object.keys(iconDefaults) as (keyof typeof iconDefaults)[]).forEach(
|
||||
(prop) => {
|
||||
// Check for default value for property
|
||||
if (data[prop] === iconDefaults[prop]) {
|
||||
delete data[prop];
|
||||
}
|
||||
const defaultValue = iconDefaults[prop];
|
||||
const propType = typeof defaultValue;
|
||||
|
||||
// Check for previously minified value
|
||||
const hasMinifiedDefault =
|
||||
typeof data[prop] === propType && data[prop] !== defaultValue;
|
||||
|
||||
// Find value that is used by most icons
|
||||
let maxCount = 0;
|
||||
let maxValue: typeof defaultValue | null = null;
|
||||
const counters: Map<typeof defaultValue, number> = new Map();
|
||||
|
||||
for (let i = 0; i < icons.length; i++) {
|
||||
const item = data.icons[icons[i]];
|
||||
|
||||
let value: typeof defaultValue;
|
||||
if (typeof item[prop] === propType) {
|
||||
value = item[prop]!;
|
||||
} else if (hasMinifiedDefault) {
|
||||
value = data[prop]!;
|
||||
} else {
|
||||
value = iconDefaults[prop];
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
// First item
|
||||
maxCount = 1;
|
||||
maxValue = value;
|
||||
counters.set(value, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!counters.has(value)) {
|
||||
// First entry for new value
|
||||
counters.set(value, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
const count = counters.get(value)! + 1;
|
||||
counters.set(value, count);
|
||||
if (count > maxCount) {
|
||||
maxCount = count;
|
||||
maxValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
const canMinify = maxValue !== null && maxCount > 1;
|
||||
|
||||
// Get default value
|
||||
const oldDefault = hasMinifiedDefault ? data[prop] : null;
|
||||
const newDefault = canMinify ? maxValue : oldDefault;
|
||||
// console.log(
|
||||
// `Prop: ${prop}, oldDefault: ${oldDefault}, canMinify: ${canMinify}, maxValue: ${maxValue}`
|
||||
// );
|
||||
|
||||
// Change global value
|
||||
if (newDefault === defaultValue) {
|
||||
delete data[prop];
|
||||
} else if (canMinify) {
|
||||
data[prop as 'height'] = newDefault as number;
|
||||
}
|
||||
|
||||
// Update all icons
|
||||
icons.forEach((key) => {
|
||||
const item = data.icons[key];
|
||||
const value =
|
||||
item[prop] === void 0
|
||||
? hasMinifiedDefault
|
||||
? oldDefault
|
||||
: defaultValue
|
||||
: item[prop];
|
||||
|
||||
if (
|
||||
value === newDefault ||
|
||||
(newDefault === null && value === defaultValue)
|
||||
) {
|
||||
// Property matches minified value
|
||||
// Property matches default value and there is no minified value
|
||||
delete item[prop];
|
||||
return;
|
||||
}
|
||||
|
||||
if (canMinify && item[prop] === void 0) {
|
||||
// Value matches old minified value
|
||||
item[prop as 'height'] = value as number;
|
||||
}
|
||||
});
|
||||
(
|
||||
Object.keys(
|
||||
defaultIconDimensions
|
||||
) as (keyof typeof defaultIconDimensions)[]
|
||||
).forEach((prop) => {
|
||||
// Check for default value for property
|
||||
if (data[prop] === defaultIconDimensions[prop]) {
|
||||
delete data[prop];
|
||||
}
|
||||
);
|
||||
const defaultValue = defaultIconDimensions[prop];
|
||||
const propType = typeof defaultValue;
|
||||
|
||||
// Check for previously minified value
|
||||
const hasMinifiedDefault =
|
||||
typeof data[prop] === propType && data[prop] !== defaultValue;
|
||||
|
||||
// Find value that is used by most icons
|
||||
let maxCount = 0;
|
||||
let maxValue: typeof defaultValue | null = null;
|
||||
const counters: Map<typeof defaultValue, number> = new Map();
|
||||
|
||||
for (let i = 0; i < icons.length; i++) {
|
||||
const item = data.icons[icons[i]];
|
||||
|
||||
let value: typeof defaultValue;
|
||||
if (typeof item[prop] === propType) {
|
||||
value = item[prop]!;
|
||||
} else if (hasMinifiedDefault) {
|
||||
value = data[prop]!;
|
||||
} else {
|
||||
value = defaultIconDimensions[prop];
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
// First item
|
||||
maxCount = 1;
|
||||
maxValue = value;
|
||||
counters.set(value, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!counters.has(value)) {
|
||||
// First entry for new value
|
||||
counters.set(value, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
const count = counters.get(value)! + 1;
|
||||
counters.set(value, count);
|
||||
if (count > maxCount) {
|
||||
maxCount = count;
|
||||
maxValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
const canMinify = maxValue !== null && maxCount > 1;
|
||||
|
||||
// Get default value
|
||||
const oldDefault = hasMinifiedDefault ? data[prop] : null;
|
||||
const newDefault = canMinify ? maxValue : oldDefault;
|
||||
// console.log(
|
||||
// `Prop: ${prop}, oldDefault: ${oldDefault}, canMinify: ${canMinify}, maxValue: ${maxValue}`
|
||||
// );
|
||||
|
||||
// Change global value
|
||||
if (newDefault === defaultValue) {
|
||||
delete data[prop];
|
||||
} else if (canMinify) {
|
||||
data[prop as 'height'] = newDefault as number;
|
||||
}
|
||||
|
||||
// Update all icons
|
||||
icons.forEach((key) => {
|
||||
const item = data.icons[key];
|
||||
const value =
|
||||
item[prop] === void 0
|
||||
? hasMinifiedDefault
|
||||
? oldDefault
|
||||
: defaultValue
|
||||
: item[prop];
|
||||
|
||||
if (
|
||||
value === newDefault ||
|
||||
(newDefault === null && value === defaultValue)
|
||||
) {
|
||||
// Property matches minified value
|
||||
// Property matches default value and there is no minified value
|
||||
delete item[prop];
|
||||
return;
|
||||
}
|
||||
|
||||
if (canMinify && item[prop] === void 0) {
|
||||
// Value matches old minified value
|
||||
item[prop as 'height'] = value as number;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { IconifyJSON } from '@iconify/types';
|
||||
import type { FullIconifyIcon } from '../icon';
|
||||
import type { FullIconifyIcon } from '../icon/defaults';
|
||||
import { getIconData } from './get-icon';
|
||||
|
||||
/**
|
||||
|
@ -1,18 +1,32 @@
|
||||
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
|
||||
*/
|
||||
const optionalProperties = {
|
||||
provider: 'string',
|
||||
aliases: 'object',
|
||||
not_found: 'object',
|
||||
} as Record<string, string>;
|
||||
const optionalPropertyDefaults = {
|
||||
provider: '',
|
||||
aliases: {},
|
||||
not_found: {},
|
||||
...defaultIconDimensions,
|
||||
} as PropsList;
|
||||
|
||||
for (const prop in iconDefaults) {
|
||||
optionalProperties[prop] =
|
||||
typeof iconDefaults[prop as keyof typeof iconDefaults];
|
||||
/**
|
||||
* Check props
|
||||
*/
|
||||
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,58 +54,35 @@ export function quicklyValidateIconSet(obj: unknown): IconifyJSON | null {
|
||||
}
|
||||
|
||||
// Check for optional properties
|
||||
for (const prop in optionalProperties) {
|
||||
if (
|
||||
(obj as Record<string, unknown>)[prop] !== void 0 &&
|
||||
typeof (obj as Record<string, unknown>)[prop] !==
|
||||
optionalProperties[prop]
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (!checkOptionalProps(obj as PropsList, optionalPropertyDefaults)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check all icons
|
||||
const icons = data.icons;
|
||||
for (const name in icons) {
|
||||
const icon = icons[name];
|
||||
if (!name.match(matchName) || typeof icon.body !== 'string') {
|
||||
if (
|
||||
!name.match(matchIconName) ||
|
||||
typeof icon.body !== 'string' ||
|
||||
!checkOptionalProps(icon as unknown as PropsList, defaultIconProps)
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check all aliases
|
||||
const aliases = data.aliases;
|
||||
if (aliases) {
|
||||
for (const name in aliases) {
|
||||
const icon = aliases[name];
|
||||
const parent = icon.parent;
|
||||
if (
|
||||
!name.match(matchName) ||
|
||||
typeof parent !== 'string' ||
|
||||
(!icons[parent] && !aliases[parent])
|
||||
) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
const aliases = data.aliases || {};
|
||||
for (const name in aliases) {
|
||||
const icon = aliases[name];
|
||||
const parent = icon.parent;
|
||||
if (
|
||||
!name.match(matchIconName) ||
|
||||
typeof parent !== 'string' ||
|
||||
(!icons[parent] && !aliases[parent]) ||
|
||||
!checkOptionalProps(icon as unknown as PropsList, defaultIconProps)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import type { IconifyJSON, IconifyOptional } from '@iconify/types';
|
||||
import { iconDefaults, matchName } from '../icon';
|
||||
import { matchIconName } from '../icon/name';
|
||||
import { defaultIconDimensions } from '../icon/defaults';
|
||||
|
||||
/**
|
||||
* Match character
|
||||
@ -112,7 +113,7 @@ export function validateIconSet(
|
||||
data.prefix = options.prefix;
|
||||
} else if (
|
||||
typeof data.prefix !== 'string' ||
|
||||
!data.prefix.match(matchName)
|
||||
!data.prefix.match(matchIconName)
|
||||
) {
|
||||
throw new Error('Invalid prefix');
|
||||
}
|
||||
@ -124,7 +125,7 @@ export function validateIconSet(
|
||||
const value = data.provider;
|
||||
if (
|
||||
typeof value !== 'string' ||
|
||||
(value !== '' && !value.match(matchName))
|
||||
(value !== '' && !value.match(matchIconName))
|
||||
) {
|
||||
if (fix) {
|
||||
delete data.provider;
|
||||
@ -137,7 +138,7 @@ export function validateIconSet(
|
||||
// Validate all icons
|
||||
const icons = data.icons;
|
||||
Object.keys(icons).forEach((name) => {
|
||||
if (!name.match(matchName)) {
|
||||
if (!name.match(matchIconName)) {
|
||||
if (fix) {
|
||||
delete icons[name];
|
||||
return;
|
||||
@ -221,7 +222,7 @@ export function validateIconSet(
|
||||
item === null ||
|
||||
typeof item.parent !== 'string' ||
|
||||
// Check if name is valid
|
||||
!name.match(matchName)
|
||||
!name.match(matchIconName)
|
||||
) {
|
||||
if (fix) {
|
||||
delete aliases[name];
|
||||
@ -283,15 +284,17 @@ export function validateIconSet(
|
||||
}
|
||||
|
||||
// Validate all properties that can be optimised
|
||||
(Object.keys(iconDefaults) as (keyof typeof iconDefaults)[]).forEach(
|
||||
(prop) => {
|
||||
const expectedType = typeof iconDefaults[prop];
|
||||
const actualType = typeof data[prop as keyof IconifyJSON];
|
||||
if (actualType !== 'undefined' && actualType !== expectedType) {
|
||||
throw new Error(`Invalid value type for "${prop}"`);
|
||||
}
|
||||
(
|
||||
Object.keys(
|
||||
defaultIconDimensions
|
||||
) as (keyof typeof defaultIconDimensions)[]
|
||||
).forEach((prop) => {
|
||||
const expectedType = typeof defaultIconDimensions[prop];
|
||||
const actualType = typeof data[prop as keyof IconifyJSON];
|
||||
if (actualType !== 'undefined' && actualType !== expectedType) {
|
||||
throw new Error(`Invalid value type for "${prop}"`);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Validate characters map
|
||||
if (data.chars !== void 0) {
|
||||
|
@ -9,11 +9,6 @@ import type {
|
||||
export { 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
|
||||
*/
|
||||
@ -39,14 +34,7 @@ export const defaultIconTransformations: Required<IconifyTransformations> =
|
||||
/**
|
||||
* Default values for all optional IconifyIcon properties
|
||||
*/
|
||||
export const iconDefaults: Required<IconifyOptional> = Object.freeze({
|
||||
export const defaultIconProps: Required<IconifyOptional> = Object.freeze({
|
||||
...defaultIconDimensions,
|
||||
...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 { iconDefaults } from './index';
|
||||
import type { IconifyDimenisons, IconifyOptional } from '@iconify/types';
|
||||
import { defaultIconDimensions } from './defaults';
|
||||
import { mergeIconTransformations } from './transformations';
|
||||
|
||||
/**
|
||||
* Merge icon and alias
|
||||
@ -8,35 +9,16 @@ export function mergeIconData<T extends IconifyOptional>(
|
||||
icon: T,
|
||||
alias: IconifyOptional
|
||||
): T {
|
||||
const result = { ...icon };
|
||||
for (const key in iconDefaults) {
|
||||
const prop = key as keyof IconifyOptional;
|
||||
// Merge transformations, while keeping other props
|
||||
const result = mergeIconTransformations(icon, alias);
|
||||
|
||||
// Merge dimensions
|
||||
for (const key in defaultIconDimensions) {
|
||||
const prop = key as keyof IconifyDimenisons;
|
||||
if (alias[prop] !== void 0) {
|
||||
const value = 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;
|
||||
}
|
||||
result[prop] = alias[prop];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { matchName } from './index';
|
||||
|
||||
/**
|
||||
* Icon name
|
||||
*/
|
||||
@ -14,6 +12,11 @@ export interface IconifyIconName {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -93,9 +96,9 @@ export const validateIcon = (
|
||||
}
|
||||
|
||||
return !!(
|
||||
(icon.provider === '' || icon.provider.match(matchName)) &&
|
||||
(icon.provider === '' || icon.provider.match(matchIconName)) &&
|
||||
((allowSimpleName && icon.prefix === '') ||
|
||||
icon.prefix.match(matchName)) &&
|
||||
icon.name.match(matchName)
|
||||
icon.prefix.match(matchIconName)) &&
|
||||
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
|
||||
export { compare as compareCustomisations } from './customisations/compare';
|
||||
export {
|
||||
defaults as defaultCustomisations,
|
||||
mergeCustomisations,
|
||||
} from './customisations/index';
|
||||
defaultIconCustomisations,
|
||||
defaultIconSizeCustomisations,
|
||||
} from './customisations/defaults';
|
||||
export { mergeCustomisations } from './customisations/merge';
|
||||
|
||||
// Customisations: converting attributes in components
|
||||
export { toBoolean } from './customisations/bool';
|
||||
@ -11,17 +11,19 @@ export { flipFromString } from './customisations/flip';
|
||||
export { rotateFromString } from './customisations/rotate';
|
||||
|
||||
// Icon names
|
||||
export { stringToIcon, validateIcon as validateIconName } from './icon/name';
|
||||
export { matchName as matchIconName } from './icon/index';
|
||||
export {
|
||||
matchIconName,
|
||||
stringToIcon,
|
||||
validateIcon as validateIconName,
|
||||
} from './icon/name';
|
||||
|
||||
// Icon data
|
||||
export { mergeIconData } from './icon/merge';
|
||||
export {
|
||||
iconDefaults as defaultIconData,
|
||||
fullIcon as fullIconData,
|
||||
defaultIconProps,
|
||||
defaultIconDimensions,
|
||||
defaultIconTransformations,
|
||||
} from './icon/index';
|
||||
} from './icon/defaults';
|
||||
|
||||
// Icon set functions
|
||||
export { parseIconSet } from './icon-set/parse';
|
||||
|
@ -1,10 +1,10 @@
|
||||
import type { IconifyJSON } from '@iconify/types';
|
||||
import type { FullIconifyIcon } from '../icon';
|
||||
import type { FullIconifyIcon } from '../icon/defaults';
|
||||
import { iconToSVG } from '../svg/build';
|
||||
import { getIconData } from '../icon-set/get-icon';
|
||||
import { mergeIconProps } from './utils';
|
||||
import createDebugger from 'debug';
|
||||
import { defaults as DefaultIconCustomizations } from '../customisations';
|
||||
import { defaultIconCustomisations } from '../customisations/defaults';
|
||||
import type { IconifyLoaderOptions } from './types';
|
||||
|
||||
const debug = createDebugger('@iconify-loader:icon');
|
||||
@ -21,7 +21,7 @@ export async function searchForIcon(
|
||||
iconData = getIconData(iconSet, id, true);
|
||||
if (iconData) {
|
||||
debug(`${collection}:${id}`);
|
||||
let defaultCustomizations = { ...DefaultIconCustomizations };
|
||||
let defaultCustomizations = { ...defaultIconCustomisations };
|
||||
if (typeof customize === 'function')
|
||||
defaultCustomizations = customize(defaultCustomizations);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { Awaitable } from '@antfu/utils';
|
||||
import type { FullIconCustomisations } from '../customisations';
|
||||
import type { FullIconCustomisations } from '../customisations/defaults';
|
||||
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 { FullIconCustomisations } from '../customisations';
|
||||
import type { FullIconifyIcon } from '../icon/defaults';
|
||||
import type { FullIconCustomisations } from '../customisations/defaults';
|
||||
import { calculateSize } from './size';
|
||||
|
||||
/**
|
||||
@ -12,10 +12,9 @@ export interface IconifyIconBuildResult {
|
||||
height: string;
|
||||
viewBox: string;
|
||||
};
|
||||
|
||||
// Content
|
||||
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,
|
||||
};
|
||||
|
||||
if (customisations.inline) {
|
||||
result.inline = true;
|
||||
}
|
||||
|
||||
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 type { FullIconifyIcon } from '../lib/icon';
|
||||
import type { FullIconifyIcon } from '../lib/icon/defaults';
|
||||
|
||||
describe('Testing parsing icon set', () => {
|
||||
test('Simple icon set', () => {
|
||||
|
@ -1,15 +1,16 @@
|
||||
import type { IconifyIconBuildResult } from '../lib/svg/build';
|
||||
import { iconToSVG } from '../lib/svg/build';
|
||||
import type { FullIconifyIcon } from '../lib/icon';
|
||||
import { fullIcon, iconDefaults } from '../lib/icon';
|
||||
import type { FullIconCustomisations } from '../lib/customisations';
|
||||
import { defaults, mergeCustomisations } from '../lib/customisations';
|
||||
import type { FullIconifyIcon } from '../lib/icon/defaults';
|
||||
import { defaultIconProps } from '../lib/icon/defaults';
|
||||
import type { FullIconCustomisations } from '../lib/customisations/defaults';
|
||||
import { defaultIconCustomisations } from '../lib/customisations/defaults';
|
||||
import { mergeCustomisations } from '../lib/customisations/merge';
|
||||
import { iconToHTML } from '../lib/svg/html';
|
||||
|
||||
describe('Testing iconToSVG', () => {
|
||||
test('Empty icon', () => {
|
||||
const custom: FullIconCustomisations = defaults;
|
||||
const icon: FullIconifyIcon = { ...iconDefaults, body: '' };
|
||||
const custom: FullIconCustomisations = defaultIconCustomisations;
|
||||
const icon: FullIconifyIcon = { ...defaultIconProps, body: '' };
|
||||
const expected: IconifyIconBuildResult = {
|
||||
attributes: {
|
||||
width: '1em',
|
||||
@ -29,14 +30,17 @@ describe('Testing iconToSVG', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Auto size, inline, body', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
||||
inline: true,
|
||||
height: 'auto',
|
||||
});
|
||||
const icon: FullIconifyIcon = fullIcon({
|
||||
test('Auto size, body', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(
|
||||
defaultIconCustomisations,
|
||||
{
|
||||
height: 'auto',
|
||||
}
|
||||
);
|
||||
const icon: FullIconifyIcon = {
|
||||
...defaultIconProps,
|
||||
body: '<path d="" />',
|
||||
});
|
||||
};
|
||||
const expected: IconifyIconBuildResult = {
|
||||
attributes: {
|
||||
width: '16',
|
||||
@ -44,7 +48,6 @@ describe('Testing iconToSVG', () => {
|
||||
viewBox: '0 0 16 16',
|
||||
},
|
||||
body: '<path d="" />',
|
||||
inline: true,
|
||||
};
|
||||
|
||||
const result = iconToSVG(icon, custom);
|
||||
@ -56,23 +59,23 @@ describe('Testing iconToSVG', () => {
|
||||
'role': 'img',
|
||||
...result.attributes,
|
||||
};
|
||||
if (result.inline) {
|
||||
htmlProps['style'] = 'vertical-align: -0.125em;';
|
||||
}
|
||||
const html = iconToHTML(result.body, htmlProps);
|
||||
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', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
||||
inline: true,
|
||||
height: 'auto',
|
||||
});
|
||||
const icon: FullIconifyIcon = fullIcon({
|
||||
test('Auto size, body', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(
|
||||
defaultIconCustomisations,
|
||||
{
|
||||
height: 'auto',
|
||||
}
|
||||
);
|
||||
const icon: FullIconifyIcon = {
|
||||
...defaultIconProps,
|
||||
body: '<path d="" />',
|
||||
});
|
||||
};
|
||||
const expected: IconifyIconBuildResult = {
|
||||
attributes: {
|
||||
width: '16',
|
||||
@ -80,7 +83,6 @@ describe('Testing iconToSVG', () => {
|
||||
viewBox: '0 0 16 16',
|
||||
},
|
||||
body: '<path d="" />',
|
||||
inline: true,
|
||||
};
|
||||
|
||||
const result = iconToSVG(icon, custom);
|
||||
@ -88,14 +90,18 @@ describe('Testing iconToSVG', () => {
|
||||
});
|
||||
|
||||
test('Custom size', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
||||
height: 'auto',
|
||||
});
|
||||
const icon: FullIconifyIcon = fullIcon({
|
||||
const custom: FullIconCustomisations = mergeCustomisations(
|
||||
defaultIconCustomisations,
|
||||
{
|
||||
height: 'auto',
|
||||
}
|
||||
);
|
||||
const icon: FullIconifyIcon = {
|
||||
...defaultIconProps,
|
||||
width: 20,
|
||||
height: 16,
|
||||
body: '<path d="..." />',
|
||||
});
|
||||
};
|
||||
const expected: IconifyIconBuildResult = {
|
||||
attributes: {
|
||||
width: '20',
|
||||
@ -110,15 +116,19 @@ describe('Testing iconToSVG', () => {
|
||||
});
|
||||
|
||||
test('Rotation', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
||||
height: '40px',
|
||||
rotate: 1,
|
||||
});
|
||||
const icon: FullIconifyIcon = fullIcon({
|
||||
const custom: FullIconCustomisations = mergeCustomisations(
|
||||
defaultIconCustomisations,
|
||||
{
|
||||
height: '40px',
|
||||
rotate: 1,
|
||||
}
|
||||
);
|
||||
const icon: FullIconifyIcon = {
|
||||
...defaultIconProps,
|
||||
width: 20,
|
||||
height: 16,
|
||||
body: '<path d="..." />',
|
||||
});
|
||||
};
|
||||
const expected: IconifyIconBuildResult = {
|
||||
attributes: {
|
||||
width: '32px',
|
||||
@ -133,15 +143,19 @@ describe('Testing iconToSVG', () => {
|
||||
});
|
||||
|
||||
test('Negative rotation', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
||||
height: '40px',
|
||||
rotate: -1,
|
||||
});
|
||||
const icon: FullIconifyIcon = fullIcon({
|
||||
const custom: FullIconCustomisations = mergeCustomisations(
|
||||
defaultIconCustomisations,
|
||||
{
|
||||
height: '40px',
|
||||
rotate: -1,
|
||||
}
|
||||
);
|
||||
const icon: FullIconifyIcon = {
|
||||
...defaultIconProps,
|
||||
width: 20,
|
||||
height: 16,
|
||||
body: '<path d="..." />',
|
||||
});
|
||||
};
|
||||
const expected: IconifyIconBuildResult = {
|
||||
attributes: {
|
||||
width: '32px',
|
||||
@ -156,15 +170,19 @@ describe('Testing iconToSVG', () => {
|
||||
});
|
||||
|
||||
test('Flip', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
||||
height: '32',
|
||||
hFlip: true,
|
||||
});
|
||||
const icon: FullIconifyIcon = fullIcon({
|
||||
const custom: FullIconCustomisations = mergeCustomisations(
|
||||
defaultIconCustomisations,
|
||||
{
|
||||
height: '32',
|
||||
hFlip: true,
|
||||
}
|
||||
);
|
||||
const icon: FullIconifyIcon = {
|
||||
...defaultIconProps,
|
||||
width: 20,
|
||||
height: 16,
|
||||
body: '<path d="..." />',
|
||||
});
|
||||
};
|
||||
const expected: IconifyIconBuildResult = {
|
||||
attributes: {
|
||||
width: '40',
|
||||
@ -179,15 +197,19 @@ describe('Testing iconToSVG', () => {
|
||||
});
|
||||
|
||||
test('Flip, rotation', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
||||
hFlip: true,
|
||||
rotate: 1,
|
||||
});
|
||||
const icon: FullIconifyIcon = fullIcon({
|
||||
const custom: FullIconCustomisations = mergeCustomisations(
|
||||
defaultIconCustomisations,
|
||||
{
|
||||
hFlip: true,
|
||||
rotate: 1,
|
||||
}
|
||||
);
|
||||
const icon: FullIconifyIcon = {
|
||||
...defaultIconProps,
|
||||
width: 20,
|
||||
height: 16,
|
||||
body: '<path d="..." />',
|
||||
});
|
||||
};
|
||||
const expected: IconifyIconBuildResult = {
|
||||
attributes: {
|
||||
width: '0.8em',
|
||||
@ -202,15 +224,19 @@ describe('Testing iconToSVG', () => {
|
||||
});
|
||||
|
||||
test('Flip icon that is rotated by default', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
||||
hFlip: true,
|
||||
});
|
||||
const icon: FullIconifyIcon = fullIcon({
|
||||
const custom: FullIconCustomisations = mergeCustomisations(
|
||||
defaultIconCustomisations,
|
||||
{
|
||||
hFlip: true,
|
||||
}
|
||||
);
|
||||
const icon: FullIconifyIcon = {
|
||||
...defaultIconProps,
|
||||
width: 20,
|
||||
height: 16,
|
||||
body: '<path d="..." />',
|
||||
rotate: 1,
|
||||
});
|
||||
};
|
||||
|
||||
// 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.
|
||||
@ -228,18 +254,22 @@ describe('Testing iconToSVG', () => {
|
||||
});
|
||||
|
||||
test('Flip and rotation canceling eachother', () => {
|
||||
const custom: FullIconCustomisations = mergeCustomisations(defaults, {
|
||||
width: '1em',
|
||||
height: 'auto',
|
||||
hFlip: true,
|
||||
vFlip: true,
|
||||
rotate: 2,
|
||||
});
|
||||
const icon: FullIconifyIcon = fullIcon({
|
||||
const custom: FullIconCustomisations = mergeCustomisations(
|
||||
defaultIconCustomisations,
|
||||
{
|
||||
width: '1em',
|
||||
height: 'auto',
|
||||
hFlip: true,
|
||||
vFlip: true,
|
||||
rotate: 2,
|
||||
}
|
||||
);
|
||||
const icon: FullIconifyIcon = {
|
||||
...defaultIconProps,
|
||||
width: 20,
|
||||
height: 16,
|
||||
body: '<path d="..." />',
|
||||
});
|
||||
};
|
||||
const expected: IconifyIconBuildResult = {
|
||||
attributes: {
|
||||
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>';
|
||||
|
||||
const custom: FullIconCustomisations = mergeCustomisations(
|
||||
defaults,
|
||||
defaultIconCustomisations,
|
||||
{}
|
||||
);
|
||||
const icon: FullIconifyIcon = fullIcon({
|
||||
const icon: FullIconifyIcon = {
|
||||
...defaultIconProps,
|
||||
body: iconBody,
|
||||
width: 128,
|
||||
height: 128,
|
||||
hFlip: true,
|
||||
});
|
||||
};
|
||||
const expected: IconifyIconBuildResult = {
|
||||
attributes: {
|
||||
width: '1em',
|
||||
|
@ -115,7 +115,7 @@ describe('Testing validation', () => {
|
||||
},
|
||||
height: 24,
|
||||
// Object
|
||||
rotate: {
|
||||
width: {
|
||||
foo: 1,
|
||||
},
|
||||
})
|
||||
@ -131,7 +131,7 @@ describe('Testing validation', () => {
|
||||
},
|
||||
height: 24,
|
||||
// Object
|
||||
hFlip: null,
|
||||
left: null,
|
||||
})
|
||||
).toBe(null);
|
||||
|
||||
|
@ -231,7 +231,7 @@ describe('Testing validation', () => {
|
||||
},
|
||||
height: 24,
|
||||
// Object
|
||||
rotate: {
|
||||
width: {
|
||||
foo: 1,
|
||||
},
|
||||
},
|
||||
@ -253,7 +253,7 @@ describe('Testing validation', () => {
|
||||
},
|
||||
height: 24,
|
||||
// Object
|
||||
hFlip: null,
|
||||
left: null,
|
||||
},
|
||||
{ fix: true }
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user