Remove lightest and dark themes, use enums for themes, standardise naming

This commit is contained in:
Andrew Kingston 2024-10-24 09:07:36 +01:00
parent 8466f8883c
commit 247d57887a
No known key found for this signature in database
10 changed files with 107 additions and 77 deletions

View File

@ -141,13 +141,13 @@
icon: "ShareAndroid", icon: "ShareAndroid",
action: () => $goto(`./automation/${automation._id}`), action: () => $goto(`./automation/${automation._id}`),
})) ?? []), })) ?? []),
...Constants.Themes.map(theme => ({ ...Constants.ThemeOptions.map(theme => ({
type: "Change Builder Theme", type: "Change Builder Theme",
name: theme.name, name: theme.name,
icon: "ColorPalette", icon: "ColorPalette",
action: () => action: () =>
themeStore.update(state => { themeStore.update(state => {
state.theme = theme.class state.theme = theme.id
return state return state
}), }),
})), })),

View File

@ -6,10 +6,10 @@
<ModalContent title="Theme"> <ModalContent title="Theme">
<Select <Select
options={Constants.Themes} options={Constants.ThemeOptions}
bind:value={$themeStore.theme} bind:value={$themeStore.theme}
placeholder={null} placeholder={null}
getOptionLabel={x => x.name} getOptionLabel={x => x.name}
getOptionValue={x => x.class} getOptionValue={x => x.id}
/> />
</ModalContent> </ModalContent>

View File

