Added toggle-all functionality to the formblock and update context to the ComponentSettingsSection

This commit is contained in:
Dean 2023-11-08 16:59:00 +00:00
parent 555f8fc22c
commit f35daee07f
6 changed files with 84 additions and 233 deletions

View File

@ -11,6 +11,7 @@
export let componentBindings export let componentBindings
export let bindings export let bindings
export let parseSettings export let parseSettings
export let disabled
const draggable = getContext("draggable") const draggable = getContext("draggable")
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -90,6 +91,7 @@
open = true open = true
} }
}} }}
{disabled}
/> />
<Popover <Popover

View File

@ -1,26 +0,0 @@
<script>
import { DrawerContent, Drawer, Button, Icon } from "@budibase/bbui"
import ValidationDrawer from "components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte"
export let column
export let type
let drawer
</script>
<Icon name="Settings" hoverable size="S" on:click={drawer.show} />
<Drawer bind:this={drawer} title="Field Validation">
<svelte:fragment slot="description">
"{column.name}" field validation
</svelte:fragment>
<Button cta slot="buttons" on:click={drawer.hide}>Save</Button>
<DrawerContent slot="body">
<div class="container">
<ValidationDrawer
slot="body"
bind:rules={column.validation}
fieldName={column.name}
{type}
/>
</div>
</DrawerContent>
</Drawer>

View File

