UNPKG

theme-vir

Version:
193 lines (192 loc) 8.44 kB
import { assert, check } from '@augment-vir/assert'; import { getObjectTypedEntries, log, } from '@augment-vir/common'; import { CSSResult } from 'element-vir'; import { defineCssVars, } from 'lit-css-vars'; /** @category Internal */ export function noRefColorInitToString(init) { if (check.isPrimitive(init) || init instanceof CSSResult) { return String(init); } else { return init.default; } } /** * Handles a color init value. * * @category Internal */ export function createColorCssVarDefault(fromName, init, defaultInit, colorsInit) { const defaultForegroundKey = `${defaultInit.prefix}-default-fg`; const defaultBackgroundKey = `${defaultInit.prefix}-default-bg`; if (check.isPrimitive(init) || init instanceof CSSResult) { return init; } else if ('refDefaultBackground' in init) { return `var(--${defaultBackgroundKey}, ${noRefColorInitToString(defaultInit.background)})`; } else if ('refDefaultForeground' in init) { return `var(--${defaultForegroundKey}, ${noRefColorInitToString(defaultInit.foreground)})`; } else if ('refBackground' in init || 'refForeground' in init) { const referenceKey = check.hasKey(init, 'refBackground') ? 'refBackground' : check.hasKey(init, 'refForeground') ? 'refForeground' : undefined; const reference = referenceKey && check.hasKey(init, referenceKey) ? init[referenceKey] : undefined; const layerKey = referenceKey === 'refBackground' ? 'background' : 'foreground'; const referenced = reference && colorsInit[reference]; if (!referenced) { throw new Error(`Color theme ${referenceKey} reference '${reference}' does not exist. (Referenced from '${fromName}'.)`); } const colorValue = referenced[layerKey] || (layerKey === 'foreground' ? createColorCssVarDefault(defaultForegroundKey, defaultInit.foreground, defaultInit, colorsInit) : createColorCssVarDefault(defaultBackgroundKey, defaultInit.background, defaultInit, colorsInit)); return `var(--${reference}-${layerKey === 'foreground' ? 'fg' : 'bg'}, ${createColorCssVarDefault(reference, colorValue, defaultInit, colorsInit)})`; } else { return init.value; } } /** * Default foreground/background color theme used in {@link ColorTheme}. Do not define a theme color * with this name! * * @category Internal */ export const themeDefaultKey = 'theme-default'; /** * Define a color theme. * * @category Color Theme */ export function defineColorTheme(defaultInit, allColorsInit) { try { if (themeDefaultKey in allColorsInit) { throw new Error(`Cannot define theme color by name '${themeDefaultKey}', it is used internally.`); } const defaultForegroundKey = `${defaultInit.prefix}-default-fg`; const defaultBackgroundKey = `${defaultInit.prefix}-default-bg`; const inverseDefaultForegroundKey = `${defaultInit.prefix}-default-inverse-fg`; const inverseDefaultBackgroundKey = `${defaultInit.prefix}-default-inverse-bg`; const defaultColorsInit = { [defaultForegroundKey]: createColorCssVarDefault(defaultForegroundKey, defaultInit.foreground, defaultInit, allColorsInit), [defaultBackgroundKey]: createColorCssVarDefault(defaultBackgroundKey, defaultInit.background, defaultInit, allColorsInit), [inverseDefaultForegroundKey]: createColorCssVarDefault(inverseDefaultForegroundKey, defaultInit.background, defaultInit, allColorsInit), [inverseDefaultBackgroundKey]: createColorCssVarDefault(inverseDefaultBackgroundKey, defaultInit.foreground, defaultInit, allColorsInit), }; const defaultColors = defineCssVars(defaultColorsInit); const cssVarsSetup = getObjectTypedEntries(allColorsInit).reduce((accum, [colorName, colorInit,]) => { const names = createCssVarNames(colorName); const foreground = colorInit.foreground ? createColorCssVarDefault([ colorName, 'foreground', ].join(' '), colorInit.foreground, defaultInit, allColorsInit) : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion `var(${defaultColors[defaultForegroundKey].name}, ${defaultColors[defaultForegroundKey].default})`; const background = colorInit.background ? createColorCssVarDefault([ colorName, 'background', ].join(' '), colorInit.background, defaultInit, allColorsInit) : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion `var(${defaultColors[defaultBackgroundKey].name}, ${defaultColors[defaultBackgroundKey].default})`; accum[names.foreground] = foreground; accum[names.background] = background; accum[names.foregroundInverse] = `var(--${names.background}, ${background})`; accum[names.backgroundInverse] = `var(--${names.foreground}, ${foreground})`; return accum; }, {}); /** * This has multiple `as` casts because `defineCssVars` complains that `cssVarsSetup` is too * generic. That is indeed true, but in this use case we do not care because the resulting * `cssVars` object is not directly exposed. */ const cssVars = defineCssVars(cssVarsSetup); const colors = {}; const inverseColors = {}; getObjectTypedEntries(allColorsInit).forEach(([colorName, colorInit,]) => { assert.isString(colorName); const names = createCssVarNames(colorName); const foreground = cssVars[names.foreground]; const background = cssVars[names.background]; const foregroundInverse = cssVars[names.foregroundInverse]; const backgroundInverse = cssVars[names.backgroundInverse]; assert.isDefined(foreground); assert.isDefined(background); assert.isDefined(foregroundInverse); assert.isDefined(backgroundInverse); colors[colorName] = { foreground, background, init: colorInit, name: colorName, }; inverseColors[colorName] = { foreground: foregroundInverse, background: backgroundInverse, init: colorInit, name: colorName, }; }); const themeDefaultColors = { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion foreground: defaultColors[defaultForegroundKey], // eslint-disable-next-line @typescript-eslint/no-non-null-assertion background: defaultColors[defaultBackgroundKey], init: defaultInit, name: themeDefaultKey, }; const themeDefaultInverseColors = { ...themeDefaultColors, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion foreground: defaultColors[inverseDefaultForegroundKey], // eslint-disable-next-line @typescript-eslint/no-non-null-assertion background: defaultColors[inverseDefaultBackgroundKey], }; return { colors: { [themeDefaultKey]: themeDefaultColors, ...colors, }, inverse: { [themeDefaultKey]: themeDefaultInverseColors, ...inverseColors, }, init: { colors: allColorsInit, default: defaultInit, }, prefix: defaultInit.prefix, }; } catch (error) { globalThis.setTimeout(() => log.error(error)); throw error; } } function createCssVarNames(colorName) { return { foreground: [ colorName, 'fg', ].join('-'), background: [ colorName, 'bg', ].join('-'), foregroundInverse: [ colorName, 'inverse', 'fg', ].join('-'), backgroundInverse: [ colorName, 'inverse', 'bg', ].join('-'), }; }