Commit bc7bd2c4 authored by Tamas Kecskes's avatar Tamas Kecskes
Browse files

Added ModalSpinner with demo

Moving ContainmentCanvas (issues with redux and global state...)
parent efc00ae4
...@@ -8,7 +8,8 @@ import DemoProjectSeedCards from '../src/components/ProjectSeedCards/demo'; ...@@ -8,7 +8,8 @@ import DemoProjectSeedCards from '../src/components/ProjectSeedCards/demo';
import DemoAttributeEditor from '../src/components/AttributeEditor/demo'; import DemoAttributeEditor from '../src/components/AttributeEditor/demo';
import DemoConfirmDialog from '../src/components/ConfirmDialog/demo'; import DemoConfirmDialog from '../src/components/ConfirmDialog/demo';
import DemoUserProfileNavigator from '../src/components/UserProfileNavigator/demo'; import DemoUserProfileNavigator from '../src/components/UserProfileNavigator/demo';
import DemoModalSpinner from '../src/components/ModalSpinner/demo';
import DemoContainmentCanvas from '../src/components/ContainmentCanvas/demo';
export default class demoApp extends Component { export default class demoApp extends Component {
componentDidMount() { componentDidMount() {
...@@ -35,6 +36,14 @@ export default class demoApp extends Component { ...@@ -35,6 +36,14 @@ export default class demoApp extends Component {
component: <DemoUserProfileNavigator/>, component: <DemoUserProfileNavigator/>,
title: 'UserProfileNavigator', title: 'UserProfileNavigator',
}, },
{
component: <DemoModalSpinner/>,
title: 'ModalSpinner',
},
// {
// component: <DemoContainmentCanvas/>,
// title: 'ContainmentCanvas',
// },
]; ];
return ( return (
...@@ -46,7 +55,7 @@ export default class demoApp extends Component { ...@@ -46,7 +55,7 @@ export default class demoApp extends Component {
<Typography variant="headline" color="textSecondary">{info.title}</Typography> <Typography variant="headline" color="textSecondary">{info.title}</Typography>
</ExpansionPanelSummary> </ExpansionPanelSummary>
<div style={{padding: 20}}> <div style={{padding: 20}}>
{info.component} {info.component}
</div> </div>
</ExpansionPanel> </ExpansionPanel>
) )
......
This diff is collapsed.
/**
* @author kecso / https://github.com/kecso
*/
export default class BasicEventManager {
constructor() {
this.subscribers = {};
this.lastEvents = {};
this.subscribe = this.subscribe.bind(this);
this.unsubscribe = this.unsubscribe.bind(this);
this.fire = this.fire.bind(this);
}
fire(id, event) {
const oldEvent = this.lastEvents[id];
this.lastEvents[id] = event;
if (JSON.stringify(oldEvent) !== JSON.stringify(event)) {
(this.subscribers[id] || []).forEach((eventFn) => {
eventFn(id, event);
});
}
}
subscribe(id, eventFn) {
this.subscribers[id] = this.subscribers[id] || [];
this.subscribers[id].push(eventFn);
return this.lastEvents[id];
}
unsubscribe(id, eventFn) {
this.subscribers[id] = this.subscribers[id] || [];
this.subscribers[id].splice(this.subscribers[id].indexOf(eventFn), 1);
}
}
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import Z_LEVELS from '../../utils/zLevels';
export default class BasicConnectingComponent extends Component {
static propTypes = {
connectionManager: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
const {connectionManager} = this.props;
connectionManager.setListener(this.onChange);
}
state = {
startPos: null,
currentPos: null,
isConnecting: false,
};
onChange = (event) => {
this.setState(event);
};
render() {
const {isConnecting, startPos, currentPos} = this.state;
let top;
let left;
let width;
let height;
if (isConnecting) {
top = Math.min(startPos.y, currentPos.y);
left = Math.min(startPos.x, currentPos.x);
height = Math.abs(currentPos.y - startPos.y) + 5;
width = Math.abs(currentPos.x - startPos.x) + 5;
if (height > 0 && width > 0) {
return (
<svg
width={width}
height={height}
viewBox={`${0} ${0} ${width} ${height}`}
style={{
position: 'absolute',
top: `${top - 5}px`,
left: `${left - 5}px`,
zIndex: Z_LEVELS.connection,
}}
>
<line
strokeWidth="3"
stroke="orange"
strokeDasharray="5 5"
x1={startPos.x - (left - 5)}
y1={startPos.y - (top - 5)}
x2={currentPos.x - (left - 5)}
y2={currentPos.y - (top - 5)}
/>
</svg>
);
}
}
return null;
}
}
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import ZLEVELS from '../../utils/zLevels';
export default class BasicConnection extends Component {
static propTypes = {
path: PropTypes.arrayOf(PropTypes.object).isRequired,
onClick: PropTypes.func,
hasWrapper: PropTypes.bool.isRequired,
dashed: PropTypes.bool.isRequired,
color: PropTypes.string,
};
static defaultProps = {
onClick: null,
color: 'black',
};
onClick = (event) => {
const {onClick} = this.props;
if (onClick) {
onClick(event);
}
};
getBoundingBox = () => {
const {path} = this.props;
let minX;
let maxX;
let minY;
let maxY;
if (path.length === 0) {
return null;
}
minX = path[0].x;
maxX = path[0].x;
minY = path[0].y;
maxY = path[0].y;
path.forEach((point) => {
if (point.x < minX) {
minX = point.x;
}
if (point.x > maxX) {
maxX = point.x;
}
if (point.y < minY) {
minY = point.y;
}
if (point.y > maxY) {
maxY = point.y;
}
});
return {
x: minX,
y: minY,
width: Math.max(maxX - minX, 2),
height: Math.max(maxY - minY, 2),
};
};
render() {
const {path, hasWrapper, dashed, color} = this.props;
const box = this.getBoundingBox();
const sections = [];
let i;
const style = hasWrapper ? {} : {
position: 'absolute',
top: box.y,
left: box.x,
zIndex: ZLEVELS.connection,
};
if (box === null) {
return null;
}
for (i = 0; i < path.length - 1; i += 1) {
sections.push(<path
key={i}
d={`M${
path[i].x} ${
path[i].y} L${
path[i + 1].x} ${
path[i + 1].y}`}
strokeWidth={1.6}
strokeDasharray={dashed ? 5 : 0}
stroke={color}
/>);
}
return (
<svg
width={box.width}
height={box.height}
style={style}
viewBox={`${box.x} ${box.y} ${box.width} ${box.height}`}
>
{sections}
</svg>);
}
}
/**
* @author kecso / https://github.com/kecso
*/
export default class ConnectionManager {
constructor() {
this.isConnecting = false;
this.type = null;
this.source = null;
this.notifyMe = null;
this.changeFn = null;
this.startPos = null;
this.currentPos = null;
this.startConnection = this.startConnection.bind(this);
this.endConnection = this.endConnection.bind(this);
this.setListener = this.setListener.bind(this);
this.clearListener = this.clearListener.bind(this);
}
startConnection(source, type, position, notifyMe) {
this.isConnecting = true;
this.type = type;
this.source = source;
this.startPos = position;
this.notifyMe = typeof notifyMe === 'function' ? notifyMe : () => {
console.log('no notification is sent');
};
this.fireChange();
}
endConnection() {
this.isConnecting = false;
const connection = {source: this.source, type: this.type};
this.source = null;
this.type = null;
if (typeof this.notifyMe === 'function') { this.notifyMe(); }
this.notifyMe = null;
this.fireChange();
return connection;
}
setListener(changeFn) {
this.changeFn = changeFn;
}
clearListener() {
this.changeFn = null;
}
onMouseMove(newPos) {
this.currentPos = newPos;
this.fireChange();
}
fireChange() {
if (this.changeFn) {
this.changeFn({
isConnecting: this.isConnecting,
startPos: this.startPos,
currentPos: this.currentPos,
});
}
}
}
import React from 'react';
import PropTypes from 'prop-types';
import {DropTarget} from 'react-dnd';
import SingleConnectedNode from './SingleConnectedNode';
import DRAG_TYPES from '../../utils/dragTypes';
import ContainmentCanvasItem from './ContainmentCanvasItem';
import ConnectionManager from './ConnectionManager';
import BasicConnectingComponent from './BasicConnectingComponent';
import BasicEventManager from '../BasicEventManager/BasicEventManager';
import getIndexedName from '../../utils/getIndexedName';
import Z_LEVELS from '../../utils/zLevels';
import ContainmentCanvasInfoCard from './ContainmentCanvasInfoCard';
// TODO we only take loaded children into account
function getChildrenNames(gmeClient, nodeId) {
const container = gmeClient.getNode(nodeId);
const childrenIds = container.getChildrenIds();
const names = [];
childrenIds.forEach((childId) => {
const node = gmeClient.getNode(childId);
if (node) {
names.push(node.getAttribute('name'));
}
});
return names;
}
const canvasTarget = {
drop(props, monitor, canvas) {
const dragItem = monitor.getItem();
if (dragItem.move) {
const offset = monitor.getDifferenceFromInitialOffset();
const node = props.gmeClient.getNode(dragItem.gmeId);
const position = node.getRegistry('position');
position.x += offset.x / props.scale;
position.y += offset.y / props.scale;
position.x = Math.trunc(position.x);
position.y = Math.trunc(position.y);
props.gmeClient.setRegistry(dragItem.gmeId, 'position', position);
} else if (dragItem.create) {
const dragOffset = monitor.getClientOffset();
const metaNode = props.gmeClient.getNode(dragItem.gmeId);
const position = {
x: ((dragOffset.x - canvas.offset.x) + canvas.props.scrollPos.x) / props.scale,
y: ((dragOffset.y - canvas.offset.y) + canvas.props.scrollPos.y) / props.scale,
};
let name = metaNode.getAttribute('ShortName') || metaNode.getAttribute('name');
name = getIndexedName(name, getChildrenNames(props.gmeClient, props.activeNode));
position.x -= dragItem.nodeData.bbox.width / 2;
position.y -= dragItem.nodeData.bbox.height / 2;
position.x = Math.trunc(position.x);
position.y = Math.trunc(position.y);
// TODO: Fix when client accepts 0
position.x = position.x > 0 ? position.x : 1;
position.y = position.y > 0 ? position.y : 1;
props.gmeClient.createNode({
parentId: props.activeNode,
baseId: dragItem.gmeId,
}, {
attributes: {
name,
},
registry: {
position,
},
});
}
canvas.setState({dragMode: 'none'});
},
hover(props, monitor, component) {
const item = monitor.getItem();
let dragState;
if (item.create) {
dragState = 'create';
}
if (item.move) {
dragState = 'move';
}
component.setState({dragMode: dragState});
},
};
function collect(connector, monitor) {
return {
connectDropTarget: connector.dropTarget(),
isOver: monitor.isOver(),
};
}
class ContainmentCanvas extends SingleConnectedNode {
static propTypes = {
gmeClient: PropTypes.object.isRequired,
scrollPos: PropTypes.object.isRequired,
activeNode: PropTypes.string.isRequired,
scale: PropTypes.number.isRequired,
connectDropTarget: PropTypes.func.isRequired,
isOver: PropTypes.bool.isRequired,
};
state = {
children: [],
nodeInfo: {},
dragMode: 'none',
};
cm = null;
em = null;
offset = {
x: 0,
y: 0,
};
constructor(props) {
super(props);
this.cm = new ConnectionManager();
this.em = new BasicEventManager();
}
populateChildren(nodeObj) {
const childrenIds = nodeObj.getChildrenIds();
const newChildren = childrenIds.map(id => ({id}));
this.setState({
children: newChildren,
nodeInfo: {
name: nodeObj.getAttribute('name'),
},
});
}
onNodeLoad(nodeObj) {
this.populateChildren(nodeObj, true);
}
onNodeUpdate(nodeObj) {
this.populateChildren(nodeObj);
}
onMouseClick = (event) => {
event.stopPropagation();
event.preventDefault();
if (this.cm.isConnecting) {
this.cm.endConnection();
}
this.props.clearSelection();
};
onMouseLeave = (event) => {
event.stopPropagation();
if (this.cm.isConnecting) {
this.cm.endConnection();
}
};
onMouseMove = (event) => {
const {scrollPos} = this.props;
this.cm.onMouseMove({
x: event.clientX + (scrollPos.x - this.offset.x),
y: event.clientY + (scrollPos.y - this.offset.y),
});
};
render() {
const {connectDropTarget, activeNode, gmeClient} = this.props;
const {children, dragMode} = this.state;
const childrenItems = children.map(child => (<ContainmentCanvasItem
key={child.id}
gmeClient={gmeClient}
activeNode={child.id}
contextNode={activeNode}
connectionManager={this.cm}
eventManager={this.em}
/>));
const content = (
<div
ref={(canvas) => {
if (canvas) {
const {offsetLeft, offsetTop} = canvas.offsetParent;
this.offset = {
x: offsetLeft,
y: offsetTop,
};
}
}}
style={{
backgroundColor: dragMode === 'create' ? 'lightgreen' : undefined,
width: '100%',
height: '100%',
overflow: 'auto',
zIndex: Z_LEVELS.canvas,
position: 'absolute',
}}
role="presentation"
onClick={this.onMouseClick}
onKeyDown={() => {}}
onContextMenu={this.onMouseClick}
onMouseLeave={this.onMouseLeave}
onMouseMove={this.onMouseMove}
>
<BasicConnectingComponent connectionManager={this.cm}/>
{childrenItems.length > 0 ? childrenItems : ContainmentCanvasInfoCard()}
</div>);
return connectDropTarget(content);
}
}
export default (DropTarget(DRAG_TYPES.GME_NODE, canvasTarget, collect)(ContainmentCanvas));
import React from 'react';
import {Link} from 'react-router-dom';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
const