Improve UX when editing view calculations
This commit is contained in:
parent
1cbae36683
commit
88b2f423f7
|
@ -147,6 +147,9 @@
|
||||||
.spectrum-Dialog--extraLarge {
|
.spectrum-Dialog--extraLarge {
|
||||||
width: 1000px;
|
width: 1000px;
|
||||||
}
|
}
|
||||||
|
.spectrum-Dialog--medium {
|
||||||
|
width: 540px;
|
||||||
|
}
|
||||||
|
|
||||||
.content-grid {
|
.content-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
ModalContent,
|
ModalContent,
|
||||||
Select,
|
Select,
|
||||||
Icon,
|
Icon,
|
||||||
|
Multiselect,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { CalculationType, FieldType } from "@budibase/types"
|
import { CalculationType, canGroupBy, FieldType } from "@budibase/types"
|
||||||
|
import InfoDisplay from "pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/InfoDisplay.svelte"
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
const { definition, datasource, rows } = getContext("grid")
|
const { definition, datasource, rows } = getContext("grid")
|
||||||
|
@ -35,13 +37,16 @@
|
||||||
|
|
||||||
let modal
|
let modal
|
||||||
let calculations = []
|
let calculations = []
|
||||||
let groupings = []
|
let groupBy = []
|
||||||
|
let schema = {}
|
||||||
|
|
||||||
$: schema = $definition?.schema || {}
|
$: schema = $definition?.schema || {}
|
||||||
|
$: count = extractCalculations($definition?.schema || {}).length
|
||||||
|
$: groupByOptions = getGroupByOptions(schema)
|
||||||
|
|
||||||
const open = () => {
|
const open = () => {
|
||||||
calculations = extractCalculations(schema)
|
calculations = extractCalculations(schema)
|
||||||
groupings = calculations.length ? extractGroupings(schema) : []
|
groupBy = calculations.length ? extractGroupBy(schema) : []
|
||||||
modal?.show()
|
modal?.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,15 +64,13 @@
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractGroupings = schema => {
|
const extractGroupBy = schema => {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
return Object.keys(schema)
|
return Object.keys(schema).filter(field => {
|
||||||
.filter(field => {
|
|
||||||
return schema[field].calculationType == null && schema[field].visible
|
return schema[field].calculationType == null && schema[field].visible
|
||||||
})
|
})
|
||||||
.map(field => ({ field }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the available types for a given calculation
|
// Gets the available types for a given calculation
|
||||||
|
@ -103,18 +106,16 @@
|
||||||
.map(([field]) => field)
|
.map(([field]) => field)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the available fields to group by for a given grouping
|
// Gets the available fields to group by
|
||||||
const getGroupingOptions = (self, groupings, schema) => {
|
const getGroupByOptions = schema => {
|
||||||
return Object.entries(schema)
|
return Object.entries(schema)
|
||||||
.filter(([field, fieldSchema]) => {
|
.filter(([field, fieldSchema]) => {
|
||||||
// Don't allow grouping by calculations
|
// Don't allow grouping by calculations
|
||||||
if (fieldSchema.calculationType) {
|
if (fieldSchema.calculationType) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Don't allow duplicates
|
// Don't allow complex types
|
||||||
return !groupings.some(grouping => {
|
return canGroupBy(fieldSchema.type)
|
||||||
return grouping !== self && grouping.field === field
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.map(([field]) => field)
|
.map(([field]) => field)
|
||||||
}
|
}
|
||||||
|
@ -126,71 +127,65 @@
|
||||||
const deleteCalc = idx => {
|
const deleteCalc = idx => {
|
||||||
calculations = calculations.toSpliced(idx, 1)
|
calculations = calculations.toSpliced(idx, 1)
|
||||||
|
|
||||||
// Remove any groupings if clearing the last calculation
|
// Remove any grouping if clearing the last calculation
|
||||||
if (!calculations.length) {
|
if (!calculations.length) {
|
||||||
groupings = []
|
groupBy = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addGrouping = () => {
|
|
||||||
groupings = [...groupings, {}]
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteGrouping = idx => {
|
|
||||||
groupings = groupings.toSpliced(idx, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
let schema = {}
|
let newSchema = {}
|
||||||
|
|
||||||
// Prune empty stuff
|
|
||||||
calculations = calculations.filter(calc => calc.type && calc.field)
|
|
||||||
groupings = groupings.filter(grouping => grouping.field)
|
|
||||||
|
|
||||||
// Add calculations
|
// Add calculations
|
||||||
for (let calc of calculations) {
|
for (let calc of calculations) {
|
||||||
|
if (!calc.type || !calc.field) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
const typeOption = calculationTypeOptions.find(x => x.value === calc.type)
|
const typeOption = calculationTypeOptions.find(x => x.value === calc.type)
|
||||||
const name = `${typeOption.label} ${calc.field}`
|
const name = `${typeOption.label} ${calc.field}`
|
||||||
schema[name] = {
|
newSchema[name] = {
|
||||||
calculationType: calc.type,
|
calculationType: calc.type,
|
||||||
field: calc.field,
|
field: calc.field,
|
||||||
visible: true,
|
visible: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add groupings
|
// Add other fields
|
||||||
for (let grouping of groupings) {
|
for (let field of Object.keys(schema)) {
|
||||||
schema[grouping.field] = {
|
if (schema[field].calculationType) {
|
||||||
...$definition.schema[grouping.field],
|
continue
|
||||||
visible: true,
|
}
|
||||||
|
newSchema[field] = {
|
||||||
|
...schema[field],
|
||||||
|
visible: groupBy.includes(field),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure primary display is valid
|
// Ensure primary display is valid
|
||||||
let primaryDisplay = $definition.primaryDisplay
|
let primaryDisplay = $definition.primaryDisplay
|
||||||
if (!primaryDisplay || !schema[primaryDisplay]) {
|
if (!primaryDisplay || !newSchema[primaryDisplay]?.visible) {
|
||||||
primaryDisplay = groupings[0]?.field
|
primaryDisplay = groupBy[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save changes
|
// Save changes
|
||||||
await datasource.actions.saveDefinition({
|
await datasource.actions.saveDefinition({
|
||||||
...$definition,
|
...$definition,
|
||||||
primaryDisplay,
|
primaryDisplay,
|
||||||
schema,
|
schema: newSchema,
|
||||||
})
|
})
|
||||||
await rows.actions.refreshData()
|
await rows.actions.refreshData()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ActionButton icon="WebPage" quiet on:click={open}>
|
<ActionButton icon="WebPage" quiet on:click={open}>
|
||||||
Configure calculations
|
Configure calculations{count ? `: ${count}` : ""}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
|
||||||
<Modal bind:this={modal}>
|
<Modal bind:this={modal}>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title="Calculations"
|
title="Calculations"
|
||||||
confirmText="Save"
|
confirmText="Save"
|
||||||
size="L"
|
size="M"
|
||||||
onConfirm={save}
|
onConfirm={save}
|
||||||
>
|
>
|
||||||
{#if !calculations.length}
|
{#if !calculations.length}
|
||||||
|
@ -222,32 +217,30 @@
|
||||||
color="var(--spectrum-global-color-gray-700)"
|
color="var(--spectrum-global-color-gray-700)"
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
{#each groupings as group, idx}
|
<span>Group by</span>
|
||||||
<span>{idx === 0 ? "Group by" : "and"}</span>
|
<div class="group-by">
|
||||||
<Select
|
<Multiselect
|
||||||
options={getGroupingOptions(group, groupings, schema)}
|
options={groupByOptions}
|
||||||
bind:value={group.field}
|
bind:value={groupBy}
|
||||||
placeholder="Column"
|
placeholder="None"
|
||||||
/>
|
/>
|
||||||
<Icon
|
</div>
|
||||||
hoverable
|
|
||||||
name="Close"
|
|
||||||
size="S"
|
|
||||||
on:click={() => deleteGrouping(idx)}
|
|
||||||
color="var(--spectrum-global-color-gray-700)"
|
|
||||||
/>
|
|
||||||
<span />
|
|
||||||
<span />
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<ActionButton quiet icon="Add" on:click={addCalc}>
|
<ActionButton
|
||||||
|
quiet
|
||||||
|
icon="Add"
|
||||||
|
on:click={addCalc}
|
||||||
|
disabled={calculations.length >= 5}
|
||||||
|
>
|
||||||
Add calculation
|
Add calculation
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
<ActionButton quiet icon="Add" on:click={addGrouping}>
|
|
||||||
Group by
|
|
||||||
</ActionButton>
|
|
||||||
</div>
|
</div>
|
||||||
|
<InfoDisplay
|
||||||
|
icon="Help"
|
||||||
|
quiet
|
||||||
|
body="Calculations only work with numeric columns and a maximum of 5 calculations can be added at once."
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -264,7 +257,9 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
.group-by {
|
||||||
|
grid-column: 2 / 5;
|
||||||
|
}
|
||||||
span {
|
span {
|
||||||
text-align: right;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
export let title
|
export let title
|
||||||
export let body
|
export let body
|
||||||
export let icon = "HelpOutline"
|
export let icon = "HelpOutline"
|
||||||
|
export let quiet = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="info" class:noTitle={!title}>
|
<div class="info" class:noTitle={!title} class:quiet>
|
||||||
{#if title}
|
{#if title}
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<Icon name={icon} />
|
<Icon name={icon} />
|
||||||
|
@ -40,15 +41,20 @@
|
||||||
color: var(--spectrum-global-color-gray-600);
|
color: var(--spectrum-global-color-gray-600);
|
||||||
}
|
}
|
||||||
.info {
|
.info {
|
||||||
padding: var(--spacing-m) var(--spacing-l) var(--spacing-l) var(--spacing-l);
|
|
||||||
background-color: var(--background-alt);
|
background-color: var(--background-alt);
|
||||||
|
padding: var(--spacing-m) var(--spacing-l) var(--spacing-m) var(--spacing-l);
|
||||||
border-radius: var(--border-radius-s);
|
border-radius: var(--border-radius-s);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
.quiet {
|
||||||
|
background: none;
|
||||||
|
color: var(--spectrum-global-color-gray-700);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
.noTitle {
|
.noTitle {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-m);
|
gap: var(--spacing-l);
|
||||||
}
|
}
|
||||||
.info :global(a) {
|
.info :global(a) {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|
Loading…
Reference in New Issue