1
0
Fork 0
mirror of synced 2024-09-10 06:26:02 +12:00

Add multi to multi pasting

This commit is contained in:
Andrew Kingston 2024-06-21 20:38:48 +01:00
parent ad0d300ff9
commit 9657781df6
No known key found for this signature in database
2 changed files with 107 additions and 26 deletions

View file

@ -5,9 +5,8 @@ import { parseCellID } from "../lib/utils"
export const createStores = () => {
const clipboard = writable({
value: null,
multiCellMode: false,
multiCellCopy: false,
})
return {
clipboard,
}
@ -16,10 +15,12 @@ export const createStores = () => {
export const deriveStores = context => {
const { clipboard, focusedCellAPI, selectedCellCount } = context
// Derive whether or not we're able to copy
const copyAllowed = derived(focusedCellAPI, $focusedCellAPI => {
return $focusedCellAPI != null
})
// Derive whether or not we're able to paste
const pasteAllowed = derived(
[clipboard, focusedCellAPI, selectedCellCount],
([$clipboard, $focusedCellAPI, $selectedCellCount]) => {
@ -30,7 +31,7 @@ export const deriveStores = context => {
// this cell is readonly
const multiCellPaste = $selectedCellCount > 1
if (
!$clipboard.multiCellMode &&
!$clipboard.multiCellCopy &&
!multiCellPaste &&
$focusedCellAPI.isReadonly()
) {
@ -49,44 +50,93 @@ export const deriveStores = context => {
export const createActions = context => {
const {
clipboard,
selectedCellCount,
focusedCellAPI,
copyAllowed,
pasteAllowed,
rows,
selectedCells,
rowLookupMap,
rowChangeCache,
rows,
columnLookupMap,
} = context
// Copies the currently selected value (or values)
const copy = () => {
if (!get(copyAllowed)) {
return
}
const $selectedCellCount = get(selectedCellCount)
const cellIds = Object.keys(get(selectedCells))
const $focusedCellAPI = get(focusedCellAPI)
const multiCellMode = $selectedCellCount > 1
const multiCellCopy = cellIds.length > 1
// Multiple values to copy
if (multiCellMode) {
// TODO
return
}
if (multiCellCopy) {
const $rowLookupMap = get(rowLookupMap)
const $rowChangeCache = get(rowChangeCache)
const $rows = get(rows)
const $columnLookupMap = get(columnLookupMap)
// Single value to copy
const value = $focusedCellAPI.getValue()
clipboard.set({
value,
multiCellMode,
})
// Go through each selected cell and group all selected cell values by
// their row ID. Order is important for pasting, so we store the index of
// both rows and values.
let map = {}
for (let cellId of cellIds) {
const { id, field } = parseCellID(cellId)
const index = $rowLookupMap[id]
if (!map[id]) {
map[id] = {
order: index,
values: [],
}
}
const row = {
...$rows[index],
...$rowChangeCache[id],
}
const columnIndex = $columnLookupMap[field]
map[id].values.push({
value: row[field],
order: columnIndex,
})
}
// Also copy a stringified version to the clipboard
let stringified = ""
if (value != null && value !== "") {
// Only conditionally stringify to avoid redundant quotes around text
stringified = typeof value === "object" ? JSON.stringify(value) : value
// Sort rows by order
let value = []
const sortedRowValues = Object.values(map)
.toSorted((a, b) => a.order - b.order)
.map(x => x.values)
// Sort all values in each row by order
for (let rowValues of sortedRowValues) {
value.push(
rowValues.toSorted((a, b) => a.order - b.order).map(x => x.value)
)
}
// Update state
clipboard.set({
value,
multiCellCopy: true,
})
} else {
// Single value to copy
const value = $focusedCellAPI.getValue()
clipboard.set({
value,
multiCellCopy,
})
// Also copy a stringified version to the clipboard
let stringified = ""
if (value != null && value !== "") {
// Only conditionally stringify to avoid redundant quotes around text
stringified = typeof value === "object" ? JSON.stringify(value) : value
}
Helpers.copyToClipboard(stringified)
}
Helpers.copyToClipboard(stringified)
}
// Pastes the previously copied value(s) into the selected cell(s)
const paste = async () => {
if (!get(pasteAllowed)) {
return
@ -98,14 +148,45 @@ export const createActions = context => {
}
// Check if we're pasting into one or more cells
const cellIds = Object.keys(get(selectedCells))
const $selectedCells = get(selectedCells)
const cellIds = Object.keys($selectedCells)
const multiCellPaste = cellIds.length > 1
if ($clipboard.multiCellMode) {
if ($clipboard.multiCellCopy) {
if (multiCellPaste) {
// Multi to multi (only paste selected cells)
const value = $clipboard.value
// Find the top left index so we can find the relative offset for each
// cell
let rowIndices = []
let columnIndices = []
for (let cellId of cellIds) {
rowIndices.push($selectedCells[cellId].rowIdx)
columnIndices.push($selectedCells[cellId].colIdx)
}
const minRowIdx = Math.min(...rowIndices)
const minColIdx = Math.min(...columnIndices)
// Build change map of values to patch
let changeMap = {}
const $rowLookupMap = get(rowLookupMap)
const $columnLookupMap = get(columnLookupMap)
for (let cellId of cellIds) {
const { id, field } = parseCellID(cellId)
const rowIdx = $rowLookupMap[id] - minRowIdx
const colIdx = $columnLookupMap[field] - minColIdx
if (colIdx in (value[rowIdx] || [])) {
if (!changeMap[id]) {
changeMap[id] = {}
}
changeMap[id][field] = value[rowIdx][colIdx]
}
}
await rows.actions.bulkUpdate(changeMap)
} else {
// Multi to single (expand to paste all values)
// TODO
}
} else {
if (multiCellPaste) {

View file

@ -60,7 +60,7 @@ export const deriveStores = context => {
rowId = $rows[rowIdx]._id
colName = $allVisibleColumns[colIdx].name
cellId = getCellID(rowId, colName)
map[cellId] = true
map[cellId] = { rowIdx, colIdx }
}
}
return map