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

View File

@ -1,6 +1,6 @@
import { derived, writable, get } from "svelte/store"
import { Helpers } from "@budibase/bbui"
import { parseCellID } from "../lib/utils"
import { parseCellID, getCellID } from "../lib/utils"
export const createStores = () => {
const clipboard = writable({
@ -62,6 +62,9 @@ export const createActions = context => {
rowLookupMap,
rowChangeCache,
rows,
focusedCellId,
columnLookupMap,
allVisibleColumns,
} = context
// Copies the currently selected value (or values)
@ -125,57 +128,86 @@ export const createActions = context => {
return
}
const { value, multiCellCopy } = get(clipboard)
const $focusedCellAPI = get(focusedCellAPI)
const $selectedCells = get(selectedCells)
const $selectedCellCount = get(selectedCellCount)
const multiCellPaste = $selectedCellCount > 1
const multiCellPaste = get(selectedCellCount) > 1
// Choose paste strategy
if (multiCellCopy) {
if (multiCellPaste) {
// Multi to multi (only paste selected cells)
// 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)
await pasteIntoSelectedCells(value)
} else {
// 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 {
if (multiCellPaste) {
// Single to multi (duplicate value in all selected cells)
let changeMap = {}
for (let row of $selectedCells) {
for (let cellId of row) {
const { id, field } = parseCellID(cellId)
if (!changeMap[id]) {
changeMap[id] = {}
}
changeMap[id][field] = value
}
}
await rows.actions.bulkUpdate(changeMap)
const $selectedCells = get(selectedCells)
const pastableValue = $selectedCells.map(row => {
return row.map(() => value)
})
await pasteIntoSelectedCells(pastableValue)
} else {
// 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 {
clipboard: {
...clipboard,

View File

@ -217,9 +217,8 @@ export const createActions = context => {
toggleSelectedRow(id)
return
}
// There should always be a last selected index
if (lastSelectedIndex == null) {
throw "NO LAST SELECTED INDEX"
return
}
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 => {
if ($selectedCellCount && get(selectedRowCount)) {
selectedRows.set({})
if ($selectedCellCount) {
if (get(selectedRowCount)) {
selectedRows.set({})
}
if (get(focusedCellId)) {
focusedCellId.set(null)
}
}
})
}