Skip to content

Commit bf5845b

Browse files
committed
fix: make selection helper observable
1 parent 338d7c6 commit bf5845b

File tree

6 files changed

+141
-347
lines changed

6 files changed

+141
-347
lines changed

packages/pluggableWidgets/selection-helper-web/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
"update-changelog": "rui-update-changelog-widget",
3737
"verify": "rui-verify-package-format"
3838
},
39+
"dependencies": {
40+
"mobx-react-lite": "4.0.7"
41+
},
3942
"devDependencies": {
4043
"@mendix/automation-utils": "workspace:*",
4144
"@mendix/eslint-config-web-widgets": "workspace:*",
Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import { Alert } from "@mendix/widget-plugin-component-kit/Alert";
2+
import { useSelectionContextValue } from "@mendix/widget-plugin-grid/selection";
3+
import { observer } from "mobx-react-lite";
14
import { createElement, ReactElement } from "react";
25
import { SelectionHelperContainerProps } from "../typings/SelectionHelperProps";
3-
import { useSelectionContextValue } from "@mendix/widget-plugin-grid/selection";
46
import { SelectionHelperComponent } from "./components/SelectionHelperComponent";
5-
import { Alert } from "@mendix/widget-plugin-component-kit/Alert";
67

7-
export function SelectionHelper(props: SelectionHelperContainerProps): ReactElement {
8+
const SelectionHelper = observer(function SelectionHelper(props: SelectionHelperContainerProps): ReactElement {
89
const contextValue = useSelectionContextValue();
910

1011
if (contextValue.hasError) {
@@ -21,15 +22,17 @@ export function SelectionHelper(props: SelectionHelperContainerProps): ReactElem
2122
return (
2223
<SelectionHelperComponent
2324
type={props.renderStyle}
24-
status={selection.status}
25-
onClick={selection.toggle}
25+
status={selection.selectionStatus}
26+
onClick={() => selection.togglePageSelection()}
2627
className={props.class}
2728
cssStyles={props.style}
2829
>
29-
{selection.status === "all" && props.customAllSelected}
30-
{selection.status === "some" && props.customSomeSelected}
31-
{selection.status === "none" && props.customNoneSelected}
30+
{selection.selectionStatus === "all" && props.customAllSelected}
31+
{selection.selectionStatus === "some" && props.customSomeSelected}
32+
{selection.selectionStatus === "none" && props.customNoneSelected}
3233
{props.checkboxCaption?.value ?? ""}
3334
</SelectionHelperComponent>
3435
);
35-
}
36+
});
37+
38+
export { SelectionHelper };

packages/pluggableWidgets/selection-helper-web/src/components/SelectionHelperComponent.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { createElement, CSSProperties, ReactElement, ReactNode, useMemo } from "react";
21
import { ThreeStateCheckBox } from "@mendix/widget-plugin-component-kit/ThreeStateCheckBox";
2+
import { createElement, CSSProperties, ReactElement, ReactNode, useMemo } from "react";
33

44
interface Props {
55
type: "checkbox" | "custom";
@@ -11,6 +11,7 @@ interface Props {
1111
}
1212

1313
export function SelectionHelperComponent(props: Props): ReactElement {
14+
// TODO: replace with useId
1415
const id = useMemo(() => {
1516
return Date.now().toString();
1617
}, []);

packages/shared/widget-plugin-grid/src/selection/context.ts

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,43 @@
1-
import { Context, createContext, useCallback, useContext, useMemo } from "react";
2-
import { error, Result, value } from "./result-meta.js";
1+
import { Context, createContext, useContext, useMemo } from "react";
32
import { SelectionHelper } from "./helpers.js";
3+
import { error, Result, value } from "./result-meta.js";
44
import { MultiSelectionStatus } from "./types.js";
55

66
const CONTEXT_OBJECT_PATH = "com.mendix.widgets.web.selectable.selectionContext" as const;
77

8-
type SelectionContextValue = { status: "all" | "some" | "none"; toggle: () => void };
9-
type SelectionContextObject = Context<SelectionContextValue | undefined>;
8+
interface SelectionStore {
9+
/** @observable */
10+
selectionStatus: MultiSelectionStatus;
11+
togglePageSelection(): void;
12+
}
13+
14+
type SelectionContextObject = Context<SelectionStore | undefined>;
15+
1016
declare global {
1117
interface Window {
1218
[CONTEXT_OBJECT_PATH]?: SelectionContextObject;
1319
}
1420
}
1521

1622
export function getGlobalSelectionContext(): SelectionContextObject {
17-
if (window[CONTEXT_OBJECT_PATH] === undefined) {
18-
window[CONTEXT_OBJECT_PATH] = createContext<SelectionContextValue | undefined>(undefined);
19-
}
20-
21-
return window[CONTEXT_OBJECT_PATH]!;
23+
return (window[CONTEXT_OBJECT_PATH] ??= createContext<SelectionStore | undefined>(undefined));
2224
}
2325

24-
type UseCreateSelectionContextValueReturn =
25-
| {
26-
status: MultiSelectionStatus;
27-
toggle: () => void;
28-
}
29-
| undefined;
26+
type UseCreateSelectionContextValueReturn = SelectionStore | undefined;
3027

3128
export function useCreateSelectionContextValue(
3229
selection: SelectionHelper | undefined
3330
): UseCreateSelectionContextValueReturn {
34-
const toggleSelection = useCallback(() => {
35-
if (selection?.type === "Multi") {
36-
if (selection.selectionStatus === "all") {
37-
selection.selectNone();
38-
} else {
39-
selection.selectAll();
40-
}
41-
}
42-
}, [selection]);
43-
const multiSelectionStatus = selection?.type === "Multi" ? selection.selectionStatus : undefined;
44-
4531
return useMemo(() => {
46-
if (multiSelectionStatus !== undefined) {
47-
return {
48-
status: multiSelectionStatus,
49-
toggle: toggleSelection
50-
};
32+
if (selection?.type === "Multi") {
33+
return selection;
5134
}
5235

5336
return undefined;
54-
}, [multiSelectionStatus, toggleSelection]);
37+
}, [selection]);
5538
}
5639

57-
export function useSelectionContextValue(): Result<SelectionContextValue, OutOfContextError> {
40+
export function useSelectionContextValue(): Result<SelectionStore, OutOfContextError> {
5841
const context = getGlobalSelectionContext();
5942
const contextValue = useContext(context);
6043

packages/shared/widget-plugin-grid/src/selection/helpers.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action";
22
import type { ActionValue, ListValue, ObjectItem, SelectionMultiValue, SelectionSingleValue } from "mendix";
3+
import { action, computed, makeObservable, observable } from "mobx";
34
import { useEffect, useRef, useState } from "react";
45
import { Direction, MoveEvent1D, MoveEvent2D, MultiSelectionStatus, ScrollKeyCode, SelectionMode, Size } from "./types";
56

@@ -32,6 +33,13 @@ export class MultiSelectionHelper {
3233
private selectableItems: ObjectItem[]
3334
) {
3435
this.rangeStart = undefined;
36+
type PrivateMembers = "selectionValue" | "selectableItems";
37+
makeObservable<this, PrivateMembers>(this, {
38+
selectionStatus: computed,
39+
selectionValue: observable.ref,
40+
selectableItems: observable.ref,
41+
updateProps: action
42+
});
3543
}
3644

3745
isSelected(value: ObjectItem): boolean {
@@ -252,7 +260,7 @@ export class MultiSelectionHelper {
252260
}
253261
}
254262

255-
_findIndexInList(index: number, direction: Direction, size: Size): number {
263+
private _findIndexInList(index: number, direction: Direction, size: Size): number {
256264
const first = 0;
257265
const last = this.selectableItems.length - 1;
258266
const isForward = direction === "forward";
@@ -266,7 +274,7 @@ export class MultiSelectionHelper {
266274
return clamp(result, first, last);
267275
}
268276

269-
_findIndexInGrid(index: number, keycode: ScrollKeyCode, numberOfColumns: number): number {
277+
private _findIndexInGrid(index: number, keycode: ScrollKeyCode, numberOfColumns: number): number {
270278
const { columnIndex } = getColumnAndRowBasedOnIndex(numberOfColumns, this.selectableItems.length, index);
271279

272280
if (keycode === "PageDown") {
@@ -319,6 +327,14 @@ export class MultiSelectionHelper {
319327

320328
this._setRangeEnd(endItem, mode);
321329
}
330+
331+
togglePageSelection(): void {
332+
if (this.selectionStatus === "all") {
333+
this.selectNone();
334+
} else {
335+
this.selectAll();
336+
}
337+
}
322338
}
323339

324340
const clamp = (num: number, min: number, max: number): number => Math.min(Math.max(num, min), max);

0 commit comments

Comments
 (0)