Save theme settings to local storage and improve slider styles

This commit is contained in:
Andrew Kingston 2020-10-30 13:23:49 +00:00
parent cb5bce660b
commit 1f5b1cfd10
4 changed files with 143 additions and 26 deletions

View File

@ -1,11 +1,13 @@
import { getStore } from "./store" import { getStore } from "./store"
import { getBackendUiStore } from "./store/backend" import { getBackendUiStore } from "./store/backend"
import { getAutomationStore } from "./store/automation/" import { getAutomationStore } from "./store/automation/"
import { getThemeStore } from "./store/theme"
import analytics from "analytics" import analytics from "analytics"
export const store = getStore() export const store = getStore()
export const backendUiStore = getBackendUiStore() export const backendUiStore = getBackendUiStore()
export const automationStore = getAutomationStore() export const automationStore = getAutomationStore()
export const themeStore = getThemeStore()
export const initialise = async () => { export const initialise = async () => {
try { try {

View File

@ -0,0 +1,43 @@
import { get, writable } from "svelte/store"
export const localStorageStore = (localStorageKey, initialValue) => {
const store = writable(initialValue, () => {
// Hydrate from local storage when we get a new subscriber
hydrate()
// Listen for local storage changes and keep store in sync
const storageListener = ({ key }) => key === localStorageKey && hydrate()
window.addEventListener("storage", storageListener)
return () => window.removeEventListener("storage", storageListener)
})
// New store setter which updates the store and localstorage
const set = value => {
store.set(value)
localStorage.setItem(localStorageKey, JSON.stringify(value))
}
// New store updater which updates the store and localstorage
const update = updaterFn => set(updaterFn(get(store)))
// Hydrates the store from localstorage
const hydrate = () => {
const localValue = localStorage.getItem(localStorageKey)
if (localValue == null) {
set(initialValue)
} else {
try {
store.set(JSON.parse(localValue))
} catch {
set(initialValue)
}
}
}
// Patch the default svelte store functions with our overrides
return {
...store,
set,
update,
}
}

View File

@ -0,0 +1,38 @@
import { localStorageStore } from "./localStorage"
export const getThemeStore = () => {
const themeElement = document.documentElement
const initialValue = {
darkMode: false,
hue: 208,
saturation: 9,
lightness: 16,
}
const store = localStorageStore("bb-theme", initialValue)
// Sets a CSS variable on the root document element
function setCSSVariable(prop, value) {
themeElement.style.setProperty(prop, value)
}
// Resets the custom theme to the default dark theme.
// The reset option is only available when dark theme is on, which is why it
// sets dark mode to true here
store.reset = () => {
store.set({
...initialValue,
darkMode: true,
})
}
// Update theme when store changes
store.subscribe(theme => {
themeElement.classList[theme.darkMode ? "add" : "remove"]("dark")
setCSSVariable("--theme-hue", Math.round(theme.hue))
setCSSVariable("--theme-saturation", `${Math.round(theme.saturation)}%`)
setCSSVariable("--theme-brightness", `${Math.round(theme.lightness)}%`)
})
return store
}

View File

@ -1,28 +1,10 @@
<script> <script>
import { themeStore } from "builderStore"
import { Label, DropdownMenu, Toggle, Button } from "@budibase/bbui" import { Label, DropdownMenu, Toggle, Button } from "@budibase/bbui"
let anchor let anchor
let popover let popover
let dark = false
let hue = 208
let saturation = 9
let lightness = 16
let showAdvanced = false let showAdvanced = false
function setCSSVariable(prop, value) {
document.documentElement.style.setProperty(prop, value)
}
function reset() {
hue = 208
saturation = 9
lightness = 16
}
$: document.documentElement.classList[dark ? "add" : "remove"]("dark")
$: setCSSVariable("--theme-hue", Math.round(hue))
$: setCSSVariable("--theme-saturation", `${Math.round(saturation)}%`)
$: setCSSVariable("--theme-brightness", `${Math.round(lightness)}%`)
</script> </script>
<div class="topnavitemright" on:click={popover.show} bind:this={anchor}> <div class="topnavitemright" on:click={popover.show} bind:this={anchor}>
@ -33,28 +15,36 @@
<div class="content"> <div class="content">
<div> <div>
<Label extraSmall grey>Theme</Label> <Label extraSmall grey>Theme</Label>
<Toggle thin text="Dark theme" bind:checked={dark} /> <Toggle thin text="Dark theme" bind:checked={$themeStore.darkMode} />
</div> </div>
{#if dark && !showAdvanced} {#if $themeStore.darkMode && !showAdvanced}
<div class="button"> <div class="button">
<Button text on:click={() => (showAdvanced = true)}>Customise</Button> <Button text on:click={() => (showAdvanced = true)}>Customise</Button>
</div> </div>
{/if} {/if}
{#if dark && showAdvanced} {#if $themeStore.darkMode && showAdvanced}
<div> <div>
<Label extraSmall grey>Hue</Label> <Label extraSmall grey>Hue</Label>
<input type="range" bind:value={hue} min="0" max="360" /> <input type="range" bind:value={$themeStore.hue} min="0" max="360" />
</div> </div>
<div> <div>
<Label extraSmall grey>Saturation</Label> <Label extraSmall grey>Saturation</Label>
<input type="range" bind:value={saturation} min="0" max="100" /> <input
type="range"
bind:value={$themeStore.saturation}
min="0"
max="100" />
</div> </div>
<div> <div>
<Label extraSmall grey>Lightness</Label> <Label extraSmall grey>Lightness</Label>
<input type="range" bind:value={lightness} min="0" max="32" /> <input
type="range"
bind:value={$themeStore.lightness}
min="0"
max="32" />
</div> </div>
<div class="button"> <div class="button">
<Button text on:click={reset}>Reset</Button> <Button text on:click={themeStore.reset}>Reset</Button>
</div> </div>
{/if} {/if}
</div> </div>
@ -104,4 +94,48 @@
.button { .button {
align-self: flex-start; align-self: flex-start;
} }
input[type="range"] {
width: 100%;
margin: 0;
background-color: transparent;
-webkit-appearance: none;
}
input[type="range"]:focus {
outline: none;
}
input[type="range"]::-webkit-slider-runnable-track {
background: var(--grey-4);
border-radius: 9px;
width: 100%;
height: 18px;
cursor: pointer;
padding: 0 2px;
}
input[type="range"]::-webkit-slider-thumb {
width: 14px;
height: 14px;
background: white;
border-radius: 100%;
cursor: pointer;
-webkit-appearance: none;
border: none;
margin-top: 2px;
}
input[type="range"]::-moz-range-track {
background: var(--grey-4);
border-radius: 9px;
width: 100%;
height: 18px;
cursor: pointer;
padding: 0 2px;
}
input[type="range"]::-moz-range-thumb {
width: 14px;
height: 14px;
background: white;
border-radius: 100%;
cursor: pointer;
border: none;
}
</style> </style>