Commit 20b6c648 authored by Patrik Meijer's avatar Patrik Meijer
Browse files

Extract defaultSVG and add PartBrowser files (WIP)

parent 0fd665fc
......@@ -1028,6 +1028,104 @@
}
}
},
"@emotion/cache": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-0.8.8.tgz",
"integrity": "sha512-yaQQjNAVkKclMX6D8jTU3rhQKjCnXU1KS+Ok0lgZcarGHI2yydU/kKHyF3PZnQhbTpIFBK5W4+HmLCtCie7ESw==",
"requires": {
"@emotion/sheet": "^0.8.1",
"@emotion/stylis": "^0.7.1",
"@emotion/utils": "^0.8.2"
}
},
"@emotion/core": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/@emotion/core/-/core-0.13.1.tgz",
"integrity": "sha512-5qzKP6bTe2Ah7Wvh1sgtzgy6ycdpxwgMAjQ/K/VxvqBxveG9PCpq+Z0GdVg7Houb1AwYjTfNtXstjSk4sqi/7g==",
"requires": {
"@emotion/cache": "^0.8.8",
"@emotion/css": "^0.9.8",
"@emotion/serialize": "^0.9.1",
"@emotion/sheet": "^0.8.1",
"@emotion/utils": "^0.8.2"
}
},
"@emotion/css": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/@emotion/css/-/css-0.9.8.tgz",
"integrity": "sha512-Stov3+9+KWZAte/ED9Hts3r4DVBADd5erDrhrywokM31ctQsRPD3qk8W4d1ca48ry57g/nc0qUHNis/xd1SoFg==",
"requires": {
"@emotion/serialize": "^0.9.1",
"@emotion/utils": "^0.8.2"
}
},
"@emotion/hash": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.6.6.tgz",
"integrity": "sha512-ojhgxzUHZ7am3D2jHkMzPpsBAiB005GF5YU4ea+8DNPybMk01JJUM9V9YRlF/GE95tcOm8DxQvWA2jq19bGalQ=="
},
"@emotion/is-prop-valid": {
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.6.8.tgz",
"integrity": "sha512-IMSL7ekYhmFlILXcouA6ket3vV7u9BqStlXzbKOF9HBtpUPMMlHU+bBxrLOa2NvleVwNIxeq/zL8LafLbeUXcA==",
"requires": {
"@emotion/memoize": "^0.6.6"
}
},
"@emotion/memoize": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz",
"integrity": "sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ=="
},
"@emotion/serialize": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.9.1.tgz",
"integrity": "sha512-zTuAFtyPvCctHBEL8KZ5lJuwBanGSutFEncqLn/m9T1a6a93smBStK+bZzcNPgj4QS8Rkw9VTwJGhRIUVO8zsQ==",
"requires": {
"@emotion/hash": "^0.6.6",
"@emotion/memoize": "^0.6.6",
"@emotion/unitless": "^0.6.7",
"@emotion/utils": "^0.8.2"
}
},
"@emotion/sheet": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.8.1.tgz",
"integrity": "sha512-p82hFBHbNkPLZ410HOeaRJZMrN1uh9rI7JAaRXIp62PP5evspPXyi3xYtxZc1+sCSlwjnQPuOIa6N88iJNtPXw=="
},
"@emotion/styled": {
"version": "0.10.6",
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-0.10.6.tgz",
"integrity": "sha512-DFNW8jlMjy1aYCj/PKsvBoJVZAQXzjmSCwtKXLs31qZzNPaUEPbTYSIKnMUtIiAOYsu0pUTGXM+l0a+MYNm4lA==",
"requires": {
"@emotion/styled-base": "^0.10.6"
}
},
"@emotion/styled-base": {
"version": "0.10.6",
"resolved": "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-0.10.6.tgz",
"integrity": "sha512-7RfdJm2oEXiy3isFRY63mHRmWWjScFXFoZTFkCJPaL8NhX+H724WwIoQOt3WA1Jd+bb97xkJg31JbYYsSqnEaQ==",
"requires": {
"@emotion/is-prop-valid": "^0.6.8",
"@emotion/serialize": "^0.9.1",
"@emotion/utils": "^0.8.2"
}
},
"@emotion/stylis": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.7.1.tgz",
"integrity": "sha512-/SLmSIkN13M//53TtNxgxo57mcJk/UJIDFRKwOiLIBEyBHEcipgR6hNMQ/59Sl4VjCJ0Z/3zeAZyvnSLPG/1HQ=="
},
"@emotion/unitless": {
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.6.7.tgz",
"integrity": "sha512-Arj1hncvEVqQ2p7Ega08uHLr1JuRYBuO5cIvcA+WWEQ5+VmkOE3ZXzl04NbQxeQpWX78G7u6MqxKuNX3wvYZxg=="
},
"@emotion/utils": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.8.2.tgz",
"integrity": "sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw=="
},
"@material-ui/core": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.1.0.tgz",
......@@ -2463,8 +2561,7 @@
"deep-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
"dev": true
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
},
"deep-is": {
"version": "0.1.3",
......@@ -6340,6 +6437,20 @@
"react-lifecycles-compat": "^3.0.4"
}
},
"react-treebeard": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/react-treebeard/-/react-treebeard-3.1.0.tgz",
"integrity": "sha512-u4OEzwZk1Xcxp2s55Ny/Ofp8fHRwlabKOAeGbzQ7XUE9YXFbPj8ajl0FInbXEP4Ys9+E1vHCtgqJ6VBsgbCPVg==",
"requires": {
"@babel/runtime": "^7.0.0",
"@emotion/core": "^0.13.1",
"@emotion/styled": "^0.10.6",
"deep-equal": "^1.0.1",
"prop-types": "^15.6.2",
"shallowequal": "^1.1.0",
"velocity-react": "^1.4.1"
}
},
"reactcss": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
......@@ -6774,6 +6885,11 @@
"safe-buffer": "^5.0.1"
}
},
"shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
......@@ -7676,6 +7792,22 @@
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
"dev": true
},
"velocity-animate": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/velocity-animate/-/velocity-animate-1.5.2.tgz",
"integrity": "sha512-m6EXlCAMetKztO1ppBhGU1/1MR3IiEevO6ESq6rcrSQ3Q77xYSW13jkfXW88o4xMrkXJhy/U7j4wFR/twMB0Eg=="
},
"velocity-react": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/velocity-react/-/velocity-react-1.4.1.tgz",
"integrity": "sha512-ZyXBm+9C/6kNUNyc+aeNKEhtTu/Mn+OfpsNBGuTxU8S2DUcis/KQL0rTN6jWL+7ygdOrun18qhheNZTA7YERmg==",
"requires": {
"lodash": "^4.17.5",
"prop-types": "^15.5.8",
"react-transition-group": "^2.0.0",
"velocity-animate": "^1.4.0"
}
},
"vm-browserify": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
......
......@@ -27,7 +27,8 @@
"blockies": "0.0.2",
"immutability-helper": "^2.8.1",
"react-color": "^2.14.1",
"react-samy-svg": "^3.0.1"
"react-samy-svg": "^3.0.1",
"react-treebeard": "^3.1.0"
},
"peerDependencies": {
"react": "^16.5.1",
......
// https://github.com/alexcurtis/react-treebeard
import React from 'react';
import PropTypes from 'prop-types';
import {Treebeard} from 'react-treebeard';
import ExpansionPanel from '@material-ui/core/ExpansionPanel';
import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import Typography from '@material-ui/core/Typography';
import SingleConnectedNode from '../SingleConnectedNode';
import {nameSort} from '../../utils/getObjectSorter';
import PartBrowserItem from './PartBrowserItem';
import {treeBeardTheme, getTreeDecorators} from './TreeOverrides';
const TREE_PATH_SEP = '$';
const EXPAND_ALL = false;
class PartBrowser extends SingleConnectedNode {
static propTypes = {
gmeClient: PropTypes.object.isRequired,
activeNode: PropTypes.string.isRequired,
scale: PropTypes.number.isRequired,
minimized: PropTypes.bool.isRequired,
};
state = {
loaded: false,
validChildren: [],
cursor: null,
};
constructor(props) {
super(props);
const {scale} = this.props;
this.tree = {};
this.items = [];
this.decorators = getTreeDecorators(PartBrowserItem, {scale});
}
componentWillUpdate(nextProps, nextState) {
// TODO: Revise this check when/if incremental updates to validChildren will be done.
const {validChildren} = nextState;
if (validChildren !== this.state.validChildren) {
// Build up a new tree structure.
this.tree = {
children: [],
folders: {},
path: 'ROOT',
};
validChildren
.forEach((childDesc) => {
let treeNode = this.tree;
this.items.push(childDesc);
if (typeof childDesc.treePath === 'string') {
childDesc.treePath.split(TREE_PATH_SEP)
.forEach((path, i, arr) => {
if (i === arr.length - 1) {
treeNode.children.push(childDesc);
} else {
if (!treeNode.folders[path]) {
treeNode.folders[path] = {
isFolder: true,
isRoot: i === 0,
toggled: EXPAND_ALL || i === 0,
name: path,
path: `${treeNode.path}$${path}`,
description: 'TODO: Fetch info',
folders: {},
children: [],
};
treeNode.children.push(treeNode.folders[path]);
}
treeNode = treeNode.folders[path];
}
});
} else {
treeNode.children.push(childDesc);
}
});
}
}
updateValidChildren(validChildrenMap) {
const {gmeClient} = this.props;
const validChildren = Object.keys(validChildrenMap)
.map((id) => {
const metaNode = gmeClient.getNode(id);
const modelicaUri = metaNode.getAttribute('ModelicaURI');
return {
id: metaNode.getId(),
name: metaNode.getAttribute('ShortName') || metaNode.getAttribute('name'),
treePath: modelicaUri ? modelicaUri.split('.')
.slice(1)
.join('$') : null,
modelicaUri: modelicaUri || 'Default',
iconUrl: modelicaUri
? `/assets/DecoratorSVG/${modelicaUri}.svg` : '/assets/DecoratorSVG/Default.svg',
bbox: {
x: 0,
y: 0,
height: 100,
width: 100,
},
};
});
this.setState({
loaded: true,
validChildren,
validChildrenMap,
});
}
onNodeLoad(nodeObj) {
this.updateValidChildren(nodeObj.getValidChildrenTypesDetailed());
}
onNodeUpdate(nodeObj) {
const newValidChildrenMap = nodeObj.getValidChildrenTypesDetailed();
const currentValidChildrenMap = this.state.validChildren;
const [newIds, prevIds] = [newValidChildrenMap, currentValidChildrenMap].map(Object.keys);
if (newIds.length !== prevIds.length) {
this.updateValidChildren(newValidChildrenMap);
} else {
for (let i = 0; i < newValidChildrenMap.length; i += 1) {
if (typeof currentValidChildrenMap[newValidChildrenMap[i]] !== 'string') {
this.updateValidChildren(newValidChildrenMap);
break;
}
}
}
}
onStateChanged() {
const {gmeClient, activeNode} = this.props;
if (this.state.loaded) {
this.onNodeUpdate(gmeClient.getNode(activeNode));
}
}
onNodeUnload() {
this.setState({validChildren: []});
}
onTreeNodeToggle = (node, toggled) => {
if (this.state.cursor) {
this.state.cursor.active = false;
}
/* eslint-disable */
node.active = true;
if (node.children) {
node.toggled = toggled;
if (node.children.length === 1 && node.children[0].isFolder) {
node.children[0].toggled = true;
}
}
/* eslint-enable */
this.setState({cursor: node});
};
buildTreeStructure = (treeNode) => {
const {scale} = this.props;
if (treeNode.isFolder) {
if (treeNode.isRoot) {
return (
<ExpansionPanel key={treeNode.path} defaultExpanded>
<ExpansionPanelSummary expandIcon={<ExpandMoreIcon/>}>
<Typography variant="subheading">{treeNode.name}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails style={{
display: 'block',
padding: 0,
paddingBottom: 10,
}}
>
<Treebeard
data={treeNode}
onToggle={this.onTreeNodeToggle}
decorators={this.decorators}
style={treeBeardTheme}
/>
</ExpansionPanelDetails>
</ExpansionPanel>);
}
return (
treeNode.children.sort(nameSort)
.map(this.buildTreeStructure)
);
}
return (
<PartBrowserItem key={treeNode.id} nodeData={treeNode} scale={scale}/>
);
};
render() {
const {minimized, scale} = this.props;
let cnt = 0;
if (!this.state.loaded) {
return null;
}
return (
<div style={{
width: '100%',
textAlign: minimized ? 'center' : undefined,
}}
>
<div style={{display: minimized ? 'none' : undefined}}>
{this.tree.children.map(this.buildTreeStructure)}
</div>
<div style={{display: minimized ? undefined : 'none'}}>
{this.items
.filter((/* child */) => {
// TODO: These should be filtered based on recent components used..
cnt += 1;
return cnt <= 10;
})
.map(child => (
<PartBrowserItem
key={child.id}
nodeData={child}
scale={scale}
listView
/>))
}
</div>
</div>
);
}
}
export default PartBrowser;
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {DragLayer} from 'react-dnd';
// noinspection JSUnresolvedVariable
import {Samy} from 'react-samy-svg';
import DRAG_TYPES from '../../utils/dragTypes';
import defaultSVG from '../../utils/defaultSVG';
// DragLayer
function collect(monitor) {
return {
item: monitor.getItem(),
itemType: monitor.getItemType(),
currentOffset: monitor.getClientOffset(),
initialOffset: monitor.getInitialClientOffset(),
isDragging: monitor.isDragging(),
};
}
class PartBrowserDragPreview extends Component {
static propTypes = {
item: PropTypes.object,
itemType: PropTypes.string,
currentOffset: PropTypes.shape({
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
}),
initialOffset: PropTypes.shape({
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
}),
isDragging: PropTypes.bool.isRequired,
scale: PropTypes.number.isRequired,
};
static defaultProps = {
item: null,
itemType: null,
currentOffset: null,
initialOffset: null,
};
render() {
const {
item, itemType, isDragging, currentOffset, initialOffset, scale,
} = this.props;
if (!isDragging || !item || itemType !== DRAG_TYPES.GME_NODE
|| !item.nodeData || !currentOffset || !initialOffset) {
return null;
}
const {x, y} = {x: currentOffset.x - initialOffset.x, y: currentOffset.y - initialOffset.y};
const transform = `translate(${x}px, ${y}px)`;
const {base, bbox} = defaultSVG;
return (
<Samy
svgXML={base}
style={{
position: 'absolute',
top: initialOffset.y - item.offset.y,
left: initialOffset.x - item.offset.x,
zIndex: 1300,
height: bbox.height * scale,
width: bbox.width * scale,
pointerEvents: 'none',
// verticalAlign: 'middle',
transform,
WebkitTransform: transform,
}}
/>
);
}
}
export default DragLayer(collect)((PartBrowserDragPreview));
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {DragSource} from 'react-dnd';
import {getEmptyImage} from 'react-dnd-html5-backend';
// noinspection JSUnresolvedVariable
import {Samy, SvgProxy} from 'react-samy-svg';
import {DRAG_TYPES} from '../CONSTANTS';
import SVGCACHE from '../../plugins/MoveSVGToRegistryUtil/svgcache.json';
// / End of DragLayer..
const partBrowserItemSource = {
beginDrag(props) {
return {
gmeId: props.nodeData.id,
create: true,
// Specifics
nodeData: props.nodeData,
offset: {
y: SVGCACHE[props.nodeData.modelicaUri].bbox.height * (props.scale / 2),
x: SVGCACHE[props.nodeData.modelicaUri].bbox.width * (props.scale / 2),
},
};
},
};
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
connectDragPreview: connect.dragPreview(),
isDragging: monitor.isDragging(),
};
}
class PartBrowserItem extends Component {
static propTypes = {
nodeData: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string,
modelicaUri: PropTypes.string,
iconUrl: PropTypes.string,
}).isRequired,
connectDragSource: PropTypes.func.isRequired,
connectDragPreview: PropTypes.func.isRequired,
listView: PropTypes.bool,
};
static defaultProps = {
listView: false,
};
componentDidMount() {
const {connectDragPreview} = this.props;
// Use empty image as a drag preview so browsers don't draw it
// and we can draw whatever we want on the custom drag layer instead.
connectDragPreview(getEmptyImage(), {
// IE fallback: specify that we'd rather screenshot the node
// when it already knows it's being dragged so we can hide it with CSS.
captureDraggingState: true,
});
}
render() {
const {nodeData, connectDragSource, listView} = this.props;
const content = (
<div
style={{
display: 'inline-flex',
width: '100%',
opacity: 0.99,
paddingTop: listView ? 10 : 0,
cursor: 'pointer',
}}
>
<div>
<Samy
svgXML={SVGCACHE[nodeData.modelicaUri].base}
style={{
height: 26,
width: 40,
verticalAlign: 'middle',
}}
>
<SvgProxy selector="text" display="none"/>
<SvgProxy selector="*" stroke-width="0.75mm"/>
</Samy>
</div>