Skip to content

Commit b7be089

Browse files
committed
refactor: separate code in plugin
1 parent aa23430 commit b7be089

6 files changed

Lines changed: 199 additions & 112 deletions

File tree

packages/shared/widget-plugin-grid/src/core/models/selection.model.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { atomFactory, ComputedAtom, DerivedPropsGate } from "@mendix/widget-plugin-mobx-kit/main";
2-
import { computed } from "mobx";
2+
import { DynamicValue } from "mendix";
3+
import { computed, observable } from "mobx";
34

4-
/** Returns selected count in multi-selection mode and -1 otherwise. */
5+
/**
6+
* Returns selected count in multi-selection mode and -1 otherwise.
7+
* @injectable
8+
*/
59
export function selectedCountMulti(gate: {
610
itemSelection?: { type: string; selection: { length: number } };
711
}): ComputedAtom<number> {
@@ -68,3 +72,32 @@ export function isCurrentPageSelectedAtom(
6872
return isCurrentPageSelected(selection.selection, items);
6973
});
7074
}
75+
76+
interface ObservableSelectorTexts {
77+
clearSelectionButtonLabel: string;
78+
selectedCountText: string;
79+
}
80+
81+
export function selectedCounterTextsStore(
82+
gate: DerivedPropsGate<{
83+
clearSelectionButtonLabel?: DynamicValue<string>;
84+
selectedCountTemplateSingular?: DynamicValue<string>;
85+
selectedCountTemplatePlural?: DynamicValue<string>;
86+
}>,
87+
selectedCount: ComputedAtom<number>
88+
): ObservableSelectorTexts {
89+
return observable({
90+
get clearSelectionButtonLabel() {
91+
return gate.props.clearSelectionButtonLabel?.value || "Clear selection";
92+
},
93+
get selectedCountText() {
94+
const formatSingular = gate.props.selectedCountTemplateSingular?.value || "%d item selected";
95+
const formatPlural = gate.props.selectedCountTemplatePlural?.value || "%d items selected";
96+
const count = selectedCount.get();
97+
98+
if (count > 1) return formatPlural.replace("%d", `${count}`);
99+
if (count === 1) return formatSingular.replace("%d", "1");
100+
return "";
101+
}
102+
});
103+
}

packages/shared/widget-plugin-grid/src/select-all/SelectAll.service.ts

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,26 @@
1-
import { DerivedPropsGate, SetupComponent, SetupComponentHost } from "@mendix/widget-plugin-mobx-kit/main";
1+
import { DerivedPropsGate, Emitter } from "@mendix/widget-plugin-mobx-kit/main";
22
import { ObjectItem, SelectionMultiValue, SelectionSingleValue } from "mendix";
33
import { action, computed, makeObservable, observable, when } from "mobx";
44
import { QueryService } from "../interfaces/QueryService";
5-
import { TaskProgressService } from "../interfaces/TaskProgressService";
5+
import { ServiceEvents } from "./select-all.model";
66

77
interface DynamicProps {
88
itemSelection?: SelectionMultiValue | SelectionSingleValue;
99
}
1010

11-
export class SelectAllService implements SetupComponent {
11+
export class SelectAllService {
1212
private locked = false;
1313
private abortController?: AbortController;
1414
private readonly pageSize = 1024;
1515

1616
constructor(
17-
host: SetupComponentHost,
1817
private gate: DerivedPropsGate<DynamicProps>,
1918
private query: QueryService,
20-
private progress: TaskProgressService
19+
private progress: Emitter<ServiceEvents>
2120
) {
22-
host.add(this);
23-
type PrivateMembers = "setIsLocked" | "locked";
21+
type PrivateMembers = "locked";
2422
makeObservable<this, PrivateMembers>(this, {
25-
setIsLocked: action,
2623
canExecute: computed,
27-
isExecuting: computed,
2824
selection: computed,
2925
locked: observable,
3026
selectAllPages: action,
@@ -33,10 +29,6 @@ export class SelectAllService implements SetupComponent {
3329
});
3430
}
3531

36-
setup(): () => void {
37-
return () => this.abort();
38-
}
39-
4032
get selection(): SelectionMultiValue | undefined {
4133
const selection = this.gate.props.itemSelection;
4234
if (selection === undefined) return;
@@ -48,14 +40,6 @@ export class SelectAllService implements SetupComponent {
4840
return this.gate.props.itemSelection?.type === "Multi" && !this.locked;
4941
}
5042

51-
get isExecuting(): boolean {
52-
return this.locked;
53-
}
54-
55-
private setIsLocked(value: boolean): void {
56-
this.locked = value;
57-
}
58-
5943
private beforeRunChecks(): boolean {
6044
const selection = this.gate.props.itemSelection;
6145

@@ -94,8 +78,10 @@ export class SelectAllService implements SetupComponent {
9478
return { success: false };
9579
}
9680

97-
this.setIsLocked(true);
81+
this.locked = true;
82+
this.abortController = new AbortController();
9883

84+
const signal = this.abortController.signal;
9985
const { offset: initOffset, limit: initLimit } = this.query;
10086
const initSelection = this.selection?.selection ?? [];
10187
const hasTotal = typeof this.query.totalCount === "number";
@@ -107,12 +93,10 @@ export class SelectAllService implements SetupComponent {
10793
new ProgressEvent(type, { loaded, total: totalCount, lengthComputable: hasTotal });
10894
// We should avoid duplicates, so, we start with clean array.
10995
const allItems: ObjectItem[] = [];
110-
this.abortController = new AbortController();
111-
const signal = this.abortController.signal;
11296

11397
performance.mark("SelectAll_Start");
11498
try {
115-
this.progress.onloadstart(pe("loadstart"));
99+
this.progress.emit("loadstart", pe("loadstart"));
116100
let loading = true;
117101
while (loading) {
118102
const loadedItems = await this.query.fetchPage({
@@ -124,7 +108,7 @@ export class SelectAllService implements SetupComponent {
124108
allItems.push(...loadedItems);
125109
loaded += loadedItems.length;
126110
offset += this.pageSize;
127-
this.progress.onprogress(pe("progress"));
111+
this.progress.emit("progress", pe("progress"));
128112
loading = !signal.aborted && this.query.hasMoreItems;
129113
}
130114
success = true;
@@ -141,9 +125,7 @@ export class SelectAllService implements SetupComponent {
141125
offset: initOffset
142126
});
143127
await this.reloadSelection();
144-
this.progress.onloadend();
145-
146-
// const selectionBeforeReload = this.selection?.selection ?? [];
128+
this.progress.emit("loadend");
147129
// Reload selection to make sure setSelection is working as expected.
148130
this.selection?.setSelection(success ? allItems : initSelection);
149131
this.locked = false;

packages/shared/widget-plugin-grid/src/select-all/SelectAllBar.store.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { action, makeObservable, observable } from "mobx";
1+
import { makeAutoObservable } from "mobx";
22
import { BarStore } from "./select-all.model";
33

44
export class SelectAllBarStore implements BarStore {
@@ -7,15 +7,7 @@ export class SelectAllBarStore implements BarStore {
77
clearBtnVisible = false;
88

99
constructor() {
10-
makeObservable(this, {
11-
pending: observable,
12-
visible: observable,
13-
clearBtnVisible: observable,
14-
setClearBtnVisible: action,
15-
setPending: action,
16-
hideBar: action,
17-
showBar: action
18-
});
10+
makeAutoObservable(this);
1911
}
2012

2113
setClearBtnVisible(value: boolean): void {

packages/shared/widget-plugin-grid/src/select-all/select-all.model.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
import { ComputedAtom, createEmitter, disposeBatch, Emitter } from "@mendix/widget-plugin-mobx-kit/main";
2-
import { reaction } from "mobx";
1+
import {
2+
ComputedAtom,
3+
createEmitter,
4+
DerivedPropsGate,
5+
disposeBatch,
6+
Emitter
7+
} from "@mendix/widget-plugin-mobx-kit/main";
8+
import { DynamicValue } from "mendix";
9+
import { observable, reaction } from "mobx";
310

411
export type ServiceEvents = {
512
loadstart: ProgressEvent;
@@ -26,6 +33,43 @@ export function selectAllEmitter(): Emitter<SelectAllEvents> {
2633
return createEmitter<SelectAllEvents>();
2734
}
2835

36+
interface ObservableSelectAllTexts {
37+
selectionStatus: string;
38+
selectAllLabel: string;
39+
}
40+
41+
/** @injectable */
42+
export function selectAllTextsStore(
43+
gate: DerivedPropsGate<{
44+
allSelectedText?: DynamicValue<string>;
45+
selectAllTemplate?: DynamicValue<string>;
46+
selectAllText?: DynamicValue<string>;
47+
}>,
48+
selectedCount: ComputedAtom<number>,
49+
selectedTexts: { selectedCountText: string },
50+
totalCount: ComputedAtom<number>,
51+
isAllItemsSelected: ComputedAtom<boolean>
52+
): ObservableSelectAllTexts {
53+
return observable({
54+
get selectAllLabel() {
55+
const selectAllFormat = gate.props.selectAllTemplate?.value || "Select all %d rows in the data source";
56+
const selectAllText = gate.props.selectAllText?.value || "Select all rows in the data source";
57+
const total = totalCount.get();
58+
if (total > 0) return selectAllFormat.replace("%d", `${total}`);
59+
return selectAllText;
60+
},
61+
get selectionStatus() {
62+
if (isAllItemsSelected.get()) return this.allSelectedText;
63+
return selectedTexts.selectedCountText;
64+
},
65+
get allSelectedText() {
66+
const str = gate.props.allSelectedText?.value ?? "All %d rows selected.";
67+
const count = selectedCount.get();
68+
return str.replace("%d", `${count}`);
69+
}
70+
});
71+
}
72+
2973
export interface BarStore {
3074
pending: boolean;
3175
visible: boolean;
@@ -75,6 +119,7 @@ export function setupSelectService(service: SelectService, emitter: Emitter<Sele
75119
add(emitter.on("startSelecting", () => service.selectAllPages()));
76120
add(emitter.on("clear", () => service.clearSelection()));
77121
add(emitter.on("abort", () => service.abort()));
122+
add(() => service.abort());
78123

79124
return disposeAll;
80125
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { ComputedAtom } from "@mendix/widget-plugin-mobx-kit/main";
2+
import { makeAutoObservable } from "mobx";
3+
4+
/** @injectable */
5+
export class SelectionCounterViewModel {
6+
constructor(
7+
private selected: ComputedAtom<number>,
8+
private texts: {
9+
clearSelectionButtonLabel: string;
10+
selectedCountText: string;
11+
},
12+
private position: "top" | "bottom" | "off"
13+
) {
14+
makeAutoObservable(this);
15+
}
16+
17+
get isTopCounterVisible(): boolean {
18+
if (this.position !== "top") return false;
19+
return this.selected.get() > 0;
20+
}
21+
22+
get isBottomCounterVisible(): boolean {
23+
if (this.position !== "bottom") return false;
24+
return this.selected.get() > 0;
25+
}
26+
27+
get clearButtonLabel(): string {
28+
return this.texts.clearSelectionButtonLabel;
29+
}
30+
31+
get selectedCount(): number {
32+
return this.selected.get();
33+
}
34+
35+
get selectedCountText(): string {
36+
return this.texts.selectedCountText;
37+
}
38+
}

0 commit comments

Comments
 (0)