Skip to content

Commit 22872ad

Browse files
committed
chore: allow recursive rendering
1 parent f594470 commit 22872ad

File tree

5 files changed

+86
-26
lines changed

5 files changed

+86
-26
lines changed
Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,45 @@
11
import { GUID, ObjectItem, Option, ValueStatus } from "mendix";
22
import { association, equals, literal } from "mendix/filters/builders";
3-
import { ReactElement, useCallback, useContext, useEffect, useId, useState } from "react";
3+
import { ReactElement, useCallback, useEffect, useId, useRef, useState } from "react";
44
import { TreeNodeContainerProps } from "../typings/TreeNodeProps";
5-
import { TreeNodeComponent } from "./components/TreeNodeComponent";
6-
import { TreeNodeBranchContext } from "./components/TreeNodeBranchContext";
5+
import { TreeNodeRoot } from "./components/TreeNodeRoot";
6+
import { TreeNodeRootContext } from "./components/TreeNodeRootContext";
77

88
type treeNodeGraph = {
9-
parentObject: ObjectItem;
9+
parentObject: ObjectItem | null;
1010
items: ObjectItem[];
1111
};
1212

1313
export function TreeNode(props: TreeNodeContainerProps): ReactElement {
1414
const { datasource } = props;
1515
const rootId = useId();
16-
16+
const parent = useRef<ObjectItem | null>(null);
1717
const [treeNodeItems, setTreeNodeItems] = useState(new Map<Option<GUID> | string, treeNodeGraph>());
18-
const { level, parent } = useContext(TreeNodeBranchContext);
1918

2019
const filterContent = useCallback(
2120
(item: Option<ObjectItem>) => {
2221
if (props.parentAssociation) {
2322
return equals(association(props.parentAssociation?.id), literal(item));
2423
}
2524
},
26-
[props.parentAssociation, treeNodeItems, parent, rootId]
25+
[props.parentAssociation]
26+
);
27+
28+
const fetchChildren = useCallback(
29+
(item?: Option<ObjectItem>) => {
30+
parent.current = item || null;
31+
if (props.parentAssociation) {
32+
datasource.setFilter(filterContent(item));
33+
}
34+
},
35+
[filterContent, datasource, props.parentAssociation]
2736
);
2837

2938
useEffect(() => {
3039
// Initial Load of Top Level Items
31-
datasource.setFilter(filterContent(parent?.id ? treeNodeItems.get(parent!.id)?.parentObject : undefined));
40+
if (props.parentAssociation) {
41+
fetchChildren(undefined);
42+
}
3243
}, []);
3344

3445
useEffect(() => {
@@ -37,9 +48,12 @@ export function TreeNode(props: TreeNodeContainerProps): ReactElement {
3748
if (datasource.status === ValueStatus.Available) {
3849
const updatedItems = new Map(treeNodeItems);
3950
if (datasource.items && datasource.items.length) {
40-
updatedItems.set(parent?.id || rootId, { items: datasource.items, parentObject: parent! });
51+
updatedItems.set(parent.current?.id || rootId, {
52+
items: datasource.items,
53+
parentObject: parent.current ?? null
54+
});
4155
} else {
42-
updatedItems.set(parent?.id || rootId, { items: [], parentObject: parent! });
56+
updatedItems.set(parent.current?.id || rootId, { items: [], parentObject: parent.current ?? null });
4357
}
4458
setTreeNodeItems(updatedItems);
4559
}
@@ -48,13 +62,16 @@ export function TreeNode(props: TreeNodeContainerProps): ReactElement {
4862
const collapsedIcon = props.collapsedIcon?.status === ValueStatus.Available ? props.collapsedIcon.value : undefined;
4963

5064
return (
51-
<TreeNodeComponent
52-
{...props}
53-
items={treeNodeItems.get(parent?.id || rootId)?.items || null}
54-
showCustomIcon={Boolean(props.expandedIcon) || Boolean(props.collapsedIcon)}
55-
expandedIcon={expandedIcon}
56-
collapsedIcon={collapsedIcon}
57-
level={level || 0}
58-
/>
65+
<TreeNodeRootContext.Provider value={{ fetchChildren, treeNodeItems, rootId }}>
66+
<TreeNodeRoot
67+
{...props}
68+
// items={treeNodeItems.get(parent?.id || rootId)?.items || null}
69+
showCustomIcon={Boolean(props.expandedIcon) || Boolean(props.collapsedIcon)}
70+
expandedIcon={expandedIcon}
71+
collapsedIcon={collapsedIcon}
72+
isInfiniteMode={props.parentAssociation !== undefined}
73+
// level={level || 0}
74+
/>
75+
</TreeNodeRootContext.Provider>
5976
);
6077
}

packages/pluggableWidgets/tree-node-web/src/components/TreeNodeBranch.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ import {
1313
useState
1414
} from "react";
1515

16-
import { OpenNodeOnEnum, ShowIconEnum } from "../../typings/TreeNodeProps";
1716
import { GUID, ObjectItem, Option } from "mendix";
17+
import { OpenNodeOnEnum, ShowIconEnum } from "../../typings/TreeNodeProps";
1818
import { useTreeNodeLazyLoading } from "./hooks/lazyLoading";
19-
import { useAnimatedTreeNodeContentHeight } from "./hooks/useAnimatedHeight";
2019
import { TreeNodeFocusChangeHandler, useTreeNodeBranchKeyboardHandler } from "./hooks/TreeNodeAccessibility";
20+
import { useAnimatedTreeNodeContentHeight } from "./hooks/useAnimatedHeight";
2121

2222
import { TreeNodeHeaderIcon } from "./HeaderIcon";
23-
import { TreeNodeState } from "./TreeNodeComponent";
2423
import { TreeNodeBranchContext, TreeNodeBranchContextProps } from "./TreeNodeBranchContext";
24+
import { TreeNodeState } from "./TreeNodeComponent";
25+
import { TreeNodeRootContext } from "./TreeNodeRootContext";
2526

2627
export interface TreeNodeBranchProps {
2728
animateTreeNodeContent: boolean;
@@ -35,6 +36,7 @@ export interface TreeNodeBranchProps {
3536
changeFocus: TreeNodeFocusChangeHandler;
3637
renderHeaderIcon: TreeNodeHeaderIcon;
3738
item: ObjectItem;
39+
level: number;
3840
}
3941

4042
export const treeNodeBranchUtils = {
@@ -54,9 +56,10 @@ export function TreeNodeBranch({
5456
openNodeOn,
5557
renderHeaderIcon,
5658
startExpanded,
57-
item
59+
item,
60+
level
5861
}: TreeNodeBranchProps): ReactElement {
59-
const { level: currentContextLevel } = useContext(TreeNodeBranchContext);
62+
const { fetchChildren } = useContext(TreeNodeRootContext);
6063

6164
const treeNodeBranchRef = useRef<HTMLLIElement>(null);
6265
const treeNodeBranchBody = useRef<HTMLDivElement>(null);
@@ -100,6 +103,7 @@ export function TreeNodeBranch({
100103
return;
101104
}
102105

106+
fetchChildren(item);
103107
if (!isActualLeafNode) {
104108
captureElementHeight();
105109
setTreeNodeState(treeNodeState => {
@@ -187,7 +191,7 @@ export function TreeNodeBranch({
187191
<TreeNodeBranchContext.Provider
188192
value={{
189193
parent: item,
190-
level: currentContextLevel + 1,
194+
level: level + 1,
191195
informParentOfChildNodes
192196
}}
193197
>

packages/pluggableWidgets/tree-node-web/src/components/TreeNodeComponent.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useTreeNodeFocusChangeHandler } from "./hooks/TreeNodeAccessibility";
99
import { useTreeNodeRef } from "./hooks/useTreeNodeRef";
1010
import { TreeNodeBranch, treeNodeBranchUtils } from "./TreeNodeBranch";
1111
import { useInformParentContextOfChildNodes } from "./TreeNodeBranchContext";
12+
import { TreeNodeRoot } from "./TreeNodeRoot";
1213

1314
export interface TreeNodeComponentProps
1415
extends Pick<
@@ -31,7 +32,8 @@ export interface TreeNodeComponentProps
3132
showCustomIcon: boolean;
3233
expandedIcon?: WebIcon;
3334
collapsedIcon?: WebIcon;
34-
level?: number;
35+
level: number;
36+
isInfiniteMode: boolean;
3537
}
3638

3739
export function TreeNodeComponent(props: TreeNodeComponentProps): ReactElement | null {
@@ -53,7 +55,8 @@ export function TreeNodeComponent(props: TreeNodeComponentProps): ReactElement |
5355
showCustomIcon,
5456
expandedIcon,
5557
collapsedIcon,
56-
level
58+
level,
59+
isInfiniteMode
5760
} = props;
5861
const [treeNodeElement, updateTreeNodeElement] = useTreeNodeRef();
5962
const isUserDefinedLeafNode = hasChildren === false;
@@ -103,8 +106,10 @@ export function TreeNodeComponent(props: TreeNodeComponentProps): ReactElement |
103106
animateTreeNodeContent={animate}
104107
openNodeOn={openNodeOn}
105108
item={item}
109+
level={level}
106110
>
107111
{children?.get(item)}
112+
{isInfiniteMode && <TreeNodeRoot {...props}></TreeNodeRoot>}
108113
</TreeNodeBranch>
109114
))}
110115
</ul>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ReactElement, useContext } from "react";
2+
import { TreeNodeBranchContext } from "./TreeNodeBranchContext";
3+
import { TreeNodeComponent, TreeNodeComponentProps } from "./TreeNodeComponent";
4+
import { TreeNodeRootContext } from "./TreeNodeRootContext";
5+
6+
export function TreeNodeRoot(props: Omit<TreeNodeComponentProps, "items" | "level">): ReactElement {
7+
const { level, parent } = useContext(TreeNodeBranchContext);
8+
const { treeNodeItems, rootId } = useContext(TreeNodeRootContext);
9+
const parentId = props.isInfiniteMode ? parent?.id || rootId : rootId;
10+
const items = treeNodeItems?.get(parentId)?.items || [];
11+
if (items.length === 0) {
12+
return <div></div>;
13+
}
14+
return <TreeNodeComponent {...props} items={items} level={level} />;
15+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ObjectItem, Option } from "mendix";
2+
import { createContext } from "react";
3+
4+
export type TreeNodeGraph = {
5+
parentObject: ObjectItem | null;
6+
items: ObjectItem[];
7+
};
8+
9+
export interface TreeNodeRootContextProps {
10+
rootId: string;
11+
fetchChildren: (item?: Option<ObjectItem>) => void;
12+
treeNodeItems?: Map<Option<string> | string, TreeNodeGraph>;
13+
}
14+
15+
export const TreeNodeRootContext = createContext<TreeNodeRootContextProps>({
16+
rootId: Math.random().toString(36).substring(2, 15),
17+
treeNodeItems: new Map<Option<string> | string, TreeNodeGraph>(),
18+
fetchChildren: (_item?: Option<ObjectItem>) => null
19+
});

0 commit comments

Comments
 (0)