feat(rg): add zoom functions
This commit is contained in:
Vendored
+136
-73
@@ -126,6 +126,42 @@
|
|||||||
return _setPrototypeOf(o, p);
|
return _setPrototypeOf(o, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _objectWithoutPropertiesLoose(source, excluded) {
|
||||||
|
if (source == null) return {};
|
||||||
|
var target = {};
|
||||||
|
var sourceKeys = Object.keys(source);
|
||||||
|
var key, i;
|
||||||
|
|
||||||
|
for (i = 0; i < sourceKeys.length; i++) {
|
||||||
|
key = sourceKeys[i];
|
||||||
|
if (excluded.indexOf(key) >= 0) continue;
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _objectWithoutProperties(source, excluded) {
|
||||||
|
if (source == null) return {};
|
||||||
|
|
||||||
|
var target = _objectWithoutPropertiesLoose(source, excluded);
|
||||||
|
|
||||||
|
var key, i;
|
||||||
|
|
||||||
|
if (Object.getOwnPropertySymbols) {
|
||||||
|
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
|
||||||
|
|
||||||
|
for (i = 0; i < sourceSymbolKeys.length; i++) {
|
||||||
|
key = sourceSymbolKeys[i];
|
||||||
|
if (excluded.indexOf(key) >= 0) continue;
|
||||||
|
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
function _assertThisInitialized(self) {
|
function _assertThisInitialized(self) {
|
||||||
if (self === void 0) {
|
if (self === void 0) {
|
||||||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
||||||
@@ -29832,6 +29868,8 @@
|
|||||||
var UPDATE_SIZE = 'UPDATE_SIZE';
|
var UPDATE_SIZE = 'UPDATE_SIZE';
|
||||||
var INIT_D3 = 'INIT_D3';
|
var INIT_D3 = 'INIT_D3';
|
||||||
var FIT_VIEW = 'FIT_VIEW';
|
var FIT_VIEW = 'FIT_VIEW';
|
||||||
|
var ZOOM_IN = 'ZOOM_IN';
|
||||||
|
var ZOOM_OUT = 'ZOOM_OUT';
|
||||||
var UPDATE_SELECTION = 'UPDATE_SELECTION';
|
var UPDATE_SELECTION = 'UPDATE_SELECTION';
|
||||||
var SET_SELECTION = 'SET_SELECTION';
|
var SET_SELECTION = 'SET_SELECTION';
|
||||||
var SET_NODES_SELECTION = 'SET_NODES_SELECTION';
|
var SET_NODES_SELECTION = 'SET_NODES_SELECTION';
|
||||||
@@ -29893,12 +29931,26 @@
|
|||||||
var k = Math.min(state.width, state.height) / Math.max(bounds.width, bounds.height);
|
var k = Math.min(state.width, state.height) / Math.max(bounds.width, bounds.height);
|
||||||
var boundsCenterX = bounds.x + bounds.width / 2;
|
var boundsCenterX = bounds.x + bounds.width / 2;
|
||||||
var boundsCenterY = bounds.y + bounds.height / 2;
|
var boundsCenterY = bounds.y + bounds.height / 2;
|
||||||
var translate = [state.width / 2 - boundsCenterX * k, state.height / 2 - boundsCenterY * k];
|
var transform = [state.width / 2 - boundsCenterX * k, state.height / 2 - boundsCenterY * k];
|
||||||
var fittedTransform = identity$1.translate(translate[0], translate[1]).scale(k);
|
var fittedTransform = identity$1.translate(transform[0], transform[1]).scale(k);
|
||||||
state.d3Selection.call(state.d3Zoom.transform, fittedTransform);
|
state.d3Selection.call(state.d3Zoom.transform, fittedTransform);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ZOOM_IN:
|
||||||
|
{
|
||||||
|
var _transform = state.transform;
|
||||||
|
state.d3Zoom.scaleTo(state.d3Selection, _transform[2] + 0.2);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ZOOM_OUT:
|
||||||
|
{
|
||||||
|
var _transform2 = state.transform;
|
||||||
|
state.d3Zoom.scaleTo(state.d3Selection, _transform2[2] - 0.2);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
case UPDATE_SELECTION:
|
case UPDATE_SELECTION:
|
||||||
{
|
{
|
||||||
var selectedNodes = getNodesInside(state.nodes, action.payload.selection, state.transform);
|
var selectedNodes = getNodesInside(state.nodes, action.payload.selection, state.transform);
|
||||||
@@ -30020,6 +30072,16 @@
|
|||||||
type: FIT_VIEW
|
type: FIT_VIEW
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
var zoomIn = function zoomIn() {
|
||||||
|
return {
|
||||||
|
type: ZOOM_IN
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var zoomOut = function zoomOut() {
|
||||||
|
return {
|
||||||
|
type: ZOOM_OUT
|
||||||
|
};
|
||||||
|
};
|
||||||
var setSelection = function setSelection(isActive) {
|
var setSelection = function setSelection(isActive) {
|
||||||
return {
|
return {
|
||||||
type: SET_SELECTION,
|
type: SET_SELECTION,
|
||||||
@@ -30059,7 +30121,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
var GraphContext = React.createContext({});
|
var GraphContext = React.createContext({});
|
||||||
|
|
||||||
var Provider = function Provider(props) {
|
var Provider = function Provider(props) {
|
||||||
var onElementClick = props.onElementClick,
|
var onElementClick = props.onElementClick,
|
||||||
children = props.children;
|
children = props.children;
|
||||||
@@ -32709,6 +32770,12 @@
|
|||||||
edges: state.edges,
|
edges: state.edges,
|
||||||
fitView: function fitView$1() {
|
fitView: function fitView$1() {
|
||||||
return dispatch(fitView());
|
return dispatch(fitView());
|
||||||
|
},
|
||||||
|
zoomIn: function zoomIn$1() {
|
||||||
|
return dispatch(zoomIn());
|
||||||
|
},
|
||||||
|
zoomOut: function zoomOut$1() {
|
||||||
|
return dispatch(zoomOut());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -32765,75 +32832,6 @@
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
var Handle = (function (props) {
|
|
||||||
return React__default.createElement("div", _extends({
|
|
||||||
className: "react-graph__handle"
|
|
||||||
}, props));
|
|
||||||
});
|
|
||||||
|
|
||||||
var nodeStyles = {
|
|
||||||
background: '#ff6060',
|
|
||||||
padding: 10,
|
|
||||||
borderRadius: 5,
|
|
||||||
width: 150
|
|
||||||
};
|
|
||||||
var DefaultNode = (function (_ref) {
|
|
||||||
var data = _ref.data,
|
|
||||||
style = _ref.style;
|
|
||||||
return React__default.createElement("div", {
|
|
||||||
style: _objectSpread2({}, nodeStyles, {}, style)
|
|
||||||
}, React__default.createElement(Handle, {
|
|
||||||
style: {
|
|
||||||
top: 0
|
|
||||||
}
|
|
||||||
}), data.label, React__default.createElement(Handle, {
|
|
||||||
style: {
|
|
||||||
bottom: 0,
|
|
||||||
top: 'auto',
|
|
||||||
transform: 'translate(-50%, 50%)'
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
var nodeStyles$1 = {
|
|
||||||
background: '#9999ff',
|
|
||||||
padding: 10,
|
|
||||||
borderRadius: 5,
|
|
||||||
width: 150
|
|
||||||
};
|
|
||||||
var InputNode = (function (_ref) {
|
|
||||||
var data = _ref.data,
|
|
||||||
style = _ref.style;
|
|
||||||
return React__default.createElement("div", {
|
|
||||||
style: _objectSpread2({}, nodeStyles$1, {}, style),
|
|
||||||
className: "react-graph__node-inner"
|
|
||||||
}, data.label, React__default.createElement(Handle, {
|
|
||||||
style: {
|
|
||||||
bottom: 0,
|
|
||||||
top: 'auto',
|
|
||||||
transform: 'translate(-50%, 50%)'
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
var nodeStyles$2 = {
|
|
||||||
background: '#55ff99',
|
|
||||||
padding: 10,
|
|
||||||
borderRadius: 5,
|
|
||||||
width: 150
|
|
||||||
};
|
|
||||||
var OutputNode = (function (_ref) {
|
|
||||||
var data = _ref.data,
|
|
||||||
style = _ref.style;
|
|
||||||
return React__default.createElement("div", {
|
|
||||||
style: _objectSpread2({}, nodeStyles$2, {}, style)
|
|
||||||
}, React__default.createElement(Handle, {
|
|
||||||
style: {
|
|
||||||
top: 0
|
|
||||||
}
|
|
||||||
}), data.label);
|
|
||||||
});
|
|
||||||
|
|
||||||
var classnames = createCommonjsModule(function (module) {
|
var classnames = createCommonjsModule(function (module) {
|
||||||
/*!
|
/*!
|
||||||
Copyright (c) 2017 Jed Watson.
|
Copyright (c) 2017 Jed Watson.
|
||||||
@@ -32883,6 +32881,71 @@
|
|||||||
}());
|
}());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var Handle = React.memo(function (_ref) {
|
||||||
|
var input = _ref.input,
|
||||||
|
output = _ref.output,
|
||||||
|
rest = _objectWithoutProperties(_ref, ["input", "output"]);
|
||||||
|
|
||||||
|
var handleClasses = classnames('react-graph__handle', {
|
||||||
|
input: input,
|
||||||
|
output: output
|
||||||
|
});
|
||||||
|
return React__default.createElement("div", _extends({
|
||||||
|
className: handleClasses
|
||||||
|
}, rest));
|
||||||
|
});
|
||||||
|
|
||||||
|
var nodeStyles = {
|
||||||
|
background: '#ff6060',
|
||||||
|
padding: 10,
|
||||||
|
borderRadius: 5,
|
||||||
|
width: 150
|
||||||
|
};
|
||||||
|
var DefaultNode = (function (_ref) {
|
||||||
|
var data = _ref.data,
|
||||||
|
style = _ref.style;
|
||||||
|
return React__default.createElement("div", {
|
||||||
|
style: _objectSpread2({}, nodeStyles, {}, style)
|
||||||
|
}, React__default.createElement(Handle, {
|
||||||
|
input: true
|
||||||
|
}), data.label, React__default.createElement(Handle, {
|
||||||
|
output: true
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
var nodeStyles$1 = {
|
||||||
|
background: '#9999ff',
|
||||||
|
padding: 10,
|
||||||
|
borderRadius: 5,
|
||||||
|
width: 150
|
||||||
|
};
|
||||||
|
var InputNode = (function (_ref) {
|
||||||
|
var data = _ref.data,
|
||||||
|
style = _ref.style;
|
||||||
|
return React__default.createElement("div", {
|
||||||
|
style: _objectSpread2({}, nodeStyles$1, {}, style),
|
||||||
|
className: "react-graph__node-inner"
|
||||||
|
}, data.label, React__default.createElement(Handle, {
|
||||||
|
output: true
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
var nodeStyles$2 = {
|
||||||
|
background: '#55dd99',
|
||||||
|
padding: 10,
|
||||||
|
borderRadius: 5,
|
||||||
|
width: 150
|
||||||
|
};
|
||||||
|
var OutputNode = (function (_ref) {
|
||||||
|
var data = _ref.data,
|
||||||
|
style = _ref.style;
|
||||||
|
return React__default.createElement("div", {
|
||||||
|
style: _objectSpread2({}, nodeStyles$2, {}, style)
|
||||||
|
}, React__default.createElement(Handle, {
|
||||||
|
input: true
|
||||||
|
}), data.label);
|
||||||
|
});
|
||||||
|
|
||||||
var isInputTarget = function isInputTarget(e) {
|
var isInputTarget = function isInputTarget(e) {
|
||||||
return ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName);
|
return ['INPUT', 'SELECT', 'TEXTAREA'].includes(e.target.nodeName);
|
||||||
};
|
};
|
||||||
@@ -33124,7 +33187,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var css = ".react-graph {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n}\n\n.react-graph__renderer {\n width: 100%;\n height: 100%;\n position: absolute;\n}\n\n.react-graph__zoompane {\n width: 100%;\n height: 100%;\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1;\n}\n\n.react-graph__selectionpane {\n width: 100%;\n height: 100%;\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2;\n}\n\n.react-graph__selection {\n position: absolute;\n top: 0;\n left: 0;\n background: rgba(0, 89, 220, 0.08);\n border: 1px dotted rgba(0, 89, 220, 0.8);\n}\n\n.react-graph__edges {\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n z-index: 2;\n}\n\n.react-graph__edge {\n fill: none;\n stroke: #333;\n stroke-width: 2;\n pointer-events: all;\n}\n\n.react-graph__edge.selected {\n stroke: #ff5050;\n }\n\n.react-graph__edge.animated {\n stroke-dasharray: 5;\n -webkit-animation: dashdraw 0.5s linear infinite;\n animation: dashdraw 0.5s linear infinite;\n }\n\n@-webkit-keyframes dashdraw {\n from {stroke-dashoffset: 10}\n}\n\n@keyframes dashdraw {\n from {stroke-dashoffset: 10}\n}\n\n.react-graph__nodes {\n width: 100%;\n height: 100%;\n position: absolute;\n z-index: 3;\n pointer-events: none;\n transform-origin: 0 0;\n}\n\n.react-graph__node {\n position: absolute;\n color: #222;\n font-family: sans-serif;\n font-size: 12px;\n text-align: center;\n cursor: -webkit-grab;\n cursor: grab;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n pointer-events: all;\n transform-origin: 0 0;\n}\n\n.react-graph__node:hover > * {\n box-shadow: 0 1px 5px 2px rgba(0, 0, 0, 0.08);\n }\n\n.react-graph__node.selected > * {\n box-shadow: 0 0 0 2px #000;\n }\n\n.react-graph__handle {\n position: absolute;\n width: 12px;\n height: 12px;\n transform: translate(-50%, -50%);\n background: #222;\n left: 50%;\n border-radius: 50%;\n}\n\n.react-graph__nodesselection {\n z-index: 3;\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n transform-origin: left top;\n pointer-events: none;\n}\n\n.react-graph__nodesselection-rect {\n position: absolute;\n background: rgba(0, 89, 220, 0.08);\n border: 1px dotted rgba(0, 89, 220, 0.8);\n pointer-events: all;\n }";
|
var css = ".react-graph {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n}\n\n.react-graph__renderer {\n width: 100%;\n height: 100%;\n position: absolute;\n}\n\n.react-graph__zoompane {\n width: 100%;\n height: 100%;\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1;\n}\n\n.react-graph__selectionpane {\n width: 100%;\n height: 100%;\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2;\n}\n\n.react-graph__selection {\n position: absolute;\n top: 0;\n left: 0;\n background: rgba(0, 89, 220, 0.08);\n border: 1px dotted rgba(0, 89, 220, 0.8);\n}\n\n.react-graph__edges {\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n z-index: 2;\n}\n\n.react-graph__edge {\n fill: none;\n stroke: #bbb;\n stroke-width: 2;\n pointer-events: all;\n}\n\n.react-graph__edge.selected {\n stroke: #555;\n }\n\n.react-graph__edge.animated {\n stroke-dasharray: 5;\n -webkit-animation: dashdraw 0.5s linear infinite;\n animation: dashdraw 0.5s linear infinite;\n }\n\n@-webkit-keyframes dashdraw {\n from {stroke-dashoffset: 10}\n}\n\n@keyframes dashdraw {\n from {stroke-dashoffset: 10}\n}\n\n.react-graph__nodes {\n width: 100%;\n height: 100%;\n position: absolute;\n z-index: 3;\n pointer-events: none;\n transform-origin: 0 0;\n}\n\n.react-graph__node {\n position: absolute;\n color: #222;\n font-family: sans-serif;\n font-size: 12px;\n text-align: center;\n cursor: -webkit-grab;\n cursor: grab;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n pointer-events: all;\n transform-origin: 0 0;\n}\n\n.react-graph__node:hover > * {\n box-shadow: 0 1px 5px 2px rgba(0, 0, 0, 0.08);\n }\n\n.react-graph__node.selected > * {\n box-shadow: 0 0 0 2px #555;\n }\n\n.react-graph__handle {\n position: absolute;\n width: 10px;\n height: 5px;\n transform: translate(-50%, 0);\n background: rgba(255, 255, 255, 0.4);\n left: 50%;\n top: 0;\n}\n\n.react-graph__handle.output {\n top: auto;\n bottom: 0;\n }\n\n.react-graph__nodesselection {\n z-index: 3;\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n transform-origin: left top;\n pointer-events: none;\n}\n\n.react-graph__nodesselection-rect {\n position: absolute;\n background: rgba(0, 89, 220, 0.08);\n border: 1px dotted rgba(0, 89, 220, 0.8);\n pointer-events: all;\n }";
|
||||||
styleInject(css);
|
styleInject(css);
|
||||||
|
|
||||||
var ReactGraph =
|
var ReactGraph =
|
||||||
|
|||||||
+39
-12
@@ -39,6 +39,7 @@ class App extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
graphLoaded: false,
|
||||||
elements: [
|
elements: [
|
||||||
{ id: '1', type: 'input', data: { label: 'Tests' }, position: { x: 250, y: 5 } },
|
{ id: '1', type: 'input', data: { label: 'Tests' }, position: { x: 250, y: 5 } },
|
||||||
{ id: '2', data: { label: 'This is a node This is a node This is a node This is a node' }, position: { x: 100, y: 100 } },
|
{ id: '2', data: { label: 'This is a node This is a node This is a node This is a node' }, position: { x: 100, y: 100 } },
|
||||||
@@ -59,7 +60,12 @@ class App extends PureComponent {
|
|||||||
this.graphInstance = graphInstance;
|
this.graphInstance = graphInstance;
|
||||||
console.log('graph loaded:', this.graphInstance);
|
console.log('graph loaded:', this.graphInstance);
|
||||||
|
|
||||||
|
window.rg = graphInstance;
|
||||||
this.graphInstance.fitView();
|
this.graphInstance.fitView();
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
graphLoaded: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(elements) {
|
onChange(elements) {
|
||||||
@@ -70,9 +76,6 @@ class App extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onFitView() {
|
onFitView() {
|
||||||
if (!this.graphInstance) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.graphInstance.fitView();
|
this.graphInstance.fitView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +83,21 @@ class App extends PureComponent {
|
|||||||
this.setState(prevState => ({
|
this.setState(prevState => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
elements: prevState.elements.concat({
|
elements: prevState.elements.concat({
|
||||||
data: { id: prevState.elements.length + 1, label: 'Added node' },
|
id: prevState.elements.length + 1,
|
||||||
|
data: { label: 'Added node' },
|
||||||
position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight }
|
position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight }
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onZoomIn() {
|
||||||
|
this.graphInstance.zoomIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
onZoomOut() {
|
||||||
|
this.graphInstance.zoomOut();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Graph
|
<Graph
|
||||||
@@ -101,18 +113,33 @@ class App extends PureComponent {
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
style={{ position: 'absolute', right: '10px', bottom: '10px', zIndex: 4 }}
|
|
||||||
onClick={() => this.onFitView()}
|
|
||||||
>
|
|
||||||
fit
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
style={{ position: 'absolute', bottom: '10px', left: '10px', zIndex: 4 }}
|
|
||||||
onClick={() => this.onAdd()}
|
onClick={() => this.onAdd()}
|
||||||
|
style={{ position: 'absolute', right: '10px', bottom: '10px', zIndex: 4 }}
|
||||||
>
|
>
|
||||||
add
|
add
|
||||||
</button>
|
</button>
|
||||||
|
{this.state.graphLoaded && (
|
||||||
|
<div className="controls">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => this.onFitView()}
|
||||||
|
>
|
||||||
|
fit
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => this.onZoomIn()}
|
||||||
|
>
|
||||||
|
zoom in
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => this.onZoomOut()}
|
||||||
|
>
|
||||||
|
zoom out
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Graph>
|
</Graph>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,12 @@
|
|||||||
html, body, #root {
|
html, body, #root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.controls {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -7,16 +7,6 @@ import { setNodes, setEdges } from '../state/actions';
|
|||||||
|
|
||||||
export const GraphContext = createContext({});
|
export const GraphContext = createContext({});
|
||||||
|
|
||||||
function usePrevious(value) {
|
|
||||||
const ref = useRef();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
ref.current = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
return ref.current;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Provider = (props) => {
|
export const Provider = (props) => {
|
||||||
const {
|
const {
|
||||||
onElementClick,
|
onElementClick,
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ import NodeRenderer from '../NodeRenderer';
|
|||||||
import EdgeRenderer from '../EdgeRenderer';
|
import EdgeRenderer from '../EdgeRenderer';
|
||||||
import UserSelection from '../UserSelection';
|
import UserSelection from '../UserSelection';
|
||||||
import NodesSelection from '../NodesSelection';
|
import NodesSelection from '../NodesSelection';
|
||||||
import { updateTransform, updateSize, initD3, fitView, setNodesSelection } from '../state/actions';
|
import {
|
||||||
|
updateTransform, updateSize, initD3, fitView,
|
||||||
|
zoomIn, zoomOut, setNodesSelection
|
||||||
|
} from '../state/actions';
|
||||||
import { useKeyPress } from '../hooks';
|
import { useKeyPress } from '../hooks';
|
||||||
|
|
||||||
const d3ZoomInstance = d3Zoom.zoom().scaleExtent([0.5, 2]);
|
const d3ZoomInstance = d3Zoom.zoom().scaleExtent([0.5, 2]);
|
||||||
@@ -59,7 +62,9 @@ const GraphView = memo((props) => {
|
|||||||
props.onLoad({
|
props.onLoad({
|
||||||
nodes: state.nodes,
|
nodes: state.nodes,
|
||||||
edges: state.edges,
|
edges: state.edges,
|
||||||
fitView: () => dispatch(fitView())
|
fitView: () => dispatch(fitView()),
|
||||||
|
zoomIn: () => dispatch(zoomIn()),
|
||||||
|
zoomOut: () => dispatch(zoomOut())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [state.d3Initialised]);
|
}, [state.d3Initialised]);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import React from 'react';
|
import React, { memo } from 'react';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
|
||||||
export default (props) => {
|
export default memo(({ input, output, ...rest }) => {
|
||||||
const handleClasses = cx(
|
const handleClasses = cx(
|
||||||
'react-graph__handle', {
|
'react-graph__handle', { input, output }
|
||||||
input: props.input,
|
|
||||||
output: props.output
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={handleClasses} {...props} />
|
<div
|
||||||
|
className={handleClasses}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {
|
|||||||
UPDATE_TRANSFORM, UPDATE_SIZE, SET_NODES, SET_EDGES,
|
UPDATE_TRANSFORM, UPDATE_SIZE, SET_NODES, SET_EDGES,
|
||||||
UPDATE_NODE_DATA, UPDATE_NODE_POS, INIT_D3, FIT_VIEW,
|
UPDATE_NODE_DATA, UPDATE_NODE_POS, INIT_D3, FIT_VIEW,
|
||||||
UPDATE_SELECTION, SET_SELECTION, SET_NODES_SELECTION,
|
UPDATE_SELECTION, SET_SELECTION, SET_NODES_SELECTION,
|
||||||
SET_SELECTED_ELEMENTS, REMOVE_NODES
|
SET_SELECTED_ELEMENTS, REMOVE_NODES, ZOOM_IN, ZOOM_OUT
|
||||||
} from './index';
|
} from './index';
|
||||||
|
|
||||||
export const updateTransform = (transform) => {
|
export const updateTransform = (transform) => {
|
||||||
@@ -58,6 +58,14 @@ export const fitView = () => {
|
|||||||
return { type: FIT_VIEW };
|
return { type: FIT_VIEW };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const zoomIn = () => {
|
||||||
|
return { type: ZOOM_IN };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const zoomOut = () => {
|
||||||
|
return { type: ZOOM_OUT };
|
||||||
|
}
|
||||||
|
|
||||||
export const setSelection = (isActive) => {
|
export const setSelection = (isActive) => {
|
||||||
return { type: SET_SELECTION, payload: { selectionActive: isActive } };
|
return { type: SET_SELECTION, payload: { selectionActive: isActive } };
|
||||||
};
|
};
|
||||||
|
|||||||
+16
-2
@@ -10,6 +10,8 @@ export const UPDATE_TRANSFORM = 'UPDATE_TRANSFORM';
|
|||||||
export const UPDATE_SIZE = 'UPDATE_SIZE';
|
export const UPDATE_SIZE = 'UPDATE_SIZE';
|
||||||
export const INIT_D3 = 'INIT_D3';
|
export const INIT_D3 = 'INIT_D3';
|
||||||
export const FIT_VIEW = 'FIT_VIEW';
|
export const FIT_VIEW = 'FIT_VIEW';
|
||||||
|
export const ZOOM_IN = 'ZOOM_IN';
|
||||||
|
export const ZOOM_OUT = 'ZOOM_OUT';
|
||||||
export const UPDATE_SELECTION = 'UPDATE_SELECTION';
|
export const UPDATE_SELECTION = 'UPDATE_SELECTION';
|
||||||
export const SET_SELECTION = 'SET_SELECTION';
|
export const SET_SELECTION = 'SET_SELECTION';
|
||||||
export const SET_NODES_SELECTION = 'SET_NODES_SELECTION';
|
export const SET_NODES_SELECTION = 'SET_NODES_SELECTION';
|
||||||
@@ -70,13 +72,25 @@ export const reducer = (state, action) => {
|
|||||||
const k = Math.min(state.width, state.height) / Math.max(bounds.width, bounds.height);
|
const k = Math.min(state.width, state.height) / Math.max(bounds.width, bounds.height);
|
||||||
const boundsCenterX = bounds.x + (bounds.width / 2);
|
const boundsCenterX = bounds.x + (bounds.width / 2);
|
||||||
const boundsCenterY = bounds.y + (bounds.height / 2);
|
const boundsCenterY = bounds.y + (bounds.height / 2);
|
||||||
const translate = [(state.width / 2) - (boundsCenterX * k), (state.height / 2) - (boundsCenterY * k)];
|
const transform = [(state.width / 2) - (boundsCenterX * k), (state.height / 2) - (boundsCenterY * k)];
|
||||||
const fittedTransform = zoomIdentity.translate(translate[0], translate[1]).scale(k);
|
const fittedTransform = zoomIdentity.translate(transform[0], transform[1]).scale(k);
|
||||||
|
|
||||||
state.d3Selection.call(state.d3Zoom.transform, fittedTransform);
|
state.d3Selection.call(state.d3Zoom.transform, fittedTransform);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
case ZOOM_IN: {
|
||||||
|
const { transform } = state;
|
||||||
|
state.d3Zoom.scaleTo(state.d3Selection, transform[2] + 0.2);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
case ZOOM_OUT: {
|
||||||
|
const { transform } = state;
|
||||||
|
state.d3Zoom.scaleTo(state.d3Selection, transform[2] - 0.2);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
case UPDATE_SELECTION: {
|
case UPDATE_SELECTION: {
|
||||||
const selectedNodes = getNodesInside(state.nodes, action.payload.selection, state.transform);
|
const selectedNodes = getNodesInside(state.nodes, action.payload.selection, state.transform);
|
||||||
const selectedEdges = getConnectedEdges(selectedNodes, state.edges);
|
const selectedEdges = getConnectedEdges(selectedNodes, state.edges);
|
||||||
|
|||||||
Reference in New Issue
Block a user