1
0
Fork 0
mirror of synced 2024-09-15 00:38:01 +12:00

Merge pull request #6409 from Budibase/useful-features

"On screen load" actions & query params binding
This commit is contained in:
Andrew Kingston 2022-06-22 08:49:51 +01:00 committed by GitHub
commit 1811926515
5 changed files with 138 additions and 89 deletions

View file

@ -390,11 +390,17 @@ const getUrlBindings = asset => {
} }
}) })
const safeURL = makePropSafe("url") const safeURL = makePropSafe("url")
return params.map(param => ({ const urlParamBindings = params.map(param => ({
type: "context", type: "context",
runtimeBinding: `${safeURL}.${makePropSafe(param)}`, runtimeBinding: `${safeURL}.${makePropSafe(param)}`,
readableBinding: `URL.${param}`, readableBinding: `URL.${param}`,
})) }))
const queryParamsBinding = {
type: "context",
runtimeBinding: makePropSafe("query"),
readableBinding: "Query params",
}
return urlParamBindings.concat([queryParamsBinding])
} }
const getRoleBindings = () => { const getRoleBindings = () => {
@ -694,6 +700,13 @@ export const getAllStateVariables = () => {
}) })
}) })
// Add on load settings from screens
get(store).screens.forEach(screen => {
if (screen.onLoad) {
eventSettings.push(screen.onLoad)
}
})
// Extract all state keys from any "update state" actions in each setting // Extract all state keys from any "update state" actions in each setting
let bindingSet = new Set() let bindingSet = new Set()
eventSettings.forEach(setting => { eventSettings.forEach(setting => {

View file

@ -17,6 +17,10 @@
import { selectedScreen, store } from "builderStore" import { selectedScreen, store } from "builderStore"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import ButtonActionEditor from "components/design/settings/controls/ButtonActionEditor/ButtonActionEditor.svelte"
import { getBindableProperties } from "builderStore/dataBinding"
$: bindings = getBindableProperties($selectedScreen, null)
let errors = {} let errors = {}
@ -147,6 +151,11 @@
disabled: !!$selectedScreen.layoutId, disabled: !!$selectedScreen.layoutId,
}, },
}, },
{
key: "onLoad",
label: "On screen load",
control: ButtonActionEditor,
},
] ]
const removeCustomLayout = async () => { const removeCustomLayout = async () => {
@ -178,6 +187,7 @@
value={deepGet($selectedScreen, setting.key)} value={deepGet($selectedScreen, setting.key)}
onChange={val => setScreenSetting(setting, val)} onChange={val => setScreenSetting(setting, val)}
props={{ ...setting.props, error: errors[setting.key] }} props={{ ...setting.props, error: errors[setting.key] }}
{bindings}
/> />
{/each} {/each}
<Button cta on:click={() => $goto("../components")}>View components</Button> <Button cta on:click={() => $goto("../components")}>View components</Button>

View file

@ -24,6 +24,7 @@
import DeviceBindingsProvider from "components/context/DeviceBindingsProvider.svelte" import DeviceBindingsProvider from "components/context/DeviceBindingsProvider.svelte"
import StateBindingsProvider from "components/context/StateBindingsProvider.svelte" import StateBindingsProvider from "components/context/StateBindingsProvider.svelte"
import RowSelectionProvider from "components/context/RowSelectionProvider.svelte" import RowSelectionProvider from "components/context/RowSelectionProvider.svelte"
import QueryParamsProvider from "components/context/QueryParamsProvider.svelte"
import SettingsBar from "components/preview/SettingsBar.svelte" import SettingsBar from "components/preview/SettingsBar.svelte"
import SelectionIndicator from "components/preview/SelectionIndicator.svelte" import SelectionIndicator from "components/preview/SelectionIndicator.svelte"
import HoverIndicator from "components/preview/HoverIndicator.svelte" import HoverIndicator from "components/preview/HoverIndicator.svelte"
@ -94,95 +95,97 @@
<UserBindingsProvider> <UserBindingsProvider>
<StateBindingsProvider> <StateBindingsProvider>
<RowSelectionProvider> <RowSelectionProvider>
<!-- Settings bar can be rendered outside of device preview --> <QueryParamsProvider>
<!-- Key block needs to be outside the if statement or it breaks --> <!-- Settings bar can be rendered outside of device preview -->
{#key $builderStore.selectedComponentId} <!-- Key block needs to be outside the if statement or it breaks -->
{#if $builderStore.inBuilder} {#key $builderStore.selectedComponentId}
<SettingsBar /> {#if $builderStore.inBuilder}
{/if} <SettingsBar />
{/key}
<!-- Clip boundary for selection indicators -->
<div
id="clip-root"
class:preview={$builderStore.inBuilder}
class:tablet-preview={$builderStore.previewDevice === "tablet"}
class:mobile-preview={$builderStore.previewDevice === "mobile"}
>
<!-- Actual app -->
<div id="app-root">
{#if showDevTools}
<DevToolsHeader />
{/if} {/if}
{/key}
<div id="app-body"> <!-- Clip boundary for selection indicators -->
{#if permissionError} <div
<div class="error"> id="clip-root"
<Layout justifyItems="center" gap="S"> class:preview={$builderStore.inBuilder}
{@html ErrorSVG} class:tablet-preview={$builderStore.previewDevice === "tablet"}
<Heading size="L"> class:mobile-preview={$builderStore.previewDevice === "mobile"}
You don't have permission to use this app >
</Heading> <!-- Actual app -->
<Body size="S"> <div id="app-root">
Ask your administrator to grant you access
</Body>
</Layout>
</div>
{:else if !$screenStore.activeLayout}
<div class="error">
<Layout justifyItems="center" gap="S">
{@html ErrorSVG}
<Heading size="L">
Something went wrong rendering your app
</Heading>
<Body size="S">
Get in touch with support if this issue persists
</Body>
</Layout>
</div>
{:else}
<CustomThemeWrapper>
{#key $screenStore.activeLayout._id}
<Component
isLayout
instance={$screenStore.activeLayout.props}
/>
{/key}
<!--
Flatpickr needs to be inside the theme wrapper.
It also needs its own container because otherwise it hijacks
key events on the whole page. It is painful to work with.
-->
<div id="flatpickr-root" />
<!-- Modal container to ensure they sit on top -->
<div class="modal-container" />
<!-- Layers on top of app -->
<NotificationDisplay />
<ConfirmationDisplay />
<PeekScreenDisplay />
</CustomThemeWrapper>
{/if}
{#if showDevTools} {#if showDevTools}
<DevTools /> <DevToolsHeader />
{/if} {/if}
</div>
</div>
<!-- Preview and dev tools utilities --> <div id="app-body">
{#if $appStore.isDevApp} {#if permissionError}
<SelectionIndicator /> <div class="error">
{/if} <Layout justifyItems="center" gap="S">
{#if $builderStore.inBuilder || $devToolsStore.allowSelection} {@html ErrorSVG}
<HoverIndicator /> <Heading size="L">
{/if} You don't have permission to use this app
{#if $builderStore.inBuilder} </Heading>
<DNDHandler /> <Body size="S">
{/if} Ask your administrator to grant you access
</div> </Body>
</Layout>
</div>
{:else if !$screenStore.activeLayout}
<div class="error">
<Layout justifyItems="center" gap="S">
{@html ErrorSVG}
<Heading size="L">
Something went wrong rendering your app
</Heading>
<Body size="S">
Get in touch with support if this issue persists
</Body>
</Layout>
</div>
{:else}
<CustomThemeWrapper>
{#key $screenStore.activeLayout._id}
<Component
isLayout
instance={$screenStore.activeLayout.props}
/>
{/key}
<!--
Flatpickr needs to be inside the theme wrapper.
It also needs its own container because otherwise it hijacks
key events on the whole page. It is painful to work with.
-->
<div id="flatpickr-root" />
<!-- Modal container to ensure they sit on top -->
<div class="modal-container" />
<!-- Layers on top of app -->
<NotificationDisplay />
<ConfirmationDisplay />
<PeekScreenDisplay />
</CustomThemeWrapper>
{/if}
{#if showDevTools}
<DevTools />
{/if}
</div>
</div>
<!-- Preview and dev tools utilities -->
{#if $appStore.isDevApp}
<SelectionIndicator />
{/if}
{#if $builderStore.inBuilder || $devToolsStore.allowSelection}
<HoverIndicator />
{/if}
{#if $builderStore.inBuilder}
<DNDHandler />
{/if}
</div>
</QueryParamsProvider>
</RowSelectionProvider> </RowSelectionProvider>
</StateBindingsProvider> </StateBindingsProvider>
</UserBindingsProvider> </UserBindingsProvider>

View file

@ -1,21 +1,36 @@
<script> <script>
import { screenStore, routeStore } from "stores" import { screenStore, routeStore, builderStore } from "stores"
import Component from "./Component.svelte" import Component from "./Component.svelte"
import Provider from "./context/Provider.svelte" import Provider from "./context/Provider.svelte"
import { onMount } from "svelte" import { onMount, getContext } from "svelte"
import { enrichButtonActions } from "utils/buttonActions.js"
export let params = {}
const context = getContext("context")
// Keep route params up to date // Keep route params up to date
export let params = {}
$: routeStore.actions.setRouteParams(params || {}) $: routeStore.actions.setRouteParams(params || {})
// Get the screen definition for the current route // Get the screen definition for the current route
$: screenDefinition = $screenStore.activeScreen?.props $: screenDefinition = $screenStore.activeScreen?.props
// Mark the router as loaded whenever the screen mounts
onMount(() => { onMount(() => {
// Mark the router as loaded whenever the screen mounts
if (!$routeStore.routerLoaded) { if (!$routeStore.routerLoaded) {
routeStore.actions.setRouterLoaded() routeStore.actions.setRouterLoaded()
} }
// Enrich and execute and on load actions.
// We manually construct the full context here as this component is the
// one that provides the url context, so it is not available in $context yet
if ($screenStore.activeScreen?.onLoad && !$builderStore.inBuilder) {
const actions = enrichButtonActions($screenStore.activeScreen.onLoad, {
...$context,
url: params,
})
actions()
}
}) })
</script> </script>

View file

@ -0,0 +1,8 @@
<script>
import Provider from "./Provider.svelte"
import { routeStore } from "stores"
</script>
<Provider key="query" data={$routeStore.queryParams}>
<slot />
</Provider>