/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
import {
Colors,
PreviewColors,
StaticColors,
ThemeDefinition,
THEME_SCOPES,
ThemeScopes,
Variants,
VariantsWithStaticColors
} from "./types";
import _ThemeLight from "./themes/default-light.json";
import tc from "tinycolor2";
import _ThemeDark from "./themes/default-dark.json";
const ThemeLight = _ThemeLight as ThemeDefinition;
const ThemeDark = _ThemeDark as ThemeDefinition;
export function getPreviewColors(theme: ThemeDefinition): PreviewColors {
const { base, navigationMenu, statusBar, list, editor } = theme.scopes;
const { primary, success } = base;
return {
navigationMenu: {
shade: deriveShadeColor(
tc(navigationMenu?.primary?.accent || primary.accent)
),
accent: navigationMenu?.primary?.accent || primary.accent,
background: navigationMenu?.primary?.background || primary.background,
icon: navigationMenu?.primary?.icon || primary.icon
},
statusBar: {
paragraph: statusBar?.primary?.paragraph || primary.paragraph,
background: statusBar?.primary?.background || primary.background,
icon: statusBar?.success?.icon || success.icon
},
editor: editor?.primary?.background || primary.background,
list: {
heading: list?.primary?.heading || primary.heading,
background: list?.primary?.background || primary.background,
accent: list?.primary?.accent || primary.accent,
accentForeground:
list?.primary?.accentForeground || primary.accentForeground
},
border: primary.border,
paragraph: primary.paragraph,
background: primary.background,
accent: primary.accent,
accentForeground: primary.accentForeground
};
}
export function themeToCSS(theme: ThemeDefinition) {
const css: string[] = [];
for (const scopeKey of THEME_SCOPES) {
const scope = theme.scopes[scopeKey] || {};
const variants = buildVariants(scopeKey, theme, scope);
let scopeCss = `.theme-scope-${scopeKey} {`;
for (const variantKey in variants) {
const variant = variants[variantKey as keyof Variants];
if (!variant) continue;
css.push(`.theme-scope-${scopeKey}-${variant} {
${colorsToCSSVariables(variant, variantKey)}
}`);
scopeCss += colorsToCSSVariables(variant, variantKey);
scopeCss += "\n\n";
}
scopeCss += "}";
css.push(scopeCss);
}
return css.join("\n\n");
}
export function buildVariants(
scope: keyof ThemeScopes,
theme: ThemeDefinition,
themeScope: Partial
): VariantsWithStaticColors {
const defaultTheme = theme.colorScheme === "dark" ? ThemeDark : ThemeLight;
const defaultThemeBase = defaultTheme.scopes.base;
const defaultThemeScope = defaultTheme.scopes[scope] || {};
function getColor(variant: keyof Variants, color: keyof Colors) {
return tc(
themeScope[variant]?.[color] ||
theme.scopes.base[variant]?.[color] ||
defaultThemeScope[variant]?.[color] ||
defaultThemeBase[variant]?.[color]
);
}
const variant: VariantsWithStaticColors = {
...defaultThemeBase,
...defaultThemeScope,
primary: {
...defaultThemeBase.primary,
...defaultThemeScope.primary,
...theme.scopes.base.primary,
...themeScope.primary,
shade: deriveShadeColor(getColor("primary", "accent"))
},
secondary: {
...defaultThemeBase.secondary,
...defaultThemeScope.secondary,
...theme.scopes.base.secondary,
...themeScope.secondary,
shade: deriveShadeColor(getColor("secondary", "accent"))
},
disabled: {
...defaultThemeBase.disabled,
...defaultThemeScope.disabled,
...theme.scopes.base.disabled,
...themeScope.disabled,
shade: deriveShadeColor(getColor("disabled", "accent"))
},
error: {
...defaultThemeBase.error,
...defaultThemeScope.error,
...theme.scopes.base.error,
...themeScope.error,
shade: deriveShadeColor(getColor("error", "accent"))
},
success: {
...defaultThemeBase.success,
...defaultThemeScope.success,
...theme.scopes.base.success,
...themeScope.success,
shade: deriveShadeColor(getColor("success", "accent"))
},
selected: {
...defaultThemeBase.selected,
...defaultThemeScope.selected,
...theme.scopes.base.selected,
...themeScope.selected,
shade: deriveShadeColor(getColor("selected", "accent"))
},
static: StaticColors
};
return variant;
}
export function colorsToCSSVariables(colors: Colors, variantKey?: string) {
let root = "";
const suffix =
!variantKey || variantKey === "primary" ? "" : `-${variantKey}`;
for (const color in colors) {
root += `--${color}${suffix}: ${colors[color as keyof Colors]};\n`;
}
return root;
}
function deriveShadeColor(color: tc.Instance) {
return color.setAlpha(0.1).toHex8String();
}