Refactor grid row actions to be more explicit and remove extraneous flags
This commit is contained in:
parent
e876d14b92
commit
acecea5704
|
@ -59,13 +59,13 @@
|
||||||
isReadonly: () => readonly,
|
isReadonly: () => readonly,
|
||||||
getType: () => column.schema.type,
|
getType: () => column.schema.type,
|
||||||
getValue: () => row[column.name],
|
getValue: () => row[column.name],
|
||||||
setValue: (value, options = { save: true }) => {
|
setValue: (value, options = { apply: true }) => {
|
||||||
validation.actions.setError(cellId, null)
|
validation.actions.setError(cellId, null)
|
||||||
updateValue({
|
updateValue({
|
||||||
rowId: row._id,
|
rowId: row._id,
|
||||||
column: column.name,
|
column: column.name,
|
||||||
value,
|
value,
|
||||||
save: options?.save,
|
apply: options?.apply,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,14 +217,14 @@
|
||||||
const type = $focusedCellAPI.getType()
|
const type = $focusedCellAPI.getType()
|
||||||
if (type === "number" && keyCodeIsNumber(keyCode)) {
|
if (type === "number" && keyCodeIsNumber(keyCode)) {
|
||||||
// Update the value locally but don't save it yet
|
// Update the value locally but don't save it yet
|
||||||
$focusedCellAPI.setValue(parseInt(key), { save: false })
|
$focusedCellAPI.setValue(parseInt(key), { apply: false })
|
||||||
$focusedCellAPI.focus()
|
$focusedCellAPI.focus()
|
||||||
} else if (
|
} else if (
|
||||||
["string", "barcodeqr", "longform"].includes(type) &&
|
["string", "barcodeqr", "longform"].includes(type) &&
|
||||||
(keyCodeIsLetter(keyCode) || keyCodeIsNumber(keyCode))
|
(keyCodeIsLetter(keyCode) || keyCodeIsNumber(keyCode))
|
||||||
) {
|
) {
|
||||||
// Update the value locally but don't save it yet
|
// Update the value locally but don't save it yet
|
||||||
$focusedCellAPI.setValue(key, { save: false })
|
$focusedCellAPI.setValue(key, { apply: false })
|
||||||
$focusedCellAPI.focus()
|
$focusedCellAPI.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { fetchData } from "../../../fetch"
|
||||||
import { NewRowID, RowPageSize } from "../lib/constants"
|
import { NewRowID, RowPageSize } from "../lib/constants"
|
||||||
import { tick } from "svelte"
|
import { tick } from "svelte"
|
||||||
import { Helpers } from "@budibase/bbui"
|
import { Helpers } from "@budibase/bbui"
|
||||||
|
import { isValid } from "@budibase/string-templates"
|
||||||
|
|
||||||
export const createStores = () => {
|
export const createStores = () => {
|
||||||
const rows = writable([])
|
const rows = writable([])
|
||||||
|
@ -327,38 +328,31 @@ export const createActions = context => {
|
||||||
get(fetch)?.getInitialData()
|
get(fetch)?.getInitialData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patches a row with some changes
|
// Checks if a changeset for a row actually mutates the row or not
|
||||||
const updateRow = async (
|
const changesAreValid = (row, changes) => {
|
||||||
rowId,
|
const columns = Object.keys(changes || {})
|
||||||
changes,
|
if (!row || !columns.length) {
|
||||||
options = { save: true, force: false }
|
return false
|
||||||
) => {
|
}
|
||||||
|
|
||||||
|
// Ensure there is at least 1 column that creates a difference
|
||||||
|
return columns.some(column => row[column] !== changes[column])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patches a row with some changes in local state, and returns whether a
|
||||||
|
// valid pending change was made or not
|
||||||
|
const stashRowChanges = (rowId, changes) => {
|
||||||
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) {
|
|
||||||
return
|
// Check this is a valid change
|
||||||
}
|
if (!row || !changesAreValid(row, changes)) {
|
||||||
if (!options?.force && !Object.keys(changes || {}).length) {
|
return false
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abandon if no changes
|
// Add change to cache
|
||||||
if (!options?.force) {
|
|
||||||
let same = true
|
|
||||||
for (let column of Object.keys(changes)) {
|
|
||||||
if (row[column] !== changes[column]) {
|
|
||||||
same = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (same) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Immediately update state so that the change is reflected
|
|
||||||
rowChangeCache.update(state => ({
|
rowChangeCache.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
[rowId]: {
|
[rowId]: {
|
||||||
|
@ -366,26 +360,30 @@ export const createActions = context => {
|
||||||
...changes,
|
...changes,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Stop here if we don't want to persist the change
|
// Saves any pending changes to a row
|
||||||
if (!options?.save && !options?.force) {
|
const applyRowChanges = async rowId => {
|
||||||
|
const $rows = get(rows)
|
||||||
|
const $rowLookupMap = get(rowLookupMap)
|
||||||
|
const index = $rowLookupMap[rowId]
|
||||||
|
const row = $rows[index]
|
||||||
|
if (row == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save change
|
// Save change
|
||||||
try {
|
try {
|
||||||
inProgressChanges.update(state => ({
|
// Mark as in progress
|
||||||
...state,
|
inProgressChanges.update(state => ({ ...state, [rowId]: true }))
|
||||||
[rowId]: true,
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Update row
|
// Update row
|
||||||
const saved = await datasource.actions.updateRow({
|
const changes = get(rowChangeCache)[rowId]
|
||||||
...cleanRow(row),
|
const newRow = { ...cleanRow(row), ...changes }
|
||||||
...get(rowChangeCache)[rowId],
|
const saved = await datasource.actions.updateRow(newRow)
|
||||||
})
|
|
||||||
|
|
||||||
// Update state after a successful change
|
// Update row state after a successful change
|
||||||
if (saved?._id) {
|
if (saved?._id) {
|
||||||
rows.update(state => {
|
rows.update(state => {
|
||||||
state[index] = saved
|
state[index] = saved
|
||||||
|
@ -395,6 +393,8 @@ export const createActions = context => {
|
||||||
// Handle users table edge case
|
// Handle users table edge case
|
||||||
await refreshRow(saved.id)
|
await refreshRow(saved.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wipe row change cache now that we've saved the row
|
||||||
rowChangeCache.update(state => {
|
rowChangeCache.update(state => {
|
||||||
delete state[rowId]
|
delete state[rowId]
|
||||||
return state
|
return state
|
||||||
|
@ -402,15 +402,17 @@ export const createActions = context => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleValidationError(rowId, error)
|
handleValidationError(rowId, error)
|
||||||
}
|
}
|
||||||
inProgressChanges.update(state => ({
|
|
||||||
...state,
|
// Mark as completed
|
||||||
[rowId]: false,
|
inProgressChanges.update(state => ({ ...state, [rowId]: false }))
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates a value of a row
|
// Updates a value of a row
|
||||||
const updateValue = async ({ rowId, column, value, save = true }) => {
|
const updateValue = async ({ rowId, column, value, apply = true }) => {
|
||||||
return await updateRow(rowId, { [column]: value }, { save })
|
const success = stashRowChanges(rowId, { [column]: value })
|
||||||
|
if (success && apply) {
|
||||||
|
await applyRowChanges(rowId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes an array of rows
|
// Deletes an array of rows
|
||||||
|
@ -420,9 +422,7 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually delete rows
|
// Actually delete rows
|
||||||
rowsToDelete.forEach(row => {
|
rowsToDelete.forEach(row => delete row.__idx)
|
||||||
delete row.__idx
|
|
||||||
})
|
|
||||||
await datasource.actions.deleteRows(rowsToDelete)
|
await datasource.actions.deleteRows(rowsToDelete)
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
|
@ -442,7 +442,7 @@ export const createActions = context => {
|
||||||
newRow = newRows[i]
|
newRow = newRows[i]
|
||||||
|
|
||||||
// Ensure we have a unique _id.
|
// Ensure we have a unique _id.
|
||||||
// This means generating one for non DS+, overriting any that may already
|
// This means generating one for non DS+, overwriting any that may already
|
||||||
// exist as we cannot allow duplicates.
|
// exist as we cannot allow duplicates.
|
||||||
if (!$isDatasourcePlus) {
|
if (!$isDatasourcePlus) {
|
||||||
newRow._id = Helpers.uuid()
|
newRow._id = Helpers.uuid()
|
||||||
|
@ -503,7 +503,7 @@ export const createActions = context => {
|
||||||
duplicateRow,
|
duplicateRow,
|
||||||
getRow,
|
getRow,
|
||||||
updateValue,
|
updateValue,
|
||||||
updateRow,
|
applyRowChanges,
|
||||||
deleteRows,
|
deleteRows,
|
||||||
hasRow,
|
hasRow,
|
||||||
loadNextPage,
|
loadNextPage,
|
||||||
|
@ -537,13 +537,13 @@ export const initialise = context => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Ensure any unsaved changes are saved when changing cell
|
// Ensure any unsaved changes are saved when changing cell
|
||||||
previousFocusedCellId.subscribe(id => {
|
previousFocusedCellId.subscribe(async id => {
|
||||||
const rowId = id?.split("-")[0]
|
const rowId = id?.split("-")[0]
|
||||||
const hasErrors = validation.actions.rowHasErrors(rowId)
|
const hasErrors = validation.actions.rowHasErrors(rowId)
|
||||||
const hasChanges = Object.keys(get(rowChangeCache)[rowId] || {}).length > 0
|
const hasChanges = Object.keys(get(rowChangeCache)[rowId] || {}).length > 0
|
||||||
const isSavingChanges = get(inProgressChanges)[rowId]
|
const isSavingChanges = get(inProgressChanges)[rowId]
|
||||||
if (rowId && !hasErrors && hasChanges && !isSavingChanges) {
|
if (rowId && !hasErrors && hasChanges && !isSavingChanges) {
|
||||||
rows.actions.updateRow(rowId, null, { force: true })
|
await rows.actions.applyRowChanges(rowId)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue