561 lines
15 KiB
Plaintext
561 lines
15 KiB
Plaintext
|
'use strict';
|
||
|
|
||
|
const core = require('@unocss/core');
|
||
|
const ruleUtils = require('@unocss/rule-utils');
|
||
|
|
||
|
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 bracketTypeRe = /^\[(color|length|position|quoted|string):/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 (unitOnlyRE.test(str))
|
||
|
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 (unitOnlyRE.test(str))
|
||
|
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)}%`;
|
||
|
}
|
||
|
}
|
||
|
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 (/^\$[^\s'"`;{}]/.test(str)) {
|
||
|
const [name, defaultValue] = str.slice(1).split(",");
|
||
|
return `var(--${core.escapeSelector(name)}${defaultValue ? `, ${defaultValue}` : ""})`;
|
||
|
}
|
||
|
}
|
||
|
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 = ruleUtils.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 [front, rest] = ruleUtils.getStringComponent(body, "[", "]", ["/", ":"]) ?? [];
|
||
|
if (front != null) {
|
||
|
const match = (front.match(bracketTypeRe) ?? [])[1];
|
||
|
if (match == null || match === type)
|
||
|
return [front, rest];
|
||
|
}
|
||
|
}
|
||
|
function parseColor(body, theme, key) {
|
||
|
const split = splitShorthand(body, "color");
|
||
|
if (!split)
|
||
|
return;
|
||
|
const [main, opacity] = split;
|
||
|
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 (/^#[\da-fA-F]+/.test(bracketOrMain))
|
||
|
color = bracketOrMain;
|
||
|
else if (/^hex-[\da-fA-F]+/.test(bracketOrMain))
|
||
|
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 (/^\d+$/.test(scale)) {
|
||
|
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: ruleUtils.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] = ruleUtils.colorToString(cssColor, alpha);
|
||
|
} else {
|
||
|
const opacityVar = `--un-${varName}-opacity`;
|
||
|
const result = ruleUtils.colorToString(cssColor, `var(${opacityVar})`);
|
||
|
if (result.includes(opacityVar))
|
||
|
css[opacityVar] = ruleUtils.colorOpacityToString(cssColor);
|
||
|
css[property] = result;
|
||
|
}
|
||
|
} else if (color) {
|
||
|
if (alpha != null) {
|
||
|
css[property] = ruleUtils.colorToString(color, alpha);
|
||
|
} else {
|
||
|
const opacityVar = `--un-${varName}-opacity`;
|
||
|
const result = ruleUtils.colorToString(color, `var(${opacityVar})`);
|
||
|
if (result.includes(opacityVar))
|
||
|
css[opacityVar] = 1;
|
||
|
css[property] = result;
|
||
|
}
|
||
|
}
|
||
|
if (shouldPass?.(css) !== false)
|
||
|
return css;
|
||
|
};
|
||
|
}
|
||
|
function colorableShadows(shadows, colorVar) {
|
||
|
const colored = [];
|
||
|
shadows = core.toArray(shadows);
|
||
|
for (let i = 0; i < shadows.length; i++) {
|
||
|
const components = ruleUtils.getStringComponents(shadows[i], " ", 6);
|
||
|
if (!components || components.length < 3)
|
||
|
return shadows;
|
||
|
if (ruleUtils.parseCssColor(components.at(0)))
|
||
|
return shadows;
|
||
|
let colorVarValue = "";
|
||
|
if (ruleUtils.parseCssColor(components.at(-1))) {
|
||
|
const color = ruleUtils.parseCssColor(components.pop());
|
||
|
if (color)
|
||
|
colorVarValue = `, ${ruleUtils.colorToString(color)}`;
|
||
|
}
|
||
|
colored.push(`${components.join(" ")} var(${colorVar}${colorVarValue})`);
|
||
|
}
|
||
|
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 value != null && cssMathFnRE.test(value);
|
||
|
}
|
||
|
function isSize(str) {
|
||
|
if (str[0] === "[" && str.slice(-1) === "]")
|
||
|
str = str.slice(1, -1);
|
||
|
return cssMathFnRE.test(str) || numberWithUnitRE.test(str);
|
||
|
}
|
||
|
|
||
|
exports.CONTROL_MINI_NO_NEGATIVE = CONTROL_MINI_NO_NEGATIVE;
|
||
|
exports.colorResolver = colorResolver;
|
||
|
exports.colorableShadows = colorableShadows;
|
||
|
exports.cornerMap = cornerMap;
|
||
|
exports.cssMathFnRE = cssMathFnRE;
|
||
|
exports.directionMap = directionMap;
|
||
|
exports.directionSize = directionSize;
|
||
|
exports.globalKeywords = globalKeywords;
|
||
|
exports.h = h;
|
||
|
exports.handler = handler;
|
||
|
exports.hasParseableColor = hasParseableColor;
|
||
|
exports.insetMap = insetMap;
|
||
|
exports.isCSSMathFn = isCSSMathFn;
|
||
|
exports.isSize = isSize;
|
||
|
exports.makeGlobalStaticRules = makeGlobalStaticRules;
|
||
|
exports.parseColor = parseColor;
|
||
|
exports.positionMap = positionMap;
|
||
|
exports.resolveBreakpoints = resolveBreakpoints;
|
||
|
exports.resolveVerticalBreakpoints = resolveVerticalBreakpoints;
|
||
|
exports.splitShorthand = splitShorthand;
|
||
|
exports.valueHandlers = valueHandlers;
|
||
|
exports.xyzMap = xyzMap;
|