@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
112 lines (111 loc) • 4.47 kB
JavaScript
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