UNPKG

@kuma-ui/sheet

Version:

🐻 Kuma UI is a utility-first, zero-runtime CSS-in-JS library that offers an outstanding developer experience and optimized performance.

352 lines (342 loc) 9.67 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // src/index.ts var src_exports = {}; __export(src_exports, { generateHash: () => generateHash, removeSpacesAroundCssPropertyValues: () => removeSpacesAroundCssPropertyValues, removeSpacesExceptInProperties: () => removeSpacesExceptInProperties, sheet: () => sheet, styleCache: () => styleCache, styleMap: () => styleMap, theme: () => theme }); module.exports = __toCommonJS(src_exports); // src/hash.ts function generateHash(str) { const m = 1540483477; const r = 24; const seed = 305419896; const len = str.length; let h = seed ^ len; for (let i = 0; i < len; i++) { let k = str.charCodeAt(i); k *= m; k ^= k >>> r; k *= m; h *= m; h ^= k; } h ^= h >>> 13; h *= m; h ^= h >>> 15; return (h >>> 0).toString(); } // src/regex.ts var removeSpacesAroundCssPropertyValues = (css) => { const regex = /(:)\s+|\s+(?=;)/g; return css.replace(regex, "$1"); }; var removeSpacesExceptInProperties = (css) => { const regex = /(:)\s+|\s+(?=;)|(\{)\s+|\s+(?=\})|(,)\s+|\s+(?=,)|\s+(?={)/g; return css.replace(regex, "$1$2$3"); }; // src/sheet.ts var import_stylis = require("stylis"); // src/theme.ts var defaultBreakpoints = Object.freeze({ sm: "576px", md: "768px", lg: "992px", xl: "1200px" }); var tokens = [ "colors", "fonts", "fontSizes", "fontWeights", "lineHeights", "letterSpacings", "spacings", "sizes", "radii", "zIndices", "breakpoints" ]; var _Theme = class { _userTheme = { ...globalThis.__KUMA_USER_THEME__, breakpoints: globalThis.__KUMA_USER_THEME__?.breakpoints ?? defaultBreakpoints }; _placeholders = {}; constructor() { } static getInstance() { if (!_Theme.instance) { _Theme.instance = new _Theme(); } return _Theme.instance; } setUserTheme(userTheme) { if (Object.keys(userTheme.breakpoints || {}).length === 0) { delete userTheme.breakpoints; } this._userTheme = { ...this._userTheme, ...userTheme }; this._placeholders = createPlaceholders(this._userTheme); } getUserTheme() { return this._userTheme; } getPlaceholders() { return this._placeholders; } getVariants(componentName) { return this._userTheme.components?.[componentName] || {}; } reset() { this._userTheme = { breakpoints: defaultBreakpoints }; } }; var Theme = _Theme; __publicField(Theme, "instance"); var theme = Theme.getInstance(); // src/placeholders.ts var applyT = (input, placeholders) => { return applyPlaceholders(input, placeholders); }; var applyPlaceholders = (input, placeholders) => { const regex = /\bt\s*\(\s*["']([^"']+)["']\s*\)/g; return input.replace(regex, (match, placeholder) => { if (typeof placeholder === "string" && placeholder in placeholders) { return placeholders[placeholder]; } return match; }); }; var createPlaceholders = (theme2) => { const result = {}; for (const token of tokens) { const tokenValue = theme2[token]; if (tokenValue) { for (const key in tokenValue) { result[key] = tokenValue[key]; } } } return result; }; // src/sheet.ts var _Sheet = class { base; responsive; pseudo; css; constructor() { this.base = []; this.responsive = []; this.pseudo = []; this.css = []; } static getInstance() { if (!_Sheet.instance) { _Sheet.instance = new _Sheet(); } return _Sheet.instance; } static getClassNamePrefix(isDynamic = false) { const isProduction = process.env.NODE_ENV === "production"; if (isProduction) return "kuma-"; return isDynamic ? "\u{1F984}-" : "\u{1F43B}-"; } addRule(style, isDynamic = false) { const className = _Sheet.getClassNamePrefix(isDynamic) + generateHash(JSON.stringify(style)); this._addBaseRule(className, this._processCSS(style.base)); for (const [breakpoint, css] of Object.entries(style.responsive)) { this._addMediaRule( className, this._processCSS(css), this._processCSS(breakpoint) ); } for (const [_, pseudo] of Object.entries(style.pseudo)) { this._addPseudoRule(className, pseudo); } return className; } _addBaseRule(className, css) { const minifiedCss = removeSpacesAroundCssPropertyValues(css); this.base.push(`.${className}{${minifiedCss}}`); } _addMediaRule(className, css, breakpoint) { const minifiedCss = removeSpacesAroundCssPropertyValues(css); const mediaCss = removeSpacesExceptInProperties( `@media (min-width: ${breakpoint}) { .${className} { ${minifiedCss} } }` ); this.responsive.push(mediaCss); } _addPseudoRule(className, pseudo) { const css = removeSpacesAroundCssPropertyValues( this._processCSS(pseudo.base) ); const pseudoCss = removeSpacesExceptInProperties( `.${className}${pseudo.key} { ${css} }` ); this.pseudo.push(pseudoCss); for (const [breakpoint, _css] of Object.entries(pseudo.responsive)) { this._addMediaRule( `${className}${pseudo.key}`, this._processCSS(_css), this._processCSS(breakpoint) ); } } _processCSS(css) { const placeholders = theme.getPlaceholders(); return applyT(css, placeholders); } /** * parseCSS takes in raw CSS and parses it to valid CSS using Stylis. * It's useful for handling complex CSS such as media queries and pseudo selectors. */ parseCSS(style) { style = this._processCSS(style); const id = _Sheet.getClassNamePrefix() + generateHash(style); const elements = []; (0, import_stylis.compile)(`.${id}{${style}}`).forEach((element) => { const { breakpoints } = theme.getUserTheme(); if (element.type === "@media") { const props = Array.isArray(element.props) ? element.props : [element.props]; const newProps = []; let newValue = element.value; for (const key in breakpoints) { newValue = newValue.replaceAll(key, breakpoints[key]); } props.forEach((prop) => { for (const key in breakpoints) { newProps.push(prop.replaceAll(key, breakpoints[key])); break; } }); element.props = newProps; element.value = newValue; } elements.push(element); }); const css = (0, import_stylis.serialize)(elements, import_stylis.stringify); this.css.push(css); return id; } removeDuplicates() { this.base = [...new Set(this.base)]; this.responsive = [...new Set(this.responsive)]; this.pseudo = [...new Set(this.pseudo)]; this.css = [...new Set(this.css)]; } getCSS() { this.removeDuplicates(); return this.base.join("") + this.responsive.join("") + this.pseudo.join("") + this.css.join(""); } reset() { this.base = []; this.responsive = []; this.pseudo = []; this.css = []; } }; var Sheet = _Sheet; __publicField(Sheet, "instance"); var sheet = Sheet.getInstance(); // src/cache.ts var _StyleCache = class { cache; constructor() { this.cache = /* @__PURE__ */ new Map(); } static getInstance() { if (!_StyleCache.instance) { _StyleCache.instance = new _StyleCache(); } return _StyleCache.instance; } get(key) { return this.cache.get(key); } set(key, styles) { this.cache.set(key, styles); } reset() { this.cache.clear(); } }; var StyleCache = _StyleCache; __publicField(StyleCache, "instance"); var styleCache = StyleCache.getInstance(); // src/styleMap.ts var _StyleMap = class { map; constructor() { this.map = /* @__PURE__ */ new Map(); } static getInstance() { if (!_StyleMap.instance) { _StyleMap.instance = new _StyleMap(); } return _StyleMap.instance; } // Add the given CSS for the specified file to the map. // In the future, we might use an id to associate the HTML tag // with the corresponding CSS (by using the data-kuma-ui attribute) // and improve performance by removing duplicate CSS across different files. set(fileName, css) { this.map.set(fileName, css); } get(fileName) { return this.map.get(fileName); } delete(fileName) { this.map.delete(fileName); } reset() { this.map.clear(); } }; var StyleMap = _StyleMap; __publicField(StyleMap, "instance"); var styleMap = StyleMap.getInstance(); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { generateHash, removeSpacesAroundCssPropertyValues, removeSpacesExceptInProperties, sheet, styleCache, styleMap, theme });