diff --git a/Gruntfile.js b/Gruntfile.js index c0ff8f15..41130226 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -86,7 +86,7 @@ connect: { server: { options: { - port: 3000, + port: 3030, hostname: '*', // Allow connection from mobile livereload: true } diff --git a/package.json b/package.json index 70d70b54..d273c258 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@carecloud/the-graph", - "version": "0.12.0", + "version": "0.12.1", "description": "flow-based programming graph editing", "author": "Forrest Oliphant, the Grid", "license": "MIT", @@ -16,6 +16,7 @@ "font-awesome": "^4.6.3", "hammerjs": "^2.0.8", "klayjs-noflo": "^0.3.1", + "phantomjs": "^2.1.7", "tv4": "^1.3.0", "yargs": "^10.0.3" }, diff --git a/the-graph/mixins.js b/the-graph/mixins.js index 1d3ca5f4..961671f1 100644 --- a/the-graph/mixins.js +++ b/the-graph/mixins.js @@ -1,4 +1,5 @@ var ReactDOM = require('react-dom'); +var getEventPosition = require('./the-graph-library').getEventPosition; // React mixins // Show fake tooltip @@ -7,11 +8,13 @@ var Tooltip = { showTooltip: function (event) { if ( !this.shouldShowTooltip() ) { return; } + // Get mouse position + var position = getEventPosition(event); var tooltipEvent = new CustomEvent('the-graph-tooltip', { detail: { tooltip: this.props.label, - x: event.clientX, - y: event.clientY + x: position.x, + y: position.y }, bubbles: true }); diff --git a/the-graph/render.js b/the-graph/render.js index 4deb9f34..294eacdb 100644 --- a/the-graph/render.js +++ b/the-graph/render.js @@ -189,7 +189,6 @@ function renderGraph(graph, options) { graph: graph, library: options.library, }; - //console.log('render', props); var wrapper = document.createElement('div'); wrapper.className = options.theme; diff --git a/the-graph/the-graph-app.js b/the-graph/the-graph-app.js index efd44093..982f8325 100644 --- a/the-graph/the-graph-app.js +++ b/the-graph/the-graph-app.js @@ -204,9 +204,12 @@ module.exports.register = function (context) { } // Safari is wheelDeltaY + var position = TheGraph.library.getEventPosition(event); + this.zoomFactor += event.deltaY ? event.deltaY : 0-event.wheelDeltaY; - this.zoomX = event.clientX; - this.zoomY = event.clientY; + this.zoomX = position.x; + this.zoomY = position.y; + requestAnimationFrame(this.scheduleWheelZoom); }, scheduleWheelZoom: function () { @@ -496,19 +499,19 @@ module.exports.register = function (context) { if (event.preventTap) { event.preventTap(); } // Get mouse position - var x = event.x || event.clientX || 0; - var y = event.y || event.clientY || 0; + var position = TheGraph.library.getEventPosition(event); + if (event.touches && event.touches.length) { - x = event.touches[0].clientX; - y = event.touches[0].clientY; + position.x = event.touches[0].clientX; + position.y = event.touches[0].clientY; } // App.showContext this.showContext({ element: this, type: "main", - x: x, - y: y, + x: position.x, + y: position.y, graph: this.props.graph, itemKey: 'graph', item: this.props.graph diff --git a/the-graph/the-graph-edge.js b/the-graph/the-graph-edge.js index 9d658061..41f783e5 100644 --- a/the-graph/the-graph-edge.js +++ b/the-graph/the-graph-edge.js @@ -114,19 +114,20 @@ module.exports.register = function (context) { if (event.gesture) { event = event.gesture.srcEvent; // unpack hammer.js gesture event } - var x = event.x || event.clientX || 0; - var y = event.y || event.clientY || 0; + + var position = TheGraph.library.getEventPosition(event); + if (event.touches && event.touches.length) { - x = event.touches[0].clientX; - y = event.touches[0].clientY; + position.x = event.touches[0].clientX; + position.y = event.touches[0].clientY; } // App.showContext this.props.showContext({ element: this, type: (this.props.export ? (this.props.isIn ? "graphInport" : "graphOutport") : "edge"), - x: x, - y: y, + x: position.x, + y: position.y, graph: this.props.graph, itemKey: (this.props.export ? this.props.exportKey : null), item: (this.props.export ? this.props.export : this.props.edge) diff --git a/the-graph/the-graph-graph.js b/the-graph/the-graph-graph.js index 803b63b8..19e3d0a4 100644 --- a/the-graph/the-graph-graph.js +++ b/the-graph/the-graph-graph.js @@ -205,19 +205,18 @@ module.exports.register = function (context) { event = event.gesture.srcEvent; // unpack hammer.js gesture event } - var x = event.x || event.clientX || 0; - var y = event.y || event.clientY || 0; + var position = TheGraph.library.getEventPosition(event); + if (event.touches && event.touches.length) { - x = event.touches[0].clientX; - y = event.touches[0].clientY; + position.x = event.touches[0].clientX; + position.y = event.touches[0].clientY; } - x -= this.props.app.state.offsetX || 0; - y -= this.props.app.state.offsetY || 0; + // The scale is used here to adapt the edgePreview position if a scale change occurs (wheel scroll) var scale = this.props.app.state.scale; this.setState({ - edgePreviewX: (x - this.props.app.state.x) / scale, - edgePreviewY: (y - this.props.app.state.y) / scale + edgePreviewX: (position.x - this.props.app.state.x) / scale, + edgePreviewY: (position.y - this.props.app.state.y) / scale }); this.markDirty(); }, diff --git a/the-graph/the-graph-group.js b/the-graph/the-graph-group.js index 9abb151d..684dbf0d 100644 --- a/the-graph/the-graph-group.js +++ b/the-graph/the-graph-group.js @@ -66,19 +66,20 @@ module.exports.register = function (context) { if (event.gesture) { event = event.gesture.srcEvent; // unpack hammer.js gesture event } - var x = event.x || event.clientX || 0; - var y = event.y || event.clientY || 0; + + var position = TheGraph.library.getEventPosition(event); + if (event.touches && event.touches.length) { - x = event.touches[0].clientX; - y = event.touches[0].clientY; + position.x = event.touches[0].clientX; + position.y = event.touches[0].clientY; } // App.showContext this.props.showContext({ element: this, type: (this.props.isSelectionGroup ? "selection" : "group"), - x: x, - y: y, + x: position.x, + y: position.y, graph: this.props.graph, itemKey: this.props.label, item: this.props.item diff --git a/the-graph/the-graph-library.js b/the-graph/the-graph-library.js index 4a0d98ff..03ee95cb 100644 --- a/the-graph/the-graph-library.js +++ b/the-graph/the-graph-library.js @@ -151,8 +151,54 @@ function libraryFromGraph(fbpGraph) { return library; } +/** + * Returns offsets for mouse position. + * + * This function need to calculate correct offset in case the-graph + * used as positioned in not (0, 0) coordinates. + * + * @param initialElement - event target. + * @param upperElement - upper the-graph element (see usages for example). + * @returns {{top: number, left: number}} + */ +function getOffsetUpToElement(initialElement, upperElement) { + var offset = {top: 0, left: 0}; + var offsetParent = initialElement; + + while (offsetParent != null && offsetParent != upperElement) { + offset.left += offsetParent.offsetLeft || 0; + offset.top += offsetParent.offsetTop || 0; + offsetParent = offsetParent.parentElement; + } + + return offset; +} + + +/** + * Returns the position {y: Y, x: X} where the provided event was triggered. + * + * @param event - event triggered from the UI. + * @returns {{y: number, x: number}} + */ +function getEventPosition(event) { + var offset = getOffsetUpToElement(event.currentTarget, event.target); + + // The offset should be applied only to clientX/Y if layerX/Y don't exist. + // TODO: Check if there is another way of doing this without using layerX/Y. + // The use of clientX/Y works in most cases except when there is margin/padding in parent elements + // That affects the value of `offset`. + return { + y: event.layerY || (event.clientY - offset.top) || 0, + x: event.layerX || (event.clientX - offset.left) || 0 + }; +} + + module.exports = { mergeComponentDefinition: mergeComponentDefinition, componentsFromGraph: componentsFromGraph, libraryFromGraph: libraryFromGraph, + getOffsetUpToElement: getOffsetUpToElement, + getEventPosition: getEventPosition, }; diff --git a/the-graph/the-graph-node.js b/the-graph/the-graph-node.js index 680cad78..6fd2539d 100644 --- a/the-graph/the-graph-node.js +++ b/the-graph/the-graph-node.js @@ -232,19 +232,20 @@ module.exports.register = function (context) { if (event.gesture) { event = event.gesture.srcEvent; // unpack hammer.js gesture event } - var x = event.x || event.clientX || 0; - var y = event.y || event.clientY || 0; + + var position = TheGraph.library.getEventPosition(event); + if (event.touches && event.touches.length) { - x = event.touches[0].clientX; - y = event.touches[0].clientY; + position.x = event.touches[0].clientX; + position.y = event.touches[0].clientY; } // App.showContext this.props.showContext({ element: this, type: (this.props.export ? (this.props.isIn ? "graphInport" : "graphOutport") : "node"), - x: x, - y: y, + x: position.x, + y: position.y, graph: this.props.graph, itemKey: (this.props.export ? this.props.exportKey : this.props.nodeID), item: (this.props.export ? this.props.export : this.props.node) diff --git a/the-graph/the-graph-port.js b/the-graph/the-graph-port.js index b9072833..4dc69d81 100644 --- a/the-graph/the-graph-port.js +++ b/the-graph/the-graph-port.js @@ -91,19 +91,20 @@ module.exports.register = function (context) { if (event.gesture) { event = event.gesture.srcEvent; // unpack hammer.js gesture event } - var x = event.x || event.clientX || 0; - var y = event.y || event.clientY || 0; + + var position = TheGraph.library.getEventPosition(event); + if (event.touches && event.touches.length) { - x = event.touches[0].clientX; - y = event.touches[0].clientY; + position.x = event.touches[0].clientX; + position.y = event.touches[0].clientY; } // App.showContext this.props.showContext({ element: this, type: (this.props.isIn ? "nodeInport" : "nodeOutport"), - x: x, - y: y, + x: position.x, + y: position.y, graph: this.props.graph, itemKey: this.props.label, item: this.props.port