Hide state action in old apps and add working basic implementation of theming

This commit is contained in:
Andrew Kingston 2021-09-02 11:38:41 +01:00
parent 551eb629b4
commit 04a26f3344
13 changed files with 230 additions and 57 deletions

View File

@ -41,6 +41,8 @@ const INITIAL_FRONTEND_STATE = {
spectrumThemes: false, spectrumThemes: false,
intelligentLoading: false, intelligentLoading: false,
deviceAwareness: false, deviceAwareness: false,
state: false,
customThemes: false,
}, },
currentFrontEndType: "none", currentFrontEndType: "none",
selectedScreenId: "", selectedScreenId: "",
@ -53,6 +55,7 @@ const INITIAL_FRONTEND_STATE = {
routes: {}, routes: {},
clientLibPath: "", clientLibPath: "",
theme: "", theme: "",
customTheme: {},
} }
export const getFrontendStore = () => { export const getFrontendStore = () => {
@ -77,6 +80,7 @@ export const getFrontendStore = () => {
layouts, layouts,
screens, screens,
theme: application.theme || "spectrum--light", theme: application.theme || "spectrum--light",
customTheme: application.customTheme,
hasAppPackage: true, hasAppPackage: true,
appInstance: application.instance, appInstance: application.instance,
clientLibPath, clientLibPath,
@ -110,6 +114,22 @@ export const getFrontendStore = () => {
} }
}, },
}, },
customTheme: {
save: async customTheme => {
const appId = get(store).appId
const response = await api.put(`/api/applications/${appId}`, {
customTheme,
})
if (response.status === 200) {
store.update(state => {
state.customTheme = customTheme
return state
})
} else {
throw new Error("Error updating theme")
}
},
},
routing: { routing: {
fetch: async () => { fetch: async () => {
const response = await api.get("/api/routing") const response = await api.get("/api/routing")

View File

@ -33,6 +33,7 @@
<style> <style>
div { div {
width: 100px;
padding-right: 8px; padding-right: 8px;
} }
</style> </style>

View File

@ -49,6 +49,7 @@
selectedComponentId, selectedComponentId,
previewType: $store.currentFrontEndType, previewType: $store.currentFrontEndType,
theme: $store.theme, theme: $store.theme,
customTheme: $store.customTheme,
} }
// Saving pages and screens to the DB causes them to have _revs. // Saving pages and screens to the DB causes them to have _revs.

View File

@ -0,0 +1,76 @@
<script>
import { get } from "svelte/store"
import {
ActionButton,
Modal,
ModalContent,
Layout,
ColorPicker,
Label,
} from "@budibase/bbui"
import { store } from "builderStore"
let modal
const defaultTheme = {
primaryColor: "var(--spectrum-global-color-blue-600)",
primaryColorHover: "var(--spectrum-global-color-blue-500)",
}
const updateProperty = property => {
return e => {
store.actions.customTheme.save({
...get(store).customTheme,
[property]: e.detail,
})
}
}
</script>
<div class="container">
<ActionButton icon="Brush" on:click={modal.show}>Edit</ActionButton>
</div>
<Modal bind:this={modal}>
<ModalContent
showConfirmButton={false}
cancelText="Close"
showCloseIcon={false}
title="Theme settings"
>
<Layout noPadding gap="XS">
<div class="setting">
<Label size="L">Primary Color</Label>
<ColorPicker
value={$store.customTheme?.primaryColor || defaultTheme.primaryColor}
on:change={updateProperty("primaryColor")}
/>
</div>
<div class="setting">
<Label size="L">Primary Color (Hover)</Label>
<ColorPicker
value={$store.customTheme?.primaryColorHover ||
defaultTheme.primaryColorHover}
on:change={updateProperty("primaryColorHover")}
/>
</div>
</Layout>
</ModalContent>
</Modal>
<style>
.container {
padding-right: 8px;
}
.setting {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding-bottom: var(--spectrum-global-dimension-static-size-100);
border-bottom: var(--border-light);
}
.setting:last-child {
padding-bottom: 0;
border-bottom: none;
}
</style>

View File

@ -66,7 +66,8 @@ export default `
screen, screen,
previewType, previewType,
appId, appId,
theme theme,
customTheme
} = parsed } = parsed
// Set some flags so the app knows we're in the builder // Set some flags so the app knows we're in the builder
@ -78,6 +79,7 @@ export default `
window["##BUDIBASE_PREVIEW_ID##"] = Math.random() window["##BUDIBASE_PREVIEW_ID##"] = Math.random()
window["##BUDIBASE_PREVIEW_TYPE##"] = previewType window["##BUDIBASE_PREVIEW_TYPE##"] = previewType
window["##BUDIBASE_PREVIEW_THEME##"] = theme window["##BUDIBASE_PREVIEW_THEME##"] = theme
window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"] = customTheme
// Initialise app // Initialise app
try { try {

View File

@ -9,11 +9,12 @@
ActionMenu, ActionMenu,
MenuItem, MenuItem,
} from "@budibase/bbui" } from "@budibase/bbui"
import actionTypes from "./actions" import { getAvailableActions } from "./actions"
import { generate } from "shortid" import { generate } from "shortid"
const flipDurationMs = 150 const flipDurationMs = 150
const EVENT_TYPE_KEY = "##eventHandlerType" const EVENT_TYPE_KEY = "##eventHandlerType"
const actionTypes = getAvailableActions()
export let actions export let actions
export let bindings = [] export let bindings = []

View File

@ -1,3 +1,6 @@
import { store } from "builderStore"
import { get } from "svelte/store"
import NavigateTo from "./NavigateTo.svelte" import NavigateTo from "./NavigateTo.svelte"
import SaveRow from "./SaveRow.svelte" import SaveRow from "./SaveRow.svelte"
import DeleteRow from "./DeleteRow.svelte" import DeleteRow from "./DeleteRow.svelte"
@ -17,49 +20,56 @@ import UpdateStateStep from "./UpdateState.svelte"
// be considered as camel case too. // be considered as camel case too.
// There is technical debt here to sanitize all these and standardise them // There is technical debt here to sanitize all these and standardise them
// across the packages but it's a breaking change to existing apps. // across the packages but it's a breaking change to existing apps.
export default [ export const getAvailableActions = () => {
{ let actions = [
name: "Save Row", {
component: SaveRow, name: "Save Row",
}, component: SaveRow,
{ },
name: "Delete Row", {
component: DeleteRow, name: "Delete Row",
}, component: DeleteRow,
{ },
name: "Navigate To", {
component: NavigateTo, name: "Navigate To",
}, component: NavigateTo,
{ },
name: "Execute Query", {
component: ExecuteQuery, name: "Execute Query",
}, component: ExecuteQuery,
{ },
name: "Trigger Automation", {
component: TriggerAutomation, name: "Trigger Automation",
}, component: TriggerAutomation,
{ },
name: "Validate Form", {
component: ValidateForm, name: "Validate Form",
}, component: ValidateForm,
{ },
name: "Log Out", {
component: LogOut, name: "Log Out",
}, component: LogOut,
{ },
name: "Clear Form", {
component: ClearForm, name: "Clear Form",
}, component: ClearForm,
{ },
name: "Close Screen Modal", {
component: CloseScreenModal, name: "Close Screen Modal",
}, component: CloseScreenModal,
{ },
name: "Change Form Step", {
component: ChangeFormStep, name: "Change Form Step",
}, component: ChangeFormStep,
{ },
name: "Update State", ]
component: UpdateStateStep,
}, if (get(store).clientFeatures?.state) {
] actions.push({
name: "Update State",
component: UpdateStateStep,
})
}
return actions
}

View File

@ -14,6 +14,7 @@
import { findComponent, findComponentPath } from "builderStore/storeUtils" import { findComponent, findComponentPath } from "builderStore/storeUtils"
import { get } from "svelte/store" import { get } from "svelte/store"
import AppThemeSelect from "components/design/AppPreview/AppThemeSelect.svelte" import AppThemeSelect from "components/design/AppPreview/AppThemeSelect.svelte"
import ThemeEditor from "components/design/AppPreview/ThemeEditor.svelte"
// Cache previous values so we don't update the URL more than necessary // Cache previous values so we don't update the URL more than necessary
let previousType let previousType
@ -153,6 +154,9 @@
{#if $store.clientFeatures.spectrumThemes} {#if $store.clientFeatures.spectrumThemes}
<AppThemeSelect /> <AppThemeSelect />
{/if} {/if}
{#if $store.clientFeatures.customThemes}
<ThemeEditor />
{/if}
</div> </div>
<div class="preview-content"> <div class="preview-content">
{#key $store.version} {#key $store.version}
@ -202,9 +206,18 @@
padding: var(--spacing-xl) 40px; padding: var(--spacing-xl) 40px;
} }
.preview-header { .preview-header {
display: grid; display: flex;
grid-template-columns: 1fr 100px; flex-direction: row;
justify-content: flex-start;
align-items: center;
} }
.preview-header > :global(*) {
flex: 0 0 auto;
}
.preview-header > :global(*:first-child) {
flex: 1 1 auto;
}
.preview-content { .preview-content {
flex: 1 1 auto; flex: 1 1 auto;
} }

View File

@ -2,7 +2,9 @@
"features": { "features": {
"spectrumThemes": true, "spectrumThemes": true,
"intelligentLoading": true, "intelligentLoading": true,
"deviceAwareness": true "deviceAwareness": true,
"state": true,
"customThemes": true
}, },
"layout": { "layout": {
"name": "Layout", "name": "Layout",

View File

@ -11,7 +11,7 @@
authStore, authStore,
routeStore, routeStore,
builderStore, builderStore,
appStore, themeStore,
} from "stores" } from "stores"
import NotificationDisplay from "components/overlay/NotificationDisplay.svelte" import NotificationDisplay from "components/overlay/NotificationDisplay.svelte"
import ConfirmationDisplay from "components/overlay/ConfirmationDisplay.svelte" import ConfirmationDisplay from "components/overlay/ConfirmationDisplay.svelte"
@ -63,9 +63,6 @@
} }
} }
} }
$: themeClass =
$builderStore.theme || $appStore.application?.theme || "spectrum--light"
</script> </script>
{#if dataLoaded} {#if dataLoaded}
@ -73,7 +70,7 @@
id="spectrum-root" id="spectrum-root"
lang="en" lang="en"
dir="ltr" dir="ltr"
class="spectrum spectrum--medium {themeClass}" class="spectrum spectrum--medium {$themeStore.theme}"
> >
{#if permissionError} {#if permissionError}
<div class="error"> <div class="error">
@ -87,7 +84,11 @@
<UserBindingsProvider> <UserBindingsProvider>
<DeviceBindingsProvider> <DeviceBindingsProvider>
<StateBindingsProvider> <StateBindingsProvider>
<div id="app-root" class:preview={$builderStore.inBuilder}> <div
id="app-root"
class:preview={$builderStore.inBuilder}
style={$themeStore.customThemeCss}
>
{#key $screenStore.activeLayout._id} {#key $screenStore.activeLayout._id}
<Component instance={$screenStore.activeLayout.props} /> <Component instance={$screenStore.activeLayout.props} />
{/key} {/key}
@ -132,6 +133,20 @@
border: 1px solid var(--spectrum-global-color-gray-300); border: 1px solid var(--spectrum-global-color-gray-300);
} }
#app-root {
/* Primary */
--spectrum-global-color-blue-600: var(--primaryColor);
--spectrum-global-color-blue-700: var(--primaryColor);
--spectrum-global-color-static-blue-600: var(--primaryColor);
--spectrum-global-color-static-blue-700: var(--primaryColor);
/* Primary hover */
--spectrum-global-color-blue-400: var(--primaryColorHover);
--spectrum-global-color-blue-500: var(--primaryColorHover);
--spectrum-global-color-static-blue-400: var(--primaryColorHover);
--spectrum-global-color-static-blue-500: var(--primaryColorHover);
}
/* Custom scrollbars */ /* Custom scrollbars */
:global(::-webkit-scrollbar) { :global(::-webkit-scrollbar) {
width: 8px; width: 8px;

View File

@ -17,6 +17,7 @@ const loadBudibase = () => {
previewId: window["##BUDIBASE_PREVIEW_ID##"], previewId: window["##BUDIBASE_PREVIEW_ID##"],
previewType: window["##BUDIBASE_PREVIEW_TYPE##"], previewType: window["##BUDIBASE_PREVIEW_TYPE##"],
theme: window["##BUDIBASE_PREVIEW_THEME##"], theme: window["##BUDIBASE_PREVIEW_THEME##"],
customTheme: window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"],
}) })
// Set app ID - this window flag is set by both the preview and the real // Set app ID - this window flag is set by both the preview and the real

View File

@ -8,6 +8,7 @@ export { dataSourceStore } from "./dataSource"
export { confirmationStore } from "./confirmation" export { confirmationStore } from "./confirmation"
export { peekStore } from "./peek" export { peekStore } from "./peek"
export { stateStore } from "./state" export { stateStore } from "./state"
export { themeStore } from "./theme"
// Context stores are layered and duplicated, so it is not a singleton // Context stores are layered and duplicated, so it is not a singleton
export { createContextStore } from "./context" export { createContextStore } from "./context"

View File

@ -0,0 +1,30 @@
import { derived } from "svelte/store"
import { appStore } from "./app"
import { builderStore } from "./builder"
const createThemeStore = () => {
const store = derived(
[builderStore, appStore],
([$builderStore, $appStore]) => {
const theme =
$builderStore.theme || $appStore.application?.theme || "spectrum--light"
const customTheme =
$builderStore.customTheme || $appStore.application?.customTheme || {}
let customThemeCss = ""
Object.entries(customTheme).forEach(([key, value]) => {
customThemeCss += `--${key}:${value};`
})
return {
theme,
customTheme,
customThemeCss,
}
}
)
return {
subscribe: store.subscribe,
}
}
export const themeStore = createThemeStore()