Add copy and paste to spreadsheet and add immediate editing of cells without additional click
This commit is contained in:
parent
6f20c3dff8
commit
ddc11ab88c
2 changed files with 83 additions and 28 deletions
|
@ -49,7 +49,9 @@
|
||||||
blur: () => api?.blur(),
|
blur: () => api?.blur(),
|
||||||
onKeyDown: (...params) => api?.onKeyDown(...params),
|
onKeyDown: (...params) => api?.onKeyDown(...params),
|
||||||
isReadonly: () => readonly,
|
isReadonly: () => readonly,
|
||||||
updateValue: value => {
|
getType: () => column.schema.type,
|
||||||
|
getValue: () => row[column.name],
|
||||||
|
setValue: value => {
|
||||||
validation.actions.setError(cellId, null)
|
validation.actions.setError(cellId, null)
|
||||||
updateRow(row._id, column.name, value)
|
updateRow(row._id, column.name, value)
|
||||||
},
|
},
|
||||||
|
@ -72,7 +74,7 @@
|
||||||
bind:api
|
bind:api
|
||||||
value={row[column.name]}
|
value={row[column.name]}
|
||||||
schema={column.schema}
|
schema={column.schema}
|
||||||
onChange={cellAPI.updateValue}
|
onChange={cellAPI.setValue}
|
||||||
{focused}
|
{focused}
|
||||||
{readonly}
|
{readonly}
|
||||||
{invertY}
|
{invertY}
|
||||||
|
|
|
@ -11,11 +11,14 @@
|
||||||
focusedCellAPI,
|
focusedCellAPI,
|
||||||
} = getContext("sheet")
|
} = getContext("sheet")
|
||||||
|
|
||||||
|
let copiedValue
|
||||||
|
|
||||||
|
// Global key listener which intercepts all key events
|
||||||
const handleKeyDown = e => {
|
const handleKeyDown = e => {
|
||||||
// If nothing selected avoid processing further key presses
|
// If nothing selected avoid processing further key presses
|
||||||
if (!$focusedCellId) {
|
if (!$focusedCellId) {
|
||||||
if (e.key === "Tab") {
|
if (e.key === "Tab") {
|
||||||
selectFirstCell()
|
focusFirstCell()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -26,7 +29,7 @@
|
||||||
api?.blur?.()
|
api?.blur?.()
|
||||||
} else if (e.key === "Tab") {
|
} else if (e.key === "Tab") {
|
||||||
api?.blur?.()
|
api?.blur?.()
|
||||||
changeSelectedColumn(1)
|
changeFocusedColumn(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass the key event to the selected cell and let it decide whether to
|
// Pass the key event to the selected cell and let it decide whether to
|
||||||
|
@ -40,29 +43,43 @@
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
// Handle the key ourselves
|
// Handle the key ourselves
|
||||||
switch (e.key) {
|
if (e.metaKey || e.ctrlKey) {
|
||||||
case "ArrowLeft":
|
switch (e.key) {
|
||||||
changeSelectedColumn(-1)
|
case "c":
|
||||||
break
|
copy()
|
||||||
case "ArrowRight":
|
break
|
||||||
changeSelectedColumn(1)
|
case "v":
|
||||||
break
|
paste()
|
||||||
case "ArrowUp":
|
break
|
||||||
changeSelectedRow(-1)
|
}
|
||||||
break
|
} else {
|
||||||
case "ArrowDown":
|
switch (e.key) {
|
||||||
changeSelectedRow(1)
|
case "ArrowLeft":
|
||||||
break
|
changeFocusedColumn(-1)
|
||||||
case "Delete":
|
break
|
||||||
deleteSelectedCell()
|
case "ArrowRight":
|
||||||
break
|
changeFocusedColumn(1)
|
||||||
case "Enter":
|
break
|
||||||
focusSelectedCell()
|
case "ArrowUp":
|
||||||
break
|
changeFocusedRow(-1)
|
||||||
|
break
|
||||||
|
case "ArrowDown":
|
||||||
|
changeFocusedRow(1)
|
||||||
|
break
|
||||||
|
case "Delete":
|
||||||
|
deleteSelectedCell()
|
||||||
|
break
|
||||||
|
case "Enter":
|
||||||
|
focusCell()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
startEnteringValue(e.key, e.which)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectFirstCell = () => {
|
// Focuses the first cell in the spreadsheet
|
||||||
|
const focusFirstCell = () => {
|
||||||
const firstRow = $enrichedRows[0]
|
const firstRow = $enrichedRows[0]
|
||||||
if (!firstRow) {
|
if (!firstRow) {
|
||||||
return
|
return
|
||||||
|
@ -74,7 +91,8 @@
|
||||||
focusedCellId.set(`${firstRow._id}-${firstColumn.name}`)
|
focusedCellId.set(`${firstRow._id}-${firstColumn.name}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeSelectedColumn = delta => {
|
// Changes the focused cell by moving it left or right to a different column
|
||||||
|
const changeFocusedColumn = delta => {
|
||||||
if (!$focusedCellId) {
|
if (!$focusedCellId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -98,7 +116,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeSelectedRow = delta => {
|
// Changes the focused cell by moving it up or down to a new row
|
||||||
|
const changeFocusedRow = delta => {
|
||||||
if (!$focusedRow) {
|
if (!$focusedRow) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -114,16 +133,50 @@
|
||||||
if ($focusedCellAPI?.isReadonly()) {
|
if ($focusedCellAPI?.isReadonly()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
$focusedCellAPI.updateValue(null)
|
$focusedCellAPI.setValue(null)
|
||||||
}, 100)
|
}, 100)
|
||||||
|
|
||||||
const focusSelectedCell = () => {
|
// Focuses the current cell for editing
|
||||||
|
const focusCell = () => {
|
||||||
if ($focusedCellAPI?.isReadonly()) {
|
if ($focusedCellAPI?.isReadonly()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
$focusedCellAPI?.focus?.()
|
$focusedCellAPI?.focus?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copies the value from the current cell
|
||||||
|
const copy = () => {
|
||||||
|
copiedValue = $focusedCellAPI?.getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pastes the copied value
|
||||||
|
const paste = () => {
|
||||||
|
if (copiedValue != null) {
|
||||||
|
$focusedCellAPI?.setValue(copiedValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utils to identify a key code
|
||||||
|
const keyCodeIsNumber = keyCode => keyCode >= 48 && keyCode <= 57
|
||||||
|
const keyCodeIsLetter = keyCode => keyCode >= 65 && keyCode <= 90
|
||||||
|
|
||||||
|
// Focuses the cell and starts entering a new value
|
||||||
|
const startEnteringValue = (key, keyCode) => {
|
||||||
|
if ($focusedCellAPI) {
|
||||||
|
const type = $focusedCellAPI.getType()
|
||||||
|
if (type === "number" && keyCodeIsNumber(keyCode)) {
|
||||||
|
$focusedCellAPI.setValue(parseInt(key))
|
||||||
|
$focusedCellAPI.focus()
|
||||||
|
} else if (
|
||||||
|
["string", "barcodeqr", "longform"].includes(type) &&
|
||||||
|
(keyCodeIsLetter(keyCode) || keyCodeIsNumber(keyCode))
|
||||||
|
) {
|
||||||
|
$focusedCellAPI.setValue(key)
|
||||||
|
$focusedCellAPI.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
document.addEventListener("keydown", handleKeyDown)
|
document.addEventListener("keydown", handleKeyDown)
|
||||||
return () => {
|
return () => {
|
||||||
|
|
Loading…
Reference in a new issue