Merge pull request #12200 from Budibase/form-block-field-layout

Form block field layout
This commit is contained in:
Martin McKeaveney 2023-11-06 18:18:40 +00:00 committed by GitHub
commit 3bbf6a2868
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 544 additions and 100 deletions

View File

@ -386,7 +386,7 @@
}
.compact .placeholder,
.compact img {
margin: 10px 16px;
margin: 8px 16px;
}
.compact img {
height: 90px;
@ -456,6 +456,12 @@
color: var(--red);
}
.spectrum-Dropzone {
height: 220px;
}
.compact .spectrum-Dropzone {
height: 40px;
}
.spectrum-Dropzone.disabled {
pointer-events: none;
background-color: var(--spectrum-global-color-gray-200);
@ -463,10 +469,6 @@
.disabled .spectrum-Heading--sizeL {
color: var(--spectrum-alias-text-color-disabled);
}
.compact .spectrum-Dropzone {
padding-top: 8px;
padding-bottom: 8px;
}
.compact .spectrum-IllustratedMessage-description {
margin: 0;
}
@ -477,7 +479,6 @@
flex-wrap: wrap;
justify-content: center;
}
.tag {
margin-top: 8px;
}

View File

@ -20,7 +20,7 @@
let open = false
// Auto hide the component when another item is selected
$: if (open && $draggable.selected != componentInstance._id) {
$: if (open && $draggable.selected !== componentInstance._id) {
popover.hide()
}
@ -100,13 +100,13 @@
}}
on:close={() => {
open = false
if ($draggable.selected == componentInstance._id) {
if ($draggable.selected === componentInstance._id) {
$draggable.actions.select()
}
}}
{anchor}
align="left-outside"
showPopover={drawers.length == 0}
showPopover={drawers.length === 0}
clickOutsideOverride={drawers.length > 0}
maxHeight={600}
handlePostionUpdate={customPositionHandler}
@ -115,6 +115,7 @@
<Layout noPadding noGap>
<slot name="header" />
<ComponentSettingsSection
includeHidden
{componentInstance}
componentDefinition={parsedComponentDef}
isScreen={false}

View File

@ -16,16 +16,18 @@
export let isScreen = false
export let onUpdateSetting
export let showSectionTitle = true
export let includeHidden = false
export let tag
$: sections = getSections(
componentInstance,
componentDefinition,
isScreen,
tag
tag,
includeHidden
)
const getSections = (instance, definition, isScreen, tag) => {
const getSections = (instance, definition, isScreen, tag, includeHidden) => {
const settings = definition?.settings ?? []
const generalSettings = settings.filter(
setting => !setting.section && setting.tag === tag
@ -52,7 +54,12 @@
return
}
section.settings.forEach(setting => {
setting.visible = canRenderControl(instance, setting, isScreen)
setting.visible = canRenderControl(
instance,
setting,
isScreen,
includeHidden
)
})
section.visible =
section.name === "General" ||
@ -122,16 +129,20 @@
})
}
const canRenderControl = (instance, setting, isScreen) => {
const canRenderControl = (instance, setting, isScreen, includeHidden) => {
// Prevent rendering on click setting for screens
if (setting?.type === "event" && isScreen) {
return false
}
// Check we have a component to render for this setting
const control = getComponentForSetting(setting)
if (!control) {
return false
}
// Check if setting is hidden
if (setting.hidden && !includeHidden) {
return false
}
return shouldDisplay(instance, setting)
}
</script>

View File

@ -2776,6 +2776,35 @@
"barTitle": "Justify text"
}
]
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -2833,6 +2862,35 @@
"type": "validation/number",
"label": "Validation",
"key": "validation"
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -2885,6 +2943,35 @@
"label": "Disabled",
"key": "disabled",
"defaultValue": false
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -2942,6 +3029,35 @@
"type": "validation/string",
"label": "Validation",
"key": "validation"
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -3110,6 +3226,35 @@
"type": "validation/string",
"label": "Validation",
"key": "validation"
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -3272,6 +3417,35 @@
"type": "validation/array",
"label": "Validation",
"key": "validation"
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -3352,6 +3526,35 @@
"type": "validation/boolean",
"label": "Validation",
"key": "validation"
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -3431,6 +3634,35 @@
"type": "validation/string",
"label": "Validation",
"key": "validation"
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -3512,6 +3744,35 @@
"type": "validation/datetime",
"label": "Validation",
"key": "validation"
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -3629,6 +3890,35 @@
"type": "validation/string",
"label": "Validation",
"key": "validation"
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -3805,6 +4095,35 @@
"type": "validation/attachment",
"label": "Validation",
"key": "validation"
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -3873,6 +4192,35 @@
"label": "Disabled",
"key": "disabled",
"defaultValue": false
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -3925,6 +4273,35 @@
"label": "Disabled",
"key": "disabled",
"defaultValue": false
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
},
@ -5606,23 +5983,6 @@
}
]
},
{
"tag": "style",
"type": "select",
"label": "Align labels",
"key": "labelPosition",
"defaultValue": "left",
"options": [
{
"label": "Left",
"value": "left"
},
{
"label": "Above",
"value": "above"
}
]
},
{
"tag": "style",
"type": "select",
@ -5937,6 +6297,35 @@
"label": "Disabled",
"key": "disabled",
"defaultValue": false
},
{
"type": "select",
"label": "Layout",
"key": "span",
"defaultValue": 6,
"hidden": true,
"showInBar": true,
"barStyle": "buttons",
"options": [
{
"label": "1 column",
"value": 6,
"barIcon": "Stop",
"barTitle": "1 column"
},
{
"label": "2 columns",
"value": 3,
"barIcon": "ColumnTwoA",
"barTitle": "2 columns"
},
{
"label": "3 columns",
"value": 2,
"barIcon": "ViewColumn",
"barTitle": "3 columns"
}
]
}
]
}