@ -1,202 +0,0 @@
<script>
import {
Button,
Icon,
DrawerContent,
Layout,
Select,
Label,
Body,
Input,
} from "@budibase/bbui"
import { flip } from "svelte/animate"
import { dndzone } from "svelte-dnd-action"
import { generate } from "shortid"
import CellEditor from "./CellEditor.svelte"
export let columns = []
export let options = []
export let schema = {}
const flipDurationMs = 150
let dragDisabled = true
$: unselectedColumns = getUnselectedColumns(options, columns)
$: columns.forEach(column => {
if (!column.id) {
column.id = generate()
}
})
const getUnselectedColumns = (allColumns, selectedColumns) => {
let optionsObj = {}
allColumns.forEach(option => {
optionsObj[option] = true
})
selectedColumns?.forEach(column => {
delete optionsObj[column.name]
})
return Object.keys(optionsObj)
}
const getRemainingColumnOptions = selectedColumn => {
if (!selectedColumn || unselectedColumns.includes(selectedColumn)) {
return unselectedColumns
}
return [selectedColumn, ...unselectedColumns]
}
const addColumn = () => {
columns = [...columns, {}]
}
const removeColumn = id => {
columns = columns.filter(column => column.id !== id)
}
const updateColumnOrder = e => {
columns = e.detail.items
}
const handleFinalize = e => {
updateColumnOrder(e)
dragDisabled = true
}
const addAllColumns = () => {
let newColumns = columns || []
options.forEach(field => {
const fieldSchema = schema[field]
const hasCol = columns && columns.findIndex(x => x.name === field) !== -1
if (!fieldSchema?.autocolumn && !hasCol) {
newColumns.push({
name: field,
displayName: field,
})
}
})
columns = newColumns
}
const reset = () => {
columns = []
}
const getFieldType = column => {
return `validation/${schema[column.name]?.type}`
}
</script>
<DrawerContent>
<div class="container">
<Layout noPadding gap="S">
{#if columns?.length}
<Layout noPadding gap="XS">
<div class="column">
<div />
<Label size="L">Column</Label>
<Label size="L">Label</Label>
<div />
<div />
</div>
<div
class="columns"
use:dndzone={{
items: columns,
flipDurationMs,
dropTargetStyle: { outline: "none" },
dragDisabled,
}}
on:finalize={handleFinalize}
on:consider={updateColumnOrder}
>
{#each columns as column (column.id)}
<div class="column" animate:flip={{ duration: flipDurationMs }}>
<div
class="handle"
aria-label="drag-handle"
style={dragDisabled ? "cursor: grab" : "cursor: grabbing"}
on:mousedown={() => (dragDisabled = false)}
>
<Icon name="DragHandle" size="XL" />
</div>
<Select
bind:value={column.name}
placeholder="Column"
options={getRemainingColumnOptions(column.name)}
on:change={e => (column.displayName = e.detail)}
/>
<Input bind:value={column.displayName} placeholder="Label" />
<CellEditor type={getFieldType(column)} bind:column />
<Icon
name="Close"
hoverable
size="S"
on:click={() => removeColumn(column.id)}
disabled={columns.length === 1}
/>
</div>
{/each}
</div>
</Layout>
{:else}
<div class="column">
<div class="wide">
<Body size="S">Add columns to be included in your form below.</Body>
</div>
</div>
{/if}
<div class="column">
<div class="buttons wide">
<Button secondary icon="Add" on:click={addColumn}>Add column</Button>
<Button secondary quiet on:click={addAllColumns}>
Add all columns
</Button>
{#if columns?.length}
<Button secondary quiet on:click={reset}>Reset columns</Button>
{/if}
</div>
</div>
</Layout>
</div>
</DrawerContent>
<style>
.container {
width: 100%;
max-width: 600px;
margin: 0 auto;
}
.columns {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-m);
}
.column {
gap: var(--spacing-l);
display: grid;
grid-template-columns: 20px 1fr 1fr 16px 16px;
align-items: center;
border-radius: var(--border-radius-s);
transition: background-color ease-in-out 130ms;
}
.column:hover {
background-color: var(--spectrum-global-color-gray-100);
}
.handle {
display: grid;
place-items: center;
}
.wide {
grid-column: 2 / -1;
}
.buttons {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: var(--spacing-m);
}
</style>

View File

@ -1,4 +1,5 @@
<script> <script>
import { Toggle } from "@budibase/bbui"
import { cloneDeep, isEqual } from "lodash/fp" import { cloneDeep, isEqual } from "lodash/fp"
import { import {
getDatasourceForProvider, getDatasourceForProvider,
@ -8,7 +9,7 @@
} from "builderStore/dataBinding" } from "builderStore/dataBinding"
import { currentAsset } from "builderStore" import { currentAsset } from "builderStore"
import DraggableList from "../DraggableList/DraggableList.svelte" import DraggableList from "../DraggableList/DraggableList.svelte"
import { createEventDispatcher } from "svelte" import { createEventDispatcher, getContext } from "svelte"
import { store, selectedScreen } from "builderStore" import { store, selectedScreen } from "builderStore"
import FieldSetting from "./FieldSetting.svelte" import FieldSetting from "./FieldSetting.svelte"
import { convertOldFieldFormat, getComponentForField } from "./utils" import { convertOldFieldFormat, getComponentForField } from "./utils"
@ -16,6 +17,8 @@
export let componentInstance export let componentInstance
export let value export let value
const updates = getContext("settings")
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let sanitisedFields let sanitisedFields
let fieldList let fieldList
@ -25,6 +28,9 @@
let sanitisedValue let sanitisedValue
let unconfigured let unconfigured
let selectAll = true
let updating = false
$: bindings = getBindableProperties($selectedScreen, componentInstance._id) $: bindings = getBindableProperties($selectedScreen, componentInstance._id)
$: actionType = componentInstance.actionType $: actionType = componentInstance.actionType
let componentBindings = [] let componentBindings = []
@ -43,6 +49,10 @@
cachedValue = cloneDeep(value) cachedValue = cloneDeep(value)
} }
$: if (typeof $updates.resp == "string") {
updating = false
}
const updateState = value => { const updateState = value => {
schema = getSchema($currentAsset, datasource) schema = getSchema($currentAsset, datasource)
options = Object.keys(schema || {}) options = Object.keys(schema || {})
@ -145,16 +155,37 @@
dispatch("change", getValidColumns(parentFieldsUpdated, options)) dispatch("change", getValidColumns(parentFieldsUpdated, options))
} }
const listUpdated = e => { const listUpdated = columns => {
const parsedColumns = getValidColumns(e.detail, options) const parsedColumns = getValidColumns(columns, options)
dispatch("change", parsedColumns) dispatch("change", parsedColumns)
} }
const toggleAll = update => {
updating = true
listUpdated(update)
}
</script> </script>
<div class="field-configuration"> <div class="field-configuration">
<div class="toggle-all">
<span />
<Toggle
on:change={() => {
let update = fieldList.map(field => ({
...field,
active: selectAll,
}))
toggleAll(update)
}}
text=""
bind:value={selectAll}
thin
disabled={updating}
/>
</div>
{#if fieldList?.length} {#if fieldList?.length}
<DraggableList <DraggableList
on:change={listUpdated} on:change={e => listUpdated(e.detail)}
on:itemChange={processItemUpdate} on:itemChange={processItemUpdate}
items={fieldList} items={fieldList}
listItemKey={"_id"} listItemKey={"_id"}
@ -162,7 +193,9 @@
listTypeProps={{ listTypeProps={{
componentBindings, componentBindings,
bindings, bindings,
updating,
}} }}
draggable={!updating}
/> />
{/if} {/if}
</div> </div>
@ -171,4 +204,21 @@
.field-configuration :global(.spectrum-ActionButton) { .field-configuration :global(.spectrum-ActionButton) {
width: 100%; width: 100%;
} }
.toggle-all {
display: flex;
justify-content: space-between;
}
.toggle-all :global(.spectrum-Switch) {
margin-right: 0px;
padding-right: calc(var(--spacing-s) - 1px);
min-height: unset;
}
.toggle-all :global(.spectrum-Switch .spectrum-Switch-switch) {
margin-top: 0px;
}
.toggle-all span {
color: var(--spectrum-global-color-gray-700);
font-size: 12px;
margin-left: calc(var(--spacing-s) - 1px);
}
</style> </style>

View File

@ -11,6 +11,7 @@
export let componentBindings export let componentBindings
export let bindings export let bindings
export let anchor export let anchor
export let updating //or disabled
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const onToggle = item => { const onToggle = item => {
@ -49,6 +50,7 @@
{bindings} {bindings}
{parseSettings} {parseSettings}
on:change on:change
disabled={updating}
> >
<div slot="header" class="type-icon"> <div slot="header" class="type-icon">
<Icon name={componentDef.icon} /> <Icon name={componentDef.icon} />
@ -58,7 +60,13 @@
<div class="field-label">{readableText}</div> <div class="field-label">{readableText}</div>
</div> </div>
<div class="list-item-right"> <div class="list-item-right">
<Toggle on:change={onToggle(item)} text="" value={item.active} thin /> <Toggle
on:change={onToggle(item)}
text=""
value={item.active}
thin
disabled={updating}
/>
</div> </div>
</div> </div>

View File

@ -8,6 +8,8 @@
import { getComponentForSetting } from "components/design/settings/componentSettings" import { getComponentForSetting } from "components/design/settings/componentSettings"
import InfoDisplay from "./InfoDisplay.svelte" import InfoDisplay from "./InfoDisplay.svelte"
import analytics, { Events } from "analytics" import analytics, { Events } from "analytics"
import { setContext } from "svelte"
import { writable } from "svelte/store"
export let componentDefinition export let componentDefinition
export let componentInstance export let componentInstance
@ -19,6 +21,21 @@
export let includeHidden = false export let includeHidden = false
export let tag export let tag
let status = writable({
status: null,
lastUpdate: null,
})
const updateStatus = resp => {
status.update(state => ({
...state,
resp,
lastUpdate: new Date().getTime(),
}))
}
setContext("settings", status)
$: sections = getSections( $: sections = getSections(
componentInstance, componentInstance,
componentDefinition, componentDefinition,
@ -76,6 +93,7 @@
} else { } else {
await store.actions.components.updateSetting(setting.key, value) await store.actions.components.updateSetting(setting.key, value)
} }
updateStatus("success")
// Send event if required // Send event if required
if (setting.sendEvents) { if (setting.sendEvents) {
analytics.captureEvent(Events.COMPONENT_UPDATED, { analytics.captureEvent(Events.COMPONENT_UPDATED, {
@ -85,6 +103,7 @@
}) })
} }
} catch (error) { } catch (error) {
updateStatus("failed")
notifications.error("Error updating component prop") notifications.error("Error updating component prop")
} }
} }