Refactor how grid settings are used and add option for row height

This commit is contained in:
Andrew Kingston 2023-06-19 17:24:40 +01:00
parent 3f3e9fca56
commit d77b2c6ab1
12 changed files with 168 additions and 73 deletions

View File

@ -5230,9 +5230,7 @@
"block": true, "block": true,
"name": "Grid block", "name": "Grid block",
"icon": "Table", "icon": "Table",
"styles": [ "styles": ["size"],
"size"
],
"size": { "size": {
"width": 600, "width": 600,
"height": 400 "height": 400
@ -5245,23 +5243,49 @@
"key": "table", "key": "table",
"required": true "required": true
}, },
{
"type": "columns",
"label": "Columns",
"key": "columns",
"dependsOn": "table"
},
{ {
"type": "filter", "type": "filter",
"label": "Filtering", "label": "Filtering",
"key": "filter" "key": "initialFilter"
}, },
{ {
"type": "field/sortable", "type": "field/sortable",
"label": "Sort column", "label": "Sort column",
"key": "sortColumn" "key": "initialSortColumn"
}, },
{ {
"type": "select", "type": "select",
"label": "Sort order", "label": "Sort order",
"key": "sortOrder", "key": "initialSortOrder",
"options": ["Ascending", "Descending"], "options": ["Ascending", "Descending"],
"defaultValue": "Ascending" "defaultValue": "Ascending"
}, },
{
"type": "select",
"label": "Row height",
"key": "initialRowHeight",
"placeholder": "Default",
"options": [
{
"label": "Small",
"value": 36
},
{
"label": "Medium",
"value": 64
},
{
"label": "Large",
"value": 92
}
]
},
{ {
"type": "boolean", "type": "boolean",
"label": "Add rows", "label": "Add rows",

View File

@ -9,9 +9,10 @@
export let allowEditRows = true export let allowEditRows = true
export let allowDeleteRows = true export let allowDeleteRows = true
export let stripeRows = false export let stripeRows = false
export let filter = null export let initialFilter = null
export let sortColumn = null export let initialSortColumn = null
export let sortOrder = null export let initialSortOrder = null
export let initialRowHeight = null
const component = getContext("component") const component = getContext("component")
const { styleable, API, builderStore } = getContext("sdk") const { styleable, API, builderStore } = getContext("sdk")
@ -28,9 +29,10 @@
{allowEditRows} {allowEditRows}
{allowDeleteRows} {allowDeleteRows}
{stripeRows} {stripeRows}
{filter} {initialFilter}
{sortColumn} {initialSortColumn}
{sortOrder} {initialSortOrder}
{initialRowHeight}
showControls={false} showControls={false}
allowExpandRows={false} allowExpandRows={false}
allowSchemaChanges={false} allowSchemaChanges={false}

View File

@ -33,22 +33,6 @@
selected: allLarge, selected: allLarge,
}, },
] ]
const changeColumnWidth = async width => {
columns.update(state => {
state.forEach(column => {
column.width = width
})
return state
})
if ($stickyColumn) {
stickyColumn.update(state => ({
...state,
width,
}))
}
await columns.actions.saveChanges()
}
</script> </script>
<div bind:this={anchor}> <div bind:this={anchor}>
@ -70,7 +54,7 @@
{#each sizeOptions as option} {#each sizeOptions as option}
<ActionButton <ActionButton
quiet quiet
on:click={() => changeColumnWidth(option.size)} on:click={() => columns.actions.changeAllColumnWidths(option.size)}
selected={option.selected} selected={option.selected}
> >
{option.label} {option.label}

View File

@ -29,7 +29,6 @@
GutterWidth, GutterWidth,
DefaultRowHeight, DefaultRowHeight,
} from "../lib/constants" } from "../lib/constants"
import { memo } from "../../../utils"
export let API = null export let API = null
export let tableId = null export let tableId = null
@ -43,48 +42,26 @@
export let collaboration = true export let collaboration = true
export let showAvatars = true export let showAvatars = true
export let showControls = true export let showControls = true
export let filter = null export let initialFilter = null
export let sortColumn = null export let initialSortColumn = null
export let sortOrder = null export let initialSortOrder = null
export let initialRowHeight = null
// Unique identifier for DOM nodes inside this instance // Unique identifier for DOM nodes inside this instance
const rand = Math.random() const rand = Math.random()
// Stores derived from props.
// We use memo here to ensure redundant store reactions don't fire and cause
// wasted API calls.
const tableIdStore = memo(tableId)
const schemaOverridesStore = memo(schemaOverrides)
const filterStore = memo(filter)
const sortStore = memo({
column: sortColumn,
order: sortOrder,
})
const config = memo({
allowAddRows,
allowExpandRows,
allowEditRows,
allowDeleteRows,
allowSchemaChanges,
stripeRows,
showControls,
})
// Build up context // Build up context
let context = { let context = {
API: API || createAPIClient(), API: API || createAPIClient(),
rand, rand,
config, props: $$props,
tableId: tableIdStore,
schemaOverrides: schemaOverridesStore,
filter: filterStore,
sort: sortStore,
} }
context = { ...context, ...createEventManagers() } context = { ...context, ...createEventManagers() }
context = attachStores(context) context = attachStores(context)
// Reference some stores for local use // Reference some stores for local use
const { const {
config,
isResizing, isResizing,
isReordering, isReordering,
ui, ui,
@ -95,22 +72,23 @@
gridFocused, gridFocused,
} = context } = context
// Keep prop-derived stores up to date // Keep config store up to date with props
$: tableIdStore.set(tableId)
$: schemaOverridesStore.set(schemaOverrides)
$: filterStore.set(filter)
$: sortStore.set({
column: sortColumn,
order: sortOrder,
})
$: config.set({ $: config.set({
tableId,
schemaOverrides,
allowAddRows, allowAddRows,
allowSchemaChanges,
allowExpandRows, allowExpandRows,
allowEditRows, allowEditRows,
allowDeleteRows, allowDeleteRows,
allowSchemaChanges,
stripeRows, stripeRows,
collaboration,
showAvatars,
showControls, showControls,
initialFilter,
initialSortColumn,
initialSortOrder,
initialRowHeight,
}) })
// Set context for children to consume // Set context for children to consume

