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
|
// 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 $rows = get(rows)
|
||||||
const $rowLookupMap = get(rowLookupMap)
|
const $rowLookupMap = get(rowLookupMap)
|
||||||
const index = $rowLookupMap[rowId]
|
const index = $rowLookupMap[rowId]
|
||||||
const row = $rows[index]
|
const row = $rows[index]
|
||||||
if (index == null || !Object.keys(changes || {}).length) {
|
if (index == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!options?.force && !Object.keys(changes || {}).length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abandon if no changes
|
// Abandon if no changes
|
||||||
let same = true
|
if (!options?.force) {
|
||||||
for (let column of Object.keys(changes)) {
|
let same = true
|
||||||
if (row[column] !== changes[column]) {
|
for (let column of Object.keys(changes)) {
|
||||||
same = false
|
if (row[column] !== changes[column]) {
|
||||||
break
|
same = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (same) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (same) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Immediately update state so that the change is reflected
|
// 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
|
// Stop here if we don't want to persist the change
|
||||||
if (!options?.save) {
|
if (!options?.save && !options?.force) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,7 +517,14 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialise = 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
|
// Wipe the row change cache when changing row
|
||||||
previousFocusedRowId.subscribe(id => {
|
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 hoveredRowId = writable(null)
|
||||||
const rowHeight = writable(get(props).fixedRowHeight || DefaultRowHeight)
|
const rowHeight = writable(get(props).fixedRowHeight || DefaultRowHeight)
|
||||||
const previousFocusedRowId = writable(null)
|
const previousFocusedRowId = writable(null)
|
||||||
|
const previousFocusedCellId = writable(null)
|
||||||
const gridFocused = writable(false)
|
const gridFocused = writable(false)
|
||||||
const isDragging = writable(false)
|
const isDragging = writable(false)
|
||||||
const buttonColumnWidth = writable(0)
|
const buttonColumnWidth = writable(0)
|
||||||
|
@ -48,6 +49,7 @@ export const createStores = context => {
|
||||||
focusedCellAPI,
|
focusedCellAPI,
|
||||||
focusedRowId,
|
focusedRowId,
|
||||||
previousFocusedRowId,
|
previousFocusedRowId,
|
||||||
|
previousFocusedCellId,
|
||||||
hoveredRowId,
|
hoveredRowId,
|
||||||
rowHeight,
|
rowHeight,
|
||||||
gridFocused,
|
gridFocused,
|
||||||
|
@ -129,6 +131,7 @@ export const initialise = context => {
|
||||||
const {
|
const {
|
||||||
focusedRowId,
|
focusedRowId,
|
||||||
previousFocusedRowId,
|
previousFocusedRowId,
|
||||||
|
previousFocusedCellId,
|
||||||
rows,
|
rows,
|
||||||
focusedCellId,
|
focusedCellId,
|
||||||
selectedRows,
|
selectedRows,
|
||||||
|
@ -181,6 +184,13 @@ export const initialise = context => {
|
||||||
lastFocusedRowId = id
|
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
|
// Remove hovered row when a cell is selected
|
||||||
focusedCellId.subscribe(cell => {
|
focusedCellId.subscribe(cell => {
|
||||||
if (cell && get(hoveredRowId)) {
|
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 = () => {
|
export const createStores = () => {
|
||||||
const validation = writable({})
|
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) => {
|
const setError = (cellId, error) => {
|
||||||
if (!cellId) {
|
if (!cellId) {
|
||||||
return
|
return
|
||||||
|
@ -13,11 +28,16 @@ export const createStores = () => {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rowHasErrors = rowId => {
|
||||||
|
return get(rowErrorMap)[rowId]
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
validation: {
|
validation: {
|
||||||
...validation,
|
...validation,
|
||||||
actions: {
|
actions: {
|
||||||
setError,
|
setError,
|
||||||
|
rowHasErrors,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue