From 1c35307281d8a1276a1adfbdd762a7a90b80be16 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Wed, 29 Nov 2017 01:14:15 +0100 Subject: [PATCH 01/20] /concepts end point changes (manual SQL query); New reducer for concept graph (isolates connex components and parses them with d3-hierarchy); d3 visualisation --- app/package-lock.json | 5 + app/package.json | 1 + .../components/d3Blocks/conceptHierarchy.less | 35 +++ .../components/d3Blocks/conceptHierarchy.tsx | 294 ++++++++++++++++++ .../application/components/utils/NavPanel.tsx | 9 +- app/src/application/containers/app.tsx | 1 - app/src/application/containers/test.tsx | 6 +- app/src/application/reducers.tsx | 11 +- app/src/application/types.tsx | 4 +- package-lock.json | 43 +-- package.json | 1 - server/src/backends/concepts/backend.ts | 26 +- 12 files changed, 359 insertions(+), 77 deletions(-) create mode 100644 app/src/application/components/d3Blocks/conceptHierarchy.less create mode 100644 app/src/application/components/d3Blocks/conceptHierarchy.tsx diff --git a/app/package-lock.json b/app/package-lock.json index 66185e7..1171afc 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -375,6 +375,11 @@ "resolved": "https://registry.npmjs.org/@types/tether/-/tether-1.4.2.tgz", "integrity": "sha512-PDiVAkmxFc3HGUQAguc1++Rbpi7jhp6Gtd+So2JRRPKHcsXO4a6UB+eUFx+XO++F8wJH3b2phnKCEYg8/NkDow==" }, + "@types/webpack-env": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.13.2.tgz", + "integrity": "sha512-pjbzi3A1Y4iLpNdNZNG4loIZKtYOnpCQY82bnsHi9lQXl4f3ul0TDsd1fUd10jbgCXR5bwaP4Ffy1BDLuEZpaQ==" + }, "accepts": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", diff --git a/app/package.json b/app/package.json index 39c30c9..5302270 100644 --- a/app/package.json +++ b/app/package.json @@ -22,6 +22,7 @@ "@types/react-router-dom": "^4.0.7", "@types/redux": "^3.6.0", "@types/redux-logger": "^3.0.0", + "@types/webpack-env": "^1.13.2", "awesome-typescript-loader": "^3.2.2", "chart.js": "^2.6.0", "classnames": "^2.2.5", diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.less b/app/src/application/components/d3Blocks/conceptHierarchy.less new file mode 100644 index 0000000..fead1b4 --- /dev/null +++ b/app/src/application/components/d3Blocks/conceptHierarchy.less @@ -0,0 +1,35 @@ +@import '~@blueprintjs/core/dist/variables.less'; + +#container { + vertical-align: baseline; +} + +rect { + stroke-width: 0 +} + +.hierarchy-rect { + fill: @gray4; + width: 45px; + height: 25px; +} + +.hierarchy-rect:hover { + fill: @gray2; +} + +.ancestor-rect { + fill: @blue3; +} + +.ancestor-rect:hover { + fill: @blue1; +} + +.selected-rect { + fill: @green3; +} + +.selected-rect:hover { + fill: @green1; +} diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx new file mode 100644 index 0000000..4718ae9 --- /dev/null +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -0,0 +1,294 @@ +import * as _ from 'lodash' +import * as d3 from 'd3' +import * as React from 'react' +import * as ReactDOM from 'react-dom' + +import './conceptHierarchy.less' + +import * as actions from '../../actions' +import * as interceptClick from './utils/interceptClick' +import { + action, + conceptLinksAttribute, + extendedConceptNodeAttribute, +} from './../../types' + +interface d3GraphNode extends d3.SimulationNodeDatum, extendedConceptNodeAttribute {} + +interface d3GraphLink extends d3.SimulationNodeDatum, conceptLinksAttribute {} + +interface Props { + version: number, + nodes: extendedConceptNodeAttribute[], + graph: any, + dimensions: { + width: number, + height: number, + } + searchedConcept: string, + dispatch: any +} + +interface State { + selected: string, +} + +export function ConceptHierarchyReducer(nodes: any): any { + let nodeDict: any = {} + let graph: any = [] + + function searchDict(slug: any): any { + if (slug in nodeDict) { + let r = searchDict(nodeDict[slug]) + nodeDict[slug] = r + return r + } else { + return slug + } + } + + let cc = 0 + nodes.forEach((node: any) => { + if (node.parent) { + nodeDict[node.slug] = searchDict(node.parent) + } else { + nodeDict[node.slug] = cc + cc++ + } + }) + + function recursiveCC(slug: any): any { + if (typeof nodeDict[slug] === 'number') { + return nodeDict[slug] + } else { + let r = recursiveCC(nodeDict[slug]) + nodeDict[slug] = r + return r + } + } + + for (let slug in nodeDict) { + nodeDict[slug] = recursiveCC(slug) + } + + nodes = _.map(nodes, (node: any) => { + return { + ...node, + connexComponent: nodeDict[node.slug], + } + }) + + for (let i = 0; i < cc; i++) { + let n = _.filter(nodes, (node: any) => node.connexComponent == i) + let h = d3.stratify() + .id((d: any) => d.slug) + .parentId((d: any) => d.parent) + (n) + graph.push(h) + } + + return { + nodes, + graph, + } +} + +export class ConceptHierarchy extends React.Component { + // D3 + + // All these parameters are simple parameters; one doesn't use + // React props only since they imply abiding by the React lifecycle + // whereas we want to have our simulation ran by d3. + width: number + height: number + rectDimensions = { + w: 45, + h: 25, + m: { + t: 15, + r: 5, + b: 5, + l: 5, + } + } + + interceptClickHandler: any + rebind = interceptClick.rebind + interceptClick = interceptClick.interceptClick + + domSvg: any + domContainer: any + domRects: any + domLines: any + + hierarchy: any + nodes: any + graph: any + fnodes: any + + selectedRoot: any + selectedNode: any + + initAttributes() { + this.interceptClickHandler = this.interceptClick() + } + + updateAttributes() { + let {width, height} = this.props.dimensions + this.width = width + this.height = height + + this.nodes = this.props.nodes + this.graph = this.props.graph + + this.selectedRoot = null + this.selectedNode = null + this.fnodes = [] + + if (this.graph.length > 0) { + this.selectedRoot = this.graph[0] + this.selectedNode = this.graph[0] + this.selectedRoot.each((node: any) => { + this.fnodes.push(node) + }) + } + + this.domContainer + .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') + } + + selectNode(d: any) { + + } + + renderNodes() { + this.domRects = this.domContainer.selectAll('rect') + .data(this.fnodes) + + this.domRects.exit().remove() + + let rectsPerLevel = [1] + let ancestors = this.selectedNode ? this.selectedNode.ancestors() : [] + let allowedNodes = _.reduce(_.map(ancestors.reverse(), (n: any) => n.children ? n.children : []), + (acc: any, list: any) => { + rectsPerLevel.push(list.length) + return acc.concat(list) + }, + [this.selectedRoot] + ) + + let depthIncrement: any = [] + for (let i = 0; i < rectsPerLevel.length; i++) { + depthIncrement.push(0) + } + + this.domRects + .enter() + .append('rect') + .merge(this.domRects) + .attr('transform', (d: any, index: number) => { + let xFactor = (this.rectDimensions.w + this.rectDimensions.m.r + this.rectDimensions.m.l) + let yFactor = (this.rectDimensions.h + this.rectDimensions.m.t + this.rectDimensions.m.b) + let xShift = 0 + let yShift = yFactor * d.depth + + if (d.depth < rectsPerLevel.length && allowedNodes.indexOf(d) > -1) { + xShift = xFactor * (depthIncrement[d.depth] - rectsPerLevel[d.depth] / 2) + depthIncrement[d.depth]++ + } + + return 'translate(' + xShift + ',' + yShift +')' + }) + .attr('display', (d: any) => { + let shouldDisplay = allowedNodes.indexOf(d) > -1 + + return shouldDisplay ? null : 'none' + }) + .attr('class', (node: any) => { + return 'hierarchy-rect' + + ((node.id == this.selectedNode.id) ? ' selected-rect' : '') + + ((ancestors.indexOf(node) > -1) ? ' ancestor-rect' : '') + }) + .call(this.interceptClickHandler + .on('customClick', this.customClick.bind(this)) + .on('customDoubleClick', this.customDoubleClick.bind(this)) + ) + } + + customClick(node: any) { + this.selectedNode = node + this.renderNodes() + } + + customDoubleClick(d: any) { + console.log('doubleclick') + } + + // Unite all previous rendering functions in just one function. + renderD3DomElements() { + this.renderNodes() + } + + // REACT LIFECYCLE + + constructor(props: Props) { + super(props) + this.state = {selected: null} + + // this.selectNode = this.selectNode.bind(this) + } + + // This function is called right after the first render() of our component. + // We'll define here our domContainer, the forces to use in our d3 simulation + // as well as other attributes which will be useful to d3 and which come from + // React props (remember we want to dissociate d3 calculations from React props). + // After that, we call our sent of simulation and rendering functions. + componentDidMount() { + this.domSvg = d3.select(this.refs.container as any) + this.domContainer = this.domSvg.select('#container') + + this.width = this.props.dimensions.width + this.height = this.props.dimensions.height + + this.initAttributes() + this.updateAttributes() + this.renderD3DomElements() + } + + // This function is called by React every time the React state or props + // of this component change. In general, we don't want our component to rerender: + // indeed, all DOM elements already exist and their interactions with the DOM are + // handled by d3. + // The only case in which one should indeed rerender this component is when + // new data comes in. This we check keeping track of the version of our data + // (the version comes as a prop, given by the parent component). + shouldComponentUpdate(nextProps: Props, nextState: State) { + if (nextProps.version === this.props.version) { + return false + } + + return true + } + + // This function is called right after the component actually rendered again. Therefore, + // at this stage, only the container is created. One thus updates the simulation + // and re-renders all possible DOM components which will populate the container. + componentDidUpdate() { + this.updateAttributes() + this.renderD3DomElements() + } + + render() { + let {width, height} = this.props.dimensions + + return ( + + + + ) + } +} diff --git a/app/src/application/components/utils/NavPanel.tsx b/app/src/application/components/utils/NavPanel.tsx index e0e435d..f414ddd 100644 --- a/app/src/application/components/utils/NavPanel.tsx +++ b/app/src/application/components/utils/NavPanel.tsx @@ -7,6 +7,7 @@ import { } from '@blueprintjs/core' import {ConceptGraph} from '../d3Blocks/conceptGraph' +import {ConceptHierarchy} from '../d3Blocks/conceptHierarchy' import {ConceptNav} from '../d3Blocks/conceptNav' import * as actions from '../../actions' import { @@ -22,7 +23,6 @@ import { interface Props { nodes: any, - links: any, graph: any, dispatch: any, toggled: boolean, @@ -143,7 +143,7 @@ export class NavPanel extends React.Component { } render() { - const {nodes, links, toggled, selectedConceptNode, displayedSlugs} = this.props + const {nodes, graph, toggled, selectedConceptNode, displayedSlugs} = this.props const {searchedConcept} = this.state const length = searchedConcept ? searchedConcept.length : 0 const toggleButtonTextIcon = (toggled) ? @@ -200,12 +200,11 @@ export class NavPanel extends React.Component {
- { > { // When page is done loading, fetch concept graph from backend componentDidMount() { - // this.props.dispatch(actions.fetchConceptGraph('concepts/', 'test')) - this.props.dispatch(actions.testFetch(['PLF2017-Nomenclature_MPA.csv'], 'test', 'http://localhost:31338/')) + this.props.dispatch(actions.fetchConceptGraph('concepts/', 'test')) + // this.props.dispatch(actions.testFetch(['PLF2017-Nomenclature_MPA.csv'], 'test', 'http://localhost:31338/')) this.props.dispatch(actions.toggleNavPanel()) } @@ -87,7 +87,6 @@ export class Test extends React.Component { > { />
- diff --git a/app/src/application/reducers.tsx b/app/src/application/reducers.tsx index 6d00b1d..f730d9b 100644 --- a/app/src/application/reducers.tsx +++ b/app/src/application/reducers.tsx @@ -12,6 +12,7 @@ import { import {TimeseriesValuesReducer} from './components/modules/timeseries_chart' import {LabelizedValuesReducer} from './components/modules/labelized_chart' import {navPanelReducer} from './components/utils/navPanel' +import {ConceptHierarchyReducer} from './components/d3Blocks/conceptHierarchy' const initialCp1State: containerState = { containerId: 'cp1', @@ -32,8 +33,6 @@ const initialTestState: containerState = { const initialAppState: appState = { conceptGraph: { nodes: [], - links: [], - suggestedLinks: [], graph: {}, selectedConceptNode: null, displayedSlugs: [], @@ -43,7 +42,7 @@ const initialAppState: appState = { cp1: initialCp1State, test: initialTestState, }, - toggled: true, + toggled: false, } export function reducer(state = initialAppState, action: action): appState { @@ -179,15 +178,13 @@ export function reducer(state = initialAppState, action: action): appState { } case 'FETCH_CONCEPT_GRAPH_SUCCESS': - let {nodes, links, suggestedLinks, graph} = navPanelReducer(action) + let {nodes, graph} = ConceptHierarchyReducer(action.value.nodes) return { ...state, conceptGraph: { ...state.conceptGraph, - nodes, - links, - suggestedLinks, + nodes: nodes, graph, }, containers: { diff --git a/app/src/application/types.tsx b/app/src/application/types.tsx index 3c207e0..77f40db 100644 --- a/app/src/application/types.tsx +++ b/app/src/application/types.tsx @@ -37,9 +37,7 @@ export interface appState { }, conceptGraph: { nodes: extendedConceptNodeAttribute[], - links: conceptLinksAttribute[], - suggestedLinks: conceptLinksAttribute[], - graph: conceptGraph, + graph?: any, selectedConceptNode: any, displayedSlugs: string[], } diff --git a/package-lock.json b/package-lock.json index defee87..f07ba4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -146,11 +146,6 @@ "@types/mime": "2.0.0" } }, - "@types/source-map": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@types/source-map/-/source-map-0.5.2.tgz", - "integrity": "sha512-++w4WmMbk3dS3UeHGzAG+xJOSz5Xqtjys/TBkqG3qp3SeWE7Wwezqe5eB7B51cxUyh4PW7bwVotpsLdBK0D8cw==" - }, "@types/superagent": { "version": "3.5.6", "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-3.5.6.tgz", @@ -167,19 +162,6 @@ "@types/superagent": "3.5.6" } }, - "@types/tapable": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-0.2.4.tgz", - "integrity": "sha512-pclMAvhPnXJcJu1ZZ8bQthuUcdDWzDuxDdbSf6l1U6s4fP6EBiZpPsOZYqFOrbqDV97sXGFSsb6AUpiLfv4xIA==" - }, - "@types/uglify-js": { - "version": "2.6.29", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-2.6.29.tgz", - "integrity": "sha512-BdFLCZW0GTl31AbqXSak8ss/MqEZ3DN2MH9rkAyGoTuzK7ifGUlX+u0nfbWeTsa7IPcZhtn8BlpYBXSV+vqGhQ==", - "requires": { - "@types/source-map": "0.5.2" - } - }, "@types/undertaker": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.1.2.tgz", @@ -216,21 +198,6 @@ "@types/vinyl": "2.0.1" } }, - "@types/webpack": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-3.8.0.tgz", - "integrity": "sha512-2DxSLuqDQwEPpPgzeCVrdD5k8ENK095d0RF1r5cDPznV7Vd99luAVNFHZVt3fnUlRr0dsX0f6efak9ETF2QN3g==", - "requires": { - "@types/node": "8.0.50", - "@types/tapable": "0.2.4", - "@types/uglify-js": "2.6.29" - } - }, - "@types/webpack-env": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.13.2.tgz", - "integrity": "sha512-pjbzi3A1Y4iLpNdNZNG4loIZKtYOnpCQY82bnsHi9lQXl4f3ul0TDsd1fUd10jbgCXR5bwaP4Ffy1BDLuEZpaQ==" - }, "accepts": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", @@ -4113,13 +4080,13 @@ } }, "sequelize-auto": { - "version": "git+https://github.com/sequelize/sequelize-auto.git#fd2add3b8b5020c7a5fdcb15279cbb1302771b95", + "version": "git+https://github.com/sequelize/sequelize-auto.git#40dfce6352eefe7fa8dec1c71dd5127c37ac0a52", "requires": { "async": "2.5.0", "eslint": "4.8.0", "graceful-fs-extra": "2.0.0", "mkdirp": "0.5.1", - "sequelize": "3.30.4", + "sequelize": "3.31.0", "yargs": "8.0.2" }, "dependencies": { @@ -4144,9 +4111,9 @@ "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" }, "sequelize": { - "version": "3.30.4", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-3.30.4.tgz", - "integrity": "sha1-vaLfHjGFSwmeQUmhEen8Clyh0aQ=", + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-3.31.0.tgz", + "integrity": "sha1-1nHW4Z+Uui+mvxJqU9yDcWaA1pI=", "requires": { "bluebird": "3.5.1", "depd": "1.1.1", diff --git a/package.json b/package.json index 6362983..507a7e1 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "@types/mocha": "^2.2.43", "@types/sequelize": "^4.0.69", "@types/supertest": "^2.0.3", - "@types/webpack-env": "^1.13.2", "body-parser": "^1.17.2", "chai": "^4.1.2", "del": "^3.0.0", diff --git a/server/src/backends/concepts/backend.ts b/server/src/backends/concepts/backend.ts index 1790d91..c425538 100644 --- a/server/src/backends/concepts/backend.ts +++ b/server/src/backends/concepts/backend.ts @@ -9,6 +9,7 @@ import { suggestion_valuesAttribute, } from '../../../../models/db' import { + sequelize, ConceptNodes, ConceptLinks, ConceptSuggestedLinks, @@ -32,31 +33,20 @@ export class ConceptBackend { // Create public Router this.router = Router({mergeParams: true}); - // Init all end points of the Router this.router.route('/') .get(async (request: Request, response: Response) => { // Get flat results (Sequelize normally returns complex Instance objects // which are later parsed by express when calling response.json() - const nodes = await ConceptNodes.findAll({ - ...options, - }); - const links = await ConceptLinks.findAll({ - ...options, - }); - const suggestedLinks = await ConceptSuggestedLinks.findAll({ - ...options, - }); - - // Promise.all() turns an array of Promises into actual values. - // const enrichedConcepts = await Promise.all( - // // TODO: En fait je ne comprends même pas pourquoi il faut bind ici :D - // concepts.map(this.enrichConcept.bind(this)) - // ) + const nodes = await sequelize + .query( + 'SELECT n.*, l.slug_to AS parent FROM concept_nodes n LEFT JOIN concept_links l ON n.slug = l.slug_from', + { + type: sequelize.QueryTypes.SELECT + } + ) response.json({ nodes, - links, - suggestedLinks, }); }) From 872ca3248afb13edff7f516212cbd2548103b2fa Mon Sep 17 00:00:00 2001 From: alexisthual Date: Wed, 29 Nov 2017 01:40:41 +0100 Subject: [PATCH 02/20] Parents alignment in d3 visualisation --- .../components/d3Blocks/conceptHierarchy.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 4718ae9..362facc 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -168,9 +168,18 @@ export class ConceptHierarchy extends React.Component { this.domRects.exit().remove() let rectsPerLevel = [1] + let indexParentPerLevel: any = [0] let ancestors = this.selectedNode ? this.selectedNode.ancestors() : [] + let allowedNodes = _.reduce(_.map(ancestors.reverse(), (n: any) => n.children ? n.children : []), (acc: any, list: any) => { + if (list.length > 0 && list[0].depth < ancestors.length) { + list.forEach((node: any, index: number) => { + if (node == ancestors[node.depth]) { + indexParentPerLevel.push(index) + } + }) + } rectsPerLevel.push(list.length) return acc.concat(list) }, @@ -193,7 +202,10 @@ export class ConceptHierarchy extends React.Component { let yShift = yFactor * d.depth if (d.depth < rectsPerLevel.length && allowedNodes.indexOf(d) > -1) { - xShift = xFactor * (depthIncrement[d.depth] - rectsPerLevel[d.depth] / 2) + xShift = xFactor * (depthIncrement[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) + if (d.depth < indexParentPerLevel.length) { + xShift -= xFactor * (indexParentPerLevel[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) + } depthIncrement[d.depth]++ } From 7cbe50b4fd294f381b923e6e87a859aba8e975a4 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Wed, 29 Nov 2017 02:21:04 +0100 Subject: [PATCH 03/20] Add dotted lines --- .../components/d3Blocks/conceptHierarchy.less | 6 +++ .../components/d3Blocks/conceptHierarchy.tsx | 42 ++++++++++++++----- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.less b/app/src/application/components/d3Blocks/conceptHierarchy.less index fead1b4..a7c6ec2 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.less +++ b/app/src/application/components/d3Blocks/conceptHierarchy.less @@ -33,3 +33,9 @@ rect { .selected-rect:hover { fill: @green1; } + +.dot-line { + stroke: @gray3; + stroke-width: 2; + stroke-dasharray: 3, 5; +} diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 362facc..9624cdb 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -105,9 +105,9 @@ export class ConceptHierarchy extends React.Component { w: 45, h: 25, m: { - t: 15, + t: 10, r: 5, - b: 5, + b: 10, l: 5, } } @@ -162,11 +162,6 @@ export class ConceptHierarchy extends React.Component { } renderNodes() { - this.domRects = this.domContainer.selectAll('rect') - .data(this.fnodes) - - this.domRects.exit().remove() - let rectsPerLevel = [1] let indexParentPerLevel: any = [0] let ancestors = this.selectedNode ? this.selectedNode.ancestors() : [] @@ -191,18 +186,24 @@ export class ConceptHierarchy extends React.Component { depthIncrement.push(0) } + let xFactor = (this.rectDimensions.w + this.rectDimensions.m.r + this.rectDimensions.m.l) + let yFactor = (this.rectDimensions.h + this.rectDimensions.m.t + this.rectDimensions.m.b) + + this.domRects = this.domContainer.selectAll('rect') + .data(this.fnodes) + + this.domRects.exit().remove() + this.domRects .enter() .append('rect') .merge(this.domRects) .attr('transform', (d: any, index: number) => { - let xFactor = (this.rectDimensions.w + this.rectDimensions.m.r + this.rectDimensions.m.l) - let yFactor = (this.rectDimensions.h + this.rectDimensions.m.t + this.rectDimensions.m.b) let xShift = 0 let yShift = yFactor * d.depth if (d.depth < rectsPerLevel.length && allowedNodes.indexOf(d) > -1) { - xShift = xFactor * (depthIncrement[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) + xShift = xFactor * (depthIncrement[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) - this.rectDimensions.w / 2 if (d.depth < indexParentPerLevel.length) { xShift -= xFactor * (indexParentPerLevel[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) } @@ -225,6 +226,27 @@ export class ConceptHierarchy extends React.Component { .on('customClick', this.customClick.bind(this)) .on('customDoubleClick', this.customDoubleClick.bind(this)) ) + + + let dataLines = rectsPerLevel + dataLines.shift() + + this.domLines = this.domContainer.selectAll('line') + .data(dataLines) + + this.domLines.exit().remove() + + this.domLines + .enter() + .append('line') + .merge(this.domLines) + .attr('class', 'dot-line') + .attr('x1', (d: any, index: number) => { + return (- ((d-1) * this.rectDimensions.w + (d-2) * (this.rectDimensions.m.r + this.rectDimensions.m.l)) / 2) + }) + .attr('x2', (d: any) => ((d-1) * this.rectDimensions.w + (d-2) * (this.rectDimensions.m.r + this.rectDimensions.m.l)) / 2) + .attr('y1', (d: any, index: any) => (index + 1) * (this.rectDimensions.h + this.rectDimensions.m.b + this.rectDimensions.m.t) - this.rectDimensions.m.t) + .attr('y2', (d: any, index: any) => (index + 1) * (this.rectDimensions.h + this.rectDimensions.m.b + this.rectDimensions.m.t) - this.rectDimensions.m.t) } customClick(node: any) { From 397ce2df51228ba13f81f7ba4b5bd55236db1933 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Wed, 29 Nov 2017 03:40:32 +0100 Subject: [PATCH 04/20] Add dot lines, ticks and transitions --- .../components/d3Blocks/conceptHierarchy.less | 15 +-- .../components/d3Blocks/conceptHierarchy.tsx | 118 ++++++++++++++++-- 2 files changed, 116 insertions(+), 17 deletions(-) diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.less b/app/src/application/components/d3Blocks/conceptHierarchy.less index a7c6ec2..b473890 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.less +++ b/app/src/application/components/d3Blocks/conceptHierarchy.less @@ -15,15 +15,16 @@ rect { } .hierarchy-rect:hover { - fill: @gray2; + fill: @gray3; } .ancestor-rect { - fill: @blue3; + stroke: @green3; + stroke-width: 4; } .ancestor-rect:hover { - fill: @blue1; + stroke: @green2; } .selected-rect { @@ -31,11 +32,11 @@ rect { } .selected-rect:hover { - fill: @green1; + fill: @green2; } .dot-line { - stroke: @gray3; - stroke-width: 2; - stroke-dasharray: 3, 5; + stroke: @gray2; + stroke-width: 1; + stroke-dasharray: 3, 2; } diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 9624cdb..c5903ec 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -111,6 +111,7 @@ export class ConceptHierarchy extends React.Component { l: 5, } } + transitionDuration = 75 interceptClickHandler: any rebind = interceptClick.rebind @@ -119,6 +120,8 @@ export class ConceptHierarchy extends React.Component { domSvg: any domContainer: any domRects: any + domTopTicks: any + domBottomTicks: any domLines: any hierarchy: any @@ -198,6 +201,12 @@ export class ConceptHierarchy extends React.Component { .enter() .append('rect') .merge(this.domRects) + .call(this.interceptClickHandler + .on('customClick', this.customClick.bind(this)) + .on('customDoubleClick', this.customDoubleClick.bind(this)) + ) + .transition() + .delay(this.transitionDuration) .attr('transform', (d: any, index: number) => { let xShift = 0 let yShift = yFactor * d.depth @@ -222,16 +231,93 @@ export class ConceptHierarchy extends React.Component { ((node.id == this.selectedNode.id) ? ' selected-rect' : '') + ((ancestors.indexOf(node) > -1) ? ' ancestor-rect' : '') }) - .call(this.interceptClickHandler - .on('customClick', this.customClick.bind(this)) - .on('customDoubleClick', this.customDoubleClick.bind(this)) - ) + depthIncrement = [] + for (let i = 0; i < rectsPerLevel.length; i++) { + depthIncrement.push(0) + } + + this.domTopTicks = this.domContainer.selectAll('.top-tick') + .data(this.fnodes) + + this.domTopTicks.exit().remove() + + this.domTopTicks + .enter() + .append('line') + .merge(this.domTopTicks) + .transition() + .delay(this.transitionDuration) + .attr('class', 'top-tick dot-line') + .attr('display', (d: any) => { + let shouldDisplay = allowedNodes.indexOf(d) > -1 && d.depth > 0 + + return shouldDisplay ? null : 'none' + }) + .attr('x1', (d: any, index: number) => this.rectDimensions.w / 2) + .attr('x2', (d: any, index: number) => this.rectDimensions.w / 2) + .attr('y1', (d: any, index: any) => -this.rectDimensions.m.t) + .attr('y2', (d: any, index: any) => 0) + .attr('transform', (d: any, index: number) => { + let xShift = 0 + let yShift = yFactor * d.depth + + if (d.depth < rectsPerLevel.length && allowedNodes.indexOf(d) > -1) { + xShift = xFactor * (depthIncrement[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) - this.rectDimensions.w / 2 + if (d.depth < indexParentPerLevel.length) { + xShift -= xFactor * (indexParentPerLevel[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) + } + depthIncrement[d.depth]++ + } + + return 'translate(' + xShift + ',' + yShift +')' + }) + + depthIncrement = [] + for (let i = 0; i < rectsPerLevel.length; i++) { + depthIncrement.push(0) + } + + this.domBottomTicks = this.domContainer.selectAll('.bottom-tick') + .data(this.fnodes) + + this.domBottomTicks.exit().remove() + + this.domBottomTicks + .enter() + .append('line') + .merge(this.domBottomTicks) + .transition() + .delay(this.transitionDuration) + .attr('class', 'bottom-tick dot-line') + .attr('display', (d: any) => { + let shouldDisplay = ancestors.indexOf(d) > -1 + + return shouldDisplay ? null : 'none' + }) + .attr('x1', (d: any, index: number) => this.rectDimensions.w / 2) + .attr('x2', (d: any, index: number) => this.rectDimensions.w / 2) + .attr('y1', (d: any, index: any) => this.rectDimensions.h) + .attr('y2', (d: any, index: any) => this.rectDimensions.h + this.rectDimensions.m.b) + .attr('transform', (d: any, index: number) => { + let xShift = 0 + let yShift = yFactor * d.depth + + if (d.depth < rectsPerLevel.length && allowedNodes.indexOf(d) > -1) { + xShift = xFactor * (depthIncrement[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) - this.rectDimensions.w / 2 + if (d.depth < indexParentPerLevel.length) { + xShift -= xFactor * (indexParentPerLevel[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) + } + depthIncrement[d.depth]++ + } + + return 'translate(' + xShift + ',' + yShift +')' + }) let dataLines = rectsPerLevel dataLines.shift() - this.domLines = this.domContainer.selectAll('line') + this.domLines = this.domContainer.selectAll('.hbar') .data(dataLines) this.domLines.exit().remove() @@ -240,13 +326,25 @@ export class ConceptHierarchy extends React.Component { .enter() .append('line') .merge(this.domLines) - .attr('class', 'dot-line') + .transition() + .delay(this.transitionDuration) + .attr('class', 'hbar dot-line') .attr('x1', (d: any, index: number) => { - return (- ((d-1) * this.rectDimensions.w + (d-2) * (this.rectDimensions.m.r + this.rectDimensions.m.l)) / 2) + let xShift = 0 + if (index+1 < indexParentPerLevel.length) { + xShift += xFactor * (indexParentPerLevel[index+1] - (d-1) / 2) + } + return -((d-1) * xFactor / 2 + xShift) + }) + .attr('x2', (d: any, index: number) => { + let xShift = 0 + if (index+1 < indexParentPerLevel.length) { + xShift += xFactor * (indexParentPerLevel[index+1] - (d-1) / 2) + } + return (d-1) * xFactor / 2 - xShift }) - .attr('x2', (d: any) => ((d-1) * this.rectDimensions.w + (d-2) * (this.rectDimensions.m.r + this.rectDimensions.m.l)) / 2) - .attr('y1', (d: any, index: any) => (index + 1) * (this.rectDimensions.h + this.rectDimensions.m.b + this.rectDimensions.m.t) - this.rectDimensions.m.t) - .attr('y2', (d: any, index: any) => (index + 1) * (this.rectDimensions.h + this.rectDimensions.m.b + this.rectDimensions.m.t) - this.rectDimensions.m.t) + .attr('y1', (d: any, index: any) => (index + 1) * yFactor - this.rectDimensions.m.t) + .attr('y2', (d: any, index: any) => (index + 1) * yFactor - this.rectDimensions.m.t) } customClick(node: any) { From cd0ecc3e0e4c30274e29304829b96f312081d8c3 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Wed, 29 Nov 2017 11:10:48 +0100 Subject: [PATCH 05/20] Factorized code; Handled selected-node-without-children's ticks and hbar --- .../components/d3Blocks/conceptHierarchy.tsx | 169 +++++++++--------- 1 file changed, 88 insertions(+), 81 deletions(-) diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index c5903ec..03eaacc 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -111,7 +111,7 @@ export class ConceptHierarchy extends React.Component { l: 5, } } - transitionDuration = 75 + transitionDuration = 50 interceptClickHandler: any rebind = interceptClick.rebind @@ -132,6 +132,14 @@ export class ConceptHierarchy extends React.Component { selectedRoot: any selectedNode: any + rectsPerLevel: any + indexParentPerLevel: any + selectedNodeAncestors: any + displayedNodes: any + xFactor: any + yFactor: any + depthIncrement: any + initAttributes() { this.interceptClickHandler = this.interceptClick() } @@ -164,34 +172,53 @@ export class ConceptHierarchy extends React.Component { } - renderNodes() { - let rectsPerLevel = [1] - let indexParentPerLevel: any = [0] - let ancestors = this.selectedNode ? this.selectedNode.ancestors() : [] + singleRectTranslation(d: any, index: number=null) { + let xShift = 0 + let yShift = this.yFactor * d.depth + + if (d.depth < this.rectsPerLevel.length && this.displayedNodes.indexOf(d) > -1) { + xShift = this.xFactor * (this.depthIncrement[d.depth] - (this.rectsPerLevel[d.depth] - 1) / 2) - this.rectDimensions.w / 2 + if (d.depth < this.indexParentPerLevel.length) { + xShift -= this.xFactor * (this.indexParentPerLevel[d.depth] - (this.rectsPerLevel[d.depth] - 1) / 2) + } + this.depthIncrement[d.depth]++ + } + + return 'translate(' + xShift + ',' + yShift +')' + } + + updateRenderingAttributes() { + this.rectsPerLevel = [1] + this.indexParentPerLevel = [0] + this.selectedNodeAncestors = this.selectedNode ? this.selectedNode.ancestors() : [] - let allowedNodes = _.reduce(_.map(ancestors.reverse(), (n: any) => n.children ? n.children : []), + this.displayedNodes = _.reduce(_.map(this.selectedNodeAncestors.reverse(), (n: any) => n.children ? n.children : []), (acc: any, list: any) => { - if (list.length > 0 && list[0].depth < ancestors.length) { + if (list.length > 0 && list[0].depth < this.selectedNodeAncestors.length) { list.forEach((node: any, index: number) => { - if (node == ancestors[node.depth]) { - indexParentPerLevel.push(index) + if (node == this.selectedNodeAncestors[node.depth]) { + this.indexParentPerLevel.push(index) } }) } - rectsPerLevel.push(list.length) + this.rectsPerLevel.push(list.length) return acc.concat(list) }, [this.selectedRoot] ) - let depthIncrement: any = [] - for (let i = 0; i < rectsPerLevel.length; i++) { - depthIncrement.push(0) - } + this.xFactor = (this.rectDimensions.w + this.rectDimensions.m.r + this.rectDimensions.m.l) + this.yFactor = (this.rectDimensions.h + this.rectDimensions.m.t + this.rectDimensions.m.b) + } - let xFactor = (this.rectDimensions.w + this.rectDimensions.m.r + this.rectDimensions.m.l) - let yFactor = (this.rectDimensions.h + this.rectDimensions.m.t + this.rectDimensions.m.b) + restartDepthIncrement() { + this.depthIncrement = [] + for (let i = 0; i < this.rectsPerLevel.length; i++) { + this.depthIncrement.push(0) + } + } + renderRectangles() { this.domRects = this.domContainer.selectAll('rect') .data(this.fnodes) @@ -207,36 +234,25 @@ export class ConceptHierarchy extends React.Component { ) .transition() .delay(this.transitionDuration) - .attr('transform', (d: any, index: number) => { - let xShift = 0 - let yShift = yFactor * d.depth - - if (d.depth < rectsPerLevel.length && allowedNodes.indexOf(d) > -1) { - xShift = xFactor * (depthIncrement[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) - this.rectDimensions.w / 2 - if (d.depth < indexParentPerLevel.length) { - xShift -= xFactor * (indexParentPerLevel[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) - } - depthIncrement[d.depth]++ - } - - return 'translate(' + xShift + ',' + yShift +')' - }) + .attr('transform', (d: any) => this.singleRectTranslation(d)) .attr('display', (d: any) => { - let shouldDisplay = allowedNodes.indexOf(d) > -1 + let shouldDisplay = this.displayedNodes.indexOf(d) > -1 return shouldDisplay ? null : 'none' }) .attr('class', (node: any) => { return 'hierarchy-rect' + ((node.id == this.selectedNode.id) ? ' selected-rect' : '') + - ((ancestors.indexOf(node) > -1) ? ' ancestor-rect' : '') + ((this.selectedNodeAncestors.indexOf(node) > -1) ? ' ancestor-rect' : '') }) - depthIncrement = [] - for (let i = 0; i < rectsPerLevel.length; i++) { - depthIncrement.push(0) + this.depthIncrement = [] + for (let i = 0; i < this.rectsPerLevel.length; i++) { + this.depthIncrement.push(0) } + } + renderTopTicks() { this.domTopTicks = this.domContainer.selectAll('.top-tick') .data(this.fnodes) @@ -250,7 +266,7 @@ export class ConceptHierarchy extends React.Component { .delay(this.transitionDuration) .attr('class', 'top-tick dot-line') .attr('display', (d: any) => { - let shouldDisplay = allowedNodes.indexOf(d) > -1 && d.depth > 0 + let shouldDisplay = this.displayedNodes.indexOf(d) > -1 && d.depth > 0 return shouldDisplay ? null : 'none' }) @@ -258,26 +274,10 @@ export class ConceptHierarchy extends React.Component { .attr('x2', (d: any, index: number) => this.rectDimensions.w / 2) .attr('y1', (d: any, index: any) => -this.rectDimensions.m.t) .attr('y2', (d: any, index: any) => 0) - .attr('transform', (d: any, index: number) => { - let xShift = 0 - let yShift = yFactor * d.depth - - if (d.depth < rectsPerLevel.length && allowedNodes.indexOf(d) > -1) { - xShift = xFactor * (depthIncrement[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) - this.rectDimensions.w / 2 - if (d.depth < indexParentPerLevel.length) { - xShift -= xFactor * (indexParentPerLevel[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) - } - depthIncrement[d.depth]++ - } - - return 'translate(' + xShift + ',' + yShift +')' - }) - - depthIncrement = [] - for (let i = 0; i < rectsPerLevel.length; i++) { - depthIncrement.push(0) - } + .attr('transform', (d: any) => this.singleRectTranslation(d)) + } + renderBottomTicks() { this.domBottomTicks = this.domContainer.selectAll('.bottom-tick') .data(this.fnodes) @@ -291,7 +291,10 @@ export class ConceptHierarchy extends React.Component { .delay(this.transitionDuration) .attr('class', 'bottom-tick dot-line') .attr('display', (d: any) => { - let shouldDisplay = ancestors.indexOf(d) > -1 + let shouldDisplay = this.selectedNodeAncestors.indexOf(d) > -1 + if (d == this.selectedNode && (d.children == null || d.children.length == 0)) { + shouldDisplay = false + } return shouldDisplay ? null : 'none' }) @@ -299,23 +302,15 @@ export class ConceptHierarchy extends React.Component { .attr('x2', (d: any, index: number) => this.rectDimensions.w / 2) .attr('y1', (d: any, index: any) => this.rectDimensions.h) .attr('y2', (d: any, index: any) => this.rectDimensions.h + this.rectDimensions.m.b) - .attr('transform', (d: any, index: number) => { - let xShift = 0 - let yShift = yFactor * d.depth - - if (d.depth < rectsPerLevel.length && allowedNodes.indexOf(d) > -1) { - xShift = xFactor * (depthIncrement[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) - this.rectDimensions.w / 2 - if (d.depth < indexParentPerLevel.length) { - xShift -= xFactor * (indexParentPerLevel[d.depth] - (rectsPerLevel[d.depth] - 1) / 2) - } - depthIncrement[d.depth]++ - } - - return 'translate(' + xShift + ',' + yShift +')' - }) + .attr('transform', (d: any) => this.singleRectTranslation(d)) + } - let dataLines = rectsPerLevel + renderHorizontalBars() { + let dataLines = this.rectsPerLevel dataLines.shift() + if (this.selectedNode && this.selectedNode.depth > 0 && (this.selectedNode.children == null || this.selectedNode.children.length == 0)) { + dataLines.pop() + } this.domLines = this.domContainer.selectAll('.hbar') .data(dataLines) @@ -331,25 +326,37 @@ export class ConceptHierarchy extends React.Component { .attr('class', 'hbar dot-line') .attr('x1', (d: any, index: number) => { let xShift = 0 - if (index+1 < indexParentPerLevel.length) { - xShift += xFactor * (indexParentPerLevel[index+1] - (d-1) / 2) + if (index+1 < this.indexParentPerLevel.length) { + xShift += this.xFactor * (this.indexParentPerLevel[index+1] - (d-1) / 2) } - return -((d-1) * xFactor / 2 + xShift) + return -((d-1) * this.xFactor / 2 + xShift) }) .attr('x2', (d: any, index: number) => { let xShift = 0 - if (index+1 < indexParentPerLevel.length) { - xShift += xFactor * (indexParentPerLevel[index+1] - (d-1) / 2) + if (index+1 < this.indexParentPerLevel.length) { + xShift += this.xFactor * (this.indexParentPerLevel[index+1] - (d-1) / 2) } - return (d-1) * xFactor / 2 - xShift + return (d-1) * this.xFactor / 2 - xShift }) - .attr('y1', (d: any, index: any) => (index + 1) * yFactor - this.rectDimensions.m.t) - .attr('y2', (d: any, index: any) => (index + 1) * yFactor - this.rectDimensions.m.t) + .attr('y1', (d: any, index: any) => (index + 1) * this.yFactor - this.rectDimensions.m.t) + .attr('y2', (d: any, index: any) => (index + 1) * this.yFactor - this.rectDimensions.m.t) + } + + renderAll() { + this.updateRenderingAttributes() + this.restartDepthIncrement() + this.renderRectangles() + this.restartDepthIncrement() + this.renderTopTicks() + this.restartDepthIncrement() + this.renderBottomTicks() + this.restartDepthIncrement() + this.renderHorizontalBars() } customClick(node: any) { this.selectedNode = node - this.renderNodes() + this.renderAll() } customDoubleClick(d: any) { @@ -358,7 +365,7 @@ export class ConceptHierarchy extends React.Component { // Unite all previous rendering functions in just one function. renderD3DomElements() { - this.renderNodes() + this.renderAll() } // REACT LIFECYCLE From 59e2fbeca79ee58372a47483eb07c2aac3ceb070 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Wed, 29 Nov 2017 12:25:01 +0100 Subject: [PATCH 06/20] Add mouseover behaviour --- .../components/d3Blocks/conceptHierarchy.less | 5 +++ .../components/d3Blocks/conceptHierarchy.tsx | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.less b/app/src/application/components/d3Blocks/conceptHierarchy.less index b473890..ccb6c75 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.less +++ b/app/src/application/components/d3Blocks/conceptHierarchy.less @@ -40,3 +40,8 @@ rect { stroke-width: 1; stroke-dasharray: 3, 2; } + +#tooltip_content { + fill: @light-gray1; + font-size: 15px; +} diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 03eaacc..401e513 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -166,6 +166,9 @@ export class ConceptHierarchy extends React.Component { this.domContainer .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') + + this.domSvg.select('#tooltip') + .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') } selectNode(d: any) { @@ -218,6 +221,29 @@ export class ConceptHierarchy extends React.Component { } } + handleMouseOver(that: any, d: any) { + console.log('mouseover') + console.log(this) + console.log(that) + console.log(d) + let tag = this as any + let attribute = tag.transform.baseVal[0].matrix + console.log(attribute) + + that.domSvg.select('#tooltip') + .style('visibility', '') + + that.domSvg.select('#tooltip_content') + .attr('transform', 'translate(' + attribute.e + ',' + attribute.f +')') + .text(d.data.name) + } + + handleMouseOut(that: any) { + console.log('mouseout') + that.domSvg.select('#tooltip') + .style('visibility', 'hidden') + } + renderRectangles() { this.domRects = this.domContainer.selectAll('rect') .data(this.fnodes) @@ -232,6 +258,8 @@ export class ConceptHierarchy extends React.Component { .on('customClick', this.customClick.bind(this)) .on('customDoubleClick', this.customDoubleClick.bind(this)) ) + .on('mouseover', _.partial(this.handleMouseOver, this)) + .on('mouseout', _.partial(this.handleMouseOut, this)) .transition() .delay(this.transitionDuration) .attr('transform', (d: any) => this.singleRectTranslation(d)) @@ -427,6 +455,9 @@ export class ConceptHierarchy extends React.Component { height={height} > + + + ) } From ac47e7129991dc775dfacdb04cc8cc41a1242c36 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Thu, 30 Nov 2017 02:33:43 +0100 Subject: [PATCH 07/20] Add text-wrapping tooltip --- .../components/d3Blocks/conceptHierarchy.less | 29 ++++++--- .../components/d3Blocks/conceptHierarchy.tsx | 62 +++++++++++++++---- .../components/d3Blocks/utils/wrap.tsx | 41 ++++++++++++ 3 files changed, 113 insertions(+), 19 deletions(-) create mode 100644 app/src/application/components/d3Blocks/utils/wrap.tsx diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.less b/app/src/application/components/d3Blocks/conceptHierarchy.less index ccb6c75..4d2c627 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.less +++ b/app/src/application/components/d3Blocks/conceptHierarchy.less @@ -5,34 +5,35 @@ } rect { - stroke-width: 0 + stroke-width: 0; + stroke-alignment: inner; } .hierarchy-rect { - fill: @gray4; + fill: @gray3; width: 45px; height: 25px; } .hierarchy-rect:hover { - fill: @gray3; + fill: @gray2; } .ancestor-rect { - stroke: @green3; + stroke: @green2; stroke-width: 4; } .ancestor-rect:hover { - stroke: @green2; + stroke: @green1; } .selected-rect { - fill: @green3; + fill: @green2; } .selected-rect:hover { - fill: @green2; + fill: @green1; } .dot-line { @@ -41,7 +42,21 @@ rect { stroke-dasharray: 3, 2; } +#tooltip { + pointer-events: none; +} + #tooltip_content { fill: @light-gray1; font-size: 15px; + text-anchor: middle; + + tspan { + alignment-baseline: hanging; + } +} + +#tooltip_background, #tooltip_pointer { + fill: black; + opacity: 0.8; } diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 401e513..7f4e7ec 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -12,6 +12,7 @@ import { conceptLinksAttribute, extendedConceptNodeAttribute, } from './../../types' +import * as wrap from './utils/wrap' interface d3GraphNode extends d3.SimulationNodeDatum, extendedConceptNodeAttribute {} @@ -111,6 +112,20 @@ export class ConceptHierarchy extends React.Component { l: 5, } } + tooltipDimensions = { + w: 120, + h: 15, + p: { + t: 10, + r: 5, + b: 10, + l: 5, + }, + t: { + w: 10, + h: 10, + } + } transitionDuration = 50 interceptClickHandler: any @@ -167,8 +182,8 @@ export class ConceptHierarchy extends React.Component { this.domContainer .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') - this.domSvg.select('#tooltip') - .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') + this.domSvg.select('#tooltip_container') + .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') } selectNode(d: any) { @@ -222,24 +237,40 @@ export class ConceptHierarchy extends React.Component { } handleMouseOver(that: any, d: any) { - console.log('mouseover') - console.log(this) - console.log(that) - console.log(d) let tag = this as any let attribute = tag.transform.baseVal[0].matrix - console.log(attribute) that.domSvg.select('#tooltip') .style('visibility', '') that.domSvg.select('#tooltip_content') - .attr('transform', 'translate(' + attribute.e + ',' + attribute.f +')') .text(d.data.name) + .call(_.partial(wrap.wrap, 120)) + + that.domSvg.select('#tooltip') + .attr('transform', () => { + let xShift = attribute.e + that.rectDimensions.w / 2 + let yShift = attribute.f - d3.selectAll('#tooltip_content tspan').size() * that.tooltipDimensions.h - that.tooltipDimensions.p.b - that.tooltipDimensions.t.h - 4 + return 'translate(' + xShift + ',' + yShift +')' + }) + + that.domSvg.select('#tooltip_background') + .attr('transform', 'translate(' + (- (that.tooltipDimensions.w + that.tooltipDimensions.p.r + that.tooltipDimensions.p.l) / 2) + ',' + (- that.tooltipDimensions.p.t) +')') + .attr('width', that.tooltipDimensions.w + that.tooltipDimensions.p.r + that.tooltipDimensions.p.l) + .attr('height', () => { + return (d3.selectAll('#tooltip_content tspan').size() * that.tooltipDimensions.h + + that.tooltipDimensions.p.t + that.tooltipDimensions.p.b) + }) + + that.domSvg.select('#tooltip_pointer') + .attr('transform', () => { + let xShift = - that.tooltipDimensions.t.w / 2 + let yShift = d3.selectAll('#tooltip_content tspan').size() * that.tooltipDimensions.h + that.tooltipDimensions.p.b + return 'translate(' + xShift + ',' + yShift +')' + }) } - handleMouseOut(that: any) { - console.log('mouseout') + handleMouseOut(that: any, d: any) { that.domSvg.select('#tooltip') .style('visibility', 'hidden') } @@ -383,6 +414,9 @@ export class ConceptHierarchy extends React.Component { } customClick(node: any) { + this.domSvg.select('#tooltip') + .style('visibility', 'hidden') + this.selectedNode = node this.renderAll() } @@ -455,8 +489,12 @@ export class ConceptHierarchy extends React.Component { height={height} > - - + + + + + + ) diff --git a/app/src/application/components/d3Blocks/utils/wrap.tsx b/app/src/application/components/d3Blocks/utils/wrap.tsx new file mode 100644 index 0000000..0a3ffcc --- /dev/null +++ b/app/src/application/components/d3Blocks/utils/wrap.tsx @@ -0,0 +1,41 @@ +import * as d3 from 'd3' + +export function wrap(width=100, texts: any) { + // TODO: investigate why function() {} and () => {} + // don't yield the same value for `this`... + texts.each(function() { + let text = d3.select(this as any), + words = text.text().split(/\s+/).reverse(), + word, + line: any= [], + lineNumber = 0, + lineHeight = 1, + x = text.attr('x') ? text.attr('x') : 0, + y = text.attr('y') ? text.attr('y') : 0, + dy = isNaN(parseFloat(text.attr('dy'))) ? 0 : parseFloat(text.attr('dy')), + tspan = text + .text(null) + .append('tspan') + .attr('x', 0) + .attr('y', y) + .attr('dy', dy + 'em') + + while (word = words.pop()) { + line.push(word) + tspan.text(line.join(' ')) + var node: any = tspan.node() + var hasGreaterWidth = node.getComputedTextLength() > width + if (hasGreaterWidth) { + line.pop() + tspan.text(line.join(' ')) + line = [word] + tspan = text + .append('tspan') + .attr('x', 0) + .attr('y', y) + .attr('dy', ++lineNumber * lineHeight + dy + 'em') + .text(word) + } + } + }) +} From 29f0af3e8a581afd509eab9d2f4b8eb64bdd80e6 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Thu, 30 Nov 2017 02:44:36 +0100 Subject: [PATCH 08/20] Tooltip bugfix --- .../application/components/d3Blocks/conceptHierarchy.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 7f4e7ec..90a9feb 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -184,6 +184,7 @@ export class ConceptHierarchy extends React.Component { this.domSvg.select('#tooltip_container') .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') + .style('visibility', 'hidden') } selectNode(d: any) { @@ -240,7 +241,7 @@ export class ConceptHierarchy extends React.Component { let tag = this as any let attribute = tag.transform.baseVal[0].matrix - that.domSvg.select('#tooltip') + that.domSvg.select('#tooltip_container') .style('visibility', '') that.domSvg.select('#tooltip_content') @@ -271,7 +272,7 @@ export class ConceptHierarchy extends React.Component { } handleMouseOut(that: any, d: any) { - that.domSvg.select('#tooltip') + that.domSvg.select('#tooltip_container') .style('visibility', 'hidden') } @@ -414,7 +415,7 @@ export class ConceptHierarchy extends React.Component { } customClick(node: any) { - this.domSvg.select('#tooltip') + this.domSvg.select('#tooltip_container') .style('visibility', 'hidden') this.selectedNode = node From 98d48728549eb829d7dc333abfad6442b1721e72 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Thu, 30 Nov 2017 14:11:06 +0100 Subject: [PATCH 09/20] Add top root nodes --- .../components/d3Blocks/conceptHierarchy.less | 5 +- .../components/d3Blocks/conceptHierarchy.tsx | 197 ++++++++++++------ 2 files changed, 132 insertions(+), 70 deletions(-) diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.less b/app/src/application/components/d3Blocks/conceptHierarchy.less index 4d2c627..9dbe8a2 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.less +++ b/app/src/application/components/d3Blocks/conceptHierarchy.less @@ -6,13 +6,10 @@ rect { stroke-width: 0; - stroke-alignment: inner; } .hierarchy-rect { fill: @gray3; - width: 45px; - height: 25px; } .hierarchy-rect:hover { @@ -58,5 +55,5 @@ rect { #tooltip_background, #tooltip_pointer { fill: black; - opacity: 0.8; + opacity: 0.85; } diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 90a9feb..93200da 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -103,13 +103,19 @@ export class ConceptHierarchy extends React.Component { width: number height: number rectDimensions = { - w: 45, - h: 25, + w: 55, + h: 30, m: { t: 10, r: 5, b: 10, l: 5, + }, + rm: { + t: 10, + r: 15, + b: 10, + l: 15, } } tooltipDimensions = { @@ -122,22 +128,25 @@ export class ConceptHierarchy extends React.Component { l: 5, }, t: { - w: 10, - h: 10, + w: 12, + h: 12, } } transitionDuration = 50 - interceptClickHandler: any rebind = interceptClick.rebind interceptClick = interceptClick.interceptClick + interceptClickHandlerRoot: any + interceptClickHandlerHierarchy: any domSvg: any - domContainer: any - domRects: any + domHierarchy: any + domHierarchyRects: any domTopTicks: any domBottomTicks: any domLines: any + domRoots: any + domRootRects: any hierarchy: any nodes: any @@ -153,10 +162,23 @@ export class ConceptHierarchy extends React.Component { displayedNodes: any xFactor: any yFactor: any + xFactorRoot: any + yFactorRoot: any depthIncrement: any initAttributes() { - this.interceptClickHandler = this.interceptClick() + this.interceptClickHandlerRoot = this.interceptClick() + this.interceptClickHandlerHierarchy = this.interceptClick() + } + + updateHierarchyNodes() { + this.fnodes = [] + + if (this.selectedRoot) { + this.selectedRoot.each((node: any) => { + this.fnodes.push(node) + }) + } } updateAttributes() { @@ -167,45 +189,24 @@ export class ConceptHierarchy extends React.Component { this.nodes = this.props.nodes this.graph = this.props.graph - this.selectedRoot = null - this.selectedNode = null - this.fnodes = [] - if (this.graph.length > 0) { this.selectedRoot = this.graph[0] this.selectedNode = this.graph[0] - this.selectedRoot.each((node: any) => { - this.fnodes.push(node) - }) } - this.domContainer + this.updateHierarchyNodes() + + this.domHierarchy .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') + this.domRoots + .attr('transform', 'translate(' + this.width / 2 + ', 50)') + this.domSvg.select('#tooltip_container') .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') .style('visibility', 'hidden') } - selectNode(d: any) { - - } - - singleRectTranslation(d: any, index: number=null) { - let xShift = 0 - let yShift = this.yFactor * d.depth - - if (d.depth < this.rectsPerLevel.length && this.displayedNodes.indexOf(d) > -1) { - xShift = this.xFactor * (this.depthIncrement[d.depth] - (this.rectsPerLevel[d.depth] - 1) / 2) - this.rectDimensions.w / 2 - if (d.depth < this.indexParentPerLevel.length) { - xShift -= this.xFactor * (this.indexParentPerLevel[d.depth] - (this.rectsPerLevel[d.depth] - 1) / 2) - } - this.depthIncrement[d.depth]++ - } - - return 'translate(' + xShift + ',' + yShift +')' - } - updateRenderingAttributes() { this.rectsPerLevel = [1] this.indexParentPerLevel = [0] @@ -228,6 +229,8 @@ export class ConceptHierarchy extends React.Component { this.xFactor = (this.rectDimensions.w + this.rectDimensions.m.r + this.rectDimensions.m.l) this.yFactor = (this.rectDimensions.h + this.rectDimensions.m.t + this.rectDimensions.m.b) + this.xFactorRoot = (this.rectDimensions.w + this.rectDimensions.rm.r + this.rectDimensions.rm.l) + this.yFactorRoot = (this.rectDimensions.h + this.rectDimensions.rm.t + this.rectDimensions.rm.b) } restartDepthIncrement() { @@ -238,6 +241,19 @@ export class ConceptHierarchy extends React.Component { } handleMouseOver(that: any, d: any) { + // Place the tooltip in the correct container + let domParentId = d3.select(this as any).node().parentNode.id + + if (domParentId == 'hierarchy') { + that.domSvg.select('#tooltip_container') + .attr('transform', 'translate(' + that.width / 2 + ',' + that.height / 2 + ')') + .style('visibility', 'hidden') + } else if (domParentId == 'roots') { + that.domSvg.select('#tooltip_container') + .attr('transform', 'translate(' + that.width / 2 + ', 50)') + .style('visibility', 'hidden') + } + let tag = this as any let attribute = tag.transform.baseVal[0].matrix @@ -276,18 +292,82 @@ export class ConceptHierarchy extends React.Component { .style('visibility', 'hidden') } + customClick(newRootNode: any, node: any, a: any) { + if (newRootNode) { + this.selectedRoot = node + this.updateHierarchyNodes() + } + + this.domSvg.select('#tooltip_container') + .style('visibility', 'hidden') + + this.selectedNode = node + this.renderAll() + } + + customDoubleClick(d: any) { + console.log('doubleclick') + } + + singleRectTranslation(d: any, index: number=null) { + let xShift = 0 + let yShift = this.yFactor * d.depth + + if (d.depth < this.rectsPerLevel.length && this.displayedNodes.indexOf(d) > -1) { + xShift = this.xFactor * (this.depthIncrement[d.depth] - (this.rectsPerLevel[d.depth] - 1) / 2) - this.rectDimensions.w / 2 + if (d.depth < this.indexParentPerLevel.length) { + xShift -= this.xFactor * (this.indexParentPerLevel[d.depth] - (this.rectsPerLevel[d.depth] - 1) / 2) + } + this.depthIncrement[d.depth]++ + } + + return 'translate(' + xShift + ',' + yShift +')' + } + renderRectangles() { - this.domRects = this.domContainer.selectAll('rect') - .data(this.fnodes) + // Draws root rectangles + let availableRoots = _.filter(this.graph, (root: any) => root != this.selectedRoot) + + this.domRootRects = this.domRoots.selectAll('rect') + .data(availableRoots, (node: any) => node.data.id) + + this.domRootRects.exit().remove() + + this.domRootRects + .enter() + .append('rect') + .attr('width', this.rectDimensions.w) + .attr('height', this.rectDimensions.h) + .merge(this.domRootRects) + .call(this.interceptClickHandlerRoot + .on('customClick', this.customClick.bind(this, true)) + .on('customDoubleClick', this.customDoubleClick.bind(this)) + ) + .on('mouseover', _.partial(this.handleMouseOver, this)) + .on('mouseout', _.partial(this.handleMouseOut, this)) + .transition() + .delay(this.transitionDuration) + .attr('transform', (d: any, index: number) => 'translate(' + this.xFactorRoot * (index - (availableRoots.length) / 2) + ',' + this.yFactorRoot + ')') + .attr('class', (node: any) => { + return 'hierarchy-rect' + + ((node.id == this.selectedNode.id) ? ' selected-rect' : '') + + ((this.selectedNodeAncestors.indexOf(node) > -1) ? ' ancestor-rect' : '') + }) + + // Draws hierarchy rectangles + this.domHierarchyRects = this.domHierarchy.selectAll('rect') + .data(this.fnodes, (node: any) => node.data.id) - this.domRects.exit().remove() + this.domHierarchyRects.exit().remove() - this.domRects + this.domHierarchyRects .enter() .append('rect') - .merge(this.domRects) - .call(this.interceptClickHandler - .on('customClick', this.customClick.bind(this)) + .attr('width', this.rectDimensions.w) + .attr('height', this.rectDimensions.h) + .merge(this.domHierarchyRects) + .call(this.interceptClickHandlerHierarchy + .on('customClick', this.customClick.bind(this, false)) .on('customDoubleClick', this.customDoubleClick.bind(this)) ) .on('mouseover', _.partial(this.handleMouseOver, this)) @@ -305,15 +385,10 @@ export class ConceptHierarchy extends React.Component { ((node.id == this.selectedNode.id) ? ' selected-rect' : '') + ((this.selectedNodeAncestors.indexOf(node) > -1) ? ' ancestor-rect' : '') }) - - this.depthIncrement = [] - for (let i = 0; i < this.rectsPerLevel.length; i++) { - this.depthIncrement.push(0) - } } renderTopTicks() { - this.domTopTicks = this.domContainer.selectAll('.top-tick') + this.domTopTicks = this.domHierarchy.selectAll('.top-tick') .data(this.fnodes) this.domTopTicks.exit().remove() @@ -338,7 +413,7 @@ export class ConceptHierarchy extends React.Component { } renderBottomTicks() { - this.domBottomTicks = this.domContainer.selectAll('.bottom-tick') + this.domBottomTicks = this.domHierarchy.selectAll('.bottom-tick') .data(this.fnodes) this.domBottomTicks.exit().remove() @@ -372,7 +447,7 @@ export class ConceptHierarchy extends React.Component { dataLines.pop() } - this.domLines = this.domContainer.selectAll('.hbar') + this.domLines = this.domHierarchy.selectAll('.hbar') .data(dataLines) this.domLines.exit().remove() @@ -414,18 +489,6 @@ export class ConceptHierarchy extends React.Component { this.renderHorizontalBars() } - customClick(node: any) { - this.domSvg.select('#tooltip_container') - .style('visibility', 'hidden') - - this.selectedNode = node - this.renderAll() - } - - customDoubleClick(d: any) { - console.log('doubleclick') - } - // Unite all previous rendering functions in just one function. renderD3DomElements() { this.renderAll() @@ -441,13 +504,14 @@ export class ConceptHierarchy extends React.Component { } // This function is called right after the first render() of our component. - // We'll define here our domContainer, the forces to use in our d3 simulation + // We'll define here our domHierarchy, the forces to use in our d3 simulation // as well as other attributes which will be useful to d3 and which come from // React props (remember we want to dissociate d3 calculations from React props). // After that, we call our sent of simulation and rendering functions. componentDidMount() { this.domSvg = d3.select(this.refs.container as any) - this.domContainer = this.domSvg.select('#container') + this.domHierarchy = this.domSvg.select('#hierarchy') + this.domRoots = this.domSvg.select('#roots') this.width = this.props.dimensions.width this.height = this.props.dimensions.height @@ -489,12 +553,13 @@ export class ConceptHierarchy extends React.Component { width={width} height={height} > - + + - + From f00f2ee47b1af991c284fe9673cfd4b288b675d7 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Thu, 30 Nov 2017 16:17:13 +0100 Subject: [PATCH 10/20] Add breadcrumbs; Bugfix conflicting breadcrumbs; Add global state management behaviour --- app/src/application/actions.tsx | 20 ++++++- .../components/d3Blocks/ConceptNav.tsx | 22 ++++---- .../components/d3Blocks/conceptGraph.tsx | 2 +- .../components/d3Blocks/conceptHierarchy.less | 17 ++++++ .../components/d3Blocks/conceptHierarchy.tsx | 55 +++++++++++++++---- .../components/d3Blocks/sunburst.less | 2 +- .../components/d3Blocks/sunburst.tsx | 31 +++++------ .../application/components/utils/NavPanel.tsx | 16 ++++-- app/src/application/containers/app.tsx | 4 +- app/src/application/containers/test.tsx | 4 +- app/src/application/reducers.tsx | 31 +++++++++-- app/src/application/types.tsx | 4 +- app/src/style.less | 8 +++ 13 files changed, 162 insertions(+), 54 deletions(-) diff --git a/app/src/application/actions.tsx b/app/src/application/actions.tsx index aa4255f..7632333 100644 --- a/app/src/application/actions.tsx +++ b/app/src/application/actions.tsx @@ -6,10 +6,24 @@ import { import config from './config' const serverUrl = config('app').serverURL -export function changeSelectedConceptNav(conceptNode: any): action { +export function changeSelectedRootNav(node: any): action { return { - type: 'CHANGE_SELECTED_CONCEPT_NAV', - value: conceptNode, + type: 'CHANGE_SELECTED_ROOT_NAV', + value: node, + } +} + +export function changeSelectedNodeNav(node: any): action { + return { + type: 'CHANGE_SELECTED_NODE_NAV', + value: node, + } +} + +export function changeDisplayedConceptNav(node: any): action { + return { + type: 'CHANGE_DISPLAYED_CONCEPT_NAV', + value: node, } } diff --git a/app/src/application/components/d3Blocks/ConceptNav.tsx b/app/src/application/components/d3Blocks/ConceptNav.tsx index 0a16091..41f6784 100644 --- a/app/src/application/components/d3Blocks/ConceptNav.tsx +++ b/app/src/application/components/d3Blocks/ConceptNav.tsx @@ -27,7 +27,7 @@ interface Props { } graph: conceptGraph, nodes: extendedConceptNodeAttribute[], - selectedConceptNode: extendedConceptNodeAttribute, + displayedNode: any, displayedSlugs: string[], dispatch: any, } @@ -63,7 +63,7 @@ export class ConceptNav extends React.Component { if (!hNode.toggled) { hNode.toggled = true hNode.visible = true - hNode.children.forEach(this.hierachicalToggle.bind(this)) + hNode.children ? hNode.children.forEach(this.hierachicalToggle.bind(this)) : null } else { hNode.toggled = false hNode.each((hNode: extendedHierarchyNode) => { @@ -74,17 +74,17 @@ export class ConceptNav extends React.Component { } updateHierarchy() { - if (this.props.selectedConceptNode && this.props.selectedConceptNode.connexComponent) { + if (this.props.displayedNode && this.props.displayedNode.data.connexComponent) { this.graph = this.props.graph let selectedNode - if (this.selectedNodeId != this.props.selectedConceptNode.id) { - this.selectedNodeId = this.props.selectedConceptNode.id - this.hierarchy = this.graph[this.props.selectedConceptNode.connexComponent] + if (this.selectedNodeId != this.props.displayedNode.data.id) { + this.selectedNodeId = this.props.displayedNode.data.id + this.hierarchy = this.graph[this.props.displayedNode.data.connexComponent] this.hierarchy.each((hNode: extendedHierarchyNode) => { - if (hNode.data.id == this.props.selectedConceptNode.id) { + if (hNode.data.id == this.props.displayedNode.data.id) { selectedNode = hNode } }) @@ -216,13 +216,13 @@ export class ConceptNav extends React.Component { // REACT LIFECYCLE selectGraph(props: Props) { - let selectedGraph: extendedHierarchyNode + let selectedGraph: any - if (props.selectedConceptNode && props.selectedConceptNode.id) { - let hierarchy = props.graph[props.selectedConceptNode.connexComponent] + if (props.displayedNode && props.displayedNode.data.id) { + let hierarchy = props.graph[props.displayedNode.data.connexComponent] hierarchy.each((node: extendedHierarchyNode) => { - if (node.data.id == props.selectedConceptNode.id) { + if (node.data.id == props.displayedNode.data.id) { selectedGraph = node } }) diff --git a/app/src/application/components/d3Blocks/conceptGraph.tsx b/app/src/application/components/d3Blocks/conceptGraph.tsx index db3fb0d..19fbf36 100644 --- a/app/src/application/components/d3Blocks/conceptGraph.tsx +++ b/app/src/application/components/d3Blocks/conceptGraph.tsx @@ -176,7 +176,7 @@ export class ConceptGraph extends React.Component { customDoubleClick(d: d3GraphNode) { // It is important that this action is dispatched first as it erases // the list of displayed slugs from the Redux state. - this.props.dispatch(actions.changeSelectedConceptNav(d)) + this.props.dispatch(actions.changeDisplayedConceptNav(d)) // TODO: associate correct container instead of default cp1 this.props.dispatch(actions.fetchConcept('concepts/' + d.slug, 'cp1')) this.props.dispatch(actions.toggleNavPanel()) diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.less b/app/src/application/components/d3Blocks/conceptHierarchy.less index 9dbe8a2..635f576 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.less +++ b/app/src/application/components/d3Blocks/conceptHierarchy.less @@ -8,6 +8,17 @@ rect { stroke-width: 0; } +.breadcrumbs { + margin: 20px 10px 10px 20px; + + li { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } +} + .hierarchy-rect { fill: @gray3; } @@ -41,6 +52,12 @@ rect { #tooltip { pointer-events: none; + text { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } } #tooltip_content { diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 93200da..2f48b91 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -22,6 +22,8 @@ interface Props { version: number, nodes: extendedConceptNodeAttribute[], graph: any, + selectedRoot: any, + selectedNode: any, dimensions: { width: number, height: number, @@ -189,10 +191,8 @@ export class ConceptHierarchy extends React.Component { this.nodes = this.props.nodes this.graph = this.props.graph - if (this.graph.length > 0) { - this.selectedRoot = this.graph[0] - this.selectedNode = this.graph[0] - } + this.selectedRoot = this.props.selectedRoot + this.selectedNode = this.props.selectedNode this.updateHierarchyNodes() @@ -205,6 +205,11 @@ export class ConceptHierarchy extends React.Component { this.domSvg.select('#tooltip_container') .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') .style('visibility', 'hidden') + + this.domSvg.select('#breadcrumbs') + .attr('width', this.width) + .attr('height', '30px') + .attr('transform', 'translate(20, 20)') } updateRenderingAttributes() { @@ -292,21 +297,30 @@ export class ConceptHierarchy extends React.Component { .style('visibility', 'hidden') } - customClick(newRootNode: any, node: any, a: any) { + customClick(newRootNode: any, node: any) { if (newRootNode) { this.selectedRoot = node this.updateHierarchyNodes() + this.props.dispatch(actions.changeSelectedRootNav(node)) } this.domSvg.select('#tooltip_container') .style('visibility', 'hidden') this.selectedNode = node + this.props.dispatch(actions.changeSelectedNodeNav(node)) this.renderAll() } - customDoubleClick(d: any) { - console.log('doubleclick') + customDoubleClick(node: any) { + this.selectedNode = node + this.props.dispatch(actions.changeSelectedNodeNav(node)) + this.renderAll() + + this.props.dispatch(actions.changeDisplayedConceptNav(node)) + // TODO: associate correct container instead of default cp1 + this.props.dispatch(actions.fetchConcept('concepts/' + node.data.slug, 'test')) + this.props.dispatch(actions.toggleNavPanel()) } singleRectTranslation(d: any, index: number=null) { @@ -350,7 +364,7 @@ export class ConceptHierarchy extends React.Component { .attr('transform', (d: any, index: number) => 'translate(' + this.xFactorRoot * (index - (availableRoots.length) / 2) + ',' + this.yFactorRoot + ')') .attr('class', (node: any) => { return 'hierarchy-rect' + - ((node.id == this.selectedNode.id) ? ' selected-rect' : '') + + ((this.selectedNode && node.id == this.selectedNode.id) ? ' selected-rect' : '') + ((this.selectedNodeAncestors.indexOf(node) > -1) ? ' ancestor-rect' : '') }) @@ -477,6 +491,20 @@ export class ConceptHierarchy extends React.Component { .attr('y2', (d: any, index: any) => (index + 1) * this.yFactor - this.rectDimensions.m.t) } + renderBreadcrumbs() { + const selectedNodePath = this.selectedNode ? this.selectedNode.ancestors().reverse() : [] + + let breads = this.domSvg.select('#breadcrumb-list') + .selectAll('li') + .data(selectedNodePath, (node: any) => node.data.id) + + breads.exit().remove() + breads.enter() + .append('li') + .attr('class', 'pt-breadcrumb') + .html((d: any) => d.data.name) + } + renderAll() { this.updateRenderingAttributes() this.restartDepthIncrement() @@ -487,6 +515,7 @@ export class ConceptHierarchy extends React.Component { this.renderBottomTicks() this.restartDepthIncrement() this.renderHorizontalBars() + this.renderBreadcrumbs() } // Unite all previous rendering functions in just one function. @@ -509,7 +538,7 @@ export class ConceptHierarchy extends React.Component { // React props (remember we want to dissociate d3 calculations from React props). // After that, we call our sent of simulation and rendering functions. componentDidMount() { - this.domSvg = d3.select(this.refs.container as any) + this.domSvg = d3.select(this.refs.containerHierarchy as any) this.domHierarchy = this.domSvg.select('#hierarchy') this.domRoots = this.domSvg.select('#roots') @@ -549,10 +578,16 @@ export class ConceptHierarchy extends React.Component { return ( + +
+ +
+
diff --git a/app/src/application/components/d3Blocks/sunburst.less b/app/src/application/components/d3Blocks/sunburst.less index 4f1fbac..35dfcc1 100644 --- a/app/src/application/components/d3Blocks/sunburst.less +++ b/app/src/application/components/d3Blocks/sunburst.less @@ -6,7 +6,7 @@ path { stroke-opacity: 1; } -#breadcrumb { +#breadcrumbs { height: 18px; font-size: 15px; } diff --git a/app/src/application/components/d3Blocks/sunburst.tsx b/app/src/application/components/d3Blocks/sunburst.tsx index 10fd30d..c438ec7 100644 --- a/app/src/application/components/d3Blocks/sunburst.tsx +++ b/app/src/application/components/d3Blocks/sunburst.tsx @@ -54,7 +54,7 @@ export class Sunburst extends React.Component { .attr('height', this.props.dimensions.height) this.domContainer .attr('transform', 'translate(' + this.props.dimensions.width / 2 + ',' + this.props.dimensions.height / 2 + ')') - d3.select('#info') + this.domSvg.select('#info') .attr('transform', 'translate(' + this.props.dimensions.width / 2 + ',' + this.props.dimensions.height / 2 + ')') this.radius = Math.min(this.props.dimensions.width, this.props.dimensions.height) / 2 @@ -119,10 +119,9 @@ export class Sunburst extends React.Component { } handleMouseleave(d: any) { - d3.select('#breadcrumb') + d3.select('#main #breadcrumbs') .style('visibility', 'hidden') - d3.selectAll('path') .transition() .duration(150) @@ -133,18 +132,19 @@ export class Sunburst extends React.Component { } updateBreadcrumbs(nodeArray: any, percentageString: any) { - d3.select('#breadcrumb').style('visibility', '') + d3.select('#main #breadcrumbs').style('visibility', '') - let breads = d3.select('#breadcrumb-list') + let breads = d3.select('#main #breadcrumbs #breadcrumb-list') .selectAll('li') - .data(nodeArray, (d: any) => d.data.name + d.depth) + // .data(nodeArray, (d: any) => d.data.name + d.depth) + .data(nodeArray) breads.exit().remove() breads.enter() .append('li') .append('a') .attr('class', 'pt-breadcrumb') - .html((d) => d.data.name) + .html((d: any) => d.data.name) } buildHierarchy(csv: any) { @@ -216,7 +216,6 @@ export class Sunburst extends React.Component { } componentDidUpdate() { - console.log('update') this.updateAttributes() let text = ` orion-sunburst-redo,120 @@ -238,22 +237,22 @@ export class Sunburst extends React.Component { return (
- diff --git a/app/src/application/containers/test.tsx b/app/src/application/containers/test.tsx index 5fa8df5..c0b6309 100644 --- a/app/src/application/containers/test.tsx +++ b/app/src/application/containers/test.tsx @@ -88,9 +88,11 @@ export class Test extends React.Component {
diff --git a/app/src/application/reducers.tsx b/app/src/application/reducers.tsx index f730d9b..b8d383f 100644 --- a/app/src/application/reducers.tsx +++ b/app/src/application/reducers.tsx @@ -27,6 +27,7 @@ const initialAppContainerState: containerState = { const initialTestState: containerState = { containerId: 'test', + concepts: [], loading: 0, } @@ -34,7 +35,9 @@ const initialAppState: appState = { conceptGraph: { nodes: [], graph: {}, - selectedConceptNode: null, + selectedRoot: null, + selectedNode: null, + displayedNode: null, displayedSlugs: [], }, containers: { @@ -42,7 +45,7 @@ const initialAppState: appState = { cp1: initialCp1State, test: initialTestState, }, - toggled: false, + toggled: true, } export function reducer(state = initialAppState, action: action): appState { @@ -87,12 +90,32 @@ export function reducer(state = initialAppState, action: action): appState { } } - case 'CHANGE_SELECTED_CONCEPT_NAV': + case 'CHANGE_SELECTED_ROOT_NAV': + return { + ...state, + conceptGraph: { + ...state.conceptGraph, + selectedRoot: action.value, + }, + containers: initialAppState.containers, + } + + case 'CHANGE_SELECTED_NODE_NAV': + return { + ...state, + conceptGraph: { + ...state.conceptGraph, + selectedNode: action.value, + }, + containers: initialAppState.containers, + } + + case 'CHANGE_DISPLAYED_CONCEPT_NAV': return { ...state, conceptGraph: { ...state.conceptGraph, - selectedConceptNode: action.value, + displayedNode: action.value, displayedSlugs: [], }, containers: initialAppState.containers, diff --git a/app/src/application/types.tsx b/app/src/application/types.tsx index 77f40db..68bc402 100644 --- a/app/src/application/types.tsx +++ b/app/src/application/types.tsx @@ -38,7 +38,9 @@ export interface appState { conceptGraph: { nodes: extendedConceptNodeAttribute[], graph?: any, - selectedConceptNode: any, + selectedRoot: any, + selectedNode: any, + displayedNode: any, displayedSlugs: string[], } dispatch?: any, diff --git a/app/src/style.less b/app/src/style.less index 53f8a72..26ee36a 100644 --- a/app/src/style.less +++ b/app/src/style.less @@ -63,6 +63,14 @@ body { .concept_nav_bar { max-width: 600px; margin: 10px auto 10px auto; + + input { + // Prevent text selection when doubleclicking other DOM elements. + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } } .block-1 { From 7c01d97f900f0a81e573b9ba9708b65d9a4d9704 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Thu, 30 Nov 2017 16:35:38 +0100 Subject: [PATCH 11/20] Root nodes alignment bugfix --- .../application/components/d3Blocks/conceptHierarchy.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 2f48b91..7ef6f47 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -319,7 +319,7 @@ export class ConceptHierarchy extends React.Component { this.props.dispatch(actions.changeDisplayedConceptNav(node)) // TODO: associate correct container instead of default cp1 - this.props.dispatch(actions.fetchConcept('concepts/' + node.data.slug, 'test')) + this.props.dispatch(actions.fetchConcept('concepts/' + node.data.slug, 'cp1')) this.props.dispatch(actions.toggleNavPanel()) } @@ -361,7 +361,10 @@ export class ConceptHierarchy extends React.Component { .on('mouseout', _.partial(this.handleMouseOut, this)) .transition() .delay(this.transitionDuration) - .attr('transform', (d: any, index: number) => 'translate(' + this.xFactorRoot * (index - (availableRoots.length) / 2) + ',' + this.yFactorRoot + ')') + .attr('transform', (d: any, index: number) => + 'translate(' + (this.xFactorRoot * (index - (availableRoots.length) / 2) + (this.rectDimensions.rm.r + this.rectDimensions.rm.l) / 2) + + ',' + this.yFactorRoot + ')' + ) .attr('class', (node: any) => { return 'hierarchy-rect' + ((this.selectedNode && node.id == this.selectedNode.id) ? ' selected-rect' : '') + From 4e2bd7473af27259801750afc7867897d4957f38 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Sat, 2 Dec 2017 18:15:31 +0100 Subject: [PATCH 12/20] Add searchbar behavior; CSS cleaning --- .../components/d3Blocks/conceptHierarchy.less | 4 ++ .../components/d3Blocks/conceptHierarchy.tsx | 57 ++++++++--------- .../application/components/utils/NavPanel.tsx | 34 ++++++----- .../components/utils/conceptSearchResults.tsx | 61 +++++++++++++++++++ app/src/application/containers/app.tsx | 1 + app/src/application/containers/test.tsx | 1 + app/src/application/reducers.tsx | 4 +- app/src/application/types.tsx | 1 + app/src/style.less | 23 ++++++- package-lock.json | 5 ++ package.json | 1 + 11 files changed, 144 insertions(+), 48 deletions(-) create mode 100644 app/src/application/components/utils/conceptSearchResults.tsx diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.less b/app/src/application/components/d3Blocks/conceptHierarchy.less index 635f576..85ad199 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.less +++ b/app/src/application/components/d3Blocks/conceptHierarchy.less @@ -1,5 +1,9 @@ @import '~@blueprintjs/core/dist/variables.less'; +svg#conceptHierarchy { + background-color: @dark-gray2; +} + #container { vertical-align: baseline; } diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 7ef6f47..0199e0a 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -90,9 +90,17 @@ export function ConceptHierarchyReducer(nodes: any): any { graph.push(h) } + let graphNodes: any = [] + graph.forEach((rootNode: any) => { + rootNode.each((node: any) => { + graphNodes.push(node) + }) + }) + return { nodes, graph, + graphNodes, } } @@ -104,6 +112,14 @@ export class ConceptHierarchy extends React.Component { // whereas we want to have our simulation ran by d3. width: number height: number + svgDimensions = { + m: { + t: 15, + r: 5, + b: 5, + l: 5, + } + } rectDimensions = { w: 55, h: 30, @@ -209,7 +225,7 @@ export class ConceptHierarchy extends React.Component { this.domSvg.select('#breadcrumbs') .attr('width', this.width) .attr('height', '30px') - .attr('transform', 'translate(20, 20)') + .attr('transform', 'translate(' + 0 + ',' + (this.height - 80) + ')') } updateRenderingAttributes() { @@ -309,13 +325,11 @@ export class ConceptHierarchy extends React.Component { this.selectedNode = node this.props.dispatch(actions.changeSelectedNodeNav(node)) - this.renderAll() } customDoubleClick(node: any) { this.selectedNode = node this.props.dispatch(actions.changeSelectedNodeNav(node)) - this.renderAll() this.props.dispatch(actions.changeDisplayedConceptNav(node)) // TODO: associate correct container instead of default cp1 @@ -399,7 +413,7 @@ export class ConceptHierarchy extends React.Component { }) .attr('class', (node: any) => { return 'hierarchy-rect' + - ((node.id == this.selectedNode.id) ? ' selected-rect' : '') + + ((this.selectedNode && node.id == this.selectedNode.id) ? ' selected-rect' : '') + ((this.selectedNodeAncestors.indexOf(node) > -1) ? ' ancestor-rect' : '') }) } @@ -521,7 +535,6 @@ export class ConceptHierarchy extends React.Component { this.renderBreadcrumbs() } - // Unite all previous rendering functions in just one function. renderD3DomElements() { this.renderAll() } @@ -531,15 +544,8 @@ export class ConceptHierarchy extends React.Component { constructor(props: Props) { super(props) this.state = {selected: null} - - // this.selectNode = this.selectNode.bind(this) } - // This function is called right after the first render() of our component. - // We'll define here our domHierarchy, the forces to use in our d3 simulation - // as well as other attributes which will be useful to d3 and which come from - // React props (remember we want to dissociate d3 calculations from React props). - // After that, we call our sent of simulation and rendering functions. componentDidMount() { this.domSvg = d3.select(this.refs.containerHierarchy as any) this.domHierarchy = this.domSvg.select('#hierarchy') @@ -553,24 +559,6 @@ export class ConceptHierarchy extends React.Component { this.renderD3DomElements() } - // This function is called by React every time the React state or props - // of this component change. In general, we don't want our component to rerender: - // indeed, all DOM elements already exist and their interactions with the DOM are - // handled by d3. - // The only case in which one should indeed rerender this component is when - // new data comes in. This we check keeping track of the version of our data - // (the version comes as a prop, given by the parent component). - shouldComponentUpdate(nextProps: Props, nextState: State) { - if (nextProps.version === this.props.version) { - return false - } - - return true - } - - // This function is called right after the component actually rendered again. Therefore, - // at this stage, only the container is created. One thus updates the simulation - // and re-renders all possible DOM components which will populate the container. componentDidUpdate() { this.updateAttributes() this.renderD3DomElements() @@ -581,9 +569,16 @@ export class ConceptHierarchy extends React.Component { return (
diff --git a/app/src/application/components/utils/NavPanel.tsx b/app/src/application/components/utils/NavPanel.tsx index 5a08492..cc540c0 100644 --- a/app/src/application/components/utils/NavPanel.tsx +++ b/app/src/application/components/utils/NavPanel.tsx @@ -9,6 +9,7 @@ import { import {ConceptGraph} from '../d3Blocks/conceptGraph' import {ConceptHierarchy} from '../d3Blocks/conceptHierarchy' import {ConceptNav} from '../d3Blocks/conceptNav' +import {ConceptSearchResults} from './conceptSearchResults' import * as actions from '../../actions' import { concept_nodesAttribute, @@ -24,6 +25,7 @@ import { interface Props { nodes: any, graph: any, + graphNodes: any, selectedRoot: any, selectedNode: any, dispatch: any, @@ -145,7 +147,7 @@ export class NavPanel extends React.Component { } render() { - const {nodes, graph, selectedRoot, selectedNode, toggled, displayedNode, displayedSlugs} = this.props + const {nodes, graph, graphNodes, selectedRoot, selectedNode, toggled, displayedNode, displayedSlugs} = this.props const {searchedConcept} = this.state const length = searchedConcept ? searchedConcept.length : 0 const toggleButtonTextIcon = (toggled) ? @@ -172,21 +174,10 @@ export class NavPanel extends React.Component { className={'pt-minimal'} onClick={() => this.props.dispatch(actions.toggleNavPanel())} > - Toggle + Panneau de Recherche {toggleButtonTextIcon} -
- - -
-
{
+
+ + +
+ { + handleClick(event: any) { + let nodeId = event.target.dataset.id + let clickedNode = _.find(this.props.graphNodes, (node: any) => node.data.id == nodeId) + let clickedRootNode = clickedNode.ancestors().reverse()[0] + console.log(clickedNode) + console.log(clickedRootNode) + + this.props.dispatch(actions.changeSelectedRootNav(clickedRootNode)) + this.props.dispatch(actions.changeSelectedNodeNav(clickedNode)) + } + + render() { + const {graphNodes} = this.props; + + const selectedNodes: any = [] + if (this.props.searchedConcept && this.props.searchedConcept.length > 0) { + graphNodes.forEach((node: any) => { + if (node.data.slug.indexOf(slug(this.props.searchedConcept, {lower: true})) != -1) { + selectedNodes.push(node) + } + }) + } + + let selectedNodesLi = _.map(selectedNodes, (node: any) => { + return
  • +
      + {_.map(node.ancestors().reverse(), (ancestor: any) => +
    • + {ancestor.data.name} +
    • + )} +
    +
  • + }) + + return ( +
    +
      + {selectedNodesLi} +
    +
    + ) + } +} diff --git a/app/src/application/containers/app.tsx b/app/src/application/containers/app.tsx index 0c84602..b8edf1b 100644 --- a/app/src/application/containers/app.tsx +++ b/app/src/application/containers/app.tsx @@ -80,6 +80,7 @@ export class App extends React.Component { { ul { + list-style-type: none; + margin: 0; + padding: 0; + + > li { + background-color: @dark-gray2; + padding: 10px 20px 10px 20px; + margin: 5px 5px 5px 5px; + } + + > li:hover { + background-color: @dark-gray3; + cursor: pointer; + } + + > li * { + pointer-events: none; + } +} + .pt-dark .pt-navbar, .pt-navbar { background-color: @dark-gray1; box-shadow: none; @@ -61,7 +82,7 @@ body { } .concept_nav_bar { - max-width: 600px; + max-width: 800px; margin: 10px auto 10px auto; input { diff --git a/package-lock.json b/package-lock.json index f07ba4a..3d9e358 100644 --- a/package-lock.json +++ b/package-lock.json @@ -146,6 +146,11 @@ "@types/mime": "2.0.0" } }, + "@types/slug": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@types/slug/-/slug-0.9.0.tgz", + "integrity": "sha1-jYurp+SsvqLRmeje5mMwg+qJ9/g=" + }, "@types/superagent": { "version": "3.5.6", "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-3.5.6.tgz", diff --git a/package.json b/package.json index 507a7e1..b4e19ba 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@types/gulp": "^4.0.4", "@types/mocha": "^2.2.43", "@types/sequelize": "^4.0.69", + "@types/slug": "^0.9.0", "@types/supertest": "^2.0.3", "body-parser": "^1.17.2", "chai": "^4.1.2", From 9e55f3d865cba89d4cdeac3193daa465ee663dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Cant=C3=A9rot?= Date: Sun, 3 Dec 2017 00:35:05 +0100 Subject: [PATCH 13/20] link is no longer a valid endpoint hence test has been removed --- test/types.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/types.ts b/test/types.ts index 0851fa9..4cff335 100644 --- a/test/types.ts +++ b/test/types.ts @@ -32,15 +32,6 @@ describe('API RETURN TYPES', () => { }) }) - it('End-point concepts/ should return correctly typed links', () => { - return request(server) - .get('/concepts') - .expect((response: Response) => { - let data = JSON.parse(JSON.stringify(response.body)) - assert(data.links.filter((link: any) => isConceptLinksAttributes(link)).length == data.links.length, "Returned JSON is of wrong type, check integrity in type guard function isConceptLinksAttributes") - }) - }) - it('Test each slug integrity', async () => { let r = await request(server) .get('/concepts') From d73493188c81ced796f580fdd033bcfdfd7e7d66 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Mon, 4 Dec 2017 16:46:15 +0100 Subject: [PATCH 14/20] Generic attribute fetching bugfix --- .../components/d3Blocks/conceptHierarchy.less | 1 + .../components/d3Blocks/conceptHierarchy.tsx | 33 ++++++++++++------- .../components/d3Blocks/scatter.tsx | 2 +- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.less b/app/src/application/components/d3Blocks/conceptHierarchy.less index 85ad199..2f1cba7 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.less +++ b/app/src/application/components/d3Blocks/conceptHierarchy.less @@ -56,6 +56,7 @@ rect { #tooltip { pointer-events: none; + text { -webkit-user-select: none; -moz-user-select: none; diff --git a/app/src/application/components/d3Blocks/conceptHierarchy.tsx b/app/src/application/components/d3Blocks/conceptHierarchy.tsx index 0199e0a..0082337 100644 --- a/app/src/application/components/d3Blocks/conceptHierarchy.tsx +++ b/app/src/application/components/d3Blocks/conceptHierarchy.tsx @@ -220,7 +220,7 @@ export class ConceptHierarchy extends React.Component { this.domSvg.select('#tooltip_container') .attr('transform', 'translate(' + this.width / 2 + ',' + this.height / 2 + ')') - .style('visibility', 'hidden') + .style('opacity', 0) this.domSvg.select('#breadcrumbs') .attr('width', this.width) @@ -261,6 +261,16 @@ export class ConceptHierarchy extends React.Component { } } + parseTranslateTransformation(inputText: string) { + let r = inputText.split('(') + if (r.length > 0) { + r = r[1].split(',') + return _.map(r, (t: string) => parseFloat(t)) + } + + return null + } + handleMouseOver(that: any, d: any) { // Place the tooltip in the correct container let domParentId = d3.select(this as any).node().parentNode.id @@ -268,18 +278,19 @@ export class ConceptHierarchy extends React.Component { if (domParentId == 'hierarchy') { that.domSvg.select('#tooltip_container') .attr('transform', 'translate(' + that.width / 2 + ',' + that.height / 2 + ')') - .style('visibility', 'hidden') + .style('opacity', 0) } else if (domParentId == 'roots') { that.domSvg.select('#tooltip_container') .attr('transform', 'translate(' + that.width / 2 + ', 50)') - .style('visibility', 'hidden') + .style('opacity', 0) } let tag = this as any - let attribute = tag.transform.baseVal[0].matrix + let attribute = d3.select(tag).attr('transform') + let translate = that.parseTranslateTransformation(attribute) that.domSvg.select('#tooltip_container') - .style('visibility', '') + .style('opacity', 1) that.domSvg.select('#tooltip_content') .text(d.data.name) @@ -287,8 +298,8 @@ export class ConceptHierarchy extends React.Component { that.domSvg.select('#tooltip') .attr('transform', () => { - let xShift = attribute.e + that.rectDimensions.w / 2 - let yShift = attribute.f - d3.selectAll('#tooltip_content tspan').size() * that.tooltipDimensions.h - that.tooltipDimensions.p.b - that.tooltipDimensions.t.h - 4 + let xShift = translate[0] + that.rectDimensions.w / 2 + let yShift = translate[1] - d3.selectAll('#tooltip_content tspan').size() * that.tooltipDimensions.h - that.tooltipDimensions.p.b - that.tooltipDimensions.t.h - 4 return 'translate(' + xShift + ',' + yShift +')' }) @@ -310,7 +321,7 @@ export class ConceptHierarchy extends React.Component { handleMouseOut(that: any, d: any) { that.domSvg.select('#tooltip_container') - .style('visibility', 'hidden') + .style('opacity', 0) } customClick(newRootNode: any, node: any) { @@ -321,7 +332,7 @@ export class ConceptHierarchy extends React.Component { } this.domSvg.select('#tooltip_container') - .style('visibility', 'hidden') + .style('opacity', 0) this.selectedNode = node this.props.dispatch(actions.changeSelectedNodeNav(node)) @@ -366,13 +377,13 @@ export class ConceptHierarchy extends React.Component { .append('rect') .attr('width', this.rectDimensions.w) .attr('height', this.rectDimensions.h) + .on('mouseover', _.partial(this.handleMouseOver, this)) + .on('mouseout', _.partial(this.handleMouseOut, this)) .merge(this.domRootRects) .call(this.interceptClickHandlerRoot .on('customClick', this.customClick.bind(this, true)) .on('customDoubleClick', this.customDoubleClick.bind(this)) ) - .on('mouseover', _.partial(this.handleMouseOver, this)) - .on('mouseout', _.partial(this.handleMouseOut, this)) .transition() .delay(this.transitionDuration) .attr('transform', (d: any, index: number) => diff --git a/app/src/application/components/d3Blocks/scatter.tsx b/app/src/application/components/d3Blocks/scatter.tsx index e7dffe6..cd89ac5 100644 --- a/app/src/application/components/d3Blocks/scatter.tsx +++ b/app/src/application/components/d3Blocks/scatter.tsx @@ -189,7 +189,7 @@ export class Scatter extends React.Component { .translateExtent([[0, 0], [this.lineDimensions.width, this.lineDimensions.height]]) .extent([[0, 0], [this.lineDimensions.width, this.lineDimensions.height]]) .on('zoom', this.zoomed.bind(this)) - + this.domAxes = this.domContainer .attr('width', this.lineDimensions.width + this.padding.left + this.padding.right) .attr('height', this.lineDimensions.height + this.padding.top + this.padding.bottom) From 29398c9986d846e123d06db8007d360e290a2962 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Fri, 8 Dec 2017 15:52:13 +0100 Subject: [PATCH 15/20] Remove unselectable behavior for inputs (bug on Safari) --- app/src/style.less | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/src/style.less b/app/src/style.less index 35f121e..46b823f 100644 --- a/app/src/style.less +++ b/app/src/style.less @@ -84,14 +84,6 @@ body { .concept_nav_bar { max-width: 800px; margin: 10px auto 10px auto; - - input { - // Prevent text selection when doubleclicking other DOM elements. - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - } } .block-1 { From 0a1ef31b874bcdb7ebafd8d12b060359943a9a0d Mon Sep 17 00:00:00 2001 From: alexis-thual Date: Fri, 8 Dec 2017 15:30:49 +0000 Subject: [PATCH 16/20] Updating secret files tracking --- app/config/secret/app.json | 5 - app/package-lock.json | 806 +------------------------------------ package-lock.json | 18 +- 3 files changed, 14 insertions(+), 815 deletions(-) delete mode 100644 app/config/secret/app.json diff --git a/app/config/secret/app.json b/app/config/secret/app.json deleted file mode 100644 index f9a3b96..0000000 --- a/app/config/secret/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "production": { - "serverURL": "http://localhost:3001/" - } -} diff --git a/app/package-lock.json b/app/package-lock.json index 1171afc..ae4b615 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1235,7 +1235,6 @@ "requires": { "anymatch": "1.3.2", "async-each": "1.0.1", - "fsevents": "1.1.3", "glob-parent": "2.0.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -2753,795 +2752,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", - "optional": true, - "requires": { - "nan": "2.8.0", - "node-pre-gyp": "0.6.39" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true - }, - "aproba": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "bundled": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "bundled": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "bundled": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.4", - "bundled": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "bundled": true - }, - "mime-types": { - "version": "2.1.15", - "bundled": true, - "requires": { - "mime-db": "1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.39", - "bundled": true, - "optional": true, - "requires": { - "detect-libc": "1.0.2", - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "bundled": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "bundled": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "bundled": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "bundled": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "bundled": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "bundled": true - }, - "semver": { - "version": "5.3.0", - "bundled": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "optional": true - } - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true - }, - "uuid": { - "version": "3.0.1", - "bundled": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "bundled": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true - } - } - }, "function-bind": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", @@ -5040,12 +4250,6 @@ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, - "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", - "optional": true - }, "nanomatch": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.0.tgz", @@ -7570,6 +6774,11 @@ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -7599,11 +6808,6 @@ } } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", diff --git a/package-lock.json b/package-lock.json index 3d9e358..9794bbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4091,7 +4091,7 @@ "eslint": "4.8.0", "graceful-fs-extra": "2.0.0", "mkdirp": "0.5.1", - "sequelize": "3.31.0", + "sequelize": "3.31.1", "yargs": "8.0.2" }, "dependencies": { @@ -4116,9 +4116,9 @@ "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" }, "sequelize": { - "version": "3.31.0", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-3.31.0.tgz", - "integrity": "sha1-1nHW4Z+Uui+mvxJqU9yDcWaA1pI=", + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-3.31.1.tgz", + "integrity": "sha512-fRb2cu3d+A7wwBuHmfe+SP5nhkQ9pDS3nR/KBmjRK0EpBYRJmNggOqfEShuBgX+QztjXLFiemnT0AtjykfVUGw==", "requires": { "bluebird": "3.5.1", "depd": "1.1.1", @@ -4297,6 +4297,11 @@ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -4321,11 +4326,6 @@ } } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", From 4b1c00a665c5da41cdc4558cf875f7b2a136d047 Mon Sep 17 00:00:00 2001 From: alexis Date: Fri, 8 Dec 2017 17:11:04 +0100 Subject: [PATCH 17/20] Rename navbar.tsx to navBar.tsx --- app/src/application/components/utils/{navbar.tsx => navBar.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/application/components/utils/{navbar.tsx => navBar.tsx} (100%) diff --git a/app/src/application/components/utils/navbar.tsx b/app/src/application/components/utils/navBar.tsx similarity index 100% rename from app/src/application/components/utils/navbar.tsx rename to app/src/application/components/utils/navBar.tsx From 34592212ce4e30efd701539f058c578c10932929 Mon Sep 17 00:00:00 2001 From: alexis Date: Fri, 8 Dec 2017 17:11:21 +0100 Subject: [PATCH 18/20] Rename NavPanel.tsx to navPanel.tsx --- .../application/components/utils/{NavPanel.tsx => navPanel.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/application/components/utils/{NavPanel.tsx => navPanel.tsx} (100%) diff --git a/app/src/application/components/utils/NavPanel.tsx b/app/src/application/components/utils/navPanel.tsx similarity index 100% rename from app/src/application/components/utils/NavPanel.tsx rename to app/src/application/components/utils/navPanel.tsx From 342c3d921b091c819362dabf8bc067c888bb8481 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Sat, 9 Dec 2017 18:24:51 +0100 Subject: [PATCH 19/20] Bundle size optimisation --- app/package-lock.json | 934 ++++++++++++++++-- app/package.json | 6 +- .../components/modules/labelized_chart.tsx | 70 -- .../components/utils/conceptBar.tsx | 2 +- .../containers/conceptsPresentation.tsx | 6 - app/src/application/reducers.tsx | 3 - app/webpack.config.js | 23 +- data/concepts/budget.js | 3 - data/concepts/pib.js | 4 - data/schema.js | 1 - server/src/backends/concepts/backend.ts | 20 - 11 files changed, 899 insertions(+), 173 deletions(-) delete mode 100644 app/src/application/components/modules/labelized_chart.tsx diff --git a/app/package-lock.json b/app/package-lock.json index ae4b615..404b505 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -19,14 +19,6 @@ "tslib": "1.7.1" } }, - "@types/chart.js": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.6.2.tgz", - "integrity": "sha512-Hze3pXv7mFkqtrQ7y3xN6Y3EbiUjFuyDqKBeJjQS8rlC5Ox3nH8YYNzfdhQTP74X9lRxH9pbfr05hIM5a+r+pQ==", - "requires": { - "@types/jquery": "3.2.11" - } - }, "@types/csv-parse": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/@types/csv-parse/-/csv-parse-1.1.11.tgz", @@ -268,11 +260,6 @@ "resolved": "https://registry.npmjs.org/@types/history/-/history-4.6.0.tgz", "integrity": "sha512-2A0stT6b61DANLErAfSkeQ77N+A3FbR7ardUJUP3xm9f4W8qtG9ispBYDUX42Fl1EbR0rqSV3IWjbB6ew7hXRw==" }, - "@types/jquery": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.2.11.tgz", - "integrity": "sha512-Hu1JquYz58pcLLJU4AWvtvo0Yq5HbEHGGJV/XlTfJtePxbpT1mzfGr6FlH08wTi/ClR2yUb21/P/lgoH+LrRow==" - }, "@types/lodash": { "version": "4.14.73", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.73.tgz", @@ -576,6 +563,12 @@ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1202,32 +1195,6 @@ "supports-color": "2.0.0" } }, - "chart.js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.6.0.tgz", - "integrity": "sha1-MI+aSwv+1aFUwU9d6x2UcNIqvnE=", - "requires": { - "chartjs-color": "2.1.0", - "moment": "2.18.1" - } - }, - "chartjs-color": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.1.0.tgz", - "integrity": "sha1-nDmsgwzNmJlq6AyfEQhv8SyYp1Y=", - "requires": { - "chartjs-color-string": "0.4.0", - "color-convert": "0.5.3" - } - }, - "chartjs-color-string": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.4.0.tgz", - "integrity": "sha1-V3SNRTCuKNjbClSSGCugbf3y9Gg=", - "requires": { - "color-name": "1.1.3" - } - }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -1235,6 +1202,7 @@ "requires": { "anymatch": "1.3.2", "async-each": "1.0.1", + "fsevents": "1.1.3", "glob-parent": "2.0.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -1383,11 +1351,6 @@ } } }, - "color-convert": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", - "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" - }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", @@ -2218,6 +2181,12 @@ "domelementtype": "1.3.0" } }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, "duplexify": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.1.tgz", @@ -2243,6 +2212,12 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "ejs": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", + "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=", + "dev": true + }, "electron-to-chromium": { "version": "1.3.18", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.18.tgz", @@ -2621,6 +2596,12 @@ "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" }, + "filesize": { + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.11.tgz", + "integrity": "sha512-ZH7loueKBoDb7yG9esn1U+fgq7BzlzW6NRi5/rMdxIZ05dj7GFD/Xc5rq2CDt5Yq86CyfSYVyx4242QQNZbx1g==", + "dev": true + }, "fill-range": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", @@ -2752,6 +2733,795 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", + "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "optional": true, + "requires": { + "nan": "2.8.0", + "node-pre-gyp": "0.6.39" + }, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.2.9" + } + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "bundled": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "optional": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.0" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "optional": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true, + "requires": { + "mime-db": "1.27.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.39", + "bundled": true, + "optional": true, + "requires": { + "detect-libc": "1.0.2", + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.0", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "bundled": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.0.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" + } + }, + "rimraf": { + "version": "2.6.1", + "bundled": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "optional": true, + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.2.9", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "optional": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + } + } + }, "function-bind": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", @@ -2853,6 +3623,15 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, + "gzip-size": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", + "integrity": "sha1-VGGI6b3DN/Zzdy+BZgRks4nc5SA=", + "dev": true, + "requires": { + "duplexer": "0.1.1" + } + }, "hammerjs": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", @@ -4250,6 +5029,12 @@ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, + "nan": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", + "optional": true + }, "nanomatch": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.0.tgz", @@ -4555,6 +5340,12 @@ "wrappy": "1.0.2" } }, + "opener": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz", + "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=", + "dev": true + }, "opn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", @@ -5538,15 +6329,6 @@ "react-transition-group": "1.2.0" } }, - "react-chartjs-2": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-2.6.2.tgz", - "integrity": "sha512-9tyl0lFKJw3o9b6o1zFgwMN2D5jhQvt+bs6vQTEFvdqi4LDM2m2LiNgbCdPgnrwYslfZ40F6/yR2I6nREEt8tQ==", - "requires": { - "lodash": "4.17.4", - "prop-types": "15.5.10" - } - }, "react-d3-graph": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/react-d3-graph/-/react-d3-graph-0.2.1.tgz", @@ -6774,11 +7556,6 @@ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -6808,6 +7585,11 @@ } } }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", @@ -7132,6 +7914,12 @@ } } }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, "union-value": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/union-value/-/union-value-0.2.4.tgz", @@ -7505,6 +8293,25 @@ } } }, + "webpack-bundle-analyzer": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.9.1.tgz", + "integrity": "sha512-a+UcvlsXvCmclNgfThT8PVyuJKd029By7CxkYEbNNCfs0Lqj9gagjkdv3S3MBvCIKBaUGYs8l4UpiVI0bFoh2Q==", + "dev": true, + "requires": { + "acorn": "5.1.1", + "chalk": "1.1.3", + "commander": "2.11.0", + "ejs": "2.5.7", + "express": "4.15.4", + "filesize": "3.5.11", + "gzip-size": "3.0.0", + "lodash": "4.17.4", + "mkdirp": "0.5.1", + "opener": "1.4.3", + "ws": "3.3.2" + } + }, "webpack-dev-middleware": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.0.tgz", @@ -7803,6 +8610,17 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "ws": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.2.tgz", + "integrity": "sha512-t+WGpsNxhMR4v6EClXS8r8km5ZljKJzyGhJf7goJz9k5Ye3+b5Bvno5rjqPuIBn5mnn5GBb7o8IrIWHxX1qOLQ==", + "dev": true, + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.1", + "ultron": "1.1.1" + } + }, "xml-char-classes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", diff --git a/app/package.json b/app/package.json index 5302270..b766307 100644 --- a/app/package.json +++ b/app/package.json @@ -7,7 +7,6 @@ }, "dependencies": { "@blueprintjs/core": "^1.25.0", - "@types/chart.js": "^2.6.2", "@types/csv-parse": "^1.1.11", "@types/d3": "^4.10.0", "@types/lodash": "^4.14.71", @@ -24,7 +23,6 @@ "@types/redux-logger": "^3.0.0", "@types/webpack-env": "^1.13.2", "awesome-typescript-loader": "^3.2.2", - "chart.js": "^2.6.0", "classnames": "^2.2.5", "css-loader": "^0.28.4", "csv-parse": "^2.0.0", @@ -39,7 +37,6 @@ "morgan": "^1.8.2", "react": "^15.6.1", "react-addons-css-transition-group": "^15.6.0", - "react-chartjs-2": "^2.6.2", "react-d3-graph": "^0.2.1", "react-dom": "^15.6.1", "react-graph-vis": "^1.0.1", @@ -57,5 +54,8 @@ "webpack": "^3.4.1", "webpack-dev-server": "^2.6.1", "whatwg-fetch": "^2.0.3" + }, + "devDependencies": { + "webpack-bundle-analyzer": "^2.9.1" } } diff --git a/app/src/application/components/modules/labelized_chart.tsx b/app/src/application/components/modules/labelized_chart.tsx deleted file mode 100644 index 76c1e11..0000000 --- a/app/src/application/components/modules/labelized_chart.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import * as _ from 'lodash' -import * as React from 'react' -import {Doughnut} from 'react-chartjs-2' - -import { - module, -} from '../../types' - -interface Props { - title?: string, - data: any, - options?: any, -} - -const colorScheme = ["#2965CC", "#29A634", "#D99E0B", "#D13913", "#8F398F", "#00B3A4", "#DB2C6F", "#9BBF30", "#96622D", "#7157D9"] - -export function LabelizedValuesReducer(m: module): any { - let i = 0 - let colors: string[] = [] - let labels: string[] = [] - let data: number[] = [] - - let datasets = _.map(m.data, (dataset: any, key: string) => { - _.each(_.orderBy(dataset.values, ['value'], ['desc']), (value: any) => { - i++ - labels.push(value.label) - colors.push(colorScheme[i % colorScheme.length]) - data.push(value.value) - }) - - return { - data: data, - backgroundColor: colors, - label: key, - borderWidth: 1, - } - }) - - return { - concept : { - ...m, - options: { - ...m.options, - colors, - }, - }, - newDataset : { - datasets, - labels, - }, - } -} - -export class DoughnutChart extends React.Component { - render () { - var {title, data, options} = this.props - - return ( -
    - -
    - ) - } -} diff --git a/app/src/application/components/utils/conceptBar.tsx b/app/src/application/components/utils/conceptBar.tsx index 458e16f..64c727e 100644 --- a/app/src/application/components/utils/conceptBar.tsx +++ b/app/src/application/components/utils/conceptBar.tsx @@ -1,5 +1,5 @@ -import * as React from 'react' import {Breadcrumb} from '@blueprintjs/core' +import * as React from 'react' import {concept} from '../../types' diff --git a/app/src/application/containers/conceptsPresentation.tsx b/app/src/application/containers/conceptsPresentation.tsx index 806afac..4c44fb0 100644 --- a/app/src/application/containers/conceptsPresentation.tsx +++ b/app/src/application/containers/conceptsPresentation.tsx @@ -17,7 +17,6 @@ import * as actions from '../actions' import {TimeseriesChart} from '../components/modules/timeseries_chart' import {HistogramChart} from '../components/modules/histogram_chart' -import {DoughnutChart} from '../components/modules/labelized_chart' import {Definition} from '../components/modules/definition' import {Suggestion} from '../components/modules/suggestion' @@ -87,11 +86,6 @@ export class ConceptsPresentation extends React.Component { this.props.dispatch(actions.fetchConcept('concepts/' + slug, this.props.containerId)) }} /> - case 'doughnut': - return default: return
    {m.type}
    } diff --git a/app/src/application/reducers.tsx b/app/src/application/reducers.tsx index 350cd81..6581c94 100644 --- a/app/src/application/reducers.tsx +++ b/app/src/application/reducers.tsx @@ -10,7 +10,6 @@ import { } from './types' import {TimeseriesValuesReducer} from './components/modules/timeseries_chart' -import {LabelizedValuesReducer} from './components/modules/labelized_chart' import {navPanelReducer} from './components/utils/navPanel' import {ConceptHierarchyReducer} from './components/d3Blocks/conceptHierarchy' @@ -160,8 +159,6 @@ export function reducer(state = initialAppState, action: action): appState { } case 'timeseries': return TimeseriesValuesReducer(m) - case 'labelizedvalues': - return LabelizedValuesReducer(m) default: return m } diff --git a/app/webpack.config.js b/app/webpack.config.js index 0a28d35..9e09d20 100644 --- a/app/webpack.config.js +++ b/app/webpack.config.js @@ -2,7 +2,8 @@ var webpack = require('webpack'), path = require('path'), htmlPlugin = require('html-webpack-plugin'), extractTextWebpackPlugin = require('extract-text-webpack-plugin'), - UglifyJSPlugin = require('uglifyjs-webpack-plugin') + UglifyJSPlugin = require('uglifyjs-webpack-plugin'), + BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin var SRC_DIR = path.join(__dirname, './src') @@ -16,15 +17,20 @@ module.exports = { entry: { main: './application/main.tsx', bundleLibraries: [ - 'chart.js', + '@blueprintjs/core', + 'csv-parse', 'd3', 'lodash', + 'moment', 'react', 'react-dom', + 'react-measure', 'react-redux', 'react-router', + 'react-router-dom', 'redux', - '@blueprintjs/core' + 'redux-logger', + 'slug', ] }, devtool: 'source-map', @@ -55,9 +61,18 @@ module.exports = { } ], }, - plugins: ((process.env.NODE_ENV == 'production') ? [new UglifyJSPlugin()] : []).concat([ + plugins: ( + (process.env.NODE_ENV == 'production') ? + [new UglifyJSPlugin()] : + [ + // Use when bundle analysis is needed: + // new BundleAnalyzerPlugin() + ] + ).concat([ // Plugin to compile libraries speficied in the entry // into a loadable bundle file. + new webpack.IgnorePlugin(/unicode\/category\/So/, /node_modules/), + new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new webpack.optimize.CommonsChunkPlugin({ name: 'bundleLibraries', filename: 'libraries.bundle.js', diff --git a/data/concepts/budget.js b/data/concepts/budget.js index d4ff60e..f192b76 100644 --- a/data/concepts/budget.js +++ b/data/concepts/budget.js @@ -4,9 +4,6 @@ exports.node = { { type: 'definition', data_identifiers: ['budget'], - },{ - type: 'doughnut', - data_identifiers: ['plf_2017'], } ] } diff --git a/data/concepts/pib.js b/data/concepts/pib.js index 7328579..1156dbc 100644 --- a/data/concepts/pib.js +++ b/data/concepts/pib.js @@ -1,10 +1,6 @@ exports.node = { name: 'PIB (produit intérieur brut)', modules: [ - { - type: 'doughnut', - data_identifiers: ['pib_world_2016'], - } ] } diff --git a/data/schema.js b/data/schema.js index 1a0a17d..f36a3a7 100644 --- a/data/schema.js +++ b/data/schema.js @@ -6,7 +6,6 @@ const DB_CONFIG = require(path.join(__dirname, '../config')).default('database') const possibleModules = [ 'timeseries', 'definition', - 'doughnut' ] const possibleDatasets = [ diff --git a/server/src/backends/concepts/backend.ts b/server/src/backends/concepts/backend.ts index c425538..58232e4 100644 --- a/server/src/backends/concepts/backend.ts +++ b/server/src/backends/concepts/backend.ts @@ -138,26 +138,6 @@ export class ConceptBackend { ...options, }) - data.push({ - info, - values, - }) - break - case 'doughnut': - var info = await Datasets.findOne({ - where: { - name: data_identifier - }, - ...options, - }) - - var values = await LabelizedValues.findAll({ - where: { - dataset: data_identifier - }, - ...options, - }) - data.push({ info, values, From 8facb2c8fc2e70fa21a52bd8d8ed99b9ba9bac02 Mon Sep 17 00:00:00 2001 From: alexisthual Date: Sat, 9 Dec 2017 18:30:37 +0100 Subject: [PATCH 20/20] Change dev dependencies --- app/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/package.json b/app/package.json index b766307..2aabf30 100644 --- a/app/package.json +++ b/app/package.json @@ -52,10 +52,11 @@ "typescript": "^2.4.2", "uglifyjs-webpack-plugin": "^1.0.0-beta.3", "webpack": "^3.4.1", + "webpack-bundle-analyzer": "^2.9.1", "webpack-dev-server": "^2.6.1", "whatwg-fetch": "^2.0.3" }, "devDependencies": { - "webpack-bundle-analyzer": "^2.9.1" + } }