Add basic keyboard interactions to dataspaces

This commit is contained in:
Andrew Kingston 2023-03-08 08:32:03 +00:00
parent 045d0c89af
commit 48309349ae
6 changed files with 99 additions and 10 deletions

View File

@ -0,0 +1,68 @@
<script>
import { getContext, onMount } from "svelte"
const { rows, selectedCellId, columns, selectedCellRow } = getContext("sheet")
const handleKeyDown = e => {
switch (e.key) {
case "ArrowLeft":
changeSelectedColumn(-1)
break
case "ArrowRight":
changeSelectedColumn(1)
break
case "ArrowUp":
changeSelectedRow(-1)
break
case "ArrowDown":
changeSelectedRow(1)
break
case "Delete":
deleteSelectedCell()
break
}
}
const changeSelectedColumn = delta => {
const cellId = $selectedCellId
if (!cellId) {
return
}
const cols = $columns
const split = cellId.split("-")
const columnName = split[1]
const column = cols.findIndex(col => col.name === columnName)
const newColumn = cols[column + delta]
if (newColumn) {
$selectedCellId = `${split[0]}-${newColumn.name}`
}
}
const changeSelectedRow = delta => {
const row = $selectedCellRow
const cellId = $selectedCellId
if (!row) {
return
}
const newRow = $rows[row.__idx + delta]
if (newRow) {
const split = cellId.split("-")
$selectedCellId = `${newRow._id}-${split[1]}`
}
}
const deleteSelectedCell = () => {
if (!$selectedCellId) {
return
}
const [rowId, column] = $selectedCellId.split("-")
rows.actions.updateRow(rowId, column, null)
}
onMount(() => {
document.addEventListener("keydown", handleKeyDown)
return () => {
document.removeEventListener("keydown", handleKeyDown)
}
})
</script>

View File

@ -9,7 +9,7 @@
import { createColumnsStores } from "./stores/columns" import { createColumnsStores } from "./stores/columns"
import { createScrollStores } from "./stores/scroll" import { createScrollStores } from "./stores/scroll"
import { createBoundsStores } from "./stores/bounds" import { createBoundsStores } from "./stores/bounds"
import { createInterfaceStores } from "./stores/interface" import { createUIStores } from "./stores/ui"
export { createUserStores } from "./stores/users" export { createUserStores } from "./stores/users"
import { createWebsocket } from "./websocket" import { createWebsocket } from "./websocket"
import { createUserStores } from "./stores/users" import { createUserStores } from "./stores/users"
@ -23,6 +23,8 @@
import MenuOverlay from "./MenuOverlay.svelte" import MenuOverlay from "./MenuOverlay.svelte"
import StickyColumn from "./StickyColumn.svelte" import StickyColumn from "./StickyColumn.svelte"
import UserAvatars from "./UserAvatars.svelte" import UserAvatars from "./UserAvatars.svelte"
import KeyboardManager from "./KeyboardManager.svelte"
import { clickOutside } from "@budibase/bbui"
export let API export let API
export let tableId export let tableId
@ -60,12 +62,12 @@
context = { ...context, ...createScrollStores(context) } context = { ...context, ...createScrollStores(context) }
context = { ...context, ...createViewportStores(context) } context = { ...context, ...createViewportStores(context) }
context = { ...context, ...createReorderStores(context) } context = { ...context, ...createReorderStores(context) }
context = { ...context, ...createInterfaceStores(context) } context = { ...context, ...createUIStores(context) }
context = { ...context, ...createUserStores(context) } context = { ...context, ...createUserStores(context) }
context = { ...context, ...createMenuStores(context) } context = { ...context, ...createMenuStores(context) }
// Reference some stores for local use // Reference some stores for local use
const { isResizing, isReordering } = context const { isResizing, isReordering, ui } = context
// Keep config store up to date // Keep config store up to date
$: config.set({ $: config.set({
@ -88,10 +90,11 @@
<div <div
class="sheet" class="sheet"
id="sheet-{rand}"
class:is-resizing={$isResizing} class:is-resizing={$isResizing}
class:is-reordering={$isReordering} class:is-reordering={$isReordering}
use:clickOutside={ui.actions.blur}
style="--cell-height:{cellHeight}px;" style="--cell-height:{cellHeight}px;"
id="sheet-{rand}"
> >
<div class="controls"> <div class="controls">
<div class="controls-left"> <div class="controls-left">
@ -112,6 +115,7 @@
<ScrollOverlay /> <ScrollOverlay />
<MenuOverlay /> <MenuOverlay />
</div> </div>
<KeyboardManager />
</div> </div>
<style> <style>

View File

@ -48,7 +48,7 @@
value={row[column.name]} value={row[column.name]}
schema={column.schema} schema={column.schema}
selected={$selectedCellId === cellId} selected={$selectedCellId === cellId}
onChange={val => rows.actions.updateRow(row._id, column, val)} onChange={val => rows.actions.updateRow(row._id, column.name, val)}
readonly={column.schema.autocolumn} readonly={column.schema.autocolumn}
/> />
</SheetCell> </SheetCell>

View File

@ -134,7 +134,7 @@
schema={$stickyColumn.schema} schema={$stickyColumn.schema}
selected={$selectedCellId === cellId} selected={$selectedCellId === cellId}
onChange={val => onChange={val =>
rows.actions.updateRow(row._id, $stickyColumn, val)} rows.actions.updateRow(row._id, $stickyColumn.name, val)}
readonly={$stickyColumn.schema.autocolumn} readonly={$stickyColumn.schema.autocolumn}
/> />
</SheetCell> </SheetCell>

View File

@ -169,12 +169,12 @@ export const createRowsStore = context => {
const $rows = get(rows) const $rows = get(rows)
const index = $rows.findIndex(x => x._id === rowId) const index = $rows.findIndex(x => x._id === rowId)
const row = $rows[index] const row = $rows[index]
if (index === -1 || row?.[column.name] === value) { if (index === -1 || row?.[column] === value) {
return return
} }
// Immediately update state so that the change is reflected // Immediately update state so that the change is reflected
let newRow = { ...row, [column.name]: value } let newRow = { ...row, [column]: value }
rows.update(state => { rows.update(state => {
state[index] = { ...newRow } state[index] = { ...newRow }
return state return state

View File

@ -1,6 +1,6 @@
import { writable, get, derived } from "svelte/store" import { writable, get, derived } from "svelte/store"
export const createInterfaceStores = context => { export const createUIStores = context => {
const { rows, rowLookupMap } = context const { rows, rowLookupMap } = context
const selectedCellId = writable(null) const selectedCellId = writable(null)
const selectedRows = writable({}) const selectedRows = writable({})
@ -62,5 +62,22 @@ export const createInterfaceStores = context => {
} }
}) })
return { selectedCellId, selectedRows, hoveredRowId, selectedCellRow } // Callback when leaving the sheet, deselecting all focussed or selected items
const blur = () => {
selectedCellId.set(null)
selectedRows.set({})
hoveredRowId.set(null)
}
return {
selectedCellId,
selectedRows,
hoveredRowId,
selectedCellRow,
ui: {
actions: {
blur,
},
},
}
} }