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,
|
||||
intelligentLoading: false,
|
||||
deviceAwareness: false,
|
||||
state: false,
|
||||
customThemes: false,
|
||||
},
|
||||
currentFrontEndType: "none",
|
||||
selectedScreenId: "",
|
||||
|
@ -53,6 +55,7 @@ const INITIAL_FRONTEND_STATE = {
|
|||
routes: {},
|
||||
clientLibPath: "",
|
||||
theme: "",
|
||||
customTheme: {},
|
||||
}
|
||||
|
||||
export const getFrontendStore = () => {
|
||||
|
@ -77,6 +80,7 @@ export const getFrontendStore = () => {
|
|||
layouts,
|
||||
screens,
|
||||
theme: application.theme || "spectrum--light",
|
||||
customTheme: application.customTheme,
|
||||
hasAppPackage: true,
|
||||
appInstance: application.instance,
|
||||
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: {
|
||||
fetch: async () => {
|
||||
const response = await api.get("/api/routing")
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
<style>
|
||||
div {
|
||||
width: 100px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
selectedComponentId,
|
||||
previewType: $store.currentFrontEndType,
|
||||
theme: $store.theme,
|
||||
customTheme: $store.customTheme,
|
||||
}
|
||||
|
||||
// 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,
|
||||
previewType,
|
||||
appId,
|
||||
theme
|
||||
theme,
|
||||
customTheme
|
||||
} = parsed
|
||||
|
||||
// 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_TYPE##"] = previewType
|
||||
window["##BUDIBASE_PREVIEW_THEME##"] = theme
|
||||
window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"] = customTheme
|
||||
|
||||
// Initialise app
|
||||
try {
|
||||
|
|
|
@ -9,11 +9,12 @@
|
|||
ActionMenu,
|
||||
MenuItem,
|
||||
} from "@budibase/bbui"
|
||||
import actionTypes from "./actions"
|
||||
import { getAvailableActions } from "./actions"
|
||||
import { generate } from "shortid"
|
||||
|
||||
const flipDurationMs = 150
|
||||
const EVENT_TYPE_KEY = "##eventHandlerType"
|
||||
const actionTypes = getAvailableActions()
|
||||
|
||||
export let actions
|
||||
export let bindings = []
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import { store } from "builderStore"
|
||||
import { get } from "svelte/store"
|
||||
|
||||
import NavigateTo from "./NavigateTo.svelte"
|
||||
import SaveRow from "./SaveRow.svelte"
|
||||
import DeleteRow from "./DeleteRow.svelte"
|
||||
|
@ -17,49 +20,56 @@ import UpdateStateStep from "./UpdateState.svelte"
|
|||
// be considered as camel case too.
|
||||
// There is technical debt here to sanitize all these and standardise them
|
||||
// across the packages but it's a breaking change to existing apps.
|
||||
export default [
|
||||
{
|
||||
name: "Save Row",
|
||||
component: SaveRow,
|
||||
},
|
||||
{
|
||||
name: "Delete Row",
|
||||
component: DeleteRow,
|
||||
},
|
||||
{
|
||||
name: "Navigate To",
|
||||
component: NavigateTo,
|
||||
},
|
||||
{
|
||||
name: "Execute Query",
|
||||
component: ExecuteQuery,
|
||||
},
|
||||
{
|
||||
name: "Trigger Automation",
|
||||
component: TriggerAutomation,
|
||||
},
|
||||
{
|
||||
name: "Validate Form",
|
||||
component: ValidateForm,
|
||||
},
|
||||
{
|
||||
name: "Log Out",
|
||||
component: LogOut,
|
||||
},
|
||||
{
|
||||
name: "Clear Form",
|
||||
component: ClearForm,
|
||||
},
|
||||
{
|
||||
name: "Close Screen Modal",
|
||||
component: CloseScreenModal,
|
||||
},
|
||||
{
|
||||
name: "Change Form Step",
|
||||
component: ChangeFormStep,
|
||||
},
|
||||
{
|
||||
name: "Update State",
|
||||
component: UpdateStateStep,
|
||||
},
|
||||
]
|
||||
export const getAvailableActions = () => {
|
||||
let actions = [
|
||||
{
|
||||
name: "Save Row",
|
||||
component: SaveRow,
|
||||
},
|
||||
{
|
||||
name: "Delete Row",
|
||||
component: DeleteRow,
|
||||
},
|
||||
{
|
||||
name: "Navigate To",
|
||||
component: NavigateTo,
|
||||
},
|
||||
{
|
||||
name: "Execute Query",
|
||||
component: ExecuteQuery,
|
||||
},
|
||||
{
|
||||
name: "Trigger Automation",
|
||||
component: TriggerAutomation,
|
||||
},
|
||||
{
|
||||
name: "Validate Form",
|
||||
component: ValidateForm,
|
||||
},
|
||||
{
|
||||
name: "Log Out",
|
||||
component: LogOut,
|
||||
},
|
||||
{
|
||||
name: "Clear Form",
|
||||
component: ClearForm,
|
||||
},
|
||||
{
|
||||
name: "Close Screen Modal",
|
||||
component: CloseScreenModal,
|
||||
},
|
||||
{
|
||||
name: "Change Form Step",
|
||||
component: ChangeFormStep,
|
||||
},
|
||||
]
|
||||
|
||||
if (get(store).clientFeatures?.state) {
|
||||
actions.push({
|
||||
name: "Update State",
|
||||
component: UpdateStateStep,
|
||||
})
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
import { findComponent, findComponentPath } from "builderStore/storeUtils"
|
||||
import { get } from "svelte/store"
|
||||
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
|
||||
let previousType
|
||||
|
@ -153,6 +154,9 @@
|
|||
{#if $store.clientFeatures.spectrumThemes}
|
||||
<AppThemeSelect />
|
||||
{/if}
|
||||
{#if $store.clientFeatures.customThemes}
|
||||
<ThemeEditor />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="preview-content">
|
||||
{#key $store.version}
|
||||
|
@ -202,9 +206,18 @@
|
|||
padding: var(--spacing-xl) 40px;
|
||||
}
|
||||
.preview-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 100px;
|
||||
display: flex;
|
||||
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 {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
"features": {
|
||||
"spectrumThemes": true,
|
||||
"intelligentLoading": true,
|
||||
"deviceAwareness": true
|
||||
"deviceAwareness": true,
|
||||
"state": true,
|
||||
"customThemes": true
|
||||
},
|
||||
"layout": {
|
||||
"name": "Layout",
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
authStore,
|
||||
routeStore,
|
||||
builderStore,
|
||||
appStore,
|
||||
themeStore,
|
||||
} from "stores"
|
||||
import NotificationDisplay from "components/overlay/NotificationDisplay.svelte"
|
||||
import ConfirmationDisplay from "components/overlay/ConfirmationDisplay.svelte"
|
||||
|
@ -63,9 +63,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: themeClass =
|
||||
$builderStore.theme || $appStore.application?.theme || "spectrum--light"
|
||||
</script>
|
||||
|
||||
{#if dataLoaded}
|
||||
|
@ -73,7 +70,7 @@
|
|||
id="spectrum-root"
|
||||
lang="en"
|
||||
dir="ltr"
|
||||
class="spectrum spectrum--medium {themeClass}"
|
||||
class="spectrum spectrum--medium {$themeStore.theme}"
|
||||
>
|
||||
{#if permissionError}
|
||||
<div class="error">
|
||||
|
@ -87,7 +84,11 @@
|
|||
<UserBindingsProvider>
|
||||
<DeviceBindingsProvider>
|
||||
<StateBindingsProvider>
|
||||
<div id="app-root" class:preview={$builderStore.inBuilder}>
|
||||
<div
|
||||
id="app-root"
|
||||
class:preview={$builderStore.inBuilder}
|
||||
style={$themeStore.customThemeCss}
|
||||
>
|
||||
{#key $screenStore.activeLayout._id}
|
||||
<Component instance={$screenStore.activeLayout.props} />
|
||||
{/key}
|
||||
|
@ -132,6 +133,20 @@
|
|||
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 */
|
||||
:global(::-webkit-scrollbar) {
|
||||
width: 8px;
|
||||
|
|
|
@ -17,6 +17,7 @@ const loadBudibase = () => {
|
|||
previewId: window["##BUDIBASE_PREVIEW_ID##"],
|
||||
previewType: window["##BUDIBASE_PREVIEW_TYPE##"],
|
||||
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
|
||||
|
|
|
@ -8,6 +8,7 @@ export { dataSourceStore } from "./dataSource"
|
|||
export { confirmationStore } from "./confirmation"
|
||||
export { peekStore } from "./peek"
|
||||
export { stateStore } from "./state"
|
||||
export { themeStore } from "./theme"
|
||||
|
||||
// Context stores are layered and duplicated, so it is not a singleton
|
||||
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