1
0
Fork 0
mirror of synced 2024-07-06 15:00:49 +12:00

Refactor grid stores and how config is handled

This commit is contained in:
Andrew Kingston 2023-07-27 14:17:26 +01:00
parent c4e4b5c979
commit deb4092cd3
25 changed files with 193 additions and 136 deletions

View file

@ -26,16 +26,14 @@
$: id = $tables.selected?._id
$: isUsersTable = id === TableNames.USERS
$: isInternal = $tables.selected?.type !== "external"
$: datasource = {
$: gridDatasource = {
type: "table",
tableId: id,
}
$: datasource = $datasources.list.find(datasource => {
$: tableDatasource = $datasources.list.find(datasource => {
return datasource._id === $tables.selected?.sourceId
})
$: relationshipsEnabled = relationshipSupport(datasource)
$: relationshipsEnabled = relationshipSupport(tableDatasource)
const relationshipSupport = datasource => {
const integration = $integrations[datasource?.source]
@ -58,7 +56,7 @@
<div class="wrapper">
<Grid
{API}
{datasource}
datasource={gridDatasource}
allowAddRows={!isUsersTable}
allowDeleteRows={!isUsersTable}
schemaOverrides={isUsersTable ? userSchemaOverrides : null}

View file

@ -1,7 +1,6 @@
export const buildViewV2Endpoints = API => ({
/**
* Create a new view
* @param tableId the id of the table where the view will be created
* @param view the view object
*/
create: async view => {
@ -10,9 +9,18 @@ export const buildViewV2Endpoints = API => ({
body: view,
})
},
/**
* Updates a view
* @param view the view object
*/
update: async view => {
return await API.put({
url: `/api/v2/views/${view.id}`,
body: view,
})
},
/**
* Fetches all rows in a view
* @param tableId the id of the table
* @param viewId the id of the view
*/
fetch: async viewId => {
@ -20,7 +28,6 @@ export const buildViewV2Endpoints = API => ({
},
/**
* Delete a view
* @param tableId the id of the table
* @param viewId the id of the view
*/
delete: async viewId => {

View file

@ -34,7 +34,7 @@
column.schema.autocolumn ||
column.schema.disabled ||
column.schema.type === "formula" ||
(!$config.allowEditRows && row._id)
(!$config.canEditRows && row._id)
// Register this cell API if the row is focused
$: {

View file

@ -40,7 +40,7 @@
<div
on:click={select}
class="checkbox"
class:visible={$config.allowDeleteRows &&
class:visible={$config.canDeleteRows &&
(disableNumber || rowSelected || rowHovered || rowFocused)}
>
<Checkbox value={rowSelected} {disabled} />
@ -48,14 +48,14 @@
{#if !disableNumber}
<div
class="number"
class:visible={!$config.allowDeleteRows ||
class:visible={!$config.canDeleteRows ||
!(rowSelected || rowHovered || rowFocused)}
>
{row.__idx + 1}
</div>
{/if}
{/if}
{#if rowSelected && $config.allowDeleteRows}
{#if rowSelected && $config.canDeleteRows}
<div class="delete" on:click={() => dispatch("request-bulk-delete")}>
<Icon
name="Delete"
@ -64,7 +64,7 @@
/>
</div>
{:else}
<div class="expand" class:visible={$config.allowExpandRows && expandable}>
<div class="expand" class:visible={$config.canExpandRows && expandable}>
<Icon
size="S"
name="Maximize"

View file

@ -167,7 +167,7 @@
<MenuItem
icon="Edit"
on:click={editColumn}
disabled={!$config.allowSchemaChanges || column.schema.disabled}
disabled={!$config.canEditColumns || column.schema.disabled}
>
Edit column
</MenuItem>
@ -175,7 +175,7 @@
icon="Label"
on:click={makeDisplayColumn}
disabled={idx === "sticky" ||
!$config.allowSchemaChanges ||
!$config.canEditPrimaryDisplay ||
bannedDisplayColumnTypes.includes(column.schema.type)}
>
Use as display column

View file

@ -1,5 +1,6 @@
<script>
import { setContext, onMount } from "svelte"
import { writable } from "svelte/store"
import { fade } from "svelte/transition"
import { clickOutside, ProgressCircle } from "@budibase/bbui"
import { createEventManagers } from "../lib/events"
@ -35,6 +36,7 @@
export let allowExpandRows = true
export let allowEditRows = true
export let allowDeleteRows = true
export let allowEditColumns = true
export let allowSchemaChanges = true
export let stripeRows = false
export let collaboration = true
@ -50,11 +52,14 @@
// Unique identifier for DOM nodes inside this instance
const rand = Math.random()
// Store props in a store for reference in other stores
const props = writable($$props)
// Build up context
let context = {
API: API || createAPIClient(),
rand,
props: $$props,
props,
}
context = { ...context, ...createEventManagers() }
context = attachStores(context)
@ -71,11 +76,10 @@
contentLines,
gridFocused,
error,
canAddRows,
} = context
// Keep config store up to date with props
$: config.set({
$: props.set({
datasource,
schemaOverrides,
columnWhitelist,
@ -83,6 +87,7 @@
allowExpandRows,
allowEditRows,
allowDeleteRows,
allowEditColumns,
allowSchemaChanges,
stripeRows,
collaboration,
@ -144,7 +149,7 @@
<HeaderRow />
<GridBody />
</div>
{#if $canAddRows}
{#if $config.canAddRows}
<NewRow />
{/if}
<div class="overlays">
@ -168,7 +173,7 @@
<ProgressCircle />
</div>
{/if}
{#if allowDeleteRows}
{#if $config.canDeleteRows}
<BulkDeleteHandler />
{/if}
<KeyboardManager />

View file

@ -9,10 +9,10 @@
renderedRows,
renderedColumns,
rowVerticalInversionIndex,
canAddRows,
hoveredRowId,
dispatch,
isDragging,
config,
} = getContext("grid")
let body
@ -43,7 +43,7 @@
invertY={idx >= $rowVerticalInversionIndex}
/>
{/each}
{#if $canAddRows}
{#if $config.canAddRows}
<div
class="blank"
class:highlighted={$hoveredRowId === BlankRowID}

View file

@ -32,7 +32,7 @@
{/each}
</div>
</GridScrollWrapper>
{#if $config.allowSchemaChanges}
{#if $config.canEditColumns}
{#key $datasource}
<TempTooltip
text="Click here to create your first column"

View file

@ -28,7 +28,7 @@
columnHorizontalInversionIndex,
selectedRows,
loading,
canAddRows,
config,
} = getContext("grid")
let visible = false
@ -154,7 +154,7 @@
condition={hasNoRows && !$loading}
type={TooltipType.Info}
>
{#if !visible && !selectedRowCount && $canAddRows}
{#if !visible && !selectedRowCount && $config.canAddRows}
<div
class="new-row-fab"
on:click={() => dispatch("add-row-inline")}

View file

@ -16,7 +16,7 @@
renderedRows,
focusedCellId,
hoveredRowId,
canAddRows,
config,
selectedCellMap,
focusedRow,
scrollLeft,
@ -92,7 +92,7 @@
{/if}
</div>
{/each}
{#if $canAddRows}
{#if $config.canAddRows}
<div
class="row new"
on:mouseenter={$isDragging

View file

@ -16,7 +16,6 @@
config,
menu,
gridFocused,
canAddRows,
} = getContext("grid")
const ignoredOriginSelectors = [
@ -46,12 +45,12 @@
e.preventDefault()
focusFirstCell()
} else if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
if ($canAddRows) {
if ($config.canAddRows) {
e.preventDefault()
dispatch("add-row-inline")
}
} else if (e.key === "Delete" || e.key === "Backspace") {
if (Object.keys($selectedRows).length && $config.allowDeleteRows) {
if (Object.keys($selectedRows).length && $config.canDeleteRows) {
dispatch("request-bulk-delete")
}
}
@ -100,7 +99,7 @@
}
break
case "Enter":
if ($canAddRows) {
if ($config.canAddRows) {
dispatch("add-row-inline")
}
}
@ -120,7 +119,7 @@
break
case "Delete":
case "Backspace":
if (Object.keys($selectedRows).length && $config.allowDeleteRows) {
if (Object.keys($selectedRows).length && $config.canDeleteRows) {
dispatch("request-bulk-delete")
} else {
deleteSelectedCell()
@ -131,7 +130,7 @@
break
case " ":
case "Space":
if ($config.allowDeleteRows) {
if ($config.canDeleteRows) {
toggleSelectRow()
}
break

View file

@ -17,7 +17,6 @@
focusedCellAPI,
focusedRowId,
notifications,
canAddRows,
} = getContext("grid")
$: style = makeStyle($menu)
@ -68,9 +67,7 @@
</MenuItem>
<MenuItem
icon="Maximize"
disabled={isNewRow ||
!$config.allowEditRows ||
!$config.allowExpandRows}
disabled={isNewRow || !$config.canEditRows || !$config.canExpandRows}
on:click={() => dispatch("edit-row", $focusedRow)}
on:click={menu.actions.close}
>
@ -94,14 +91,14 @@
</MenuItem>
<MenuItem
icon="Duplicate"
disabled={isNewRow || !$canAddRows}
disabled={isNewRow || !$config.canAddRows}
on:click={duplicate}
>
Duplicate row
</MenuItem>
<MenuItem
icon="Delete"
disabled={isNewRow || !$config.allowDeleteRows}
disabled={isNewRow || !$config.canDeleteRows}
on:click={deleteRow}
>
Delete row

View file

@ -8,7 +8,7 @@ export const createStores = () => {
}
}
export const deriveStores = context => {
export const createActions = context => {
const { copiedCell, focusedCellAPI } = context
const copy = () => {

View file

@ -35,20 +35,10 @@ export const createStores = () => {
[]
)
// Checks if we have a certain column by name
const hasColumn = column => {
const $columns = get(columns)
const $sticky = get(stickyColumn)
return $columns.some(col => col.name === column) || $sticky?.name === column
}
return {
columns: {
...columns,
subscribe: enrichedColumns.subscribe,
actions: {
hasColumn,
},
},
stickyColumn,
visibleColumns,
@ -56,10 +46,43 @@ export const createStores = () => {
}
export const deriveStores = context => {
const { columns, stickyColumn } = context
// Derive if we have any normal columns
const hasNonAutoColumn = derived(
[columns, stickyColumn],
([$columns, $stickyColumn]) => {
let allCols = $columns || []
if ($stickyColumn) {
allCols = [...allCols, $stickyColumn]
}
const normalCols = allCols.filter(column => {
return !column.schema?.autocolumn
})
return normalCols.length > 0
}
)
return {
hasNonAutoColumn,
}
}
export const createActions = context => {
const { table, columns, stickyColumn, API, dispatch, config } = context
// Checks if we have a certain column by name
const hasColumn = column => {
const $columns = get(columns)
const $sticky = get(stickyColumn)
return $columns.some(col => col.name === column) || $sticky?.name === column
}
// Updates the tables primary display column
const changePrimaryDisplay = async column => {
if (!get(config).canEditPrimaryDisplay) {
return
}
return await saveTable({
...get(table),
primaryDisplay: column,
@ -83,21 +106,6 @@ export const deriveStores = context => {
await saveChanges()
}
// Derive if we have any normal columns
const hasNonAutoColumn = derived(
[columns, stickyColumn],
([$columns, $stickyColumn]) => {
let allCols = $columns || []
if ($stickyColumn) {
allCols = [...allCols, $stickyColumn]
}
const normalCols = allCols.filter(column => {
return !column.schema?.autocolumn
})
return normalCols.length > 0
}
)
// Persists column changes by saving metadata against table schema
const saveChanges = async () => {
const $columns = get(columns)
@ -133,7 +141,7 @@ export const deriveStores = context => {
table.set(newTable)
// Update server
if (get(config).allowSchemaChanges) {
if (get(config).canSaveSchema) {
await API.saveTable(newTable)
}
@ -143,11 +151,10 @@ export const deriveStores = context => {
}
return {
hasNonAutoColumn,
columns: {
...columns,
actions: {
...columns.actions,
hasColumn,
saveChanges,
saveTable,
changePrimaryDisplay,

View file

@ -1,9 +1,9 @@
import { writable } from "svelte/store"
import { derivedMemo } from "../../../utils"
import { derived } from "svelte/store"
export const createStores = context => {
const config = writable(context.props)
const getProp = prop => derivedMemo(config, $config => $config[prop])
export const deriveStores = context => {
const { props, hasNonAutoColumn } = context
const getProp = prop => derivedMemo(props, $props => $props[prop])
// Derive and memoize some props so that we can react to them in isolation
const datasource = getProp("datasource")
@ -16,6 +16,45 @@ export const createStores = context => {
const notifySuccess = getProp("notifySuccess")
const notifyError = getProp("notifyError")
// Derive features
const config = derived(
[props, hasNonAutoColumn],
([$props, $hasNonAutoColumn]) => {
let config = {
// Row features
canAddRows: $props.allowAddRows,
canExpandRows: $props.allowExpandRows,
canEditRows: $props.allowEditRows,
canDeleteRows: $props.allowDeleteRows,
// Column features
canEditColumns: $props.allowEditColumns,
canEditPrimaryDisplay: $props.allowEditColumns,
canSaveSchema: $props.allowSchemaChanges,
}
// Disable some features if we're editing a view
if ($props.datasource?.type === "viewV2") {
config.canEditPrimaryDisplay = false
config.canEditColumns = false
config.canEditRows = false
config.canDeleteRows = false
config.canAddRows = false
}
console.log($hasNonAutoColumn)
// Disable adding rows if we don't have any valid columns
if (!$hasNonAutoColumn) {
config.canAddRows = false
}
console.log(config)
return config
}
)
return {
config,
datasource,

View file

@ -1,10 +1,10 @@
import { writable } from "svelte/store"
import { writable, get } from "svelte/store"
export const createStores = context => {
const { props } = context
// Initialise to default props
const filter = writable(props.initialFilter)
const filter = writable(get(props).initialFilter)
return {
filter,

View file

@ -17,7 +17,6 @@ import * as Filter from "./filter"
import * as Notifications from "./notifications"
const DependencyOrderedStores = [
Config,
Notifications,
Sort,
Filter,
@ -34,6 +33,7 @@ const DependencyOrderedStores = [
Menu,
Pagination,
Clipboard,
Config,
]
export const attachStores = context => {
@ -47,6 +47,11 @@ export const attachStores = context => {
context = { ...context, ...store.deriveStores?.(context) }
}
// Action creation
for (let store of DependencyOrderedStores) {
context = { ...context, ...store.createActions?.(context) }
}
// Initialise any store logic
for (let store of DependencyOrderedStores) {
store.initialise?.(context)

View file

@ -12,7 +12,7 @@ export const createStores = () => {
}
}
export const deriveStores = context => {
export const createActions = context => {
const { menu, focusedCellId, rand } = context
const open = (cellId, e) => {

View file

@ -23,7 +23,7 @@ export const createStores = () => {
}
}
export const deriveStores = context => {
export const createActions = context => {
const {
reorder,
columns,

View file

@ -19,7 +19,7 @@ export const createStores = () => {
}
}
export const deriveStores = context => {
export const createActions = context => {
const { resize, columns, stickyColumn, ui } = context
// Starts resizing a certain column

View file

@ -38,8 +38,24 @@ export const createStores = () => {
}
})
// Enrich rows with an index property and any pending changes
const enrichedRows = derived(
[rows, rowChangeCache],
([$rows, $rowChangeCache]) => {
return $rows.map((row, idx) => ({
...row,
...$rowChangeCache[row._id],
__idx: idx,
}))
},
[]
)
return {
rows,
rows: {
...rows,
subscribe: enrichedRows.subscribe,
},
rowLookupMap,
table,
loaded,
@ -51,7 +67,7 @@ export const createStores = () => {
}
}
export const deriveStores = context => {
export const createActions = context => {
const {
rows,
rowLookupMap,
@ -71,27 +87,14 @@ export const deriveStores = context => {
hasNextPage,
error,
notifications,
config,
} = context
const instanceLoaded = writable(false)
const fetch = writable(null)
const tableId = writable(null)
// Local cache of row IDs to speed up checking if a row exists
let rowCacheMap = {}
// Enrich rows with an index property and any pending changes
const enrichedRows = derived(
[rows, rowChangeCache],
([$rows, $rowChangeCache]) => {
return $rows.map((row, idx) => ({
...row,
...$rowChangeCache[row._id],
__idx: idx,
}))
},
[]
)
// Reset everything when table ID changes
let unsubscribe = null
let lastResetKey = null
@ -102,6 +105,8 @@ export const deriveStores = context => {
instanceLoaded.set(false)
loading.set(true)
console.log($datasource)
// Abandon if we don't have a valid datasource
if (!$datasource?.tableId) {
return
@ -501,7 +506,6 @@ export const deriveStores = context => {
})
return {
enrichedRows,
rows: {
...rows,
actions: {

View file

@ -1,12 +1,13 @@
import { writable } from "svelte/store"
import { writable, get } from "svelte/store"
export const createStores = context => {
const { props } = context
const $props = get(props)
// Initialise to default props
const sort = writable({
column: props.initialSortColumn,
order: props.initialSortOrder || "ascending",
column: $props.initialSortColumn,
order: $props.initialSortOrder || "ascending",
})
return {

View file

@ -14,7 +14,7 @@ export const createStores = context => {
const focusedCellAPI = writable(null)
const selectedRows = writable({})
const hoveredRowId = writable(null)
const rowHeight = writable(props.fixedRowHeight || DefaultRowHeight)
const rowHeight = writable(get(props).fixedRowHeight || DefaultRowHeight)
const previousFocusedRowId = writable(null)
const gridFocused = writable(false)
const isDragging = writable(false)
@ -61,23 +61,13 @@ export const createStores = context => {
}
export const deriveStores = context => {
const {
focusedCellId,
selectedRows,
hoveredRowId,
enrichedRows,
rowLookupMap,
rowHeight,
stickyColumn,
width,
hasNonAutoColumn,
config,
} = context
const { focusedCellId, rows, rowLookupMap, rowHeight, stickyColumn, width } =
context
// Derive the row that contains the selected cell
const focusedRow = derived(
[focusedCellId, rowLookupMap, enrichedRows],
([$focusedCellId, $rowLookupMap, $enrichedRows]) => {
[focusedCellId, rowLookupMap, rows],
([$focusedCellId, $rowLookupMap, $rows]) => {
const rowId = $focusedCellId?.split("-")[0]
// Edge case for new rows
@ -87,18 +77,11 @@ export const deriveStores = context => {
// All normal rows
const index = $rowLookupMap[rowId]
return $enrichedRows[index]
return $rows[index]
},
null
)
// Callback when leaving the grid, deselecting all focussed or selected items
const blur = () => {
focusedCellId.set(null)
selectedRows.set({})
hoveredRowId.set(null)
}
// Derive the amount of content lines to show in cells depending on row height
const contentLines = derived(rowHeight, $rowHeight => {
if ($rowHeight >= LargeRowHeight) {
@ -114,19 +97,24 @@ export const deriveStores = context => {
return ($stickyColumn?.width || 0) + $width + GutterWidth < 1100
})
// Derive if we're able to add rows
const canAddRows = derived(
[config, hasNonAutoColumn],
([$config, $hasNonAutoColumn]) => {
return $config.allowAddRows && $hasNonAutoColumn
}
)
return {
canAddRows,
focusedRow,
contentLines,
compact,
}
}
export const createActions = context => {
const { focusedCellId, selectedRows, hoveredRowId } = context
// Callback when leaving the grid, deselecting all focussed or selected items
const blur = () => {
focusedCellId.set(null)
selectedRows.set({})
hoveredRowId.set(null)
}
return {
ui: {
actions: {
blur,

View file

@ -39,6 +39,14 @@ export const deriveStores = context => {
}
)
return {
selectedCellMap,
}
}
export const createActions = context => {
const { users } = context
const updateUser = user => {
const $users = get(users)
if (!$users.some(x => x.sessionId === user.sessionId)) {
@ -66,6 +74,5 @@ export const deriveStores = context => {
removeUser,
},
},
selectedCellMap,
}
}

View file

@ -10,7 +10,7 @@ export const deriveStores = context => {
const {
rowHeight,
visibleColumns,
enrichedRows,
rows,
scrollTop,
scrollLeft,
width,
@ -35,9 +35,9 @@ export const deriveStores = context => {
0
)
const renderedRows = derived(
[enrichedRows, scrolledRowCount, visualRowCapacity],
([$enrichedRows, $scrolledRowCount, $visualRowCapacity]) => {
return $enrichedRows.slice(
[rows, scrolledRowCount, visualRowCapacity],
([$rows, $scrolledRowCount, $visualRowCapacity]) => {
return $rows.slice(
$scrolledRowCount,
$scrolledRowCount + $visualRowCapacity
)