UNPKG

@sanity/ui

Version:

The Sanity UI components.

533 lines (476 loc) 16.1 kB
import {COLOR_HUES} from '@sanity/color' import {ThemeColorPalette, ThemeConfig} from '../config' import {defaultColorPalette} from '../defaults/colorPalette' import { THEME_COLOR_STATE_TONES, ThemeColorAvatar_v2, ThemeColorBadge_v2, ThemeColorBadgeTone_v2, ThemeColorBlendModeKey, ThemeColorButton_v2, ThemeColorButtonMode_v2, ThemeColorButtonTone_v2, ThemeColorCard_v2, ThemeColorCardToneKey, ThemeColorInput_v2, ThemeColorInputMode_v2, ThemeColorInputState_v2, ThemeColorKBD, ThemeColorScheme_v2, ThemeColorSchemes_v2, ThemeColorSelectable_v2, ThemeColorSelectableTone_v2, ThemeColorShadow, ThemeColorState_v2, ThemeColorSyntax, } from '../system' import {renderColorValue, RenderColorValueOptions} from './renderColorValue' export function renderThemeColorSchemes( value: ThemeColorSchemes_v2, config?: ThemeConfig, ): ThemeColorSchemes_v2 { const colorPalette = config?.palette ?? defaultColorPalette return { light: renderThemeColorScheme(colorPalette, value.light), dark: renderThemeColorScheme(colorPalette, value.dark), } } function renderThemeColorScheme( colorPalette: ThemeColorPalette, value: ThemeColorScheme_v2, ): ThemeColorScheme_v2 { const toneEntries = Object.entries(value) as [ThemeColorCardToneKey, ThemeColorCard_v2][] const [, transparentTone] = toneEntries.find(([k]) => k === 'transparent')! const [, defaultTone] = toneEntries.find(([k]) => k === 'default')! // The `transparent` and `default` tones are special cases, so we render them first // (rendered without a `bg` option). // But the rest of the tones are rendered on top of the `default` tone's `bg`. const renderedTransparentTone = renderThemeColor(transparentTone, {colorPalette}) const renderedDefaultTone = renderThemeColor(defaultTone, {colorPalette}) // Get the `default` tone's `bg` property const bg = renderedDefaultTone.bg if (bg === 'white') { throw new Error('Cannot blend with white background') } return Object.fromEntries([ ['transparent', renderedTransparentTone], ['default', renderedDefaultTone], ...toneEntries .filter(([k]) => k !== 'default' && k !== 'transparent') .map(([k, v]) => [k, renderThemeColor(v, {bg, colorPalette})]), ]) as ThemeColorScheme_v2 } function renderThemeColor( value: ThemeColorCard_v2, options: { bg?: string colorPalette: ThemeColorPalette }, ): ThemeColorCard_v2 { const {colorPalette, bg} = options const blendMode = value._blend || 'multiply' const baseBg = renderColorValue(value.bg, {colorPalette, bg, blendMode}) const colorOptions: RenderColorValueOptions = {colorPalette, bg: baseBg, blendMode} const button = renderThemeColorButton(value.button, { baseBg, blendMode, colorPalette, }) const selectable = renderThemeColorSelectable(value.selectable, { colorPalette, baseBg, blendMode, }) const shadow: ThemeColorShadow = { outline: renderColorValue(value.shadow.outline, colorOptions), umbra: renderColorValue(value.shadow.umbra, { ...colorOptions, bg: undefined, colorPalette: {...colorPalette, black: '#000000'}, }), penumbra: renderColorValue(value.shadow.penumbra, { ...colorOptions, bg: undefined, colorPalette: {...colorPalette, black: '#000000'}, }), ambient: renderColorValue(value.shadow.ambient, { ...colorOptions, bg: undefined, colorPalette: {...colorPalette, black: '#000000'}, }), } return { _blend: blendMode, _dark: value._dark, accent: { fg: renderColorValue(value.accent.fg, colorOptions), }, avatar: renderThemeColorAvatar(value.avatar, {baseBg, colorPalette, blendMode}), backdrop: renderColorValue(value.backdrop, colorOptions), badge: renderThemeColorBadge(value.badge, {baseBg, colorPalette, blendMode}), bg: baseBg, border: renderColorValue(value.border, colorOptions), button, code: { bg: renderColorValue(value.code.bg, colorOptions), fg: renderColorValue(value.code.fg, colorOptions), }, fg: renderColorValue(value.fg, colorOptions), focusRing: renderColorValue(value.focusRing, colorOptions), icon: renderColorValue(value.icon, colorOptions), input: renderThemeColorInput(value.input, {baseBg, colorPalette, blendMode}), kbd: renderThemeColorKBD(value.kbd, {baseBg, colorPalette, blendMode}), link: { fg: renderColorValue(value.link.fg, colorOptions), }, muted: { bg: renderColorValue(value.muted.bg, colorOptions), fg: renderColorValue(value.muted.fg, colorOptions), }, shadow, skeleton: { from: renderColorValue(value.skeleton.from, colorOptions), to: renderColorValue(value.skeleton.to, colorOptions), }, syntax: renderSyntaxColorTheme(value.syntax, {baseBg, colorPalette, blendMode}), selectable, } } function renderThemeColorKBD( value: ThemeColorKBD, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorKBD { const {baseBg, blendMode, colorPalette} = options const rootOptions: RenderColorValueOptions = { bg: baseBg, blendMode, colorPalette, } const bg = renderColorValue(value.bg, rootOptions) const colorOptions: RenderColorValueOptions = { bg, blendMode, colorPalette, } return { bg, fg: renderColorValue(value.fg, colorOptions), border: renderColorValue(value.border, colorOptions), } } function renderThemeColorAvatar( value: ThemeColorAvatar_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorAvatar_v2 { const colorAvatar = {} as ThemeColorAvatar_v2 for (const hue of COLOR_HUES) { colorAvatar[hue] = renderThemeColorAvatarColor(value[hue], options) } return colorAvatar } function renderThemeColorAvatarColor( value: ThemeColorAvatar_v2['gray'], options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorAvatar_v2['gray'] { const {baseBg, blendMode: rootBlendMode, colorPalette} = options const blendMode = value._blend || 'multiply' const rootOptions: RenderColorValueOptions = { bg: baseBg, blendMode: rootBlendMode, colorPalette, } const bg = renderColorValue(value.bg, rootOptions) const colorOptions: RenderColorValueOptions = { bg, blendMode, colorPalette, } return { _blend: blendMode, bg, fg: renderColorValue(value.fg, colorOptions), } } function renderThemeColorBadge( value: ThemeColorBadge_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorBadge_v2 { const colorBadge = {} as ThemeColorBadge_v2 for (const tone of THEME_COLOR_STATE_TONES) { colorBadge[tone] = renderThemeColorBadgeColor(value[tone], options) } return colorBadge } function renderThemeColorBadgeColor( value: ThemeColorBadgeTone_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorBadgeTone_v2 { const {baseBg, blendMode: rootBlendMode, colorPalette} = options const blendMode = rootBlendMode const rootOptions: RenderColorValueOptions = { bg: baseBg, blendMode: rootBlendMode, colorPalette, } const bg = renderColorValue(value.bg, rootOptions) const colorOptions: RenderColorValueOptions = { bg, blendMode, colorPalette, } return { bg, dot: renderColorValue(value.dot, colorOptions), fg: renderColorValue(value.fg, colorOptions), icon: renderColorValue(value.icon, colorOptions), } } function renderThemeColorButton( value: ThemeColorButton_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorButton_v2 { return { default: renderThemeColorButtonTones(value.default, options), ghost: renderThemeColorButtonTones(value.ghost, options), bleed: renderThemeColorButtonTones(value.bleed, options), } } function renderThemeColorButtonTones( value: ThemeColorButtonMode_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorButtonMode_v2 { const colorButtonMode = {} as ThemeColorButtonMode_v2 for (const tone of THEME_COLOR_STATE_TONES) { colorButtonMode[tone] = renderThemeColorButtonStates(value[tone], options) } return colorButtonMode } function renderThemeColorButtonStates( value: ThemeColorButtonTone_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorButtonTone_v2 { return { enabled: renderThemeColorState(value.enabled, options), hovered: renderThemeColorState(value.hovered, options), pressed: renderThemeColorState(value.pressed, options), selected: renderThemeColorState(value.selected, options), disabled: renderThemeColorState(value.disabled, options), } } function renderThemeColorState( value: ThemeColorState_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorState_v2 { const {baseBg, blendMode: rootBlendMode, colorPalette} = options const blendMode = value._blend || 'multiply' const rootOptions: RenderColorValueOptions = { bg: baseBg, blendMode: rootBlendMode, colorPalette, } const bg = renderColorValue(value.bg, rootOptions) const colorOptions: RenderColorValueOptions = { bg, blendMode, colorPalette, } return { _blend: blendMode, accent: { fg: renderColorValue(value.accent.fg, colorOptions), }, avatar: renderThemeColorAvatar(value.avatar, {baseBg: bg, colorPalette, blendMode}), badge: renderThemeColorBadge(value.badge, {baseBg: bg, colorPalette, blendMode}), bg, border: renderColorValue(value.border, colorOptions), code: { bg: renderColorValue(value.code.bg, colorOptions), fg: renderColorValue(value.code.fg, colorOptions), }, fg: renderColorValue(value.fg, colorOptions), icon: renderColorValue(value.icon, colorOptions), link: { fg: renderColorValue(value.link.fg, colorOptions), }, muted: { bg: renderColorValue(value.muted.bg, colorOptions), fg: renderColorValue(value.muted.fg, colorOptions), }, kbd: { bg: renderColorValue(value.kbd.bg, colorOptions), fg: renderColorValue(value.kbd.fg, colorOptions), border: renderColorValue(value.kbd.border, colorOptions), }, skeleton: { from: renderColorValue(value.skeleton?.from, colorOptions), to: renderColorValue(value.skeleton?.to, colorOptions), }, } } function renderThemeColorInput( value: ThemeColorInput_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorInput_v2 { return { default: renderInputStatesColorTheme(value.default, options), invalid: renderInputStatesColorTheme(value.invalid, options), } } function renderInputStatesColorTheme( value: ThemeColorInputMode_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorInputMode_v2 { return { enabled: renderInputStateColorTheme(value.enabled, options), hovered: renderInputStateColorTheme(value.hovered, options), readOnly: renderInputStateColorTheme(value.readOnly, options), disabled: renderInputStateColorTheme(value.disabled, options), } } function renderInputStateColorTheme( value: ThemeColorInputState_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorInputState_v2 { const {baseBg, blendMode: rootBlendMode, colorPalette} = options const blendMode = value._blend || 'multiply' const rootOptions: RenderColorValueOptions = {colorPalette, bg: baseBg, blendMode: rootBlendMode} const bg = renderColorValue(value.bg, rootOptions) const colorOptions: RenderColorValueOptions = {colorPalette, bg, blendMode} return { _blend: blendMode, bg, border: renderColorValue(value.border, colorOptions), fg: renderColorValue(value.fg, colorOptions), muted: { bg: renderColorValue(value.muted.bg, colorOptions), }, placeholder: renderColorValue(value.placeholder, colorOptions), } } function renderThemeColorSelectable( value: ThemeColorSelectable_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorSelectable_v2 { const colorSelectable = {} as ThemeColorSelectable_v2 for (const tone of THEME_COLOR_STATE_TONES) { colorSelectable[tone] = renderThemeColorSelectableStates(value[tone], options) } return colorSelectable } function renderThemeColorSelectableStates( value: ThemeColorSelectableTone_v2, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorSelectableTone_v2 { return { enabled: renderThemeColorState(value.enabled, options), hovered: renderThemeColorState(value.hovered, options), pressed: renderThemeColorState(value.pressed, options), selected: renderThemeColorState(value.selected, options), disabled: renderThemeColorState(value.disabled, options), } } function renderSyntaxColorTheme( value: ThemeColorSyntax, options: { baseBg: string blendMode: ThemeColorBlendModeKey colorPalette: ThemeColorPalette }, ): ThemeColorSyntax { const {colorPalette, baseBg, blendMode} = options const colorOptions: RenderColorValueOptions = {colorPalette, bg: baseBg, blendMode} return { atrule: renderColorValue(value.atrule, colorOptions), attrName: renderColorValue(value.attrName, colorOptions), attrValue: renderColorValue(value.attrValue, colorOptions), attribute: renderColorValue(value.attribute, colorOptions), boolean: renderColorValue(value.boolean, colorOptions), builtin: renderColorValue(value.builtin, colorOptions), cdata: renderColorValue(value.cdata, colorOptions), char: renderColorValue(value.char, colorOptions), class: renderColorValue(value.class, colorOptions), className: renderColorValue(value.className, colorOptions), comment: renderColorValue(value.comment, colorOptions), constant: renderColorValue(value.constant, colorOptions), deleted: renderColorValue(value.deleted, colorOptions), doctype: renderColorValue(value.doctype, colorOptions), entity: renderColorValue(value.entity, colorOptions), function: renderColorValue(value.function, colorOptions), hexcode: renderColorValue(value.hexcode, colorOptions), id: renderColorValue(value.id, colorOptions), important: renderColorValue(value.important, colorOptions), inserted: renderColorValue(value.inserted, colorOptions), keyword: renderColorValue(value.keyword, colorOptions), number: renderColorValue(value.number, colorOptions), operator: renderColorValue(value.operator, colorOptions), prolog: renderColorValue(value.prolog, colorOptions), property: renderColorValue(value.property, colorOptions), pseudoClass: renderColorValue(value.pseudoClass, colorOptions), pseudoElement: renderColorValue(value.pseudoElement, colorOptions), punctuation: renderColorValue(value.punctuation, colorOptions), regex: renderColorValue(value.regex, colorOptions), selector: renderColorValue(value.selector, colorOptions), string: renderColorValue(value.string, colorOptions), symbol: renderColorValue(value.symbol, colorOptions), tag: renderColorValue(value.tag, colorOptions), unit: renderColorValue(value.unit, colorOptions), url: renderColorValue(value.url, colorOptions), variable: renderColorValue(value.variable, colorOptions), } }