Skip to content

Commit f594470

Browse files
committed
chore: state change of tree node to accomodate graph
1 parent 8704e54 commit f594470

File tree

9 files changed

+159
-121
lines changed

9 files changed

+159
-121
lines changed
Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,48 @@
1-
import { parseStyle } from "@mendix/widget-plugin-platform/preview/parse-style";
2-
import { mapPreviewIconToWebIcon } from "@mendix/widget-plugin-platform/preview/map-icon";
3-
import { GUID } from "mendix";
41
import { ReactElement } from "react";
52
import { TreeNodePreviewProps } from "../typings/TreeNodeProps";
6-
import { TreeNode } from "./components/TreeNode";
73

8-
function renderTextTemplateWithFallback(textTemplateValue: string, placeholder: string): string {
9-
if (textTemplateValue.trim().length === 0) {
10-
return placeholder;
11-
}
12-
return textTemplateValue;
13-
}
4+
// function renderTextTemplateWithFallback(textTemplateValue: string, placeholder: string): string {
5+
// if (textTemplateValue.trim().length === 0) {
6+
// return placeholder;
7+
// }
8+
// return textTemplateValue;
9+
// }
1410

15-
export function preview(props: TreeNodePreviewProps): ReactElement | null {
11+
export function preview(_props: TreeNodePreviewProps): ReactElement | null {
1612
return (
17-
<TreeNode
18-
class={props.className}
19-
style={parseStyle(props.style)}
20-
items={[
21-
{
22-
id: "1" as GUID,
23-
headerContent:
24-
props.headerType === "text" ? (
25-
renderTextTemplateWithFallback(props.headerCaption, "[No header caption configured]")
26-
) : (
27-
<props.headerContent.renderer caption="Place header contents here.">
28-
<div />
29-
</props.headerContent.renderer>
30-
),
31-
bodyContent: (
32-
<props.children.renderer caption="Place other tree nodes here.">
33-
<div />
34-
</props.children.renderer>
35-
)
36-
}
37-
]}
38-
isUserDefinedLeafNode={!props.hasChildren}
39-
startExpanded
40-
showCustomIcon={Boolean(props.expandedIcon) || Boolean(props.collapsedIcon)}
41-
iconPlacement={props.showIcon}
42-
collapsedIcon={mapPreviewIconToWebIcon(props.collapsedIcon)}
43-
expandedIcon={mapPreviewIconToWebIcon(props.expandedIcon)}
44-
animateIcon={false}
45-
animateTreeNodeContent={false}
46-
openNodeOn={"headerClick"}
47-
/>
13+
<div> test </div>
14+
// <TreeNodeComponent
15+
// {...props}
16+
// headerCaption={dynamc
17+
// // class={props.className}
18+
// // style={parseStyle(props.style)}
19+
// items={[
20+
// {
21+
// id: "1" as GUID,
22+
// // headerContent:
23+
// // props.headerType === "text" ? (
24+
// // renderTextTemplateWithFallback(props.headerCaption, "[No header caption configured]")
25+
// // ) : (
26+
// // <props.headerContent.renderer caption="Place header contents here.">
27+
// // <div />
28+
// // </props.headerContent.renderer>
29+
// // ),
30+
// // bodyContent: (
31+
// // <props.children.renderer caption="Place other tree nodes here.">
32+
// // <div />
33+
// // </props.children.renderer>
34+
// // )
35+
// }
36+
// ]}
37+
// // isUserDefinedLeafNode={!props.hasChildren}
38+
// startExpanded
39+
// showCustomIcon={Boolean(props.expandedIcon) || Boolean(props.collapsedIcon)}
40+
// // iconPlacement={props.showIcon}
41+
// collapsedIcon={mapPreviewIconToWebIcon(props.collapsedIcon)}
42+
// expandedIcon={mapPreviewIconToWebIcon(props.expandedIcon)}
43+
// animateIcon={false}
44+
// // animateTreeNodeContent={false}
45+
// openNodeOn={"headerClick"}
46+
// />
4847
);
4948
}
Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,60 @@
1-
import { ReactElement, useEffect, useState } from "react";
2-
import { ObjectItem, ValueStatus } from "mendix";
1+
import { GUID, ObjectItem, Option, ValueStatus } from "mendix";
2+
import { association, equals, literal } from "mendix/filters/builders";
3+
import { ReactElement, useCallback, useContext, useEffect, useId, useState } from "react";
34
import { TreeNodeContainerProps } from "../typings/TreeNodeProps";
4-
import { TreeNode as TreeNodeComponent, TreeNodeItem } from "./components/TreeNode";
5+
import { TreeNodeComponent } from "./components/TreeNodeComponent";
6+
import { TreeNodeBranchContext } from "./components/TreeNodeBranchContext";
57

6-
function mapDataSourceItemToTreeNodeItem(item: ObjectItem, props: TreeNodeContainerProps): TreeNodeItem {
7-
return {
8-
id: item.id,
9-
headerContent:
10-
props.headerType === "text" ? props.headerCaption?.get(item).value : props.headerContent?.get(item),
11-
bodyContent: props.children?.get(item)
12-
};
13-
}
8+
type treeNodeGraph = {
9+
parentObject: ObjectItem;
10+
items: ObjectItem[];
11+
};
1412

1513
export function TreeNode(props: TreeNodeContainerProps): ReactElement {
1614
const { datasource } = props;
15+
const rootId = useId();
16+
17+
const [treeNodeItems, setTreeNodeItems] = useState(new Map<Option<GUID> | string, treeNodeGraph>());
18+
const { level, parent } = useContext(TreeNodeBranchContext);
19+
20+
const filterContent = useCallback(
21+
(item: Option<ObjectItem>) => {
22+
if (props.parentAssociation) {
23+
return equals(association(props.parentAssociation?.id), literal(item));
24+
}
25+
},
26+
[props.parentAssociation, treeNodeItems, parent, rootId]
27+
);
1728

18-
const [treeNodeItems, setTreeNodeItems] = useState<TreeNodeItem[] | null>([]);
29+
useEffect(() => {
30+
// Initial Load of Top Level Items
31+
datasource.setFilter(filterContent(parent?.id ? treeNodeItems.get(parent!.id)?.parentObject : undefined));
32+
}, []);
1933

2034
useEffect(() => {
2135
// only get the items when datasource is actually available
2236
// this is to prevent treenode resetting it's render while datasource is loading.
2337
if (datasource.status === ValueStatus.Available) {
38+
const updatedItems = new Map(treeNodeItems);
2439
if (datasource.items && datasource.items.length) {
25-
setTreeNodeItems(datasource.items.map(item => mapDataSourceItemToTreeNodeItem(item, props)));
40+
updatedItems.set(parent?.id || rootId, { items: datasource.items, parentObject: parent! });
2641
} else {
27-
setTreeNodeItems([]);
42+
updatedItems.set(parent?.id || rootId, { items: [], parentObject: parent! });
2843
}
44+
setTreeNodeItems(updatedItems);
2945
}
3046
}, [datasource.status, datasource.items]);
3147
const expandedIcon = props.expandedIcon?.status === ValueStatus.Available ? props.expandedIcon.value : undefined;
3248
const collapsedIcon = props.collapsedIcon?.status === ValueStatus.Available ? props.collapsedIcon.value : undefined;
3349

3450
return (
3551
<TreeNodeComponent
36-
class={props.class}
37-
style={props.style}
38-
items={treeNodeItems}
39-
isUserDefinedLeafNode={!props.hasChildren}
40-
startExpanded={props.startExpanded}
52+
{...props}
53+
items={treeNodeItems.get(parent?.id || rootId)?.items || null}
4154
showCustomIcon={Boolean(props.expandedIcon) || Boolean(props.collapsedIcon)}
42-
iconPlacement={props.showIcon}
4355
expandedIcon={expandedIcon}
4456
collapsedIcon={collapsedIcon}
45-
tabIndex={props.tabIndex}
46-
animateIcon={props.animate && props.animateIcon}
47-
animateTreeNodeContent={props.animate}
48-
openNodeOn={props.openNodeOn}
57+
level={level || 0}
4958
/>
5059
);
5160
}

packages/pluggableWidgets/tree-node-web/src/TreeNode.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@
1616
<caption>Data source</caption>
1717
<description />
1818
</property>
19+
<property key="parentAssociation" type="association" dataSource="datasource" selectableObjects="datasource" required="false">
20+
<caption>Parent association</caption>
21+
<description>Select the self-referencing association that connects each item to its parent, enabling infinite depth hierarchies.</description>
22+
<associationTypes>
23+
<associationType name="Reference" />
24+
</associationTypes>
25+
</property>
1926
<property key="headerType" type="enumeration" defaultValue="text">
2027
<caption>Header type</caption>
2128
<description />

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import { ShowIconEnum } from "../../typings/TreeNodeProps";
55
import loadingCircleSvg from "../assets/loading-circle.svg";
66

77
import { ChevronIcon, CustomHeaderIcon } from "./Icons";
8-
import { TreeNodeProps, TreeNodeState } from "./TreeNode";
8+
import { TreeNodeComponentProps, TreeNodeState } from "./TreeNodeComponent";
99

10-
export type IconOptions = Pick<TreeNodeProps, "animateIcon" | "collapsedIcon" | "expandedIcon" | "showCustomIcon">;
10+
export type IconOptions = Pick<
11+
TreeNodeComponentProps,
12+
"animateIcon" | "collapsedIcon" | "expandedIcon" | "showCustomIcon"
13+
>;
1114

1215
export type TreeNodeHeaderIcon = (
1316
treeNodeState: TreeNodeState,

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,33 @@ import {
1414
} from "react";
1515

1616
import { OpenNodeOnEnum, ShowIconEnum } from "../../typings/TreeNodeProps";
17-
17+
import { GUID, ObjectItem, Option } from "mendix";
1818
import { useTreeNodeLazyLoading } from "./hooks/lazyLoading";
1919
import { useAnimatedTreeNodeContentHeight } from "./hooks/useAnimatedHeight";
2020
import { TreeNodeFocusChangeHandler, useTreeNodeBranchKeyboardHandler } from "./hooks/TreeNodeAccessibility";
2121

2222
import { TreeNodeHeaderIcon } from "./HeaderIcon";
23-
import { TreeNodeItem, TreeNodeState } from "./TreeNode";
23+
import { TreeNodeState } from "./TreeNodeComponent";
2424
import { TreeNodeBranchContext, TreeNodeBranchContextProps } from "./TreeNodeBranchContext";
2525

2626
export interface TreeNodeBranchProps {
2727
animateTreeNodeContent: boolean;
2828
children: ReactNode;
2929
headerContent: ReactNode;
3030
iconPlacement: ShowIconEnum;
31-
id: TreeNodeItem["id"];
31+
id: Option<GUID>;
3232
isUserDefinedLeafNode: boolean;
3333
openNodeOn: OpenNodeOnEnum;
3434
startExpanded: boolean;
3535
changeFocus: TreeNodeFocusChangeHandler;
3636
renderHeaderIcon: TreeNodeHeaderIcon;
37+
item: ObjectItem;
3738
}
3839

3940
export const treeNodeBranchUtils = {
4041
bodyClassName: "widget-tree-node-body",
41-
getHeaderId: (id: TreeNodeItem["id"]) => `${id}TreeNodeBranchHeader`,
42-
getBodyId: (id: TreeNodeItem["id"]) => `${id}TreeNodeBranchBody`
42+
getHeaderId: (id: Option<GUID>) => `${id}TreeNodeBranchHeader`,
43+
getBodyId: (id: Option<GUID>) => `${id}TreeNodeBranchBody`
4344
};
4445

4546
export function TreeNodeBranch({
@@ -52,7 +53,8 @@ export function TreeNodeBranch({
5253
isUserDefinedLeafNode,
5354
openNodeOn,
5455
renderHeaderIcon,
55-
startExpanded
56+
startExpanded,
57+
item
5658
}: TreeNodeBranchProps): ReactElement {
5759
const { level: currentContextLevel } = useContext(TreeNodeBranchContext);
5860

@@ -184,6 +186,7 @@ export function TreeNodeBranch({
184186
{((!isActualLeafNode && treeNodeState !== TreeNodeState.COLLAPSED_WITH_JS) || isAnimating) && (
185187
<TreeNodeBranchContext.Provider
186188
value={{
189+
parent: item,
187190
level: currentContextLevel + 1,
188191
informParentOfChildNodes
189192
}}

packages/pluggableWidgets/tree-node-web/src/components/TreeNodeBranchContext.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
import { ObjectItem } from "mendix";
12
import { createContext, useContext, useEffect } from "react";
23

34
export interface TreeNodeBranchContextProps {
5+
parent?: ObjectItem;
46
level: number;
57
informParentOfChildNodes: (numberOfNodes: number | undefined) => void;
68
}
79

810
export const TreeNodeBranchContext = createContext<TreeNodeBranchContextProps>({
11+
parent: undefined,
912
level: 0,
1013
informParentOfChildNodes: () => null
1114
});

0 commit comments

Comments
 (0)