Ensure unsaved pending changes to rows are applied when changing cell

This commit is contained in:
Andrew Kingston 2024-02-28 16:43:41 +00:00
parent 9a9b745795
commit e876d14b92
3 changed files with 70 additions and 13 deletions

View File

@ -328,16 +328,24 @@ export const createActions = context => {
}
// Patches a row with some changes
const updateRow = async (rowId, changes, options = { save: true }) => {
const updateRow = async (
rowId,
changes,
options = { save: true, force: false }
) => {
const $rows = get(rows)
const $rowLookupMap = get(rowLookupMap)
const index = $rowLookupMap[rowId]
const row = $rows[index]
if (index == null || !Object.keys(changes || {}).length) {
if (index == null) {
return
}
if (!options?.force && !Object.keys(changes || {}).length) {
return
}
// Abandon if no changes
if (!options?.force) {
let same = true
for (let column of Object.keys(changes)) {
if (row[column] !== changes[column]) {
@ -348,6 +356,7 @@ export const createActions = context => {
if (same) {
return
}
}
// Immediately update state so that the change is reflected
rowChangeCache.update(state => ({
@ -359,7 +368,7 @@ export const createActions = context => {
}))
// Stop here if we don't want to persist the change
if (!options?.save) {
if (!options?.save && !options?.force) {
return
}
@ -508,7 +517,14 @@ export const createActions = context => {
}
export const initialise = context => {
const { rowChangeCache, inProgressChanges, previousFocusedRowId } = context
const {
rowChangeCache,
inProgressChanges,
previousFocusedRowId,
previousFocusedCellId,
rows,
validation,
} = context
// Wipe the row change cache when changing row
previousFocusedRowId.subscribe(id => {
@ -519,4 +535,15 @@ export const initialise = context => {
})
}
})
// Ensure any unsaved changes are saved when changing cell
previousFocusedCellId.subscribe(id => {
const rowId = id?.split("-")[0]
const hasErrors = validation.actions.rowHasErrors(rowId)
const hasChanges = Object.keys(get(rowChangeCache)[rowId] || {}).length > 0
const isSavingChanges = get(inProgressChanges)[rowId]
if (rowId && !hasErrors && hasChanges && !isSavingChanges) {
rows.actions.updateRow(rowId, null, { force: true })
}
})
}

View File

@ -16,6 +16,7 @@ export const createStores = context => {
const hoveredRowId = writable(null)
const rowHeight = writable(get(props).fixedRowHeight || DefaultRowHeight)
const previousFocusedRowId = writable(null)
const previousFocusedCellId = writable(null)
const gridFocused = writable(false)
const isDragging = writable(false)
const buttonColumnWidth = writable(0)
@ -48,6 +49,7 @@ export const createStores = context => {
focusedCellAPI,
focusedRowId,
previousFocusedRowId,
previousFocusedCellId,
hoveredRowId,
rowHeight,
gridFocused,
@ -129,6 +131,7 @@ export const initialise = context => {
const {
focusedRowId,
previousFocusedRowId,
previousFocusedCellId,
rows,
focusedCellId,
selectedRows,
@ -181,6 +184,13 @@ export const initialise = context => {
lastFocusedRowId = id
})
// Remember the last focused cell ID so that we can store the previous one
let lastFocusedCellId = null
focusedCellId.subscribe(id => {
previousFocusedCellId.set(lastFocusedCellId)
lastFocusedCellId = id
})
// Remove hovered row when a cell is selected
focusedCellId.subscribe(cell => {
if (cell && get(hoveredRowId)) {

View File

@ -1,8 +1,23 @@
import { writable, get } from "svelte/store"
import { writable, get, derived } from "svelte/store"
// Normally we would break out actions into the explicit "createActions"
// function, but for validation all these actions are pure so can go into
// "createStores" instead to make dependency ordering simpler
export const createStores = () => {
const validation = writable({})
// Derive which rows have errors so that we can use that info later
const rowErrorMap = derived(validation, $validation => {
let map = {}
Object.entries($validation).forEach(([key, error]) => {
// Extract row ID from all errored cell IDs
if (error) {
map[key.split("-")[0]] = true
}
})
return map
})
const setError = (cellId, error) => {
if (!cellId) {
return
@ -13,11 +28,16 @@ export const createStores = () => {
}))
}
const rowHasErrors = rowId => {
return get(rowErrorMap)[rowId]
}
return {
validation: {
...validation,
actions: {
setError,
rowHasErrors,
},
},
}