Hide state action in old apps and add working basic implementation of theming
This commit is contained in:
parent
551eb629b4
commit
04a26f3344
|
@ -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")
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
|
width: 100px;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>
|
|
@ -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 {
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
|
@ -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,7 +20,8 @@ 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",
|
name: "Save Row",
|
||||||
component: SaveRow,
|
component: SaveRow,
|
||||||
|
@ -58,8 +62,14 @@ export default [
|
||||||
name: "Change Form Step",
|
name: "Change Form Step",
|
||||||
component: ChangeFormStep,
|
component: ChangeFormStep,
|
||||||
},
|
},
|
||||||
{
|
]
|
||||||
|
|
||||||
|
if (get(store).clientFeatures?.state) {
|
||||||
|
actions.push({
|
||||||
name: "Update State",
|
name: "Update State",
|
||||||
component: UpdateStateStep,
|
component: UpdateStateStep,
|
||||||
},
|
})
|
||||||
]
|
}
|
||||||
|
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()
|
Loading…
Reference in New Issue