diff --git a/package.json b/package.json index 6bdda98..a7ac777 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iknow-entity-browser", - "version": "0.0.6", + "version": "0.0.8", "description": "Visualizer for iKnow entities", "main": "gulpfile.babel.js", "scripts": { diff --git a/src/static/fonts/iknowentitybrowsericons.eot b/src/static/fonts/iknowentitybrowsericons.eot index b11aa1d..0eeee94 100644 Binary files a/src/static/fonts/iknowentitybrowsericons.eot and b/src/static/fonts/iknowentitybrowsericons.eot differ diff --git a/src/static/fonts/iknowentitybrowsericons.svg b/src/static/fonts/iknowentitybrowsericons.svg index 0a22186..d8ee8b4 100644 --- a/src/static/fonts/iknowentitybrowsericons.svg +++ b/src/static/fonts/iknowentitybrowsericons.svg @@ -22,4 +22,5 @@ + diff --git a/src/static/fonts/iknowentitybrowsericons.ttf b/src/static/fonts/iknowentitybrowsericons.ttf index 6269145..9c2d00d 100644 Binary files a/src/static/fonts/iknowentitybrowsericons.ttf and b/src/static/fonts/iknowentitybrowsericons.ttf differ diff --git a/src/static/fonts/iknowentitybrowsericons.woff b/src/static/fonts/iknowentitybrowsericons.woff index 1a4c453..cc22276 100644 Binary files a/src/static/fonts/iknowentitybrowsericons.woff and b/src/static/fonts/iknowentitybrowsericons.woff differ diff --git a/src/static/index.html b/src/static/index.html index d831898..b566b57 100644 --- a/src/static/index.html +++ b/src/static/index.html @@ -4,6 +4,7 @@ iKnow Entity Browser + @@ -13,8 +14,11 @@
+
+ +
-
+

Selected Nodes

@@ -44,11 +48,29 @@

Selected Nodes

Please, select a node.
-

WELCOME

-

THIS IS A GAME

