UNPKG

@studiocms/ui

Version:

The UI library for StudioCMS. Includes the layouts & components we use to build StudioCMS.

328 lines (327 loc) 9.34 kB
function getSVGViewBox(value) { const result = value.trim().split(/\s+/).map(Number); if (result.length === 4 && result.reduce((prev, value2) => prev && !Number.isNaN(value2), true)) { return result; } return void 0; } 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 }; } function mergeDefsAndContent(defs, content) { return defs ? `<defs>${defs}</defs>${content}` : content; } function wrapSVGContent(body, start, end) { const split = splitSVGDefs(body); return mergeDefsAndContent(split.defs, start + split.content + end); } 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 = Number.parseFloat(code); if (Number.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; } } const defaultIconDimensions = Object.freeze({ left: 0, top: 0, width: 16, height: 16 }); const defaultIconTransformations = Object.freeze({ rotate: 0, vFlip: false, hFlip: false }); const defaultIconProps = Object.freeze({ ...defaultIconDimensions, ...defaultIconTransformations }); const defaultExtendedIconProps = Object.freeze({ ...defaultIconProps, body: "", hidden: false }); 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; } const defaultIconSizeCustomisations = Object.freeze( { width: null, height: null } ); const defaultIconCustomisations = Object.freeze({ // Dimensions ...defaultIconSizeCustomisations, // Transformations ...defaultIconTransformations }); 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; } function getIconsTree(data, names) { const icons = data.icons; const aliases = data.aliases || /* @__PURE__ */ Object.create(null); const resolved = /* @__PURE__ */ Object.create(null); function resolve(name) { if (icons[name]) { return resolved[name] = []; } if (!(name in resolved)) { resolved[name] = null; const parent = aliases[name]?.parent; const value = parent && resolve(parent); if (value) { resolved[name] = [parent].concat(value); } } return resolved[name] || null; } (names || Object.keys(icons).concat(Object.keys(aliases))).forEach(resolve); return resolved; } function internalGetIconData(data, name, tree) { const icons = data.icons; const aliases = data.aliases || /* @__PURE__ */ Object.create(null); let currentProps = {}; function parse(name2) { currentProps = mergeIconData( icons[name2] || aliases[name2], currentProps ); } parse(name); tree.forEach(parse); return mergeIconData(data, currentProps); } function getIconData(data, name) { if (data.icons[name]) { return internalGetIconData(data, name, []); } const tree = getIconsTree(data, [name])[name]; return tree ? internalGetIconData(data, name, tree) : null; } const isUnsetKeyword = (value) => value === "unset" || value === "undefined" || value === "none"; const regex = /\sid="(\S+)"/g; const randomPrefix = `IconifyId${Date.now().toString(16)}${(Math.random() * 16777216 | 0).toString(16)}`; let counter = 0; 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( // Allowed characters before id: [#;"] // Allowed characters after id: [)"], .[a-z] new RegExp(`([#;"])(${escapedID})([")]|\\.[a-z])`, "g"), `$1${newID}${suffix}$3` ); }); body = body.replace(new RegExp(suffix, "g"), ""); return 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 }; } export { calculateSize, defaultExtendedIconProps, defaultIconCustomisations, defaultIconDimensions, defaultIconProps, defaultIconSizeCustomisations, defaultIconTransformations, getIconData, getIconsTree, getSVGViewBox, iconToSVG, internalGetIconData, isUnsetKeyword, mergeDefsAndContent, mergeIconData, mergeIconTransformations, replaceIDs, splitSVGDefs, wrapSVGContent };