Merge pull request #2344 from Budibase/ak-fixes
Fixes and features for various issues
This commit is contained in:
commit
73def04951
|
@ -5,6 +5,7 @@
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { generateID } from "../../utils/helpers"
|
import { generateID } from "../../utils/helpers"
|
||||||
import Icon from "../../Icon/Icon.svelte"
|
import Icon from "../../Icon/Icon.svelte"
|
||||||
|
import Link from "../../Link/Link.svelte"
|
||||||
|
|
||||||
const BYTES_IN_KB = 1000
|
const BYTES_IN_KB = 1000
|
||||||
const BYTES_IN_MB = 1000000
|
const BYTES_IN_MB = 1000000
|
||||||
|
@ -117,7 +118,13 @@
|
||||||
{#if gallery}
|
{#if gallery}
|
||||||
<div class="gallery">
|
<div class="gallery">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div class="filename">{selectedImage.name}</div>
|
<div class="filename">
|
||||||
|
{#if selectedUrl}
|
||||||
|
<Link href={selectedUrl}>{selectedImage.name}</Link>
|
||||||
|
{:else}
|
||||||
|
{selectedImage.name}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{#if selectedImage.size}
|
{#if selectedImage.size}
|
||||||
<div class="filesize">
|
<div class="filesize">
|
||||||
{#if selectedImage.size <= BYTES_IN_MB}
|
{#if selectedImage.size <= BYTES_IN_MB}
|
||||||
|
|
|
@ -23,7 +23,11 @@
|
||||||
export let readonly = false
|
export let readonly = false
|
||||||
export let quiet = false
|
export let quiet = false
|
||||||
export let autoWidth = false
|
export let autoWidth = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
$: sortedOptions = getSortedOptions(options, getOptionLabel)
|
||||||
|
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
dispatch("click")
|
dispatch("click")
|
||||||
if (readonly) {
|
if (readonly) {
|
||||||
|
@ -31,6 +35,17 @@
|
||||||
}
|
}
|
||||||
open = true
|
open = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSortedOptions = (options, getLabel) => {
|
||||||
|
if (!options?.length || !Array.isArray(options)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return options.sort((a, b) => {
|
||||||
|
const labelA = getLabel(a)
|
||||||
|
const labelB = getLabel(b)
|
||||||
|
return labelA > labelB ? 1 : -1
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
@ -101,8 +116,8 @@
|
||||||
</svg>
|
</svg>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
{#if options && Array.isArray(options)}
|
{#if sortedOptions.length}
|
||||||
{#each options as option, idx}
|
{#each sortedOptions as option, idx}
|
||||||
<li
|
<li
|
||||||
class="spectrum-Menu-item"
|
class="spectrum-Menu-item"
|
||||||
class:is-selected={isOptionSelected(getOptionValue(option, idx))}
|
class:is-selected={isOptionSelected(getOptionValue(option, idx))}
|
||||||
|
@ -121,9 +136,9 @@
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
<span class="spectrum-Menu-itemLabel"
|
<span class="spectrum-Menu-itemLabel">
|
||||||
>{getOptionLabel(option, idx)}</span
|
{getOptionLabel(option, idx)}
|
||||||
>
|
</span>
|
||||||
<svg
|
<svg
|
||||||
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
||||||
focusable="false"
|
focusable="false"
|
||||||
|
|
|
@ -282,12 +282,12 @@
|
||||||
gap: var(--spacing-l);
|
gap: var(--spacing-l);
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: auto 1fr auto 1fr 1fr 1fr 1fr auto auto;
|
grid-template-columns: auto 160px auto 1fr 130px 130px 1fr auto auto;
|
||||||
border-radius: var(--border-radius-s);
|
border-radius: var(--border-radius-s);
|
||||||
transition: background-color ease-in-out 130ms;
|
transition: background-color ease-in-out 130ms;
|
||||||
}
|
}
|
||||||
.condition.update {
|
.condition.update {
|
||||||
grid-template-columns: auto 1fr 1fr auto 1fr auto 1fr 1fr 1fr 1fr auto;
|
grid-template-columns: auto 160px 1fr auto 1fr auto 1fr 130px 130px 1fr auto auto;
|
||||||
}
|
}
|
||||||
.condition:hover {
|
.condition:hover {
|
||||||
background-color: var(--spectrum-global-color-gray-100);
|
background-color: var(--spectrum-global-color-gray-100);
|
||||||
|
|
|
@ -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,19 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { notifications, ActionButton, Button, Drawer } from "@budibase/bbui"
|
||||||
notifications,
|
|
||||||
ActionButton,
|
|
||||||
Button,
|
|
||||||
Drawer,
|
|
||||||
Body,
|
|
||||||
DrawerContent,
|
|
||||||
Layout,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import {
|
import {
|
||||||
getDatasourceForProvider,
|
getDatasourceForProvider,
|
||||||
getSchemaForDatasource,
|
getSchemaForDatasource,
|
||||||
} from "builderStore/dataBinding"
|
} from "builderStore/dataBinding"
|
||||||
import LuceneFilterBuilder from "./LuceneFilterBuilder.svelte"
|
import FilterDrawer from "./FilterDrawer.svelte"
|
||||||
import { currentAsset } from "builderStore"
|
import { currentAsset } from "builderStore"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
@ -25,9 +17,6 @@
|
||||||
let drawer
|
let drawer
|
||||||
let tempValue = value || []
|
let tempValue = value || []
|
||||||
|
|
||||||
$: numFilters = Array.isArray(tempValue)
|
|
||||||
? tempValue.length
|
|
||||||
: Object.keys(tempValue || {}).length
|
|
||||||
$: dataSource = getDatasourceForProvider($currentAsset, componentInstance)
|
$: dataSource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
$: schema = getSchemaForDatasource($currentAsset, dataSource)?.schema
|
$: schema = getSchemaForDatasource($currentAsset, dataSource)?.schema
|
||||||
$: schemaFields = Object.values(schema || {})
|
$: schemaFields = Object.values(schema || {})
|
||||||
|
@ -43,17 +32,10 @@
|
||||||
<ActionButton on:click={drawer.show}>Define filters</ActionButton>
|
<ActionButton on:click={drawer.show}>Define filters</ActionButton>
|
||||||
<Drawer bind:this={drawer} title="Filtering">
|
<Drawer bind:this={drawer} title="Filtering">
|
||||||
<Button cta slot="buttons" on:click={saveFilter}>Save</Button>
|
<Button cta slot="buttons" on:click={saveFilter}>Save</Button>
|
||||||
<DrawerContent slot="body">
|
<FilterDrawer
|
||||||
<Layout noPadding>
|
slot="body"
|
||||||
<Body size="S">
|
bind:filters={tempValue}
|
||||||
{#if !numFilters}
|
{bindings}
|
||||||
Add your first filter column.
|
{schemaFields}
|
||||||
{:else}
|
/>
|
||||||
Results are filtered to only those which match all of the following
|
|
||||||
constraints.
|
|
||||||
{/if}
|
|
||||||
</Body>
|
|
||||||
<LuceneFilterBuilder bind:value={tempValue} {schemaFields} {bindings} />
|
|
||||||
</Layout>
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
|
@ -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>
|
|
|
@ -25,4 +25,7 @@
|
||||||
.spectrum-Form {
|
.spectrum-Form {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.spectrum-Form--labelsAbove {
|
||||||
|
gap: var(--spectrum-global-dimension-size-100);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue