Skip to content

Commit 14f1f59

Browse files
committed
feat: dynamic pagination
1 parent 0ce3dc7 commit 14f1f59

25 files changed

+458
-131
lines changed

packages/pluggableWidgets/datagrid-web/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1010

1111
- We added a new property for export to excel. The new property allows to set the cell export type and also the format for type number and date.
1212

13+
- We have introduced the "Page" and "Page Size" attributes to provide complete control over DataGrid pagination.
14+
1315
## [3.7.0] - 2025-11-11
1416

1517
### Added

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,25 @@ export function getProperties(values: DatagridPreviewProps, defaultProperties: P
7272

7373
if (values.pagination === "buttons") {
7474
hidePropertyIn(defaultProperties, values, "showNumberOfRows");
75+
76+
if (values.useCustomPagination === false) {
77+
hidePropertyIn(defaultProperties, values, "customPagination");
78+
} else {
79+
hidePropertiesIn(defaultProperties, values, ["pagingPosition", "showPagingButtons"]);
80+
}
7581
} else {
7682
hidePropertyIn(defaultProperties, values, "showPagingButtons");
7783

7884
if (values.showNumberOfRows === false) {
7985
hidePropertyIn(defaultProperties, values, "pagingPosition");
8086
}
87+
88+
hidePropertiesIn(defaultProperties, values, [
89+
"dynamicPage",
90+
"dynamicPageSize",
91+
"useCustomPagination",
92+
"customPagination"
93+
]);
8194
}
8295

8396
if (values.pagination !== "loadMore") {

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

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,14 @@
280280
<enumerationValue key="loadMore">Load more</enumerationValue>
281281
</enumerationValues>
282282
</property>
283+
<property key="useCustomPagination" type="boolean" defaultValue="false">
284+
<caption>Custom pagination</caption>
285+
<description />
286+
</property>
287+
<property key="customPagination" type="widgets" required="false">
288+
<caption>Custom pagination</caption>
289+
<description />
290+
</property>
283291
<property key="showPagingButtons" type="enumeration" defaultValue="always">
284292
<caption>Show paging buttons</caption>
285293
<description />
@@ -301,20 +309,34 @@
301309
<enumerationValue key="both">Both</enumerationValue>
302310
</enumerationValues>
303311
</property>
304-
<property key="dynamicPageSize" type="attribute" required="false">
305-
<caption>Dynamic page size attribute</caption>
306-
<description>Optional attribute to set the page size dynamically. The attribute requires Integer type.</description>
307-
<attributeTypes>
308-
<attributeType name="Integer" />
309-
</attributeTypes>
310-
</property>
311312
<property key="loadMoreButtonCaption" type="textTemplate" required="false">
312313
<caption>Load more caption</caption>
313314
<description />
314315
<translations>
315316
<translation lang="en_US">Load More</translation>
316317
</translations>
317318
</property>
319+
<property key="dynamicPageSize" type="attribute" required="false">
320+
<caption>Page size attribute</caption>
321+
<description>Attribute to set the page size dynamically.</description>
322+
<attributeTypes>
323+
<attributeType name="Integer" />
324+
</attributeTypes>
325+
</property>
326+
<property key="dynamicPage" type="attribute" required="false">
327+
<caption>Page attribute</caption>
328+
<description>Attribute to set the page dynamically.</description>
329+
<attributeTypes>
330+
<attributeType name="Integer" />
331+
</attributeTypes>
332+
</property>
333+
<property key="totalCountValue" type="attribute" required="false">
334+
<caption>Total count</caption>
335+
<description>Attribute to store current total count</description>
336+
<attributeTypes>
337+
<attributeType name="Integer" />
338+
</attributeTypes>
339+
</property>
318340
</propertyGroup>
319341
<propertyGroup caption="Appearance">
320342
<property key="showEmptyPlaceholder" type="enumeration" defaultValue="none">

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
useDatagridConfig,
66
useItemCount,
77
useLoaderViewModel,
8-
usePaginationService,
8+
usePaginationVM,
99
useVisibleColumnsCount
1010
} from "../model/hooks/injection-hooks";
1111
import { useBodyScroll } from "../model/hooks/useBodyScroll";
@@ -31,7 +31,7 @@ export const GridBody = observer(function GridBody(props: PropsWithChildren): Re
3131

3232
const ContentGuard = observer(function ContentGuard(props: PropsWithChildren): ReactNode {
3333
const loaderVM = useLoaderViewModel();
34-
const { pageSize } = usePaginationService();
34+
const { pageSize } = usePaginationVM();
3535
const config = useDatagridConfig();
3636
const columnsCount = useVisibleColumnsCount().get();
3737
const itemCount = useItemCount().get();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { Pagination as PaginationComponent } from "@mendix/widget-plugin-grid/components/Pagination";
22
import { observer } from "mobx-react-lite";
33
import { ReactNode } from "react";
4-
import { usePaginationService } from "../model/hooks/injection-hooks";
4+
import { usePaginationVM } from "../model/hooks/injection-hooks";
55

66
export const Pagination = observer(function Pagination(): ReactNode {
7-
const paging = usePaginationService();
7+
const paging = usePaginationVM();
88

99
if (!paging.paginationVisible) return null;
1010

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { observer } from "mobx-react-lite";
33
import { ReactElement } from "react";
44
import { SelectionCounter } from "../features/selection-counter/SelectionCounter";
55
import { useSelectionCounterViewModel } from "../features/selection-counter/injection-hooks";
6-
import { useDatagridConfig, usePaginationService, useTexts } from "../model/hooks/injection-hooks";
6+
import { useDatagridConfig, usePaginationVM, useTexts } from "../model/hooks/injection-hooks";
77
import { Pagination } from "./Pagination";
88

99
export const WidgetFooter = observer(function WidgetFooter(): ReactElement {
1010
const config = useDatagridConfig();
11-
const paging = usePaginationService();
11+
const paging = usePaginationVM();
1212
const { loadMoreButtonCaption } = useTexts();
1313
const selectionCounterVM = useSelectionCounterViewModel();
1414

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { ComputedAtom, disposeBatch, SetupComponent, SetupComponentHost } from "@mendix/widget-plugin-mobx-kit/main";
2+
import { autorun, reaction } from "mobx";
3+
import { GridPageControl } from "./GridPageControl";
4+
5+
export class DynamicPaginationFeature implements SetupComponent {
6+
id = "DynamicPaginationFeature";
7+
constructor(
8+
host: SetupComponentHost,
9+
private config: { dynamicPageSizeEnabled: boolean; dynamicPageEnabled: boolean },
10+
private dynamicPage: ComputedAtom<number>,
11+
private dynamicPageSize: ComputedAtom<number>,
12+
private totalCount: ComputedAtom<number>,
13+
private service: GridPageControl
14+
) {
15+
host.add(this);
16+
}
17+
18+
setup(): () => void {
19+
const [add, disposeAll] = disposeBatch();
20+
21+
if (this.config.dynamicPageSizeEnabled) {
22+
add(
23+
reaction(
24+
() => this.dynamicPageSize.get(),
25+
pageSize => {
26+
if (pageSize < 0) return;
27+
this.service.setPageSize(pageSize);
28+
},
29+
{ delay: 250 }
30+
)
31+
);
32+
}
33+
34+
if (this.config.dynamicPageEnabled) {
35+
add(
36+
reaction(
37+
() => this.dynamicPage.get(),
38+
page => {
39+
if (page < 0) return;
40+
this.service.setPage(page);
41+
},
42+
{ delay: 250 }
43+
)
44+
);
45+
add(
46+
autorun(() => {
47+
this.service.setTotalCount(this.totalCount.get());
48+
})
49+
);
50+
}
51+
52+
return disposeAll;
53+
}
54+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface GridPageControl {
2+
setPage(page: number): void;
3+
setPageSize(pageSize: number): void;
4+
setTotalCount(totalCount: number): void;
5+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { SetPageAction, SetPageSizeAction } from "@mendix/widget-plugin-grid/main";
2+
import { DerivedPropsGate } from "@mendix/widget-plugin-mobx-kit/main";
3+
import { Big } from "big.js";
4+
import { EditableValue } from "mendix";
5+
import { GridPageControl } from "./GridPageControl";
6+
7+
export class PageControlService implements GridPageControl {
8+
constructor(
9+
private gate: DerivedPropsGate<{
10+
totalCountValue?: EditableValue<Big>;
11+
}>,
12+
private setPageSizeAction: SetPageSizeAction,
13+
private setPageAction: SetPageAction
14+
) {}
15+
16+
setPageSize(pageSize: number): void {
17+
this.setPageSizeAction(pageSize);
18+
}
19+
20+
setPage(page: number): void {
21+
this.setPageAction(page);
22+
}
23+
24+
setTotalCount(count: number): void {
25+
const value = this.gate.props.totalCountValue;
26+
if (!value || value.readOnly) return;
27+
value.setValue(new Big(count));
28+
}
29+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { QueryService, SetPageAction } from "@mendix/widget-plugin-grid/main";
2+
import { ComputedAtom } from "@mendix/widget-plugin-mobx-kit/main";
3+
import { computed, makeObservable } from "mobx";
4+
import { PaginationEnum, ShowPagingButtonsEnum } from "../../../typings/DatagridProps";
5+
import { PaginationConfig } from "./pagination.config";
6+
7+
export class PaginationViewModel {
8+
readonly pagination: PaginationEnum;
9+
readonly showPagingButtons: ShowPagingButtonsEnum;
10+
11+
constructor(
12+
private config: PaginationConfig,
13+
private query: QueryService,
14+
private currentPageAtom: ComputedAtom<number>,
15+
private pageSizeAtom: ComputedAtom<number>,
16+
private setPageAction: SetPageAction
17+
) {
18+
this.pagination = config.pagination;
19+
this.showPagingButtons = config.showPagingButtons;
20+
21+
makeObservable(this, {
22+
pageSize: computed,
23+
currentPage: computed,
24+
paginationVisible: computed,
25+
hasMoreItems: computed,
26+
totalCount: computed
27+
});
28+
}
29+
30+
get pageSize(): number {
31+
return this.pageSizeAtom.get();
32+
}
33+
34+
get currentPage(): number {
35+
return this.currentPageAtom.get();
36+
}
37+
38+
get paginationVisible(): boolean {
39+
switch (this.config.paginationKind) {
40+
case "buttons.always":
41+
return true;
42+
case "buttons.auto": {
43+
const { totalCount = -1 } = this.query;
44+
return totalCount > this.query.limit;
45+
}
46+
case "custom": {
47+
return false;
48+
}
49+
default:
50+
return this.config.showNumberOfRows;
51+
}
52+
}
53+
54+
get hasMoreItems(): boolean {
55+
return this.query.hasMoreItems;
56+
}
57+
58+
get totalCount(): number | undefined {
59+
return this.query.totalCount;
60+
}
61+
62+
setPage: SetPageAction = value => {
63+
this.setPageAction(value);
64+
};
65+
}

0 commit comments

Comments
 (0)