Add background context menu & keyboard load-module
actions
This commit is contained in:
parent
143dcce068
commit
a6caad2489
|
@ -28,6 +28,9 @@ class GraphView extends GraphViewBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
|
_super_renderBackground: this.renderBackground,
|
||||||
|
renderBackground: this.constructor.prototype.renderBackground.bind(this),
|
||||||
|
|
||||||
_super_handleNodeMove: this.handleNodeMove,
|
_super_handleNodeMove: this.handleNodeMove,
|
||||||
handleNodeMove: this.constructor.prototype.handleNodeMove.bind(this),
|
handleNodeMove: this.constructor.prototype.handleNodeMove.bind(this),
|
||||||
|
|
||||||
|
@ -136,6 +139,18 @@ class GraphView extends GraphViewBase {
|
||||||
return super.getMouseCoordinates();
|
return super.getMouseCoordinates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderBackground() {
|
||||||
|
const { gridSize, backgroundFillId, renderBackground, onBackgroundMouseDown } = this.props;
|
||||||
|
if (renderBackground) {
|
||||||
|
return renderBackground({
|
||||||
|
gridSize,
|
||||||
|
backgroundFillId,
|
||||||
|
onMouseDown: onBackgroundMouseDown,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this._super_renderBackground();
|
||||||
|
}
|
||||||
|
|
||||||
getNodeComponent(id, node) {
|
getNodeComponent(id, node) {
|
||||||
const { nodeTypes, nodeSubtypes, nodeSize, renderNode, renderNodeText, nodeKey } = this.props;
|
const { nodeTypes, nodeSubtypes, nodeSize, renderNode, renderNodeText, nodeKey } = this.props;
|
||||||
return r(Node, {
|
return r(Node, {
|
||||||
|
|
|
@ -262,6 +262,19 @@ const renderDefs = () => r(React.Fragment, [
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const renderBackground = ({
|
||||||
|
gridSize = 40960,
|
||||||
|
onMouseDown,
|
||||||
|
}) => r.rect({
|
||||||
|
className: 'background',
|
||||||
|
x: -(gridSize || 0) / 4,
|
||||||
|
y: -(gridSize || 0) / 4,
|
||||||
|
width: gridSize,
|
||||||
|
height: gridSize,
|
||||||
|
fill: 'url(#background-pattern)',
|
||||||
|
onMouseDown,
|
||||||
|
});
|
||||||
|
|
||||||
const renderNode = (nodeRef, data, key, selected, hovered) => r({
|
const renderNode = (nodeRef, data, key, selected, hovered) => r({
|
||||||
sink: Sink,
|
sink: Sink,
|
||||||
source: Source,
|
source: Source,
|
||||||
|
@ -562,7 +575,39 @@ const renderEdgeText = state => ({ data: dgo, transform, selected }) => {
|
||||||
|
|
||||||
const layoutEngine = new LayoutEngine();
|
const layoutEngine = new LayoutEngine();
|
||||||
|
|
||||||
class GraphContextMenu extends React.PureComponent {
|
class BackgroundContextMenu extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
return r(PopupMenu, {
|
||||||
|
onClose: this.props.onClose,
|
||||||
|
}, [
|
||||||
|
r(MenuItem, {
|
||||||
|
label: 'Create',
|
||||||
|
}, [
|
||||||
|
r(MenuItem, {
|
||||||
|
label: 'Loopback',
|
||||||
|
onClick: this.props.onLoadModuleLoopback,
|
||||||
|
}),
|
||||||
|
|
||||||
|
r(MenuItem, {
|
||||||
|
label: 'Simultaneous output',
|
||||||
|
onClick: this.props.onLoadModuleCombineSink,
|
||||||
|
}),
|
||||||
|
|
||||||
|
r(MenuItem, {
|
||||||
|
label: 'Null output',
|
||||||
|
onClick: this.props.onLoadModuleNullSink,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
|
||||||
|
r(MenuItem, {
|
||||||
|
label: 'Load a module...',
|
||||||
|
onClick: this.props.onLoadModule,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GraphObjectContextMenu extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
return r(PopupMenu, {
|
return r(PopupMenu, {
|
||||||
onClose: this.props.onClose,
|
onClose: this.props.onClose,
|
||||||
|
@ -575,7 +620,7 @@ class GraphContextMenu extends React.PureComponent {
|
||||||
r(MenuItem.Separator),
|
r(MenuItem.Separator),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
r(MenuItem, {
|
this.props.canDelete() && r(MenuItem, {
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
onClick: this.props.onDelete,
|
onClick: this.props.onDelete,
|
||||||
}),
|
}),
|
||||||
|
@ -583,6 +628,8 @@ class GraphContextMenu extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const backgroundSymbol = Symbol('graph.backgroundSymbol');
|
||||||
|
|
||||||
class Graph extends React.Component {
|
class Graph extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -590,11 +637,14 @@ class Graph extends React.Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
selected: null,
|
selected: null,
|
||||||
moved: null,
|
moved: null,
|
||||||
|
contexted: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
this._requestedIcons = new Set();
|
this._requestedIcons = new Set();
|
||||||
|
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
|
onBackgroundMouseDown: this.onBackgroundMouseDown.bind(this),
|
||||||
|
|
||||||
onSelectNode: this.onSelectNode.bind(this),
|
onSelectNode: this.onSelectNode.bind(this),
|
||||||
onCreateNode: this.onCreateNode.bind(this),
|
onCreateNode: this.onCreateNode.bind(this),
|
||||||
onUpdateNode: this.onUpdateNode.bind(this),
|
onUpdateNode: this.onUpdateNode.bind(this),
|
||||||
|
@ -613,7 +663,12 @@ class Graph extends React.Component {
|
||||||
canContextMenuSetAsDefault: this.canContextMenuSetAsDefault.bind(this),
|
canContextMenuSetAsDefault: this.canContextMenuSetAsDefault.bind(this),
|
||||||
onContextMenuSetAsDefault: this.onContextMenuSetAsDefault.bind(this),
|
onContextMenuSetAsDefault: this.onContextMenuSetAsDefault.bind(this),
|
||||||
|
|
||||||
|
canContextMenuDelete: this.canContextMenuDelete.bind(this),
|
||||||
onContextMenuDelete: this.onContextMenuDelete.bind(this),
|
onContextMenuDelete: this.onContextMenuDelete.bind(this),
|
||||||
|
|
||||||
|
onLoadModuleLoopback: this.onLoadModuleLoopback.bind(this),
|
||||||
|
onLoadModuleCombineSink: this.onLoadModuleCombineSink.bind(this),
|
||||||
|
onLoadModuleNullSink: this.onLoadModuleNullSink.bind(this),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,7 +750,7 @@ class Graph extends React.Component {
|
||||||
|
|
||||||
let { selected, moved, contexted } = state;
|
let { selected, moved, contexted } = state;
|
||||||
|
|
||||||
if (contexted && selected !== contexted) {
|
if (contexted && contexted !== backgroundSymbol && selected !== contexted) {
|
||||||
contexted = null;
|
contexted = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -709,7 +764,7 @@ class Graph extends React.Component {
|
||||||
find(x => x.id === moved.id, edges);
|
find(x => x.id === moved.id, edges);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contexted) {
|
if (contexted && contexted !== backgroundSymbol) {
|
||||||
contexted = find(x => x.id === contexted.id, nodes) ||
|
contexted = find(x => x.id === contexted.id, nodes) ||
|
||||||
find(x => x.id === contexted.id, edges);
|
find(x => x.id === contexted.id, edges);
|
||||||
}
|
}
|
||||||
|
@ -765,6 +820,12 @@ class Graph extends React.Component {
|
||||||
this._requestedIcons.add(icon);
|
this._requestedIcons.add(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBackgroundMouseDown() {
|
||||||
|
this.setState({
|
||||||
|
contexted: backgroundSymbol,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onSelectNode(selected) {
|
onSelectNode(selected) {
|
||||||
this.setState({ selected });
|
this.setState({ selected });
|
||||||
}
|
}
|
||||||
|
@ -916,8 +977,12 @@ class Graph extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canContextMenuDelete() {
|
||||||
|
return this.state.contexted !== backgroundSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
onContextMenuDelete() {
|
onContextMenuDelete() {
|
||||||
this.onDelete(this.state.selected);
|
this.onDelete(this.state.contexted);
|
||||||
}
|
}
|
||||||
|
|
||||||
onContextMenuClose() {
|
onContextMenuClose() {
|
||||||
|
@ -1214,6 +1279,22 @@ class Graph extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hotKeyAdd() {
|
||||||
|
this.props.openNewGraphObjectModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadModuleLoopback() {
|
||||||
|
this.props.loadModule('module-loopback', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadModuleCombineSink() {
|
||||||
|
this.props.loadModule('module-combine-sink', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadModuleNullSink() {
|
||||||
|
this.props.loadModule('module-null-sink', '');
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { nodes, edges } = this.state;
|
const { nodes, edges } = this.state;
|
||||||
|
|
||||||
|
@ -1236,6 +1317,8 @@ class Graph extends React.Component {
|
||||||
nodeSubtypes: {},
|
nodeSubtypes: {},
|
||||||
edgeTypes: {},
|
edgeTypes: {},
|
||||||
|
|
||||||
|
onBackgroundMouseDown: this.onBackgroundMouseDown,
|
||||||
|
|
||||||
onSelectNode: this.onSelectNode,
|
onSelectNode: this.onSelectNode,
|
||||||
onCreateNode: this.onCreateNode,
|
onCreateNode: this.onCreateNode,
|
||||||
onUpdateNode: this.onUpdateNode,
|
onUpdateNode: this.onUpdateNode,
|
||||||
|
@ -1255,7 +1338,7 @@ class Graph extends React.Component {
|
||||||
|
|
||||||
layoutEngine,
|
layoutEngine,
|
||||||
|
|
||||||
backgroundFillId: '#background-pattern',
|
renderBackground,
|
||||||
|
|
||||||
renderDefs,
|
renderDefs,
|
||||||
|
|
||||||
|
@ -1266,14 +1349,27 @@ class Graph extends React.Component {
|
||||||
renderEdgeText: renderEdgeText(this.props),
|
renderEdgeText: renderEdgeText(this.props),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
this.state.contexted && r(GraphContextMenu, {
|
this.state.contexted && (
|
||||||
|
this.state.contexted === backgroundSymbol ?
|
||||||
|
r(BackgroundContextMenu, {
|
||||||
|
onClose: this.onContextMenuClose,
|
||||||
|
|
||||||
|
onLoadModule: this.props.openLoadModuleModal,
|
||||||
|
|
||||||
|
onLoadModuleLoopback: this.onLoadModuleLoopback,
|
||||||
|
onLoadModuleCombineSink: this.onLoadModuleCombineSink,
|
||||||
|
onLoadModuleNullSink: this.onLoadModuleNullSink,
|
||||||
|
}) :
|
||||||
|
r(GraphObjectContextMenu, {
|
||||||
onClose: this.onContextMenuClose,
|
onClose: this.onContextMenuClose,
|
||||||
|
|
||||||
canSetAsDefault: this.canContextMenuSetAsDefault,
|
canSetAsDefault: this.canContextMenuSetAsDefault,
|
||||||
onSetAsDefault: this.onContextMenuSetAsDefault,
|
onSetAsDefault: this.onContextMenuSetAsDefault,
|
||||||
|
|
||||||
|
canDelete: this.canContextMenuDelete,
|
||||||
onDelete: this.onContextMenuDelete,
|
onDelete: this.onContextMenuDelete,
|
||||||
}),
|
})
|
||||||
|
),
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,8 @@ const keyMap = {
|
||||||
hotKeyShiftMute: 'shift+space',
|
hotKeyShiftMute: 'shift+space',
|
||||||
|
|
||||||
hotKeySetAsDefault: 'f',
|
hotKeySetAsDefault: 'f',
|
||||||
|
|
||||||
|
hotKeyAdd: 'a',
|
||||||
};
|
};
|
||||||
|
|
||||||
class MyHotKeys extends React.Component {
|
class MyHotKeys extends React.Component {
|
||||||
|
|
|
@ -28,6 +28,8 @@ const { modules } = require('../../constants/pulse');
|
||||||
|
|
||||||
const ConnectToServerModal = require('./connect-to-server');
|
const ConnectToServerModal = require('./connect-to-server');
|
||||||
const ConfirmationModal = require('./confirmation');
|
const ConfirmationModal = require('./confirmation');
|
||||||
|
const NewGraphObjectModal = require('./new-graph-object');
|
||||||
|
const LoadModuleModal = require('./load-module');
|
||||||
|
|
||||||
Modal.setAppElement('#root');
|
Modal.setAppElement('#root');
|
||||||
|
|
||||||
|
@ -46,9 +48,14 @@ class Modals extends React.PureComponent {
|
||||||
continuation: null,
|
continuation: null,
|
||||||
|
|
||||||
connectToServerModalOpen: false,
|
connectToServerModalOpen: false,
|
||||||
|
newGraphObjectModalOpen: false,
|
||||||
|
loadModuleModalOpen: false,
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
openConnectToServerModal: this.openConnectToServerModal.bind(this),
|
openConnectToServerModal: this.openConnectToServerModal.bind(this),
|
||||||
|
|
||||||
|
openNewGraphObjectModal: this.openNewGraphObjectModal.bind(this),
|
||||||
|
openLoadModuleModal: this.openLoadModuleModal.bind(this),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
this.state = this.initialState;
|
this.state = this.initialState;
|
||||||
|
@ -97,6 +104,14 @@ class Modals extends React.PureComponent {
|
||||||
this.setState({ connectToServerModalOpen: true });
|
this.setState({ connectToServerModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openNewGraphObjectModal() {
|
||||||
|
this.setState({ newGraphObjectModalOpen: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
openLoadModuleModal() {
|
||||||
|
this.setState({ loadModuleModalOpen: true });
|
||||||
|
}
|
||||||
|
|
||||||
handleCancel() {
|
handleCancel() {
|
||||||
this.setState(this.initialState);
|
this.setState(this.initialState);
|
||||||
}
|
}
|
||||||
|
@ -122,6 +137,18 @@ class Modals extends React.PureComponent {
|
||||||
isOpen: this.state.connectToServerModalOpen,
|
isOpen: this.state.connectToServerModalOpen,
|
||||||
onRequestClose: this.handleCancel,
|
onRequestClose: this.handleCancel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
r(NewGraphObjectModal, {
|
||||||
|
isOpen: this.state.newGraphObjectModalOpen,
|
||||||
|
onRequestClose: this.handleCancel,
|
||||||
|
|
||||||
|
openLoadModuleModal: this.state.actions.openLoadModuleModal,
|
||||||
|
}),
|
||||||
|
|
||||||
|
r(LoadModuleModal, {
|
||||||
|
isOpen: this.state.loadModuleModalOpen,
|
||||||
|
onRequestClose: this.handleCancel,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
93
components/modals/load-module.js
Normal file
93
components/modals/load-module.js
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
|
||||||
|
const r = require('r-dom');
|
||||||
|
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
|
const { connect } = require('react-redux');
|
||||||
|
const { bindActionCreators } = require('redux');
|
||||||
|
|
||||||
|
const Modal = require('react-modal');
|
||||||
|
|
||||||
|
const Button = require('../button');
|
||||||
|
const Label = require('../label');
|
||||||
|
const Input = require('../input');
|
||||||
|
|
||||||
|
const {
|
||||||
|
pulse: pulseActions,
|
||||||
|
} = require('../../actions');
|
||||||
|
|
||||||
|
class LoadModuleModal extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
name: '',
|
||||||
|
args: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const { name, args } = this.state;
|
||||||
|
this.props.loadModule(name, args);
|
||||||
|
this.props.onRequestClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isOpen, onRequestClose } = this.props;
|
||||||
|
|
||||||
|
return r(Modal, {
|
||||||
|
isOpen,
|
||||||
|
onRequestClose,
|
||||||
|
}, [
|
||||||
|
r.h3('Load a module'),
|
||||||
|
|
||||||
|
r.form({
|
||||||
|
onSubmit: this.handleSubmit,
|
||||||
|
}, [
|
||||||
|
r(Label, [
|
||||||
|
r.div('Module name:'),
|
||||||
|
r.p([
|
||||||
|
r(Input, {
|
||||||
|
style: { width: '100%' },
|
||||||
|
autoFocus: true,
|
||||||
|
value: this.state.name,
|
||||||
|
onChange: e => this.setState({ name: e.target.value }),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
|
||||||
|
r(Label, [
|
||||||
|
r.div('Arguments:'),
|
||||||
|
r.p([
|
||||||
|
r(Input, {
|
||||||
|
style: { width: '100%' },
|
||||||
|
value: this.state.args,
|
||||||
|
onChange: e => this.setState({ args: e.target.value }),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
|
||||||
|
r.div({
|
||||||
|
className: 'button-group',
|
||||||
|
}, [
|
||||||
|
r(Button, {
|
||||||
|
onClick: onRequestClose,
|
||||||
|
}, 'Cancel'),
|
||||||
|
|
||||||
|
r(Button, {
|
||||||
|
type: 'submit',
|
||||||
|
}, 'Confirm'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(
|
||||||
|
null,
|
||||||
|
dispatch => bindActionCreators(pulseActions, dispatch),
|
||||||
|
)(LoadModuleModal);
|
38
components/modals/new-graph-object.js
Normal file
38
components/modals/new-graph-object.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
const r = require('r-dom');
|
||||||
|
|
||||||
|
const React = require('react');
|
||||||
|
|
||||||
|
const Modal = require('react-modal');
|
||||||
|
|
||||||
|
const Button = require('../button');
|
||||||
|
|
||||||
|
class NewGraphObjectModal extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
name: '',
|
||||||
|
args: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isOpen, onRequestClose, openLoadModuleModal } = this.props;
|
||||||
|
|
||||||
|
return r(Modal, {
|
||||||
|
isOpen,
|
||||||
|
onRequestClose,
|
||||||
|
}, [
|
||||||
|
r.h3('Add something'),
|
||||||
|
|
||||||
|
r(Button, {
|
||||||
|
style: { width: '100%' },
|
||||||
|
onClick: openLoadModuleModal,
|
||||||
|
autoFocus: true,
|
||||||
|
}, 'Load a module...'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = NewGraphObjectModal;
|
Loading…
Reference in New Issue
Block a user