dnd column ordering

This commit is contained in:
Shams mosowi
2020-11-02 12:36:45 +11:00
parent 9bb5ba2c97
commit a2cff390ec
4 changed files with 202 additions and 116 deletions

View File

@@ -33,6 +33,8 @@
"react-color": "^2.17.3",
"react-data-grid": "^7.0.0-canary.27",
"react-div-100vh": "^0.3.8",
"react-dnd": "^11.1.3",
"react-dnd-html5-backend": "^11.1.3",
"react-dom": "^16.9.0",
"react-dropzone": "^10.1.8",
"react-hook-form": "^6.5.0",

View File

@@ -1,7 +1,8 @@
import React from "react";
import clsx from "clsx";
import { Column } from "react-data-grid";
import { Column, HeaderRendererProps } from "react-data-grid";
import { useDrag, useDrop, DragObjectWithType } from "react-dnd";
import { useCombinedRefs } from "react-data-grid/lib/hooks";
import {
makeStyles,
createStyles,
@@ -85,7 +86,15 @@ const useStyles = makeStyles((theme) =>
})
);
const ColumnHeader: Column<any>["headerRenderer"] = ({ column }) => {
interface ColumnDragObject extends DragObjectWithType {
key: string;
}
export default function DraggableHeaderRenderer<R>({
column,
}: HeaderRendererProps<R> & {
onColumnsReorder: (sourceKey: string, targetKey: string) => void;
}) {
const classes = useStyles();
const {
@@ -94,6 +103,28 @@ const ColumnHeader: Column<any>["headerRenderer"] = ({ column }) => {
userClaims,
columnMenuRef,
} = useFiretableContext();
const [{ isDragging }, drag] = useDrag({
item: { key: column.key, type: "COLUMN_DRAG" },
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}),
});
const [{ isOver }, drop] = useDrop({
accept: "COLUMN_DRAG",
drop({ key, type }: ColumnDragObject) {
if (type === "COLUMN_DRAG") {
// onColumnsReorder(key, props.column.key);
tableActions?.column.reorder(key, column.key);
}
},
collect: (monitor) => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop(),
}),
});
const headerRef = useCombinedRefs(drag, drop);
if (!columnMenuRef || !tableState || !tableActions) return null;
const { orderBy } = tableState;
@@ -124,7 +155,18 @@ const ColumnHeader: Column<any>["headerRenderer"] = ({ column }) => {
};
return (
<Grid container className={classes.root} alignItems="center" wrap="nowrap">
<Grid
ref={headerRef}
container
className={classes.root}
style={{
opacity: isDragging ? 0.5 : 1,
backgroundColor: isOver ? "#ececec" : "inherit",
cursor: "move",
}}
alignItems="center"
wrap="nowrap"
>
<Tooltip
title={
<>
@@ -215,6 +257,16 @@ const ColumnHeader: Column<any>["headerRenderer"] = ({ column }) => {
)}
</Grid>
);
};
export default ColumnHeader;
// return (
// <div
// ref={useCombinedRefs(drag, drop)}
// style={{
// opacity: isDragging ? 0.5 : 1,
// backgroundColor: isOver ? '#ececec' : 'inherit',
// cursor: 'move'
// }}
// >
// {props.column.name}
// </div>
// );
}

View File

