forked from tsai/budibase
18 changed files with 270 additions and 30 deletions
@ -0,0 +1,33 @@ |
|||||
|
<script> |
||||
|
import OptionSelect from "./OptionSelect.svelte" |
||||
|
import { |
||||
|
getDatasourceForProvider, |
||||
|
getSchemaForDatasource, |
||||
|
} from "builderStore/dataBinding" |
||||
|
import { currentAsset } from "builderStore" |
||||
|
import { findClosestMatchingComponent } from "builderStore/storeUtils" |
||||
|
|
||||
|
export let componentInstance |
||||
|
export let value |
||||
|
export let onChange |
||||
|
export let type |
||||
|
|
||||
|
$: form = findClosestMatchingComponent( |
||||
|
$currentAsset.props, |
||||
|
componentInstance._id, |
||||
|
component => component._component === "@budibase/standard-components/form" |
||||
|
) |
||||
|
$: datasource = getDatasourceForProvider(form) |
||||
|
$: schema = getSchemaForDatasource(datasource).schema |
||||
|
$: options = getOptions(schema, type) |
||||
|
|
||||
|
const getOptions = (schema, fieldType) => { |
||||
|
let entries = Object.entries(schema) |
||||
|
if (fieldType) { |
||||
|
entries = entries.filter(entry => entry[1].type === fieldType) |
||||
|
} |
||||
|
return entries.map(entry => entry[0]) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<OptionSelect {value} {onChange} {options} /> |
||||
@ -0,0 +1,5 @@ |
|||||
|
<script> |
||||
|
import FormFieldSelect from "./FormFieldSelect.svelte" |
||||
|
</script> |
||||
|
|
||||
|
<FormFieldSelect {...$$props} type="number" /> |
||||
@ -0,0 +1,5 @@ |
|||||
|
<script> |
||||
|
import FormFieldSelect from "./FormFieldSelect.svelte" |
||||
|
</script> |
||||
|
|
||||
|
<FormFieldSelect {...$$props} type="options" /> |
||||
@ -0,0 +1,5 @@ |
|||||
|
<script> |
||||
|
import FormFieldSelect from "./FormFieldSelect.svelte" |
||||
|
</script> |
||||
|
|
||||
|
<FormFieldSelect {...$$props} type="string" /> |
||||
@ -0,0 +1,5 @@ |
|||||
|
<script> |
||||
|
import StringField from "./StringField.svelte" |
||||
|
</script> |
||||
|
|
||||
|
<StringField {...$$props} type="number" /> |
||||
@ -0,0 +1 @@ |
|||||
|
Select |
||||
@ -0,0 +1,17 @@ |
|||||
|
<script> |
||||
|
import { getContext } from "svelte" |
||||
|
|
||||
|
const { styleable, builderStore } = getContext("sdk") |
||||
|
const component = getContext("component") |
||||
|
|
||||
|
$: styles = { |
||||
|
id: $component.styles.id, |
||||
|
allowSelection: $component.styles.allowSelection, |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
{#if $builderStore.inBuilder} |
||||
|
<div use:styleable={styles}> |
||||
|
<slot /> |
||||
|
</div> |
||||
|
{/if} |
||||
@ -1,2 +1,4 @@ |
|||||
export { default as form } from "./Form.svelte" |
export { default as form } from "./Form.svelte" |
||||
export { default as input } from "./Input.svelte" |
export { default as stringfield } from "./StringField.svelte" |
||||
|
export { default as numberfield } from "./NumberField.svelte" |
||||
|
export { default as optionsfield } from "./OptionsField.svelte" |
||||
|
|||||
@ -0,0 +1,78 @@ |
|||||
|
export const createValidatorFromConstraints = (constraints, field, table) => { |
||||
|
let checks = [] |
||||
|
|
||||
|
if (constraints) { |
||||
|
// Required constraint
|
||||
|
if ( |
||||
|
field === table?.primaryDisplay || |
||||
|
constraints.presence?.allowEmpty === false |
||||
|
) { |
||||
|
checks.push(presenceConstraint) |
||||
|
} |
||||
|
|
||||
|
// String length constraint
|
||||
|
if (constraints.length?.maximum) { |
||||
|
const length = constraints.length?.maximum |
||||
|
checks.push(lengthConstraint(length)) |
||||
|
} |
||||
|
|
||||
|
// Min / max number constraint
|
||||
|
if (constraints.numericality?.greaterThanOrEqualTo != null) { |
||||
|
const min = constraints.numericality.greaterThanOrEqualTo |
||||
|
checks.push(numericalConstraint(x => x >= min, `Minimum value is ${min}`)) |
||||
|
} |
||||
|
if (constraints.numericality?.lessThanOrEqualTo != null) { |
||||
|
const max = constraints.numericality.lessThanOrEqualTo |
||||
|
checks.push(numericalConstraint(x => x <= max, `Maximum value is ${max}`)) |
||||
|
} |
||||
|
|
||||
|
// Inclusion constraint
|
||||
|
if (constraints.inclusion !== undefined) { |
||||
|
const options = constraints.inclusion |
||||
|
checks.push(inclusionConstraint(options)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Evaluate each constraint
|
||||
|
return value => { |
||||
|
for (let check of checks) { |
||||
|
const error = check(value) |
||||
|
if (error) { |
||||
|
return error |
||||
|
} |
||||
|
} |
||||
|
return null |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const presenceConstraint = value => { |
||||
|
return value == null || value === "" ? "Required" : null |
||||
|
} |
||||
|
|
||||
|
const lengthConstraint = maxLength => value => { |
||||
|
if (value && value.length > maxLength) { |
||||
|
;`Maximum ${maxLength} characters` |
||||
|
} |
||||
|
return null |
||||
|
} |
||||
|
|
||||
|
const numericalConstraint = (constraint, error) => value => { |
||||
|
if (isNaN(value)) { |
||||
|
return "Must be a number" |
||||
|
} |
||||
|
const number = parseFloat(value) |
||||
|
if (!constraint(number)) { |
||||
|
return error |
||||
|
} |
||||
|
return null |
||||
|
} |
||||
|
|
||||
|
const inclusionConstraint = (options = []) => value => { |
||||
|
if (value == null || value === "") { |
||||
|
return null |
||||
|
} |
||||
|
if (!options.includes(value)) { |
||||
|
return "Invalid value" |
||||
|
} |
||||
|
return null |
||||
|
} |
||||
Loading…
Reference in new issue