1
0
Fork 0
mirror of synced 2024-10-03 02:27:06 +13:00

Add client app state and button actions to control it

This commit is contained in:
Andrew Kingston 2021-08-26 11:28:44 +01:00
parent 46dad8b841
commit 9369e8b375
8 changed files with 143 additions and 19 deletions

View file

@ -18,7 +18,9 @@ export const getBindableProperties = (asset, componentId) => {
const userBindings = getUserBindings() const userBindings = getUserBindings()
const urlBindings = getUrlBindings(asset) const urlBindings = getUrlBindings(asset)
const deviceBindings = getDeviceBindings() const deviceBindings = getDeviceBindings()
const stateBindings = getStateBindings()
return [ return [
...stateBindings,
...deviceBindings, ...deviceBindings,
...urlBindings, ...urlBindings,
...contextBindings, ...contextBindings,
@ -256,6 +258,19 @@ const getDeviceBindings = () => {
return bindings return bindings
} }
/**
* Gets all state bindings that are globally available.
*/
const getStateBindings = () => {
return [
{
type: "context",
runtimeBinding: makePropSafe("state"),
readableBinding: `State`,
},
]
}
/** /**
* Gets all bindable properties from URL parameters. * Gets all bindable properties from URL parameters.
*/ */

View file

@ -0,0 +1,56 @@
<script>
import { Select, Label, Input } from "@budibase/bbui"
import { onMount } from "svelte"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
export let parameters
export let bindings = []
const typeOptions = [
{
label: "Set value",
value: "set",
},
{
label: "Delete value",
value: "delete",
},
]
onMount(() => {
if (!parameters.type) {
parameters.type = "set"
}
})
</script>
<div class="root">
<Label small>Type</Label>
<Select
placeholder={null}
bind:value={parameters.type}
options={typeOptions}
/>
<Label small>Key</Label>
<Input bind:value={parameters.key} />
{#if parameters.type === "set"}
<Label small>Value</Label>
<DrawerBindableInput
{bindings}
value={parameters.value}
on:change={e => (parameters.value = e.detail)}
/>
{/if}
</div>
<style>
.root {
display: grid;
column-gap: var(--spacing-l);
row-gap: var(--spacing-s);
grid-template-columns: 60px 1fr;
align-items: center;
max-width: 400px;
margin: 0 auto;
}
</style>

View file

@ -8,6 +8,7 @@ import LogOut from "./LogOut.svelte"
import ClearForm from "./ClearForm.svelte" import ClearForm from "./ClearForm.svelte"
import CloseScreenModal from "./CloseScreenModal.svelte" import CloseScreenModal from "./CloseScreenModal.svelte"
import ChangeFormStep from "./ChangeFormStep.svelte" import ChangeFormStep from "./ChangeFormStep.svelte"
import UpdateStateStep from "./UpdateState.svelte"
// Defines which actions are available to configure in the front end. // Defines which actions are available to configure in the front end.
// Unfortunately the "name" property is used as the identifier so please don't // Unfortunately the "name" property is used as the identifier so please don't
@ -57,4 +58,8 @@ export default [
name: "Change Form Step", name: "Change Form Step",
component: ChangeFormStep, component: ChangeFormStep,
}, },
{
name: "Update State",
component: UpdateStateStep,
},
] ]

View file

@ -22,6 +22,7 @@
import ErrorSVG from "../../../builder/assets/error.svg" import ErrorSVG from "../../../builder/assets/error.svg"
import UserBindingsProvider from "./UserBindingsProvider.svelte" import UserBindingsProvider from "./UserBindingsProvider.svelte"
import DeviceBindingsProvider from "./DeviceBindingsProvider.svelte" import DeviceBindingsProvider from "./DeviceBindingsProvider.svelte"
import StateBindingsProvider from "./StateBindingsProvider.svelte"
// Provide contexts // Provide contexts
setContext("sdk", SDK) setContext("sdk", SDK)
@ -85,6 +86,7 @@
{:else if $screenStore.activeLayout} {:else if $screenStore.activeLayout}
<UserBindingsProvider> <UserBindingsProvider>
<DeviceBindingsProvider> <DeviceBindingsProvider>
<StateBindingsProvider>
<div id="app-root" class:preview={$builderStore.inBuilder}> <div id="app-root" class:preview={$builderStore.inBuilder}>
{#key $screenStore.activeLayout._id} {#key $screenStore.activeLayout._id}
<Component instance={$screenStore.activeLayout.props} /> <Component instance={$screenStore.activeLayout.props} />
@ -107,6 +109,7 @@
<SelectionIndicator /> <SelectionIndicator />
<HoverIndicator /> <HoverIndicator />
{/if} {/if}
</StateBindingsProvider>
</DeviceBindingsProvider> </DeviceBindingsProvider>
</UserBindingsProvider> </UserBindingsProvider>
{/if} {/if}

View file

@ -0,0 +1,8 @@
<script>
import Provider from "./Provider.svelte"
import { stateStore } from "../store"
</script>
<Provider key="state" data={$stateStore}>
<slot />
</Provider>

View file

@ -7,6 +7,7 @@ export { builderStore } from "./builder"
export { dataSourceStore } from "./dataSource" export { dataSourceStore } from "./dataSource"
export { confirmationStore } from "./confirmation" export { confirmationStore } from "./confirmation"
export { peekStore } from "./peek" export { peekStore } from "./peek"
export { stateStore } from "./state"
// 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,25 @@
import { writable } from "svelte/store"
const createStateStore = () => {
const store = writable({})
const setValue = (key, value) => {
store.update(state => {
state[key] = value
return state
})
}
const deleteValue = key => {
store.update(state => {
delete state[key]
return state
})
}
return {
subscribe: store.subscribe,
actions: { setValue, deleteValue },
}
}
export const stateStore = createStateStore()

View file

@ -5,6 +5,7 @@ import {
confirmationStore, confirmationStore,
authStore, authStore,
peekStore, peekStore,
stateStore,
} from "../store" } from "../store"
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api" import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
import { ActionTypes } from "../constants" import { ActionTypes } from "../constants"
@ -122,6 +123,15 @@ const closeScreenModalHandler = () => {
window.dispatchEvent(new Event("close-screen-modal")) window.dispatchEvent(new Event("close-screen-modal"))
} }
const updateStateHandler = action => {
const { type, key, value } = action.parameters
if (type === "set") {
stateStore.actions.setValue(key, value)
} else if (type === "delete") {
stateStore.actions.deleteValue(key)
}
}
const handlerMap = { const handlerMap = {
["Save Row"]: saveRowHandler, ["Save Row"]: saveRowHandler,
["Delete Row"]: deleteRowHandler, ["Delete Row"]: deleteRowHandler,
@ -134,6 +144,7 @@ const handlerMap = {
["Clear Form"]: clearFormHandler, ["Clear Form"]: clearFormHandler,
["Close Screen Modal"]: closeScreenModalHandler, ["Close Screen Modal"]: closeScreenModalHandler,
["Change Form Step"]: changeFormStepHandler, ["Change Form Step"]: changeFormStepHandler,
["Update State"]: updateStateHandler,
} }
const confirmTextMap = { const confirmTextMap = {