diff --git a/package.json b/package.json index bdcf235..6bdda98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iknow-entity-browser", - "version": "0.0.5", + "version": "0.0.6", "description": "Visualizer for iKnow entities", "main": "gulpfile.babel.js", "scripts": { diff --git a/src/static/index.html b/src/static/index.html index a41cd2b..d831898 100644 --- a/src/static/index.html +++ b/src/static/index.html @@ -36,6 +36,19 @@

Selected Nodes

+
+
+
+ +
+
Please, select a node.
+
+
+

WELCOME

+

THIS IS A GAME

+
TEST GAME!
+
+
\ No newline at end of file diff --git a/src/static/js/details/index.js b/src/static/js/details/index.js new file mode 100644 index 0000000..351fac4 --- /dev/null +++ b/src/static/js/details/index.js @@ -0,0 +1,41 @@ +import * as model from "../model"; +import { onSelectionUpdate } from "../selection"; + +let selectedNode = null; + +onSelectionUpdate((selection) => { + + d3.select("#nodeDetailsToggle").classed("disabled", selection.length !== 1); + selectedNode = selection.length === 1 ? selection[0] : null; + updateHeader(); + +}); + +function updateHeader () { + + d3.select("#nodeDetails .header .text").text( + selectedNode + ? `Node "${ selectedNode.label }" (${ selectedNode.type }) selected.` + : "Please, select one node." + ); + + if (!selectedNode) { + d3.select("#nodeDetails").classed("active", model.uiState.detailsToggled = false); + } + +} + +export function init () { + + d3.select("#nodeDetailsToggle") + .data([model.uiState]) + .on("click", function (d) { + if (!selectedNode) + return; + d.detailsToggled = !d.detailsToggled; + d3.select("#nodeDetails").classed("active", d.detailsToggled); + }); + + updateHeader(); + +} \ No newline at end of file diff --git a/src/static/js/graph/index.js b/src/static/js/graph/index.js index aea9dec..2734e45 100644 --- a/src/static/js/graph/index.js +++ b/src/static/js/graph/index.js @@ -1,5 +1,6 @@ import { updateSelectedNodes } from "../tabular"; import { getGraphData } from "../model"; +import { updateSelection } from "../selection"; export function update () { @@ -75,7 +76,7 @@ export function update () { node.classed("selected", (p) => p.selected = p.previouslySelected = false) } d3.select(this).classed("selected", d.selected = !d.selected); // (!prevSel) - updateSelectedNodes(); + updateSelection(); }); var circle = node.append("circle") @@ -116,7 +117,7 @@ export function update () { if (!d3.event.sourceEvent) return; setTimeout(() => { brush.call(brusher.move, null); - updateSelectedNodes(); + updateSelection(); }, 25); }); diff --git a/src/static/js/index.js b/src/static/js/index.js index 576e95d..106ee08 100644 --- a/src/static/js/index.js +++ b/src/static/js/index.js @@ -1,9 +1,11 @@ import { update } from "./graph"; import * as tabular from "./tabular"; +import * as details from "./details"; window.init = () => { update(); tabular.init(); + details.init(); }; \ No newline at end of file diff --git a/src/static/js/model/index.js b/src/static/js/model/index.js index d771d67..e7ed5f8 100644 --- a/src/static/js/model/index.js +++ b/src/static/js/model/index.js @@ -12,5 +12,6 @@ export function getGraphData () { } export var uiState = { - tableToggled: false + tabularToggled: false, + detailsToggled: false }; diff --git a/src/static/js/selection.js b/src/static/js/selection.js new file mode 100644 index 0000000..934f58f --- /dev/null +++ b/src/static/js/selection.js @@ -0,0 +1,28 @@ +import * as model from "./model"; + +let selection = [], + callbacks = []; + +export function updateSelection () { + + selection = model.getGraphData().nodes.filter(node => !!node.selected); + + callbacks.forEach(c => c(selection)); + +} + +/** + * The callback is invoked when selection changes. + * @param {selectionCallback} callback + */ +export function onSelectionUpdate (callback) { + + callbacks.push(callback); + +} + +/** + * This callback is invoked when selection changes. + * @callback selectionCallback + * @param {*[]} nodes - Currently selected nodes. + */ \ No newline at end of file diff --git a/src/static/js/tabular/index.js b/src/static/js/tabular/index.js index 71390fa..887823f 100644 --- a/src/static/js/tabular/index.js +++ b/src/static/js/tabular/index.js @@ -1,12 +1,13 @@ import { csv } from "./export"; import * as model from "../model"; +import { onSelectionUpdate, updateSelection } from "../selection"; var graph; -export function updateSelectedNodes () { - if (!model.uiState.tableToggled) +onSelectionUpdate((selection) => { + if (!model.uiState.tabularToggled) return; - let data = graph.nodes.filter(node => !!node.selected).sort((a, b) => + let data = selection.sort((a, b) => a.entities[0].score > b.entities[0].score ? -1 : 1 ), table = document.querySelector("#table table tbody"); @@ -20,7 +21,7 @@ export function updateSelectedNodes () { row.insertCell(3).textContent = node.entities[0].frequency; row.insertCell(4).textContent = node.entities[0].spread; } -} +}); export function init () { @@ -29,10 +30,10 @@ export function init () { d3.select("#tableToggle") .data([model.uiState]) .on("click", function (d) { - d.tableToggled = !d.tableToggled; - d3.select(this).classed("toggled", d.tableToggled); - d3.select("#table").classed("active", d.tableToggled); - updateSelectedNodes(); + d.tabularToggled = !d.tabularToggled; + d3.select(this).classed("toggled", d.tabularToggled); + d3.select("#table").classed("active", d.tabularToggled); + updateSelection(); }); d3.select("#exportCSV").on("click", () => { diff --git a/src/static/scss/const.scss b/src/static/scss/const.scss index e2888d5..fe9d402 100644 --- a/src/static/scss/const.scss +++ b/src/static/scss/const.scss @@ -1,5 +1,5 @@ $defaultTransition: all .3s ease; $defaultPadding: 10px; -$defaultShadow: 0 1px 2px gray; +$defaultShadow: 0 0 2px gray; $colorA: #02ad8b; \ No newline at end of file diff --git a/src/static/scss/icons-all.scss b/src/static/scss/icons-all.scss index f4c328c..c66b04b 100644 --- a/src/static/scss/icons-all.scss +++ b/src/static/scss/icons-all.scss @@ -11,6 +11,10 @@ color: $colorA; &:hover { color: black; } } + &.disabled { + color: gray; + &:hover { color: gray; } + } } .uiIconGroup { diff --git a/src/static/scss/index.scss b/src/static/scss/index.scss index bb4a8b1..fee36b3 100644 --- a/src/static/scss/index.scss +++ b/src/static/scss/index.scss @@ -20,4 +20,5 @@ html, body { @import "icons-all"; @import "icons"; @import "graph"; -@import "table"; \ No newline at end of file +@import "tabular"; +@import "nodeDetails"; \ No newline at end of file diff --git a/src/static/scss/nodeDetails.scss b/src/static/scss/nodeDetails.scss new file mode 100644 index 0000000..57b29d9 --- /dev/null +++ b/src/static/scss/nodeDetails.scss @@ -0,0 +1,73 @@ +@import "mixins"; +@import "const"; + +#nodeDetails { + + position: absolute; + box-sizing: border-box; + bottom: 0; + left: 0; + width: 100%; + max-height: 100%; + background: white; + overflow: visible; + box-shadow: $defaultShadow; + @include transition($defaultTransition); + @include transform(translate(0,100%)); + &.active { + @include transform(translate(0,0)); + } + + > .header { + + position: absolute; + left: 0; + right: 0; + top: -28px; + height: 20px; + max-width: 80%; + width: 350px; + margin: 0 auto; + border-radius: 5px 5px 0 0; + background: white; + padding: 4px; + font-size: 16px; + box-shadow: 0 0 2px gray; + + > .icons { + display: block; + float: left; + width: 30px; + font-size: 24px; + color: gray; + } + + > .text { + margin-left: 30px; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + } + + > .content { + + overflow: auto; + max-height: 100%; + padding: $defaultPadding; + box-sizing: border-box; + + > .wrapper { + + position: relative; + + table { + width: 100%; + } + + } + + } + +} \ No newline at end of file diff --git a/src/static/scss/table.scss b/src/static/scss/tabular.scss similarity index 100% rename from src/static/scss/table.scss rename to src/static/scss/tabular.scss