UNPKG

@windijs/helpers

Version:

@windijs/helpers

1,104 lines (1,088 loc) 41.3 kB
import { entries, hash, camelToDash, indent, range, parenWrap } from '@windijs/shared'; // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types // pre-defined keywords const initial = "initial"; const inherit = "inherit"; const unset = "unset"; const none = "none"; /** * Merge two object and their nested children */ function mergeObject(a, b) { return Object.entries(b).reduce((o, [k, v]) => { o[k] = v && typeof v === "object" ? mergeObject((o[k] = o[k] || (Array.isArray(v) ? [] : {})), v) : v; return o; }, a); } const SymbolCSS = Symbol.for("css"); const SymbolMeta = Symbol.for("meta"); const SymbolData = Symbol.for("data"); const SymbolProxy = Symbol.for("proxy"); function isProxy(i) { return i != null && SymbolProxy in i; } function isStyleObject(i) { return i != null && (typeof i === "object" || typeof i === "function") && i[SymbolCSS] != null; } function isStyleArray(i) { return Array.isArray(i) && i.find(o => isStyleObject(o) || isStyleArray(o)); } function getStyleVariants(style) { return style[SymbolMeta].variants; } function getStyleProps(style) { const { uid, children, props } = style[SymbolMeta]; if (Array.isArray(children)) style = children[0]; return [uid, ...(props !== null && props !== void 0 ? props : [])].filter(i => i != null); } function getStyleIdent(style) { return (getStyleVariants(style) .map(i => i + ":") .join("") + getStyleProps(style).join(".")); } function getFirstVar(style) { const css = style[SymbolCSS]; for (const [k, v] of entries(css)) if (k.startsWith("--w-")) return [k, v]; return undefined; } function applyVariant(utility) { let css = utility[SymbolCSS]; for (const variant of utility[SymbolMeta].variants) css = { [variant]: css, }; return css; } function useArrayHelper() { // eslint-disable-next-line no-extend-native Array.prototype.toString = function () { return this.join(isStyleArray(this) ? " " : ","); }; } /** * Bundle all utilities to a single css object. * @param utilities - Utilities and Variants * @returns CSSObject */ function bundle(utilities) { let vcss; const css = new Map(); for (const utility of utilities.flat().filter(i => i != null)) { vcss = applyVariant(utility); for (const [k, v] of entries(vcss)) if (v != null) css.set(k, css.has(k) && typeof v === "object" ? mergeObject(css.get(k), v) : v); } return css; } const prop = (strings, ...expr) => strings.map((string, i) => string + (expr[i] || "")).join(""); // https://drafts.csswg.org/cssom/#serialize-an-identifier function escapeCSS(str) { const length = str.length; let index = -1; let codeUnit; let result = ""; const firstCodeUnit = str.charCodeAt(0); while (++index < length) { codeUnit = str.charCodeAt(index); // Note: there’s no need to special-case astral symbols, surrogate // pairs, or lone surrogates. // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER // (U+FFFD). if (codeUnit === 0x0000) { result += "\uFFFD"; continue; } // Comma if (codeUnit === 44) { result += "\\2c "; continue; } if ( // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is // U+007F, […] (codeUnit >= 0x0001 && codeUnit <= 0x001f) || codeUnit === 0x007f || // If the character is the first character and is in the range [0-9] // (U+0030 to U+0039), […] (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) || // If the character is the second character and is in the range [0-9] // (U+0030 to U+0039) and the first character is a `-` (U+002D), […] (index === 1 && codeUnit >= 0x0030 && codeUnit <= 0x0039 && firstCodeUnit === 0x002d)) { // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point result += "\\" + codeUnit.toString(16) + " "; continue; } if ( // If the character is the first character and is a `-` (U+002D), and // there is no second character, […] index === 0 && length === 1 && codeUnit === 0x002d) { result += "\\" + str.charAt(index); continue; } // If the character is not handled by one of the above rules and is // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to // U+005A), or [a-z] (U+0061 to U+007A), […] if (codeUnit >= 0x0080 || codeUnit === 0x002d || codeUnit === 0x005f || (codeUnit >= 0x0030 && codeUnit <= 0x0039) || (codeUnit >= 0x0041 && codeUnit <= 0x005a) || (codeUnit >= 0x0061 && codeUnit <= 0x007a)) { // the character itself result += str.charAt(index); continue; } // Otherwise, the escaped character. // https://drafts.csswg.org/cssom/#escape-a-character result += "\\" + str.charAt(index); } return result; } // eslint-disable-next-line @typescript-eslint/no-use-before-define let CURRENT_NAMER = alphaNamer; let MINI_COUNT = 0; const MINI_MAP = {}; // a, b, c ... a0, a1, ... function alphaCount(n) { if (n < 26) return String.fromCharCode(n + 97); const x = (n - 26) % 36; return alphaCount(Math.floor((n - 26) / 36)) + (x < 10 ? x.toString() : String.fromCharCode(x + 87)); } function alphaNamer(style) { const key = getStyleIdent(style); if (key in MINI_MAP) return MINI_MAP[key]; return (MINI_MAP[key] = alphaCount(MINI_COUNT++)); } function atomicNamer(style) { // TODO: apply varaint for this return escapeCSS(getStyleProps(style).join(".")); } function hashNamer(style) { return "w-" + hash(getStyleIdent(style)); } function useNamer(f) { CURRENT_NAMER = f; } function nameStyle(style) { return CURRENT_NAMER(style); } function inline(x, ...utilities) { const isGet = isStyleObject(x); const styles = []; for (const [key, value] of Object.entries(bundle(utilities))) if (typeof value === "string") isGet ? styles.push(key + ":" + value) : x.style.setProperty(key, value); if (isGet) return styles.join(";"); } function cssBlock(selector, body = [], rootIndent = 0, childIndent = rootIndent + 2) { const statements = []; for (const item of body) if (typeof item === "string") statements.push(indent(item, childIndent)); else statements.push(cssBlock(item.selector, item.body, childIndent + 2)); return [indent(selector, rootIndent) + " {", ...statements, indent("}", rootIndent)].join("\n"); } function createRules(css, selector) { const rules = []; const rootRule = { selector, children: [] }; for (const [key, value] of entries(css)) if (typeof value === "string" || value instanceof String) rootRule.children.push({ property: key, value: value }); else if (typeof value === "number") rootRule.children.push({ property: key, value: value.toString() }); else if (Array.isArray(value)) value.map(i => rootRule.children.push({ property: key, value: i })); else if (typeof value === "object" && value != null) { if (rootRule.children[0]) rules.push({ ...rootRule }); rootRule.children = []; const children = selector.charCodeAt(0) === 64 /* @ */ ? { [selector]: value } : value; if (key.charCodeAt(0) === 64 /* @ */) rules.push({ rule: key, children: createRules(children, selector) }); else rules.push(...createRules(children, key.replace(/&/g, selector))); } rootRule.children[0] && rules.push(rootRule); return rules; } function buildDecl({ value, property }) { if (Array.isArray(value)) return value.map(i => property + ": " + i + ";"); return property.startsWith("webkit") ? "-" : "" + camelToDash(property) + ": " + value + ";"; } function buildRule({ selector, children }, indent = 0) { return cssBlock(selector, children.map(i => buildDecl(i)).flat(), indent); } function buildAtRule({ rule, children }, indent = 0) { // eslint-disable-next-line @typescript-eslint/no-use-before-define return cssBlock(rule, createBlockBody(children, indent + 2), indent, 0); } function createBlockBody(rules, indent = 0) { const blocks = []; for (const rule of rules) if ("selector" in rule) blocks.push(buildRule(rule, indent)); else blocks.push(buildAtRule(rule, indent)); return blocks; } function buildRules(rules) { return createBlockBody(rules).join("\n\n"); } function dedupRules(rules) { const styles = []; const atRules = {}; for (const r of rules) if ("selector" in r) styles.push(r); else if (r.rule in atRules) atRules[r.rule].children.push(...r.children); else atRules[r.rule] = r; return [...styles, ...Object.values(atRules)]; } /** build a single StyleObject to css */ function buildStyle(className, style) { return buildRules(createRules(applyVariant(style), "." + className)); } function atomic(...utilities) { const rules = []; for (const utility of utilities.flat().filter(i => i != null)) rules.push(...createRules(applyVariant(utility), "." + nameStyle(utility))); return buildRules(dedupRules(rules)); } const _unify = (selector, utilities) => buildRules(createRules(bundle(utilities), selector)); function unify(...params) { if (typeof params[0] === "string") return _unify(params[0], params.slice(1)); const map = Object.assign({}, ...params); return Object.entries(map) .map(([k, v]) => (Array.isArray(v) ? _unify(k, v) : _unify(k, [v]))) .join("\n\n"); } function build(...utilities) { const rules = []; for (const utility of utilities.flat().filter(i => i != null)) { const selector = utility[SymbolMeta].selector; if (typeof selector === "string") rules.push(...createRules(applyVariant(utility), selector)); } return buildRules(dedupRules(rules)); } function defineConfig(config) { return config; } // TODO: should return object, like CSSPercentage/CSSDimension... // TODO: support multiple add/sub/mul/div function sub(left, right) { if (typeof left === "object" && typeof right === "object") if (left.type === right.type) return left.value - right.value + (left.type === "percent" ? "%" : left.type); return left + " - " + right; } function add(left, right) { if (typeof left === "object" && typeof right === "object") if (left.type === right.type) return left.value + right.value + (left.type === "percent" ? "%" : left.type); return left + " + " + right; } function mul(left, right) { if (typeof left === "number" && typeof right === "object") return left * right.value + (right.type === "percent" ? "%" : right.type); if (typeof left === "object" && typeof right === "number") return left.value * right + (left.type === "percent" ? "%" : left.type); if (typeof left === "number" && typeof right === "number") return left * right + ""; return left + " * " + right; } function div(left, right) { if (typeof left === "object" && typeof right === "number") return left.value / right + (left.type === "percent" ? "%" : left.type); if (typeof left === "number" && typeof right === "number") return left / right + ""; return left + " / " + right; } const WindiPrecision = 10; function prec(n) { return +n.toFixed(WindiPrecision); } /* eslint-disable @typescript-eslint/no-this-alias */ function digitToHEX(d) { let hex = d.toString(16); hex = "00".slice(0, 2 - hex.length) + hex; return hex; } function rgbToHEX(rgba) { const rgb = rgba.slice(0, 3); return "#" + rgb.map(digitToHEX).join("").toLowerCase(); } function hexToRGB(hex) { if (hex.length === 4) hex = "#" + [hex[1], hex[1], hex[2], hex[2], hex[3], hex[3]].join(""); else if (hex.length === 5) hex = "#" + [hex[1], hex[1], hex[2], hex[2], hex[3], hex[3], hex[4], hex[4]].join(""); const c = +("0x" + hex.substring(1)); if (hex.length === 9) return [(c >> 24) & 255, (c >> 16) & 255, (c >> 8) & 255, (c & 255) / 256]; return [(c >> 16) & 255, (c >> 8) & 255, c & 255, 1]; } function sliceColor(str) { const params = str.slice(str.indexOf("(") + 1, str.indexOf(")")); return params.indexOf(",") !== -1 ? params.split(/,\s*/) : params.split(/\s+\/?\s*/); } function rgbToHSL(rgba) { const r = rgba[0] / 255; const g = rgba[1] / 255; const b = rgba[2] / 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let h; let s; const l = (max + min) / 2; const d = max - min; const huecalc = { [r]: () => (60 * (g - b)) / d + (g < b ? 360 : 0), [g]: () => (60 * (b - r)) / d + 120, [b]: () => (60 * (r - g)) / d + 240, }; if (d === 0) h = s = 0; // grayscaled color else { s = l < 0.5 ? d / (max + min) : d / (2 - max - min); h = huecalc[max](); } return [h, s * 100, l * 100].concat([rgba[3]]); } function hueToRGB(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; } function hslToRGB(hsla) { let r; let g; let b; const h = hsla[0] / 360; const s = hsla[1] / 100; const l = hsla[2] / 100; if (s === 0) r = g = b = l; // grayscaled color else { const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; r = hueToRGB(p, q, h + 1 / 3); g = hueToRGB(p, q, h); b = hueToRGB(p, q, h - 1 / 3); } return [r, g, b] .map(function (c) { return Math.round(c * 255); }) .concat([hsla[3]]); } function hwbToRGB(hue, whiteness, blackness, alpha = 1) { const scaledHue = (hue % 360) / 360; let scaledWhiteness = whiteness / 100; let scaledBlackness = blackness / 100; const sum = scaledWhiteness + scaledBlackness; if (sum > 1) { scaledWhiteness /= sum; scaledBlackness /= sum; } const factor = 1 - scaledWhiteness - scaledBlackness; function toRgb(hue) { const channel = hueToRGB(0, 1, hue) * factor + scaledWhiteness; return Math.round(channel * 255); } return [toRgb(scaledHue + 1 / 3), toRgb(scaledHue), toRgb(scaledHue - 1 / 3), alpha]; } function adjustWithScale(rgba, deg, scale = 0, index = 0, len = 100) { rgba = [...rgba]; rgba[index] = deg ? Math.max(Math.min(rgba[index] + deg, len), 0) : scale >= 0 ? Math.floor(((len - rgba[index]) * scale) / 100 + rgba[index]) : Math.floor(rgba[index] - (rgba[index] * Math.abs(scale)) / 100); return rgba; } function adjustHue(hsla, deg) { hsla = [...hsla]; hsla[0] = hsla[0] + deg; return hsla; } const adjustSaturation = (hsla, deg, scale = 0) => adjustWithScale(hsla, deg, scale, 1); const adjustLightness = (hsla, deg, scale = 0) => adjustWithScale(hsla, deg, scale, 2); const adjustRed = (rgba, deg, scale = 0) => adjustWithScale(rgba, deg, scale, 0, 255); const adjustGreen = (rgba, deg, scale = 0) => adjustWithScale(rgba, deg, scale, 1, 255); const adjustBlue = (rgba, deg, scale = 0) => adjustWithScale(rgba, deg, scale, 2, 255); const adjustAlpha = (hsla, deg, scale = 0) => { hsla = [...hsla]; hsla[3] = deg ? Math.round(Math.max(Math.min(hsla[3] + deg, 1), 0) * 100) / 100 : scale >= 0 ? Math.floor((1 - hsla[3]) * scale + hsla[3] * 100) / 100 : Math.floor(hsla[3] * (100 + scale)) / 100; return hsla; }; function mixColor(color1, color2, w = 50) { const weightScale = w / 100; const normalizedWeight = weightScale * 2 - 1; const alphaDistance = color1[3] - color2[3]; const combinedWeight1 = normalizedWeight * alphaDistance === -1 ? normalizedWeight : (normalizedWeight + alphaDistance) / (1 + normalizedWeight * alphaDistance); const weight1 = (combinedWeight1 + 1) / 2; const weight2 = 1 - weight1; return [ Math.round(color1[0] * weight1 + color2[0] * weight2), Math.round(color1[1] * weight1 + color2[1] * weight2), Math.round(color1[2] * weight1 + color2[2] * weight2), color1[3] * weightScale + color2[3] * (1 - weightScale), ]; } function subMixColor(color1, color2, w = 50) { return color1.map((c, i) => (i === 3 ? color2[i] + (c - color2[i]) * (w / 100) : Math.floor(color2[i] + (c - color2[i]) * (w / 100)))); } class Color { constructor(hexval, rgbaval, hslaval) { if (Array.isArray(hexval)) { if (hexval[3] == null) hexval.push(1); this.rgbaval = hexval; this.hexval = rgbToHEX(this.rgbaval); } else { this.hexval = hexval; this.rgbaval = rgbaval !== null && rgbaval !== void 0 ? rgbaval : hexToRGB(hexval); } this.hslaval = hslaval !== null && hslaval !== void 0 ? hslaval : rgbToHSL(this.rgbaval); } static hex(str) { const hexval = str.toLowerCase(); const rgbaval = hexToRGB(hexval); const hslaval = rgbToHSL(rgbaval); return new Color(hexval, rgbaval, hslaval); } static rgb(r, g, b, a = 1) { return Color.rgba(r, g, b, a); } static rgba(r, g, b, a) { const rgbaval = [r, g, b, a]; const hexval = rgbToHEX(rgbaval); const hslaval = rgbToHSL(rgbaval); return new Color(hexval, rgbaval, hslaval); } static hsl(h, s, l, a = 1) { return Color.hsla(h, s, l, a); } static hsla(h, s, l, a) { const hslaval = [h, s, l, a]; const rgbaval = hslToRGB(hslaval); const hexval = rgbToHEX(rgbaval); return new Color(hexval, rgbaval, hslaval); } static hwb(hue, whiteness, blackness, alpha = 1) { return Color.rgba(...hwbToRGB(hue, whiteness, blackness, alpha)); } get hex() { const v = this.hexval; return v[1] === v[2] && v[3] === v[4] && v[5] === v[6] ? "#" + v[1] + v[3] + v[5] : v; } get rgb() { return this.rgba.slice(0, 3); } get rgba() { return this.rgbaval; } get hsl() { return this.hsla.slice(0, 3); } get hsla() { return this.hslaval.map(i => prec(i)); } get hwb() { return [this.hue, this.whiteness, this.blackness]; } static mix(c1, c2, w = 50) { return Color.rgba(...mixColor(c1.rgbaval, c2.rgbaval, w)); } static subcolormix(c1, c2, w = 50) { return Color.rgba(...subMixColor(c1.rgbaval, c2.rgbaval, w)); } get red() { return this.rgbaval[0]; } get green() { return this.rgbaval[1]; } get blue() { return this.rgbaval[2]; } get hue() { return prec(this.hslaval[0]); } get saturation() { return prec(this.hslaval[1]); } get lightness() { return prec(this.hslaval[2]); } get alpha() { return this.rgbaval[3]; } get opacity() { return this.alpha; } get whiteness() { const [r, g, b] = this.rgbaval; return prec((Math.min(Math.min(r, g), b) / 255) * 100); } get blackness() { const [r, g, b] = this.rgbaval; return prec(100 - (Math.max(Math.max(r, g), b) / 255) * 100); } get ieHexStr() { return "#" + (digitToHEX(this.alpha * 255) + this.hexval.slice(1)).toUpperCase(); } get rgbStr() { return `rgb(${this.rgb.join(", ")})`; } get rgbaStr() { return `rgba(${this.rgba.join(", ")})`; } get hslStr() { const [h, s, l] = this.hsl; return `hsl(${h}, ${s}%, ${l}%)`; } get hslaStr() { const [h, s, l, a] = this.hsla; return `hsla(${h}, ${s}%, ${l}%, ${a})`; } get hwbStr() { const [h, w, b] = this.hwb; const a = this.opacity; return a < 1 ? `hwb(${h} ${w}% ${b}% / ${a})` : `hwb(${h} ${w}% ${b}%)`; } invert(weight = 100) { const inverted = this.rgba.map((c, i) => (i === 3 ? c : Math.round(255 - c))); return Color.mix(new Color(rgbToHEX(inverted), inverted, rgbToHSL(inverted).concat([inverted[3]])), this, weight); } adjustRed(deg, scale = 0) { return Color.rgba(...adjustRed(this.rgbaval, deg, scale)); } adjustGreen(deg, scale = 0) { return Color.rgba(...adjustGreen(this.rgbaval, deg, scale)); } adjustBlue(deg, scale = 0) { return Color.rgba(...adjustBlue(this.rgbaval, deg, scale)); } adjustHue(deg) { return Color.hsla(...adjustHue(this.hslaval, deg)); } adjustSaturation(deg, scale = 0) { return Color.hsla(...adjustSaturation(this.hslaval, deg, scale)); } adjustLightness(deg, scale = 0) { return Color.hsla(...adjustLightness(this.hslaval, deg, scale)); } adjustAlpha(deg, scale = 0) { return Color.hsla(...adjustAlpha(this.hslaval, deg, scale)); } complement() { return this.adjustHue(180); } saturate(deg) { return this.adjustSaturation(deg); } desaturate(deg) { return this.adjustSaturation(-deg); } grayscale() { return this.adjustSaturation(-100); } lighten(amount) { return this.adjustLightness(amount); } darken(amount) { return this.adjustLightness(-amount); } opacify(deg) { return this.adjustAlpha(deg); } transparentize(deg) { return this.adjustAlpha(-deg); } fadeIn(deg) { return this.adjustAlpha(deg); } fadeOut(deg) { return this.adjustAlpha(-deg); } adjust(opt) { let color = this; for (const [k, v] of Object.entries(opt)) color = color[("adjust" + k[0].toUpperCase() + k.slice(1))](v); return color; } scale(opt) { let color = this; for (const [k, v] of Object.entries(opt)) color = color[("adjust" + k[0].toUpperCase() + k.slice(1))](undefined, v); return color; } change(opt) { let rgba = this.rgba; let hsla = this.hsla; for (const [k, v] of Object.entries(opt)) switch (k) { case "red": rgba[0] = v; hsla = rgbToHSL(rgba); break; case "green": rgba[1] = v; hsla = rgbToHSL(rgba); break; case "blue": rgba[2] = v; hsla = rgbToHSL(rgba); break; case "hue": hsla[0] = v; rgba = hslToRGB(hsla); break; case "saturation": hsla[1] = v; rgba = hslToRGB(hsla); break; case "lightness": hsla[2] = v; rgba = hslToRGB(hsla); break; case "alpha": rgba[3] = v; hsla[3] = v; break; } return new Color(rgbToHEX(rgba), rgba, hsla); } // Color Sets lightenSet(n) { return [this].concat(range(1, n).map(i => this.adjustLightness(undefined, (100 / n) * i))); } darkenSet(n) { return [this].concat(range(1, n).map(i => this.adjustLightness(undefined, -((100 / n) * i)))); } desaturateSet(n) { return [this].concat(range(1, n).map(i => this.adjustSaturation(undefined, -((100 / n) * i)))); } complementSet(n) { const comp = this.complement(); return range(0, n) .reverse() .map(i => (i === n - 1 ? this : i === 0 ? comp : Color.subcolormix(this, comp, (100 / (n - 1)) * i))); } invertSet(n) { const inv = this.invert(); return range(0, n) .reverse() .map(i => (i === n - 1 ? this : i === 0 ? inv : Color.subcolormix(this, inv, (100 / (n - 1)) * i))); } } function colorLuminance(color) { const rgb = color.rgb.map(i => i / 255).map(v => (v < 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2)); return prec(rgb[0] * 0.2126 + rgb[1] * 0.7152 + rgb[2] * 0.0722); } function getLightColor(color, lightness = 96) { return color.change({ lightness: color.lightness > 96 ? color.lightness : lightness }); } function getDarkColor(color, lightness = 29) { return color.change({ lightness: Math.max(lightness, Math.round(lightness + (0.53 - colorLuminance(color)) * 53)), }); } let CurrentMeta; function resetMeta(uid = "css", type = "css", props = [], variants = []) { CurrentMeta = { uid, type, props, variants }; } resetMeta(); function getMeta() { return CurrentMeta; } function getUid() { return CurrentMeta.uid; } function pushMetaProp(prop) { return CurrentMeta.props.push(prop); } function updateMetaType(type) { CurrentMeta.type = type; } function resetStyleMeta(style, meta = CurrentMeta) { style[SymbolMeta] = meta; return style; } const baseStyleTarget = (css, meta, data) => { if (data != null) // push func prop, for example bg.red.opacity(50) -> [..., opacity(50)] for (const [k, v] of Object.entries(data)) if (typeof v === "function") data[k] = (...args) => { pushMetaProp(parenWrap(k, args.toString())); return v(...args); }; return { [SymbolProxy]: true, [SymbolCSS]: css, [SymbolMeta]: meta, [SymbolData]: data, }; }; const baseStyleHandler = (target, prop, receiver) => { if (prop === "css") return Reflect.get(target, SymbolCSS, receiver); if (prop === "meta") return Reflect.get(target, SymbolMeta, receiver); const data = Reflect.get(target, SymbolData, receiver); if (data && prop in data) return data[prop]; return Reflect.get(target, prop, receiver); }; let CurrentLoader = (css, meta, data) => new Proxy(baseStyleTarget(css, meta, data), { get: baseStyleHandler }); function useStyleLoader(loader) { CurrentLoader = loader; } function css(css, data, meta) { return CurrentLoader(css, meta !== null && meta !== void 0 ? meta : getMeta(), data); } const BUILDED_CLASSES = []; function injectCSS(css) { const container = document.getElementById("windijs"); if (container) container.textContent += "\n" + css; else { const el = document.createElement("style"); el.id = "windijs"; el.setAttribute("type", "text/css"); el.textContent = css; document.head.appendChild(el); } } /** Create a styleLoader */ function createStyleLoader(inject) { return (css, meta, data) => { const baseStyle = baseStyleTarget(css, meta, data); const className = nameStyle(baseStyle); return new Proxy({ [className]: true, ...baseStyle, }, { get(target, prop, receiver) { // for react, svelte... if (prop === "toString") return () => { inject(className, baseStyle); return Object.keys(target).join(" "); }; // for vue if (prop === className) { inject(className, baseStyle); return Reflect.get(target, prop, receiver); } return baseStyleHandler(target, prop, receiver); }, }); }; } /** CSS-In-JS Loader */ const cssInJsLoader = createStyleLoader((className, style) => { if (!BUILDED_CLASSES.includes(className)) { BUILDED_CLASSES.push(className); injectCSS(buildStyle(className, style)); } }); const SSR_CLASSES = []; const SSR_CSS_LIST = []; /** SSR Loader */ const ssrLoader = createStyleLoader((className, style) => { if (!SSR_CLASSES.includes(className)) { SSR_CLASSES.push(className); SSR_CSS_LIST.push(buildStyle(className, style)); } }); /** Mount server side generated css */ function mountCSS() { return SSR_CSS_LIST.join("\n"); } function apply(selector, ...utilities) { return css(bundle(utilities), undefined, { uid: "apply", type: "css", props: [selector], variants: [], selector, }); } function funcProxy(f) { return new Proxy(f, { get(target, p) { return Reflect.apply(target, undefined, [p]); }, apply(target, thisArg, argArray) { return Reflect.apply(target, thisArg, argArray); }, }); } let GLOBAL_STYLES = []; function queryStyles(selector) { for (let i = GLOBAL_STYLES.length - 1; i >= 0; i--) if (GLOBAL_STYLES[i].selector === selector) return GLOBAL_STYLES[i].children; } function globalApply(selector, ...utilities) { const style = apply(selector, ...utilities); GLOBAL_STYLES.push({ selector, children: utilities.flat().filter(i => i != null), style, }); return style; } function createDollarCall(selector) { return new Proxy(globalApply, { get(target, p) { var _a; // eslint-disable-next-line @typescript-eslint/no-use-before-define if (p === "ATTR") return createAttrProxy(selector); if (p === "styles") return (_a = queryStyles(selector)) !== null && _a !== void 0 ? _a : []; if (p === "$") selector += ", "; else if (p === "$$") selector += " > "; else if (p === "_") selector += " "; else if (p === "__") selector += " + "; else if (p === "_$_") selector += " ~ "; else if (p.charCodeAt(0) < 91) selector += p.toLowerCase(); // HTML Elements // eslint-disable-next-line @typescript-eslint/no-unused-vars else selector += "." + p; return createDollarCall(selector); }, apply(target, thisArg, argArray) { return Reflect.apply(target, thisArg, [selector, ...argArray]); }, }); } const attrMatches = { match: "=", hyphenMatch: "|=", contains: "~=", includes: "*=", startsWith: "^=", endsWith: "$=", }; const createAttrProxy = (selector) => funcProxy(function (attribute) { return new Proxy(globalApply, { get(target, p) { if (p in attrMatches) return funcProxy(function (v) { selector += `[${attribute}${attrMatches[p]}${JSON.stringify(v)}]`; return createDollarCall(selector); }); return Reflect.get(createDollarCall(selector), p); }, apply(target, thisArg, argArray) { selector += `[${attribute}]`; return Reflect.apply(target, thisArg, [selector, ...argArray]); }, }); }); const $ = new Proxy(globalApply, { get(target, p) { if (p === "call") return (thisArg, ...args) => Reflect.apply(target, thisArg, args); if (p === "init") return () => (GLOBAL_STYLES = []); if (p === "exports") return GLOBAL_STYLES; if (p === "styles") return new Proxy(GLOBAL_STYLES.map(i => i.children).flat(), { get(target, p) { if (Reflect.has(target, p)) return Reflect.get(target, p); return queryStyles(p); }, }); let selector = ""; if (p === "ID") return funcProxy(function (id) { selector += "#" + id; return createDollarCall(selector); }); if (p === "ATTR") return createAttrProxy(selector); if (p === "All") selector += "*"; else if (p === "Root") selector += ":root"; // TODO: support :host(.selector) else if (p === "Host") selector += ":host"; else if (p.charCodeAt(0) < 91) selector += p.toLowerCase(); // HTML Elements // eslint-disable-next-line @typescript-eslint/no-unused-vars else selector += "." + p; return createDollarCall(selector); }, }); function quote(str) { return `${JSON.stringify(str).replace(/\\\\/g, "\\")}`; } // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions // TODO: more detailed help documentation for css functions // TODO: support var as type & param // TODO: support calc as type & param // reference functionns function attr(name, type, fallback) { const defaultValue = fallback != null ? ", " + fallback : ""; if (!type) return parenWrap("attr", name + defaultValue); return parenWrap("attr", name + " " + type + defaultValue); } function url(url, base64 = false, dataType = "image/png") { return parenWrap("url", base64 ? `data:${dataType};base64,` + url : url); } function $var(name, defaultValue) { return defaultValue ? `var(--${name}, ${defaultValue})` : `var(--${name})`; } // shape functions function path(path, fillRule) { return parenWrap("path", fillRule ? fillRule + ", " + JSON.stringify(path) : JSON.stringify(path)); } // color functions /** Creates a Color from hue, white and black. */ function hwb(hue, whiteness, blackness, alpha) { return parenWrap("hwb", [hue, whiteness, blackness].join(" ") + (alpha ? " / " + alpha : "")); } // filter functions function dropShadow(offsetX, offsetY, blurRadius, color) { return parenWrap("drop-shadow", [offsetX, offsetY, blurRadius, color].join(" ")); } function counters(name, char, style) { return parenWrap("counters", [name, JSON.stringify(char), style].filter(i => i != null).join(", ")); } // shape functions function circle(shapeRadius, positon) { return parenWrap("circle", positon == null ? shapeRadius.toString() : shapeRadius + " at " + (Array.isArray(positon) ? positon.join(" ") : positon)); } function ellipse(shapeRadiusX, shapeRadiusY, positon) { const shapeRadius = [shapeRadiusX, shapeRadiusY].join(" "); return parenWrap("ellipse", positon == null ? shapeRadius : shapeRadius + " at " + (Array.isArray(positon) ? positon.join(" ") : positon)); } function round(radius, radius2, radius3, radius4) { if (Array.isArray(radius) || Array.isArray(radius2)) return [radius, radius2].map(i => (Array.isArray(i) ? i.join(" ") : i)).join(" / "); return [radius, radius2, radius3, radius4].filter(i => i != null).join(" "); } class Inset { constructor(values) { this.values = []; this.values = values; this.round = ((a, b, c, d) => parenWrap("inset", this.values.filter(i => i != null).join(" ") + " round " + round(a, b, c, d))); } toString() { return parenWrap("inset", this.values.filter(i => i != null).join(" ")); } } function inset(lengthOrPercent, lengthOrPercent2, lengthOrPercent3, lengthOrPercent4) { return new Inset([lengthOrPercent, lengthOrPercent2, lengthOrPercent3, lengthOrPercent4]); } function polygon(fillRule, ...lengthOrPercent) { return parenWrap("polygon", [fillRule, ...lengthOrPercent] .filter(i => i != null) .map(i => (Array.isArray(i) ? i.join(" ") : i)) .join(", ")); } const { matrix, matrix3d, perspective, rotate, rotate3d, rotateX, rotateY, rotateZ, scale, scale3d, scaleX, scaleY, scaleZ, skew, skewX, skewY, translate, translate3d, translateX, translateY, translateZ, steps, calc, clamp, max, min, abs, sign, blur, brightness, contrast, grayscale, invert, opacity, saturate, sepia, rgb, rgba, hsl, hsla, counter, env, minmax, repeat, } = new Proxy({}, { get(_, p) { return (...args) => p + "(" + args.filter(i => i != null).join(", ") + ")"; }, }); const { hueRotate, fitContent, cubicBezier, linearGradient, radialGradient, conicGradient, repeatingConicGradient, repeatingLinearGradient, repeatingRadialGradient, } = new Proxy({}, { get(_, p) { return (...args) => camelToDash(p) + "(" + args .map(i => (Array.isArray(i) ? i.join(" ") : i)) .filter(i => i != null) .join(", ") + ")"; }, }); const filters = { blur, brightness, contrast, dropShadow, grayscale, hueRotate, invert, saturate, sepia, }; // export const transforms = { rotate, rotate3d, rotateX, rotateY, rotateZ, scale, scale3d, scaleX, scaleY, scaleZ, skew, skewX, skewY, translate, translate3d, translateX, translateY, translateZ }; const transforms = { rotate, scale, skew, translate }; // Color const color = new Proxy({}, { get: (_, v) => { const map = { var: $var, calc, rgb, rgba, hsl, hsla, hwb }; return v in map ? map[v] : v; }, }); function dimension(type, suffix = type) { return new Proxy({}, { get: (_, value) => Object.create({ value: +value, type, valueOf: () => value + suffix, toString: () => value + suffix, }), }); } const percent = dimension("percent", "%"); const { deg, grad, rad, turn } = new Proxy({}, { get: (_, k) => dimension(k) }); const { s, ms } = new Proxy({}, { get: (_, k) => dimension(k) }); const fr = dimension("fr"); const $in = dimension("in"); const { dpi, dpcm, dppx, x } = new Proxy({}, { get: (_, k) => dimension(k) }); const { px, pc, pt, cm, mm, Q, ch, ex, em, rem, vw, vh, vmax, vmin } = new Proxy({}, { get: (_, k) => dimension(k), }); export { $, $in, $var, Color, Q, SymbolCSS, SymbolData, SymbolMeta, SymbolProxy, abs, add, adjustAlpha, adjustBlue, adjustGreen, adjustHue, adjustLightness, adjustRed, adjustSaturation, alphaCount, alphaNamer, apply, applyVariant, atomic, atomicNamer, attr, baseStyleHandler, baseStyleTarget, blur, brightness, build, buildAtRule, buildDecl, buildRule, buildRules, buildStyle, bundle, calc, ch, circle, clamp, cm, color, colorLuminance, conicGradient, contrast, counter, counters, createRules, createStyleLoader, css, cssInJsLoader, cubicBezier, dedupRules, defineConfig, deg, digitToHEX, div, dpcm, dpi, dppx, dropShadow, ellipse, em, env, escapeCSS, ex, filters, fitContent, fr, getDarkColor, getFirstVar, getLightColor, getMeta, getStyleIdent, getStyleProps, getStyleVariants, getUid, grad, grayscale, hashNamer, hexToRGB, hsl, hslToRGB, hsla, hueRotate, hueToRGB, hwb, hwbToRGB, inherit, initial, injectCSS, inline, inset, invert, isProxy, isStyleArray, isStyleObject, linearGradient, matrix, matrix3d, max, mergeObject, min, minmax, mixColor, mm, mountCSS, ms, mul, nameStyle, none, opacity, path, pc, percent, perspective, polygon, prec, prop, pt, pushMetaProp, px, queryStyles, quote, rad, radialGradient, rem, repeat, repeatingConicGradient, repeatingLinearGradient, repeatingRadialGradient, resetMeta, resetStyleMeta, rgb, rgbToHEX, rgbToHSL, rgba, rotate, rotate3d, rotateX, rotateY, rotateZ, s, saturate, scale, scale3d, scaleX, scaleY, scaleZ, sepia, sign, skew, skewX, skewY, sliceColor, ssrLoader, steps, sub, subMixColor, transforms, translate, translate3d, translateX, translateY, translateZ, turn, unify, unset, updateMetaType, url, useArrayHelper, useNamer, useStyleLoader, vh, vmax, vmin, vw, x };