@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
JavaScript
;
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
});