View File

@ -26,15 +26,15 @@
$: parentId = $component?.id
$: inBuilder = $builderStore.inBuilder
$: instance = {
...props,
_component: getComponent(type),
_id: id,
_instanceName: getInstanceName(name, type),
_containsSlot: containsSlot,
_styles: {
...styles,
normal: styles?.normal || {},
},
_containsSlot: containsSlot,
...props,
}
// Register this block component if we're inside the builder so it can be

View File

@ -140,6 +140,7 @@
interactive &&
!isLayout &&
!isRoot &&
!isBlock &&
definition?.draggable !== false
$: droppable = interactive
$: builderHidden =
@ -194,6 +195,7 @@
interactive,
draggable,
editable,
isBlock,
},
empty: emptyState,
selected,

View File

@ -10,7 +10,6 @@
export let size
export let disabled
export let fields
export let labelPosition
export let title
export let description
export let showDeleteButton
@ -97,7 +96,6 @@
size,
disabled,
fields: fieldsOrDefault,
labelPosition,
title,
description,
saveButtonLabel: saveLabel,

View File

@ -2,6 +2,7 @@
import BlockComponent from "components/BlockComponent.svelte"
import Placeholder from "components/app/Placeholder.svelte"
import { makePropSafe as safe } from "@budibase/string-templates"
import { getContext } from "svelte"
export let dataSource
export let actionUrl
@ -9,7 +10,6 @@
export let size
export let disabled
export let fields
export let labelPosition
export let title
export let description
export let saveButtonLabel
@ -33,6 +33,7 @@
barcodeqr: "codescanner",
bb_reference: "bbreferencefield",
}
const context = getContext("context")
let formId
@ -226,16 +227,20 @@
<BlockComponent type="text" props={{ text: description }} order={1} />
{/if}
{#key fields}
<BlockComponent type="fieldgroup" props={{ labelPosition }} order={1}>
{#each fields as field, idx}
{#if getComponentForField(field) && field.active}
<BlockComponent
type={getComponentForField(field)}
props={getPropsForField(field)}
order={idx}
/>
{/if}
{/each}
<BlockComponent type="container">
<div class="form-block fields" class:mobile={$context.device.mobile}>
{#each fields as field, idx}
{#if getComponentForField(field) && field.active}
<BlockComponent
type={getComponentForField(field)}
props={getPropsForField(field)}
order={idx}
interactive
name={field?.field}
/>
{/if}
{/each}
</div>
</BlockComponent>
{/key}
</BlockComponent>
@ -245,3 +250,14 @@
text="Choose your table and add some fields to your form to get started"
/>
{/if}
<style>
.fields {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 8px 16px;
}
.fields.mobile :global(.spectrum-Form-item) {
grid-column: span 6 !important;
}
</style>

View File

@ -11,6 +11,7 @@
export let extensions
export let onChange
export let maximum = undefined
export let span
let fieldState
let fieldApi
@ -72,32 +73,25 @@
{field}
{disabled}
{validation}
{span}
type="attachment"
bind:fieldState
bind:fieldApi
defaultValue={[]}
>
<div class="minHeightWrapper">
{#if fieldState}
<CoreDropzone
value={fieldState.value}
disabled={fieldState.disabled}
error={fieldState.error}
on:change={handleChange}
{processFiles}
{deleteAttachments}
{handleFileTooLarge}
{handleTooManyFiles}
{maximum}
{extensions}
{compact}
/>
{/if}
</div>
{#if fieldState}
<CoreDropzone
value={fieldState.value}
disabled={fieldState.disabled}
error={fieldState.error}
on:change={handleChange}
{processFiles}
{deleteAttachments}
{handleFileTooLarge}
{handleTooManyFiles}
{maximum}
{extensions}
{compact}
/>
{/if}
</Field>
<style>
.minHeightWrapper {
min-height: 80px;
}
</style>

View File

@ -13,6 +13,7 @@
export let validation
export let defaultValue
export let onChange
export let span
let fieldState
let fieldApi
@ -31,6 +32,7 @@
{disabled}
{validation}
{defaultValue}
{span}
type="datetime"
bind:fieldState
bind:fieldApi

View File

@ -1,6 +1,5 @@
<script>
import Placeholder from "../Placeholder.svelte"
import FieldGroupFallback from "./FieldGroupFallback.svelte"
import { getContext, onDestroy } from "svelte"
export let label
@ -12,6 +11,7 @@
export let type
export let disabled = false
export let validation
export let span = 6
// Get contexts
const formContext = getContext("form")
@ -62,40 +62,58 @@
})
</script>
<FieldGroupFallback>
<div class="spectrum-Form-item" use:styleable={$component.styles}>
{#key $component.editing}
<label
bind:this={labelNode}
contenteditable={$component.editing}
on:blur={$component.editing ? updateLabel : null}
class:hidden={!label}
for={fieldState?.fieldId}
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelClass}`}
>
{label || " "}
</label>
{/key}
<div class="spectrum-Form-itemField">
{#if !formContext}
<Placeholder text="Form components need to be wrapped in a form" />
{:else if !fieldState}
<Placeholder />
{:else if schemaType && schemaType !== type && !["options", "longform"].includes(type)}
<Placeholder
text="This Field setting is the wrong data type for this component"
/>
{:else}
<slot />
{#if fieldState.error}
<div class="error">{fieldState.error}</div>
{/if}
<div
class="spectrum-Form-item"
class:span-2={span === 2}
class:span-3={span === 3}
class:span-6={span === 6 || !span}
use:styleable={$component.styles}
class:above={labelPos === "above"}
>
{#key $component.editing}
<label
bind:this={labelNode}
contenteditable={$component.editing}
on:blur={$component.editing ? updateLabel : null}
class:hidden={!label}
for={fieldState?.fieldId}
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelClass}`}
>
{label || " "}
</label>
{/key}
<div class="spectrum-Form-itemField">
{#if !formContext}
<Placeholder text="Form components need to be wrapped in a form" />
{:else if !fieldState}
<Placeholder />
{:else if schemaType && schemaType !== type && !["options", "longform"].includes(type)}
<Placeholder
text="This Field setting is the wrong data type for this component"
/>
{:else}
<slot />
{#if fieldState.error}
<div class="error">{fieldState.error}</div>
{/if}
</div>
{/if}
</div>
</FieldGroupFallback>
</div>
<style>
:global(.form-block .spectrum-Form-item.span-2) {
grid-column: span 2;
}
:global(.form-block .spectrum-Form-item.span-3) {
grid-column: span 3;
}
:global(.form-block .spectrum-Form-item.span-6) {
grid-column: span 6;
}
.spectrum-Form-item.above {
display: flex;
flex-direction: column;
}
label {
white-space: nowrap;
}

View File

@ -17,6 +17,7 @@
export let onChange
export let optionsType = "select"
export let direction = "vertical"
export let span
let fieldState
let fieldApi
@ -56,6 +57,7 @@
{label}
{disabled}
{validation}
{span}
defaultValue={expandedDefaultValue}
type="array"
bind:fieldState

View File

@ -18,6 +18,7 @@
export let direction = "vertical"
export let onChange
export let sort = true
export let span
let fieldState
let fieldApi
@ -47,6 +48,7 @@
{disabled}
{validation}
{defaultValue}
{span}
type="options"
bind:fieldState
bind:fieldApi

View File

@ -18,6 +18,7 @@
export let filter
export let datasourceType = "table"
export let primaryDisplay
export let span
let fieldState
let fieldApi
@ -188,6 +189,7 @@
{validation}
defaultValue={expandedDefaultValue}
{type}
{span}
bind:fieldState
bind:fieldApi
bind:fieldSchema

View File

@ -11,6 +11,7 @@
export let defaultValue = ""
export let align
export let onChange
export let span
let fieldState
let fieldApi
@ -29,6 +30,7 @@
{disabled}
{validation}
{defaultValue}
{span}
type={type === "number" ? "number" : "string"}
bind:fieldState
bind:fieldApi

View File

@ -40,6 +40,7 @@ export const styleable = (node, styles = {}) => {
const componentId = newStyles.id
const customStyles = newStyles.custom || ""
const { isBlock } = newStyles
const normalStyles = { ...baseStyles, ...newStyles.normal }
const hoverStyles = {
...normalStyles,
@ -76,6 +77,9 @@ export const styleable = (node, styles = {}) => {
// Handler to start editing a component (if applicable) when double
// clicking in the builder preview
editComponent = event => {
if (isBlock) {
return
}
if (newStyles.interactive && newStyles.editable) {
builderStore.actions.setEditMode(true)
}