Simplify new paste logic
This commit is contained in:
parent
b094f0bc31
commit
d4d63c6115
|
@ -86,17 +86,18 @@
|
||||||
if (e.button !== 0) {
|
if (e.button !== 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// focusedCellId.set(cellId)
|
selectedCells.actions.startSelecting(cellId)
|
||||||
selectedCells.actions.start(cellId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateSelection = e => {
|
const updateSelection = () => {
|
||||||
focusedCellId.set(null)
|
if ($focusedCellId) {
|
||||||
selectedCells.actions.update(cellId)
|
focusedCellId.set(null)
|
||||||
|
}
|
||||||
|
selectedCells.actions.updateTarget(cellId)
|
||||||
}
|
}
|
||||||
|
|
||||||
const stopSelection = e => {
|
const stopSelection = () => {
|
||||||
selectedCells.actions.stop()
|
selectedCells.actions.stopSelecting()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,6 @@
|
||||||
onConfirm={clipboard.actions.paste}
|
onConfirm={clipboard.actions.paste}
|
||||||
size="M"
|
size="M"
|
||||||
>
|
>
|
||||||
Are you sure you want to paste values into {$selectedCellCount} cells?
|
Are you sure you want to paste? This will update multiple values.
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -12,15 +12,15 @@
|
||||||
selectedRows,
|
selectedRows,
|
||||||
visibleColumns,
|
visibleColumns,
|
||||||
hoveredRowId,
|
hoveredRowId,
|
||||||
selectedCellMap,
|
|
||||||
focusedRow,
|
focusedRow,
|
||||||
contentLines,
|
contentLines,
|
||||||
isDragging,
|
isDragging,
|
||||||
dispatch,
|
dispatch,
|
||||||
rows,
|
rows,
|
||||||
columnRenderMap,
|
columnRenderMap,
|
||||||
|
userCellMap,
|
||||||
isSelectingCells,
|
isSelectingCells,
|
||||||
selectedCells,
|
selectedCellMap,
|
||||||
selectedCellCount,
|
selectedCellCount,
|
||||||
} = getContext("grid")
|
} = getContext("grid")
|
||||||
|
|
||||||
|
@ -48,12 +48,12 @@
|
||||||
{row}
|
{row}
|
||||||
{rowFocused}
|
{rowFocused}
|
||||||
{rowSelected}
|
{rowSelected}
|
||||||
cellSelected={$selectedCells[cellId]}
|
cellSelected={$selectedCellMap[cellId]}
|
||||||
highlighted={rowHovered || rowFocused || reorderSource === column.name}
|
highlighted={rowHovered || rowFocused || reorderSource === column.name}
|
||||||
rowIdx={row.__idx}
|
rowIdx={row.__idx}
|
||||||
topRow={top}
|
topRow={top}
|
||||||
focused={$focusedCellId === cellId}
|
focused={$focusedCellId === cellId}
|
||||||
selectedUser={$selectedCellMap[cellId]}
|
selectedUser={$userCellMap[cellId]}
|
||||||
width={column.width}
|
width={column.width}
|
||||||
contentLines={$contentLines}
|
contentLines={$contentLines}
|
||||||
hidden={!$columnRenderMap[column.name]}
|
hidden={!$columnRenderMap[column.name]}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
hoveredRowId,
|
hoveredRowId,
|
||||||
config,
|
config,
|
||||||
selectedCellMap,
|
selectedCellMap,
|
||||||
|
userCellMap,
|
||||||
focusedRow,
|
focusedRow,
|
||||||
scrollLeft,
|
scrollLeft,
|
||||||
dispatch,
|
dispatch,
|
||||||
|
@ -91,12 +92,12 @@
|
||||||
{cellId}
|
{cellId}
|
||||||
{rowFocused}
|
{rowFocused}
|
||||||
{rowSelected}
|
{rowSelected}
|
||||||
cellSelected={$selectedCells[cellId]}
|
cellSelected={$selectedCellMap[cellId]}
|
||||||
highlighted={rowHovered || rowFocused}
|
highlighted={rowHovered || rowFocused}
|
||||||
rowIdx={row.__idx}
|
rowIdx={row.__idx}
|
||||||
topRow={idx === 0}
|
topRow={idx === 0}
|
||||||
focused={$focusedCellId === cellId}
|
focused={$focusedCellId === cellId}
|
||||||
selectedUser={$selectedCellMap[cellId]}
|
selectedUser={$userCellMap[cellId]}
|
||||||
width={$stickyColumn.width}
|
width={$stickyColumn.width}
|
||||||
column={$stickyColumn}
|
column={$stickyColumn}
|
||||||
contentLines={$contentLines}
|
contentLines={$contentLines}
|
||||||
|
|
|
@ -13,22 +13,25 @@ export const createStores = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deriveStores = context => {
|
export const deriveStores = context => {
|
||||||
const { clipboard, focusedCellAPI, selectedCellCount } = context
|
const { clipboard, focusedCellAPI, selectedCellCount, config } = context
|
||||||
|
|
||||||
// Derive whether or not we're able to copy
|
// Derive whether or not we're able to copy
|
||||||
const copyAllowed = derived(focusedCellAPI, $focusedCellAPI => {
|
const copyAllowed = derived(
|
||||||
return $focusedCellAPI != null
|
[focusedCellAPI, selectedCellCount],
|
||||||
})
|
([$focusedCellAPI, $selectedCellCount]) => {
|
||||||
|
return $focusedCellAPI || $selectedCellCount
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Derive whether or not we're able to paste
|
// Derive whether or not we're able to paste
|
||||||
const pasteAllowed = derived(
|
const pasteAllowed = derived(
|
||||||
[clipboard, focusedCellAPI, selectedCellCount],
|
[clipboard, focusedCellAPI, selectedCellCount, config],
|
||||||
([$clipboard, $focusedCellAPI, $selectedCellCount]) => {
|
([$clipboard, $focusedCellAPI, $selectedCellCount, $config]) => {
|
||||||
if ($clipboard.value == null || !$focusedCellAPI) {
|
if ($clipboard.value == null || !$config.canEditRows) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Prevent pasting into a single cell, if we have a single cell value and
|
|
||||||
// this cell is readonly
|
// Prevent single-single pasting if the cell is readonly
|
||||||
const multiCellPaste = $selectedCellCount > 1
|
const multiCellPaste = $selectedCellCount > 1
|
||||||
if (
|
if (
|
||||||
!$clipboard.multiCellCopy &&
|
!$clipboard.multiCellCopy &&
|
||||||
|
@ -37,7 +40,8 @@ export const deriveStores = context => {
|
||||||
) {
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
|
return $focusedCellAPI || $selectedCellCount
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,10 +58,10 @@ export const createActions = context => {
|
||||||
copyAllowed,
|
copyAllowed,
|
||||||
pasteAllowed,
|
pasteAllowed,
|
||||||
selectedCells,
|
selectedCells,
|
||||||
|
selectedCellCount,
|
||||||
rowLookupMap,
|
rowLookupMap,
|
||||||
rowChangeCache,
|
rowChangeCache,
|
||||||
rows,
|
rows,
|
||||||
columnLookupMap,
|
|
||||||
} = context
|
} = context
|
||||||
|
|
||||||
// Copies the currently selected value (or values)
|
// Copies the currently selected value (or values)
|
||||||
|
@ -65,52 +69,31 @@ export const createActions = context => {
|
||||||
if (!get(copyAllowed)) {
|
if (!get(copyAllowed)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const cellIds = Object.keys(get(selectedCells))
|
const $selectedCells = get(selectedCells)
|
||||||
const $focusedCellAPI = get(focusedCellAPI)
|
const $focusedCellAPI = get(focusedCellAPI)
|
||||||
const multiCellCopy = cellIds.length > 1
|
const $selectedCellCount = get(selectedCellCount)
|
||||||
|
const multiCellCopy = $selectedCellCount > 1
|
||||||
|
|
||||||
// Multiple values to copy
|
// Multiple values to copy
|
||||||
if (multiCellCopy) {
|
if (multiCellCopy) {
|
||||||
const $rowLookupMap = get(rowLookupMap)
|
const $rowLookupMap = get(rowLookupMap)
|
||||||
const $rowChangeCache = get(rowChangeCache)
|
const $rowChangeCache = get(rowChangeCache)
|
||||||
const $rows = get(rows)
|
const $rows = get(rows)
|
||||||
const $columnLookupMap = get(columnLookupMap)
|
|
||||||
|
|
||||||
// Go through each selected cell and group all selected cell values by
|
// Extract value of each selected cell
|
||||||
// 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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort rows by order
|
|
||||||
let value = []
|
let value = []
|
||||||
const sortedRowValues = Object.values(map)
|
for (let row of $selectedCells) {
|
||||||
.toSorted((a, b) => a.order - b.order)
|
const rowValues = []
|
||||||
.map(x => x.values)
|
for (let cellId of row) {
|
||||||
|
const { id, field } = parseCellID(cellId)
|
||||||
// Sort all values in each row by order
|
const rowIndex = $rowLookupMap[id]
|
||||||
for (let rowValues of sortedRowValues) {
|
const row = {
|
||||||
value.push(
|
...$rows[rowIndex],
|
||||||
rowValues.toSorted((a, b) => a.order - b.order).map(x => x.value)
|
...$rowChangeCache[id],
|
||||||
)
|
}
|
||||||
|
rowValues.push(row[field])
|
||||||
|
}
|
||||||
|
value.push(rowValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
|
@ -141,42 +124,26 @@ export const createActions = context => {
|
||||||
if (!get(pasteAllowed)) {
|
if (!get(pasteAllowed)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const $clipboard = get(clipboard)
|
const { value, multiCellCopy } = get(clipboard)
|
||||||
const $focusedCellAPI = get(focusedCellAPI)
|
const $focusedCellAPI = get(focusedCellAPI)
|
||||||
if ($clipboard.value == null || !$focusedCellAPI) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we're pasting into one or more cells
|
|
||||||
const $selectedCells = get(selectedCells)
|
const $selectedCells = get(selectedCells)
|
||||||
const cellIds = Object.keys($selectedCells)
|
const $selectedCellCount = get(selectedCellCount)
|
||||||
const multiCellPaste = cellIds.length > 1
|
const multiCellPaste = $selectedCellCount > 1
|
||||||
|
|
||||||
if ($clipboard.multiCellCopy) {
|
// Choose paste strategy
|
||||||
|
if (multiCellCopy) {
|
||||||
if (multiCellPaste) {
|
if (multiCellPaste) {
|
||||||
// Multi to multi (only paste selected cells)
|
// Multi to multi (only paste selected cells)
|
||||||
const value = $clipboard.value
|
// 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)
|
||||||
|
|
||||||
// Find the top left index so we can find the relative offset for each
|
// Build change map
|
||||||
// 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 = {}
|
let changeMap = {}
|
||||||
const $rowLookupMap = get(rowLookupMap)
|
for (let rowIdx = 0; rowIdx < rowExtent; rowIdx++) {
|
||||||
const $columnLookupMap = get(columnLookupMap)
|
for (let colIdx = 0; colIdx < colExtent; colIdx++) {
|
||||||
for (let cellId of cellIds) {
|
const cellId = $selectedCells[rowIdx][colIdx]
|
||||||
const { id, field } = parseCellID(cellId)
|
const { id, field } = parseCellID(cellId)
|
||||||
const rowIdx = $rowLookupMap[id] - minRowIdx
|
|
||||||
const colIdx = $columnLookupMap[field] - minColIdx
|
|
||||||
if (colIdx in (value[rowIdx] || [])) {
|
|
||||||
if (!changeMap[id]) {
|
if (!changeMap[id]) {
|
||||||
changeMap[id] = {}
|
changeMap[id] = {}
|
||||||
}
|
}
|
||||||
|
@ -192,17 +159,19 @@ export const createActions = context => {
|
||||||
if (multiCellPaste) {
|
if (multiCellPaste) {
|
||||||
// Single to multi (duplicate value in all selected cells)
|
// Single to multi (duplicate value in all selected cells)
|
||||||
let changeMap = {}
|
let changeMap = {}
|
||||||
for (let cellId of cellIds) {
|
for (let row of $selectedCells) {
|
||||||
const { id, field } = parseCellID(cellId)
|
for (let cellId of row) {
|
||||||
if (!changeMap[id]) {
|
const { id, field } = parseCellID(cellId)
|
||||||
changeMap[id] = {}
|
if (!changeMap[id]) {
|
||||||
|
changeMap[id] = {}
|
||||||
|
}
|
||||||
|
changeMap[id][field] = value
|
||||||
}
|
}
|
||||||
changeMap[id][field] = $clipboard.value
|
|
||||||
}
|
}
|
||||||
await rows.actions.bulkUpdate(changeMap)
|
await rows.actions.bulkUpdate(changeMap)
|
||||||
} else {
|
} else {
|
||||||
// Single to single
|
// Single to single
|
||||||
$focusedCellAPI.setValue($clipboard.value)
|
$focusedCellAPI.setValue(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,8 @@ const DependencyOrderedStores = [
|
||||||
Users,
|
Users,
|
||||||
Menu,
|
Menu,
|
||||||
Pagination,
|
Pagination,
|
||||||
Clipboard,
|
|
||||||
Config,
|
Config,
|
||||||
|
Clipboard,
|
||||||
Notifications,
|
Notifications,
|
||||||
Cache,
|
Cache,
|
||||||
]
|
]
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const createActions = context => {
|
||||||
gridID,
|
gridID,
|
||||||
selectedRows,
|
selectedRows,
|
||||||
selectedRowCount,
|
selectedRowCount,
|
||||||
selectedCells,
|
selectedCellMap,
|
||||||
selectedCellCount,
|
selectedCellCount,
|
||||||
} = context
|
} = context
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ export const createActions = context => {
|
||||||
// Check if there are multiple cells selected, and if this is one of them
|
// Check if there are multiple cells selected, and if this is one of them
|
||||||
let multiCellMode = false
|
let multiCellMode = false
|
||||||
if (!multiRowMode && get(selectedCellCount) > 1) {
|
if (!multiRowMode && get(selectedCellCount) > 1) {
|
||||||
if (get(selectedCells)[cellId]) {
|
if (get(selectedCellMap)[cellId]) {
|
||||||
multiCellMode = true
|
multiCellMode = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ export const deriveStores = context => {
|
||||||
([$cellSelection, $rowLookupMap, $columnLookupMap]) => {
|
([$cellSelection, $rowLookupMap, $columnLookupMap]) => {
|
||||||
const { sourceCellId, targetCellId } = $cellSelection
|
const { sourceCellId, targetCellId } = $cellSelection
|
||||||
if (!sourceCellId || !targetCellId || sourceCellId === targetCellId) {
|
if (!sourceCellId || !targetCellId || sourceCellId === targetCellId) {
|
||||||
return {}
|
return []
|
||||||
}
|
}
|
||||||
const $rows = get(rows)
|
const $rows = get(rows)
|
||||||
const $allVisibleColumns = get(allVisibleColumns)
|
const $allVisibleColumns = get(allVisibleColumns)
|
||||||
|
@ -130,24 +130,36 @@ export const deriveStores = context => {
|
||||||
const lowerColIndex = Math.min(sourceColIndex, targetColIndex)
|
const lowerColIndex = Math.min(sourceColIndex, targetColIndex)
|
||||||
const upperColIndex = Math.max(sourceColIndex, targetColIndex)
|
const upperColIndex = Math.max(sourceColIndex, targetColIndex)
|
||||||
|
|
||||||
// Build map of all cells inside these bounds
|
// Build 2 dimensional array of all cells inside these bounds
|
||||||
let map = {}
|
let cells = []
|
||||||
let rowId, colName, cellId
|
let rowId, colName
|
||||||
for (let rowIdx = lowerRowIndex; rowIdx <= upperRowIndex; rowIdx++) {
|
for (let rowIdx = lowerRowIndex; rowIdx <= upperRowIndex; rowIdx++) {
|
||||||
|
let rowCells = []
|
||||||
for (let colIdx = lowerColIndex; colIdx <= upperColIndex; colIdx++) {
|
for (let colIdx = lowerColIndex; colIdx <= upperColIndex; colIdx++) {
|
||||||
rowId = $rows[rowIdx]._id
|
rowId = $rows[rowIdx]._id
|
||||||
colName = $allVisibleColumns[colIdx].name
|
colName = $allVisibleColumns[colIdx].name
|
||||||
cellId = getCellID(rowId, colName)
|
rowCells.push(getCellID(rowId, colName))
|
||||||
map[cellId] = { rowIdx, colIdx }
|
|
||||||
}
|
}
|
||||||
|
cells.push(rowCells)
|
||||||
}
|
}
|
||||||
return map
|
return cells
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Derive a quick lookup map of the selected cells
|
||||||
|
const selectedCellMap = derived(selectedCells, $selectedCells => {
|
||||||
|
let map = {}
|
||||||
|
for (let row of $selectedCells) {
|
||||||
|
for (let cell of row) {
|
||||||
|
map[cell] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
})
|
||||||
|
|
||||||
// Derive the count of the selected cells
|
// Derive the count of the selected cells
|
||||||
const selectedCellCount = derived(selectedCells, $selectedCells => {
|
const selectedCellCount = derived(selectedCellMap, $selectedCellMap => {
|
||||||
return Object.keys($selectedCells).length
|
return Object.keys($selectedCellMap).length
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -158,6 +170,7 @@ export const deriveStores = context => {
|
||||||
selectedRowCount,
|
selectedRowCount,
|
||||||
isSelectingCells,
|
isSelectingCells,
|
||||||
selectedCells,
|
selectedCells,
|
||||||
|
selectedCellMap,
|
||||||
selectedCellCount,
|
selectedCellCount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,9 +285,9 @@ export const createActions = context => {
|
||||||
selectedCells: {
|
selectedCells: {
|
||||||
...selectedCells,
|
...selectedCells,
|
||||||
actions: {
|
actions: {
|
||||||
start: startCellSelection,
|
startSelecting: startCellSelection,
|
||||||
update: updateCellSelection,
|
updateTarget: updateCellSelection,
|
||||||
stop: stopCellSelection,
|
stopSelecting: stopCellSelection,
|
||||||
clear: clearCellSelection,
|
clear: clearCellSelection,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,7 +25,7 @@ export const deriveStores = context => {
|
||||||
|
|
||||||
// Generate a lookup map of cell ID to the user that has it selected, to make
|
// Generate a lookup map of cell ID to the user that has it selected, to make
|
||||||
// lookups inside cells extremely fast
|
// lookups inside cells extremely fast
|
||||||
const selectedCellMap = derived(
|
const userCellMap = derived(
|
||||||
[users, focusedCellId],
|
[users, focusedCellId],
|
||||||
([$users, $focusedCellId]) => {
|
([$users, $focusedCellId]) => {
|
||||||
let map = {}
|
let map = {}
|
||||||
|
@ -40,7 +40,7 @@ export const deriveStores = context => {
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectedCellMap,
|
userCellMap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue