Skip to content

Commit 8e59fd2

Browse files
committed
feat(datagrid-web): add multipage selection to dg2
1 parent 92b7439 commit 8e59fd2

16 files changed

+558
-30
lines changed

packages/pluggableWidgets/datagrid-web/src/Datagrid.editorConfig.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ export function getProperties(
154154
}
155155

156156
function hideSelectionProperties(defaultProperties: Properties, values: DatagridPreviewProps): void {
157-
const { itemSelection, itemSelectionMethod } = values;
157+
const { itemSelection, itemSelectionMethod, selectAllPagesEnabled } = values;
158158

159159
if (itemSelection === "None") {
160160
hidePropertiesIn(defaultProperties, values, ["itemSelectionMethod", "itemSelectionMode", "onSelectionChange"]);
@@ -170,6 +170,14 @@ function hideSelectionProperties(defaultProperties: Properties, values: Datagrid
170170

171171
if (itemSelection !== "Multi") {
172172
hidePropertyIn(defaultProperties, values, "keepSelection");
173+
hidePropertyIn(defaultProperties, values, "selectAllPagesEnabled");
174+
}
175+
176+
if (!selectAllPagesEnabled) {
177+
hidePropertyIn(defaultProperties, values, "selectAllPagesBufferSize");
178+
hidePropertyIn(defaultProperties, values, "selectAllPagesLabel");
179+
hidePropertyIn(defaultProperties, values, "selectingAllLabel");
180+
hidePropertyIn(defaultProperties, values, "cancelSelectionLabel");
173181
}
174182
}
175183

packages/pluggableWidgets/datagrid-web/src/Datagrid.editorPreview.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ColumnPreview } from "./helpers/ColumnPreview";
1818
import { DatagridContext } from "./helpers/root-context";
1919
import { useSelectActionHelper } from "./helpers/SelectActionHelper";
2020
import { GridBasicData } from "./helpers/state/GridBasicData";
21+
import { SelectAllProgressStore } from "./features/multi-page-selection/SelectAllProgressStore";
2122

2223
import { SelectionCountStore } from "@mendix/widget-plugin-grid/selection/stores/SelectionCountStore";
2324
import "./ui/DatagridPreview.scss";
@@ -97,7 +98,9 @@ export function preview(props: DatagridPreviewProps): ReactElement {
9798
cellEventsController: eventsController,
9899
checkboxEventsController: eventsController,
99100
focusController,
100-
selectionCountStore
101+
selectionCountStore,
102+
selectAllProgressStore: new SelectAllProgressStore(),
103+
rootStore: {} as any // Mock for preview
101104
};
102105
});
103106

packages/pluggableWidgets/datagrid-web/src/Datagrid.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ const Container = observer((props: Props): ReactElement => {
7373
cellEventsController,
7474
checkboxEventsController,
7575
focusController,
76-
selectionCountStore: rootStore.selectionCountStore
76+
selectionCountStore: rootStore.selectionCountStore,
77+
selectAllProgressStore: rootStore.selectAllProgressStore,
78+
rootStore
7779
};
7880
});
7981

packages/pluggableWidgets/datagrid-web/src/Datagrid.xml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,35 @@
5353
<caption>Keep selection</caption>
5454
<description>If enabled, selected items will stay selected unless cleared by the user or a Nanoflow.</description>
5555
</property>
56+
<property key="selectAllPagesEnabled" type="boolean" defaultValue="false">
57+
<caption>Enable select all pages</caption>
58+
<description>Allow select all through multiple pages (based on current filter). Only works if total count is known.</description>
59+
</property>
60+
<property key="selectAllPagesBufferSize" type="integer" defaultValue="500">
61+
<caption>Select all buffer size</caption>
62+
<description>Batch size for processing select all operations. If total count is less than buffer size, selection is immediate.</description>
63+
</property>
64+
<property key="selectAllPagesLabel" type="textTemplate" required="false">
65+
<caption>Select all pages label</caption>
66+
<description>Text shown when selecting all items across pages. Use %d for total count placeholder.</description>
67+
<translations>
68+
<translation lang="en_US">Select all %d items</translation>
69+
</translations>
70+
</property>
71+
<property key="selectingAllLabel" type="textTemplate" required="false">
72+
<caption>Selecting all label</caption>
73+
<description>Label shown in the progress dialog when selecting all items</description>
74+
<translations>
75+
<translation lang="en_US">Selecting all items...</translation>
76+
</translations>
77+
</property>
78+
<property key="cancelSelectionLabel" type="textTemplate" required="false">
79+
<caption>Cancel selection label</caption>
80+
<description>Label for the cancel button in the selection progress dialog</description>
81+
<translations>
82+
<translation lang="en_US">Cancel selection</translation>
83+
</translations>
84+
</property>
5685
<property key="loadingType" type="enumeration" defaultValue="spinner" required="true">
5786
<caption>Loading type</caption>
5887
<description />

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import { ThreeStateCheckBox } from "@mendix/widget-plugin-component-kit/ThreeStateCheckBox";
2-
import { createElement, Fragment, ReactElement, useCallback } from "react";
2+
import { createElement, Fragment, ReactElement } from "react";
33
import { useDatagridRootScope } from "../helpers/root-context";
44

