Files
ob248.com/node_modules/@iconify/react/dist/offline.cjs
2026-02-05 17:31:20 +00:00

838 lines
23 KiB
JavaScript

'use client';
'use strict';
var react = require('react');
/**
* Default values for dimensions
*/
const defaultIconDimensions = Object.freeze({
left: 0,
top: 0,
width: 16,
height: 16
});
/**
* Default values for transformations
*/
const defaultIconTransformations = Object.freeze({
rotate: 0,
vFlip: false,
hFlip: false
});
/**
* Default values for all optional IconifyIcon properties
*/
const defaultIconProps = Object.freeze({
...defaultIconDimensions,
...defaultIconTransformations
});
/**
* Default values for all properties used in ExtendedIconifyIcon
*/
const defaultExtendedIconProps = Object.freeze({
...defaultIconProps,
body: "",
hidden: false
});
/**
* Resolve icon set icons
*
* Returns parent icon for each icon
*/
function getIconsTree(data, names) {
const icons = data.icons;
const aliases = data.aliases || Object.create(null);
const resolved = Object.create(null);
function resolve(name) {
if (icons[name]) return resolved[name] = [];
if (!(name in resolved)) {
resolved[name] = null;
const parent = aliases[name] && aliases[name].parent;
const value = parent && resolve(parent);
if (value) resolved[name] = [parent].concat(value);
}
return resolved[name];
}
(Object.keys(icons).concat(Object.keys(aliases))).forEach(resolve);
return resolved;
}
/**
* Merge transformations
*/
function mergeIconTransformations(obj1, obj2) {
const result = {};
if (!obj1.hFlip !== !obj2.hFlip) result.hFlip = true;
if (!obj1.vFlip !== !obj2.vFlip) result.vFlip = true;
const rotate = ((obj1.rotate || 0) + (obj2.rotate || 0)) % 4;
if (rotate) result.rotate = rotate;
return result;
}
/**
* Merge icon and alias
*
* Can also be used to merge default values and icon
*/
function mergeIconData(parent, child) {
const result = mergeIconTransformations(parent, child);
for (const key in defaultExtendedIconProps) if (key in defaultIconTransformations) {
if (key in parent && !(key in result)) result[key] = defaultIconTransformations[key];
} else if (key in child) result[key] = child[key];
else if (key in parent) result[key] = parent[key];
return result;
}
/**
* Get icon data, using prepared aliases tree
*/
function internalGetIconData(data, name, tree) {
const icons = data.icons;
const aliases = data.aliases || Object.create(null);
let currentProps = {};
function parse(name$1) {
currentProps = mergeIconData(icons[name$1] || aliases[name$1], currentProps);
}
parse(name);
tree.forEach(parse);
return mergeIconData(data, currentProps);
}
/**
* Extract icons from an icon set
*
* Returns list of icons that were found in icon set
*/
function parseIconSet(data, callback) {
const names = [];
if (typeof data !== "object" || typeof data.icons !== "object") return names;
if (data.not_found instanceof Array) data.not_found.forEach((name) => {
callback(name, null);
names.push(name);
});
const tree = getIconsTree(data);
for (const name in tree) {
const item = tree[name];
if (item) {
callback(name, internalGetIconData(data, name, item));
names.push(name);
}
}
return names;
}
/**
* Optional properties
*/
const optionalPropertyDefaults = {
provider: "",
aliases: {},
not_found: {},
...defaultIconDimensions
};
/**
* Check props
*/
function checkOptionalProps(item, defaults) {
for (const prop in defaults) if (prop in item && typeof item[prop] !== typeof defaults[prop]) return false;
return true;
}
/**
* Validate icon set, return it as IconifyJSON on success, null on failure
*
* Unlike validateIconSet(), this function is very basic.
* It does not throw exceptions, it does not check metadata, it does not fix stuff.
*/
function quicklyValidateIconSet(obj) {
if (typeof obj !== "object" || obj === null) return null;
const data = obj;
if (typeof data.prefix !== "string" || !obj.icons || typeof obj.icons !== "object") return null;
if (!checkOptionalProps(obj, optionalPropertyDefaults)) return null;
const icons = data.icons;
for (const name in icons) {
const icon = icons[name];
if (!name || typeof icon.body !== "string" || !checkOptionalProps(icon, defaultExtendedIconProps)) return null;
}
const aliases = data.aliases || Object.create(null);
for (const name in aliases) {
const icon = aliases[name];
const parent = icon.parent;
if (!name || typeof parent !== "string" || !icons[parent] && !aliases[parent] || !checkOptionalProps(icon, defaultExtendedIconProps)) return null;
}
return data;
}
/**
* Default icon customisations values
*/
const defaultIconSizeCustomisations = Object.freeze({
width: null,
height: null
});
const defaultIconCustomisations = Object.freeze({
...defaultIconSizeCustomisations,
...defaultIconTransformations
});
/**
* Convert IconifyIconCustomisations to FullIconCustomisations, checking value types
*/
function mergeCustomisations(defaults, item) {
const result = { ...defaults };
for (const key in item) {
const value = item[key];
const valueType = typeof value;
if (key in defaultIconSizeCustomisations) {
if (value === null || value && (valueType === "string" || valueType === "number")) result[key] = value;
} else if (valueType === typeof result[key]) result[key] = key === "rotate" ? value % 4 : value;
}
return result;
}
const separator = /[\s,]+/;
/**
* Apply "flip" string to icon customisations
*/
function flipFromString(custom, flip) {
flip.split(separator).forEach((str) => {
const value = str.trim();
switch (value) {
case "horizontal":
custom.hFlip = true;
break;
case "vertical":
custom.vFlip = true;
break;
}
});
}
/**
* Get rotation value
*/
function rotateFromString(value, defaultValue = 0) {
const units = value.replace(/^-?[0-9.]*/, "");
function cleanup(value$1) {
while (value$1 < 0) value$1 += 4;
return value$1 % 4;
}
if (units === "") {
const num = parseInt(value);
return isNaN(num) ? 0 : cleanup(num);
} else if (units !== value) {
let split = 0;
switch (units) {
case "%":
split = 25;
break;
case "deg": split = 90;
}
if (split) {
let num = parseFloat(value.slice(0, value.length - units.length));
if (isNaN(num)) return 0;
num = num / split;
return num % 1 === 0 ? cleanup(num) : 0;
}
}
return defaultValue;
}
/**
* Regular expressions for calculating dimensions
*/
const unitsSplit = /(-?[0-9.]*[0-9]+[0-9.]*)/g;
const unitsTest = /^-?[0-9.]*[0-9]+[0-9.]*$/g;
function calculateSize(size, ratio, precision) {
if (ratio === 1) return size;
precision = precision || 100;
if (typeof size === "number") return Math.ceil(size * ratio * precision) / precision;
if (typeof size !== "string") return size;
const oldParts = size.split(unitsSplit);
if (oldParts === null || !oldParts.length) return size;
const newParts = [];
let code = oldParts.shift();
let isNumber = unitsTest.test(code);
while (true) {
if (isNumber) {
const num = parseFloat(code);
if (isNaN(num)) newParts.push(code);
else newParts.push(Math.ceil(num * ratio * precision) / precision);
} else newParts.push(code);
code = oldParts.shift();
if (code === void 0) return newParts.join("");
isNumber = !isNumber;
}
}
function splitSVGDefs(content, tag = "defs") {
let defs = "";
const index = content.indexOf("<" + tag);
while (index >= 0) {
const start = content.indexOf(">", index);
const end = content.indexOf("</" + tag);
if (start === -1 || end === -1) break;
const endEnd = content.indexOf(">", end);
if (endEnd === -1) break;
defs += content.slice(start + 1, end).trim();
content = content.slice(0, index).trim() + content.slice(endEnd + 1);
}
return {
defs,
content
};
}
/**
* Merge defs and content
*/
function mergeDefsAndContent(defs, content) {
return defs ? "<defs>" + defs + "</defs>" + content : content;
}
/**
* Wrap SVG content, without wrapping definitions
*/
function wrapSVGContent(body, start, end) {
const split = splitSVGDefs(body);
return mergeDefsAndContent(split.defs, start + split.content + end);
}
/**
* Check if value should be unset. Allows multiple keywords
*/
const isUnsetKeyword = (value) => value === "unset" || value === "undefined" || value === "none";
/**
* Get SVG attributes and content from icon + customisations
*
* Does not generate style to make it compatible with frameworks that use objects for style, such as React.
* Instead, it generates 'inline' value. If true, rendering engine should add verticalAlign: -0.125em to icon.
*
* Customisations should be normalised by platform specific parser.
* Result should be converted to <svg> by platform specific parser.
* Use replaceIDs to generate unique IDs for body.
*/
function iconToSVG(icon, customisations) {
const fullIcon = {
...defaultIconProps,
...icon
};
const fullCustomisations = {
...defaultIconCustomisations,
...customisations
};
const box = {
left: fullIcon.left,
top: fullIcon.top,
width: fullIcon.width,
height: fullIcon.height
};
let body = fullIcon.body;
[fullIcon, fullCustomisations].forEach((props) => {
const transformations = [];
const hFlip = props.hFlip;
const vFlip = props.vFlip;
let rotation = props.rotate;
if (hFlip) if (vFlip) rotation += 2;
else {
transformations.push("translate(" + (box.width + box.left).toString() + " " + (0 - box.top).toString() + ")");
transformations.push("scale(-1 1)");
box.top = box.left = 0;
}
else if (vFlip) {
transformations.push("translate(" + (0 - box.left).toString() + " " + (box.height + box.top).toString() + ")");
transformations.push("scale(1 -1)");
box.top = box.left = 0;
}
let tempValue;
if (rotation < 0) rotation -= Math.floor(rotation / 4) * 4;
rotation = rotation % 4;
switch (rotation) {
case 1:
tempValue = box.height / 2 + box.top;
transformations.unshift("rotate(90 " + tempValue.toString() + " " + tempValue.toString() + ")");
break;
case 2:
transformations.unshift("rotate(180 " + (box.width / 2 + box.left).toString() + " " + (box.height / 2 + box.top).toString() + ")");
break;
case 3:
tempValue = box.width / 2 + box.left;
transformations.unshift("rotate(-90 " + tempValue.toString() + " " + tempValue.toString() + ")");
break;
}
if (rotation % 2 === 1) {
if (box.left !== box.top) {
tempValue = box.left;
box.left = box.top;
box.top = tempValue;
}
if (box.width !== box.height) {
tempValue = box.width;
box.width = box.height;
box.height = tempValue;
}
}
if (transformations.length) body = wrapSVGContent(body, "<g transform=\"" + transformations.join(" ") + "\">", "</g>");
});
const customisationsWidth = fullCustomisations.width;
const customisationsHeight = fullCustomisations.height;
const boxWidth = box.width;
const boxHeight = box.height;
let width;
let height;
if (customisationsWidth === null) {
height = customisationsHeight === null ? "1em" : customisationsHeight === "auto" ? boxHeight : customisationsHeight;
width = calculateSize(height, boxWidth / boxHeight);
} else {
width = customisationsWidth === "auto" ? boxWidth : customisationsWidth;
height = customisationsHeight === null ? calculateSize(width, boxHeight / boxWidth) : customisationsHeight === "auto" ? boxHeight : customisationsHeight;
}
const attributes = {};
const setAttr = (prop, value) => {
if (!isUnsetKeyword(value)) attributes[prop] = value.toString();
};
setAttr("width", width);
setAttr("height", height);
const viewBox = [
box.left,
box.top,
boxWidth,
boxHeight
];
attributes.viewBox = viewBox.join(" ");
return {
attributes,
viewBox,
body
};
}
/**
* IDs usage:
*
* id="{id}"
* xlink:href="#{id}"
* url(#{id})
*
* From SVG animations:
*
* begin="0;{id}.end"
* begin="{id}.end"
* begin="{id}.click"
*/
/**
* Regular expression for finding ids
*/
const regex = /\sid="(\S+)"/g;
/**
* New random-ish prefix for ids
*
* Do not use dash, it cannot be used in SVG 2 animations
*/
const randomPrefix = "IconifyId" + Date.now().toString(16) + (Math.random() * 16777216 | 0).toString(16);
/**
* Counter for ids, increasing with every replacement
*/
let counter = 0;
/**
* Replace IDs in SVG output with unique IDs
*/
function replaceIDs(body, prefix = randomPrefix) {
const ids = [];
let match;
while (match = regex.exec(body)) ids.push(match[1]);
if (!ids.length) return body;
const suffix = "suffix" + (Math.random() * 16777216 | Date.now()).toString(16);
ids.forEach((id) => {
const newID = typeof prefix === "function" ? prefix(id) : prefix + (counter++).toString();
const escapedID = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
body = body.replace(new RegExp("([#;\"])(" + escapedID + ")([\")]|\\.[a-z])", "g"), "$1" + newID + suffix + "$3");
});
body = body.replace(new RegExp(suffix, "g"), "");
return body;
}
/**
* Generate <svg>
*/
function iconToHTML(body, attributes) {
let renderAttribsHTML = body.indexOf("xlink:") === -1 ? "" : " xmlns:xlink=\"http://www.w3.org/1999/xlink\"";
for (const attr in attributes) renderAttribsHTML += " " + attr + "=\"" + attributes[attr] + "\"";
return "<svg xmlns=\"http://www.w3.org/2000/svg\"" + renderAttribsHTML + ">" + body + "</svg>";
}
/**
* Encode SVG for use in url()
*
* Short alternative to encodeURIComponent() that encodes only stuff used in SVG, generating
* smaller code.
*/
function encodeSVGforURL(svg) {
return svg.replace(/"/g, "'").replace(/%/g, "%25").replace(/#/g, "%23").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/\s+/g, " ");
}
/**
* Generate data: URL from SVG
*/
function svgToData(svg) {
return "data:image/svg+xml," + encodeSVGforURL(svg);
}
/**
* Generate url() from SVG
*/
function svgToURL(svg) {
return "url(\"" + svgToData(svg) + "\")";
}
let policy;
/**
* Attempt to create policy
*/
function createPolicy() {
try {
policy = window.trustedTypes.createPolicy("iconify", { createHTML: (s) => s });
} catch (err) {
policy = null;
}
}
/**
* Clean up value for innerHTML assignment
*
* This code doesn't actually clean up anything.
* It is intended be used with Iconify icon data, which has already been validated
*/
function cleanUpInnerHTML(html) {
if (policy === void 0) createPolicy();
return policy ? policy.createHTML(html) : html;
}
const defaultExtendedIconCustomisations = {
...defaultIconCustomisations,
inline: false,
};
/**
* Expression to test part of icon name.
*
* Used when loading icons from Iconify API due to project naming convension.
* Ignored when using custom icon sets - convension does not apply.
*/
/**
* Convert string icon name to IconifyIconName object.
*/
const stringToIcon = (value, validate, allowSimpleName, provider = "") => {
const colonSeparated = value.split(":");
if (value.slice(0, 1) === "@") {
if (colonSeparated.length < 2 || colonSeparated.length > 3) return null;
provider = colonSeparated.shift().slice(1);
}
if (colonSeparated.length > 3 || !colonSeparated.length) return null;
if (colonSeparated.length > 1) {
const name$1 = colonSeparated.pop();
const prefix = colonSeparated.pop();
const result = {
provider: colonSeparated.length > 0 ? colonSeparated[0] : provider,
prefix,
name: name$1
};
return result;
}
const name = colonSeparated[0];
const dashSeparated = name.split("-");
if (dashSeparated.length > 1) {
const result = {
provider,
prefix: dashSeparated.shift(),
name: dashSeparated.join("-")
};
return result;
}
if (provider === "") {
const result = {
provider,
prefix: "",
name
};
return result;
}
return null;
};
/**
* Default SVG attributes
*/
const svgDefaults = {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
};
/**
* Style modes
*/
const commonProps = {
display: 'inline-block',
};
const monotoneProps = {
backgroundColor: 'currentColor',
};
const coloredProps = {
backgroundColor: 'transparent',
};
// Dynamically add common props to variables above
const propsToAdd = {
Image: 'var(--svg)',
Repeat: 'no-repeat',
Size: '100% 100%',
};
const propsToAddTo = {
WebkitMask: monotoneProps,
mask: monotoneProps,
background: coloredProps,
};
for (const prefix in propsToAddTo) {
const list = propsToAddTo[prefix];
for (const prop in propsToAdd) {
list[prefix + prop] = propsToAdd[prop];
}
}
/**
* Default values for customisations for inline icon
*/
const inlineDefaults = {
...defaultExtendedIconCustomisations,
inline: true,
};
/**
* Fix size: add 'px' to numbers
*/
function fixSize(value) {
return value + (value.match(/^[-0-9.]+$/) ? 'px' : '');
}
/**
* Render icon
*/
const render = (
// Icon must be validated before calling this function
icon,
// Partial properties
props,
// Icon name
name) => {
// Get default properties
const defaultProps = props.inline
? inlineDefaults
: defaultExtendedIconCustomisations;
// Get all customisations
const customisations = mergeCustomisations(defaultProps, props);
// Check mode
const mode = props.mode || 'svg';
// Create style
const style = {};
const customStyle = props.style || {};
// Create SVG component properties
const componentProps = {
...(mode === 'svg' ? svgDefaults : {}),
};
if (name) {
const iconName = stringToIcon(name);
if (iconName) {
const classNames = ['iconify'];
const props = [
'provider',
'prefix',
];
for (const prop of props) {
if (iconName[prop]) {
classNames.push('iconify--' + iconName[prop]);
}
}
componentProps.className = classNames.join(' ');
}
}
// Get element properties
for (let key in props) {
const value = props[key];
if (value === void 0) {
continue;
}
switch (key) {
// Properties to ignore
case 'icon':
case 'style':
case 'children':
case 'onLoad':
case 'mode':
case 'ssr':
case 'fallback':
break;
// Forward ref
case '_ref':
componentProps.ref = value;
break;
// Merge class names
case 'className':
componentProps[key] =
(componentProps[key] ? componentProps[key] + ' ' : '') +
value;
break;
// Boolean attributes
case 'inline':
case 'hFlip':
case 'vFlip':
customisations[key] =
value === true || value === 'true' || value === 1;
break;
// Flip as string: 'horizontal,vertical'
case 'flip':
if (typeof value === 'string') {
flipFromString(customisations, value);
}
break;
// Color: copy to style
case 'color':
style.color = value;
break;
// Rotation as string
case 'rotate':
if (typeof value === 'string') {
customisations[key] = rotateFromString(value);
}
else if (typeof value === 'number') {
customisations[key] = value;
}
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:
if (defaultProps[key] === void 0) {
componentProps[key] = value;
}
}
}
// Generate icon
const item = iconToSVG(icon, customisations);
const renderAttribs = item.attributes;
// Inline display
if (customisations.inline) {
style.verticalAlign = '-0.125em';
}
if (mode === 'svg') {
// Add style
componentProps.style = {
...style,
...customStyle,
};
// Add icon stuff
Object.assign(componentProps, renderAttribs);
// Counter for ids based on "id" property to render icons consistently on server and client
let localCounter = 0;
let id = props.id;
if (typeof id === 'string') {
// Convert '-' to '_' to avoid errors in animations
id = id.replace(/-/g, '_');
}
// Add icon stuff
componentProps.dangerouslySetInnerHTML = {
__html: cleanUpInnerHTML(replaceIDs(item.body, id ? () => id + 'ID' + localCounter++ : 'iconifyReact')),
};
return react.createElement('svg', componentProps);
}
// Render <span> with style
const { body, width, height } = icon;
const useMask = mode === 'mask' ||
(mode === 'bg' ? false : body.indexOf('currentColor') !== -1);
// Generate SVG
const html = iconToHTML(body, {
...renderAttribs,
width: width + '',
height: height + '',
});
// Generate style
componentProps.style = {
...style,
'--svg': svgToURL(html),
'width': fixSize(renderAttribs.width),
'height': fixSize(renderAttribs.height),
...commonProps,
...(useMask ? monotoneProps : coloredProps),
...customStyle,
};
return react.createElement('span', componentProps);
};
/**
* Storage for icons referred by name
*/
const storage = Object.create(null);
function IconComponent(props) {
const icon = props.icon;
const data = typeof icon === 'string' ? storage[icon] : icon;
if (!data) {
return props.children
? props.children
: react.createElement('span', {});
}
return render({
...defaultIconProps,
...data,
}, props, typeof icon === 'string' ? icon : undefined);
}
/**
* Block icon
*
* @param props - Component properties
*/
const Icon = react.memo(react.forwardRef((props, ref) => IconComponent({
...props,
_ref: ref,
})));
/**
* Inline icon (has negative verticalAlign that makes it behave like icon font)
*
* @param props - Component properties
*/
const InlineIcon = react.memo(react.forwardRef((props, ref) => IconComponent({
inline: true,
...props,
_ref: ref,
})));
/**
* Add icon to storage, allowing to call it by name
*
* @param name
* @param data
*/
function addIcon(name, data) {
storage[name] = data;
}
/**
* Add collection to storage, allowing to call icons by name
*
* @param data Icon set
* @param prefix Optional prefix to add to icon names, true (default) if prefix from icon set should be used.
*/
function addCollection(data, prefix) {
const iconPrefix = typeof prefix === 'string'
? prefix
: prefix !== false && typeof data.prefix === 'string'
? data.prefix + ':'
: '';
quicklyValidateIconSet(data) &&
parseIconSet(data, (name, icon) => {
if (icon) {
storage[iconPrefix + name] = icon;
}
});
}
exports.Icon = Icon;
exports.InlineIcon = InlineIcon;
exports.addCollection = addCollection;
exports.addIcon = addIcon;