- add complete theme generation

This commit is contained in:
enricobuehler 2021-11-11 16:35:27 +01:00
parent 4b94f300f2
commit dbbbbf5997
3 changed files with 176 additions and 18 deletions

View File

@ -1,20 +1,33 @@
import { countDecimals } from "Utils";
import { fromString } from "css-color-converter";
export interface ColorsInput {
[key: string]: string;
}
export interface ShadesOptions {
keys?: string[];
keyPrefix?: string;
keySuffix?: string;
opacities?: number[];
structure?: "flat" | "nested";
format?: "default" | "css-variable";
}
const DEFAULT_OPACITIES = [0.1, 0.25, 0.5, 0.75];
const DEFAULT_STRUCTURE = "flat";
const formatColor = ({
prefix,
key,
rgb,
opacity,
}: {
prefix?: string;
key?: string;
rgb: string;
opacity?: number;
}) => {
return {
[`${prefix ? prefix + "-" : ""}${
[`${key ? key + "-" : ""}${
opacity !== undefined
? countDecimals(opacity) === 1
? (opacity + "").split(".")[1] + "0"
@ -24,16 +37,7 @@ const formatColor = ({
};
};
const generateShades = (
colors: { [key: string]: string },
options?: {
keys?: string[];
keyPrefix?: string;
opacities?: number[];
structure?: "flat" | "nested";
format?: "default" | "css-variable";
}
) => {
const generateShades = (colors: ColorsInput, options?: ShadesOptions) => {
const colorKeys = options?.keys
? Object.keys(colors).filter((key) => options.keys?.includes(key))
: Object.keys(colors);
@ -47,7 +51,7 @@ const generateShades = (
const generatedColorShadesArr: object[] = [];
colorKeys.forEach((_key) => {
const key = (options?.keyPrefix || "") + _key;
const key = (options?.keyPrefix || "") + _key + (options?.keySuffix || "");
const colorString = colors[_key];
const opacities = options?.opacities
@ -57,9 +61,7 @@ const generateShades = (
if (structure === "flat") {
opacities.forEach((opacity) => {
const rgba = fromString(colorString).toRgbaArray();
generatedColorShadesArr.push(
formatColor({ prefix: key, rgb: rgba, opacity })
);
generatedColorShadesArr.push(formatColor({ key, rgb: rgba, opacity }));
});
} else {
generatedColorShadesArr.push({

74
src/Theme/index.test.ts Normal file
View File

@ -0,0 +1,74 @@
import {
generateThemeOverrideClass,
generateThemeRoot,
generateTheme,
ThemeInput,
} from ".";
const exampleProperties = {
"color-main-10": "black",
"color-main-50": "grey",
};
const exampleThemes: ThemeInput[] = [
{
name: "default",
colors: { "color-main": "black" },
},
{
name: "dark",
colors: { "color-main": "white", "color-secondary": "blue" },
},
{ name: "light", colors: { "color-secondary": "grey" } },
];
test("it generates css based on input properties", () => {
const themeClass = generateThemeOverrideClass({
properties: exampleProperties,
className: "dark-theme",
});
expect(themeClass).toBeDefined();
});
test("no input so it returns empty string", () => {
const themeClass = generateThemeOverrideClass({
properties: {},
className: "dark-theme",
});
expect(themeClass).toBeDefined();
expect(themeClass).toEqual("");
});
test("it generates the theme root", () => {
const themeRoot = generateThemeRoot({ properties: exampleProperties });
console.log(themeRoot);
expect(themeRoot).toBeDefined();
});
test("it generates the complete theme with overrides", () => {
const theme = generateTheme({
themes: exampleThemes,
output: "CSS",
});
console.log(theme);
expect(theme).toBeDefined();
});
test("it generates the complete theme with overrides and color shades", () => {
const theme = generateTheme({
themes: exampleThemes,
output: "CSS",
generateColorShades: {
structure: "flat",
format: "css-variable",
},
});
console.log(theme);
expect(theme).toBeDefined();
});

View File

@ -1,3 +1,85 @@
import * as Colors from "./Colors";
import { generateShades, ColorsInput, ShadesOptions } from "./Colors";
export default { Colors };
export interface ThemeInput {
name: string;
colors: ColorsInput;
}
const generateThemeOverrideClass = (input: {
properties: any;
className: string;
}) => {
const propKeys = Object.keys(input.properties);
if (propKeys.length > 0) {
return `
.${input.className} {${propKeys
.map((key) => `\t--${key}: ${input.properties[key]};`)
.join("\n")}}`;
} else return "";
};
const generateThemeRoot = (input: { properties: any }) => {
const propKeys = Object.keys(input.properties);
if (propKeys.length > 0) {
return `
:root {${propKeys
.map((key) => `\t--${key}: ${input.properties[key]};`)
.join("\n")}}`;
} else return "";
};
const generateTheme = (options: {
output?: "CSS";
themes: ThemeInput[];
generateColorShades?: boolean | ShadesOptions;
}) => {
if (
options.generateColorShades !== undefined &&
options.generateColorShades !== false
) {
options.themes = options.themes.map((theme) => {
theme.colors = {
...generateShades(
theme.colors,
typeof options.generateColorShades !== "boolean"
? options.generateColorShades
: undefined
),
...theme.colors,
};
return theme;
});
}
const themeRoot = generateThemeRoot({
properties: { ...options.themes[0].colors },
});
if (options.themes.length > 1) {
const themeOverrides = options.themes
.slice(1)
.map((theme) => {
const overrideClass = generateThemeOverrideClass({
properties: { ...theme.colors },
className: theme.name,
});
return overrideClass;
})
.join("\n");
return themeRoot + themeOverrides;
} else return themeRoot;
};
export { generateThemeOverrideClass, generateThemeRoot, generateTheme };
export default {
Colors,
generateThemeOverrideClass,
generateThemeRoot,
generateTheme,
};