Skip to content

Commit b2c9f9a

Browse files
committed
feat: add sab vm
1 parent cffff18 commit b2c9f9a

File tree

8 files changed

+180
-51
lines changed

8 files changed

+180
-51
lines changed

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

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -219,27 +219,13 @@
219219
<description>If enabled, selected items will stay selected unless cleared by the user or a Nanoflow.</description>
220220
</property>
221221
<property key="selectAllPagesEnabled" type="boolean" defaultValue="false">
222-
<caption>Enable select all pages</caption>
223-
<description>Allow select all through multiple pages (based on current filter). Only works if total count is known.</description>
222+
<caption>Enable select all</caption>
223+
<description>Allow select all through multiple pages (based on current filter).</description>
224224
</property>
225-
<property key="selectAllPagesPageSize" type="integer" defaultValue="500">
225+
<property key="selectAllPagesPageSize" type="integer" defaultValue="1024">
226226
<caption>Select all page size</caption>
227227
<description>When selecting items from a large data source, items are selected in batches. This setting controls the size of the batches.</description>
228228
</property>
229-
<property key="selectingAllLabel" type="textTemplate" required="false">
230-
<caption>Selecting all label</caption>
231-
<description>Label shown in the progress dialog when selecting all items</description>
232-
<translations>
233-
<translation lang="en_US">Selecting all items...</translation>
234-
</translations>
235-
</property>
236-
<property key="cancelSelectionLabel" type="textTemplate" required="false">
237-
<caption>Cancel selection label</caption>
238-
<description>Label for the cancel button in the selection progress dialog</description>
239-
<translations>
240-
<translation lang="en_US">Cancel selection</translation>
241-
</translations>
242-
</property>
243229
</propertyGroup>
244230
<propertyGroup caption="Loading state">
245231
<property key="loadingType" type="enumeration" defaultValue="spinner" required="true">
@@ -363,7 +349,7 @@
363349
</property>
364350
</propertyGroup>
365351
</propertyGroup>
366-
<propertyGroup caption="Accessibility">
352+
<propertyGroup caption="Texts">
367353
<propertyGroup caption="Aria labels">
368354
<property key="filterSectionTitle" type="textTemplate" required="false">
369355
<caption>Filter section</caption>
@@ -384,19 +370,35 @@
384370
</translations>
385371
</property>
386372
<property key="selectRowLabel" type="textTemplate" required="false">
387-
<caption>Select row</caption>
373+
<caption>Select row label</caption>
388374
<description>If selection is enabled, assistive technology will read this upon reaching a checkbox.</description>
389375
<translations>
390376
<translation lang="en_US">Select row</translation>
391377
</translations>
392378
</property>
393379
<property key="selectAllRowsLabel" type="textTemplate" required="false">
394-
<caption>Select all row</caption>
380+
<caption>Select all label</caption>
395381
<description>If selection is enabled, assistive technology will read this upon reaching 'Select all' checkbox.</description>
396382
<translations>
397383
<translation lang="en_US">Select all rows</translation>
398384
</translations>
399385
</property>
386+
<property key="selectingAllLabel" type="textTemplate" required="false">
387+
<caption>Selecting all label</caption>
388+
<description>ARIA label for the progress dialog when selecting all items</description>
389+
<translations>
390+
<translation lang="en_US">Selecting all items...</translation>
391+
</translations>
392+
</property>
393+
<property key="cancelSelectionLabel" type="textTemplate" required="false">
394+
<caption>Cancel selection label</caption>
395+
<description>ARIA label for the cancel button in the selection progress dialog</description>
396+
<translations>
397+
<translation lang="en_US">Cancel selection</translation>
398+
</translations>
399+
</property>
400+
</propertyGroup>
401+
<propertyGroup caption="Captions">
400402
<property key="selectedCountTemplateSingular" type="textTemplate" required="false">
401403
<caption>Row count singular</caption>
402404
<description>Must include '%d' to denote number position ('%d row selected')</description>
@@ -405,6 +407,27 @@
405407
<caption>Row count plural</caption>
406408
<description>Must include '%d' to denote number position ('%d rows selected')</description>
407409
</property>
410+
<property key="selectAllTemplate" type="textTemplate">
411+
<caption>Select all</caption>
412+
<description>This caption used when total count is available.</description>
413+
<translations>
414+
<translation lang="en_US">Select all %d items in the data source.</translation>
415+
</translations>
416+
</property>
417+
<property key="selectRemainingTemplate" type="textTemplate">
418+
<caption>2</caption>
419+
<description />
420+
<translations>
421+
<translation lang="en_US">Select remaining items in the data source.</translation>
422+
</translations>
423+
</property>
424+
<property key="clearSelectionCaption" type="textTemplate">
425+
<caption>Clear selection caption</caption>
426+
<description />
427+
<translations>
428+
<translation lang="en_US">Clear selection</translation>
429+
</translations>
430+
</property>
408431
</propertyGroup>
409432
</propertyGroup>
410433
</properties>
Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
1+
import { If } from "@mendix/widget-plugin-component-kit/If";
12
import { observer } from "mobx-react-lite";
23
import { createElement } from "react";
34
import { useDatagridRootScope } from "../helpers/root-context";
45

