@asup/tree-of-nodes
Version:
REACT Typescript Treeview component
539 lines (498 loc) • 25.3 kB
JavaScript
import "./main.css";
import {jsxs as $duWW8$jsxs, Fragment as $duWW8$Fragment, jsx as $duWW8$jsx} from "react/jsx-runtime";
import {ContextMenuHandler as $duWW8$ContextMenuHandler} from "@asup/context-menu";
import $duWW8$react, {useContext as $duWW8$useContext, useMemo as $duWW8$useMemo, useState as $duWW8$useState, useEffect as $duWW8$useEffect, useRef as $duWW8$useRef, useCallback as $duWW8$useCallback, createContext as $duWW8$createContext} from "react";
import {ExclamationCircleFill as $duWW8$ExclamationCircleFill, CaretRightFill as $duWW8$CaretRightFill, CaretDownFill as $duWW8$CaretDownFill, CaretRight as $duWW8$CaretRight} from "react-bootstrap-icons";
import {Spinner as $duWW8$Spinner} from "react-bootstrap";
function $parcel$export(e, n, v, s) {
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
}
var $b65191f6d0a0a991$exports = {};
$parcel$export($b65191f6d0a0a991$exports, "TreeNode", function () { return $47c36cf4281201f5$export$2eba8ec3a333fdbb; });
$parcel$export($b65191f6d0a0a991$exports, "TreeOfNodes", function () { return $ba47aaf387735a90$export$5580fff3e08a8be5; });
$parcel$export($b65191f6d0a0a991$exports, "WordEntry", function () { return $84765747a8c6bfe2$export$14f190ac910bde69; });
const $6303f0814ed0ce8b$export$c6fd3ab3f44300d0 = (id, included, hierarchyNodes)=>{
const processIds = Array.isArray(id) ? id : [
id
];
const current = processIds.map((i)=>hierarchyNodes.find((h)=>h.id === i)).filter((h)=>h !== undefined);
if (current.length === 0) return included;
const parents = current.map((h)=>h.parentId).map((pid)=>hierarchyNodes.find((h)=>pid === h.id)).filter((p)=>p !== undefined);
if (parents.length === 0) return included;
else return $6303f0814ed0ce8b$export$c6fd3ab3f44300d0(parents.map((p)=>p.id), [
...included,
...parents.filter((p)=>!included.includes(p))
], hierarchyNodes);
};
const $33abc00b1ca5c55c$export$bd923365b402cb04 = (id, hierarchyNodes)=>{
const processIds = Array.isArray(id) ? id : [
id
];
const newChildren = hierarchyNodes.filter((h)=>h.parentId !== undefined && processIds.includes(h.parentId) && !processIds.includes(h.id)).map((h)=>h.id);
return newChildren.length === 0 ? processIds : $33abc00b1ca5c55c$export$bd923365b402cb04([
...processIds,
...newChildren
], hierarchyNodes);
};
const $886f402bccb0fd30$export$29ec378200e05207 = (0, $duWW8$createContext)(undefined);
const $84765747a8c6bfe2$export$14f190ac910bde69 = /*#__PURE__*/ (0, $duWW8$react).forwardRef(({ id: id, value: value, editing: editing, saving: saving, setValue: setValue, sendEscape: sendEscape, style: style, spellCheck: spellCheck = "true" }, ref)=>{
const [currentValue, setCurrentValue] = (0, $duWW8$useState)(value !== null && value !== void 0 ? value : "");
(0, $duWW8$useEffect)(()=>{
setCurrentValue(value !== null && value !== void 0 ? value : "");
}, [
value
]);
const returnData = (0, $duWW8$useCallback)(()=>{
setValue === null || setValue === void 0 ? void 0 : setValue(currentValue);
}, [
currentValue,
setValue
]);
const keyPress = (0, $duWW8$useCallback)((e)=>{
if (e.code === "Escape") {
setCurrentValue(value !== null && value !== void 0 ? value : "");
sendEscape === null || sendEscape === void 0 ? void 0 : sendEscape();
} else if (e.code === "Enter") returnData();
}, [
returnData,
sendEscape,
value
]);
// Add window onMouseDown event to stop events when editing
(0, $duWW8$useEffect)(()=>{
const handleMouseDown = (e)=>{
const target = e.target;
if (editing && target.id !== `${id}` && target.id !== `${id}-holder` && target.id !== `${id}-spinner` && target.className !== "ton-rename") {
e.preventDefault();
e.stopPropagation();
returnData();
}
};
window.addEventListener("mousedown", handleMouseDown);
return ()=>{
window.removeEventListener("mousedown", handleMouseDown);
};
}, [
editing,
id,
returnData
]);
return !editing ? /*#__PURE__*/ (0, $duWW8$jsx)("span", {
className: "ton-label-span",
style: style,
id: `${id}`,
children: value ? value : /*#__PURE__*/ (0, $duWW8$jsx)((0, $duWW8$Fragment), {
children: "\xa0"
})
}) : /*#__PURE__*/ (0, $duWW8$jsxs)("div", {
style: style,
id: `${id}-holder`,
className: "ton-rename-holder",
children: [
saving && /*#__PURE__*/ (0, $duWW8$jsx)((0, $duWW8$Spinner), {
id: `${id}-spinner`,
size: "sm",
animation: "border"
}),
/*#__PURE__*/ (0, $duWW8$jsx)("input", {
autoFocus: true,
spellCheck: spellCheck,
id: String(id),
ref: ref,
type: "text",
className: "ton-rename",
value: currentValue,
disabled: saving,
onChange: (e)=>setCurrentValue(e.currentTarget.value),
onBlur: returnData,
onKeyDownCapture: keyPress
})
]
});
});
$84765747a8c6bfe2$export$14f190ac910bde69.displayName = "WordEntry";
const $47c36cf4281201f5$export$2eba8ec3a333fdbb = ({ id: id, canRemove: canRemove, canRename: canRename, canAddChildren: canAddChildren, canRemoveChildren: canRemoveChildren, canRenameChildren: canRenameChildren })=>{
// Contexts
const treeContext = (0, $duWW8$useContext)((0, $886f402bccb0fd30$export$29ec378200e05207));
// Node information
const thisNode = (0, $duWW8$useMemo)(()=>treeContext === null || treeContext === void 0 ? void 0 : treeContext.nodeList.find((n)=>n.id === id), [
id,
treeContext === null || treeContext === void 0 ? void 0 : treeContext.nodeList
]);
const childNodes = (0, $duWW8$useMemo)(()=>treeContext === null || treeContext === void 0 ? void 0 : treeContext.nodeList.filter((n)=>n.parentId === id), [
id,
treeContext === null || treeContext === void 0 ? void 0 : treeContext.nodeList
]);
const descendents = (0, $duWW8$useMemo)(()=>{
var _treeContext_nodeList;
return (0, $33abc00b1ca5c55c$export$bd923365b402cb04)(id, (_treeContext_nodeList = treeContext === null || treeContext === void 0 ? void 0 : treeContext.nodeList) !== null && _treeContext_nodeList !== void 0 ? _treeContext_nodeList : []);
}, [
id,
treeContext
]);
// State
const [error, setError] = (0, $duWW8$useState)(false);
const [errorText, setErrorText] = (0, $duWW8$useState)("");
const expanded = (0, $duWW8$useMemo)(()=>{
var _treeContext_expandedNodes_findIndex;
return ((_treeContext_expandedNodes_findIndex = treeContext === null || treeContext === void 0 ? void 0 : treeContext.expandedNodes.findIndex((e)=>e === id)) !== null && _treeContext_expandedNodes_findIndex !== void 0 ? _treeContext_expandedNodes_findIndex : -1) > -1;
}, [
id,
treeContext === null || treeContext === void 0 ? void 0 : treeContext.expandedNodes
]);
// Ensures that child nodes are drawn before they are expanded
const [hasRenderedClosed, setHasRenderedClosed] = (0, $duWW8$useState)(childNodes !== undefined && childNodes.length > 0);
(0, $duWW8$useEffect)(()=>{
if (childNodes && childNodes.length > 0 && !hasRenderedClosed) setHasRenderedClosed(true);
}, [
childNodes,
hasRenderedClosed
]);
// Checkbox
const checkRef = (0, $duWW8$useRef)(null);
(0, $duWW8$useEffect)(()=>{
if (checkRef.current) {
if (treeContext === null || treeContext === void 0 ? void 0 : treeContext.selected.includes(id)) {
checkRef.current.checked = true;
checkRef.current.indeterminate = false;
} else if (descendents.some((d)=>treeContext === null || treeContext === void 0 ? void 0 : treeContext.selected.includes(d))) {
checkRef.current.checked = false;
checkRef.current.indeterminate = true;
} else {
checkRef.current.checked = false;
checkRef.current.indeterminate = false;
}
}
}, [
descendents,
id,
treeContext === null || treeContext === void 0 ? void 0 : treeContext.selected
]);
// Apply selected border
const currentBorder = (0, $duWW8$useMemo)(()=>{
return (treeContext === null || treeContext === void 0 ? void 0 : treeContext.selected.includes(id)) ? "1px solid black" : "";
}, [
id,
treeContext === null || treeContext === void 0 ? void 0 : treeContext.selected
]);
const nodeColour = (0, $duWW8$useMemo)(()=>{
if (treeContext && treeContext.selected) {
const anc = (0, $6303f0814ed0ce8b$export$c6fd3ab3f44300d0)(treeContext.selected, [], treeContext === null || treeContext === void 0 ? void 0 : treeContext.nodeList).map((n)=>n.id);
if ([
...anc,
treeContext.selected
].includes(id)) return treeContext.nodeHighlight;
}
return;
}, [
id,
treeContext
]);
// New node parameters
const newNameRef = (0, $duWW8$useRef)(null);
const [showNewNode, setShowNewNode] = (0, $duWW8$useState)(false);
const [savingNewNode, setSavingNewNode] = (0, $duWW8$useState)(false);
// Rename parameters
const currentNameRef = (0, $duWW8$useRef)(null);
const [renaming, setRenaming] = (0, $duWW8$useState)(false);
const [updatingNode, setUpdatingNode] = (0, $duWW8$useState)(false);
// Context functions
const handleReturn = (0, $duWW8$useCallback)((ret)=>{
if (!ret.success) {
setError(true);
var _ret_ErrorText;
setErrorText((_ret_ErrorText = ret.ErrorText) !== null && _ret_ErrorText !== void 0 ? _ret_ErrorText : "An unknown error has occured");
} else {
setError(false);
setErrorText("");
}
}, []);
const confirmNewNode = (0, $duWW8$useCallback)(async (label)=>{
setSavingNewNode(true);
if (treeContext === null || treeContext === void 0 ? void 0 : treeContext.onAddChild) handleReturn(await treeContext.onAddChild(id, label));
setSavingNewNode(false);
setShowNewNode(false);
}, [
handleReturn,
id,
treeContext
]);
const renameNode = (0, $duWW8$useCallback)(async (newLabel)=>{
setRenaming(false);
if (treeContext === null || treeContext === void 0 ? void 0 : treeContext.onRename) {
setUpdatingNode(true);
handleReturn(await treeContext.onRename(id, newLabel));
setUpdatingNode(false);
}
}, [
handleReturn,
id,
treeContext
]);
const deleteThis = (0, $duWW8$useCallback)(async ()=>{
if (treeContext === null || treeContext === void 0 ? void 0 : treeContext.onRemove) {
setUpdatingNode(true);
handleReturn(await treeContext.onRemove(id));
setUpdatingNode(false);
}
}, [
handleReturn,
id,
treeContext
]);
// Context actions
const addChild = (0, $duWW8$useCallback)(()=>{
var _treeContext_handleExpandClick;
setShowNewNode(true);
treeContext === null || treeContext === void 0 ? void 0 : (_treeContext_handleExpandClick = treeContext.handleExpandClick) === null || _treeContext_handleExpandClick === void 0 ? void 0 : _treeContext_handleExpandClick.call(treeContext, id, true);
}, [
id,
treeContext
]);
(0, $duWW8$useEffect)(()=>{
if (showNewNode === true && newNameRef.current) newNameRef.current.focus();
}, [
showNewNode
]);
const renameThis = (0, $duWW8$useCallback)(()=>{
var _treeContext_handleSelect;
setRenaming(true);
treeContext === null || treeContext === void 0 ? void 0 : (_treeContext_handleSelect = treeContext.handleSelect) === null || _treeContext_handleSelect === void 0 ? void 0 : _treeContext_handleSelect.call(treeContext, id);
}, [
id,
treeContext
]);
// Context menu
const menuItems = (0, $duWW8$useMemo)(()=>{
const menuItems = [];
if (canAddChildren) menuItems.push({
label: "Add",
action: addChild
});
if (canRename) menuItems.push({
label: "Rename",
action: renameThis
});
if (canRemove && (childNodes === undefined || childNodes.length === 0)) menuItems.push({
label: "Delete",
action: deleteThis
});
return menuItems;
}, [
addChild,
canAddChildren,
canRemove,
canRename,
childNodes,
deleteThis,
renameThis
]);
// Return node
return !treeContext ? null : /*#__PURE__*/ (0, $duWW8$jsxs)((0, $duWW8$Fragment), {
children: [
/*#__PURE__*/ (0, $duWW8$jsx)((0, $duWW8$ContextMenuHandler), {
menuItems: menuItems,
style: {
display: "inline-block",
width: "100%"
},
children: /*#__PURE__*/ (0, $duWW8$jsx)("div", {
id: `${treeContext.id}-treenode-${id}`,
className: "treenode",
children: error ? /*#__PURE__*/ (0, $duWW8$jsxs)((0, $duWW8$Fragment), {
children: [
/*#__PURE__*/ (0, $duWW8$jsx)((0, $duWW8$ExclamationCircleFill), {
color: "var(--bs-danger)"
}),
" ",
errorText
]
}) : /*#__PURE__*/ (0, $duWW8$jsxs)("div", {
className: "ton-node",
children: [
treeContext.showCheckBox && /*#__PURE__*/ (0, $duWW8$jsx)("input", {
ref: checkRef,
type: "checkbox",
role: "checkbox",
className: "ton-checkbox",
id: `${treeContext.id}-treenode-checkbox-${id}`,
onClick: ()=>{
var _treeContext_handleSelect;
return (_treeContext_handleSelect = treeContext.handleSelect) === null || _treeContext_handleSelect === void 0 ? void 0 : _treeContext_handleSelect.call(treeContext, descendents);
}
}),
childNodes && childNodes.length > 0 ? !expanded ? /*#__PURE__*/ (0, $duWW8$jsx)((0, $duWW8$CaretRightFill), {
id: `${treeContext.id}-treenode-caret-${id}`,
style: {
width: "16px",
height: "16px"
},
className: "ton-expander",
role: "button",
color: nodeColour,
"aria-expanded": false,
"aria-label": "Expander",
onClick: ()=>treeContext.handleExpandClick(id)
}) : /*#__PURE__*/ (0, $duWW8$jsx)((0, $duWW8$CaretDownFill), {
id: `${treeContext.id}-treenode-caret-${id}`,
className: "ton-expander",
role: "button",
"aria-expanded": true,
"aria-label": "Expander",
color: nodeColour,
onClick: ()=>treeContext.handleExpandClick(id)
}) : /*#__PURE__*/ (0, $duWW8$jsx)((0, $duWW8$CaretRight), {
id: `${treeContext.id}-treenode-caret-${id}`,
className: "ton-expander",
role: "button",
color: nodeColour,
"aria-expanded": false,
"aria-label": "Disabled expander",
onClick: ()=>{
var _treeContext_handleSelect;
return (_treeContext_handleSelect = treeContext.handleSelect) === null || _treeContext_handleSelect === void 0 ? void 0 : _treeContext_handleSelect.call(treeContext, id);
}
}),
thisNode && /*#__PURE__*/ (0, $duWW8$jsx)("div", {
className: "ton-label",
style: {
width: `calc(100% - ${treeContext.showCheckBox ? "32px" : "16px"})`
},
onContextMenuCapture: ()=>{
var _treeContext_handleSelect;
return (_treeContext_handleSelect = treeContext.handleSelect) === null || _treeContext_handleSelect === void 0 ? void 0 : _treeContext_handleSelect.call(treeContext, id);
},
onClickCapture: ()=>{
var _treeContext_handleSelect;
if (!treeContext.expandedNodes.includes(id)) treeContext.handleExpandClick(id);
(_treeContext_handleSelect = treeContext.handleSelect) === null || _treeContext_handleSelect === void 0 ? void 0 : _treeContext_handleSelect.call(treeContext, id);
},
onFocusCapture: ()=>{
var _treeContext_handleSelect;
return (_treeContext_handleSelect = treeContext.handleSelect) === null || _treeContext_handleSelect === void 0 ? void 0 : _treeContext_handleSelect.call(treeContext, id);
},
children: /*#__PURE__*/ (0, $duWW8$jsx)((0, $84765747a8c6bfe2$export$14f190ac910bde69), {
id: `${treeContext.id}-treenode-entry-${id}`,
ref: currentNameRef,
value: thisNode.label,
editing: renaming,
saving: updatingNode,
setValue: (ret)=>renameNode(ret),
sendEscape: ()=>setRenaming(false),
style: {
border: currentBorder,
margin: currentBorder === "" ? "1px" : "",
backgroundColor: currentBorder === "" ? "" : treeContext.textHighlight
},
spellCheck: treeContext.spellCheck
})
})
]
})
})
}),
childNodes && /*#__PURE__*/ (0, $duWW8$jsx)("div", {
className: `ton-collapsible-wrapper ${expanded && hasRenderedClosed && childNodes.length > 0 ? "" : "collapsed"}`,
children: childNodes.length > 0 && /*#__PURE__*/ (0, $duWW8$jsx)("div", {
className: "ton-collapsible",
children: childNodes.map((h)=>/*#__PURE__*/ (0, $duWW8$jsx)("span", {
id: `${treeContext.id}-treenode-child-${h.id}`,
className: "ton-child",
children: /*#__PURE__*/ (0, $duWW8$jsx)($47c36cf4281201f5$export$2eba8ec3a333fdbb, {
id: h.id,
canRemove: canRemoveChildren,
canRename: canRenameChildren,
canAddChildren: canAddChildren,
canRemoveChildren: canRemoveChildren,
canRenameChildren: canRenameChildren
})
}, h.id))
})
}),
showNewNode && /*#__PURE__*/ (0, $duWW8$jsx)((0, $84765747a8c6bfe2$export$14f190ac910bde69), {
style: {
marginLeft: "14px"
},
id: `treenode-new-${id}`,
ref: newNameRef,
editing: true,
setValue: (ret)=>confirmNewNode(ret),
saving: savingNewNode,
sendEscape: ()=>setShowNewNode(false),
spellCheck: treeContext.spellCheck
})
]
});
};
$47c36cf4281201f5$export$2eba8ec3a333fdbb.displayName = "TreeNode";
const $c7008d808d3e1c7a$export$40c47a7ac5f85aba = (nodeList, selected, expandedNodes)=>{
return [
...expandedNodes,
...(0, $6303f0814ed0ce8b$export$c6fd3ab3f44300d0)(selected, [], nodeList).map((n)=>n.id).filter((k)=>!expandedNodes.includes(k))
].sort((a, b)=>`${a}`.localeCompare(`${b}`));
};
const $ba47aaf387735a90$export$5580fff3e08a8be5 = ({ id: id, nodeList: nodeList, roots: roots, selected: selected = [], showCheckBox: showCheckBox = false, handleSelect: handleSelect, onAdd: onAdd, onRename: onRename, onRemove: onRemove, canRemoveRoot: canRemoveRoot = false, canRenameRoot: canRenameRoot = false, canAddChildren: canAddChildren = false, canRemoveChildren: canRemoveChildren = false, canRenameChildren: canRenameChildren = false, nodeHighlight: nodeHighlight = "red", textHighlight: textHighlight = "rgba(255, 0, 0, 0.2)", spellCheck: spellCheck = "true" })=>{
const [expandedNodes, setExpandedNodes] = (0, $duWW8$useState)([]);
const selectedArray = (0, $duWW8$useMemo)(()=>Array.isArray(selected) ? selected : [
selected
], [
selected
]);
(0, $duWW8$useEffect)(()=>{
if (selectedArray.length > 0) {
// Check all nodes are showing that should be
const shouldExpand = (0, $c7008d808d3e1c7a$export$40c47a7ac5f85aba)(nodeList, selectedArray, expandedNodes);
if (shouldExpand.filter((n)=>!expandedNodes.includes(n)).length > 0) setExpandedNodes(shouldExpand);
}
}, [
expandedNodes,
nodeList,
selectedArray
]);
// Change expansion and update contexts
const changeExpand = (0, $duWW8$useCallback)((eKey, force)=>{
const newExpandedNodes = [
...expandedNodes
];
const ix = newExpandedNodes.findIndex((nKey)=>nKey === eKey);
if (ix === -1 && force === undefined || force === true) newExpandedNodes.push(eKey);
else newExpandedNodes.splice(ix, 1);
setExpandedNodes(newExpandedNodes);
}, [
expandedNodes
]);
return /*#__PURE__*/ (0, $duWW8$jsx)("div", {
id: id,
className: "tree-of-nodes",
children: /*#__PURE__*/ (0, $duWW8$jsx)((0, $886f402bccb0fd30$export$29ec378200e05207).Provider, {
value: {
id: id,
nodeList: nodeList,
selected: selectedArray,
showCheckBox: showCheckBox,
handleSelect: handleSelect,
expandedNodes: expandedNodes,
handleExpandClick: (r, f)=>changeExpand(r, f),
onAddChild: onAdd,
onRename: onRename,
onRemove: onRemove,
nodeHighlight: nodeHighlight,
textHighlight: textHighlight,
spellCheck: spellCheck
},
children: roots.map((r)=>/*#__PURE__*/ (0, $duWW8$jsx)((0, $47c36cf4281201f5$export$2eba8ec3a333fdbb), {
id: r,
canRemove: canRemoveRoot,
canAddChildren: canAddChildren,
canRename: canRenameRoot,
canRemoveChildren: canRemoveChildren,
canRenameChildren: canRenameChildren
}, r))
})
});
};
$ba47aaf387735a90$export$5580fff3e08a8be5.displayName = "TreeOfNodes";
var $3d005c8713d7dfb7$exports = {};
$parcel$export($3d005c8713d7dfb7$exports, "getParents", function () { return $6303f0814ed0ce8b$export$c6fd3ab3f44300d0; });
export {$47c36cf4281201f5$export$2eba8ec3a333fdbb as TreeNode, $ba47aaf387735a90$export$5580fff3e08a8be5 as TreeOfNodes, $84765747a8c6bfe2$export$14f190ac910bde69 as WordEntry, $6303f0814ed0ce8b$export$c6fd3ab3f44300d0 as getAncestors};
//# sourceMappingURL=main.js.map