UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

112 lines (111 loc) 4.47 kB
import { createLobeAntdTheme } from "../styles/theme/antdTheme.mjs"; import { hashCss } from "./hash.mjs"; import { createRequire } from "node:module"; import { theme } from "antd"; import { existsSync, readdirSync } from "node:fs"; import path from "node:path"; //#region src/static-css/themeVars.ts const require = createRequire(import.meta.url); const unitless = /* @__PURE__ */ new Set([ "fontWeightStrong", "lineHeight", "lineHeightHeading1", "lineHeightHeading2", "lineHeightHeading3", "lineHeightHeading4", "lineHeightHeading5", "lineHeightLG", "lineHeightSM", "opacityImage", "opacityLoading", "zIndexBase", "zIndexPopupBase" ]); const ignoredTokens = /* @__PURE__ */ new Set(["motionBase", "motionUnit"]); const toKebabCase = (str) => str.replaceAll(/([a-z])([A-Z])/g, "$1-$2").replaceAll(/([a-z])(\d)/g, "$1-$2").replaceAll(/(\d)([A-Z])/g, "$1-$2").replaceAll(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase(); const defaultThemeFactory = (appearance) => createLobeAntdTheme({ appearance }); const buildDesignToken = (themeFactory, appearance) => { const themeConfig = themeFactory(appearance); return theme.getDesignToken({ ...themeConfig, algorithm: [appearance === "dark" ? theme.darkAlgorithm : theme.defaultAlgorithm, themeConfig.algorithm ?? []].flat() }); }; const toDeclarations = (token) => Object.entries(token).filter(([key, value]) => !key.startsWith("_") && !key.startsWith("screen") && !key.includes("-") && !ignoredTokens.has(key) && (typeof value === "string" || typeof value === "number")).map(([key, value]) => { const cssValue = typeof value === "number" && !unitless.has(key) ? `${value}px` : String(value); return `--ant-${toKebabCase(key)}: ${cssValue};`; }).join("\n"); const loadAntdTokenModules = () => { const antdLibDir = path.join(path.dirname(require.resolve("antd/package.json")), "lib"); return readdirSync(antdLibDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort().map((name) => ({ component: name, path: path.join(antdLibDir, name, "style", "token.js") })).filter(({ path }) => existsSync(path)); }; const buildDarkComponentVars = (tokens) => { const declarations = []; const skipped = []; for (const { component, path } of loadAntdTokenModules()) try { const prepare = require(path).prepareComponentToken; if (typeof prepare !== "function") continue; const light = prepare(tokens.light); const dark = prepare(tokens.dark); for (const [key, value] of Object.entries(dark)) { if (typeof value !== "string" || light[key] === value) continue; declarations.push(`--ant-${component}-${toKebabCase(key)}:${value};`); } } catch { skipped.push(component); } return { css: declarations.join(""), skipped }; }; const readableOnPrimary = "contrast-color(var(--ant-color-primary))"; const buildCompatRules = (darkSelector) => { return [ [ `${darkSelector} .ant-btn-primary:not(:disabled)`, `${darkSelector} .ant-btn-primary:not(:disabled):hover`, `${darkSelector} .ant-btn-primary:not(:disabled):active` ].join(",") + `{color:${readableOnPrimary} !important;}`, `${darkSelector} .ant-checkbox-inner:after{border-color:${readableOnPrimary} !important;}`, `${darkSelector} .ant-radio-wrapper .ant-radio-checked .ant-radio-inner:after{background:${readableOnPrimary};}` ].join("\n"); }; const buildThemeVarsCss = (options = {}) => { const { theme = defaultThemeFactory, appearanceSelector = (appearance) => `html[data-theme='${appearance}']`, compatRules = true, hrefTemplate = (hash) => `/theme-vars.css?v=${hash}` } = options; const tokens = { dark: buildDesignToken(theme, "dark"), light: buildDesignToken(theme, "light") }; const light = appearanceSelector("light"); const dark = appearanceSelector("dark"); const componentVars = buildDarkComponentVars(tokens); const blocks = [ [ ":root", `${light} [class*='css-var-']`, `${light} .ant-app` ].join(",") + `{${toDeclarations(tokens.light)}}`, [ dark, `${dark} [class*='css-var-']`, `${dark} .ant-app` ].join(",") + `{${toDeclarations(tokens.dark)}}`, `${dark} [class*='css-var-']{${componentVars.css}}` ]; if (compatRules) blocks.push(buildCompatRules(dark)); const css = blocks.join("\n"); const hash = hashCss(css); return { css, hash, href: hrefTemplate(hash), skippedComponents: componentVars.skipped }; }; //#endregion export { buildThemeVarsCss, toKebabCase }; //# sourceMappingURL=themeVars.mjs.map