Ensure unsaved pending changes to rows are applied when changing cell
This commit is contained in:
parent
9a9b745795
commit
e876d14b92
|
@ -328,25 +328,34 @@ 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
|
||||
let same = true
|
||||
for (let column of Object.keys(changes)) {
|
||||
if (row[column] !== changes[column]) {
|
||||
same = false
|
||||
break
|
||||
if (!options?.force) {
|
||||
let same = true
|
||||
for (let column of Object.keys(changes)) {
|
||||
if (row[column] !== changes[column]) {
|
||||
same = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (same) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (same) {
|
||||
return
|
||||
}
|
||||
|
||||
// Immediately update state so that the change is reflected
|
||||
|
@ -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 })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue