predix-ui
Version:
px-* web components as React styled components
275 lines (243 loc) • 6.78 kB
JavaScript
/* eslint-disable */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Tree from './tree';
import Node from './TreeNode';
import styled from 'styled-components';
const MTree = styled.div`
position: relative; overflow: hidden; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;
.collapse.caret-right { display: inline; }
.collapse.caret-right { display: inline; }
.node {
display: flex; padding: .2rem; align-items: center; line-height: 1.8rem; font-size: 1rem;
flex: 1;
&:hover{
color: var(--px-tree-text-color--hover, #000);
background-color: var(--px-tree-background-color--hover, #d3d3d3);
}
}
.is-active {
color: var(--px-tree-text-color--selected, #fff);
background-color: var(--px-tree-background-color--selected, gray);
}
.m-draggable { position: absolute; opacity: 0.8; }
.placeholder > * { visibility: hidden; }
.placeholder { border: 1px dashed var(--px-tree-placeholder-border-color, gray);; }
.inner {
position: relative;
cursor: pointer;
padding-left: 10px;
display: flex;
align-items: center;
}
.collapse { cursor: pointer; }
.caret-down:before { content: '\\25BE'; }
.caret-right:before { content: '\\25B8'; }
`;
class UITree extends Component {
constructor(props) {
super(props);
this.state = this.init(props);
}
componentWillReceiveProps(nextProps) {
if (!this._updated) {
this.setState(this.init(nextProps));
} else {
this._updated = false;
}
}
init = (props) => {
const tree = new Tree(props.tree);
tree.isNodeCollapsed = props.isNodeCollapsed;
tree.renderNode = props.renderNode;
tree.changeNodeCollapsed = props.changeNodeCollapsed;
tree.updateNodesPosition();
return {
tree,
dragging: {
id: null,
x: null,
y: null,
w: null,
h: null
}
};
};
getDraggingDom = () => {
const { tree, dragging } = this.state;
if (dragging && dragging.id) {
const draggingIndex = tree.getIndex(dragging.id);
const draggingStyles = {
top: dragging.y,
left: dragging.x,
width: dragging.w
};
return (
<div className="m-draggable" style={draggingStyles}>
<Node
tree={tree}
index={draggingIndex}
paddingLeft={this.props.paddingLeft}
/>
</div>
);
}
return null;
};
render() {
const { tree, dragging } = this.state;
const draggingDom = this.getDraggingDom();
return (
<MTree className="m-tree">
{draggingDom}
<Node
tree={tree}
index={tree.getIndex(1)}
key={1}
paddingLeft={this.props.paddingLeft}
onDragStart={this.dragStart}
onCollapse={this.toggleCollapse}
dragging={dragging && dragging.id}
/>
</MTree>
);
}
dragStart = (id, dom, e) => {
this.dragging = {
id,
w: dom.offsetWidth,
h: dom.offsetHeight,
x: dom.offsetLeft,
y: dom.offsetTop
};
this._startX = dom.offsetLeft;
this._startY = dom.offsetTop;
this._offsetX = e.clientX;
this._offsetY = e.clientY;
this._start = true;
window.addEventListener('mousemove', this.drag);
window.addEventListener('mouseup', this.dragEnd);
};
// oh
drag = (e) => {
if (this._start) {
this.setState({
dragging: this.dragging
});
this._start = false;
}
const { tree, dragging, paddingLeft } = this.state;
let newIndex = null;
let index = tree.getIndex(dragging.id);
const { collapsed } = index.node;
const {
_startX,
_startY,
_offsetX,
_offsetY
} = this;
const pos = {
x: _startX + e.clientX - _offsetX,
y: _startY + e.clientY - _offsetY
};
dragging.x = pos.x;
dragging.y = pos.y;
const diffX = dragging.x - paddingLeft / 2 - (index.left - 2) * paddingLeft;
const diffY = dragging.y - dragging.h / 2 - (index.top - 2) * dragging.h;
if (diffX < 0) {
// left
if (index.parent && !index.next) {
newIndex = tree.move(index.id, index.parent, 'after');
}
} else if (diffX > paddingLeft) {
// right
if (index.prev) {
const prevNode = tree.getIndex(index.prev).node;
if (!prevNode.collapsed && !prevNode.leaf) {
newIndex = tree.move(index.id, index.prev, 'append');
}
}
}
if (newIndex) {
index = newIndex;
newIndex.node.collapsed = collapsed;
dragging.id = newIndex.id;
}
if (diffY < 0) {
// up
const above = tree.getNodeByTop(index.top - 1);
newIndex = tree.move(index.id, above.id, 'before');
} else if (diffY > dragging.h) {
// down
if (index.next) {
const below = tree.getIndex(index.next);
if (below.children && below.children.length && !below.node.collapsed) {
newIndex = tree.move(index.id, index.next, 'prepend');
} else {
newIndex = tree.move(index.id, index.next, 'after');
}
} else {
const below = tree.getNodeByTop(index.top + index.height);
if (below && below.parent !== index.id) {
if (
below.children &&
below.children.length &&
!below.node.collapsed
) {
newIndex = tree.move(index.id, below.id, 'prepend');
} else {
newIndex = tree.move(index.id, below.id, 'after');
}
}
}
}
if (newIndex) {
newIndex.node.collapsed = collapsed;
dragging.id = newIndex.id;
}
this.setState({
tree,
dragging
});
};
dragEnd = () => {
this.setState({
dragging: {
id: null,
x: null,
y: null,
w: null,
h: null
}
});
this.change(this.state.tree);
window.removeEventListener('mousemove', this.drag);
window.removeEventListener('mouseup', this.dragEnd);
};
change = (tree) => {
this._updated = true;
if (this.props.onChange) this.props.onChange(tree.obj);
};
toggleCollapse = (nodeId) => {
const { tree } = this.state;
const index = tree.getIndex(nodeId);
const { node } = index;
node.collapsed = !node.collapsed;
tree.updateNodesPosition();
this.setState({
tree
});
this.change(tree);
};
}
UITree.propTypes = {
onChange: PropTypes.func,
//tree: PropTypes.object.isRequired,
paddingLeft: PropTypes.number,
renderNode: PropTypes.func.isRequired
};
UITree.defaultProps = {
paddingLeft: 20,
onChange: null
};
export default UITree;