Jelajahi Sumber

Merge pull request #13433 from GordonSmith/GRAPH_FAST

HPCC-22549 Switch to latest Graph Widget

Reviewed-By: Miguel Vazquez <miguel.vazquez@lexisnexis.com>
Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 5 tahun lalu
induk
melakukan
86e8dbf041

+ 1 - 1
esp/src/eclwatch/WUQueryWidget.js

@@ -74,6 +74,7 @@ define([
             this.downListForm = registry.byId(this.id + "DownListForm");
             this.fileName = registry.byId(this.id + "FileName");
             this.mineControl = registry.byId(this.id + "Mine");
+            this.wuCopyButton = registry.byId(this.id + "Copy");
         },
 
         startup: function (args) {
@@ -334,7 +335,6 @@ define([
                 this.mineControl.set("disabled", true);
             }
 
-            this.wuCopyButton = registry.byId(this.id + "Copy");
             Clippy.attachDomNode(this.wuCopyButton.domNode, function () {
                 var wuids = [];
                 arrayUtil.forEach(context.workunitsGrid.getSelected(), function (item, idx) {

+ 7 - 0
esp/src/eclwatch/css/hpcc.css

@@ -1699,6 +1699,13 @@ span.dijitReset.dijitInline.dijitIcon.fa {
     min-height: 16px;
 }
 
+span.dijitReset.dijitInline.dijitIcon.fa.disabled {
+    font-family: "FontAwesome";
+    color:darkgray;
+    min-width: 16px;
+    min-height: 16px;
+}
+
 .eclwatch_DPReport.report-col-count-2 .ddCell:nth-child(4n+3),
 .eclwatch_DPReport.report-col-count-2 .ddCell:nth-child(4n+4),
 .eclwatch_DPReport.report-col-count-3 .ddCell:nth-child(6n+4),

+ 2 - 0
esp/src/eclwatch/nls/hpcc.js

@@ -372,6 +372,8 @@ define({
             LastRun: "Last Run",
             LearnMore: "Learn More",
             LegacyForm: "Legacy Form",
+            LegacyGraphWidget: "Legacy Graph Widget",
+            LegacyGraphLayout: "Legacy Graph Layout",
             Legend: "Legend",
             LDAPWarning: "<b>LDAP Services Error:</b>  &lsquo;Too Many Users&rsquo; - Please use a Filter.",
             LibrariesUsed: "Libraries Used",

+ 2 - 1
esp/src/eclwatch/stub.js

@@ -133,7 +133,8 @@ define([
             pathname: location.pathname,
             hash: hashNodes.length >= 2 ? hashNodes[1] : "",
             resourcePath: baseHost + "/esp/files/eclwatch",
-            basePath: baseHost + "/esp/files"
+            basePath: baseHost + "/esp/files",
+            fullPath: location.origin + "/esp/files"
         };
     }
 

+ 9 - 2
esp/src/eclwatch/templates/GraphTree7Widget.html

@@ -18,8 +18,8 @@
                 <div id="${id}GraphToolbar" class="topPanel dijit dijitToolbar" role="toolbar">
                     <div data-dojo-attach-event="onClick:_onGraphRefresh" data-dojo-props="iconClass:'fa fa-refresh', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.Refresh}</div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
-                    <div data-dojo-attach-event="onClick:_onPartial" data-dojo-props="iconClass:'fa fa-window-restore', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.CollapseAll}</div>
-                    <div data-dojo-attach-event="onClick:_onMax" data-dojo-props="iconClass:'fa fa-window-maximize', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.Restore}</div>
+                    <div id="${id}Partial" data-dojo-attach-event="onClick:_onPartial" data-dojo-props="iconClass:'fa fa-window-restore', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.CollapseAll}</div>
+                    <div id="${id}Max" data-dojo-attach-event="onClick:_onMax" data-dojo-props="iconClass:'fa fa-window-maximize', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.Restore}</div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <div data-dojo-attach-event="onClick:_onZoomToFit" data-dojo-props="iconClass:'fa fa-arrows-alt', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.ZoomAll}</div>
                     <div data-dojo-attach-event="onClick:_onZoomToWidth" data-dojo-props="iconClass:'fa fa-arrows-h', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.ZoomWidth}</div>
@@ -46,6 +46,13 @@
                                         <input title="${i18n.Label}:" name="elabel" value="%Label%\n%NumRowsProcessed%\n%SkewMinRowsProcessed% / %SkewMaxRowsProcessed%" style="width: 95%;" data-dojo-props="trim: true" data-dojo-type="dijit.form.TextBox" />
                                     </div>
                                 </div>
+                                <div data-dojo-type="dijit.Fieldset">
+                                    <legend>${i18n.Advanced}</legend>
+                                    <div data-dojo-type="hpcc.TableContainer">
+                                        <input title="${i18n.LegacyGraphWidget}" name="LegacyGraph" data-dojo-type="dijit.form.CheckBox" />
+                                        <input title="${i18n.LegacyGraphLayout}" name="LegacyLayout" data-dojo-type="dijit.form.CheckBox" />
+                                    </div>
+                                </div>
                                 <div class="dijitDialogPaneActionBar">
                                     <button type="submit" data-dojo-attach-event="onClick:_onOptionsApply" data-dojo-type="dijit.form.Button">${i18n.Apply}</button>
                                     <button data-dojo-attach-event="onClick:_onOptionsReset" data-dojo-type="dijit.form.Button">${i18n.Defaults}</button>

File diff ditekan karena terlalu besar
+ 1398 - 1124
esp/src/package-lock.json


+ 16 - 10
esp/src/package.json

@@ -3,7 +3,6 @@
   "version": "1.0.0",
   "description": "'ECL Watch' Web interface for HPCC Platform.",
   "scripts": {
-    "preinstall-tmp": "rimraf ./node_modules/@hpcc-js || true",
     "clean": "rimraf ./build && rimraf ./lib",
     "jslint": "jshint --config ./.jshintrc ./eclwatch",
     "tslint": "tslint --project . src/**/*.ts",
@@ -15,8 +14,9 @@
     "copy-res-dojox": "cpx \"./node_modules/dojox/widget/ColorPicker/images/**/*.{png,jpg,gif}\" ./build/eclwatch/img/",
     "copy-res-TopoJSON": "cpx \"./node_modules/@hpcc-js/map/TopoJSON/**/*\" ./build/dist/TopoJSON/",
     "copy-res-font-awesome": "cpx \"./node_modules/font-awesome/**/*\" ./build/dist/font-awesome/",
+    "copy-res-wasm": "cpx \"node_modules/@hpcc-js/wasm/dist/graphvizlib.wasm\" ./build/dist/",
     "copy-res-stub_htm": "cpx \"./stub.htm\" ./build/",
-    "copy-res": "run-p copy-res-es6-promise copy-res-eclwatch-img copy-res-eclwatch-ecl copy-res-dojo copy-res-dojox copy-res-TopoJSON copy-res-font-awesome copy-res-stub_htm",
+    "copy-res": "run-p copy-res-es6-promise copy-res-eclwatch-img copy-res-eclwatch-ecl copy-res-dojo copy-res-dojox copy-res-TopoJSON copy-res-font-awesome copy-res-stub_htm copy-res-wasm",
     "compile": "tsc",
     "compile-watch": "npm run compile -- -w",
     "bundle": "node node_modules/webpack/bin/webpack.js --env production --config webpack.config.js",
@@ -32,13 +32,19 @@
   },
   "main": "src/stub.js",
   "dependencies": {
-    "@hpcc-js/chart": "2.27.0",
-    "@hpcc-js/comms": "2.12.0",
-    "@hpcc-js/eclwatch": "2.8.2",
-    "@hpcc-js/html": "2.8.2",
-    "@hpcc-js/map": "2.14.7",
-    "@hpcc-js/other": "2.13.12",
-    "@hpcc-js/tree": "2.9.1",
+    "@hpcc-js/chart": "^2.27.1",
+    "@hpcc-js/common": "^2.21.2",
+    "@hpcc-js/comms": "^2.12.3",
+    "@hpcc-js/eclwatch": "^2.8.7",
+    "@hpcc-js/graph": "^2.15.0",
+    "@hpcc-js/html": "^2.8.3",
+    "@hpcc-js/layout": "^2.16.13",
+    "@hpcc-js/map": "^2.14.12",
+    "@hpcc-js/other": "^2.13.16",
+    "@hpcc-js/react": "^2.9.0",
+    "@hpcc-js/tree": "^2.10.0",
+    "@hpcc-js/util": "^2.11.0",
+    "@hpcc-js/wasm": "^0.3.6",
     "@material-ui/core": "4.8.3",
     "@material-ui/icons": "^4.9.1",
     "@material-ui/lab": "^4.0.0-alpha.42",
@@ -62,7 +68,7 @@
     "dojo-webpack-plugin": "2.8.12",
     "file-loader": "5.0.2",
     "jshint": "2.10.2",
-    "local-web-server": "3.0.7",
+    "local-web-server": "4.0.0",
     "npm-run-all": "4.1.5",
     "rimraf": "3.0.0",
     "style-loader": "1.1.2",

+ 306 - 141
esp/src/src/GraphTree7Widget.ts

@@ -10,8 +10,8 @@ import * as topic from "dojo/topic";
 
 import * as registry from "dijit/registry";
 
-import { ScopeGraph, Workunit } from "@hpcc-js/comms";
-import { Graph as GraphWidget, Subgraph, Vertex } from "@hpcc-js/graph";
+import { ECLGraph, ScopeGraph, Workunit } from "@hpcc-js/comms";
+import { Graph as GraphWidget, Graph2 as Graph2Widget, Subgraph, Vertex } from "@hpcc-js/graph";
 import { hashSum } from "@hpcc-js/util";
 
 // @ts-ignore
@@ -22,7 +22,7 @@ import { GraphStore, GraphTreeStore } from "./GraphStore";
 import { debounce, getImageURL, Persist } from "./Utility";
 import * as WsWorkunits from "./WsWorkunits";
 import { WUGraphLegend } from "./WUGraphLegend";
-import { WUScopeController } from "./WUScopeController";
+import { WUScopeController, WUScopeController8 } from "./WUScopeController";
 
 // @ts-ignore
 import * as template from "dojo/text!hpcc/templates/GraphTree7Widget.html";
@@ -44,6 +44,42 @@ import "dijit/ToolbarSeparator";
 import "dijit/TooltipDialog";
 import "hpcc/TableContainer";
 
+declare const dojoConfig;
+
+window["__hpcc_wasmFolder"] = dojoConfig.urlInfo.fullPath + "/dist";
+
+class DataGraph {
+    private _controller: WUScopeController = new WUScopeController();
+    private _widget: GraphWidget = new GraphWidget();
+
+    constructor() {
+    }
+
+    widget(): GraphWidget {
+        return this._widget;
+    }
+
+    controller(): WUScopeController {
+        return this._controller;
+    }
+}
+
+class DataGraph2 {
+    private _controller: WUScopeController8 = new WUScopeController8();
+    private _widget: Graph2Widget = new Graph2Widget();
+
+    constructor() {
+    }
+
+    widget(): Graph2Widget {
+        return this._widget;
+    }
+
+    controller(): WUScopeController8 {
+        return this._controller;
+    }
+}
+
 type _Widget = {
     id: string;
     widget: any;
@@ -85,9 +121,8 @@ export class GraphTree7Widget {
     found = [];
     foundIndex = 0;
 
-    _graph: GraphWidget;
+    _graph: DataGraph | DataGraph2;
     protected _legend: WUGraphLegend;
-    _gc = new WUScopeController();
     protected treeStore = new GraphTreeStore();
     protected subgraphsStore = new GraphStore("Id");
     protected verticesStore = new GraphStore("Id");
@@ -95,15 +130,6 @@ export class GraphTree7Widget {
     private persist = new Persist("GraphTree7Widget");
 
     constructor() {
-        this._gc.minClick = (sg: Subgraph) => {
-            this.loadGraph(w => {
-                this._graph
-                    .selection([sg])
-                    .centerOnItem(sg)
-                    ;
-                this.syncSelectionFrom(this._graph);
-            });
-        };
     }
 
     //  Options ---
@@ -112,19 +138,22 @@ export class GraphTree7Widget {
         const optionsValues = this.optionsForm.getValues();
         this.persist.setObj("options", optionsValues);
         this.optionsDropDown.closeDropDown();
-        this.loadGraph();
+        this.initGraph();
+        this.refreshData();
+        this.refreshActionState();
     }
 
     _onOptionsReset() {
         this.optionsForm.setValues(this._optionsDefault);
-        this.loadGraph();
+        this.initGraph();
+        this.refreshData();
+        this.refreshActionState();
     }
 
     //  Data ---
     private _prevHashSum;
-    private _prevScopeGraph: Promise<ScopeGraph>;
+    private _prevScopeGraph: Promise<ECLGraph[]>;
     fetchScopeGraph(wuid: string, graphID: string, subgraphID: string = "", refresh: boolean = false): Promise<ScopeGraph> {
-        this.graphStatus.innerText = this.i18n.FetchingData;
         const hash = hashSum({
             wuid,
             graphID,
@@ -132,26 +161,28 @@ export class GraphTree7Widget {
         });
         if (!this._prevScopeGraph || refresh || this._prevHashSum !== hash) {
             this._prevHashSum = hash;
-            this._gc.clear();
+            this.graphStatus.innerText = this.i18n.FetchingData;
+            this._graph.controller().clear();
             const wu = Workunit.attach({ baseUrl: "" }, wuid);
-            this._prevScopeGraph = wu.fetchGraphs().then(graphs => {
-                for (const graph of graphs) {
-                    if (graph.Name === graphID) {
-                        return graph.fetchScopeGraph(subgraphID).then(scopedGraph => {
-                            this.graphStatus.innerText = this.i18n.Loading;
-                            return new Promise<ScopeGraph>((resolve, reject) => {
-                                setTimeout(() => {
-                                    this._gc.set(scopedGraph);
-                                    this._legend.data(this._gc.calcLegend());
-                                    resolve(scopedGraph);
-                                }, 0);
-                            });
+            this._prevScopeGraph = wu.fetchGraphs();
+        }
+        return this._prevScopeGraph.then(graphs => {
+            for (const graph of graphs) {
+                if (graph.Name === graphID) {
+                    return graph.fetchScopeGraph(subgraphID).then(scopedGraph => {
+                        this.graphStatus.innerText = this.i18n.Loading;
+                        return new Promise<ScopeGraph>((resolve, reject) => {
+                            this.graphStatus.innerText = "";
+                            setTimeout(() => {
+                                this._graph.controller().set(scopedGraph);
+                                this._legend.data(this._graph.controller().calcLegend());
+                                resolve(scopedGraph);
+                            }, 0);
                         });
-                    }
+                    });
                 }
-            });
-        }
-        return this._prevScopeGraph;
+            }
+        });
     }
     //  --- ---
 
@@ -196,7 +227,7 @@ export class GraphTree7Widget {
     _initGraphControls() {
         aspect.after(registry.byId(this.id + "MainBorderContainer"), "resize", () => {
             if (this._graph) {
-                this._graph
+                this._graph.widget()
                     .resize()
                     .render()
                     ;
@@ -216,57 +247,65 @@ export class GraphTree7Widget {
     }
 
     _onRefresh() {
-        this.refreshData();
+        this.refreshData(true);
     }
 
     _onGraphRefresh() {
-        this._graph.data().subgraphs.forEach((sg: Subgraph) => {
-            sg.minState("normal");
-        });
-        delete this._graph["_prevLayout"];
+        if (this._graph instanceof DataGraph) {
+            this._graph.widget().data().subgraphs.forEach((sg: Subgraph) => {
+                sg.minState("normal");
+            });
+            delete this._graph.widget()["_prevLayout"];
+        } else {
+            this._graph.widget().resetLayout();
+        }
         this.loadGraph(w => {
-            this._graph.zoomToFit();
+            this._graph.widget().zoomToFit();
         });
     }
 
     _onPartial(args) {
-        this._graph.data().subgraphs.forEach((sg: Subgraph) => {
-            sg.minState("partial");
-        });
-        this.loadGraph(w => {
-            this._graph.zoomToFit();
-        });
+        if (this._graph instanceof DataGraph) {
+            this._graph.widget().data().subgraphs.forEach((sg: Subgraph) => {
+                sg.minState("partial");
+            });
+            this.loadGraph(w => {
+                this._graph.widget().zoomToFit();
+            });
+        }
     }
 
     _onMax(args) {
-        this._graph.data().subgraphs.forEach((sg: Subgraph) => {
-            sg.minState("normal");
-        });
-        this.loadGraph(w => {
-            this._graph.zoomToFit();
-        });
+        if (this._graph instanceof DataGraph) {
+            this._graph.widget().data().subgraphs.forEach((sg: Subgraph) => {
+                sg.minState("normal");
+            });
+            this.loadGraph(w => {
+                this._graph.widget().zoomToFit();
+            });
+        }
     }
 
     _onZoomToFit(args) {
-        this._graph.zoomToFit();
+        this._graph.widget().zoomToFit();
     }
 
     _onZoomToWidth(args) {
-        this._graph.zoomToWidth();
+        this._graph.widget().zoomToWidth();
     }
 
     _onZoomToPlus(args) {
-        this._graph.zoomPlus();
+        this._graph.widget().zoomPlus();
     }
 
     _onZoomToMinus(args) {
-        this._graph.zoomMinus();
+        this._graph.widget().zoomMinus();
     }
 
     _doFind(prev) {
         if (this.findText !== this.widget.FindField.value) {
             this.findText = this.widget.FindField.value;
-            this.found = this._gc.find(this.findText);
+            this.found = this._graph.controller().find(this.findText);
             this.syncSelectionFrom(this.found);
             this.foundIndex = -1;
         }
@@ -277,7 +316,13 @@ export class GraphTree7Widget {
             this.foundIndex = 0;
         }
         if (this.found.length) {
-            this._graph.centerOnItem(this._gc.item(this.found[this.foundIndex]));
+            if (this._graph instanceof DataGraph) {
+                const item = this._graph.controller().item(this.found[this.foundIndex]);
+                this._graph.widget().centerOnItem(item);
+            } else {
+                const item = this._graph.controller().item(this.found[this.foundIndex]);
+                this._graph.widget().centerOnItem(item.id);
+            }
         }
         this.refreshActionState();
     }
@@ -312,7 +357,7 @@ export class GraphTree7Widget {
         if (this.inherited(arguments))
             return;
 
-        this.initGraph();
+        this.initWidgets();
         this.initSubgraphs();
         this.initVertices();
         this.initEdges();
@@ -349,15 +394,16 @@ export class GraphTree7Widget {
         });
     }
 
-    refreshData() {
+    refreshData(refresh: boolean = false) {
         if (this.isWorkunit()) {
-            return this.loadGraphFromWu(this.wuid, this.graphName, this.subGraphId, true);
+            return this.loadGraphFromWu(this.wuid, this.graphName, this.subGraphId, refresh);
         } else if (this.isQuery()) {
         }
         return Promise.resolve();
     }
 
     loadGraphFromWu(wuid, graphName, subGraphId, refresh: boolean = false) {
+        this.initGraphController();
         return this.fetchScopeGraph(wuid, graphName, subGraphId, refresh).then(() => {
             this.loadGraph();
             this.loadSubgraphs();
@@ -367,37 +413,109 @@ export class GraphTree7Widget {
     }
 
     initGraph() {
+        const options = this.optionsForm.getValues();
+        const prevGraph = this._graph;
+        if (options.LegacyGraph.length) {
+            if (!(prevGraph instanceof DataGraph)) {
+                this._graph = new DataGraph();
+                this._graph.controller().minClick = (sg: Subgraph) => {
+                    this.loadGraph(w => {
+                        (this._graph as DataGraph).widget()
+                            .selection([sg])
+                            .centerOnItem(sg)
+                            ;
+                        this.syncSelectionFrom(this._graph);
+                    });
+                };
+            }
+        } else {
+            if (!(prevGraph instanceof DataGraph2)) {
+                this._graph = new DataGraph2();
+            }
+        }
+        if (this._graph !== prevGraph) {
+            if (prevGraph) {
+                prevGraph.widget().target(null);
+            }
+            if (this._graph instanceof DataGraph) {
+                this._graph.widget()
+                    .target(this.id + "MainGraphWidget")
+                    .layout("Hierarchy")
+                    .applyScaleOnLayout(true)
+                    .showToolbar(false)
+                    .allowDragging(false)
+                    .on("vertex_click", sel => {
+                        this.syncSelectionFrom(this._graph);
+                    })
+                    .on("edge_click", sel => {
+                        this.syncSelectionFrom(this._graph);
+                    })
+                    .on("progress", what => {
+                        switch (what) {
+                            case "start":
+                            case "layout-start":
+                            case "layout-tick":
+                                this.graphStatus.innerText = this.i18n.PerformingLayout;
+                                break;
+                            case "layout-end":
+                            case "end":
+                            default:
+                                this.graphStatus.innerText = "";
+                                break;
+                        }
+                    })
+                    ;
+            } else {
+                this._graph.widget()
+                    .target(this.id + "MainGraphWidget")
+                    .layout("DOT")
+                    .applyScaleOnLayout(true)
+                    .showToolbar(false)
+                    .allowDragging(false)
+                    .minScale(0)
+                    .maxScale(999999999)
+                    .on("subgraph_click", sel => {
+                        this.syncSelectionFrom(this._graph);
+                    })
+                    .on("vertex_click", sel => {
+                        this.syncSelectionFrom(this._graph);
+                    })
+                    .on("edge_click", sel => {
+                        this.syncSelectionFrom(this._graph);
+                    })
+                    .on("progress", what => {
+                        switch (what) {
+                            case "start":
+                            case "stop":
+                                break;
+                            case "layout-start":
+                            case "layout-tick":
+                                // render(Busy, { open: true }, this.id + "MainGraphWidgetStatus");
+                                this.graphStatus.innerText = this.i18n.PerformingLayout;
+                                break;
+                            case "layout-stop":
+                                this.graphStatus.innerText = "";
+                                // render(Busy, { open: false }, this.id + "MainGraphWidgetStatus");
+                                break;
+                        }
+                    })
+                    ;
+            }
+        }
+        if (this._graph instanceof DataGraph2) {
+            this._graph.widget().layout(options.LegacyLayout.length ? "Hierarchy" : "DOT");
+        }
+    }
+
+    initWidgets() {
         this.graphStatus = dom.byId(this.id + "GraphStatus");
-        this._graph = new GraphWidget()
-            .target(this.id + "MainGraphWidget")
-            .layout("Hierarchy")
-            .applyScaleOnLayout(true)
-            .showToolbar(false)
-            .allowDragging(false)
-            .on("vertex_click", sel => {
-                this.syncSelectionFrom(this._graph);
-            })
-            .on("edge_click", sel => {
-                this.syncSelectionFrom(this._graph);
-            })
-            .on("progress", what => {
-                switch (what) {
-                    case "start":
-                    case "layout-start":
-                    case "layout-tick":
-                        this.graphStatus.innerText = this.i18n.PerformingLayout;
-                        break;
-                    case "layout-end":
-                    case "end":
-                    default:
-                        this.graphStatus.innerText = "";
-                        break;
-                }
-            });
+        this.initGraph();
 
-        this._graph.tooltipHTML((v: Vertex) => {
-            return this._gc.calcGraphTooltip2(v);
-        });
+        if (this._graph instanceof DataGraph) {
+            this._graph.widget().tooltipHTML((v: Vertex) => {
+                return (this._graph.controller() as WUScopeController).calcGraphTooltip2(v);
+            });
+        }
 
         this._legend = new WUGraphLegend(this as any)
             .target(this.id + "LegendGrid")
@@ -406,30 +524,58 @@ export class GraphTree7Widget {
             })
             .on("mouseover", kind => {
                 const verticesMap: { [id: string]: boolean } = {};
-                for (const vertex of this._gc.vertices(kind)) {
-                    verticesMap[vertex.id()] = true;
+
+                if (this._graph instanceof DataGraph) {
+                    for (const vertex of this._graph.controller().vertices(kind)) {
+                        verticesMap[vertex.id()] = true;
+                    }
+                } else {
+                    for (const vertex of this._graph.controller().vertices(kind)) {
+                        verticesMap[vertex.id] = true;
+                    }
                 }
-                this._graph.highlightVerticies(verticesMap);
+                this._graph.widget().highlightVerticies(verticesMap);
             })
             .on("mouseout", kind => {
-                this._graph.highlightVerticies();
+                this._graph.widget().highlightVerticies();
             })
             ;
     }
 
-    loadGraph(callback?) {
+    initGraphController() {
         const options = this.optionsForm.getValues();
-        this._gc
-            .showSubgraphs(options.subgraph.length)
-            .showIcon(options.vicon.length)
-            .vertexLabelTpl(options.vlabel)
-            .edgeLabelTpl(options.elabel)
-            .disabled(this._legend.disabled())
-            ;
-        this._graph
-            .data(this._gc.graphData(), true)
-            .render(callback)
-            ;
+        if (this._graph instanceof DataGraph) {
+            this._graph.controller()
+                .showSubgraphs(options.subgraph.length)
+                .showIcon(options.vicon.length)
+                .vertexLabelTpl(options.vlabel)
+                .edgeLabelTpl(options.elabel)
+                .disabled(this._legend.disabled())
+                ;
+        } else {
+            this._graph.controller()
+                .showSubgraphs(options.subgraph.length)
+                .showIcon(options.vicon.length)
+                .vertexLabelTpl(options.vlabel)
+                .edgeLabelTpl(options.elabel)
+                .disabled(this._legend.disabled())
+                ;
+        }
+    }
+
+    loadGraph(callback?) {
+        this.initGraphController();
+        if (this._graph instanceof DataGraph) {
+            this._graph.widget()
+                .data(this._graph.controller().graphData(), true)
+                .render(callback)
+                ;
+        } else {
+            this._graph.widget()
+                .data(this._graph.controller().graphData())
+                .render(callback)
+                ;
+        }
         this._legend
             .render()
             ;
@@ -460,7 +606,7 @@ export class GraphTree7Widget {
     }
 
     loadSubgraphs() {
-        const subgraphs = this._gc.subgraphStoreData();
+        const subgraphs = this._graph.controller().subgraphStoreData();
         this.subgraphsStore.setData(subgraphs);
         const context = this;
         const img = getImageURL("folder.png");
@@ -487,7 +633,7 @@ export class GraphTree7Widget {
     }
 
     loadVertices() {
-        const vertices = this._gc.activityStoreData();
+        const vertices = this._graph.controller().activityStoreData();
         this.verticesStore.setData(vertices);
         const columns = [
             {
@@ -514,7 +660,7 @@ export class GraphTree7Widget {
     }
 
     loadEdges() {
-        const edges = this._gc.edgeStoreData();
+        const edges = this._graph.controller().edgeStoreData();
         this.edgesStore.setData(edges);
         const columns = [
             { label: this.i18n.ID, field: "Id", width: 50 }
@@ -527,31 +673,35 @@ export class GraphTree7Widget {
 
     centerOn(itemID?: string) {
         if (itemID) {
-            let refresh = false;
-            let scopeItem = this._gc.scopeItem(itemID);
-            while (scopeItem) {
-                const w = this._gc.item(scopeItem._.Id);
-                if (w && w instanceof Subgraph && w.minState() !== "normal") {
-                    w.minState("normal");
-                    refresh = true;
+            if (this._graph instanceof DataGraph) {
+                let refresh = false;
+                let scopeItem = this._graph.controller().scopeItem(itemID);
+                while (scopeItem) {
+                    const w = this._graph.controller().item(scopeItem._.Id);
+                    if (w && w instanceof Subgraph && w.minState() !== "normal") {
+                        w.minState("normal");
+                        refresh = true;
+                    }
+                    scopeItem = scopeItem.parent;
                 }
-                scopeItem = scopeItem.parent;
-            }
-            const w = this._gc.item(itemID);
-            if (w) {
-                if (refresh) {
-                    this._graph
-                        .data(this._gc.graphData(), true)   //  Force re-render
-                        .render(w => {
-                            setTimeout(() => {
-                                this._graph
-                                    .centerOnItem(w)
-                                    ;
-                            }, 1000);
-                        });
-                } else {
-                    this._graph.centerOnItem(w);
+                const w = this._graph.controller().item(itemID);
+                if (w) {
+                    if (refresh) {
+                        this._graph.widget()
+                            .data(this._graph.controller().graphData(), true)   //  Force re-render
+                            .render(() => {
+                                setTimeout(() => {
+                                    if (this._graph instanceof DataGraph) {
+                                        this._graph.widget().centerOnItem(w);
+                                    }
+                                }, 1000);
+                            });
+                    } else {
+                        this._graph.widget().centerOnItem(w);
+                    }
                 }
+            } else {
+                this._graph.widget().centerOnItem(itemID);
             }
         }
     }
@@ -583,6 +733,8 @@ export class GraphTree7Widget {
         this.setDisabled(this.id + "FindPrevious", this.foundIndex <= 0, "iconLeft", "iconLeftDisabled");
         this.setDisabled(this.id + "FindNext", this.foundIndex >= this.found.length - 1, "iconRight", "iconRightDisabled");
         this.setDisabled(this.id + "ActivityMetric", tab && tab.id !== this.id + "ActivitiesTreeMap");
+        this.setDisabled(this.id + "Partial", this._graph instanceof DataGraph2, "fa fa-window-restore", "disabled fa fa-window-restore");
+        this.setDisabled(this.id + "Max", this._graph instanceof DataGraph2, "fa fa-window-maximize", "disabled fa fa-window-maximize");
     }
 }
 
@@ -601,10 +753,18 @@ GraphTree7Widget.prototype._syncSelectionFrom = debounce(function (this: GraphTr
                 selectedGlobalIDs = [this.subGraphId];
             }
         } else if (sourceControl === this._graph) {
-            selectedGlobalIDs = this._graph.selection()
-                .map((w: any) => this._gc.rItem(w))
-                .filter(item => !!item)
-                .map(item => item._.Id);
+            if (this._graph instanceof DataGraph) {
+                selectedGlobalIDs = this._graph.widget().selection()
+                    .map(w => (this._graph.controller() as WUScopeController).rItem(w as any))
+                    .filter(item => !!item)
+                    .map(item => item._.Id);
+            } else {
+                selectedGlobalIDs = this._graph.widget().selection()
+                    .map(w => (this._graph.controller() as WUScopeController8).rItem(w.id))
+                    .filter(item => !!item)
+                    .map(item => item._.Id)
+                    ;
+            }
         } else if (sourceControl === this.verticesGrid || sourceControl === this.edgesGrid || sourceControl === this.subgraphsGrid) {
             const items = sourceControl.getSelected();
             for (let i = 0; i < items.length; ++i) {
@@ -632,15 +792,20 @@ GraphTree7Widget.prototype._syncSelectionFrom = debounce(function (this: GraphTr
 
     //  Refresh Graph Controls  ---
     if (sourceControl !== this._graph) {
-        const items = this._gc.items(selectedGlobalIDs);
-        this._graph.selection(items);
+        if (this._graph instanceof DataGraph) {
+            const items = this._graph.controller().items(selectedGlobalIDs);
+            this._graph.widget().selection(items);
+        } else {
+            const items = this._graph.controller().items(selectedGlobalIDs);
+            this._graph.widget().selection(items);
+        }
     }
 
     const propertiesDom = dom.byId(this.id + "Properties");
     propertiesDom.innerHTML = "";
     let html = "";
     for (const id of selectedGlobalIDs) {
-        html += this._gc.calcGraphTooltip(id, this.findText);
+        html += this._graph.controller().calcGraphTooltip(id, this.findText);
     }
     propertiesDom.innerHTML = html;
     const context = this;

File diff ditekan karena terlalu besar
+ 766 - 549
esp/src/src/WUScopeController.ts


+ 5 - 5
esp/src/src/react/hooks/useWsStore.ts

@@ -1,17 +1,17 @@
-import {useEffect, useState} from 'react';
+import { useEffect, useState } from "react";
 import { userKeyValStore } from "../../KeyValStore";
 
 const user_store = userKeyValStore();
 
 export const useGet = (key: string, filter?: object) => {
-    const [responseState, setResponseState] = useState({data: null, loading: true});
+    const [responseState, setResponseState] = useState({ data: null, loading: true });
     useEffect(() => {
-        setResponseState({data:null, loading: true});
+        setResponseState({ data: null, loading: true });
         user_store.get(key)
             .then(item => (item ? JSON.parse(item) : undefined))
             .then(response => {
-                setResponseState({data: response, loading: false});
+                setResponseState({ data: response, loading: false });
             });
     }, [filter]);
     return responseState;
-}
+};

+ 2 - 1
esp/src/src/react/render.ts

@@ -1,7 +1,8 @@
 import * as React from "react";
 import * as ReactDOM from "react-dom";
 
-export function render<P>(C: React.FunctionComponent<P>, props: Readonly<P>, parent: Element | Document | ShadowRoot | DocumentFragment, replaceNode?: Element | Text) {
+export function render<P>(C: React.FunctionComponent<P>, props: Readonly<P>, _parent: Element | Document | ShadowRoot | DocumentFragment | string, replaceNode?: Element | Text) {
+    const parent = typeof _parent === "string" ? document.getElementById(_parent) : _parent;
     ReactDOM.render(React.createElement(C, props), parent, replaceNode);
 }
 

+ 2 - 2
esp/src/src/react/wuStatus.tsx

@@ -31,7 +31,7 @@ const Steps = [
 
 const wuSteps = (compile: boolean) => {
     return compile ? [Steps[0], Steps[1], Steps[3]] : [...Steps];
-}
+};
 
 const wuStep = (wu?: Workunit): number => {
     switch (wu ? wu.StateID : WUStateID.Unknown) {
@@ -104,5 +104,5 @@ export const WUStatus: React.FunctionComponent<WUStatus> = ({
                 })}
             </Stepper>;
         </MuiThemeProvider>
-    )
+    );
 };