Simplify and improve bulk pasting logic

This commit is contained in:
Andrew Kingston 2024-06-23 13:35:45 +01:00
parent 9c360a1f02
commit 70fd643431
No known key found for this signature in database
3 changed files with 76 additions and 44 deletions

View File

@ -95,9 +95,6 @@
selectedCells.actions.stopSelecting() selectedCells.actions.stopSelecting()
return return
} }
if ($focusedCellId) {
focusedCellId.set(null)
}
selectedCells.actions.updateTarget(cellId) selectedCells.actions.updateTarget(cellId)
} }
@ -109,7 +106,6 @@
if (e.shiftKey && $focusedCellId) { if (e.shiftKey && $focusedCellId) {
// If we have a focused cell, select the range from that cell to here // If we have a focused cell, select the range from that cell to here
selectedCells.actions.setRange($focusedCellId, cellId) selectedCells.actions.setRange($focusedCellId, cellId)
focusedCellId.set(null)
} else if (e.shiftKey && $selectedCellCount) { } else if (e.shiftKey && $selectedCellCount) {
// If we already have a selected range of cell, update it // If we already have a selected range of cell, update it
selectedCells.actions.updateTarget(cellId) selectedCells.actions.updateTarget(cellId)

View File

@ -1,6 +1,6 @@
import { derived, writable, get } from "svelte/store" import { derived, writable, get } from "svelte/store"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
import { parseCellID } from "../lib/utils" import { parseCellID, getCellID } from "../lib/utils"
export const createStores = () => { export const createStores = () => {
const clipboard = writable({ const clipboard = writable({
@ -62,6 +62,9 @@ export const createActions = context => {
rowLookupMap, rowLookupMap,
rowChangeCache, rowChangeCache,
rows, rows,
focusedCellId,
columnLookupMap,
allVisibleColumns,
} = context } = context
// Copies the currently selected value (or values) // Copies the currently selected value (or values)
@ -125,57 +128,86 @@ export const createActions = context => {
return return
} }
const { value, multiCellCopy } = get(clipboard) const { value, multiCellCopy } = get(clipboard)
const $focusedCellAPI = get(focusedCellAPI) const multiCellPaste = get(selectedCellCount) > 1
const $selectedCells = get(selectedCells)
const $selectedCellCount = get(selectedCellCount)
const multiCellPaste = $selectedCellCount > 1
// Choose paste strategy // Choose paste strategy
if (multiCellCopy) { if (multiCellCopy) {
if (multiCellPaste) { if (multiCellPaste) {
// Multi to multi (only paste selected cells) // Multi to multi (only paste selected cells)
// Find the extent at which we can paste await pasteIntoSelectedCells(value)
const rowExtent = Math.min(value.length, $selectedCells.length)
const colExtent = Math.min(value[0].length, $selectedCells[0].length)
// Build change map
let changeMap = {}
for (let rowIdx = 0; rowIdx < rowExtent; rowIdx++) {
for (let colIdx = 0; colIdx < colExtent; colIdx++) {
const cellId = $selectedCells[rowIdx][colIdx]
const { id, field } = parseCellID(cellId)
if (!changeMap[id]) {
changeMap[id] = {}
}
changeMap[id][field] = value[rowIdx][colIdx]
}
}
await rows.actions.bulkUpdate(changeMap)
} else { } else {
// Multi to single (expand to paste all values) // Multi to single (expand to paste all values)
// TODO // Get indices of focused cell
const $focusedCellId = get(focusedCellId)
const { id, field } = parseCellID($focusedCellId)
const $rowLookupMap = get(rowLookupMap)
const $columnLookupMap = get(columnLookupMap)
const rowIdx = $rowLookupMap[id]
const colIdx = $columnLookupMap[field]
// Get limits of how many rows and columns we're able to paste into
const $rows = get(rows)
const $allVisibleColumns = get(allVisibleColumns)
const colCount = $allVisibleColumns.length
const rowCount = $rows.length
const selectedRows = value.length
const selectedColumns = value[0].length
const rowExtent = Math.min(selectedRows, rowCount - rowIdx) - 1
const colExtent = Math.min(selectedColumns, colCount - colIdx) - 1
// Get the target cell ID (bottom right of our pastable extent)
const targetRowId = $rows[rowIdx + rowExtent]._id
const targetColName = $allVisibleColumns[colIdx + colExtent].name
const targetCellId = getCellID(targetRowId, targetColName)
// Paste into target cell range
if (targetCellId === $focusedCellId) {
// Single cell edge case
get(focusedCellAPI).setValue(value[0][0])
} else {
// Select the new cells to paste into, then paste
selectedCells.actions.updateTarget(targetCellId)
await pasteIntoSelectedCells(value)
}
} }
} else { } else {
if (multiCellPaste) { if (multiCellPaste) {
// Single to multi (duplicate value in all selected cells) // Single to multi (duplicate value in all selected cells)
let changeMap = {} const $selectedCells = get(selectedCells)
for (let row of $selectedCells) { const pastableValue = $selectedCells.map(row => {
for (let cellId of row) { return row.map(() => value)
const { id, field } = parseCellID(cellId) })
if (!changeMap[id]) { await pasteIntoSelectedCells(pastableValue)
changeMap[id] = {}
}
changeMap[id][field] = value
}
}
await rows.actions.bulkUpdate(changeMap)
} else { } else {
// Single to single // Single to single
$focusedCellAPI.setValue(value) get(focusedCellAPI).setValue(value)
} }
} }
} }
// Paste the specified value into the currently selected cells
const pasteIntoSelectedCells = async value => {
const $selectedCells = get(selectedCells)
// Find the extent at which we can paste
const rowExtent = Math.min(value.length, $selectedCells.length)
const colExtent = Math.min(value[0].length, $selectedCells[0].length)
// Build change map
let changeMap = {}
for (let rowIdx = 0; rowIdx < rowExtent; rowIdx++) {
for (let colIdx = 0; colIdx < colExtent; colIdx++) {
const cellId = $selectedCells[rowIdx][colIdx]
const { id, field } = parseCellID(cellId)
if (!changeMap[id]) {
changeMap[id] = {}
}
changeMap[id][field] = value[rowIdx][colIdx]
}
}
await rows.actions.bulkUpdate(changeMap)
}
return { return {
clipboard: { clipboard: {
...clipboard, ...clipboard,

View File

@ -217,9 +217,8 @@ export const createActions = context => {
toggleSelectedRow(id) toggleSelectedRow(id)
return return
} }
// There should always be a last selected index
if (lastSelectedIndex == null) { if (lastSelectedIndex == null) {
throw "NO LAST SELECTED INDEX" return
} }
const thisIndex = get(rowLookupMap)[id] const thisIndex = get(rowLookupMap)[id]
@ -417,10 +416,15 @@ export const initialise = context => {
} }
}) })
// Clear selected rows when selecting cells // Clear state when selecting cells
selectedCellCount.subscribe($selectedCellCount => { selectedCellCount.subscribe($selectedCellCount => {
if ($selectedCellCount && get(selectedRowCount)) { if ($selectedCellCount) {
selectedRows.set({}) if (get(selectedRowCount)) {
selectedRows.set({})
}
if (get(focusedCellId)) {
focusedCellId.set(null)
}
} }
}) })
} }