2022-10-25 15:49:38 +11:00
|
|
|
|
import { useMemo, useRef, useState, useEffect, useCallback } from "react";
|
2022-11-21 17:16:32 +11:00
|
|
|
|
import useStateRef from "react-usestateref";
|
2022-05-19 16:37:56 +10:00
|
|
|
|
import { useAtom, useSetAtom } from "jotai";
|
2022-10-24 15:59:56 +11:00
|
|
|
|
import { useThrottledCallback } from "use-debounce";
|
2022-10-14 16:15:41 +11:00
|
|
|
|
import {
|
|
|
|
|
|
createColumnHelper,
|
|
|
|
|
|
getCoreRowModel,
|
|
|
|
|
|
useReactTable,
|
|
|
|
|
|
} from "@tanstack/react-table";
|
2022-11-28 18:42:44 +11:00
|
|
|
|
import type {
|
|
|
|
|
|
ColumnPinningState,
|
|
|
|
|
|
VisibilityState,
|
|
|
|
|
|
} from "@tanstack/react-table";
|
2022-11-15 17:36:58 +11:00
|
|
|
|
import { DropResult } from "react-beautiful-dnd";
|
2022-11-08 16:18:33 +11:00
|
|
|
|
import { get } from "lodash-es";
|
2022-10-14 16:15:41 +11:00
|
|
|
|
|
2022-10-31 17:04:18 +11:00
|
|
|
|
import StyledTable from "./Styled/StyledTable";
|
2022-11-15 18:14:44 +11:00
|
|
|
|
import TableHeader from "./TableHeader";
|
2022-11-15 17:36:58 +11:00
|
|
|
|
import TableBody from "./TableBody";
|
2022-10-31 17:56:54 +11:00
|
|
|
|
import FinalColumn from "./FinalColumn/FinalColumn";
|
2022-10-31 17:04:18 +11:00
|
|
|
|
import ContextMenu from "./ContextMenu";
|
2022-05-27 15:10:06 +10:00
|
|
|
|
import EmptyState from "@src/components/EmptyState";
|
2022-05-19 16:37:56 +10:00
|
|
|
|
// import BulkActions from "./BulkActions";
|
|
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
|
tableScope,
|
|
|
|
|
|
tableSchemaAtom,
|
|
|
|
|
|
tableColumnsOrderedAtom,
|
|
|
|
|
|
tableRowsAtom,
|
2022-05-30 12:48:27 +10:00
|
|
|
|
tableNextPageAtom,
|
2022-05-19 16:37:56 +10:00
|
|
|
|
tablePageAtom,
|
|
|
|
|
|
updateColumnAtom,
|
2023-01-14 11:19:55 +00:00
|
|
|
|
selectedCellAtom,
|
2023-02-26 16:13:03 +05:30
|
|
|
|
tableSortsAtom,
|
|
|
|
|
|
tableIdAtom,
|
2022-05-19 16:37:56 +10:00
|
|
|
|
} from "@src/atoms/tableScope";
|
2023-02-26 16:17:46 +05:30
|
|
|
|
import { projectScope, userSettingsAtom } from "@src/atoms/projectScope";
|
2022-05-24 20:34:28 +10:00
|
|
|
|
import { getFieldType, getFieldProp } from "@src/components/fields";
|
2022-10-18 17:53:47 +11:00
|
|
|
|
import { useKeyboardNavigation } from "./useKeyboardNavigation";
|
2023-01-14 11:35:32 +00:00
|
|
|
|
import { useMenuAction } from "./useMenuAction";
|
2022-10-24 14:59:29 +11:00
|
|
|
|
import { useSaveColumnSizing } from "./useSaveColumnSizing";
|
2023-01-14 11:35:32 +00:00
|
|
|
|
import useHotKeys from "./useHotKey";
|
2022-11-28 18:42:44 +11:00
|
|
|
|
import type { TableRow, ColumnConfig } from "@src/types/table";
|
2022-05-19 16:37:56 +10:00
|
|
|
|
|
|
|
|
|
|
export const DEFAULT_ROW_HEIGHT = 41;
|
2022-06-13 16:02:38 +10:00
|
|
|
|
export const DEFAULT_COL_WIDTH = 150;
|
2022-10-24 14:59:29 +11:00
|
|
|
|
export const MIN_COL_WIDTH = 80;
|
2022-10-21 14:58:25 +11:00
|
|
|
|
export const TABLE_PADDING = 16;
|
2022-10-24 15:19:28 +11:00
|
|
|
|
export const OUT_OF_ORDER_MARGIN = 8;
|
2022-10-24 13:12:53 +11:00
|
|
|
|
export const DEBOUNCE_DELAY = 500;
|
2022-05-19 16:37:56 +10:00
|
|
|
|
|
2022-10-14 16:15:41 +11:00
|
|
|
|
declare module "@tanstack/table-core" {
|
2022-11-15 17:36:58 +11:00
|
|
|
|
/** The `column.meta` property contains the column config from tableSchema */
|
2022-10-14 16:15:41 +11:00
|
|
|
|
interface ColumnMeta<TData, TValue> extends ColumnConfig {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const columnHelper = createColumnHelper<TableRow>();
|
|
|
|
|
|
const getRowId = (row: TableRow) => row._rowy_ref.path || row._rowy_ref.id;
|
2022-05-19 16:37:56 +10:00
|
|
|
|
|
2022-11-01 13:19:31 +11:00
|
|
|
|
export interface ITableProps {
|
2022-11-18 17:31:03 +11:00
|
|
|
|
/** Determines if “Add column” button is displayed */
|
2022-11-15 16:53:20 +11:00
|
|
|
|
canAddColumns: boolean;
|
2022-11-18 17:31:03 +11:00
|
|
|
|
/** Determines if columns can be rearranged */
|
2022-11-15 16:53:20 +11:00
|
|
|
|
canEditColumns: boolean;
|
2022-11-18 17:31:03 +11:00
|
|
|
|
/**
|
|
|
|
|
|
* Determines if any cell can be edited.
|
|
|
|
|
|
* If false, `Table` only ever renders `EditorCell`.
|
|
|
|
|
|
*/
|
2022-11-15 16:53:20 +11:00
|
|
|
|
canEditCells: boolean;
|
2022-11-18 17:31:03 +11:00
|
|
|
|
/** The hidden columns saved to user settings */
|
2022-11-01 13:19:31 +11:00
|
|
|
|
hiddenColumns?: string[];
|
2022-11-18 17:31:03 +11:00
|
|
|
|
/**
|
|
|
|
|
|
* Displayed when `tableRows` is empty.
|
|
|
|
|
|
* Loading state handled by Suspense in parent component.
|
|
|
|
|
|
*/
|
2022-11-01 13:19:31 +11:00
|
|
|
|
emptyState?: React.ReactNode;
|
|
|
|
|
|
}
|
2022-05-19 16:37:56 +10:00
|
|
|
|
|
2022-11-18 17:31:03 +11:00
|
|
|
|
/**
|
|
|
|
|
|
* Takes table schema and row data from `tableScope` and makes it compatible
|
|
|
|
|
|
* with TanStack Table. Renders table children and cell context menu.
|
|
|
|
|
|
*
|
|
|
|
|
|
* - Calls `useKeyboardNavigation` hook
|
|
|
|
|
|
* - Handles rearranging columns
|
|
|
|
|
|
* - Handles infinite scrolling
|
|
|
|
|
|
* - Stores local state for resizing columns, and asks admins if they want to
|
|
|
|
|
|
* save to table schema for all users
|
|
|
|
|
|
*/
|
2022-11-01 13:19:31 +11:00
|
|
|
|
export default function Table({
|
2022-11-15 16:53:20 +11:00
|
|
|
|
canAddColumns,
|
|
|
|
|
|
canEditColumns,
|
|
|
|
|
|
canEditCells,
|
2022-11-01 13:19:31 +11:00
|
|
|
|
hiddenColumns,
|
|
|
|
|
|
emptyState,
|
|
|
|
|
|
}: ITableProps) {
|
2022-05-19 16:37:56 +10:00
|
|
|
|
const [tableSchema] = useAtom(tableSchemaAtom, tableScope);
|
|
|
|
|
|
const [tableColumnsOrdered] = useAtom(tableColumnsOrderedAtom, tableScope);
|
|
|
|
|
|
const [tableRows] = useAtom(tableRowsAtom, tableScope);
|
2022-05-30 12:48:27 +10:00
|
|
|
|
const [tableNextPage] = useAtom(tableNextPageAtom, tableScope);
|
2022-10-24 13:22:46 +11:00
|
|
|
|
const [tablePage, setTablePage] = useAtom(tablePageAtom, tableScope);
|
2022-05-19 16:37:56 +10:00
|
|
|
|
|
|
|
|
|
|
const updateColumn = useSetAtom(updateColumnAtom, tableScope);
|
|
|
|
|
|
|
2023-02-26 16:17:46 +05:30
|
|
|
|
// Get user settings and tableId for applying sort sorting
|
2023-02-26 16:13:03 +05:30
|
|
|
|
const [userSettings] = useAtom(userSettingsAtom, projectScope);
|
|
|
|
|
|
const [tableId] = useAtom(tableIdAtom, tableScope);
|
|
|
|
|
|
const setTableSorts = useSetAtom(tableSortsAtom, tableScope);
|
|
|
|
|
|
|
2022-11-21 17:16:32 +11:00
|
|
|
|
// Store a **state** and reference to the container element
|
|
|
|
|
|
// so the state can re-render `TableBody`, preventing virtualization
|
|
|
|
|
|
// not detecting scroll if the container element was initially `null`
|
|
|
|
|
|
const [containerEl, setContainerEl, containerRef] =
|
|
|
|
|
|
useStateRef<HTMLDivElement | null>(null);
|
2022-10-17 17:57:26 +11:00
|
|
|
|
const gridRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
|
2022-10-14 16:15:41 +11:00
|
|
|
|
// Get column defs from table schema
|
2022-11-18 17:31:03 +11:00
|
|
|
|
// Also add end column for admins (canAddColumns || canEditCells)
|
2022-05-19 16:37:56 +10:00
|
|
|
|
const columns = useMemo(() => {
|
2022-10-14 16:15:41 +11:00
|
|
|
|
const _columns = tableColumnsOrdered
|
2022-10-19 12:41:20 +11:00
|
|
|
|
// Hide column for all users using table schema
|
|
|
|
|
|
.filter((column) => !column.hidden)
|
2022-10-14 16:15:41 +11:00
|
|
|
|
.map((columnConfig) =>
|
2022-11-08 16:18:33 +11:00
|
|
|
|
columnHelper.accessor((row) => get(row, columnConfig.fieldName), {
|
2022-10-17 17:57:26 +11:00
|
|
|
|
id: columnConfig.fieldName,
|
2022-10-14 16:15:41 +11:00
|
|
|
|
meta: columnConfig,
|
2022-10-24 13:12:53 +11:00
|
|
|
|
size: columnConfig.width,
|
|
|
|
|
|
enableResizing: columnConfig.resizable !== false,
|
|
|
|
|
|
minSize: MIN_COL_WIDTH,
|
2022-11-02 16:36:10 +11:00
|
|
|
|
cell: getFieldProp("TableCell", getFieldType(columnConfig)),
|
2022-10-14 16:15:41 +11:00
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2022-11-15 16:53:20 +11:00
|
|
|
|
if (canAddColumns || canEditCells) {
|
2022-10-19 12:41:20 +11:00
|
|
|
|
_columns.push(
|
|
|
|
|
|
columnHelper.display({
|
|
|
|
|
|
id: "_rowy_column_actions",
|
2022-10-31 17:56:54 +11:00
|
|
|
|
cell: FinalColumn as any,
|
2022-10-19 12:41:20 +11:00
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2022-05-19 16:37:56 +10:00
|
|
|
|
|
|
|
|
|
|
return _columns;
|
2022-11-15 16:53:20 +11:00
|
|
|
|
}, [tableColumnsOrdered, canAddColumns, canEditCells]);
|
2022-10-19 12:41:20 +11:00
|
|
|
|
|
2022-11-18 17:31:03 +11:00
|
|
|
|
// Get user’s hidden columns from props and memoize into a `VisibilityState`
|
2022-11-28 18:42:44 +11:00
|
|
|
|
const columnVisibility: VisibilityState = useMemo(() => {
|
2022-11-01 13:19:31 +11:00
|
|
|
|
if (!Array.isArray(hiddenColumns)) return {};
|
|
|
|
|
|
return hiddenColumns.reduce((a, c) => ({ ...a, [c]: false }), {});
|
|
|
|
|
|
}, [hiddenColumns]);
|
2022-06-03 18:17:27 +10:00
|
|
|
|
|
2022-11-18 17:31:03 +11:00
|
|
|
|
// Get frozen columns and memoize into a `ColumnPinningState`
|
2022-11-28 18:42:44 +11:00
|
|
|
|
const columnPinning: ColumnPinningState = useMemo(
|
2022-10-21 14:58:25 +11:00
|
|
|
|
() => ({
|
2022-11-28 18:42:44 +11:00
|
|
|
|
left: columns
|
|
|
|
|
|
.filter(
|
|
|
|
|
|
(c) => c.meta?.fixed && c.id && columnVisibility[c.id] !== false
|
|
|
|
|
|
)
|
|
|
|
|
|
.map((c) => c.id!),
|
2022-10-21 14:58:25 +11:00
|
|
|
|
}),
|
2022-11-28 18:42:44 +11:00
|
|
|
|
[columns, columnVisibility]
|
2022-10-21 14:58:25 +11:00
|
|
|
|
);
|
|
|
|
|
|
const lastFrozen: string | undefined =
|
2022-11-28 18:42:44 +11:00
|
|
|
|
columnPinning.left![columnPinning.left!.length - 1];
|
2022-10-21 14:58:25 +11:00
|
|
|
|
|
2022-10-24 13:12:53 +11:00
|
|
|
|
// Call TanStack Table
|
2022-10-14 16:15:41 +11:00
|
|
|
|
const table = useReactTable({
|
|
|
|
|
|
data: tableRows,
|
|
|
|
|
|
columns,
|
|
|
|
|
|
getCoreRowModel: getCoreRowModel(),
|
|
|
|
|
|
getRowId,
|
|
|
|
|
|
columnResizeMode: "onChange",
|
|
|
|
|
|
});
|
2022-10-24 13:12:53 +11:00
|
|
|
|
|
2022-11-18 17:31:03 +11:00
|
|
|
|
// Store local `columnSizing` state so we can save it to table schema
|
2022-10-25 15:49:38 +11:00
|
|
|
|
// in `useSaveColumnSizing`. This could be generalized by storing the
|
|
|
|
|
|
// entire table state.
|
2022-10-24 13:12:53 +11:00
|
|
|
|
const [columnSizing, setColumnSizing] = useState(
|
|
|
|
|
|
table.initialState.columnSizing
|
|
|
|
|
|
);
|
|
|
|
|
|
table.setOptions((prev) => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
state: { ...prev.state, columnVisibility, columnPinning, columnSizing },
|
|
|
|
|
|
onColumnSizingChange: setColumnSizing,
|
|
|
|
|
|
}));
|
2022-11-18 17:31:03 +11:00
|
|
|
|
// Get rows and columns for virtualization
|
2022-10-18 17:53:47 +11:00
|
|
|
|
const { rows } = table.getRowModel();
|
2022-10-19 12:41:20 +11:00
|
|
|
|
const leafColumns = table.getVisibleLeafColumns();
|
2022-10-17 17:57:26 +11:00
|
|
|
|
|
2022-11-18 17:31:03 +11:00
|
|
|
|
// Handle keyboard navigation
|
2022-11-08 16:18:33 +11:00
|
|
|
|
const { handleKeyDown } = useKeyboardNavigation({
|
2022-10-18 17:53:47 +11:00
|
|
|
|
gridRef,
|
|
|
|
|
|
tableRows,
|
2022-10-19 12:41:20 +11:00
|
|
|
|
leafColumns,
|
2022-10-18 17:53:47 +11:00
|
|
|
|
});
|
2023-01-14 11:19:55 +00:00
|
|
|
|
const [selectedCell] = useAtom(selectedCellAtom, tableScope);
|
|
|
|
|
|
const { handleCopy, handlePaste, handleCut } = useMenuAction(selectedCell);
|
2023-01-14 11:35:32 +00:00
|
|
|
|
const { handler: hotKeysHandler } = useHotKeys([
|
2023-01-14 11:19:55 +00:00
|
|
|
|
["mod+C", handleCopy],
|
|
|
|
|
|
["mod+X", handleCut],
|
|
|
|
|
|
["mod+V", handlePaste],
|
|
|
|
|
|
]);
|
2022-11-15 17:36:58 +11:00
|
|
|
|
|
2022-11-18 17:31:03 +11:00
|
|
|
|
// Handle prompt to save local column sizes if user `canEditColumns`
|
2022-11-15 16:53:20 +11:00
|
|
|
|
useSaveColumnSizing(columnSizing, canEditColumns);
|
2022-10-18 17:53:47 +11:00
|
|
|
|
|
2022-10-25 15:49:38 +11:00
|
|
|
|
const handleDropColumn = useCallback(
|
|
|
|
|
|
(result: DropResult) => {
|
|
|
|
|
|
if (result.destination?.index === undefined || !result.draggableId)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
console.log(result.draggableId, result.destination.index);
|
|
|
|
|
|
|
|
|
|
|
|
updateColumn({
|
|
|
|
|
|
key: result.draggableId,
|
|
|
|
|
|
index: result.destination.index,
|
|
|
|
|
|
config: {},
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
[updateColumn]
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2022-10-18 17:53:47 +11:00
|
|
|
|
const fetchMoreOnBottomReached = useThrottledCallback(
|
|
|
|
|
|
(containerElement?: HTMLDivElement | null) => {
|
|
|
|
|
|
if (!containerElement) return;
|
|
|
|
|
|
|
|
|
|
|
|
const { scrollHeight, scrollTop, clientHeight } = containerElement;
|
|
|
|
|
|
if (scrollHeight - scrollTop - clientHeight < 300) {
|
|
|
|
|
|
setTablePage((p) => p + 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2022-10-24 13:12:53 +11:00
|
|
|
|
DEBOUNCE_DELAY
|
2022-10-18 17:53:47 +11:00
|
|
|
|
);
|
2022-10-24 13:22:46 +11:00
|
|
|
|
// Check on mount and after fetch to see if the table is at the bottom
|
|
|
|
|
|
// for large screen heights
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
fetchMoreOnBottomReached(containerRef.current);
|
2022-11-21 17:16:32 +11:00
|
|
|
|
}, [
|
|
|
|
|
|
fetchMoreOnBottomReached,
|
|
|
|
|
|
tablePage,
|
|
|
|
|
|
tableNextPage.loading,
|
|
|
|
|
|
containerRef,
|
|
|
|
|
|
]);
|
2022-05-19 16:37:56 +10:00
|
|
|
|
|
2023-02-26 16:13:03 +05:30
|
|
|
|
// apply user default sort on first render
|
|
|
|
|
|
const [applySort, setApplySort] = useState(true);
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (applySort && Object.keys(tableSchema).length) {
|
|
|
|
|
|
const userDefaultSort = userSettings.tables?.[tableId]?.sorts || [];
|
|
|
|
|
|
setTableSorts(
|
|
|
|
|
|
userDefaultSort.length ? userDefaultSort : tableSchema.sorts || []
|
|
|
|
|
|
);
|
|
|
|
|
|
setApplySort(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [tableSchema, userSettings, tableId, setTableSorts, applySort]);
|
|
|
|
|
|
|
2022-05-19 16:37:56 +10:00
|
|
|
|
return (
|
2022-10-18 17:53:47 +11:00
|
|
|
|
<div
|
2022-11-21 17:16:32 +11:00
|
|
|
|
ref={(el) => setContainerEl(el)}
|
2022-10-19 12:54:31 +11:00
|
|
|
|
onScroll={(e) => fetchMoreOnBottomReached(e.target as HTMLDivElement)}
|
2022-10-18 17:53:47 +11:00
|
|
|
|
style={{ overflow: "auto", width: "100%", height: "100%" }}
|
|
|
|
|
|
>
|
2022-10-14 16:15:41 +11:00
|
|
|
|
<StyledTable
|
2022-10-17 17:57:26 +11:00
|
|
|
|
ref={gridRef}
|
2022-10-14 16:15:41 +11:00
|
|
|
|
role="grid"
|
2022-11-15 16:53:20 +11:00
|
|
|
|
aria-readonly={!canEditCells}
|
2022-10-17 17:57:26 +11:00
|
|
|
|
aria-colcount={columns.length}
|
|
|
|
|
|
aria-rowcount={tableRows.length + 1}
|
2022-10-24 15:38:35 +11:00
|
|
|
|
style={
|
|
|
|
|
|
{
|
|
|
|
|
|
width: table.getTotalSize(),
|
|
|
|
|
|
userSelect: "none",
|
|
|
|
|
|
"--row-height": `${tableSchema.rowHeight || DEFAULT_ROW_HEIGHT}px`,
|
|
|
|
|
|
} as any
|
|
|
|
|
|
}
|
2023-01-14 11:19:55 +00:00
|
|
|
|
onKeyDown={(e) => {
|
|
|
|
|
|
handleKeyDown(e);
|
2023-01-14 11:35:32 +00:00
|
|
|
|
hotKeysHandler(e);
|
2023-01-14 11:19:55 +00:00
|
|
|
|
}}
|
2022-10-14 16:15:41 +11:00
|
|
|
|
>
|
2022-10-17 17:57:26 +11:00
|
|
|
|
<div
|
|
|
|
|
|
className="thead"
|
|
|
|
|
|
role="rowgroup"
|
2022-10-21 14:58:25 +11:00
|
|
|
|
style={{
|
|
|
|
|
|
position: "sticky",
|
|
|
|
|
|
top: 0,
|
|
|
|
|
|
zIndex: 10,
|
|
|
|
|
|
padding: `0 ${TABLE_PADDING}px`,
|
|
|
|
|
|
}}
|
2022-10-17 17:57:26 +11:00
|
|
|
|
>
|
2022-11-15 18:14:44 +11:00
|
|
|
|
<TableHeader
|
2022-11-15 16:53:20 +11:00
|
|
|
|
headerGroups={table.getHeaderGroups()}
|
|
|
|
|
|
handleDropColumn={handleDropColumn}
|
|
|
|
|
|
canAddColumns={canAddColumns}
|
|
|
|
|
|
canEditColumns={canEditColumns}
|
|
|
|
|
|
lastFrozen={lastFrozen}
|
2022-11-17 18:17:43 +11:00
|
|
|
|
columnSizing={columnSizing}
|
2022-11-15 16:53:20 +11:00
|
|
|
|
/>
|
2022-10-14 16:15:41 +11:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2022-11-15 17:36:58 +11:00
|
|
|
|
{tableRows.length === 0 ? (
|
|
|
|
|
|
emptyState ?? <EmptyState sx={{ py: 8 }} />
|
|
|
|
|
|
) : (
|
2023-01-14 11:19:55 +00:00
|
|
|
|
<TableBody
|
|
|
|
|
|
containerEl={containerEl}
|
|
|
|
|
|
containerRef={containerRef}
|
|
|
|
|
|
leafColumns={leafColumns}
|
|
|
|
|
|
rows={rows}
|
|
|
|
|
|
canEditCells={canEditCells}
|
|
|
|
|
|
lastFrozen={lastFrozen}
|
|
|
|
|
|
columnSizing={columnSizing}
|
|
|
|
|
|
/>
|
2022-11-15 17:36:58 +11:00
|
|
|
|
)}
|
2022-10-14 16:15:41 +11:00
|
|
|
|
</StyledTable>
|
2022-10-25 15:49:38 +11:00
|
|
|
|
|
2022-11-11 15:49:47 +11:00
|
|
|
|
<div
|
|
|
|
|
|
id="rowy-table-editable-cell-description"
|
|
|
|
|
|
style={{ display: "none" }}
|
|
|
|
|
|
>
|
|
|
|
|
|
Press Enter to edit.
|
|
|
|
|
|
</div>
|
2022-10-31 16:17:21 +11:00
|
|
|
|
|
|
|
|
|
|
<ContextMenu />
|
2022-10-18 17:53:47 +11:00
|
|
|
|
</div>
|
2022-05-19 16:37:56 +10:00
|
|
|
|
);
|
|
|
|
|
|
}
|