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,
"name": "Grid block",
"icon": "Table",
"styles": [
"size"
],
"styles": ["size"],
"size": {
"width": 600,
"height": 400
@ -5245,23 +5243,49 @@
"key": "table",
"required": true
},
{
"type": "columns",
"label": "Columns",
"key": "columns",
"dependsOn": "table"
},
{
"type": "filter",
"label": "Filtering",
"key": "filter"
"key": "initialFilter"
},
{
"type": "field/sortable",
"label": "Sort column",
"key": "sortColumn"
"key": "initialSortColumn"
},
{
"type": "select",
"label": "Sort order",
"key": "sortOrder",
"key": "initialSortOrder",
"options": ["Ascending", "Descending"],
"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",
"label": "Add rows",

View File

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

View File

@ -33,22 +33,6 @@
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>
<div bind:this={anchor}>
@ -70,7 +54,7 @@
{#each sizeOptions as option}
<ActionButton
quiet
on:click={() => changeColumnWidth(option.size)}
on:click={() => columns.actions.changeAllColumnWidths(option.size)}
selected={option.selected}
>
{option.label}

View File

@ -29,7 +29,6 @@
GutterWidth,
DefaultRowHeight,
} from "../lib/constants"
import { memo } from "../../../utils"
export let API = null
export let tableId = null
@ -43,48 +42,26 @@
export let collaboration = true
export let showAvatars = true
export let showControls = true
export let filter = null
export let sortColumn = null
export let sortOrder = null
export let initialFilter = null
export let initialSortColumn = null
export let initialSortOrder = null
export let initialRowHeight = null
// Unique identifier for DOM nodes inside this instance
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
let context = {
API: API || createAPIClient(),
rand,
config,
tableId: tableIdStore,
schemaOverrides: schemaOverridesStore,
filter: filterStore,
sort: sortStore,
props: $$props,
}
context = { ...context, ...createEventManagers() }
context = attachStores(context)
// Reference some stores for local use
const {
config,
isResizing,
isReordering,
ui,
@ -95,22 +72,23 @@
gridFocused,
} = context
// Keep prop-derived stores up to date
$: tableIdStore.set(tableId)
$: schemaOverridesStore.set(schemaOverrides)
$: filterStore.set(filter)
$: sortStore.set({
column: sortColumn,
order: sortOrder,
})
// Keep config store up to date with props
$: config.set({
tableId,
schemaOverrides,
allowAddRows,
allowSchemaChanges,
allowExpandRows,
allowEditRows,
allowDeleteRows,
allowSchemaChanges,
stripeRows,
collaboration,
showAvatars,
showControls,
initialFilter,
initialSortColumn,
initialSortOrder,
initialRowHeight,
})
// 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
const saveChanges = async () => {
const $columns = get(columns)
@ -107,6 +124,7 @@ export const deriveStores = context => {
saveChanges,
saveTable,
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 Viewport from "./viewport"
import * as Clipboard from "./clipboard"
import * as Config from "./config"
import * as Sort from "./sort"
import * as Filter from "./filter"
const DependencyOrderedStores = [
Config,
Sort,
Filter,
Bounds,
Scroll,
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,
} from "../lib/constants"
export const createStores = () => {
export const createStores = context => {
const { props } = context
const focusedCellId = writable(null)
const focusedCellAPI = writable(null)
const selectedRows = writable({})
const hoveredRowId = writable(null)
const rowHeight = writable(DefaultRowHeight)
const rowHeight = writable(props.initialRowHeight || DefaultRowHeight)
const previousFocusedRowId = writable(null)
const gridFocused = 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
const contentLines = derived(rowHeight, $rowHeight => {
if ($rowHeight === LargeRowHeight) {
if ($rowHeight >= LargeRowHeight) {
return 3
} else if ($rowHeight === MediumRowHeight) {
} else if ($rowHeight >= MediumRowHeight) {
return 2
}
return 1
@ -133,6 +134,7 @@ export const initialise = context => {
hoveredRowId,
table,
rowHeight,
initialRowHeight,
} = context
// Ensure we clear invalid rows from state if they disappear
@ -189,4 +191,7 @@ export const initialise = context => {
table.subscribe($table => {
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 RoleUtils from "./roles"
export * as Utils from "./utils"
export { memo } from "./memo"
export { memo, derivedMemo } from "./memo"
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
// 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
}