Merge pull request #15457 from Budibase/grid-column-formatting

Table column "format" setting
This commit is contained in:
Andrew Kingston 2025-02-03 15:14:33 +00:00 committed by GitHub
commit 3b58dca570
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 76 additions and 25 deletions

View File

@ -61,6 +61,7 @@
anchor={primaryDisplayColumnAnchor} anchor={primaryDisplayColumnAnchor}
item={columns.primary} item={columns.primary}
on:change={e => columns.update(e.detail)} on:change={e => columns.update(e.detail)}
{bindings}
/> />
</div> </div>
</div> </div>

View File

@ -8,6 +8,7 @@
export let item export let item
export let anchor export let anchor
export let bindings
let draggableStore = writable({ let draggableStore = writable({
selected: null, selected: null,
@ -48,6 +49,7 @@
componentInstance={item} componentInstance={item}
{parseSettings} {parseSettings}
on:change on:change
{bindings}
> >
<div slot="header" class="type-icon"> <div slot="header" class="type-icon">
<Icon name={icon} /> <Icon name={icon} />

View File

@ -69,6 +69,7 @@ const toGridFormat = draggableListColumns => {
active: entry.active, active: entry.active,
width: entry.width, width: entry.width,
conditions: entry.conditions, conditions: entry.conditions,
format: entry.format,
})) }))
} }
@ -85,6 +86,7 @@ const toDraggableListFormat = (gridFormatColumns, createComponent, schema) => {
columnType: column.columnType || schema[column.field].type, columnType: column.columnType || schema[column.field].type,
width: column.width, width: column.width,
conditions: column.conditions, conditions: column.conditions,
format: column.format,
}, },
{} {}
) )

View File

@ -165,7 +165,7 @@
} }
.text { .text {
font-size: var(--spectrum-global-dimension-font-size-75); font-size: var(--spectrum-global-dimension-font-size-75);
color: var(--grey-6); color: var(--spectrum-global-color-gray-700);
grid-column: 2 / 2; grid-column: 2 / 2;
} }

View File

@ -3089,6 +3089,12 @@
"type": "tableConditions", "type": "tableConditions",
"label": "Conditions", "label": "Conditions",
"key": "conditions" "key": "conditions"
},
{
"type": "text",
"label": "Format",
"key": "format",
"info": "Changing format will display values as text"
} }
] ]
}, },
@ -7685,7 +7691,8 @@
{ {
"type": "columns/grid", "type": "columns/grid",
"key": "columns", "key": "columns",
"resetOn": "table" "resetOn": "table",
"nested": true
} }
] ]
}, },

View File

