瀏覽代碼

DAG comp (#192) (#194)

* #192 DAG comp

* add doc WiP
add properties
- id
- class_name

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide 1 年之前
父節點
當前提交
17ecc011fc

+ 6 - 2
gui/src/NodeSelector.tsx

@@ -35,7 +35,7 @@ import {
     Pipeline as PipelineIcon,
     Scenario as ScenarioIcon,
 } from "./icons";
-import { BadgePos, BadgeSx, BaseTreeViewSx, FlagSx, MainBoxSx, ParentItemSx,  } from "./utils";
+import { BadgePos, BadgeSx, BaseTreeViewSx, FlagSx, MainBoxSx, ParentItemSx, useClassNames } from "./utils";
 
 interface NodeSelectorProps {
     id?: string;
@@ -53,6 +53,9 @@ interface NodeSelectorProps {
     value?: string;
     defaultValue?: string;
     height: string;
+    libClassName?: string;
+    className?: string;
+    dynamicClassName?: string;
 }
 
 const treeItemLabelSx = {
@@ -117,6 +120,7 @@ const NodeSelector = (props: NodeSelectorProps) => {
     const { id = "", datanodes = [], propagate = true } = props;
 
     const [selected, setSelected] = useState("");
+    const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
 
     const dispatch = useDispatch();
     const module = useModule();
@@ -180,7 +184,7 @@ const NodeSelector = (props: NodeSelectorProps) => {
     const treeViewSx = useMemo(() => ({ ...BaseTreeViewSx, maxHeight: props.height || "50vh" }), [props.height]);
 
     return (
-        <Box sx={MainBoxSx}>
+        <Box sx={MainBoxSx} id={props.id} className={className}>
             <TreeView
                 defaultCollapseIcon={<ExpandMore />}
                 defaultExpandIcon={<ChevronRight />}

+ 33 - 98
gui/src/ScenarioDag.tsx

@@ -1,17 +1,10 @@
-import React, { useCallback, useEffect, useMemo, useState } from "react";
+import React, { useEffect, useMemo, useState } from "react";
 import { CanvasWidget } from "@projectstorm/react-canvas-core";
 import Box from "@mui/material/Box";
-import Button from "@mui/material/Button";
 import AppBar from "@mui/material/AppBar";
-import Dialog from "@mui/material/Dialog";
-import DialogTitle from "@mui/material/DialogTitle/DialogTitle";
-import DialogContent from "@mui/material/DialogContent/DialogContent";
 import IconButton from "@mui/material/IconButton";
-import Slide from "@mui/material/Slide";
 import Toolbar from "@mui/material/Toolbar";
-import Typography from "@mui/material/Typography";
-import { TransitionProps } from "@mui/material/transitions";
-import { Close, ZoomIn } from "@mui/icons-material";
+import { ZoomIn } from "@mui/icons-material";
 
 import { DisplayModel } from "./utils/types";
 import { initDiagram, populateModel, relayoutDiagram } from "./utils/diagram";
@@ -24,20 +17,22 @@ import {
     useDynamicProperty,
     useModule,
 } from "taipy-gui";
+import { useClassNames } from "./utils";
 
 interface ScenarioDagProps {
     id?: string;
     scenario?: DisplayModel | DisplayModel[];
     coreChanged?: Record<string, unknown>;
-    buttonLabel?: string;
-    defaultButtonLabel?: string;
     updateVarName?: string;
-    show?: boolean;
-    defaultShow?: boolean;
-    withButton?: boolean;
+    render?: boolean;
+    defaultRender?: boolean;
+    showToolbar?: boolean;
     width?: string;
     height?: string;
     updateVars: string;
+    libClassName?: string;
+    className?: string;
+    dynamicClassName?: string;
 }
 
 const boxSx = { "&>div": { height: "100%", width: "100%" }, height: "100%", width: "100%" };
@@ -50,67 +45,41 @@ const relayout = () => relayoutDiagram(engine, dagreEngine);
 
 const zoomToFit = () => engine.zoomToFit();
 
-const Transition = React.forwardRef(function Transition(
-    props: TransitionProps & {
-        children: React.ReactElement;
-    },
-    ref: React.Ref<unknown>
-) {
-    return <Slide direction="up" ref={ref} {...props} />;
-});
-
 interface DagTitleProps {
-    title: string;
     zoomToFit: () => void;
-    hideDialog?: () => void;
 }
 const DagTitle = (props: DagTitleProps) => (
     <AppBar sx={appBarSx}>
         <Toolbar>
-            <Typography sx={titleSx} variant="h6" component="div">
-                Scenario: {props.title}
-            </Typography>
+            <Box sx={titleSx} />{" "}
             <IconButton edge="end" color="inherit" onClick={props.zoomToFit} title="zoom to fit">
                 <ZoomIn />
-            </IconButton>{" "}
-            {props.hideDialog ? (
-                <IconButton edge="end" color="inherit" onClick={props.hideDialog} title="close">
-                    <Close />
-                </IconButton>
-            ) : null}
+            </IconButton>
         </Toolbar>
     </AppBar>
 );
 
 const ScenarioDag = (props: ScenarioDagProps) => {
-    const { withButton = true } = props;
-
-    const [open, setOpen] = useState(false);
-    const [disabled, setDisabled] = useState(false);
-    const [title, setTitle] = useState("");
-
+    const { showToolbar = true } = props;
+    const [scenarioId, setScenarioId] = useState("");
     const dispatch = useDispatch();
     const module = useModule();
 
-    const label = useDynamicProperty(props.buttonLabel, props.defaultButtonLabel, "Show DAG");
-    const show = useDynamicProperty(props.show, props.defaultShow, !withButton);
-
-    const [full, sizeSx] = useMemo(() => {
-        return [!props.width && !props.height, { width: props.width || "50vw", height: props.height || "50vh" }];
-    }, [props.width, props.height]);
+    const render = useDynamicProperty(props.render, props.defaultRender, true);
+    const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
 
-    const showGraph = useCallback(() => {
-        setTimeout(relayout, 500);
-        setOpen(true);
-    }, []);
-    const hideGraph = useCallback(() => setOpen(false), []);
+    const sizeSx = useMemo(
+        () => ({ width: props.width || "50vw", height: props.height || "50vh" }),
+        [props.width, props.height]
+    );
 
     // Refresh on broadcast
     useEffect(() => {
-        if (props.coreChanged?.scenario) {
+        const ids = props.coreChanged?.scenario;
+        if (typeof ids === "string" ? ids === scenarioId : Array.isArray(ids) ? ids.includes(scenarioId) : ids) {
             props.updateVarName && dispatch(createRequestUpdateAction(props.id, module, [props.updateVarName], true));
         }
-    }, [props.coreChanged, props.updateVarName, module, dispatch, props.id]);
+    }, [props.coreChanged, props.updateVarName, scenarioId, module, dispatch, props.id]);
 
     useEffect(() => {
         const displayModel = Array.isArray(props.scenario)
@@ -123,12 +92,8 @@ const ScenarioDag = (props: ScenarioDagProps) => {
 
         // clear model
         const model = new TaipyDiagramModel();
-        if (!displayModel || !props.scenario) {
-            setDisabled(true);
-            setTitle("");
-        } else {
-            setDisabled(false);
-            setTitle(displayModel[0]);
+        if (displayModel && props.scenario) {
+            setScenarioId(displayModel[0]);
             // populate model
             populateModel(displayModel, model);
         }
@@ -139,48 +104,18 @@ const ScenarioDag = (props: ScenarioDagProps) => {
         setTimeout(relayout, 500);
     }, [props.scenario]);
 
-    useEffect(() => {
-        if (withButton) {
-            show && setTimeout(relayout, 500);
-            setOpen(!!show);
-        }
-    }, [show, withButton]);
-
     useEffect(() => {
         const showVar = getUpdateVar(props.updateVars, "show");
-        showVar && dispatch(createSendUpdateAction(showVar, show, module));
-    }, [show, props.updateVars, dispatch, module]);
-
-    return withButton ? (
-        <>
-            <Button id={props.id} variant="outlined" onClick={showGraph} disabled={disabled}>
-                {label}
-            </Button>
-            {full ? (
-                <Dialog fullScreen open={open} onClose={hideGraph} TransitionComponent={Transition}>
-                    <DagTitle title={title} zoomToFit={zoomToFit} hideDialog={hideGraph} />
-                    <Box sx={boxSx}>{open ? <CanvasWidget engine={engine} /> : null}</Box>
-                </Dialog>
-            ) : (
-                <Dialog open={open} onClose={hideGraph} TransitionComponent={Transition}>
-                    <DialogTitle>
-                        <DagTitle title={title} zoomToFit={zoomToFit} hideDialog={hideGraph} />
-                    </DialogTitle>
-                    <DialogContent sx={sizeSx}>
-                        <Box sx={boxSx}>{open ? <CanvasWidget engine={engine} /> : null}</Box>
-                    </DialogContent>
-                </Dialog>
-            )}
-        </>
-    ) : show ? (
-        <>
-            <Box sx={sizeSx}>
-                <DagTitle title={title} zoomToFit={zoomToFit} />
-                <Box sx={boxSx}>
-                    <CanvasWidget engine={engine} />
-                </Box>
+        showVar && dispatch(createSendUpdateAction(showVar, render, module));
+    }, [render, props.updateVars, dispatch, module]);
+
+    return render ? (
+        <Box sx={sizeSx} id={props.id} className={className}>
+            {showToolbar ? <DagTitle zoomToFit={zoomToFit} /> : null}
+            <Box sx={boxSx}>
+                <CanvasWidget engine={engine} />
             </Box>
-        </>
+        </Box>
     ) : null;
 };
 

+ 19 - 4
gui/src/ScenarioSelector.tsx

@@ -49,7 +49,17 @@ import {
 
 import { Cycle, Scenario } from "./icons";
 import ConfirmDialog from "./utils/ConfirmDialog";
-import { BadgePos, BadgeSx, BaseTreeViewSx, FlagSx, MainBoxSx, ParentItemSx, ScFProps, ScenarioFull } from "./utils";
+import {
+    BadgePos,
+    BadgeSx,
+    BaseTreeViewSx,
+    FlagSx,
+    MainBoxSx,
+    ParentItemSx,
+    ScFProps,
+    ScenarioFull,
+    useClassNames,
+} from "./utils";
 
 enum NodeType {
     CYCLE = 0,
@@ -96,6 +106,9 @@ interface ScenarioSelectorProps {
     value?: string;
     defaultValue?: string;
     height: string;
+    libClassName?: string;
+    className?: string;
+    dynamicClassName?: string;
 }
 
 interface ScenarioNodesProps {
@@ -516,6 +529,8 @@ const ScenarioSelector = (props: ScenarioSelectorProps) => {
     const [actionEdit, setActionEdit] = useState<boolean>(false);
     const [selected, setSelected] = useState("");
 
+    const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
+
     const dispatch = useDispatch();
     const module = useModule();
 
@@ -613,8 +628,8 @@ const ScenarioSelector = (props: ScenarioSelectorProps) => {
     const treeViewSx = useMemo(() => ({ ...BaseTreeViewSx, maxHeight: props.height || "50vh" }), [props.height]);
 
     return (
-        <div>
-            <Box sx={MainBoxSx}>
+        <>
+            <Box sx={MainBoxSx} id={props.id} className={className}>
                 <TreeView
                     defaultCollapseIcon={<ExpandMore />}
                     defaultExpandIcon={<ChevronRight />}
@@ -689,7 +704,7 @@ const ScenarioSelector = (props: ScenarioSelectorProps) => {
                 scenario={props.scenarioEdit}
                 submit={onSubmit}
             ></ScenarioEditDialog>
-        </div>
+        </>
     );
 };
 

+ 6 - 2
gui/src/ScenarioViewer.tsx

@@ -35,7 +35,7 @@ import {
     useModule,
 } from "taipy-gui";
 
-import { FlagSx, Property, ScFProps, ScenarioFull, ScenarioFullLength } from "./utils";
+import { FlagSx, Property, ScFProps, ScenarioFull, ScenarioFullLength, useClassNames } from "./utils";
 import ConfirmDialog from "./utils/ConfirmDialog";
 
 interface ScenarioViewerProps {
@@ -60,6 +60,9 @@ interface ScenarioViewerProps {
     showSubmit?: boolean;
     showSubmitPipelines?: boolean;
     showTags?: boolean;
+    libClassName?: string;
+    className?: string;
+    dynamicClassName?: string;
 }
 
 interface PipelinesRowProps {
@@ -237,6 +240,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
 
     const active = useDynamicProperty(props.active, props.defaultActive, true);
     const expanded = useDynamicProperty(props.expanded, props.defaultExpanded, false);
+    const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
 
     const [deleteDialog, setDeleteDialogOpen] = useState(false);
     const openDeleteDialog = useCallback(() => setDeleteDialogOpen(true), []);
@@ -446,7 +450,7 @@ const ScenarioViewer = (props: ScenarioViewerProps) => {
 
     return (
         <>
-            <Box sx={MainBoxSx} id={id} onClick={onFocus}>
+            <Box sx={MainBoxSx} id={id} onClick={onFocus} className={className}>
                 <Accordion
                     defaultExpanded={expandable && expanded}
                     expanded={userExpanded}

+ 1 - 1
gui/src/index.ts

@@ -3,4 +3,4 @@ import ScenarioViewer from "./ScenarioViewer";
 import ScenarioDag from "./ScenarioDag";
 import NodeSelector from "./NodeSelector";
 
-export { ScenarioSelector, ScenarioDag as Dag, ScenarioViewer as Scenario, NodeSelector as DataNodeSelector };
+export { ScenarioSelector, ScenarioDag, ScenarioViewer as Scenario, NodeSelector as DataNodeSelector };

+ 6 - 1
gui/src/utils.ts

@@ -11,6 +11,8 @@
  * specific language governing permissions and limitations under the License.
  */
 
+import { useDynamicProperty } from "taipy-gui";
+
 // id, is_primary, config_id, creation_date, label, tags, properties(key, value), pipelines(id, label), authorized_tags, deletable
 export type ScenarioFull = [
     string,
@@ -93,7 +95,7 @@ export const BaseTreeViewSx = {
     "& .MuiTreeItem-iconContainer:empty": {
         display: "none",
     },
-    maxHeight: "50vh"
+    maxHeight: "50vh",
 };
 
 export const ParentItemSx = {
@@ -103,3 +105,6 @@ export const ParentItemSx = {
         },
     },
 };
+
+export const useClassNames = (libClassName?: string, dynamicClassName?: string, className?: string) =>
+    ((libClassName || "") + " " + (useDynamicProperty(dynamicClassName, className, undefined) || "")).trim();

+ 10 - 5
src/taipy/gui_core/GuiCoreLib.py

@@ -88,7 +88,7 @@ class _GuiCoreScenarioDagAdapter(_TaipyBase):
                         "type": node.entity.storage_type() if hasattr(node.entity, "storage_type") else None,
                     }
                 return [
-                    data.get_label(),
+                    data.id,
                     nodes,
                     [
                         (
@@ -355,12 +355,14 @@ class _GuiCore(ElementLibrary):
         "scenario_selector": Element(
             "value",
             {
+                "id": ElementProperty(PropertyType.string),
                 "show_add_button": ElementProperty(PropertyType.dynamic_boolean, True),
                 "display_cycles": ElementProperty(PropertyType.dynamic_boolean, True),
                 "show_primary_flag": ElementProperty(PropertyType.dynamic_boolean, True),
                 "value": ElementProperty(PropertyType.lov_value),
                 "on_change": ElementProperty(PropertyType.function),
                 "height": ElementProperty(PropertyType.string, "50vh"),
+                "class_name": ElementProperty(PropertyType.dynamic_string),
             },
             inner_properties={
                 "scenarios": ElementProperty(PropertyType.lov, f"{{{__CTX_VAR_NAME}.get_scenarios()}}"),
@@ -392,6 +394,7 @@ class _GuiCore(ElementLibrary):
                 "show_properties": ElementProperty(PropertyType.boolean, True),
                 "show_pipelines": ElementProperty(PropertyType.boolean, True),
                 "show_submit_pipelines": ElementProperty(PropertyType.boolean, True),
+                "class_name": ElementProperty(PropertyType.dynamic_string),
             },
             inner_properties={
                 "on_edit": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.edit_entity}}"),
@@ -401,16 +404,16 @@ class _GuiCore(ElementLibrary):
                 "error": ElementProperty(PropertyType.react, f"{{{_GuiCoreContext._SCENARIO_VIZ_ERROR_VAR}}}"),
             },
         ),
-        "dag": Element(
+        "scenario_dag": Element(
             "scenario",
             {
                 "id": ElementProperty(PropertyType.string),
                 "scenario": ElementProperty(_GuiCoreScenarioDagAdapter),
-                "button_label": ElementProperty(PropertyType.dynamic_string),
-                "show": ElementProperty(PropertyType.dynamic_boolean, True),
-                "with_button": ElementProperty(PropertyType.boolean, True),
+                "render": ElementProperty(PropertyType.dynamic_boolean, True),
+                "show_toolbar": ElementProperty(PropertyType.boolean, True),
                 "width": ElementProperty(PropertyType.string),
                 "height": ElementProperty(PropertyType.string),
+                "class_name": ElementProperty(PropertyType.dynamic_string),
             },
             inner_properties={
                 "core_changed": ElementProperty(PropertyType.broadcast, _GuiCoreContext._CORE_CHANGED_NAME),
@@ -419,11 +422,13 @@ class _GuiCore(ElementLibrary):
         "data_node_selector": Element(
             "value",
             {
+                "id": ElementProperty(PropertyType.string),
                 "display_cycles": ElementProperty(PropertyType.dynamic_boolean, True),
                 "show_primary_flag": ElementProperty(PropertyType.dynamic_boolean, True),
                 "value": ElementProperty(PropertyType.lov_value),
                 "on_change": ElementProperty(PropertyType.function),
                 "height": ElementProperty(PropertyType.string, "50vh"),
+                "class_name": ElementProperty(PropertyType.dynamic_string),
             },
             inner_properties={
                 "datanodes": ElementProperty(PropertyType.lov, f"{{{__CTX_VAR_NAME}.get_datanodes_tree()}}"),

+ 246 - 38
src/taipy/gui_core/viselements.json

@@ -1,42 +1,250 @@
 {
-  "controls": [
-    [
-      "scenario_selector",
-      {
-        "properties": [
-          {
-            "name": "value",
-            "default_property": true,
-            "type": "dynamic(List[Scenario])",
-            "default_value": "[]",
-            "doc": "TODO - The list of selected scenarios."
-          },
-          {
-            "name": "show_add_button",
-            "type": "dynamic(bool)",
-            "default_value": "True",
-            "doc": "TODO"
-          },
-          {
-            "name": "display_cycles",
-            "type": "dynamic(bool)",
-            "default_value": "True",
-            "doc": "TODO"
-          },
-          {
-            "name": "show_primary_flag",
-            "type": "dynamic(bool)",
-            "default_value": "True",
-            "doc": "TODO"
-          },
-          {
-            "name": "on_change",
-            "type": "Callback",
-            "doc": "The name of a function that is triggered when the selection is updated.<br/>The parameters of that function are all optional:\nTODO",
-            "signature": [["TODO", ""]]
-          }
+    "controls": [
+        [
+            "scenario_selector",
+            {
+                "properties": [
+                    {
+                      "name": "id",
+                      "type": "str",
+                      "doc": "The identifier that will be assigned to the rendered HTML component."
+                    },
+                    {
+                      "name": "class_name",
+                      "type": "dynamic(str)",
+                      "doc": "The list of CSS class names that will be associated with the generated HTML Element.<br/>These class names will be added to the default <code>taipy_gui_core-&lt;element_type&gt;</code>."
+                    },
+                    {
+                        "name": "value",
+                        "default_property": true,
+                        "type": "dynamic(List[Scenario])",
+                        "default_value": "[]",
+                        "doc": "TODO - The list of selected scenarios."
+                    },
+                    {
+                        "name": "show_add_button",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "display_cycles",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "show_primary_flag",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "on_change",
+                        "type": "Callback",
+                        "doc": "The name of a function that is triggered when the selection is updated.<br/>The parameters of that function are all optional:\nTODO",
+                        "signature": [
+                            [
+                                "TODO",
+                                ""
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "height",
+                        "type": "str",
+                        "default_value": "50vh",
+                        "doc": "The maximum height (in CSS unit) of the tree."
+                    }
+                ]
+            }
+        ],
+        [
+            "scenario",
+            {
+                "properties": [
+                    {
+                      "name": "id",
+                      "type": "str",
+                      "doc": "The identifier that will be assigned to the rendered HTML component."
+                    },
+                    {
+                      "name": "class_name",
+                      "type": "dynamic(str)",
+                      "doc": "The list of CSS class names that will be associated with the generated HTML Element.<br/>These class names will be added to the default <code>taipy_gui_core-&lt;element_type&gt;</code>."
+                    },
+                    {
+                        "name": "scenario",
+                        "default_property": true,
+                        "type": "Scenario",
+                        "doc": "TODO - The scenario to display/edit."
+                    },
+                    {
+                        "name": "active",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "Indicates if this component is active.<br/>An inactive component allows no user interaction."
+                    },
+                    {
+                        "name": "expandable",
+                        "type": "bool",
+                        "default_value": "True",
+                        "doc": "If True, the scenario visualizer can be expanded.<br/>If False, the scenario visualizer is collapsed and only its name an submit button are visible."
+                    },
+                    {
+                        "name": "expanded",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "If True, the scenario visualizer is expanded, and its content is displayed.<br/>If False, the scenario visualizer is collapsed and only its name an submit button are visible."
+                    },
+                    {
+                        "name": "show_submit",
+                        "type": "bool",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "show_delete",
+                        "type": "bool",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "show_config",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "show_cycle",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "show_tags",
+                        "type": "bool",
+                        "default_value": "False",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "show_properties",
+                        "type": "bool",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "show_pipelines",
+                        "type": "bool",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "show_submit_pipelines",
+                        "type": "bool",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    }
+                ]
+            }
+        ],
+        [
+            "scenario_dag",
+            {
+                "properties": [
+                    {
+                      "name": "id",
+                      "type": "str",
+                      "doc": "The identifier that will be assigned to the rendered HTML component."
+                    },
+                    {
+                      "name": "class_name",
+                      "type": "dynamic(str)",
+                      "doc": "The list of CSS class names that will be associated with the generated HTML Element.<br/>These class names will be added to the default <code>taipy_gui_core-&lt;element_type&gt;</code>."
+                    },
+                    {
+                        "name": "scenario",
+                        "type": "Scenario",
+                        "doc": "TODO - The selected scenario."
+                    },
+                    {
+                        "name": "render",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "show_toolbar",
+                        "type": "bool",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "height",
+                        "type": "str",
+                        "default_value": "50vh",
+                        "doc": "The maximum height (in CSS unit) of the DAG."
+                    },
+                    {
+                        "name": "width",
+                        "type": "str",
+                        "default_value": "50vw",
+                        "doc": "The maximum width (in CSS unit) of the DAG."
+                    }
+                ]
+            }
+        ],
+        [
+            "data_node_selector",
+            {
+                "properties": [
+                    {
+                      "name": "id",
+                      "type": "str",
+                      "doc": "The identifier that will be assigned to the rendered HTML component."
+                    },
+                    {
+                      "name": "class_name",
+                      "type": "dynamic(str)",
+                      "doc": "The list of CSS class names that will be associated with the generated HTML Element.<br/>These class names will be added to the default <code>taipy_gui_core-&lt;element_type&gt;</code>."
+                    },
+                    {
+                        "name": "value",
+                        "default_property": true,
+                        "type": "dynamic(DataNode)",
+                        "doc": "TODO - The selected datanode."
+                    },
+                    {
+                        "name": "display_cycles",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "show_primary_flag",
+                        "type": "dynamic(bool)",
+                        "default_value": "True",
+                        "doc": "TODO"
+                    },
+                    {
+                        "name": "on_change",
+                        "type": "Callback",
+                        "doc": "The name of a function that is triggered when the selection is updated.<br/>The parameters of that function are all optional:\nTODO",
+                        "signature": [
+                            [
+                                "TODO",
+                                ""
+                            ]
+                        ]
+                    },
+                    {
+                        "name": "height",
+                        "type": "str",
+                        "default_value": "50vh",
+                        "doc": "The maximum height (in CSS unit) of the tree."
+                    }
+                ]
+            }
         ]
-      }
     ]
-  ]
 }