import { createValueHandler, parseCssColor, colorToString, colorOpacityToString, getStringComponents } from '@unocss/rule-utils'; import { escapeSelector, toArray } from '@unocss/core'; const directionMap = { "l": ["-left"], "r": ["-right"], "t": ["-top"], "b": ["-bottom"], "s": ["-inline-start"], "e": ["-inline-end"], "x": ["-left", "-right"], "y": ["-top", "-bottom"], "": [""], "bs": ["-block-start"], "be": ["-block-end"], "is": ["-inline-start"], "ie": ["-inline-end"], "block": ["-block-start", "-block-end"], "inline": ["-inline-start", "-inline-end"] }; const insetMap = { ...directionMap, s: ["-inset-inline-start"], start: ["-inset-inline-start"], e: ["-inset-inline-end"], end: ["-inset-inline-end"], bs: ["-inset-block-start"], be: ["-inset-block-end"], is: ["-inset-inline-start"], ie: ["-inset-inline-end"], block: ["-inset-block-start", "-inset-block-end"], inline: ["-inset-inline-start", "-inset-inline-end"] }; const cornerMap = { "l": ["-top-left", "-bottom-left"], "r": ["-top-right", "-bottom-right"], "t": ["-top-left", "-top-right"], "b": ["-bottom-left", "-bottom-right"], "tl": ["-top-left"], "lt": ["-top-left"], "tr": ["-top-right"], "rt": ["-top-right"], "bl": ["-bottom-left"], "lb": ["-bottom-left"], "br": ["-bottom-right"], "rb": ["-bottom-right"], "": [""], "bs": ["-start-start", "-start-end"], "be": ["-end-start", "-end-end"], "s": ["-end-start", "-start-start"], "is": ["-end-start", "-start-start"], "e": ["-start-end", "-end-end"], "ie": ["-start-end", "-end-end"], "ss": ["-start-start"], "bs-is": ["-start-start"], "is-bs": ["-start-start"], "se": ["-start-end"], "bs-ie": ["-start-end"], "ie-bs": ["-start-end"], "es": ["-end-start"], "be-is": ["-end-start"], "is-be": ["-end-start"], "ee": ["-end-end"], "be-ie": ["-end-end"], "ie-be": ["-end-end"] }; const xyzMap = { "x": ["-x"], "y": ["-y"], "z": ["-z"], "": ["-x", "-y"] }; const basePositionMap = [ "top", "top center", "top left", "top right", "bottom", "bottom center", "bottom left", "bottom right", "left", "left center", "left top", "left bottom", "right", "right center", "right top", "right bottom", "center", "center top", "center bottom", "center left", "center right", "center center" ]; const positionMap = Object.assign( {}, ...basePositionMap.map((p) => ({ [p.replace(/ /, "-")]: p })), ...basePositionMap.map((p) => ({ [p.replace(/\b(\w)\w+/g, "$1").replace(/ /, "")]: p })) ); const globalKeywords = [ "inherit", "initial", "revert", "revert-layer", "unset" ]; const cssMathFnRE = /(?:calc|clamp|min|max)\s*\(.*\)/; const numberWithUnitRE = /^(-?\d*(?:\.\d+)?)(px|pt|pc|%|r?(?:em|ex|lh|cap|ch|ic)|(?:[sld]?v|cq)(?:[whib]|min|max)|in|cm|mm|rpx)?$/i; const numberRE = /^(-?\d*(?:\.\d+)?)$/i; const unitOnlyRE = /^(px)$/i; const cssProps = [ // basic props "color", "border-color", "background-color", "flex-grow", "flex", "flex-shrink", "caret-color", "font", "gap", "opacity", "visibility", "z-index", "font-weight", "zoom", "text-shadow", "transform", "box-shadow", // positions "background-position", "left", "right", "top", "bottom", "object-position", // sizes "max-height", "min-height", "max-width", "min-width", "height", "width", "border-width", "margin", "padding", "outline-width", "outline-offset", "font-size", "line-height", "text-indent", "vertical-align", "border-spacing", "letter-spacing", "word-spacing", // enhances "stroke", "filter", "backdrop-filter", "fill", "mask", "mask-size", "mask-border", "clip-path", "clip", "border-radius" ]; function round(n) { return n.toFixed(10).replace(/\.0+$/, "").replace(/(\.\d+?)0+$/, "$1"); } function numberWithUnit(str) { const match = str.match(numberWithUnitRE); if (!match) return; const [, n, unit] = match; const num = Number.parseFloat(n); if (unit && !Number.isNaN(num)) return `${round(num)}${unit}`; } function auto(str) { if (str === "auto" || str === "a") return "auto"; } function rem(str) { if (str.match(unitOnlyRE)) return `1${str}`; const match = str.match(numberWithUnitRE); if (!match) return; const [, n, unit] = match; const num = Number.parseFloat(n); if (!Number.isNaN(num)) { if (num === 0) return "0"; return unit ? `${round(num)}${unit}` : `${round(num / 4)}rem`; } } function px(str) { if (str.match(unitOnlyRE)) return `1${str}`; const match = str.match(numberWithUnitRE); if (!match) return; const [, n, unit] = match; const num = Number.parseFloat(n); if (!Number.isNaN(num)) return unit ? `${round(num)}${unit}` : `${round(num)}px`; } function number(str) { if (!numberRE.test(str)) return; const num = Number.parseFloat(str); if (!Number.isNaN(num)) return round(num); } function percent(str) { if (str.endsWith("%")) str = str.slice(0, -1); if (!numberRE.test(str)) return; const num = Number.parseFloat(str); if (!Number.isNaN(num)) return `${round(num / 100)}`; } function fraction(str) { if (str === "full") return "100%"; const [left, right] = str.split("/"); const num = Number.parseFloat(left) / Number.parseFloat(right); if (!Number.isNaN(num)) { if (num === 0) return "0"; return `${round(num * 100)}%`; } } const bracketTypeRe = /^\[(color|length|position|quoted|string):/i; function bracketWithType(str, requiredType) { if (str && str.startsWith("[") && str.endsWith("]")) { let base; let hintedType; const match = str.match(bracketTypeRe); if (!match) { base = str.slice(1, -1); } else { if (!requiredType) hintedType = match[1]; base = str.slice(match[0].length, -1); } if (!base) return; if (base === '=""') return; if (base.startsWith("--")) base = `var(${base})`; let curly = 0; for (const i of base) { if (i === "[") { curly += 1; } else if (i === "]") { curly -= 1; if (curly < 0) return; } } if (curly) return; switch (hintedType) { case "string": return base.replace(/(^|[^\\])_/g, "$1 ").replace(/\\_/g, "_"); case "quoted": return base.replace(/(^|[^\\])_/g, "$1 ").replace(/\\_/g, "_").replace(/(["\\])/g, "\\$1").replace(/^(.+)$/, '"$1"'); } return base.replace(/(url\(.*?\))/g, (v) => v.replace(/_/g, "\\_")).replace(/(^|[^\\])_/g, "$1 ").replace(/\\_/g, "_").replace(/(?:calc|clamp|max|min)\((.*)/g, (match2) => { const vars = []; return match2.replace(/var\((--.+?)[,)]/g, (match3, g1) => { vars.push(g1); return match3.replace(g1, "--un-calc"); }).replace(/(-?\d*\.?\d(?!\b-\d.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g, "$1 $2 ").replace(/--un-calc/g, () => vars.shift()); }); } } function bracket(str) { return bracketWithType(str); } function bracketOfColor(str) { return bracketWithType(str, "color"); } function bracketOfLength(str) { return bracketWithType(str, "length"); } function bracketOfPosition(str) { return bracketWithType(str, "position"); } function cssvar(str) { if (str.match(/^\$[^\s'"`;{}]/)) return `var(--${escapeSelector(str.slice(1))})`; } function time(str) { const match = str.match(/^(-?[0-9.]+)(s|ms)?$/i); if (!match) return; const [, n, unit] = match; const num = Number.parseFloat(n); if (!Number.isNaN(num)) { if (num === 0 && !unit) return "0s"; return unit ? `${round(num)}${unit}` : `${round(num)}ms`; } } function degree(str) { const match = str.match(/^(-?[0-9.]+)(deg|rad|grad|turn)?$/i); if (!match) return; const [, n, unit] = match; const num = Number.parseFloat(n); if (!Number.isNaN(num)) { if (num === 0) return "0"; return unit ? `${round(num)}${unit}` : `${round(num)}deg`; } } function global(str) { if (globalKeywords.includes(str)) return str; } function properties(str) { if (str.split(",").every((prop) => cssProps.includes(prop))) return str; } function position(str) { if (["top", "left", "right", "bottom", "center"].includes(str)) return str; } const valueHandlers = { __proto__: null, auto: auto, bracket: bracket, bracketOfColor: bracketOfColor, bracketOfLength: bracketOfLength, bracketOfPosition: bracketOfPosition, cssvar: cssvar, degree: degree, fraction: fraction, global: global, number: number, numberWithUnit: numberWithUnit, percent: percent, position: position, properties: properties, px: px, rem: rem, time: time }; const handler = createValueHandler(valueHandlers); const h = handler; const CONTROL_MINI_NO_NEGATIVE = "$$mini-no-negative"; function directionSize(propertyPrefix) { return ([_, direction, size], { theme }) => { const v = theme.spacing?.[size || "DEFAULT"] ?? h.bracket.cssvar.global.auto.fraction.rem(size); if (v != null) return directionMap[direction].map((i) => [`${propertyPrefix}${i}`, v]); }; } function getThemeColorForKey(theme, colors, key = "colors") { let obj = theme[key]; let index = -1; for (const c of colors) { index += 1; if (obj && typeof obj !== "string") { const camel = colors.slice(index).join("-").replace(/(-[a-z])/g, (n) => n.slice(1).toUpperCase()); if (obj[camel]) return obj[camel]; if (obj[c]) { obj = obj[c]; continue; } } return void 0; } return obj; } function getThemeColor(theme, colors, key) { return getThemeColorForKey(theme, colors, key) || getThemeColorForKey(theme, colors, "colors"); } function splitShorthand(body, type) { const split = body.split(/(?:\/|:)/); if (split[0] === `[${type}`) { return [ split.slice(0, 2).join(":"), split[2] ]; } return split; } function parseColor(body, theme, key) { const [main, opacity] = splitShorthand(body, "color"); const colors = main.replace(/([a-z])([0-9])/g, "$1-$2").split(/-/g); const [name] = colors; if (!name) return; let color; const bracket = h.bracketOfColor(main); const bracketOrMain = bracket || main; if (h.numberWithUnit(bracketOrMain)) return; if (bracketOrMain.match(/^#[\da-fA-F]+/g)) color = bracketOrMain; else if (bracketOrMain.match(/^hex-[\da-fA-F]+/g)) color = `#${bracketOrMain.slice(4)}`; else if (main.startsWith("$")) color = h.cssvar(main); color = color || bracket; if (!color) { const colorData = getThemeColor(theme, [main], key); if (typeof colorData === "string") color = colorData; } let no = "DEFAULT"; if (!color) { let colorData; const [scale] = colors.slice(-1); if (scale.match(/^\d+$/)) { no = scale; colorData = getThemeColor(theme, colors.slice(0, -1), key); if (!colorData || typeof colorData === "string") color = void 0; else color = colorData[no]; } else { colorData = getThemeColor(theme, colors, key); if (!colorData && colors.length <= 2) { [, no = no] = colors; colorData = getThemeColor(theme, [name], key); } if (typeof colorData === "string") color = colorData; else if (no && colorData) color = colorData[no]; } } return { opacity, name, no, color, cssColor: parseCssColor(color), alpha: h.bracket.cssvar.percent(opacity ?? "") }; } function colorResolver(property, varName, key, shouldPass) { return ([, body], { theme }) => { const data = parseColor(body, theme, key); if (!data) return; const { alpha, color, cssColor } = data; const css = {}; if (cssColor) { if (alpha != null) { css[property] = colorToString(cssColor, alpha); } else { css[`--un-${varName}-opacity`] = colorOpacityToString(cssColor); css[property] = colorToString(cssColor, `var(--un-${varName}-opacity)`); } } else if (color) { css[property] = colorToString(color, alpha); } if (shouldPass?.(css) !== false) return css; }; } function colorableShadows(shadows, colorVar) { const colored = []; shadows = toArray(shadows); for (let i = 0; i < shadows.length; i++) { const components = getStringComponents(shadows[i], " ", 6); if (!components || components.length < 3) return shadows; const color = parseCssColor(components.pop()); if (color == null) return shadows; colored.push(`${components.join(" ")} var(${colorVar}, ${colorToString(color)})`); } return colored; } function hasParseableColor(color, theme, key) { return color != null && !!parseColor(color, theme, key)?.color; } function resolveBreakpoints({ theme, generator }, key = "breakpoints") { let breakpoints; if (generator.userConfig && generator.userConfig.theme) breakpoints = generator.userConfig.theme[key]; if (!breakpoints) breakpoints = theme[key]; return breakpoints ? Object.entries(breakpoints).sort((a, b) => Number.parseInt(a[1].replace(/[a-z]+/gi, "")) - Number.parseInt(b[1].replace(/[a-z]+/gi, ""))).map(([point, size]) => ({ point, size })) : void 0; } function resolveVerticalBreakpoints(context) { return resolveBreakpoints(context, "verticalBreakpoints"); } function makeGlobalStaticRules(prefix, property) { return globalKeywords.map((keyword) => [`${prefix}-${keyword}`, { [property ?? prefix]: keyword }]); } function isCSSMathFn(value) { return cssMathFnRE.test(value); } function isSize(str) { if (str[0] === "[" && str.slice(-1) === "]") str = str.slice(1, -1); return cssMathFnRE.test(str) || numberWithUnitRE.test(str); } export { CONTROL_MINI_NO_NEGATIVE as C, hasParseableColor as a, colorableShadows as b, colorResolver as c, positionMap as d, directionMap as e, cornerMap as f, globalKeywords as g, h, isCSSMathFn as i, isSize as j, insetMap as k, directionSize as l, makeGlobalStaticRules as m, numberWithUnitRE as n, cssMathFnRE as o, parseColor as p, handler as q, resolveBreakpoints as r, splitShorthand as s, resolveVerticalBreakpoints as t, valueHandlers as v, xyzMap as x };