theme.ts

  1import {
  2    SingleBoxShadowToken,
  3    SingleColorToken,
  4    SingleOtherToken,
  5    TokenTypes,
  6} from "@tokens-studio/types"
  7import {
  8    Shadow,
  9    SyntaxHighlightStyle,
 10    ThemeSyntax,
 11} from "../create_theme"
 12import { LayerToken, layer_token } from "./layer"
 13import { PlayersToken, players_token } from "./players"
 14import { color_token } from "./token"
 15import { Syntax } from "../syntax"
 16import editor from "../../style_tree/editor"
 17import { useTheme } from "../../../src/common"
 18
 19interface ThemeTokens {
 20    name: SingleOtherToken
 21    appearance: SingleOtherToken
 22    lowest: LayerToken
 23    middle: LayerToken
 24    highest: LayerToken
 25    players: PlayersToken
 26    popover_shadow: SingleBoxShadowToken
 27    modal_shadow: SingleBoxShadowToken
 28    syntax?: Partial<ThemeSyntaxColorTokens>
 29}
 30
 31const create_shadow_token = (
 32    shadow: Shadow,
 33    token_name: string
 34): SingleBoxShadowToken => {
 35    return {
 36        name: token_name,
 37        type: TokenTypes.BOX_SHADOW,
 38        value: `${shadow.offset[0]}px ${shadow.offset[1]}px ${shadow.blur}px 0px ${shadow.color}`,
 39    }
 40}
 41
 42const popover_shadow_token = (): SingleBoxShadowToken => {
 43    const theme = useTheme()
 44    const shadow = theme.popover_shadow
 45    return create_shadow_token(shadow, "popover_shadow")
 46}
 47
 48const modal_shadow_token = (): SingleBoxShadowToken => {
 49    const theme = useTheme()
 50    const shadow = theme.modal_shadow
 51    return create_shadow_token(shadow, "modal_shadow")
 52}
 53
 54type ThemeSyntaxColorTokens = Record<keyof ThemeSyntax, SingleColorToken>
 55
 56function syntax_highlight_style_color_tokens(
 57    syntax: Syntax
 58): ThemeSyntaxColorTokens {
 59    const style_keys = Object.keys(syntax) as (keyof Syntax)[]
 60
 61    return style_keys.reduce((acc, style_key) => {
 62        // Hack: The type of a style could be "Function"
 63        // This can happen because we have a "constructor" property on the syntax object
 64        // and a "constructor" property on the prototype of the syntax object
 65        // To work around this just assert that the type of the style is not a function
 66        if (!syntax[style_key] || typeof syntax[style_key] === "function")
 67            return acc
 68        const { color } = syntax[style_key] as Required<SyntaxHighlightStyle>
 69        return { ...acc, [style_key]: color_token(style_key, color) }
 70    }, {} as ThemeSyntaxColorTokens)
 71}
 72
 73const syntax_tokens = (): ThemeTokens["syntax"] => {
 74    const syntax = editor().syntax
 75
 76    return syntax_highlight_style_color_tokens(syntax)
 77}
 78
 79export function theme_tokens(): ThemeTokens {
 80    const theme = useTheme()
 81
 82    return {
 83        name: {
 84            name: "themeName",
 85            value: theme.name,
 86            type: TokenTypes.OTHER,
 87        },
 88        appearance: {
 89            name: "themeAppearance",
 90            value: theme.is_light ? "light" : "dark",
 91            type: TokenTypes.OTHER,
 92        },
 93        lowest: layer_token(theme.lowest, "lowest"),
 94        middle: layer_token(theme.middle, "middle"),
 95        highest: layer_token(theme.highest, "highest"),
 96        popover_shadow: popover_shadow_token(),
 97        modal_shadow: modal_shadow_token(),
 98        players: players_token(),
 99        syntax: syntax_tokens(),
100    }
101}