55
export function CheckboxColumnHeader(): ReactElement {
6-
const { selectActionHelper, basicData } = useDatagridRootScope();
6+
const { selectActionHelper, basicData, selectAllProgressStore, rootStore } = useDatagridRootScope();
77
const { showCheckboxColumn, showSelectAllToggle, onSelectAll } = selectActionHelper;
88
const { selectionStatus, selectAllRowsLabel } = basicData;
99

10-
const onChange = useCallback(() => onSelectAll(), [onSelectAll]);
11-
1210
if (showCheckboxColumn === false) {
1311
return <Fragment />;
1412
}
@@ -20,10 +18,30 @@ export function CheckboxColumnHeader(): ReactElement {
2018
throw new Error("Don't know how to render checkbox with selectionStatus=unknown");
2119
}
2220

21+
const handleHeaderToggle = async (): Promise<void> => {
22+
if (selectAllProgressStore.selecting) {
23+
return;
24+
}
25+
26+
if (selectActionHelper.canSelectAllPages && selectionStatus !== "none") {
27+
// Toggle off still uses normal flow
28+
onSelectAll();
29+
return;
30+
}
31+
32+
if (selectActionHelper.canSelectAllPages && selectionStatus === "none") {
33+
// Delegate to root store orchestration
34+
await rootStore?.startMultiPageSelectAll(selectActionHelper);
35+
return;
36+
}
37+
38+
onSelectAll();
39+
};
40+
2341
checkbox = (
2442
<ThreeStateCheckBox
2543
value={selectionStatus}
26-
onChange={onChange}
44+
onChange={handleHeaderToggle}
2745
aria-label={selectAllRowsLabel ?? "Select all rows"}
2846
/>
2947
);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { createElement, ReactElement } from "react";
2+
import { PseudoModal } from "./PseudoModal";
3+
import { ExportAlert } from "./ExportAlert";
4+
5+
export type SelectionProgressDialogProps = {
6+
open: boolean;
7+
selectingLabel: string;
8+
cancelLabel: string;
9+
onCancel: () => void;
10+
progress: number;
11+
total: number;
12+
};
13+
14+
export function SelectionProgressDialog({
15+
open,
16+
selectingLabel,
17+
cancelLabel,
18+
onCancel,
19+
progress,
20+
total
21+
}: SelectionProgressDialogProps): ReactElement | null {
22+
if (!open) return null;
23+
return (
24+
<PseudoModal>
25+
<ExportAlert
26+
alertLabel={selectingLabel}
27+
cancelLabel={cancelLabel}
28+
failed={false}
29+
onCancel={onCancel}
30+
progress={progress}
31+
total={total}
32+
/>
33+
</PseudoModal>
34+
);
35+
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { WidgetContent } from "./WidgetContent";
2424
import { WidgetFooter } from "./WidgetFooter";
2525
import { WidgetHeader } from "./WidgetHeader";
2626
import { WidgetRoot } from "./WidgetRoot";
27+
import { SelectionProgressDialog } from "./SelectionProgressDialog";
2728
import { WidgetTopBar } from "./WidgetTopBar";
2829

2930
export interface WidgetProps<C extends GridColumn, T extends ObjectItem = ObjectItem> {
@@ -80,7 +81,7 @@ export interface WidgetProps<C extends GridColumn, T extends ObjectItem = Object
8081

8182
export const Widget = observer(<C extends GridColumn>(props: WidgetProps<C>): ReactElement => {
8283
const { className, exporting, numberOfItems, onExportCancel, selectActionHelper } = props;
83-
const { basicData } = useDatagridRootScope();
84+
const { basicData, selectAllProgressStore, rootStore } = useDatagridRootScope();
8485

8586
const selectionEnabled = selectActionHelper.selectionType !== "None";
8687

@@ -91,8 +92,17 @@ export const Widget = observer(<C extends GridColumn>(props: WidgetProps<C>): Re
9192
selection={selectionEnabled}
9293
style={{}}
9394
exporting={exporting}
95+
selectingAllPages={selectAllProgressStore.selecting}
9496
>
9597
<Main {...props} data={exporting ? [] : props.data} />
98+
<SelectionProgressDialog
99+
open={selectAllProgressStore.selecting}
100+
selectingLabel={basicData.selectingAllLabel ?? "Selecting all items..."}
101+
cancelLabel={basicData.cancelSelectionLabel ?? "Cancel selection"}
102+
onCancel={() => rootStore.abortMultiPageSelect()}
103+
progress={selectAllProgressStore.loaded}
104+
total={selectAllProgressStore.total}
105+
/>
96106
{exporting && (
97107
<ExportWidget
98108
alertLabel={basicData.exportDialogLabel ?? "Export progress"}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,19 @@ export interface WidgetRootProps extends P {
99
selection?: boolean;
1010
selectionMethod: SelectionMethod;
1111
exporting?: boolean;
12+
selectingAllPages?: boolean;
1213
}
1314

1415
export function WidgetRoot(props: WidgetRootProps): ReactElement {
1516
const ref = useRef<HTMLDivElement>(null);
16-
const { className, selectionMethod, selection, exporting, children, ...rest } = props;
17+
const { className, selectionMethod, selection, exporting, selectingAllPages, children, ...rest } = props;
1718
const style = useMemo(() => {
1819
const s = { ...props.style };
19-
if (exporting && ref.current) {
20+
if ((exporting || selectingAllPages) && ref.current) {
2021
s.height = ref.current.offsetHeight;
2122
}
2223
return s;
23-
}, [props.style, exporting]);
24+
}, [props.style, exporting, selectingAllPages]);
2425

2526
return (
2627
<div
@@ -29,6 +30,7 @@ export function WidgetRoot(props: WidgetRootProps): ReactElement {
2930
style={style}
3031
className={classNames(className, "widget-datagrid", {
3132
"widget-datagrid-exporting": exporting,
33+
"widget-datagrid-selecting-all-pages": selectingAllPages,
3234
"widget-datagrid-selectable-rows": selection,
3335
"widget-datagrid-selection-method-checkbox": selection && selectionMethod === "checkbox",
3436
"widget-datagrid-selection-method-click": selection && selectionMethod === "rowClick"

0 commit comments

Comments
 (0)