feat: update and refactor colorScheme

Sergey Onufrienko created

Change summary

styles/src/themes/common/colorScheme.ts | 188 +++++++++++++++++++++++
styles/src/themes/common/ramps.ts       | 218 +++-----------------------
2 files changed, 212 insertions(+), 194 deletions(-)

Detailed changes

styles/src/themes/common/colorScheme.ts 🔗

@@ -1,6 +1,12 @@
-import { Scale } from "chroma-js"
+import { Scale, Color } from "chroma-js"
 import { Syntax, ThemeSyntax, SyntaxHighlightStyle } from "./syntax"
 export { Syntax, ThemeSyntax, SyntaxHighlightStyle }
+import {
+    ThemeConfig,
+    ThemeAppearance,
+    ThemeConfigInputColors,
+} from "../../themeConfig"
+import { getRamps } from "./ramps"
 
 export interface ColorScheme {
     name: string
@@ -103,3 +109,183 @@ export interface Style {
     border: string
     foreground: string
 }
+
+export function createColorScheme(theme: ThemeConfig): ColorScheme {
+    const {
+        name,
+        appearance,
+        inputColor,
+        override: { syntax },
+    } = theme
+
+    const isLight = appearance === ThemeAppearance.Light
+    const colorRamps: ThemeConfigInputColors = inputColor
+
+    // Chromajs scales from 0 to 1 flipped if isLight is true
+    const ramps = getRamps(isLight, colorRamps)
+    const lowest = lowestLayer(ramps)
+    const middle = middleLayer(ramps)
+    const highest = highestLayer(ramps)
+
+    const popoverShadow = {
+        blur: 4,
+        color: ramps
+            .neutral(isLight ? 7 : 0)
+            .darken()
+            .alpha(0.2)
+            .hex(), // TODO used blend previously. Replace with something else
+        offset: [1, 2],
+    }
+
+    const modalShadow = {
+        blur: 16,
+        color: ramps
+            .neutral(isLight ? 7 : 0)
+            .darken()
+            .alpha(0.2)
+            .hex(), // TODO used blend previously. Replace with something else
+        offset: [0, 2],
+    }
+
+    const players = {
+        "0": player(ramps.blue),
+        "1": player(ramps.green),
+        "2": player(ramps.magenta),
+        "3": player(ramps.orange),
+        "4": player(ramps.violet),
+        "5": player(ramps.cyan),
+        "6": player(ramps.red),
+        "7": player(ramps.yellow),
+    }
+
+    return {
+        name,
+        isLight,
+
+        ramps,
+
+        lowest,
+        middle,
+        highest,
+
+        popoverShadow,
+        modalShadow,
+
+        players,
+        syntax,
+    }
+}
+
+function player(ramp: Scale): Player {
+    return {
+        selection: ramp(0.5).alpha(0.24).hex(),
+        cursor: ramp(0.5).hex(),
+    }
+}
+
+function lowestLayer(ramps: RampSet): Layer {
+    return {
+        base: buildStyleSet(ramps.neutral, 0.2, 1),
+        variant: buildStyleSet(ramps.neutral, 0.2, 0.7),
+        on: buildStyleSet(ramps.neutral, 0.1, 1),
+        accent: buildStyleSet(ramps.blue, 0.1, 0.5),
+        positive: buildStyleSet(ramps.green, 0.1, 0.5),
+        warning: buildStyleSet(ramps.yellow, 0.1, 0.5),
+        negative: buildStyleSet(ramps.red, 0.1, 0.5),
+    }
+}
+
+function middleLayer(ramps: RampSet): Layer {
+    return {
+        base: buildStyleSet(ramps.neutral, 0.1, 1),
+        variant: buildStyleSet(ramps.neutral, 0.1, 0.7),
+        on: buildStyleSet(ramps.neutral, 0, 1),
+        accent: buildStyleSet(ramps.blue, 0.1, 0.5),
+        positive: buildStyleSet(ramps.green, 0.1, 0.5),
+        warning: buildStyleSet(ramps.yellow, 0.1, 0.5),
+        negative: buildStyleSet(ramps.red, 0.1, 0.5),
+    }
+}
+
+function highestLayer(ramps: RampSet): Layer {
+    return {
+        base: buildStyleSet(ramps.neutral, 0, 1),
+        variant: buildStyleSet(ramps.neutral, 0, 0.7),
+        on: buildStyleSet(ramps.neutral, 0.1, 1),
+        accent: buildStyleSet(ramps.blue, 0.1, 0.5),
+        positive: buildStyleSet(ramps.green, 0.1, 0.5),
+        warning: buildStyleSet(ramps.yellow, 0.1, 0.5),
+        negative: buildStyleSet(ramps.red, 0.1, 0.5),
+    }
+}
+
+function buildStyleSet(
+    ramp: Scale,
+    backgroundBase: number,
+    foregroundBase: number,
+    step: number = 0.08
+): StyleSet {
+    let styleDefinitions = buildStyleDefinition(
+        backgroundBase,
+        foregroundBase,
+        step
+    )
+
+    function colorString(indexOrColor: number | Color): string {
+        if (typeof indexOrColor === "number") {
+            return ramp(indexOrColor).hex()
+        } else {
+            return indexOrColor.hex()
+        }
+    }
+
+    function buildStyle(style: Styles): Style {
+        return {
+            background: colorString(styleDefinitions.background[style]),
+            border: colorString(styleDefinitions.border[style]),
+            foreground: colorString(styleDefinitions.foreground[style]),
+        }
+    }
+
+    return {
+        default: buildStyle("default"),
+        hovered: buildStyle("hovered"),
+        pressed: buildStyle("pressed"),
+        active: buildStyle("active"),
+        disabled: buildStyle("disabled"),
+        inverted: buildStyle("inverted"),
+    }
+}
+
+function buildStyleDefinition(
+    bgBase: number,
+    fgBase: number,
+    step: number = 0.08
+) {
+    return {
+        background: {
+            default: bgBase,
+            hovered: bgBase + step,
+            pressed: bgBase + step * 1.5,
+            active: bgBase + step * 2.2,
+            disabled: bgBase,
+            inverted: fgBase + step * 6,
+        },
+        border: {
+            default: bgBase + step * 1,
+            hovered: bgBase + step,
+            pressed: bgBase + step,
+            active: bgBase + step * 3,
+            disabled: bgBase + step * 0.5,
+            inverted: bgBase - step * 3,
+        },
+        foreground: {
+            default: fgBase,
+            hovered: fgBase,
+            pressed: fgBase,
+            active: fgBase + step * 6,
+            disabled: bgBase + step * 4,
+            inverted: bgBase + step * 2,
+        },
+    }
+}

styles/src/themes/common/ramps.ts 🔗

@@ -1,14 +1,9 @@
 import chroma, { Color, Scale } from "chroma-js"
+import { RampSet } from "./colorScheme"
 import {
-    ColorScheme,
-    Layer,
-    Player,
-    RampSet,
-    Style,
-    Styles,
-    StyleSet,
-    ThemeSyntax,
-} from "./colorScheme"
+    ThemeConfigInputColors,
+    ThemeConfigInputColorsKeys,
+} from "../../themeConfig"
 
 export function colorRamp(color: Color): Scale {
     let endColor = color.desaturate(1).brighten(5)
@@ -16,200 +11,37 @@ export function colorRamp(color: Color): Scale {
     return chroma.scale([startColor, color, endColor]).mode("lab")
 }
 
-export function createColorScheme(
-    name: string,
+/**
+ * Chromajs mutates the underlying ramp when you call domain. This causes problems because
+    we now store the ramps object in the theme so that we can pull colors out of them.
+    So instead of calling domain and storing the result, we have to construct new ramps for each
+    theme so that we don't modify the passed in ramps.
+    This combined with an error in the type definitions for chroma js means we have to cast the colors
+    function to any in order to get the colors back out from the original ramps.
+ * @param isLight 
+ * @param colorRamps 
+ * @returns 
+ */
+export function getRamps(
     isLight: boolean,
-    colorRamps: { [rampName: string]: Scale },
-    syntax?: ThemeSyntax
-): ColorScheme {
-    // Chromajs scales from 0 to 1 flipped if isLight is true
-    let ramps: RampSet = {} as any
+    colorRamps: ThemeConfigInputColors
+): RampSet {
+    const ramps: RampSet = {} as any
+    const colorsKeys = Object.keys(colorRamps) as ThemeConfigInputColorsKeys[]
 
-    // Chromajs mutates the underlying ramp when you call domain. This causes problems because
-    // we now store the ramps object in the theme so that we can pull colors out of them.
-    // So instead of calling domain and storing the result, we have to construct new ramps for each
-    // theme so that we don't modify the passed in ramps.
-    // This combined with an error in the type definitions for chroma js means we have to cast the colors
-    // function to any in order to get the colors back out from the original ramps.
     if (isLight) {
-        for (var rampName in colorRamps) {
-            ;(ramps as any)[rampName] = chroma.scale(
+        for (const rampName of colorsKeys) {
+            ramps[rampName] = chroma.scale(
                 colorRamps[rampName].colors(100).reverse()
             )
         }
         ramps.neutral = chroma.scale(colorRamps.neutral.colors(100).reverse())
     } else {
-        for (var rampName in colorRamps) {
-            ;(ramps as any)[rampName] = chroma.scale(
-                colorRamps[rampName].colors(100)
-            )
+        for (const rampName of colorsKeys) {
+            ramps[rampName] = chroma.scale(colorRamps[rampName].colors(100))
         }
         ramps.neutral = chroma.scale(colorRamps.neutral.colors(100))
     }
 
-    let lowest = lowestLayer(ramps)
-    let middle = middleLayer(ramps)
-    let highest = highestLayer(ramps)
-
-    let popoverShadow = {
-        blur: 4,
-        color: ramps
-            .neutral(isLight ? 7 : 0)
-            .darken()
-            .alpha(0.2)
-            .hex(), // TODO used blend previously. Replace with something else
-        offset: [1, 2],
-    }
-
-    let modalShadow = {
-        blur: 16,
-        color: ramps
-            .neutral(isLight ? 7 : 0)
-            .darken()
-            .alpha(0.2)
-            .hex(), // TODO used blend previously. Replace with something else
-        offset: [0, 2],
-    }
-
-    let players = {
-        "0": player(ramps.blue),
-        "1": player(ramps.green),
-        "2": player(ramps.magenta),
-        "3": player(ramps.orange),
-        "4": player(ramps.violet),
-        "5": player(ramps.cyan),
-        "6": player(ramps.red),
-        "7": player(ramps.yellow),
-    }
-
-    return {
-        name,
-        isLight,
-
-        ramps,
-
-        lowest,
-        middle,
-        highest,
-
-        popoverShadow,
-        modalShadow,
-
-        players,
-        syntax,
-    }
-}
-
-function player(ramp: Scale): Player {
-    return {
-        selection: ramp(0.5).alpha(0.24).hex(),
-        cursor: ramp(0.5).hex(),
-    }
-}
-
-function lowestLayer(ramps: RampSet): Layer {
-    return {
-        base: buildStyleSet(ramps.neutral, 0.2, 1),
-        variant: buildStyleSet(ramps.neutral, 0.2, 0.7),
-        on: buildStyleSet(ramps.neutral, 0.1, 1),
-        accent: buildStyleSet(ramps.blue, 0.1, 0.5),
-        positive: buildStyleSet(ramps.green, 0.1, 0.5),
-        warning: buildStyleSet(ramps.yellow, 0.1, 0.5),
-        negative: buildStyleSet(ramps.red, 0.1, 0.5),
-    }
-}
-
-function middleLayer(ramps: RampSet): Layer {
-    return {
-        base: buildStyleSet(ramps.neutral, 0.1, 1),
-        variant: buildStyleSet(ramps.neutral, 0.1, 0.7),
-        on: buildStyleSet(ramps.neutral, 0, 1),
-        accent: buildStyleSet(ramps.blue, 0.1, 0.5),
-        positive: buildStyleSet(ramps.green, 0.1, 0.5),
-        warning: buildStyleSet(ramps.yellow, 0.1, 0.5),
-        negative: buildStyleSet(ramps.red, 0.1, 0.5),
-    }
-}
-
-function highestLayer(ramps: RampSet): Layer {
-    return {
-        base: buildStyleSet(ramps.neutral, 0, 1),
-        variant: buildStyleSet(ramps.neutral, 0, 0.7),
-        on: buildStyleSet(ramps.neutral, 0.1, 1),
-        accent: buildStyleSet(ramps.blue, 0.1, 0.5),
-        positive: buildStyleSet(ramps.green, 0.1, 0.5),
-        warning: buildStyleSet(ramps.yellow, 0.1, 0.5),
-        negative: buildStyleSet(ramps.red, 0.1, 0.5),
-    }
-}
-
-function buildStyleSet(
-    ramp: Scale,
-    backgroundBase: number,
-    foregroundBase: number,
-    step: number = 0.08
-): StyleSet {
-    let styleDefinitions = buildStyleDefinition(
-        backgroundBase,
-        foregroundBase,
-        step
-    )
-
-    function colorString(indexOrColor: number | Color): string {
-        if (typeof indexOrColor === "number") {
-            return ramp(indexOrColor).hex()
-        } else {
-            return indexOrColor.hex()
-        }
-    }
-
-    function buildStyle(style: Styles): Style {
-        return {
-            background: colorString(styleDefinitions.background[style]),
-            border: colorString(styleDefinitions.border[style]),
-            foreground: colorString(styleDefinitions.foreground[style]),
-        }
-    }
-
-    return {
-        default: buildStyle("default"),
-        hovered: buildStyle("hovered"),
-        pressed: buildStyle("pressed"),
-        active: buildStyle("active"),
-        disabled: buildStyle("disabled"),
-        inverted: buildStyle("inverted"),
-    }
-}
-
-function buildStyleDefinition(
-    bgBase: number,
-    fgBase: number,
-    step: number = 0.08
-) {
-    return {
-        background: {
-            default: bgBase,
-            hovered: bgBase + step,
-            pressed: bgBase + step * 1.5,
-            active: bgBase + step * 2.2,
-            disabled: bgBase,
-            inverted: fgBase + step * 6,
-        },
-        border: {
-            default: bgBase + step * 1,
-            hovered: bgBase + step,
-            pressed: bgBase + step,
-            active: bgBase + step * 3,
-            disabled: bgBase + step * 0.5,
-            inverted: bgBase - step * 3,
-        },
-        foreground: {
-            default: fgBase,
-            hovered: fgBase,
-            pressed: fgBase,
-            active: fgBase + step * 6,
-            disabled: bgBase + step * 4,
-            inverted: bgBase + step * 2,
-        },
-    }
+    return ramps
 }