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

Extract defaultSVG and add PartBrowser files (WIP)

parent 0fd665fc
Loading
Loading
Loading
Loading
+134 −2
Original line number Diff line number Diff line
@@ -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",
+2 −1
Original line number Diff line number Diff line
@@ -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",
+249 −0
Original line number Diff line number Diff line
// 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;
+78 −0
Original line number Diff line number Diff line
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));
+107 −0
Original line number Diff line number Diff line
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>

                <div style={{
                    paddingTop: 5,
                    overflowX: 'hidden',
                    textOverflow: 'ellipsis',
                    fontSize: 13,
                    display: listView ? 'none' : undefined,
                }}
                >
                    {nodeData.name}
                </div>
            </div>);

        return connectDragSource(content);
    }
}

export default DragSource(DRAG_TYPES.GME_NODE, partBrowserItemSource, collect)(PartBrowserItem);
Loading