56
export const SelectAllBar = observer(function SelectAllBar(): React.ReactNode {
6-
const {
7-
selectAllController,
8-
basicData: { selectionStatus }
9-
} = useDatagridRootScope();
7+
const { selectAllBarViewModel } = useDatagridRootScope();
8+
const { barVisible, selectionCountText, clearVisible, clearSelectionLabel, selectAllVisible, selectAllLabel } =
9+
selectAllBarViewModel;
1010

11-
if (selectionStatus === "unknown") return null;
12-
13-
if (selectionStatus === "none") return null;
11+
if (!barVisible) return null;
1412

1513
return (
1614
<div className="widget-datagrid-select-all-bar">
17-
<button onClick={() => selectAllController.selectAllPages()}>Select remaining</button>
15+
{selectionCountText}&nbsp;
16+
<If condition={selectAllVisible}>
17+
<button className="btn" onClick={() => selectAllBarViewModel.onSelectAll()}>
18+
{selectAllLabel}
19+
</button>
20+
</If>
21+
<If condition={clearVisible}>
22+
<button className="btn" onClick={() => selectAllBarViewModel.onClear()}>
23+
{clearSelectionLabel}
24+
</button>
25+
</If>
1826
</div>
1927
);
2028
});

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ const SelectionCounter = observer(function SelectionCounter() {
4444
const { selectionCountStore, selectActionHelper } = useDatagridRootScope();
4545

4646
return (
47-
<If condition={selectionCountStore.displayCount !== ""}>
48-
<span className="widget-datagrid-selection-count">{selectionCountStore.displayCount}</span>&nbsp;|&nbsp;
47+
<If condition={selectionCountStore.selectedCountText !== ""}>
48+
<span className="widget-datagrid-selection-count">{selectionCountStore.selectedCountText}</span>
49+
&nbsp;|&nbsp;
4950
<button className="widget-datagrid-clear-selection" onClick={selectActionHelper.onClearSelection}>
5051
Clear selection
5152
</button>

packages/pluggableWidgets/datagrid-web/src/helpers/root-context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { createContext, useContext } from "react";
66
import { GridBasicData } from "../helpers/state/GridBasicData";
77
import { EventsController } from "../typings/CellComponent";
88
import { SelectActionHelper } from "./SelectActionHelper";
9+
import { SelectAllBarViewModel } from "./state/SelectAllBarViewModel";
910

1011
export interface DatagridRootScope {
1112
basicData: GridBasicData;
@@ -18,6 +19,7 @@ export interface DatagridRootScope {
1819
focusController: FocusTargetController;
1920
selectionCountStore: SelectionCountStore;
2021
selectAllProgressStore: ProgressStore;
22+
selectAllBarViewModel: SelectAllBarViewModel;
2123
}
2224

2325
export const DatagridContext = createContext<DatagridRootScope | null>(null);

packages/pluggableWidgets/datagrid-web/src/helpers/state/GridBasicData.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,18 @@ type Props = Pick<
1313
| "onClick"
1414
| "selectingAllLabel"
1515
| "cancelSelectionLabel"
16+
| "selectAllTemplate"
17+
| "selectRemainingTemplate"
18+
| "clearSelectionCaption"
1619
>;
1720

1821
type Gate = DerivedPropsGate<Props>;
1922

20-
/** This is basic data class, just a props mapper. Don't add any state or complex logic. */
23+
/**
24+
* This is basic data class, just a props mapper.
25+
* Don't add any state or complex logic.
26+
* Don't use this class to share instances. Use context.
27+
*/
2128
export class GridBasicData {
2229
private gate: Gate;
2330
private selectionHelper: SelectionHelper | null = null;
@@ -58,12 +65,4 @@ export class GridBasicData {
5865
get selectionStatus(): SelectionStatus {
5966
return this.selectionHelper?.type === "Multi" ? this.selectionHelper.selectionStatus : "none";
6067
}
61-
62-
get currentSelectionHelper(): SelectionHelper | null {
63-
return this.selectionHelper;
64-
}
65-
66-
setSelectionHelper(selectionHelper: SelectionHelper | undefined): void {
67-
this.selectionHelper = selectionHelper ?? null;
68-
}
6968
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { SelectAllController } from "@mendix/widget-plugin-grid/selection";
2+
import { SelectionCountStore } from "@mendix/widget-plugin-grid/stores/SelectionCountStore";
3+
import { DerivedPropsGate } from "@mendix/widget-plugin-mobx-kit/props-gate";
4+
import { makeAutoObservable } from "mobx";
5+
import { DatagridContainerProps } from "../../../typings/DatagridProps";
6+
7+
type Props = Pick<
8+
DatagridContainerProps,
9+
| "cancelSelectionLabel"
10+
| "selectAllTemplate"
11+
| "selectRemainingTemplate"
12+
| "clearSelectionCaption"
13+
| "itemSelection"
14+
| "selectedCountTemplatePlural"
15+
| "selectedCountTemplateSingular"
16+
| "datasource"
17+
>;
18+
19+
type Gate = DerivedPropsGate<Props>;
20+
21+
export class SelectAllBarViewModel {
22+
showClear = false;
23+
24+
constructor(
25+
private gate: Gate,
26+
private selectAllController: SelectAllController,
27+
private count = new SelectionCountStore(gate)
28+
) {
29+
makeAutoObservable(this);
30+
}
31+
32+
private setShowClear(value: boolean): void {
33+
this.showClear = value;
34+
}
35+
36+
private get total(): number {
37+
return this.gate.props.datasource.totalCount ?? 0;
38+
}
39+
40+
private get selectAllFormat(): string {
41+
return this.gate.props.selectAllTemplate?.value ?? "select.all.items";
42+
}
43+
44+
private get selectRemainingText(): string {
45+
return this.gate.props.selectRemainingTemplate?.value ?? "select.remaining.items";
46+
}
47+
48+
get selectAllLabel(): string {
49+
if (this.total > 0) return this.selectAllFormat.replace("%d", `${this.total}`);
50+
return this.selectRemainingText;
51+
}
52+
53+
get clearSelectionLabel(): string {
54+
return this.gate.props.clearSelectionCaption?.value ?? "clear.selection.caption";
55+
}
56+
57+
get selectionCountText(): string {
58+
return this.count.selectedCountText;
59+
}
60+
61+
get barVisible(): boolean {
62+
return this.count.selectedCountText !== "";
63+
}
64+
65+
get clearVisible(): boolean {
66+
if (this.total > 0) return this.total === this.count.selectedCount;
67+
return this.showClear;
68+
}
69+
70+
get selectAllVisible(): boolean {
71+
if (this.clearVisible) return false;
72+
if (this.total > 0) return this.total > this.count.selectedCount;
73+
return this.gate.props.datasource.hasMoreItems ?? false;
74+
}
75+
76+
onClear(): void {
77+
this.selectAllController.clearSelection();
78+
this.setShowClear(false);
79+
}
80+
81+
async onSelectAll(): Promise<void> {
82+
await this.selectAllController.selectAllPages();
83+
this.setShowClear(true);
84+
}
85+
}

packages/pluggableWidgets/datagrid-web/typings/DatagridProps.d.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ export interface DatagridContainerProps {
103103
keepSelection: boolean;
104104
selectAllPagesEnabled: boolean;
105105
selectAllPagesPageSize: number;
106-
selectingAllLabel?: DynamicValue<string>;
107-
cancelSelectionLabel?: DynamicValue<string>;
108106
loadingType: LoadingTypeEnum;
109107
refreshIndicator: boolean;
110108
pageSize: number;
@@ -128,8 +126,13 @@ export interface DatagridContainerProps {
128126
cancelExportLabel?: DynamicValue<string>;
129127
selectRowLabel?: DynamicValue<string>;
130128
selectAllRowsLabel?: DynamicValue<string>;
129+
selectingAllLabel?: DynamicValue<string>;
130+
cancelSelectionLabel?: DynamicValue<string>;
131131
selectedCountTemplateSingular?: DynamicValue<string>;
132132
selectedCountTemplatePlural?: DynamicValue<string>;
133+
selectAllTemplate: DynamicValue<string>;
134+
selectRemainingTemplate: DynamicValue<string>;
135+
clearSelectionCaption: DynamicValue<string>;
133136
}
134137

135138
export interface DatagridPreviewProps {
@@ -158,8 +161,6 @@ export interface DatagridPreviewProps {
158161
keepSelection: boolean;
159162
selectAllPagesEnabled: boolean;
160163
selectAllPagesPageSize: number | null;
161-
selectingAllLabel: string;
162-
cancelSelectionLabel: string;
163164
loadingType: LoadingTypeEnum;
164165
refreshIndicator: boolean;
165166
pageSize: number | null;
@@ -184,6 +185,11 @@ export interface DatagridPreviewProps {
184185
cancelExportLabel: string;
185186
selectRowLabel: string;
186187
selectAllRowsLabel: string;
188+
selectingAllLabel: string;
189+
cancelSelectionLabel: string;
187190
selectedCountTemplateSingular: string;
188191
selectedCountTemplatePlural: string;
192+
selectAllTemplate: string;
193+
selectRemainingTemplate: string;
194+
clearSelectionCaption: string;
189195
}

packages/shared/widget-plugin-grid/src/stores/SelectionCountStore.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ type Gate = DerivedPropsGate<{
66
itemSelection?: SelectionSingleValue | SelectionMultiValue;
77
selectedCountTemplateSingular?: DynamicValue<string>;
88
selectedCountTemplatePlural?: DynamicValue<string>;
9+
clearSelectionCaption?: DynamicValue<string>;
910
}>;
1011

1112
export class SelectionCountStore {
@@ -19,18 +20,18 @@ export class SelectionCountStore {
1920
this.gate = gate;
2021

2122
makeObservable(this, {
22-
displayCount: computed,
23+
selectedCountText: computed,
2324
selectedCount: computed,
24-
fmtSingular: computed,
25-
fmtPlural: computed
25+
formatSingular: computed,
26+
formatPlural: computed
2627
});
2728
}
2829

29-
get fmtSingular(): string {
30+
get formatSingular(): string {
3031
return this.gate.props.selectedCountTemplateSingular?.value || this.singular;
3132
}
3233

33-
get fmtPlural(): string {
34+
get formatPlural(): string {
3435
return this.gate.props.selectedCountTemplatePlural?.value || this.plural;
3536
}
3637

@@ -49,10 +50,14 @@ export class SelectionCountStore {
4950
return itemSelection.selection?.length ?? 0;
5051
}
5152

52-
get displayCount(): string {
53+
get selectedCountText(): string {
5354
const count = this.selectedCount;
5455
if (count === 0) return "";
55-
if (count === 1) return this.fmtSingular.replace("%d", "1");
56-
return this.fmtPlural.replace("%d", `${count}`);
56+
if (count === 1) return this.formatSingular.replace("%d", "1");
57+
return this.formatPlural.replace("%d", `${count}`);
58+
}
59+
60+
get clearSelectionLabel(): string {
61+
return this.gate.props.clearSelectionCaption?.value ?? "clear.selection.caption";
5762
}
5863
}

0 commit comments

Comments
 (0)