View File

@ -56,6 +56,23 @@ export const deriveStores = context => {
}) })
} }
// Updates the width of all columns
const changeAllColumnWidths = async width => {
columns.update(state => {
return state.map(col => ({
...col,
width,
}))
})
if (get(stickyColumn)) {
stickyColumn.update(state => ({
...state,
width,
}))
}
await saveChanges()
}
// Persists column changes by saving metadata against table schema // Persists column changes by saving metadata against table schema
const saveChanges = async () => { const saveChanges = async () => {
const $columns = get(columns) const $columns = get(columns)
@ -107,6 +124,7 @@ export const deriveStores = context => {
saveChanges, saveChanges,
saveTable, saveTable,
changePrimaryDisplay, changePrimaryDisplay,
changeAllColumnWidths,
}, },
}, },
} }

View File

@ -0,0 +1,23 @@
import { writable } from "svelte/store"
import { derivedMemo } from "../../../utils"
export const createStores = context => {
const config = writable(context.props)
const getProp = prop => derivedMemo(config, $config => $config[prop])
// Derive and memoize some props so that we can react to them in isolation
const tableId = getProp("tableId")
const initialSortColumn = getProp("initialSortColumn")
const initialSortOrder = getProp("initialSortOrder")
const initialFilter = getProp("initialFilter")
const initialRowHeight = getProp("initialRowHeight")
return {
config,
tableId,
initialSortColumn,
initialSortOrder,
initialFilter,
initialRowHeight,
}
}

View File

@ -0,0 +1,20 @@
import { writable } from "svelte/store"
import { derivedMemo } from "../../../utils"
export const createStores = context => {
const { props } = context
// Initialise to default props
const filter = writable(props.initialFilter)
return {
filter,
}
}
export const initialise = context => {
const { filter, initialFilter } = context
// Reset filter when initial filter prop changes
initialFilter.subscribe(filter.set)
}

View File

