Skip to content

Commit

Permalink
Enable multi-cell selection for deleting content of cells (#390)
Browse files Browse the repository at this point in the history
  • Loading branch information
guergana authored May 27, 2024
1 parent f94d64b commit 0de6230
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 7 deletions.
15 changes: 15 additions & 0 deletions client/components/Controllers/Table/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import TableEditor from '../../Editors/Table'
import { useStore } from './store'
import * as React from 'react'

export default function Editor() {
const schema = useStore((state) => state.record?.resource.schema)
Expand All @@ -11,6 +12,17 @@ export default function Editor() {
const saveEditing = useStore((state) => state.saveEditing)
const stopEditing = useStore((state) => state.stopEditing)
const updateState = useStore((state) => state.updateState)
const deleteMultipleCells = useStore((state) => state.deleteMultipleCells)

const [cellSelection, setCellSelection] = React.useState({})

const onKeyDown = React.useCallback(
(event: { key: any }) => {
if (event.key === 'Delete') deleteMultipleCells(cellSelection)
},
[cellSelection]
)

if (!schema) return null
if (!report) return null
return (
Expand All @@ -25,6 +37,9 @@ export default function Editor() {
onEditComplete={saveEditing}
onEditStop={stopEditing}
handle={(gridRef) => updateState({ gridRef })}
onKeyDown={onKeyDown}
defaultCellSelection={cellSelection}
onCellSelectionChange={setCellSelection}
/>
)
}
40 changes: 37 additions & 3 deletions client/components/Controllers/Table/store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as React from 'react'
import * as zustand from 'zustand'
import noop from 'lodash/noop'
import mapValues from 'lodash/mapValues'
import isNull from 'lodash/isNull'
import isEqual from 'fast-deep-equal'
import cloneDeep from 'lodash/cloneDeep'
import { createStore } from 'zustand/vanilla'
Expand Down Expand Up @@ -50,13 +52,14 @@ export interface State {

// Editing

initialEditingValue?: any
initialEditingValue?: string | number
// TODO: find proper context type
startEditing: (context: any) => void
saveEditing: (context: any) => void
stopEditing: (context: any) => void
undoChange: () => void
redoChange: () => void
deleteMultipleCells: (cells: object) => Promise<void>
}

export function makeStore(props: TableProps) {
Expand Down Expand Up @@ -158,8 +161,15 @@ export function makeStore(props: TableProps) {
desc: sortInfo?.dir === -1,
})

helpers.applyTableHistory(history, rows)
return { data: rows, count: rowCount || 0 }
// convert null fields in db to empty strings to avoid errors
// in table representation. InovuaDataGrid complains with null values
const rowsNotNull = []
for (const row of rows) {
rowsNotNull.push(mapValues(row, (value) => (isNull(value) ? '' : value)))
}

helpers.applyTableHistory(history, rowsNotNull)
return { data: rowsNotNull, count: rowCount || 0 }
},
toggleErrorMode: async () => {
const { path, client, mode, gridRef } = get()
Expand Down Expand Up @@ -218,6 +228,7 @@ export function makeStore(props: TableProps) {
history.changes.push(change)
undoneHistory.changes = []
set({ history: { ...history } })
gridRef?.current?.reload()
},
stopEditing: () => {
const { gridRef, updateState } = get()
Expand All @@ -240,6 +251,29 @@ export function makeStore(props: TableProps) {
set({ history: { ...history } })
gridRef?.current?.reload()
},
deleteMultipleCells: async (cells: object) => {
const { gridRef, history, undoneHistory } = get()
const grid = gridRef?.current
if (!grid) return

const cellChanges = []

for (const [key] of Object.entries(cells)) {
const row = key.substring(0, key.indexOf(','))
const rowNumber = parseInt(row)
const column = key.substring(key.indexOf(',') + 1, key.length)

cellChanges.push({ rowNumber, fieldName: column, value: '' })
}
const change: types.IChange = {
type: 'multiple-cells-update',
cells: cellChanges,
}
history.changes.push(change)
helpers.applyTableHistory({ changes: [change] }, grid.data)
undoneHistory.changes = []
set({ history: { ...history } })
},
}))
}

Expand Down
5 changes: 5 additions & 0 deletions client/helpers/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export function createChangeIndex(patch?: types.IHistory) {
} else if (change.type === 'cell-update') {
const cellKey = `${change.rowNumber},${change.fieldName}`
changeIndex.cell[cellKey] = change
} else if (change.type == 'multiple-cells-update') {
for (const cell of change.cells) {
const cellKey = `${cell.rowNumber},${cell.fieldName}`
changeIndex.cell[cellKey] = change
}
}
}
return changeIndex
Expand Down
6 changes: 6 additions & 0 deletions client/helpers/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export function applyTableHistory(history: types.IHistory, rows: types.IRow[]) {
for (const row of rows) {
if (row._rowNumber === change.rowNumber) row[change.fieldName] = change.value
}
} else if (change.type == 'multiple-cells-update') {
for (const row of rows) {
for (const cell of change.cells) {
if (row._rowNumber === cell.rowNumber) row[cell.fieldName] = cell.value
}
}
}
}
}
12 changes: 11 additions & 1 deletion client/types/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface IHistory {
changes: IChange[]
}

export type IChange = IRowDelete | ICellUpdate
export type IChange = IRowDelete | ICellUpdate | IMultipleCellUpdate

interface IRowDelete {
type: 'row-delete'
Expand All @@ -24,3 +24,13 @@ interface ICellUpdate {
fieldName: string
value: any
}
interface ICell {
rowNumber: number
fieldName: string
value: any
}

interface IMultipleCellUpdate {
type: 'multiple-cells-update'
cells: ICell[]
}
13 changes: 13 additions & 0 deletions server/endpoints/table/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ def action(project: Project, props: Props) -> Result:
.where(table.c._rowNumber == change.rowNumber)
.values(**{change.fieldName: value})
)
elif change.type == "multiple-cells-update":
for cell in change.cells:
# Prepare value
value = cell.value
if schema.has_field(cell.fieldName):
field = schema.get_field(cell.fieldName)
value, _ = field.read_cell(value)
# Write value
conn.execute(
sa.update(table)
.where(table.c._rowNumber == cell.rowNumber)
.values(**{cell.fieldName: value})
)

# Export database table
target = fs.get_fullpath(props.toPath or props.path)
Expand Down
21 changes: 18 additions & 3 deletions server/models/history.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Any, List, Literal, Union

from pydantic import BaseModel
from pydantic import BaseModel, Field
from typing_extensions import Annotated


class Change(BaseModel):
type: str
type: Any


class RowDelete(Change):
Expand All @@ -18,6 +19,20 @@ class CellUpdate(Change):
fieldName: str
value: Any

class Cell(BaseModel):
rowNumber: int
fieldName: str
value: Any

class MultipleCellUpdate(Change):
type: Literal["multiple-cells-update"]
cells: List[Cell]


class History(BaseModel):
changes: List[Union[RowDelete, CellUpdate]]
changes: List[
Annotated[
Union[RowDelete, CellUpdate, MultipleCellUpdate],
Field(discriminator="type")
]
]

0 comments on commit 0de6230

Please sign in to comment.