forked from tsai/budibase
committed by
GitHub
7 changed files with 223 additions and 185 deletions
@ -0,0 +1,182 @@ |
|||||
|
<script> |
||||
|
import { |
||||
|
DatePicker, |
||||
|
Icon, |
||||
|
Button, |
||||
|
Select, |
||||
|
Combobox, |
||||
|
Input, |
||||
|
DrawerContent, |
||||
|
Layout, |
||||
|
Body, |
||||
|
} from "@budibase/bbui" |
||||
|
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" |
||||
|
import { generate } from "shortid" |
||||
|
import { OperatorOptions, getValidOperatorsForType } from "helpers/lucene" |
||||
|
|
||||
|
export let schemaFields |
||||
|
export let filters = [] |
||||
|
export let bindings = [] |
||||
|
|
||||
|
const BannedTypes = ["link", "attachment", "formula"] |
||||
|
|
||||
|
$: fieldOptions = (schemaFields ?? []) |
||||
|
.filter(field => !BannedTypes.includes(field.type)) |
||||
|
.map(field => field.name) |
||||
|
|
||||
|
const addFilter = () => { |
||||
|
filters = [ |
||||
|
...filters, |
||||
|
{ |
||||
|
id: generate(), |
||||
|
field: null, |
||||
|
operator: OperatorOptions.Equals.value, |
||||
|
value: null, |
||||
|
valueType: "Value", |
||||
|
}, |
||||
|
] |
||||
|
} |
||||
|
|
||||
|
const removeFilter = id => { |
||||
|
filters = filters.filter(field => field.id !== id) |
||||
|
} |
||||
|
|
||||
|
const duplicateFilter = id => { |
||||
|
const existingFilter = filters.find(filter => filter.id === id) |
||||
|
const duplicate = { ...existingFilter, id: generate() } |
||||
|
filters = [...filters, duplicate] |
||||
|
} |
||||
|
|
||||
|
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).map( |
||||
|
x => x.value |
||||
|
) |
||||
|
if (!validOperators.includes(expression.operator)) { |
||||
|
expression.operator = validOperators[0] ?? 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> |
||||
|
|
||||
|
<DrawerContent> |
||||
|
<div class="container"> |
||||
|
<Layout noPadding> |
||||
|
<Body size="S"> |
||||
|
{#if !filters?.length} |
||||
|
Add your first filter column. |
||||
|
{:else} |
||||
|
Results are filtered to only those which match all of the following |
||||
|
constraints. |
||||
|
{/if} |
||||
|
</Body> |
||||
|
{#if filters?.length} |
||||
|
<div class="fields"> |
||||
|
{#each filters as filter, idx} |
||||
|
<Select |
||||
|
bind:value={filter.field} |
||||
|
options={fieldOptions} |
||||
|
on:change={e => onFieldChange(filter, e.detail)} |
||||
|
placeholder="Column" |
||||
|
/> |
||||
|
<Select |
||||
|
disabled={!filter.field} |
||||
|
options={getValidOperatorsForType(filter.type)} |
||||
|
bind:value={filter.operator} |
||||
|
on:change={e => onOperatorChange(filter, e.detail)} |
||||
|
placeholder={null} |
||||
|
/> |
||||
|
<Select |
||||
|
disabled={filter.noValue || !filter.field} |
||||
|
options={["Value", "Binding"]} |
||||
|
bind:value={filter.valueType} |
||||
|
placeholder={null} |
||||
|
/> |
||||
|
{#if filter.valueType === "Binding"} |
||||
|
<DrawerBindableInput |
||||
|
disabled={filter.noValue} |
||||
|
title={`Value for "${filter.field}"`} |
||||
|
value={filter.value} |
||||
|
placeholder="Value" |
||||
|
{bindings} |
||||
|
on:change={event => (filter.value = event.detail)} |
||||
|
/> |
||||
|
{:else if ["string", "longform", "number"].includes(filter.type)} |
||||
|
<Input disabled={filter.noValue} bind:value={filter.value} /> |
||||
|
{:else if filter.type === "options"} |
||||
|
<Combobox |
||||
|
disabled={filter.noValue} |
||||
|
options={getFieldOptions(filter.field)} |
||||
|
bind:value={filter.value} |
||||
|
/> |
||||
|
{:else if filter.type === "boolean"} |
||||
|
<Combobox |
||||
|
disabled={filter.noValue} |
||||
|
options={[ |
||||
|
{ label: "True", value: "true" }, |
||||
|
{ label: "False", value: "false" }, |
||||
|
]} |
||||
|
bind:value={filter.value} |
||||
|
/> |
||||
|
{:else if filter.type === "datetime"} |
||||
|
<DatePicker disabled={filter.noValue} bind:value={filter.value} /> |
||||
|
{:else} |
||||
|
<DrawerBindableInput disabled /> |
||||
|
{/if} |
||||
|
<Icon |
||||
|
name="Duplicate" |
||||
|
hoverable |
||||
|
size="S" |
||||
|
on:click={() => duplicateFilter(filter.id)} |
||||
|
/> |
||||
|
<Icon |
||||
|
name="Close" |
||||
|
hoverable |
||||
|
size="S" |
||||
|
on:click={() => removeFilter(filter.id)} |
||||
|
/> |
||||
|
{/each} |
||||
|
</div> |
||||
|
{/if} |
||||
|
<div> |
||||
|
<Button icon="AddCircle" size="M" secondary on:click={addFilter}> |
||||
|
Add filter |
||||
|
</Button> |
||||
|
</div> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
</DrawerContent> |
||||
|
|
||||
|
<style> |
||||
|
.container { |
||||
|
width: 100%; |
||||
|
max-width: 1000px; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
.fields { |
||||
|
display: grid; |
||||
|
column-gap: var(--spacing-l); |
||||
|
row-gap: var(--spacing-s); |
||||
|
align-items: center; |
||||
|
grid-template-columns: 1fr 120px 120px 1fr auto auto; |
||||
|
} |
||||
|
</style> |
||||
@ -1,151 +0,0 @@ |
|||||
<script> |
|
||||
import { |
|
||||
DatePicker, |
|
||||
ActionButton, |
|
||||
Button, |
|
||||
Select, |
|
||||
Combobox, |
|
||||
Input, |
|
||||
} from "@budibase/bbui" |
|
||||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" |
|
||||
import { generate } from "shortid" |
|
||||
import { OperatorOptions, getValidOperatorsForType } from "helpers/lucene" |
|
||||
|
|
||||
export let schemaFields |
|
||||
export let value |
|
||||
export let bindings = [] |
|
||||
|
|
||||
const BannedTypes = ["link", "attachment"] |
|
||||
$: 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, |
|
||||
valueType: "Value", |
|
||||
}, |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
const removeField = id => { |
|
||||
value = value.filter(field => field.id !== id) |
|
||||
} |
|
||||
|
|
||||
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).map( |
|
||||
x => x.value |
|
||||
) |
|
||||
if (!validOperators.includes(expression.operator)) { |
|
||||
expression.operator = validOperators[0] ?? 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} |
|
||||
/> |
|
||||
<Select |
|
||||
disabled={expression.noValue || !expression.field} |
|
||||
options={["Value", "Binding"]} |
|
||||
bind:value={expression.valueType} |
|
||||
placeholder={null} |
|
||||
/> |
|
||||
{#if expression.valueType === "Binding"} |
|
||||
<DrawerBindableInput |
|
||||
disabled={expression.noValue} |
|
||||
title={`Value for "${expression.field}"`} |
|
||||
value={expression.value} |
|
||||
placeholder="Value" |
|
||||
{bindings} |
|
||||
on:change={event => (expression.value = event.detail)} |
|
||||
/> |
|
||||
{:else if ["string", "longform", "number"].includes(expression.type)} |
|
||||
<Input disabled={expression.noValue} bind:value={expression.value} /> |
|
||||
{:else if expression.type === "options"} |
|
||||
<Combobox |
|
||||
disabled={expression.noValue} |
|
||||
options={getFieldOptions(expression.field)} |
|
||||
bind:value={expression.value} |
|
||||
/> |
|
||||
{:else if expression.type === "boolean"} |
|
||||
<Combobox |
|
||||
disabled={expression.noValue} |
|
||||
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 120px 1fr auto; |
|
||||
} |
|
||||
</style> |
|
||||
Loading…
Reference in new issue