mui-draggable-treeview
Version:
React Treeview component built on Material-UI with drag and drop features
405 lines (389 loc) • 19.2 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = require('react');
var React__default = _interopDefault(React);
var MuiTreeView = _interopDefault(require('@material-ui/lab/TreeView'));
var styles = require('@material-ui/core/styles');
var core = require('@material-ui/core');
var lab = require('@material-ui/lab');
var icons = require('@material-ui/icons');
var styled = _interopDefault(require('styled-components'));
var Immutable = _interopDefault(require('immutable'));
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function __makeTemplateObject(cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
}
var StyledTreeItem = function (props) {
var classes = useTreeItemStyles();
var labelText = props.labelText, LabelIcon = props.labelIcon, labelInfo = props.labelInfo, labelIconUrl = props.labelIconUrl, other = __rest(props, ["labelText", "labelIcon", "labelInfo", "labelIconUrl"]);
var handleDragOver = function (ev, isBeforeDestinationNode) {
ev.stopPropagation();
if (!props.validateDragOver(isBeforeDestinationNode))
return;
ev.preventDefault();
ev.currentTarget.style.height = "30px";
ev.currentTarget.style.borderWidth = "3px";
};
var handleDragLeave = function (ev) {
ev.stopPropagation();
ev.currentTarget.style.height = "3px";
ev.currentTarget.style.borderWidth = "0px";
};
var handleOrderChange = function (ev, isBeforeDestinationNode) {
ev.stopPropagation();
ev.preventDefault();
if (props.onNodeReOrder)
props.onNodeReOrder(ev, isBeforeDestinationNode);
ev.currentTarget.style.height = "3px";
ev.currentTarget.style.borderWidth = "0px";
};
var iconContainerStyle = props.node.children && props.node.children.length > 0
? classes.iconContainer
: classes.hiddenIconContainer;
var labelStyle = !(labelIconUrl || LabelIcon) ? classes.label : classes.labelWithIcon;
var renderLabel = function () {
var labelRootStyle = props.node.children && props.node.children.length > 0
? classes.labelRootParent
: classes.labelRootChild;
labelRootStyle += " " + (props.node.disabled ? classes.disabled : "");
return (React__default.createElement(React__default.Fragment, null,
props.depth !== 0 && (React__default.createElement("div", { onDrop: function (ev) { return handleOrderChange(ev, true); }, onDragOver: function (ev) { return handleDragOver(ev, true); }, onDragLeave: handleDragLeave, draggable: false, style: {
height: "3px",
border: "dashed 0px white",
backgroundColor: "transparent !important",
} })),
React__default.createElement("div", { className: labelRootStyle, draggable: false },
(labelIconUrl || LabelIcon) && (React__default.createElement("div", { className: classes.labelIcon }, LabelIcon ? React__default.createElement(LabelIcon, { color: "inherit" }) : React__default.createElement("img", { src: labelIconUrl, alt: "image" }))),
React__default.createElement(core.Typography, { variant: "body2", className: classes.labelText }, labelText),
labelInfo && (React__default.createElement(core.Typography, { variant: "caption", color: "inherit" }, labelInfo))),
!props.isExpanded && (React__default.createElement("div", { onDrop: function (ev) { return handleOrderChange(ev, false); }, onDragOver: function (ev) { return handleDragOver(ev, false); }, onDragLeave: handleDragLeave, draggable: false, style: {
height: "3px",
border: "dashed 0px white",
backgroundColor: "transparent !important",
} }))));
};
return (React__default.createElement(lab.TreeItem, __assign({ label: renderLabel(), key: props.nodeId + "_treeItem", classes: {
root: classes.root,
content: classes.content,
label: labelStyle,
iconContainer: iconContainerStyle,
group: classes.group,
expanded: classes.expanded,
} }, other)));
};
var useTreeItemStyles = core.makeStyles(function (theme) {
return core.createStyles({
root: {
color: theme.palette.text.secondary,
"&:focus > $content": {
color: "var(--tree-view-color)",
},
"&:focus > $content $label, &:hover > $content $label, &$selected > $content $label": {
backgroundColor: "transparent",
},
marginBottom: "10px",
},
disabled: {
opacity: "0.5",
},
group: {
marginLeft: "23px",
},
expanded: {},
content: {
color: theme.palette.text.secondary,
paddingRight: theme.spacing(1),
fontWeight: theme.typography.fontWeightMedium,
"$expanded > &": {
fontWeight: theme.typography.fontWeightRegular,
paddingBottom: "10px",
},
alignItems: "baseline",
marginBottom: "10px",
},
label: {
fontWeight: "inherit",
color: "inherit",
paddingLeft: "0px !important",
bottom: "3px",
},
labelWithIcon: {
fontWeight: "inherit",
color: "inherit",
paddingLeft: "0px !important",
top: "1px",
},
labelRootParent: {
display: "flex",
alignItems: "center",
padding: "0 0 4px 0",
marginLeft: "3px",
},
labelRootChild: {
display: "flex",
alignItems: "center",
padding: "0 0 4px 0",
},
labelIcon: {
marginRight: "5px",
},
labelText: {
fontWeight: "inherit",
flexGrow: 1,
},
iconContainer: {
width: "15px",
marginLeft: "1px",
},
hiddenIconContainer: {
display: "none",
},
});
});
var moveNode = function (node, sourceNode, destinationNodeId) {
if (node.id === destinationNodeId) {
if (!node.children)
node.children = [];
var order = node.children && node.children.length > 0
? node.children[node.children.length - 1].order || 0
: 0;
sourceNode.order = order + 1;
node.children.push(sourceNode);
return true;
}
if (node.children) {
for (var index = 0; index < node.children.length; index++) {
var isMoved = moveNode(node.children[index], sourceNode, destinationNodeId);
if (isMoved)
return true;
}
}
return false;
};
var reOrderNodes = function (node, draggedTreeNode, destinationNodeId, parentNodeIdOfDestinationNode, addBeforeDestinationNode) {
var _a;
if (node.id === parentNodeIdOfDestinationNode && node.children && node.children.length > 0) {
if (((_a = draggedTreeNode.parentNode) === null || _a === void 0 ? void 0 : _a.id) !== parentNodeIdOfDestinationNode) {
if (!node.children.some(function (x) { return x.id === draggedTreeNode.node.id; })) {
node.children.push(draggedTreeNode.node);
}
}
node.children.sort(function (a, b) { return (a.order || 0) - (b.order || 0); });
var draggedNode = node.children.find(function (x) { return x.id === draggedTreeNode.node.id; });
if (!draggedNode)
return {};
for (var index = 0; index < node.children.length; index++) {
if (node.children[index].id === destinationNodeId) {
var result = { siblingsOfDraggedTreeNode: [] };
draggedNode.order = node.children[index].order || 0;
if (!addBeforeDestinationNode)
draggedNode.order++;
result.draggedTreeNodeOrder = draggedNode.order;
for (var subIndex = addBeforeDestinationNode ? index : index + 1; subIndex < node.children.length; subIndex++) {
if (node.children[subIndex].id !== draggedNode.id) {
node.children[subIndex].order = (node.children[subIndex].order || 0) + 1;
result.siblingsOfDraggedTreeNode.push(node.children[subIndex]);
}
}
return result;
}
}
}
if (node.children) {
for (var index = 0; index < node.children.length; index++) {
var result = reOrderNodes(node.children[index], draggedTreeNode, destinationNodeId, parentNodeIdOfDestinationNode, addBeforeDestinationNode);
if (result)
return result;
}
}
return null;
};
var deleteNode = function (node, sourceNodeId, parentNodeId) {
var _a;
if (node.id === parentNodeId) {
node.children = (_a = node.children) === null || _a === void 0 ? void 0 : _a.filter(function (x) { return x.id !== sourceNodeId; });
return true;
}
if (node.children) {
for (var index = 0; index < node.children.length; index++) {
var isDeleted = deleteNode(node.children[index], sourceNodeId, parentNodeId);
if (isDeleted)
return true;
}
}
return false;
};
var MuiDraggableTreeView = function (props) {
var _a, _b;
var _c = React.useState(props.tree), tree = _c[0], setTree = _c[1];
var _d = React.useState([]), allParentIds = _d[0], setAllParentIds = _d[1];
var _f = React.useState([props.tree.id]), expanded = _f[0], setExpanded = _f[1];
var classes = useStyles();
var draggedTreeNode;
React.useEffect(function () {
var ids = [];
populateAllParentNodeIds(ids, props.tree);
setAllParentIds(ids);
setTree(props.tree);
}, [props.tree]);
var handleDragOver = function (ev, destinationNode, depth) {
var _a;
ev.stopPropagation();
var allowToDrag = true;
if (!draggedTreeNode)
return;
if (((_a = draggedTreeNode.parentNode) === null || _a === void 0 ? void 0 : _a.id) === destinationNode.id ||
draggedTreeNode.node.id === destinationNode.id ||
(draggedTreeNode.node.children &&
draggedTreeNode.node.children.length > 0 &&
draggedTreeNode.depth < depth))
return;
if (props.onNodeDragOver) {
allowToDrag = props.onNodeDragOver(draggedTreeNode.node, destinationNode);
}
if (allowToDrag) {
ev.preventDefault();
ev.currentTarget.classList.add(classes.dragOver);
}
};
var handleDrop = function (ev, destinationNode) {
ev.preventDefault();
ev.stopPropagation();
if (!draggedTreeNode || !draggedTreeNode.parentNode)
return;
var newTree = Immutable.fromJS(tree).toJS();
moveNode(newTree, draggedTreeNode.node, destinationNode.id);
deleteNode(newTree, draggedTreeNode.node.id, draggedTreeNode.parentNode.id);
setTree(newTree);
ev.currentTarget.classList.remove(classes.dragOver);
if (props.onNodeDrop)
props.onNodeDrop(draggedTreeNode.node, destinationNode);
};
var handleDragStart = function (ev, parentNode, node, depth) {
ev.stopPropagation();
draggedTreeNode = { parentNode: parentNode, node: node, depth: depth };
};
var validateDragOver = function (node, depth, isBeforeDestinationNode) {
if (!draggedTreeNode)
return false;
if (draggedTreeNode.node.id.toString() === node.id ||
(draggedTreeNode.parentNode && draggedTreeNode.parentNode.id.toString() === node.id) ||
(draggedTreeNode.node.children &&
draggedTreeNode.node.children.length > 0 &&
draggedTreeNode.depth < depth))
return false;
if (props.onNodeReOrderOver &&
!props.onNodeReOrderOver(draggedTreeNode.node, node, isBeforeDestinationNode))
return false;
return true;
};
var handleDragLeave = function (ev) {
ev.currentTarget.classList.remove(classes.dragOver);
};
var handleNodeReOrder = function (ev, destinationNode, parentNodeIdOfDestinationNode, addBeforeDestinationNode) {
ev.preventDefault();
ev.stopPropagation();
if (!draggedTreeNode)
return;
var newTree = Immutable.fromJS(tree).toJS();
var result = reOrderNodes(newTree, draggedTreeNode, destinationNode.id, parentNodeIdOfDestinationNode, addBeforeDestinationNode);
if (!result)
return;
draggedTreeNode.node.order = result.draggedTreeNodeOrder;
if (draggedTreeNode.parentNode &&
draggedTreeNode.parentNode.id !== parentNodeIdOfDestinationNode) {
deleteNode(newTree, draggedTreeNode.node.id, draggedTreeNode.parentNode.id);
}
setTree(newTree);
if (props.onNodeReOrder)
props.onNodeReOrder(draggedTreeNode.node, destinationNode);
};
var populateAllParentNodeIds = function (ids, node) {
var _a;
if (node.children)
ids.push(node.id);
(_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(function (x) {
populateAllParentNodeIds(ids, x);
});
};
var renderTree = function (parentNode, node, depth) {
return (React__default.createElement(StyledTreeItem, { key: node.id, isExpanded: expanded.some(function (x) { return x === node.id; }), nodeId: node.id, labelIcon: node.icon, labelIconUrl: node.iconUrl, node: node, depth: depth, draggable: parentNode !== null && props.enableDragAndDrop, onNodeReOrder: function (ev, isBeforeDestinationNode) {
return parentNode && handleNodeReOrder(ev, node, parentNode.id, isBeforeDestinationNode);
}, validateDragOver: function (isBeforeDestinationNode) {
return validateDragOver(node, depth, isBeforeDestinationNode);
}, onDrop: function (ev) { return handleDrop(ev, node); }, onDragLeave: handleDragLeave, onDragStart: function (ev) { return parentNode && handleDragStart(ev, parentNode, node, depth); }, onDragOver: function (ev) { return handleDragOver(ev, node, depth); }, onLabelClick: function () { var _a; return (_a = props.onNodeSelected) === null || _a === void 0 ? void 0 : _a.call(props, node); }, labelText: node.name }, node.children
? node.children
.sort(function (a, b) { return (a.order || 0) - (b.order || 0); })
.map(function (childNode) { return renderTree(node, childNode, depth + 1); })
: null));
};
var handleToggle = function (_e, nodeIds) {
setExpanded(nodeIds);
};
return (React__default.createElement("div", { className: props.className },
React__default.createElement("div", { className: classes.expandCollapseButtonContainer },
React__default.createElement("div", { className: classes.expandCollapseAllButton, onClick: function () { return setExpanded(allParentIds); } }, "ExpandAll"),
React__default.createElement("div", { className: classes.expandCollapseAllButton, onClick: function () { return setExpanded([props.tree.id]); } }, "CollapseAll")),
React__default.createElement("div", null,
React__default.createElement(MuiTreeView, __assign({}, props, { expanded: expanded, className: (_b = (_a = props.classes) === null || _a === void 0 ? void 0 : _a.root) !== null && _b !== void 0 ? _b : classes.root, defaultCollapseIcon: React__default.createElement(CollapseIcon, null), defaultExpandIcon: React__default.createElement(ExpandIcon, null), onNodeToggle: handleToggle }), renderTree(null, tree, 0)))));
};
var MuiDraggableTreeView$1 = styles.withTheme(MuiDraggableTreeView);
var CollapseIcon = styled(icons.IndeterminateCheckBox)(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n transform: scale(1.3);\n"], ["\n transform: scale(1.3);\n"])));
var ExpandIcon = styled(icons.AddBox)(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n transform: scale(1.3);\n"], ["\n transform: scale(1.3);\n"])));
var useStyles = styles.makeStyles(styles.createStyles({
root: {
flexGrow: 1,
},
expandCollapseAllButton: {
textDecoration: "underline",
color: "rgb(0, 145, 255)",
cursor: "pointer",
marginLeft: "10px",
},
expandCollapseButtonContainer: {
display: "flex",
justifyContent: "flex-end",
},
dragOver: {
backgroundColor: "#bdbdbd",
borderBottomRightRadius: "16px",
borderTopRightRadius: "16px",
transition: "opacity 200ms",
},
}));
var templateObject_1, templateObject_2;
exports.MuiDraggableTreeView = MuiDraggableTreeView$1;