-
TEST GAME!
+ + + + + + + + + + + + + + +
EntityIDFrequencyScoreSpread
+
+ +
\ No newline at end of file diff --git a/src/static/js/details/index.js b/src/static/js/details/index.js index 351fac4..d932cce 100644 --- a/src/static/js/details/index.js +++ b/src/static/js/details/index.js @@ -3,24 +3,58 @@ import { onSelectionUpdate } from "../selection"; let selectedNode = null; -onSelectionUpdate((selection) => { +onSelectionUpdate((selection, lastNodeSelected) => { - d3.select("#nodeDetailsToggle").classed("disabled", selection.length !== 1); - selectedNode = selection.length === 1 ? selection[0] : null; + d3.select("#nodeDetailsToggle").classed("disabled", selection.length === 0); + selectedNode = lastNodeSelected + ? lastNodeSelected + : selection.length > 0 + ? selection[selection.length - 1] + : null; updateHeader(); }); function updateHeader () { + let typeName = ((selectedNode || {}).type || "Node".toString()); + d3.select("#nodeDetails .header .text").text( selectedNode - ? `Node "${ selectedNode.label }" (${ selectedNode.type }) selected.` + ? `${ typeName[0].toUpperCase() + typeName.slice(1) } "${ + selectedNode.label }" selected.` : "Please, select one node." ); if (!selectedNode) { d3.select("#nodeDetails").classed("active", model.uiState.detailsToggled = false); + } else { + updateData(); + } + +} + +function updateData () { + + if (!selectedNode) + return; + + let tableElement = document.querySelector("#nodeDetails-entitiesTable tbody"); + // labelElement = document.querySelector("#nodeDetails-label"), + // idElement = document.querySelector("#nodeDetails-id"); + + // idElement.textContent = selectedNode.id; + // labelElement.textContent = selectedNode.label; + + tableElement.textContent = ""; + for (let i = 0; i < (selectedNode.entities || []).length; i++) { + let row = tableElement.insertRow(i), + entity = selectedNode.entities[i]; + row.insertCell(0).textContent = entity.value; + row.insertCell(1).textContent = entity.id; + row.insertCell(2).textContent = entity.frequency; + row.insertCell(3).textContent = entity.score; + row.insertCell(4).textContent = entity.spread; } } diff --git a/src/static/js/graph/index.js b/src/static/js/graph/index.js index 2734e45..358bcc6 100644 --- a/src/static/js/graph/index.js +++ b/src/static/js/graph/index.js @@ -1,6 +1,6 @@ import { updateSelectedNodes } from "../tabular"; import { getGraphData } from "../model"; -import { updateSelection } from "../selection"; +import { updateSelection, setLastSelectedNode } from "../selection"; export function update () { @@ -76,6 +76,7 @@ export function update () { node.classed("selected", (p) => p.selected = p.previouslySelected = false) } d3.select(this).classed("selected", d.selected = !d.selected); // (!prevSel) + setLastSelectedNode(d.selected ? d : null); updateSelection(); }); @@ -108,9 +109,10 @@ export function update () { if (!extent) return; node.classed("selected", (d) => { - return d.selected = d.previouslySelected ^ - (extent[0][0] <= d.x && d.x < extent[1][0] + let selected = (extent[0][0] <= d.x && d.x < extent[1][0] && extent[0][1] <= d.y && d.y < extent[1][1]); + if (selected) setLastSelectedNode(d); + return d.selected = d.previouslySelected ^ selected; }); }) .on("end.brush", () => { diff --git a/src/static/js/index.js b/src/static/js/index.js index 106ee08..790ff61 100644 --- a/src/static/js/index.js +++ b/src/static/js/index.js @@ -1,11 +1,13 @@ import { update } from "./graph"; import * as tabular from "./tabular"; import * as details from "./details"; +import * as settings from "./settings"; window.init = () => { update(); tabular.init(); details.init(); + settings.init(); }; \ No newline at end of file diff --git a/src/static/js/model/index.js b/src/static/js/model/index.js index e7ed5f8..e7bda0c 100644 --- a/src/static/js/model/index.js +++ b/src/static/js/model/index.js @@ -13,5 +13,6 @@ export function getGraphData () { export var uiState = { tabularToggled: false, - detailsToggled: false + detailsToggled: false, + settingsToggled: false }; diff --git a/src/static/js/selection.js b/src/static/js/selection.js index 934f58f..9956db6 100644 --- a/src/static/js/selection.js +++ b/src/static/js/selection.js @@ -1,13 +1,30 @@ import * as model from "./model"; let selection = [], - callbacks = []; + callbacks = [], + lastSelectedNode = null; export function updateSelection () { selection = model.getGraphData().nodes.filter(node => !!node.selected); - callbacks.forEach(c => c(selection)); + if (!selection.length) lastSelectedNode = null; + if (lastSelectedNode && !lastSelectedNode.selected) { + lastSelectedNode = selection[selection.length - 1]; + } + + callbacks.forEach( + c => c(selection, lastSelectedNode || selection[selection.length - 1] || null) + ); + +} + +export function setLastSelectedNode (node) { + + if (node && typeof node.id !== "undefined") + lastSelectedNode = node; + else + lastSelectedNode = null; } @@ -25,4 +42,5 @@ export function onSelectionUpdate (callback) { * This callback is invoked when selection changes. * @callback selectionCallback * @param {*[]} nodes - Currently selected nodes. + * @param {*} lastNodeSelected - The last node selected by user. */ \ No newline at end of file diff --git a/src/static/js/settings/index.js b/src/static/js/settings/index.js new file mode 100644 index 0000000..d31e1af --- /dev/null +++ b/src/static/js/settings/index.js @@ -0,0 +1,19 @@ +import * as model from "../model"; + +function toggleSettings (uiStateModel) { + uiStateModel.settingsToggled = !uiStateModel.settingsToggled; + d3.select("#settings").classed("active", uiStateModel.settingsToggled); + d3.select("#windows").classed("offScreen", uiStateModel.settingsToggled); +} + +export function init () { + + d3.select("#settingsToggle") + .data([model.uiState]) + .on("click", toggleSettings); + + d3.select("#closeSettingsToggle") + .data([model.uiState]) + .on("click", toggleSettings); + +} \ No newline at end of file diff --git a/src/static/scss/const.scss b/src/static/scss/const.scss index fe9d402..28a869f 100644 --- a/src/static/scss/const.scss +++ b/src/static/scss/const.scss @@ -2,4 +2,6 @@ $defaultTransition: all .3s ease; $defaultPadding: 10px; $defaultShadow: 0 0 2px gray; -$colorA: #02ad8b; \ No newline at end of file +$colorA: #02ad8b; + +$zIndexInterface: 100; \ No newline at end of file diff --git a/src/static/scss/icons.scss b/src/static/scss/icons.scss index 84ca814..fb5e111 100644 --- a/src/static/scss/icons.scss +++ b/src/static/scss/icons.scss @@ -85,3 +85,6 @@ .icon-android-options:before { content: "\6e"; } +.icon-close:before { + content: "\70"; +} diff --git a/src/static/scss/index.scss b/src/static/scss/index.scss index fee36b3..88c69a5 100644 --- a/src/static/scss/index.scss +++ b/src/static/scss/index.scss @@ -1,4 +1,5 @@ @import "mixins"; +@import "const"; html, body { position: relative; @@ -10,10 +11,19 @@ html, body { } #windows { + position: relative; overflow: hidden; + top: 0; width: 100%; height: 100%; + @include transition($defaultTransition); + + &.offScreen { + top: 100%; + overflow: visible; + } + } @import "basic"; @@ -21,4 +31,5 @@ html, body { @import "icons"; @import "graph"; @import "tabular"; -@import "nodeDetails"; \ No newline at end of file +@import "nodeDetails"; +@import "settings"; \ No newline at end of file diff --git a/src/static/scss/nodeDetails.scss b/src/static/scss/nodeDetails.scss index 57b29d9..16f5997 100644 --- a/src/static/scss/nodeDetails.scss +++ b/src/static/scss/nodeDetails.scss @@ -18,28 +18,32 @@ @include transform(translate(0,0)); } + $headerPadding: 4px; + $headerFontSize: 16px; + > .header { position: absolute; left: 0; right: 0; top: -28px; - height: 20px; + height: 20px + 2 * $headerPadding; max-width: 80%; width: 350px; margin: 0 auto; border-radius: 5px 5px 0 0; background: white; - padding: 4px; - font-size: 16px; + padding: $headerPadding; + font-size: $headerFontSize; box-shadow: 0 0 2px gray; + box-sizing: border-box; + @include transition($defaultTransition); > .icons { display: block; float: left; width: 30px; font-size: 24px; - color: gray; } > .text { @@ -70,4 +74,20 @@ } + &.active { + + > .header { + top: 0; + border-radius: 0 0 5px 5px; + max-width: 100%; + width: 100%; + box-shadow: none; + } + + > .content { + padding-top: $defaultPadding + $headerFontSize + $headerPadding; + } + + } + } \ No newline at end of file diff --git a/src/static/scss/settings.scss b/src/static/scss/settings.scss new file mode 100644 index 0000000..d377909 --- /dev/null +++ b/src/static/scss/settings.scss @@ -0,0 +1,26 @@ +@import "const"; +@import "mixins"; + +#settings { + + position: absolute; + top: -100%; + width: 100%; + height: 100%; + box-sizing: border-box; + background: white; + padding: $defaultPadding; + + &.active { + + } + +} + +#closeSettingsToggle { + + position: absolute; + right: 7px; + top: 7px; + +} \ No newline at end of file diff --git a/src/static/scss/tabular.scss b/src/static/scss/tabular.scss index 58a8058..c6325df 100644 --- a/src/static/scss/tabular.scss +++ b/src/static/scss/tabular.scss @@ -22,9 +22,15 @@ overflow: auto; height: 100%; - padding: $defaultPadding; + padding: 0 $defaultPadding $defaultPadding $defaultPadding; box-sizing: border-box; + > .controls { + position: relative; + padding-left: 30px; + top: 5px; + } + > .wrapper { position: relative; @@ -40,7 +46,28 @@ } #tableToggle { + position: absolute; left: -35px; top: 6px; + z-index: $zIndexInterface; + + &.toggled { + left: 7px; + } + +} + +#rightTopIcons { + + position: absolute; + left: -35px; + top: 36px; + z-index: $zIndexInterface; + @include transition($defaultTransition); + +} + +#table.active #rightTopIcons { + top: 6px; } \ No newline at end of file