@@ -4,7 +4,8 @@ import _orderBy from "lodash/orderBy";
import _isEmpty from "lodash/isEmpty";
import _find from "lodash/find";
import _difference from "lodash/difference";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import BulkActions from "./BulkActions";
import "react-data-grid/dist/react-data-grid.css";
import DataGrid, {
@@ -29,7 +30,6 @@ import useWindowSize from "hooks/useWindowSize";
import useStyles from "./styles";
import { useAppContext } from "contexts/appContext";
import _get from "lodash/get";
// const Hotkeys = lazy(() => import("./HotKeys" /* webpackChunkName: "HotKeys" */));
export type FiretableColumn = Column<any> & {
isNew?: boolean;
@@ -116,8 +116,8 @@ export default function Table() {
),
[columns, tableState?.rows]
) ?? [];
const rowsContainerRef = useRef<HTMLDivElement>(null);
const rowsContainerRef = useRef<HTMLDivElement>(null);
const [selectedRowsSet, setSelectedRowsSet] = useState<Set<React.Key>>();
const [selectedRows, setSelectedRows] = useState<any[]>([]);
// Gets more rows when scrolled down.
@@ -161,113 +161,96 @@ export default function Table() {
/>
{!tableState.loadingColumns ? (
<DataGrid
onColumnResize={tableActions.column.resize}
onScroll={handleScroll}
ref={dataGridRef}
rows={rows}
columns={columns as any}
rowHeight={rowHeight ?? 43}
headerRowHeight={44}
className="rdg-light" // Handle dark mode in MUI theme
enableCellCopyPaste
enableCellDragAndDrop
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);
}}
onRowsUpdate={(e) => {
const { action, fromRow, toRow, updated, cellKey } = e;
switch (action) {
case "CELL_UPDATE":
break;
case "CELL_DRAG":
if (toRow > fromRow)
[...rows]
.splice(fromRow, toRow - fromRow + 1)
.forEach((row) => row.ref.update(updated));
if (toRow < fromRow)
[...rows]
.splice(toRow, fromRow - toRow + 1)
.forEach((row) => row.ref.update(updated));
break;
default:
break;
}
}}
onRowClick={(rowIdx, row, column) => {
if (sideDrawerRef?.current) {
sideDrawerRef.current.setCell({
row: rowIdx,
column: column.key as string,
<DndProvider backend={HTML5Backend}>
<DataGrid
onColumnResize={tableActions.column.resize}
onScroll={handleScroll}
ref={dataGridRef}
rows={rows}
columns={columns}
rowHeight={rowHeight ?? 43}
headerRowHeight={44}
className="rdg-light" // Handle dark mode in MUI theme
enableCellCopyPaste
enableCellDragAndDrop
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]);
});
}
}}
// onGridRowsUpdated={(event) => {
// const { action, cellKey, updated } = event;
// if (action === "CELL_UPDATE" && updated !== null)
// updateCell!(rows[event.toRow].ref, cellKey as string, updated);
// }}
// TODO: Investigate why setting a numeric value causes
// LOADING to pop up on screen when scrolling horizontally
// width={windowSize.width - DRAWER_COLLAPSED_WIDTH}
// minWidth={tableWidth}
// minHeight={windowSize.height - APP_BAR_HEIGHT - TABLE_HEADER_HEIGHT}
// enableCellCopyPaste
// enableCellDragAndDrop
//cellNavigationMode={CellNavigationMode.CHANGE_ROW}
// onCheckCellIsEditable={({ column }) =>
// Boolean(
// column.editable &&
// column.editor?.displayName &&
// column.editor?.displayName === "WithStyles(TextEditor)"
// )
// }
// enableCellSelect
// RowsContainer={(props) => (
// <>
// <div {...props} ref={rowsContainerRef} />
// <Grid
// container
// className={classes.loadingContainer}
// alignItems="center"
// justify="center"
// >
// {tableState.rows.length > 0 && tableState.loadingRows && (
// <CircularProgress disableShrink />
// )}
// </Grid>
// </>
// )}
/>
removedSelections.forEach((rowId) => {
setSelectedRows(
selectedRows.filter((row) => row.id !== rowId)
);
});
setSelectedRowsSet(newSelectedSet);
}}
onRowsUpdate={(e) => {
const { action, fromRow, toRow, updated, cellKey } = e;
switch (action) {
case "CELL_UPDATE":
break;
case "CELL_DRAG":
if (toRow > fromRow)
[...rows]
.splice(fromRow, toRow - fromRow + 1)
.forEach((row) => row.ref.update(updated));
if (toRow < fromRow)
[...rows]
.splice(toRow, fromRow - toRow + 1)
.forEach((row) => row.ref.update(updated));
break;
default:
break;
}
}}
onRowClick={(rowIdx, row, column) => {
if (sideDrawerRef?.current) {
sideDrawerRef.current.setCell({
row: rowIdx,
column: column.key as string,
});
}
}}
// TODO: Investigate why setting a numeric value causes
// LOADING to pop up on screen when scrolling horizontally
// width={windowSize.width - DRAWER_COLLAPSED_WIDTH}
// minWidth={tableWidth}
// minHeight={windowSize.height - APP_BAR_HEIGHT - TABLE_HEADER_HEIGHT}
// RowsContainer={(props) => (
// <>
// <div {...props} ref={rowsContainerRef} />
// <Grid
// container
// className={classes.loadingContainer}
// alignItems="center"
// justify="center"
// >
// {tableState.rows.length > 0 && tableState.loadingRows && (
// <CircularProgress disableShrink />
// )}
// </Grid>
// </>
// )}
/>
</DndProvider>
) : (
<Loading message="Fetching columns" />
)}

View File

@@ -2286,6 +2286,21 @@
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=
"@react-dnd/asap@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-4.0.0.tgz#b300eeed83e9801f51bd66b0337c9a6f04548651"
integrity sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ==
"@react-dnd/invariant@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-2.0.0.tgz#09d2e81cd39e0e767d7da62df9325860f24e517e"
integrity sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==
"@react-dnd/shallowequal@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz#a3031eb54129f2c66b2753f8404266ec7bf67f0a"
integrity sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
@@ -2508,6 +2523,14 @@
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.5.tgz#527d20ef68571a4af02ed74350164e7a67544860"
integrity sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw==
"@types/hoist-non-react-statics@^3.3.1":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
@@ -5481,6 +5504,15 @@ dir-glob@2.0.0:
arrify "^1.0.1"
path-type "^3.0.0"
dnd-core@^11.1.3:
version "11.1.3"
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-11.1.3.tgz#f92099ba7245e49729d2433157031a6267afcc98"
integrity sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA==
dependencies:
"@react-dnd/asap" "^4.0.0"
"@react-dnd/invariant" "^2.0.0"
redux "^4.0.4"
dns-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
@@ -12350,6 +12382,23 @@ react-div-100vh@^0.3.8:
resolved "https://registry.yarnpkg.com/react-div-100vh/-/react-div-100vh-0.3.8.tgz#54e4c32d0286a65e92367fc0a07cc3f2f00739d8"
integrity sha512-1kDFW+HXYpfac1tfJ4BcQmgTSeTtLVs2FO2ZNHcwLIga+oVluexUEISCBJvr9xq98DK8tcygY3259EvIy5O+3g==
react-dnd-html5-backend@^11.1.3:
version "11.1.3"
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz#2749f04f416ec230ea193f5c1fbea2de7dffb8f7"
integrity sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw==
dependencies:
dnd-core "^11.1.3"
react-dnd@^11.1.3:
version "11.1.3"
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-11.1.3.tgz#f9844f5699ccc55dfc81462c2c19f726e670c1af"
integrity sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ==
dependencies:
"@react-dnd/shallowequal" "^2.0.0"
"@types/hoist-non-react-statics" "^3.3.1"
dnd-core "^11.1.3"
hoist-non-react-statics "^3.3.0"
react-dom@^16.9.0:
version "16.13.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.0.tgz#cdde54b48eb9e8a0ca1b3dc9943d9bb409b81866"