Allow custom CSS to be bindable and allow overriding via conditional UI

This commit is contained in:
Andrew Kingston 2022-09-05 16:35:25 +01:00
parent eb8171dde1
commit 178dc26c16
3 changed files with 51 additions and 34 deletions

View File

@ -57,8 +57,8 @@
let dragDisabled = true let dragDisabled = true
$: settings = getComponentSettings($selectedComponent?._component)?.concat({ $: settings = getComponentSettings($selectedComponent?._component)?.concat({
label: "CSS", label: "Custom CSS",
key: "css", key: "_css",
type: "text", type: "text",
}) })
$: settingOptions = settings.map(setting => ({ $: settingOptions = settings.map(setting => ({

View File

@ -1,30 +1,41 @@
<script> <script>
import { import {
TextArea,
DetailSummary, DetailSummary,
ActionButton, ActionButton,
Drawer, Drawer,
DrawerContent,
Layout,
Body,
Button, Button,
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import { store } from "builderStore" import { selectedScreen, store } from "builderStore"
import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte"
import {
getBindableProperties,
readableToRuntimeBinding,
runtimeToReadableBinding,
} from "builderStore/dataBinding"
export let componentInstance export let componentInstance
let tempValue let tempValue
let drawer let drawer
$: bindings = getBindableProperties(
$selectedScreen,
$store.selectedComponentId
)
const openDrawer = () => { const openDrawer = () => {
tempValue = componentInstance?._styles?.custom tempValue = runtimeToReadableBinding(
bindings,
componentInstance?._styles?.custom
)
drawer.show() drawer.show()
} }
const save = async () => { const save = async () => {
try { try {
await store.actions.components.updateCustomStyle(tempValue) const value = readableToRuntimeBinding(bindings, tempValue)
await store.actions.components.updateCustomStyle(value)
} catch (error) { } catch (error) {
notifications.error("Error updating custom style") notifications.error("Error updating custom style")
} }
@ -42,26 +53,17 @@
</DetailSummary> </DetailSummary>
{#key componentInstance?._id} {#key componentInstance?._id}
<Drawer bind:this={drawer} title="Custom CSS"> <Drawer bind:this={drawer} title="Custom CSS">
<svelte:fragment slot="description">
Custom CSS overrides all other component styles.
</svelte:fragment>
<Button cta slot="buttons" on:click={save}>Save</Button> <Button cta slot="buttons" on:click={save}>Save</Button>
<DrawerContent slot="body"> <svelte:component
<div class="content"> this={ClientBindingPanel}
<Layout gap="S" noPadding> slot="body"
<Body size="S">Custom CSS overrides all other component styles.</Body> value={tempValue}
<TextArea bind:value={tempValue} placeholder="Enter some CSS..." /> on:change={event => (tempValue = event.detail)}
</Layout> allowJS
</div> {bindings}
</DrawerContent> />
</Drawer> </Drawer>
{/key} {/key}
<style>
.content {
max-width: 800px;
margin: 0 auto;
}
.content :global(textarea) {
font-family: monospace;
min-height: 240px !important;
font-size: var(--font-size-s);
}
</style>

View File

@ -142,6 +142,10 @@
// Determine and apply settings to the component // Determine and apply settings to the component
$: applySettings(staticSettings, enrichedSettings, conditionalSettings) $: applySettings(staticSettings, enrichedSettings, conditionalSettings)
// Determine custom css.
// Broken out as a separate variable to minimize reactivity updates.
$: customCSS = cachedSettings?._css
// Scroll the selected element into view // Scroll the selected element into view
$: selected && scrollIntoView() $: selected && scrollIntoView()
@ -151,6 +155,7 @@
children: children.length, children: children.length,
styles: { styles: {
...instance._styles, ...instance._styles,
custom: customCSS,
id, id,
empty: emptyState, empty: emptyState,
interactive, interactive,
@ -259,14 +264,18 @@
// Get raw settings // Get raw settings
let settings = {} let settings = {}
Object.entries(instance) Object.entries(instance)
.filter(([name]) => name === "_conditions" || !name.startsWith("_")) .filter(([name]) => !name.startsWith("_"))
.forEach(([key, value]) => { .forEach(([key, value]) => {
settings[key] = value settings[key] = value
}) })
// Derive static, dynamic and nested settings if the instance changed
let newStaticSettings = { ...settings } let newStaticSettings = { ...settings }
let newDynamicSettings = { ...settings } let newDynamicSettings = { ...settings }
// Attach some internal properties
newDynamicSettings["_conditions"] = instance._conditions
newDynamicSettings["_css"] = instance._styles?.custom
// Derive static, dynamic and nested settings if the instance changed
settingsDefinition?.forEach(setting => { settingsDefinition?.forEach(setting => {
if (setting.nested) { if (setting.nested) {
delete newDynamicSettings[setting.key] delete newDynamicSettings[setting.key]
@ -372,6 +381,10 @@
initialSettings = cachedSettings initialSettings = cachedSettings
} else { } else {
Object.keys(allSettings).forEach(key => { Object.keys(allSettings).forEach(key => {
if (key === "_css") {
console.log(enrichedSettings._css)
}
const same = propsAreSame(allSettings[key], cachedSettings[key]) const same = propsAreSame(allSettings[key], cachedSettings[key])
if (!same) { if (!same) {
// Updated cachedSettings (which is assigned by reference to // Updated cachedSettings (which is assigned by reference to
@ -379,8 +392,10 @@
// initial props are up to date. By setting it this way rather than // initial props are up to date. By setting it this way rather than
// setting it on initialSettings directly, we avoid a double render. // setting it on initialSettings directly, we avoid a double render.
cachedSettings[key] = allSettings[key] cachedSettings[key] = allSettings[key]
if (key === "css") {
instance._styles.custom = cachedSettings[key] // Don't update components for internal properties
if (key.startsWith("_")) {
return
} }
if (ref?.$$set) { if (ref?.$$set) {