forked from tsai/budibase
16 changed files with 619 additions and 85 deletions
@ -0,0 +1,38 @@ |
|||||
|
<script> |
||||
|
import { Select, Label } from "@budibase/bbui" |
||||
|
import { currentAsset, store } from "builderStore" |
||||
|
import { getActionProviderComponents } from "builderStore/dataBinding" |
||||
|
|
||||
|
export let parameters |
||||
|
|
||||
|
$: actionProviders = getActionProviderComponents( |
||||
|
$currentAsset, |
||||
|
$store.selectedComponentId, |
||||
|
"NextPage" |
||||
|
) |
||||
|
</script> |
||||
|
|
||||
|
<div class="root"> |
||||
|
<Label small>Data Provider</Label> |
||||
|
<Select |
||||
|
bind:value={parameters.componentId} |
||||
|
options={actionProviders} |
||||
|
getOptionLabel={(x) => x._instanceName} |
||||
|
getOptionValue={(x) => x._id} |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.root { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
max-width: 800px; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
|
||||
|
.root :global(> div) { |
||||
|
flex: 1; |
||||
|
margin-left: var(--spacing-l); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,38 @@ |
|||||
|
<script> |
||||
|
import { Select, Label } from "@budibase/bbui" |
||||
|
import { currentAsset, store } from "builderStore" |
||||
|
import { getActionProviderComponents } from "builderStore/dataBinding" |
||||
|
|
||||
|
export let parameters |
||||
|
|
||||
|
$: actionProviders = getActionProviderComponents( |
||||
|
$currentAsset, |
||||
|
$store.selectedComponentId, |
||||
|
"PrevPage" |
||||
|
) |
||||
|
</script> |
||||
|
|
||||
|
<div class="root"> |
||||
|
<Label small>Data Provider</Label> |
||||
|
<Select |
||||
|
bind:value={parameters.componentId} |
||||
|
options={actionProviders} |
||||
|
getOptionLabel={(x) => x._instanceName} |
||||
|
getOptionValue={(x) => x._id} |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.root { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
max-width: 800px; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
|
||||
|
.root :global(> div) { |
||||
|
flex: 1; |
||||
|
margin-left: var(--spacing-l); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,217 @@ |
|||||
|
<script> |
||||
|
import { |
||||
|
DatePicker, |
||||
|
ActionButton, |
||||
|
Button, |
||||
|
Select, |
||||
|
Combobox, |
||||
|
} from "@budibase/bbui" |
||||
|
import { store, currentAsset } from "builderStore" |
||||
|
import { getBindableProperties } from "builderStore/dataBinding" |
||||
|
import { createEventDispatcher } from "svelte" |
||||
|
import DrawerBindableInput from "components/common/DrawerBindableInput.svelte" |
||||
|
import { generate } from "shortid" |
||||
|
|
||||
|
const dispatch = createEventDispatcher() |
||||
|
|
||||
|
export let schemaFields |
||||
|
export let value |
||||
|
|
||||
|
const OperatorOptions = { |
||||
|
Equals: { |
||||
|
value: "equal", |
||||
|
label: "Equals", |
||||
|
}, |
||||
|
NotEquals: { |
||||
|
value: "notEqual", |
||||
|
label: "Not equals", |
||||
|
}, |
||||
|
Empty: { |
||||
|
value: "empty", |
||||
|
label: "Is empty", |
||||
|
}, |
||||
|
NotEmpty: { |
||||
|
value: "notEmpty", |
||||
|
label: "Is not empty", |
||||
|
}, |
||||
|
StartsWith: { |
||||
|
value: "string", |
||||
|
label: "Starts with", |
||||
|
}, |
||||
|
Like: { |
||||
|
value: "fuzzy", |
||||
|
label: "Like", |
||||
|
}, |
||||
|
MoreThan: { |
||||
|
value: "rangeLow", |
||||
|
label: "More than", |
||||
|
}, |
||||
|
LessThan: { |
||||
|
value: "rangeHigh", |
||||
|
label: "Less than", |
||||
|
}, |
||||
|
} |
||||
|
const BannedTypes = ["link", "attachment"] |
||||
|
$: bindableProperties = getBindableProperties( |
||||
|
$currentAsset, |
||||
|
$store.selectedComponentId |
||||
|
) |
||||
|
$: fieldOptions = (schemaFields ?? []) |
||||
|
.filter(field => !BannedTypes.includes(field.type)) |
||||
|
.map(field => field.name) |
||||
|
|
||||
|
const addField = () => { |
||||
|
value = [ |
||||
|
...value, |
||||
|
{ |
||||
|
id: generate(), |
||||
|
field: null, |
||||
|
operator: OperatorOptions.Equals.value, |
||||
|
value: null, |
||||
|
}, |
||||
|
] |
||||
|
} |
||||
|
|
||||
|
const removeField = id => { |
||||
|
value = value.filter(field => field.id !== id) |
||||
|
} |
||||
|
|
||||
|
const getValidOperatorsForType = type => { |
||||
|
const Op = OperatorOptions |
||||
|
if (type === "string") { |
||||
|
return [ |
||||
|
Op.Equals, |
||||
|
Op.NotEquals, |
||||
|
Op.StartsWith, |
||||
|
Op.Like, |
||||
|
Op.Empty, |
||||
|
Op.NotEmpty, |
||||
|
] |
||||
|
} else if (type === "number") { |
||||
|
return [ |
||||
|
Op.Equals, |
||||
|
Op.NotEquals, |
||||
|
Op.MoreThan, |
||||
|
Op.LessThan, |
||||
|
Op.Empty, |
||||
|
Op.NotEmpty, |
||||
|
] |
||||
|
} else if (type === "options") { |
||||
|
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty] |
||||
|
} else if (type === "boolean") { |
||||
|
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty] |
||||
|
} else if (type === "longform") { |
||||
|
return [ |
||||
|
Op.Equals, |
||||
|
Op.NotEquals, |
||||
|
Op.StartsWith, |
||||
|
Op.Like, |
||||
|
Op.Empty, |
||||
|
Op.NotEmpty, |
||||
|
] |
||||
|
} else if (type === "datetime") { |
||||
|
return [ |
||||
|
Op.Equals, |
||||
|
Op.NotEquals, |
||||
|
Op.MoreThan, |
||||
|
Op.LessThan, |
||||
|
Op.Empty, |
||||
|
Op.NotEmpty, |
||||
|
] |
||||
|
} |
||||
|
return [] |
||||
|
} |
||||
|
|
||||
|
const onFieldChange = (expression, field) => { |
||||
|
// Update the field type |
||||
|
expression.type = schemaFields.find(x => x.name === field)?.type |
||||
|
|
||||
|
// Ensure a valid operator is set |
||||
|
const validOperators = getValidOperatorsForType(expression.type) |
||||
|
if (!validOperators.includes(expression.operator)) { |
||||
|
expression.operator = |
||||
|
validOperators[0]?.value ?? OperatorOptions.Equals.value |
||||
|
onOperatorChange(expression, expression.operator) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const onOperatorChange = (expression, operator) => { |
||||
|
const noValueOptions = [ |
||||
|
OperatorOptions.Empty.value, |
||||
|
OperatorOptions.NotEmpty.value, |
||||
|
] |
||||
|
expression.noValue = noValueOptions.includes(operator) |
||||
|
if (expression.noValue) { |
||||
|
expression.value = null |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const getFieldOptions = field => { |
||||
|
const schema = schemaFields.find(x => x.name === field) |
||||
|
return schema?.constraints?.inclusion || [] |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
{#if value?.length} |
||||
|
<div class="fields"> |
||||
|
{#each value as expression, idx} |
||||
|
<Select |
||||
|
bind:value={expression.field} |
||||
|
options={fieldOptions} |
||||
|
on:change={e => onFieldChange(expression, e.detail)} |
||||
|
placeholder="Column" /> |
||||
|
<Select |
||||
|
disabled={!expression.field} |
||||
|
options={getValidOperatorsForType(expression.type)} |
||||
|
bind:value={expression.operator} |
||||
|
on:change={e => onOperatorChange(expression, e.detail)} |
||||
|
placeholder={null} /> |
||||
|
{#if ['string', 'longform', 'number'].includes(expression.type)} |
||||
|
<DrawerBindableInput |
||||
|
disabled={expression.noValue} |
||||
|
title={`Value for "${expression.field}"`} |
||||
|
value={expression.value} |
||||
|
placeholder="Value" |
||||
|
bindings={bindableProperties} |
||||
|
on:change={event => (expression.value = event.detail)} /> |
||||
|
{:else if expression.type === 'options'} |
||||
|
<Combobox |
||||
|
disabled={expression.noValue} |
||||
|
options={getFieldOptions(expression.field)} |
||||
|
bind:value={expression.value} /> |
||||
|
{:else if expression.type === 'boolean'} |
||||
|
<Combobox |
||||
|
disabled |
||||
|
options={[{ label: 'True', value: true }, { label: 'False', value: false }]} |
||||
|
bind:value={expression.value} /> |
||||
|
{:else if expression.type === 'datetime'} |
||||
|
<DatePicker |
||||
|
disabled={expression.noValue} |
||||
|
bind:value={expression.value} /> |
||||
|
{:else} |
||||
|
<DrawerBindableInput disabled /> |
||||
|
{/if} |
||||
|
|
||||
|
<ActionButton |
||||
|
size="S" |
||||
|
quiet |
||||
|
icon="Close" |
||||
|
on:click={() => removeField(expression.id)} /> |
||||
|
{/each} |
||||
|
</div> |
||||
|
{/if} |
||||
|
<div> |
||||
|
<Button icon="AddCircle" size="M" secondary on:click={addField}> |
||||
|
Add expression |
||||
|
</Button> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.fields { |
||||
|
display: grid; |
||||
|
column-gap: var(--spacing-l); |
||||
|
row-gap: var(--spacing-s); |
||||
|
align-items: center; |
||||
|
grid-template-columns: 1fr 120px 1fr auto; |
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue