From 06e90ebaf1cb79026be016995fe3f21841508f03 Mon Sep 17 00:00:00 2001
From: Mathis Neumann <mathis@simpletechs.net>
Date: Fri, 22 Apr 2016 18:00:02 +0200
Subject: [PATCH] very basic ember js and klay+d3 integration

---
 app/components/architecture-visualisation.js  | 95 +++++++++++++++++++
 app/router.js                                 |  1 +
 app/routes/architecture.js                    | 61 ++++++++++++
 app/styles/app.scss                           | 29 ++++++
 app/styles/components/_architecture.scss      | 12 +++
 app/templates/application.hbs                 |  1 +
 app/templates/architecture.hbs                |  2 +
 .../components/architecture-visualisation.hbs |  1 +
 bower.json                                    |  4 +-
 package.json                                  | 12 ++-
 10 files changed, 216 insertions(+), 2 deletions(-)
 create mode 100644 app/components/architecture-visualisation.js
 create mode 100644 app/routes/architecture.js
 create mode 100644 app/styles/app.scss
 create mode 100644 app/styles/components/_architecture.scss
 create mode 100644 app/templates/architecture.hbs
 create mode 100644 app/templates/components/architecture-visualisation.hbs

diff --git a/app/components/architecture-visualisation.js b/app/components/architecture-visualisation.js
new file mode 100644
index 0000000..999db59
--- /dev/null
+++ b/app/components/architecture-visualisation.js
@@ -0,0 +1,95 @@
+import Ember from 'ember';
+import d3 from 'd3';
+import klay from 'npm:klayjs-d3';
+import _ from 'npm:lodash';
+
+export default Ember.Component.extend({
+    didInsertElement: function() {
+        const log = this.debug.bind(this);
+        const width = window.innerWidth;
+        const height = window.innerHeight;
+        this.debug('element', this.element);
+        const zoom = d3.behavior.zoom()
+            .on("zoom", redraw);
+
+        const svg = d3.select(this.element)
+            .append("svg")
+            .attr("width", width)
+            .attr("height", height)
+            .attr('class', 'architectureVisualisation')
+            // .call(zoom)
+            .append("g");
+        const root = svg.append("g");
+
+        this.layouter = klay.d3kgraph()
+            .size([width, height])
+            .transformGroup(root)
+            .options({
+                edgeRouting: "ORTHOGONAL"
+            });
+        // group shizzle
+
+        this.debug('element', this.element);
+
+        function redraw() {
+            svg.attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")");
+        }
+
+        const onFinish = (layout) => {
+            log('loaded layout', layout);
+            const nodes = this.layouter.nodes();
+            const links = this.layouter.links(nodes);
+
+            const nodeData = root.selectAll('.node')
+                .data(nodes, p => p.id);
+            const node = nodeData.enter()
+                .append('g')
+                .attr('class', d => d.children? 'node compound' : 'node leaf');
+
+            const atoms = node.append('rect')
+                .attr('width', 10)
+                .attr('height', 10)
+                .attr('x', 0)
+                .attr('y', 0);
+
+            node.append('title')
+                .text(d => d.id);
+
+            node.append('text')
+                .attr('x', (d) => d.children? 0 : 2.5)
+                .attr('y', 5)
+                .text((d) => _.get(d, 'labels.0.text', d.id))
+                .attr('font-size', '4px')
+                .attr('font-size', '4px');
+
+
+            const linkData = root.selectAll('.link')
+                .data(links, p => p.id);
+            const link = linkData.enter()
+                .append('path')
+                .attr('class', 'link')
+                .attr('d', 'M0 0');
+
+            // apply edge routes
+            link.transition().attr('d', (d) => {
+              let path = '';
+              path += 'M' + d.sourcePoint.x + ' ' + d.sourcePoint.y + ' ';
+                (d.bendPoints || []).forEach(bp => path += 'L' + bp.x + ' ' + bp.y + ' ');
+              path += 'L' + d.targetPoint.x + ' ' + d.targetPoint.y + ' ';
+              return path;
+            });
+
+            // apply node positions
+            node.transition()
+              .attr('transform', (d) => 'translate(' + (d.x || 0) + ' ' + (d.y || 0) + ')');
+
+            atoms.transition()
+              .attr('width', (d) => d.width)
+              .attr('height', (d) => d.height);
+        };
+        this.layouter.on('finish', onFinish);
+        this.layouter.kgraph(this.get('graph'));
+    }
+});
+
+
diff --git a/app/router.js b/app/router.js
index 3bba78e..42acc41 100644
--- a/app/router.js
+++ b/app/router.js
@@ -6,6 +6,7 @@ const Router = Ember.Router.extend({
 });
 
 Router.map(function() {
+    this.route('architecture', {path: '/architecture'});
 });
 
 export default Router;
diff --git a/app/routes/architecture.js b/app/routes/architecture.js
new file mode 100644
index 0000000..ac893ce
--- /dev/null
+++ b/app/routes/architecture.js
@@ -0,0 +1,61 @@
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model() {
+    return {
+        id: 'root',
+        labels: [{text: 'My Architecture'}],
+        children: [{
+            id: 'VM_Big',
+            children: [{
+                id: 'VM_Big>Auth'
+            }, {
+                id: 'VM_Big>User'
+            }],
+            edges: [{
+                id: 'VM_Big>Auth->VM_Big>User',
+                labels: [ { text: 'e1' } ],
+                source: 'VM_Big>Auth',
+                target: 'VM_Big>User'
+            }]
+        },
+        {
+            id: 'VM_1',
+            children: [{
+                id: 'VM_1>Auth',
+                labels: [{text: "Auth 1"}]
+            }]
+        },
+        {
+            id: 'VM_2',
+            children: [{
+                id: 'VM_2>Auth',
+                labels: [{text: "Auth 2"}]
+            }]
+        },
+        {
+            id: 'VM_3',
+            children: [{
+                id: 'VM_3>User',
+                labels: [{text: "User 1"}]
+            }]
+        },
+        {
+            id: 'VM_4',
+            children: [{
+                id: 'VM_4>User',
+                labels: [{text: "User 2"}]
+            }]
+        }],
+        edges: [{
+            id: 'VM_1>Auth->VM_3>User',
+            source: 'VM_1>Auth',
+            target: 'VM_3>User',
+        }, {
+            id: 'VM_2>Auth->VM_4>User',
+            source: 'VM_2>Auth',
+            target: 'VM_4>User',
+        }]
+    };
+  }
+});
\ No newline at end of file
diff --git a/app/styles/app.scss b/app/styles/app.scss
new file mode 100644
index 0000000..22c865c
--- /dev/null
+++ b/app/styles/app.scss
@@ -0,0 +1,29 @@
+@import "components/_architecture";
+
+body {
+    font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
+}
+g.leaf > rect {
+  stroke: #fff;
+  stroke-width: 1px;
+  opacity: .5;
+}
+
+g.compound > rect {
+  opacity: 0.1;
+}
+
+.node {
+}
+
+.link {
+  stroke: #999;
+  stroke-opacity: .6;
+  fill: none;
+}
+
+.port {
+  stroke: #000;
+  width: 1px;
+  opacity: .6;
+}
diff --git a/app/styles/components/_architecture.scss b/app/styles/components/_architecture.scss
new file mode 100644
index 0000000..e7d390a
--- /dev/null
+++ b/app/styles/components/_architecture.scss
@@ -0,0 +1,12 @@
+svg.architectureVisualisation {
+    g.compound > rect:hover {
+        stroke: white;
+        stroke-opacity: 1;
+        fill: red;
+    }
+    .link:hover {
+      stroke: #A000;
+      stroke-opacity: 1;
+      fill: black;
+    }
+}
\ No newline at end of file
diff --git a/app/templates/application.hbs b/app/templates/application.hbs
index f8bc38e..3f1cdac 100644
--- a/app/templates/application.hbs
+++ b/app/templates/application.hbs
@@ -1,3 +1,4 @@
 <h2 id="title">Welcome to Ember</h2>
+<h3>Go to {{#link-to 'architecture'}}Klay-based Architecture{{/link-to}}</h3>
 
 {{outlet}}
diff --git a/app/templates/architecture.hbs b/app/templates/architecture.hbs
new file mode 100644
index 0000000..a7234d6
--- /dev/null
+++ b/app/templates/architecture.hbs
@@ -0,0 +1,2 @@
+Visualisierung:
+{{architecture-visualisation graph=model}}
\ No newline at end of file
diff --git a/app/templates/components/architecture-visualisation.hbs b/app/templates/components/architecture-visualisation.hbs
new file mode 100644
index 0000000..0eddf3f
--- /dev/null
+++ b/app/templates/components/architecture-visualisation.hbs
@@ -0,0 +1 @@
+<!-- will only contain svg which is dynamically added -->
\ No newline at end of file
diff --git a/bower.json b/bower.json
index 67577b5..826579c 100644
--- a/bower.json
+++ b/bower.json
@@ -4,6 +4,8 @@
     "ember": "~2.4.1",
     "ember-cli-shims": "0.1.0",
     "ember-cli-test-loader": "0.2.2",
-    "ember-qunit-notifications": "0.1.0"
+    "ember-qunit-notifications": "0.1.0",
+    "d3": "^3.5.16",
+    "visionmedia-debug": "2.2"
   }
 }
diff --git a/package.json b/package.json
index c44e9b0..46367b2 100644
--- a/package.json
+++ b/package.json
@@ -21,22 +21,32 @@
   "devDependencies": {
     "broccoli-asset-rev": "^2.2.0",
     "ember-ajax": "0.7.1",
+    "ember-browserify": "^1.1.8",
     "ember-cli": "2.4.2",
     "ember-cli-app-version": "^1.0.0",
     "ember-cli-babel": "^5.1.5",
+    "ember-cli-d3": "1.1.6",
     "ember-cli-dependency-checker": "^1.2.0",
     "ember-cli-htmlbars": "^1.0.1",
     "ember-cli-htmlbars-inline-precompile": "^0.3.1",
     "ember-cli-inject-live-reload": "^1.3.1",
     "ember-cli-qunit": "^1.2.1",
     "ember-cli-release": "0.2.8",
+    "ember-cli-sass": "5.3.1",
     "ember-cli-sri": "^2.1.0",
     "ember-cli-uglify": "^1.2.0",
+    "ember-d3": "0.1.0",
     "ember-data": "^2.4.0",
+    "ember-debug-logger": "0.2.0",
     "ember-disable-proxy-controllers": "^1.0.1",
     "ember-export-application-global": "^1.0.4",
     "ember-load-initializers": "^0.5.0",
     "ember-resolver": "^2.0.3",
-    "loader.js": "^4.0.0"
+    "loader.js": "^4.0.0",
+    "lodash": "^4.11.1"
+  },
+  "dependencies": {
+    "klayjs": "^0.4.1",
+    "klayjs-d3": "^0.3.4"
   }
 }
-- 
GitLab