Add new form field component for JSON fields, JSON validation and update autoscreen generation
This commit is contained in:
parent
f2a2f5ac70
commit
1e38628a4b
|
@ -137,6 +137,7 @@ const fieldTypeToComponentMap = {
|
|||
datetime: "datetimefield",
|
||||
attachment: "attachmentfield",
|
||||
link: "relationshipfield",
|
||||
json: "jsonfield",
|
||||
}
|
||||
|
||||
export function makeDatasourceFormComponents(datasource) {
|
||||
|
|
|
@ -43,7 +43,8 @@
|
|||
"attachmentfield",
|
||||
"relationshipfield",
|
||||
"daterangepicker",
|
||||
"multifieldselect"
|
||||
"multifieldselect",
|
||||
"jsonfield"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -45,6 +45,7 @@ const componentMap = {
|
|||
"field/attachment": FormFieldSelect,
|
||||
"field/link": FormFieldSelect,
|
||||
"field/array": FormFieldSelect,
|
||||
"field/json": FormFieldSelect,
|
||||
// Some validation types are the same as others, so not all types are
|
||||
// explicitly listed here. e.g. options uses string validation
|
||||
"validation/string": ValidationEditor,
|
||||
|
|
|
@ -2418,6 +2418,40 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"jsonfield": {
|
||||
"name": "Key/Value Field",
|
||||
"icon": "Brackets",
|
||||
"styles": ["size"],
|
||||
"editable": true,
|
||||
"settings": [
|
||||
{
|
||||
"type": "field/json",
|
||||
"label": "Field",
|
||||
"key": "field"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"label": "Label",
|
||||
"key": "label"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"label": "Placeholder",
|
||||
"key": "placeholder"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"label": "Default value",
|
||||
"key": "defaultValue"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"label": "Disabled",
|
||||
"key": "disabled",
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"dataprovider": {
|
||||
"name": "Data Provider",
|
||||
"info": "Pagination is only available for data stored in tables.",
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<script>
|
||||
import { CoreTextArea } from "@budibase/bbui"
|
||||
import Field from "./Field.svelte"
|
||||
import { getContext } from "svelte"
|
||||
|
||||
export let field
|
||||
export let label
|
||||
export let placeholder
|
||||
export let disabled = false
|
||||
export let defaultValue = ""
|
||||
|
||||
const component = getContext("component")
|
||||
const validation = [
|
||||
{
|
||||
constraint: "json",
|
||||
type: "json",
|
||||
error: "JSON syntax is invalid",
|
||||
},
|
||||
]
|
||||
let fieldState
|
||||
let fieldApi
|
||||
|
||||
$: height = $component.styles?.normal?.height || "124px"
|
||||
|
||||
const serialiseValue = value => {
|
||||
return JSON.stringify(value || undefined, null, 4) || ""
|
||||
}
|
||||
|
||||
const parseValue = value => {
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
} catch (error) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Field
|
||||
{label}
|
||||
{field}
|
||||
{disabled}
|
||||
{validation}
|
||||
{defaultValue}
|
||||
type="json"
|
||||
bind:fieldState
|
||||
bind:fieldApi
|
||||
>
|
||||
{#if fieldState}
|
||||
<div style="--height: {height};">
|
||||
<CoreTextArea
|
||||
value={serialiseValue(fieldState.value)}
|
||||
on:change={e => fieldApi.setValue(parseValue(e.detail))}
|
||||
disabled={fieldState.disabled}
|
||||
error={fieldState.error}
|
||||
id={fieldState.fieldId}
|
||||
{placeholder}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</Field>
|
||||
|
||||
<style>
|
||||
:global(.spectrum-Form-itemField .spectrum-Textfield--multiline) {
|
||||
min-height: calc(var(--height) - 24px);
|
||||
}
|
||||
:global(.spectrum-Form--labelsAbove
|
||||
.spectrum-Form-itemField
|
||||
.spectrum-Textfield--multiline) {
|
||||
min-height: calc(var(--height) - 24px);
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import { CoreTextArea } from "@budibase/bbui"
|
||||
import Field from "./Field.svelte"
|
||||
import { getContext } from "svelte"
|
||||
|
||||
export let field
|
||||
export let label
|
||||
|
@ -11,6 +12,9 @@
|
|||
|
||||
let fieldState
|
||||
let fieldApi
|
||||
|
||||
const component = getContext("component")
|
||||
$: height = $component.styles?.normal?.height || "124px"
|
||||
</script>
|
||||
|
||||
<Field
|
||||
|
@ -24,13 +28,26 @@
|
|||
bind:fieldApi
|
||||
>
|
||||
{#if fieldState}
|
||||
<CoreTextArea
|
||||
value={fieldState.value}
|
||||
on:change={e => fieldApi.setValue(e.detail)}
|
||||
disabled={fieldState.disabled}
|
||||
error={fieldState.error}
|
||||
id={fieldState.fieldId}
|
||||
{placeholder}
|
||||
/>
|
||||
<div style="--height: {height};">
|
||||
<CoreTextArea
|
||||
value={fieldState.value}
|
||||
on:change={e => fieldApi.setValue(e.detail)}
|
||||
disabled={fieldState.disabled}
|
||||
error={fieldState.error}
|
||||
id={fieldState.fieldId}
|
||||
{placeholder}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</Field>
|
||||
|
||||
<style>
|
||||
:global(.spectrum-Form-itemField .spectrum-Textfield--multiline) {
|
||||
min-height: calc(var(--height) - 24px);
|
||||
}
|
||||
:global(.spectrum-Form--labelsAbove
|
||||
.spectrum-Form-itemField
|
||||
.spectrum-Textfield--multiline) {
|
||||
min-height: calc(var(--height) - 24px);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -11,3 +11,4 @@ export { default as attachmentfield } from "./AttachmentField.svelte"
|
|||
export { default as relationshipfield } from "./RelationshipField.svelte"
|
||||
export { default as passwordfield } from "./PasswordField.svelte"
|
||||
export { default as formstep } from "./FormStep.svelte"
|
||||
export { default as jsonfield } from "./JSONField.svelte"
|
||||
|
|
|
@ -206,6 +206,7 @@ const parseType = (value, type) => {
|
|||
return value
|
||||
}
|
||||
|
||||
// Parse array, treating no elements as null
|
||||
if (type === FieldTypes.ARRAY) {
|
||||
if (!Array.isArray(value) || !value.length) {
|
||||
return null
|
||||
|
@ -213,6 +214,12 @@ const parseType = (value, type) => {
|
|||
return value
|
||||
}
|
||||
|
||||
// For JSON we don't touch the value at all as we want to verify it in its
|
||||
// raw form
|
||||
if (type === FieldTypes.JSON) {
|
||||
return value
|
||||
}
|
||||
|
||||
// If some unknown type, treat as null to avoid breaking validators
|
||||
return null
|
||||
}
|
||||
|
@ -290,6 +297,19 @@ const notContainsHandler = (value, rule) => {
|
|||
return !containsHandler(value, rule)
|
||||
}
|
||||
|
||||
// Evaluates a constraint that the value must be a valid json object
|
||||
const jsonHandler = value => {
|
||||
if (typeof value !== "object" || Array.isArray(value)) {
|
||||
return false
|
||||
}
|
||||
try {
|
||||
JSON.parse(JSON.stringify(value))
|
||||
return true
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of constraint types to handlers.
|
||||
*/
|
||||
|
@ -306,6 +326,7 @@ const handlerMap = {
|
|||
notRegex: notRegexHandler,
|
||||
contains: containsHandler,
|
||||
notContains: notContainsHandler,
|
||||
json: jsonHandler,
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,6 +13,7 @@ export const FieldTypes = {
|
|||
ATTACHMENT: "attachment",
|
||||
LINK: "link",
|
||||
FORMULA: "formula",
|
||||
JSON: "json",
|
||||
}
|
||||
|
||||
export const UnsortableTypes = [
|
||||
|
|
Loading…
Reference in New Issue