@ -11,8 +11,14 @@ import * as Users from "./users"
import * as Validation from "./validation" import * as Validation from "./validation"
import * as Viewport from "./viewport" import * as Viewport from "./viewport"
import * as Clipboard from "./clipboard" import * as Clipboard from "./clipboard"
import * as Config from "./config"
import * as Sort from "./sort"
import * as Filter from "./filter"
const DependencyOrderedStores = [ const DependencyOrderedStores = [
Config,
Sort,
Filter,
Bounds, Bounds,
Scroll, Scroll,
Rows, Rows,

View File

@ -0,0 +1,27 @@
import { writable } from "svelte/store"
export const createStores = context => {
const { props } = context
// Initialise to default props
const sort = writable({
column: props.initialSortColumn,
order: props.initialSortOrder || "ascending",
})
return {
sort,
}
}
export const initialise = context => {
const { sort, initialSortColumn, initialSortOrder } = context
// Reset sort when initial sort props change
initialSortColumn.subscribe(newSortColumn => {
sort.update(state => ({ ...state, column: newSortColumn }))
})
initialSortOrder.subscribe(newSortOrder => {
sort.update(state => ({ ...state, order: newSortOrder }))
})
}

View File

@ -8,12 +8,13 @@ import {
NewRowID, NewRowID,
} from "../lib/constants" } from "../lib/constants"
export const createStores = () => { export const createStores = context => {
const { props } = context
const focusedCellId = writable(null) const focusedCellId = writable(null)
const focusedCellAPI = writable(null) const focusedCellAPI = writable(null)
const selectedRows = writable({}) const selectedRows = writable({})
const hoveredRowId = writable(null) const hoveredRowId = writable(null)
const rowHeight = writable(DefaultRowHeight) const rowHeight = writable(props.initialRowHeight || DefaultRowHeight)
const previousFocusedRowId = writable(null) const previousFocusedRowId = writable(null)
const gridFocused = writable(false) const gridFocused = writable(false)
const isDragging = writable(false) const isDragging = writable(false)
@ -98,9 +99,9 @@ export const deriveStores = context => {
// Derive the amount of content lines to show in cells depending on row height // Derive the amount of content lines to show in cells depending on row height
const contentLines = derived(rowHeight, $rowHeight => { const contentLines = derived(rowHeight, $rowHeight => {
if ($rowHeight === LargeRowHeight) { if ($rowHeight >= LargeRowHeight) {
return 3 return 3
} else if ($rowHeight === MediumRowHeight) { } else if ($rowHeight >= MediumRowHeight) {
return 2 return 2
} }
return 1 return 1
@ -133,6 +134,7 @@ export const initialise = context => {
hoveredRowId, hoveredRowId,
table, table,
rowHeight, rowHeight,
initialRowHeight,
} = context } = context
// Ensure we clear invalid rows from state if they disappear // Ensure we clear invalid rows from state if they disappear
@ -189,4 +191,7 @@ export const initialise = context => {
table.subscribe($table => { table.subscribe($table => {
rowHeight.set($table?.rowHeight || DefaultRowHeight) rowHeight.set($table?.rowHeight || DefaultRowHeight)
}) })
// Reset row height when initial row height prop changes
initialRowHeight.subscribe(rowHeight.set)
} }

View File

@ -3,5 +3,5 @@ export * as JSONUtils from "./json"
export * as CookieUtils from "./cookies" export * as CookieUtils from "./cookies"
export * as RoleUtils from "./roles" export * as RoleUtils from "./roles"
export * as Utils from "./utils" export * as Utils from "./utils"
export { memo } from "./memo" export { memo, derivedMemo } from "./memo"
export { createWebsocket } from "./websocket" export { createWebsocket } from "./websocket"

View File

@ -1,4 +1,4 @@
import { writable, get } from "svelte/store" import { writable, get, derived } from "svelte/store"
// A simple svelte store which deeply compares all changes and ensures that // A simple svelte store which deeply compares all changes and ensures that
// subscribed children will only fire when a new value is actually set // subscribed children will only fire when a new value is actually set
@ -33,3 +33,11 @@ export const memo = initialValue => {
}, },
} }
} }
// Enriched version of svelte's derived store which returns a memo
export const derivedMemo = (store, derivation) => {
const derivedStore = derived(store, derivation)
const memoStore = memo(get(derivedStore))
derivedStore.subscribe(memoStore.set)
return memoStore
}