Files
rowy/src/components/Table/index.tsx
2021-10-29 11:47:12 +11:00

260 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect, useRef, useMemo, useState } from "react";
import _orderBy from "lodash/orderBy";
import _find from "lodash/find";
import _findIndex from "lodash/findIndex";
import _difference from "lodash/difference";
import _get from "lodash/get";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
// import "react-data-grid/dist/react-data-grid.css";
import DataGrid, {
Column,
SelectColumn as _SelectColumn,
} from "react-data-grid";
import Loading from "@src/components/Loading";
import TableHeader from "./TableHeader";
import ColumnHeader from "./ColumnHeader";
import ColumnMenu from "./ColumnMenu";
import FinalColumnHeader from "./FinalColumnHeader";
import FinalColumn from "./formatters/FinalColumn";
// import BulkActions from "./BulkActions";
import { getFieldProp } from "@src/components/fields";
import { FieldType } from "@src/constants/fields";
import { formatSubTableName } from "@src/utils/fns";
import { useAppContext } from "@src/contexts/AppContext";
import { useProjectContext } from "@src/contexts/ProjectContext";
import useWindowSize from "@src/hooks/useWindowSize";
import useStyles from "./styles";
export type TableColumn = Column<any> & {
isNew?: boolean;
type: FieldType;
[key: string]: any;
};
const rowKeyGetter = (row: any) => row.id;
const SelectColumn = { ..._SelectColumn, width: 42, maxWidth: 42 };
export default function Table() {
const classes = useStyles();
const { tableState, tableActions, dataGridRef, sideDrawerRef, updateCell } =
useProjectContext();
const { userDoc } = useAppContext();
const userDocHiddenFields =
userDoc.state.doc?.tables?.[formatSubTableName(tableState?.tablePath)]
?.hiddenFields ?? [];
const [columns, setColumns] = useState<TableColumn[]>([]);
useEffect(() => {
if (!tableState?.loadingColumns && tableState?.columns) {
const _columns = _orderBy(
Object.values(tableState?.columns).filter(
(column: any) => !column.hidden && column.key
),
"index"
)
.map((column: any) => ({
draggable: true,
editable: true,
resizable: true,
frozen: column.fixed,
headerRenderer: ColumnHeader,
formatter:
getFieldProp(
"TableCell",
column.config?.renderFieldType ?? column.type
) ??
function InDev() {
return null;
},
editor:
getFieldProp(
"TableEditor",
column.config?.renderFieldType ?? column.type
) ??
function InDev() {
return null;
},
...column,
width: (column.width as number)
? (column.width as number) > 380
? 380
: (column.width as number)
: 150,
}))
.filter((column) => !userDocHiddenFields.includes(column.key));
setColumns([
// TODO: ENABLE ONCE BULK ACTIONS READY
// SelectColumn,
..._columns,
{
isNew: true,
key: "new",
name: "Add column",
type: FieldType.last,
index: _columns.length ?? 0,
width: 204,
headerRenderer: FinalColumnHeader,
headerCellClass: "final-column-header",
cellClass: "final-column-cell",
formatter: FinalColumn,
editable: false,
},
]);
}
}, [
tableState?.loadingColumns,
tableState?.columns,
JSON.stringify(userDocHiddenFields),
]);
const rows =
useMemo(
() =>
tableState?.rows.map((row) =>
columns.reduce(
(acc, currColumn) => {
if (currColumn.key.includes(".")) {
return {
...acc,
[currColumn.key]: _get(row, currColumn.key),
};
} else return acc;
},
{ ...row, id: row.id as string, ref: row.ref }
)
),
[columns, tableState?.rows]
) ?? [];
const rowsContainerRef = useRef<HTMLDivElement>(null);
const [selectedRowsSet, setSelectedRowsSet] = useState<Set<React.Key>>();
const [selectedRows, setSelectedRows] = useState<any[]>([]);
// Gets more rows when scrolled down.
// https://github.com/adazzle/react-data-grid/blob/ead05032da79d7e2b86e37cdb9af27f2a4d80b90/stories/demos/AllFeatures.tsx#L60
const handleScroll = (event: React.UIEvent<HTMLDivElement>) => {
const target = event.target as HTMLDivElement;
const offset = 800;
const isAtBottom =
target.clientHeight + target.scrollTop >= target.scrollHeight - offset;
if (!isAtBottom) return;
// Prevent calling more rows when theyve already been called
if (tableState!.loadingRows) return;
// Call for 30 more rows. Note we dont know here if there are no more
// rows left in the database. This is done in the useTable hook.
tableActions?.row.more(30);
};
const windowSize = useWindowSize();
if (!windowSize || !windowSize.height) return <></>;
if (!tableActions || !tableState) return <></>;
const rowHeight = tableState.config.rowHeight;
return (
<>
{/* <Suspense fallback={<Loading message="Loading header" />}>
<Hotkeys selectedCell={selectedCell} />
</Suspense> */}
<div className={classes.tableWrapper} ref={rowsContainerRef}>
<TableHeader />
{!tableState.loadingColumns ? (
<DndProvider backend={HTML5Backend}>
<DataGrid
onColumnResize={tableActions.column.resize}
onScroll={handleScroll}
ref={dataGridRef}
rows={rows}
columns={columns}
rowHeight={rowHeight ?? 42}
headerRowHeight={42}
className="rdg-light" // Handle dark mode in MUI theme
cellNavigationMode="LOOP_OVER_ROW"
rowKeyGetter={rowKeyGetter}
selectedRows={selectedRowsSet}
onSelectedRowsChange={(newSelectedSet) => {
const newSelectedArray = newSelectedSet
? [...newSelectedSet]
: [];
const prevSelectedRowsArray = selectedRowsSet
? [...selectedRowsSet]
: [];
const addedSelections = _difference(
newSelectedArray,
prevSelectedRowsArray
);
const removedSelections = _difference(
prevSelectedRowsArray,
newSelectedArray
);
addedSelections.forEach((id) => {
const newRow = _find(rows, { id });
setSelectedRows([...selectedRows, newRow]);
});
removedSelections.forEach((rowId) => {
setSelectedRows(
selectedRows.filter((row) => row.id !== rowId)
);
});
setSelectedRowsSet(newSelectedSet);
}}
// onRowsChange={() => {
//console.log('onRowsChange',rows)
// }}
// TODO: onFill={(e) => {
// console.log("onFill", e);
// const { columnKey, sourceRow, targetRows } = e;
// if (updateCell)
// targetRows.forEach((row) =>
// updateCell(row.ref, columnKey, sourceRow[columnKey])
// );
// return [];
// }}
onPaste={(e) => {
const copiedValue = e.sourceRow[e.sourceColumnKey];
if (updateCell) {
updateCell(e.targetRow.ref, e.targetColumnKey, copiedValue);
}
}}
onRowClick={(row, column) => {
if (sideDrawerRef?.current) {
sideDrawerRef.current.setCell({
row: _findIndex(tableState.rows, { id: row.id }),
column: column.key,
});
}
}}
/>
</DndProvider>
) : (
<Loading message="Fetching columns" />
)}
</div>
<ColumnMenu />
{/* <BulkActions
selectedRows={selectedRows}
columns={columns}
clearSelection={() => {
setSelectedRowsSet(new Set());
setSelectedRows([]);
}}
/> */}
</>
);
}