Skip to content

Commit 8cc0a3e

Browse files
committed
refactor: update components to use new state management
1 parent c284f0a commit 8cc0a3e

File tree

4 files changed

+160
-247
lines changed

4 files changed

+160
-247
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import classNames from "classnames";
2+
import { HTMLAttributes, KeyboardEvent, ReactElement, ReactNode, useMemo } from "react";
3+
import { FaArrowsAltV } from "./icons/FaArrowsAltV";
4+
import { FaLongArrowAltDown } from "./icons/FaLongArrowAltDown";
5+
import { FaLongArrowAltUp } from "./icons/FaLongArrowAltUp";
6+
7+
import ColumnHeader from "./ColumnHeader";
8+
9+
import { useColumn, useColumnsStore, useDatagridConfig, useHeaderDragDrop } from "../model/hooks/injection-hooks";
10+
import { GridColumn } from "../typings/GridColumn";
11+
import { ColumnResizerProps } from "./ColumnResizer";
12+
import { ColumnHeaderViewModel } from "../features/column/ColumnHeader.viewModel";
13+
import { observer } from "mobx-react-lite";
14+
15+
export interface ColumnContainerProps {
16+
isLast?: boolean;
17+
resizer: ReactElement<ColumnResizerProps>;
18+
}
19+
20+
export const ColumnContainer = observer(function ColumnContainer(props: ColumnContainerProps): ReactElement {
21+
const { columnsFilterable, id: gridId } = useDatagridConfig();
22+
const columnsStore = useColumnsStore();
23+
const column = useColumn();
24+
const { canDrag, canSort } = column;
25+
26+
const headerDragDropStore = useHeaderDragDrop();
27+
const columnHeaderVM = useMemo(
28+
() =>
29+
new ColumnHeaderViewModel({
30+
dndStore: headerDragDropStore,
31+
columnsStore,
32+
columnsDraggable: canDrag
33+
}),
34+
[headerDragDropStore, columnsStore, canDrag]
35+
);
36+
const draggableProps = columnHeaderVM.draggableProps;
37+
const dropTarget = columnHeaderVM.dropTarget;
38+
const isDragging = columnHeaderVM.dragging;
39+
40+
const sortProps = canSort ? getSortProps(column) : null;
41+
const caption = column.header.trim();
42+
43+
return (
44+
<div
45+
aria-sort={getAriaSort(canSort, column)}
46+
className={classNames("th", {
47+
[`drop-${dropTarget?.[1]}`]: column.columnId === dropTarget?.[0],
48+
dragging: column.columnId === isDragging?.[1],
49+
"dragging-over-self": column.columnId === isDragging?.[1] && !dropTarget
50+
})}
51+
role="columnheader"
52+
style={!canSort ? { cursor: "unset" } : undefined}
53+
title={caption}
54+
ref={ref => column.setHeaderElementRef(ref)}
55+
data-column-id={column.columnId}
56+
onDrop={draggableProps.onDrop}
57+
onDragEnter={draggableProps.onDragEnter}
58+
onDragOver={draggableProps.onDragOver}
59+
>
60+
<div
61+
className={classNames("column-container")}
62+
id={`${gridId}-column${column.columnId}`}
63+
draggable={draggableProps.draggable}
64+
onDragStart={draggableProps.onDragStart}
65+
onDragEnd={draggableProps.onDragEnd}
66+
>
67+
<ColumnHeader
68+
sortProps={sortProps}
69+
canSort={canSort}
70+
caption={caption}
71+
isDragging={isDragging}
72+
columnAlignment={column.alignment}
73+
>
74+
{canSort ? <SortIcon /> : null}
75+
</ColumnHeader>
76+
{columnsFilterable && (
77+
<div className="filter" style={{ pointerEvents: isDragging ? "none" : undefined }}>
78+
{columnsStore.columnFilters[column.columnIndex]?.renderFilterWidgets()}
79+
</div>
80+
)}
81+
</div>
82+
{column.canResize ? props.resizer : null}
83+
</div>
84+
);
85+
});
86+
87+
function SortIcon(): ReactNode {
88+
const column = useColumn();
89+
switch (column.sortDir) {
90+
case "asc":
91+
return <FaLongArrowAltUp />;
92+
case "desc":
93+
return <FaLongArrowAltDown />;
94+
default:
95+
return <FaArrowsAltV />;
96+
}
97+
}
98+
99+
function getAriaSort(canSort: boolean, column: GridColumn): "ascending" | "descending" | "none" | undefined {
100+
if (!canSort) {
101+
return undefined;
102+
}
103+
104+
switch (column.sortDir) {
105+
case "asc":
106+
return "ascending";
107+
case "desc":
108+
return "descending";
109+
default:
110+
return "none";
111+
}
112+
}
113+
114+
function getSortProps(column: GridColumn): HTMLAttributes<HTMLDivElement> {
115+
return {
116+
onClick: () => {
117+
column.toggleSort();
118+
},
119+
onKeyDown: (e: KeyboardEvent<HTMLDivElement>) => {
120+
if (e.key === "Enter" || e.key === " ") {
121+
e.preventDefault();
122+
column.toggleSort();
123+
}
124+
},
125+
role: "button",
126+
tabIndex: 0
127+
};
128+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import classNames from "classnames";
2+
import { HTMLAttributes, ReactElement, ReactNode } from "react";
3+
4+
export interface ColumnHeaderProps {
5+
children?: ReactNode;
6+
sortProps?: HTMLAttributes<HTMLDivElement> | null;
7+
canSort: boolean;
8+
caption: string;
9+
isDragging?: [string | undefined, string, string | undefined] | undefined;
10+
columnAlignment?: "left" | "center" | "right";
11+
}
12+
13+
export default function ColumnHeader(props: ColumnHeaderProps): ReactElement {
14+
return (
15+
<div
16+
className={classNames(
17+
"column-header",
18+
{ clickable: props.canSort },
19+
`align-column-${props.columnAlignment}`
20+
)}
21+
style={{ pointerEvents: props.isDragging ? "none" : undefined }}
22+
{...props.sortProps}
23+
aria-label={props.canSort ? "sort " + props.caption : props.caption}
24+
>
25+
<span>{props.caption.length > 0 ? props.caption : "\u00a0"}</span>
26+
{props.children}
27+
</div>
28+
);
29+
}

packages/pluggableWidgets/datagrid-web/src/components/GridHeader.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1-
import { ReactElement, useState } from "react";
1+
import { ReactElement } from "react";
22
import { useColumnsStore, useDatagridConfig } from "../model/hooks/injection-hooks";
3-
import { ColumnId } from "../typings/GridColumn";
43
import { CheckboxColumnHeader } from "./CheckboxColumnHeader";
54
import { ColumnProvider } from "./ColumnProvider";
65
import { ColumnResizer } from "./ColumnResizer";
76
import { ColumnSelector } from "./ColumnSelector";
8-
import { Header } from "./Header";
7+
import { ColumnContainer } from "./ColumnContainer";
98
import { HeaderSkeletonLoader } from "./loader/HeaderSkeletonLoader";
109

1110
export function GridHeader(): ReactElement {
1211
const { columnsHidable, id: gridId } = useDatagridConfig();
1312
const columnsStore = useColumnsStore();
1413
const columns = columnsStore.visibleColumns;
15-
const [dragOver, setDragOver] = useState<[ColumnId, "before" | "after"] | undefined>(undefined);
16-
const [isDragging, setIsDragging] = useState<[ColumnId | undefined, ColumnId, ColumnId | undefined] | undefined>();
1714

1815
if (!columnsStore.loaded) {
1916
return <HeaderSkeletonLoader size={columns.length} />;
@@ -25,18 +22,14 @@ export function GridHeader(): ReactElement {
2522
<CheckboxColumnHeader key="headers_column_select_all" />
2623
{columns.map(column => (
2724
<ColumnProvider column={column} key={`${column.columnId}`}>
28-
<Header
29-
dropTarget={dragOver}
30-
isDragging={isDragging}
25+
<ColumnContainer
3126
resizer={
3227
<ColumnResizer
3328
onResizeStart={() => columnsStore.setIsResizing(true)}
3429
onResizeEnds={() => columnsStore.setIsResizing(false)}
3530
setColumnWidth={(width: number) => column.setSize(width)}
3631
/>
3732
}
38-
setDropTarget={setDragOver}
39-
setIsDragging={setIsDragging}
4033
/>
4134
</ColumnProvider>
4235
))}

0 commit comments

Comments
 (0)