@ -5,6 +5,7 @@
import { get, derived, readable } from "svelte/store" import { get, derived, readable } from "svelte/store"
import { featuresStore } from "stores" import { featuresStore } from "stores"
import { Grid } from "@budibase/frontend-core" import { Grid } from "@budibase/frontend-core"
import { processStringSync } from "@budibase/string-templates"
// table is actually any datasource, but called table for legacy compatibility // table is actually any datasource, but called table for legacy compatibility
export let table export let table
@ -42,6 +43,7 @@
let gridContext let gridContext
let minHeight = 0 let minHeight = 0
$: id = $component.id
$: currentTheme = $context?.device?.theme $: currentTheme = $context?.device?.theme
$: darkMode = !currentTheme?.includes("light") $: darkMode = !currentTheme?.includes("light")
$: parsedColumns = getParsedColumns(columns) $: parsedColumns = getParsedColumns(columns)
@ -65,7 +67,6 @@
const clean = gridContext?.rows.actions.cleanRow || (x => x) const clean = gridContext?.rows.actions.cleanRow || (x => x)
const cleaned = rows.map(clean) const cleaned = rows.map(clean)
const goldenRow = generateGoldenSample(cleaned) const goldenRow = generateGoldenSample(cleaned)
const id = get(component).id
return { return {
// Not sure what this one is for... // Not sure what this one is for...
[id]: goldenRow, [id]: goldenRow,
@ -104,6 +105,7 @@
order: idx, order: idx,
conditions: column.conditions, conditions: column.conditions,
visible: !!column.active, visible: !!column.active,
format: createFormatter(column),
} }
if (column.width) { if (column.width) {
overrides[column.field].width = column.width overrides[column.field].width = column.width
@ -112,6 +114,13 @@
return overrides return overrides
} }
const createFormatter = column => {
if (typeof column.format !== "string" || !column.format.trim().length) {
return null
}
return row => processStringSync(column.format, { [id]: row })
}
const enrichButtons = buttons => { const enrichButtons = buttons => {
if (!buttons?.length) { if (!buttons?.length) {
return null return null

View File

@ -3,6 +3,7 @@
import GridCell from "./GridCell.svelte" import GridCell from "./GridCell.svelte"
import { getCellRenderer } from "../lib/renderers" import { getCellRenderer } from "../lib/renderers"
import { derived, writable } from "svelte/store" import { derived, writable } from "svelte/store"
import TextCell from "./TextCell.svelte"
const { const {
rows, rows,
@ -36,11 +37,17 @@
let api let api
// Get the appropriate cell renderer and value
$: hasCustomFormat = column.format && !row._isNewRow
$: renderer = hasCustomFormat ? TextCell : getCellRenderer(column)
$: value = hasCustomFormat ? row.__formatted?.[column.name] : row[column.name]
// Get the error for this cell if the cell is focused or selected // Get the error for this cell if the cell is focused or selected
$: error = getErrorStore(rowFocused, cellId) $: error = getErrorStore(rowFocused, cellId)
// Determine if the cell is editable // Determine if the cell is editable
$: readonly = $: readonly =
hasCustomFormat ||
columns.actions.isReadonly(column) || columns.actions.isReadonly(column) ||
(!$config.canEditRows && !row._isNewRow) (!$config.canEditRows && !row._isNewRow)
@ -69,7 +76,7 @@
onKeyDown: (...params) => api?.onKeyDown?.(...params), onKeyDown: (...params) => api?.onKeyDown?.(...params),
isReadonly: () => readonly, isReadonly: () => readonly,
getType: () => column.schema.type, getType: () => column.schema.type,
getValue: () => row[column.name], getValue: () => value,
setValue: (value, options = { apply: true }) => { setValue: (value, options = { apply: true }) => {
validation.actions.setError(cellId, null) validation.actions.setError(cellId, null)
updateValue({ updateValue({
@ -136,9 +143,9 @@
}} }}
> >
<svelte:component <svelte:component
this={getCellRenderer(column)} this={renderer}
bind:api bind:api
value={row[column.name]} {value}
schema={column.schema} schema={column.schema}
onChange={cellAPI.setValue} onChange={cellAPI.setValue}
{focused} {focused}

View File

@ -53,7 +53,6 @@ export const getCellRenderer = (column: UIColumn) => {
if (column.calculationType) { if (column.calculationType) {
return NumberCell return NumberCell
} }
return ( return (
getCellRendererByType(column.schema?.cellRenderType) || getCellRendererByType(column.schema?.cellRenderType) ||
getCellRendererByType(column.schema?.type) || getCellRendererByType(column.schema?.type) ||

View File

@ -188,6 +188,7 @@ export const initialise = (context: StoreContext) => {
conditions: fieldSchema.conditions, conditions: fieldSchema.conditions,
related: fieldSchema.related, related: fieldSchema.related,
calculationType: fieldSchema.calculationType, calculationType: fieldSchema.calculationType,
format: fieldSchema.format,
__left: undefined as any, // TODO __left: undefined as any, // TODO
__idx: undefined as any, // TODO __idx: undefined as any, // TODO
} }

View File

@ -16,6 +16,7 @@ import { Store as StoreContext } from "."
interface IndexedUIRow extends UIRow { interface IndexedUIRow extends UIRow {
__idx: number __idx: number
__formatted: Record<string, any>
} }
interface RowStore { interface RowStore {
@ -114,17 +115,19 @@ export const createStores = (): RowStore => {
export const deriveStores = (context: StoreContext): RowDerivedStore => { export const deriveStores = (context: StoreContext): RowDerivedStore => {
const { rows, enrichedSchema } = context const { rows, enrichedSchema } = context
// Enrich rows with an index property and any pending changes // Enrich rows with an index property and additional values
const enrichedRows = derived( const enrichedRows = derived(
[rows, enrichedSchema], [rows, enrichedSchema],
([$rows, $enrichedSchema]) => { ([$rows, $enrichedSchema]) => {
const customColumns = Object.values($enrichedSchema || {}).filter( // Find columns which require additional processing
f => f.related const cols = Object.values($enrichedSchema || {})
) const relatedColumns = cols.filter(col => col.related)
return $rows.map<IndexedUIRow>((row, idx) => ({ const formattedColumns = cols.filter(col => col.format)
...row,
__idx: idx, return $rows.map<IndexedUIRow>((row, idx) => {
...customColumns.reduce<Record<string, string>>((map, column) => { // Derive any values that need enriched from related rows
const relatedValues = relatedColumns.reduce<Record<string, string>>(
(map, column) => {
const fromField = $enrichedSchema![column.related!.field] const fromField = $enrichedSchema![column.related!.field]
map[column.name] = getRelatedTableValues( map[column.name] = getRelatedTableValues(
row, row,
@ -132,8 +135,24 @@ export const deriveStores = (context: StoreContext): RowDerivedStore => {
fromField fromField
) )
return map return map
}, {}), },
})) {}
)
// Derive any display-only formatted values for this row
const formattedValues = formattedColumns.reduce<Record<string, any>>(
(map, column) => {
map[column.name] = column.format!(row)
return map
},
{}
)
return {
...row,
...relatedValues,
__formatted: formattedValues,
__idx: idx,
}
})
} }
) )
@ -791,6 +810,7 @@ export const createActions = (context: StoreContext): RowActionStore => {
let clone: Row = { ...row } let clone: Row = { ...row }
delete clone.__idx delete clone.__idx
delete clone.__metadata delete clone.__metadata
delete clone.__formatted
if (!get(hasBudibaseIdentifiers) && isGeneratedRowID(clone._id!)) { if (!get(hasBudibaseIdentifiers) && isGeneratedRowID(clone._id!)) {
delete clone._id delete clone._id
} }

View File

@ -1,9 +1,10 @@
import { CalculationType, FieldSchema, FieldType } from "@budibase/types" import { CalculationType, FieldSchema, FieldType, UIRow } from "@budibase/types"
export type UIColumn = FieldSchema & { export type UIColumn = FieldSchema & {
label: string label: string
readonly: boolean readonly: boolean
conditions: any conditions: any
format?: (row: UIRow) => any
related?: { related?: {
field: string field: string
subField: string subField: string

View File

@ -5,6 +5,7 @@ import {
RelationSchemaField, RelationSchemaField,
SortOrder, SortOrder,
Table, Table,
UIRow,
UISearchFilter, UISearchFilter,
} from "@budibase/types" } from "@budibase/types"
@ -27,6 +28,7 @@ export type UIFieldSchema = FieldSchema &
columns?: Record<string, UIRelationSchemaField> columns?: Record<string, UIRelationSchemaField>
cellRenderType?: string cellRenderType?: string
disabled?: boolean disabled?: boolean
format?: (row: UIRow) => any
} }
interface UIRelationSchemaField extends RelationSchemaField { interface UIRelationSchemaField extends RelationSchemaField {