2021-04-29 21:06:26 +00:00
|
|
|
import type { IconifyIcon } from '@iconify/types';
|
2021-05-11 20:27:13 +00:00
|
|
|
import {
|
|
|
|
defaults,
|
|
|
|
mergeCustomisations,
|
2021-05-24 10:25:02 +00:00
|
|
|
} from '@iconify/utils/lib/customisations';
|
2020-08-22 12:31:44 +00:00
|
|
|
import {
|
|
|
|
flipFromString,
|
|
|
|
alignmentFromString,
|
2021-05-24 10:25:02 +00:00
|
|
|
} from '@iconify/utils/lib/customisations/shorthand';
|
|
|
|
import { rotateFromString } from '@iconify/utils/lib/customisations/rotate';
|
2020-08-22 12:31:44 +00:00
|
|
|
import { iconToSVG } from '@iconify/core/lib/builder';
|
2021-05-24 10:25:02 +00:00
|
|
|
import { replaceIDs } from '@iconify/utils/lib/svg/id';
|
2021-04-29 18:06:25 +00:00
|
|
|
import type { IconProps } from './props';
|
2020-08-22 12:31:44 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Default SVG attributes
|
|
|
|
*/
|
|
|
|
const svgDefaults = {
|
|
|
|
'xmlns': 'http://www.w3.org/2000/svg',
|
|
|
|
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
|
|
|
'aria-hidden': true,
|
|
|
|
'role': 'img',
|
|
|
|
};
|
|
|
|
|
2021-04-29 18:06:25 +00:00
|
|
|
/**
|
|
|
|
* Result
|
|
|
|
*/
|
2021-04-29 21:06:26 +00:00
|
|
|
export interface RenderResult {
|
2021-04-29 18:06:25 +00:00
|
|
|
attributes: Record<string, unknown>;
|
|
|
|
body: string;
|
|
|
|
}
|
|
|
|
|
2020-08-22 12:31:44 +00:00
|
|
|
/**
|
|
|
|
* Generate icon from properties
|
|
|
|
*/
|
2021-04-29 21:06:26 +00:00
|
|
|
export function render(
|
|
|
|
// Icon must be validated before calling this function
|
|
|
|
icon: Required<IconifyIcon>,
|
|
|
|
// Properties
|
|
|
|
props: IconProps
|
|
|
|
): RenderResult {
|
2021-05-11 20:27:13 +00:00
|
|
|
const customisations = mergeCustomisations(
|
|
|
|
defaults,
|
|
|
|
props as typeof defaults
|
|
|
|
);
|
2021-05-24 10:25:02 +00:00
|
|
|
const componentProps = { ...svgDefaults } as Record<string, unknown>;
|
2020-08-22 12:31:44 +00:00
|
|
|
|
|
|
|
// Create style if missing
|
|
|
|
let style = typeof props.style === 'string' ? props.style : '';
|
|
|
|
|
|
|
|
// Get element properties
|
|
|
|
for (let key in props) {
|
2021-04-29 18:06:25 +00:00
|
|
|
const value = props[key as keyof typeof props] as unknown;
|
2021-05-11 20:27:13 +00:00
|
|
|
if (value === void 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-08-22 12:31:44 +00:00
|
|
|
switch (key) {
|
|
|
|
// Properties to ignore
|
|
|
|
case 'icon':
|
|
|
|
case 'style':
|
2021-05-10 15:29:30 +00:00
|
|
|
case 'onLoad':
|
2020-08-22 12:31:44 +00:00
|
|
|
break;
|
|
|
|
|
2021-05-11 20:27:13 +00:00
|
|
|
// Boolean attributes
|
|
|
|
case 'inline':
|
|
|
|
case 'hFlip':
|
|
|
|
case 'vFlip':
|
|
|
|
customisations[key] =
|
|
|
|
value === true || value === 'true' || value === 1;
|
|
|
|
break;
|
|
|
|
|
2020-08-22 12:31:44 +00:00
|
|
|
// Flip as string: 'horizontal,vertical'
|
|
|
|
case 'flip':
|
2021-04-29 18:06:25 +00:00
|
|
|
if (typeof value === 'string') {
|
|
|
|
flipFromString(customisations, value);
|
|
|
|
}
|
2020-08-22 12:31:44 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Alignment as string
|
|
|
|
case 'align':
|
2021-04-29 18:06:25 +00:00
|
|
|
if (typeof value === 'string') {
|
|
|
|
alignmentFromString(customisations, value);
|
|
|
|
}
|
2020-08-22 12:31:44 +00:00
|
|
|
break;
|
|
|
|
|
2021-04-30 09:51:31 +00:00
|
|
|
// Color: copy to style, add extra ';' in case style is missing it
|
2020-08-22 12:31:44 +00:00
|
|
|
case 'color':
|
2021-04-30 09:51:31 +00:00
|
|
|
style =
|
|
|
|
style +
|
|
|
|
(style.length > 0 && style.trim().slice(-1) !== ';'
|
|
|
|
? ';'
|
|
|
|
: '') +
|
|
|
|
'color: ' +
|
|
|
|
value +
|
|
|
|
'; ';
|
2020-08-22 12:31:44 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Rotation as string
|
|
|
|
case 'rotate':
|
2021-04-29 18:06:25 +00:00
|
|
|
if (typeof value === 'string') {
|
2020-08-22 12:31:44 +00:00
|
|
|
customisations[key] = rotateFromString(value);
|
2021-04-29 18:06:25 +00:00
|
|
|
} else if (typeof value === 'number') {
|
2021-05-06 13:49:46 +00:00
|
|
|
customisations[key] = value;
|
2020-08-22 12:31:44 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Remove aria-hidden
|
|
|
|
case 'ariaHidden':
|
|
|
|
case 'aria-hidden':
|
|
|
|
if (value !== true && value !== 'true') {
|
|
|
|
delete componentProps['aria-hidden'];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Copy missing property if it does not exist in customisations
|
|
|
|
default:
|
2021-04-29 18:06:25 +00:00
|
|
|
if ((defaults as Record<string, unknown>)[key] === void 0) {
|
2020-08-22 12:31:44 +00:00
|
|
|
componentProps[key] = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate icon
|
2021-04-29 21:06:26 +00:00
|
|
|
const item = iconToSVG(icon, customisations);
|
2020-08-22 12:31:44 +00:00
|
|
|
|
|
|
|
// Add icon stuff
|
|
|
|
for (let key in item.attributes) {
|
2021-04-29 18:06:25 +00:00
|
|
|
componentProps[key] =
|
|
|
|
item.attributes[key as keyof typeof item.attributes];
|
2020-08-22 12:31:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (item.inline) {
|
2021-04-30 09:51:31 +00:00
|
|
|
// Style overrides it
|
2020-08-22 12:31:44 +00:00
|
|
|
style = 'vertical-align: -0.125em; ' + style;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Style
|
|
|
|
if (style !== '') {
|
|
|
|
componentProps.style = style;
|
|
|
|
}
|
|
|
|
|
2021-02-15 08:31:05 +00:00
|
|
|
// Counter for ids based on "id" property to render icons consistently on server and client
|
|
|
|
let localCounter = 0;
|
|
|
|
const id = props.id;
|
|
|
|
|
2020-08-22 12:31:44 +00:00
|
|
|
// Generate HTML
|
|
|
|
return {
|
|
|
|
attributes: componentProps,
|
2021-02-15 08:31:05 +00:00
|
|
|
body: replaceIDs(
|
|
|
|
item.body,
|
|
|
|
id ? () => id + '-' + localCounter++ : 'iconify-svelte-'
|
|
|
|
),
|
2020-08-22 12:31:44 +00:00
|
|
|
};
|
|
|
|
}
|