@ -1,11 +1,11 @@
<script> <script>
import { notifications } from "@budibase/bbui" import { notifications } from "@budibase/bbui"
import { themeStore, appStore } from "stores/builder" import { themeStore, appStore } from "stores/builder"
import { Constants } from "@budibase/frontend-core" import { Constants, getThemeClassNames } from "@budibase/frontend-core"
const onChangeTheme = async theme => { const onChangeTheme = async theme => {
try { try {
await themeStore.save(`spectrum--${theme}`, $appStore.appId) await themeStore.save(theme, $appStore.appId)
} catch (error) { } catch (error) {
notifications.error("Error updating theme") notifications.error("Error updating theme")
} }
@ -15,16 +15,13 @@
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="container"> <div class="container">
{#each Constants.Themes as theme} {#each Constants.ThemeOptions as theme}
<div <div
class="theme" class="theme"
class:selected={`spectrum--${theme.class}` === $themeStore.theme} class:selected={theme.id === $themeStore.theme}
on:click={() => onChangeTheme(theme.class)} on:click={() => onChangeTheme(theme.id)}
> >
<div <div class="color {getThemeClassNames(theme.id)}" />
style="background: {theme.preview}"
class="color spectrum--{theme.class}"
/>
{theme.name} {theme.name}
</div> </div>
{/each} {/each}

View File

@ -21,7 +21,10 @@
import { sdk } from "@budibase/shared-core" import { sdk } from "@budibase/shared-core"
import { API } from "api" import { API } from "api"
import ErrorSVG from "./ErrorSVG.svelte" import ErrorSVG from "./ErrorSVG.svelte"
import { getBaseTheme, ClientAppSkeleton } from "@budibase/frontend-core" import {
ClientAppSkeleton,
getThemeClassNames,
} from "@budibase/frontend-core"
import { contextMenuStore } from "stores/builder" import { contextMenuStore } from "stores/builder"
$: app = $enrichedApps.find(app => app.appId === $params.appId) $: app = $enrichedApps.find(app => app.appId === $params.appId)
@ -163,9 +166,7 @@
class:hide={!loading || !app?.features?.skeletonLoader} class:hide={!loading || !app?.features?.skeletonLoader}
class="loading" class="loading"
> >
<div <div class="loadingThemeWrapper {getThemeClassNames(app.theme)}">
class={`loadingThemeWrapper ${getBaseTheme(app.theme)} ${app.theme}`}
>
<ClientAppSkeleton <ClientAppSkeleton
noAnimation noAnimation
hideDevTools={app?.status === "published"} hideDevTools={app?.status === "published"}

View File

@ -1,24 +1,24 @@
import { writable, get } from "svelte/store" import { writable, get } from "svelte/store"
import { API } from "api" import { API } from "api"
import { getBaseTheme } from "@budibase/frontend-core" import { Constants, ensureValidTheme } from "@budibase/frontend-core"
const DefaultAppTheme = Constants.Themes.Light
const INITIAL_THEMES_STATE = { const INITIAL_THEMES_STATE = {
theme: "", theme: DefaultAppTheme,
customTheme: {}, customTheme: {},
} }
export const themes = () => { export const createThemeStore = () => {
const store = writable({ const store = writable({
...INITIAL_THEMES_STATE, ...INITIAL_THEMES_STATE,
}) })
const syncAppTheme = app => { const syncAppTheme = app => {
store.update(state => { store.update(state => {
const theme = app.theme || "spectrum--light" const theme = ensureValidTheme(app.theme, DefaultAppTheme)
return { return {
...state, ...state,
theme, theme,
baseTheme: getBaseTheme(theme),
customTheme: app.customTheme, customTheme: app.customTheme,
} }
}) })
@ -51,7 +51,7 @@ export const themes = () => {
const { theme, customTheme } = metadata const { theme, customTheme } = metadata
store.update(state => ({ store.update(state => ({
...state, ...state,
theme, theme: ensureValidTheme(theme, DefaultAppTheme),
customTheme, customTheme,
})) }))
} }
@ -66,4 +66,4 @@ export const themes = () => {
} }
} }
export const themeStore = themes() export const themeStore = createThemeStore()

View File

@ -1,38 +1,36 @@
import { Constants, createLocalStorageStore } from "@budibase/frontend-core" import {
Constants,
createLocalStorageStore,
ensureValidTheme,
getThemeClassNames,
} from "@budibase/frontend-core"
import { derived } from "svelte/store"
export const getThemeStore = () => { export const getThemeStore = () => {
const defaultBuilderTheme = Constants.Themes.Darkest
const themeElement = document.documentElement const themeElement = document.documentElement
const initialValue = { const initialValue = {
theme: "darkest", theme: defaultBuilderTheme,
} }
const store = createLocalStorageStore("bb-theme", initialValue) const store = createLocalStorageStore("bb-theme", initialValue)
const derivedStore = derived(store, $store => ({
...$store,
theme: ensureValidTheme($store.theme, defaultBuilderTheme),
}))
// Update theme class when store changes // Update theme class when store changes
store.subscribe(state => { derivedStore.subscribe(({ theme }) => {
// Handle any old local storage values - this can be removed after the update const classNames = getThemeClassNames(theme).split(" ")
if (state.darkMode !== undefined) { Constants.ThemeOptions.forEach(option => {
store.set(initialValue) const className = `${Constants.ThemeClassPrefix}${option.id}`
return themeElement.classList.toggle(className, classNames.includes(className))
}
// Update global class names to use the new theme and remove others
Constants.Themes.forEach(option => {
themeElement.classList.toggle(
`spectrum--${option.class}`,
option.class === state.theme
)
}) })
// Add base theme if required
const selectedTheme = Constants.Themes.find(x => x.class === state.theme)
if (selectedTheme?.base) {
themeElement.classList.add(`spectrum--${selectedTheme.base}`)
}
}) })
return store return {
...store,
subscribe: derivedStore.subscribe,
}
} }
// ?? confusion
export const themeStore = getThemeStore() export const themeStore = getThemeStore()

View File

@ -3,7 +3,11 @@
import { setContext, onMount } from "svelte" import { setContext, onMount } from "svelte"
import { Layout, Heading, Body } from "@budibase/bbui" import { Layout, Heading, Body } from "@budibase/bbui"
import ErrorSVG from "@budibase/frontend-core/assets/error.svg" import ErrorSVG from "@budibase/frontend-core/assets/error.svg"
import { Constants, CookieUtils } from "@budibase/frontend-core" import {
Constants,
CookieUtils,
getThemeClassNames,
} from "@budibase/frontend-core"
import Component from "./Component.svelte" import Component from "./Component.svelte"
import SDK from "sdk" import SDK from "sdk"
import { import {
@ -154,7 +158,7 @@
id="spectrum-root" id="spectrum-root"
lang="en" lang="en"
dir="ltr" dir="ltr"
class="spectrum spectrum--medium {$themeStore.baseTheme} {$themeStore.theme}" class="spectrum spectrum--medium {getThemeClassNames($themeStore.theme)}"
class:builder={$builderStore.inBuilder} class:builder={$builderStore.inBuilder}
class:show={fontsLoaded && dataLoaded} class:show={fontsLoaded && dataLoaded}
> >

View File

@ -1,12 +1,12 @@
import { derived } from "svelte/store" import { derived } from "svelte/store"
import { appStore } from "./app" import { appStore } from "./app"
import { builderStore } from "./builder" import { builderStore } from "./builder"
import { getBaseTheme } from "@budibase/frontend-core" import { Constants, ensureValidTheme } from "@budibase/frontend-core"
// This is the good old acorn bug where having the word "g l o b a l" makes it // This is the good old acorn bug where having the word "g l o b a l" makes it
// think that this is not ES6 compatible and starts throwing errors when using // think that this is not ES6 compatible and starts throwing errors when using
// optional chaining. Piss off acorn. // optional chaining. Piss off acorn.
const defaultTheme = "spectrum--light" const defaultTheme = Constants.Themes.Light
const defaultCustomTheme = { const defaultCustomTheme = {
primaryColor: "var(--spectrum-glo" + "bal-color-blue-600)", primaryColor: "var(--spectrum-glo" + "bal-color-blue-600)",
primaryColorHover: "var(--spectrum-glo" + "bal-color-blue-500)", primaryColorHover: "var(--spectrum-glo" + "bal-color-blue-500)",
@ -27,7 +27,7 @@ const createThemeStore = () => {
} }
// Ensure theme is set // Ensure theme is set
theme = theme || defaultTheme theme = ensureValidTheme(theme, defaultTheme)
// Delete and nullish keys from the custom theme // Delete and nullish keys from the custom theme
if (customTheme) { if (customTheme) {
@ -52,7 +52,6 @@ const createThemeStore = () => {
return { return {
theme, theme,
baseTheme: getBaseTheme(theme),
customTheme, customTheme,
customThemeCss, customThemeCss,
} }

View File

@ -108,32 +108,34 @@ export const Roles = {
CREATOR: "CREATOR", CREATOR: "CREATOR",
} }
export const Themes = [ // Theming
export const ThemeClassPrefix = "spectrum--"
export const Themes = {
Lightest: "lightest",
Light: "light",
Dark: "dark",
Darkest: "darkest",
Nord: "nord",
Midnight: "midnight",
}
export const ThemeOptions = [
{ {
class: "lightest", id: Themes.Light,
name: "Lightest",
},
{
class: "light",
name: "Light", name: "Light",
}, },
{ {
class: "dark", id: Themes.Darkest,
name: "Dark", name: "Dark",
}, },
{ {
class: "darkest", id: Themes.Nord,
name: "Darkest",
},
{
class: "nord",
name: "Nord", name: "Nord",
base: "darkest", base: Themes.Darkest,
}, },
{ {
class: "midnight", id: Themes.Midnight,
name: "Midnight", name: "Midnight",
base: "darkest", base: Themes.Darkest,
}, },
] ]

View File

@ -1,12 +1,41 @@
import { Themes } from "../constants.js" import { Themes, ThemeOptions, ThemeClassPrefix } from "../constants.js"
export const getBaseTheme = theme => { // Gets the CSS class names for the specified theme
if (!theme) { export const getThemeClassNames = theme => {
return "" theme = ensureValidTheme(theme)
} let classNames = `${ThemeClassPrefix}${theme}`
let base = Themes.find(x => `spectrum--${x.class}` === theme)?.base || ""
// Prefix with base class if required
const base = ThemeOptions.find(x => x.id === theme)?.base
if (base) { if (base) {
base = `spectrum--${base}` classNames = `${ThemeClassPrefix}${base} ${classNames}`
} }
return base
return classNames
}
// Ensures a theme value is a valid option
export const ensureValidTheme = (theme, fallback = Themes.Darkest) => {
// Default to darkest
if (!theme) {
return fallback
}
// Ensure we aren't using the spectrum prefix
if (theme.startsWith(ThemeClassPrefix)) {
theme = theme.split(ThemeClassPrefix)[1]
}
// Check we aren't using a deprecated theme, and migrate
// to the nearest valid theme if we are
if (!ThemeOptions.some(x => x.id === theme)) {
if (theme === Themes.Lightest) {
return Themes.Light
} else if (theme === Themes.Dark) {
return Themes.Darkest
} else {
return fallback
}
}
return theme
} }