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 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