Merge pull request #2344 from Budibase/ak-fixes

Fixes and features for various issues
This commit is contained in:
Andrew Kingston 2021-08-16 09:04:13 +01:00 committed by GitHub
commit 73def04951
7 changed files with 223 additions and 185 deletions

View File

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

View File

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

View File

@ -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);

View File

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

View File

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

View File

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

View File

@ -25,4 +25,7 @@
.spectrum-Form { .spectrum-Form {
width: 100%; width: 100%;
} }
.spectrum-Form--labelsAbove {
gap: var(--spectrum-global-dimension-size-100);
}
</style> </style>