UNPKG

@nodeject/ui-components

Version:

UI library for non-trivial components

754 lines (752 loc) 30.7 kB
var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(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); }; var __rest = (this && this.__rest) || function (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; }; var __spreadArrays = (this && this.__spreadArrays) || function () { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; import produce from 'immer'; import { createContainer } from 'unstated-next'; import { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { arrayToTree } from '../../arrayToTree'; import { LayoutStyle } from '../dtos'; var useDnDTree = function (props) { var mouseDownRef = useRef(0); var _a = useState(0), mouseDown = _a[0], setMouseDown = _a[1]; mouseDownRef.current = mouseDown; document.body.onmousedown = function () { setMouseDown(function (m) { return m + 1; }); }; document.body.onmouseup = function () { setMouseDown(function (m) { return m - 1; }); setIsDragValid(null); setNodeDraggingId(null); }; var actionsFromServer = props.actions, activeNode = props.activeNode, components = props.components, onNodeClicked = props.onNodeClicked, treeMode = props.treeMode; var treeOptionsDefault = { canEditTreeStructure: false, canExpandCollapse: true }; var treeOptions = __assign(__assign({}, treeOptionsDefault), props.treeOptions); // const [treeDataFromServer, setTreeDataFromServer] = useState<TreeData>( // props.treeData // ) var treeDataFromServer = props.treeData; var _b = useState(), newNode = _b[0], setNewNode = _b[1]; var _c = useState({ saveNewNode: null }), saveNewNode = _c[0], setSaveNewNode = _c[1]; var _d = useState(props.treeData), treeDataLocal = _d[0], setTreeDataLocal = _d[1]; var _e = useState({ id: '', isExpanded: false }), storedDraggedNodePreviousExpandedStatus = _e[0], setStoredDraggedNodePreviousExpandedStatus = _e[1]; var _f = useState(Object.assign.apply(Object, __spreadArrays([{}], props.treeData.nodes.map(function (item) { var _a; return _a = {}, _a[item.data.id] = { isExpanded: false }, _a; })))), expandedNodes = _f[0], setExpandedNodes = _f[1]; var _g = useState(getUnflattenedTreeData(props.treeData.nodes)), unflattenedTreeData = _g[0], setUnflattenedTreeData = _g[1]; var _h = useState(''), draggingId = _h[0], setDraggingId = _h[1]; // Synchronizes local component state with new data coming from server (props) useEffect(function () { setTreeDataLocal(treeDataFromServer); setExpandedNodes(function (expandedNodes) { return Object.assign.apply(Object, __spreadArrays([{}], treeDataFromServer.nodes.map(function (item) { var _a; return _a = {}, _a[item.data.id] = { isExpanded: expandedNodes[item.data.id] }, _a; }))); }); }, [JSON.stringify(treeDataFromServer.nodes)]); // Reconciliates draggingId and expandedNodes // It sets bach the dragged node to previous collapsed or expanded status useEffect(function () { if (draggingId === '') { if (storedDraggedNodePreviousExpandedStatus.id !== '') { setExpandedNodes(function (expandedNodes) { var newExpandedNodes = produce(expandedNodes, function (draft) { draft[storedDraggedNodePreviousExpandedStatus.id] = { isExpanded: storedDraggedNodePreviousExpandedStatus.isExpanded }; }); return newExpandedNodes; }); } } }, [draggingId]); var _j = useState(null), draggedId = _j[0], setIsDragValid = _j[1]; var isDragValidRef = useRef(null); isDragValidRef.current = draggedId; // treeDataFromServer useLayoutEffect(function () { var nextUnflattenedTreeData = produce(treeDataLocal, function (draftState) { return getUnflattenedTreeData(draftState.nodes); }); setUnflattenedTreeData(nextUnflattenedTreeData); // console.log( // 'AFTER', // JSON.stringify( // treeDataFromServer.nodes.map((n, i) => { // return `id: ${n.data.id}, index: ${i} }` // }), // null, // 2 // ) // ) // console.log('_______________________________________-') }, [JSON.stringify(treeDataLocal)]); var setNodeDraggingId = function (nodeId) { setDraggingId(nodeId); }; var timeoutBeforeCollapsingNodeChildren; // onClick on Node handle var onMouseDownHandle = function (nodeId) { // // setDraggingId(nodeId) // collapseNode(nodeId) // console.log('HANDLE', nodeId) if (treeMode === 'dragdrop' && nodeId !== isDragValidRef.current) { setIsDragValid(nodeId); // console.log('onMouseDown set to true', draggedId) timeoutBeforeCollapsingNodeChildren = setTimeout(function () { if (isDragValidRef.current) { // console.log('onMouseDown test', isDragValidRef.current) var mouse = mouseDownRef.current !== 0 ? true : false; if (mouse) { onMouseDownHandle(nodeId); setStoredDraggedNodePreviousExpandedStatus({ id: nodeId, isExpanded: expandedNodes[nodeId].isExpanded }); setNodeDraggingId(nodeId); } } }, 100); } }; var onMouseUpHandle = function (nodeId) { // // setDraggingId(nodeId) // collapseNode(nodeId) // console.log('HANDLE', nodeId) if (treeMode === 'dragdrop') { setIsDragValid(null); if (timeoutBeforeCollapsingNodeChildren) { clearTimeout(timeoutBeforeCollapsingNodeChildren); } // setStoredDraggedNodePreviousExpandedStatus({ // id: null, // isExpanded: expandedNodes[nodeId].isExpanded // }) setNodeDraggingId(null); } }; /** const onMouseUp = () => { console.log('MouseUp') setIsDragValid(false) if (timeoutBeforeCollapsingNodeChildren) { clearTimeout(timeoutBeforeCollapsingNodeChildren) } } const onMouseDown = () => { if (canDragDrop) { setIsDragValid(true) console.log('onMouseDown set to true', isDragValid) timeoutBeforeCollapsingNodeChildren = setTimeout(() => { if (isDragValidRef.current) { console.log('onMouseDown test', isDragValidRef.current) onMouseDownHandle(nodeId) } }, 100) } } */ var createNewNode = function (args) { // create new node var newNode = props.actions.createNewNode(args); setNewNode(newNode.data.id); // updates collapsedNodes setExpandedNodes(function (collapsedNodes) { var nextCollapsedNodes = produce(collapsedNodes, function (draft) { var newCollapsedNode = { isExpanded: false }; draft[newNode.data.id] = newCollapsedNode; }); return nextCollapsedNodes; }); return newNode; }; /** * Add child node * @param nodeId */ var addChildNode = function (nodeId) { // Early exit if (!nodeId) { return; } // console.log('addChildNodeClient LOCAL') // create new node var child = createNewNode({ data: { parent: nodeId, layoutStyle: LayoutStyle.List } }); // add new node as a child locally setTreeDataLocal(function (treeDataLocal) { var nextTreeDataLocal = produce(treeDataLocal, function (draftState) { var index; // Find index of node's deepest descendant, and insert child after this index var descendants = getDescendantsFromFlatNode(nodeId, unflattenedTreeData); if (descendants.length > 0) { var deepestDescendantId_1 = descendants[descendants.length - 1].data.id; var deepestDescendantIndex = treeDataLocal.nodes.findIndex(function (n) { return n.data.id === deepestDescendantId_1; }); index = deepestDescendantIndex + 1; } else { index = treeDataLocal.nodes.findIndex(function (n) { return n.data.id === nodeId; }) + 1; } // insert child at index draftState.nodes.splice(index, 0, child); }); return nextTreeDataLocal; }); // add child on server if (actionsFromServer.addChildNode) { var saveNewNodeFn = function (data) { return actionsFromServer .addChildNode({ component: nodeId, newComponent: child.data.id, data: data }) .then(function () { return setNewNode(''); }); // resets }; setSaveNewNode({ saveNewNode: saveNewNodeFn }); } else { console.warn('actionsFromServer is not implemented. Please provide a actionsFromServer function'); } }; /** * Delete node and all descendants * @param nodeId */ var deleteNode = function (nodeId) { // Find nodeIndex to delete, find descendants count and add to the nodeIndex var node = treeDataLocal.nodes.find(function (n) { return n.data.id === nodeId; }); var nodeIndexToDelete = treeDataLocal.nodes.findIndex(function (n) { return n.data.id === nodeId; }); if (nodeIndexToDelete === -1 || !node) { return; } // Delete locally setTreeDataLocal(function (treeDataLocal) { var nextTreeDataLocal = produce(treeDataLocal, function (draftState) { var descendantsCount = getDescendantsFromFlatNode(node.data.id, unflattenedTreeData).length; draftState.nodes.splice(nodeIndexToDelete, 1 + descendantsCount); }); return nextTreeDataLocal; }); // Delete on server if (actionsFromServer.deleteNode) { actionsFromServer.deleteNode(nodeId); } else { console.warn('deleteNode is not implemented. Please provide a deleteNode function'); } }; /** * Insert sibling before * @param args */ var insertSiblingBefore = function (beforeNodeId) { // Can't insert a sibling to a node that does not have parent var nodeParentId = treeDataLocal.nodes.find(function (n) { return n.data.id === beforeNodeId; }).data.parent; if (!nodeParentId) { return; } var newNode = createNewNode({ data: { layoutStyle: LayoutStyle.List, parent: nodeParentId } }); setTreeDataLocal(function (treeDataLocal) { // insert sibling locally var nextTreeDataLocal = produce(treeDataLocal, function (draftState) { // find beforeNode's index var beforeNodeIndex = treeDataLocal.nodes.findIndex(function (n) { return n.data.id === beforeNodeId; }); draftState.nodes.splice(beforeNodeIndex, 0, newNode); }); return nextTreeDataLocal; }); // insert sibling on server if (actionsFromServer.insertSiblingBefore) { var saveNewNodeFn = function (data) { return actionsFromServer .insertSiblingBefore({ component: beforeNodeId, newComponent: newNode.data.id, data: data }) .then(function () { return setNewNode(''); }); // resets }; setSaveNewNode({ saveNewNode: saveNewNodeFn }); } else { console.warn('actionsFromServer is not implemented. Please provide a actionsFromServer function'); } }; /** * Insert sibling after * @param args */ var insertSiblingAfter = function (afterNodeId) { // Can't insert a sibling to a node that does not have parent var nodeParentId = treeDataLocal.nodes.find(function (n) { return n.data.id === afterNodeId; }).data.parent; if (!nodeParentId) { return; } var newNode = createNewNode({ data: { layoutStyle: LayoutStyle.List, parent: nodeParentId } }); setTreeDataLocal(function (treeDataLocal) { // insert sibling locally var nextTreeDataLocal = produce(treeDataLocal, function (draftState) { var afterNodeIndex = treeDataLocal.nodes.findIndex(function (n) { return n.data.id === afterNodeId; }); // find node's index // const descendants = getDescendantsFromFlatNode( // afterNodeId, // unflattenedTreeData // ) // let index: number = // treeDataLocal.nodes.findIndex( // (n) => n.data.id === afterNodeId // ) + 1 // if (descendants.length > 0) { // const deepestDescendant = // descendants[descendants.length - 1] // const deepestDescendantIndex = treeDataLocal.nodes.findIndex( // (n) => n.data.id === deepestDescendant.data.id // ) // index = deepestDescendantIndex + 1 // } // insert new node before index draftState.nodes.splice(afterNodeIndex + 1, 0, newNode); }); return nextTreeDataLocal; }); // insert sibling on server if (actionsFromServer.insertSiblingAfter) { var saveNewNodeFn = function (data) { return actionsFromServer .insertSiblingAfter({ component: afterNodeId, newComponent: newNode.data.id, data: data }) .then(function () { return setNewNode(''); }); // resets }; setSaveNewNode({ saveNewNode: saveNewNodeFn }); } else { console.warn('actionsFromServer is not implemented. Please provide a actionsFromServer function'); } }; /** * Insert parent * @param nodeId */ var insertParent = function (nodeId) { // const node = treeDataFromServer.nodes.find(n => n.data.id === nodeId) var newParentId; setTreeDataLocal(function (treeDataLocal) { // Insert locally var nextTreeDataLocal = produce(treeDataLocal, function (draftState) { var node = treeDataLocal.nodes.find(function (n) { return n.data.id === nodeId; }); var newParentNode = createNewNode({ data: { layoutStyle: LayoutStyle.List, parent: node.data.parent } }); newParentId = newParentNode.data.id; var newNode = produce(node, function (draftNodeState) { draftNodeState.data.parent = newParentNode.data.id; }); // Find current node and replace it with new node with updated parent id var nodeIndex = treeDataLocal.nodes.findIndex(function (n) { return n.data.id === nodeId; }); draftState.nodes.splice(nodeIndex, 1, newNode); // Insert parent before node's index draftState.nodes.splice(nodeIndex - 1, 0, newParentNode); }); return nextTreeDataLocal; }); if (actionsFromServer.insertParent) { var saveNewNodeFn = function (data) { return actionsFromServer .insertParent({ component: nodeId, newComponent: newParentId, data: data }) .then(function () { return setNewNode(''); }); // resets }; setSaveNewNode({ saveNewNode: saveNewNodeFn }); } else { console.warn('insertParent is not implemented. Please provide a insertParent function'); } }; var remove = function (arr, index) { var removedElement = arr[index]; var newArray = produce(arr, function (draft) { draft.splice(index, 1)[0]; return draft; }); return { array: newArray, removedElement: removedElement }; }; var insert = function (arr, atIndex, element) { var newArr = produce(arr, function (draft) { draft.splice(atIndex, 0, element); }); return newArr; }; var move = function (args) { var arr = args.arr, id = args.id, toId = args.toId, newElement = args.newElement; var fromNode = arr.find(function (n) { return n.data.id === id; }); var toNode = arr.find(function (n) { return n.data.id === toId; }); var fromIndex = arr.findIndex(function (n) { return n.data.id === id; }); var array = remove(arr, fromIndex).array; var toIndex = toId ? array.findIndex(function (n) { return n.data.id === toId; }) : array.length; // console.log('MOVE START___________________') // console.log( // 'Before remove node', // arr.map((n, index) => `${(n as any).data.wbsId} | ${index}`), // newElement // ) // console.log( // 'After remove node', // array.map((n, index) => `${(n as any).data.wbsId} | ${index}`), // newElement // ) var finalIndex = toIndex; if (toIndex === array.length - 1) { finalIndex = toIndex + 1; // console.log('A, CASE move to very end') } if (fromIndex < toIndex) { if (fromNode.data.parent === toNode.data.parent) { finalIndex = toIndex + 1; // console.log('B-1') } else { finalIndex = toIndex; // console.log('B-2') } } if (fromIndex === toIndex) { if (fromNode.data.parent === toNode.data.parent) { finalIndex = toIndex + 1; // console.log('C-1') } else { finalIndex = toIndex; // console.log('C-2') } } if (fromIndex > toIndex) { finalIndex = toIndex; // console.log('D') } if (newElement.data.parent === toId) { // console.log('WARNING, need to do this finalIndex case') } // console.log('finalIndex', finalIndex) var newArr = insert(array, finalIndex, newElement); var sorted = sortByLevels(newArr); // console.log( // 'MOVE COMPLETED', // sorted.map((n, index) => `${(n as any).data.wbsId} | ${index}`) // ) return sorted; }; var sortByLevels = function (arr) { var newArr = produce(arr, function (draft) { var levels = []; var root = draft.find(function (n) { return n.data.parent === null; }); var loop = function (arr, node) { // console.log(node.data.wbsId) var level = getHierarchyLevel(arr, node.data.id); var arrayIndex = level - 1; while (!levels[arrayIndex]) { levels.push([]); } levels[arrayIndex].push(node); var children = arr.filter(function (n) { return n.data.parent === node.data.id; }); children.map(function (c) { loop(arr, c); }); }; loop(draft, root); // console.log('levels', levels) return [].concat.apply([], levels); }); return newArr; }; /** * Root level = 1. Child of root level = 2. Child of Chilf of root level = 3. Etc. * @param arr * @param itemId */ var getHierarchyLevel = function (arr, itemId) { var level = 1; var item = arr.find(function (i) { return i.data.id === itemId; }); while (item.data.parent) { level += 1; item = arr.find(function (i) { return i.data.id === item.data.parent; }); } return level; }; /** * Move node * @param args */ var moveNode = function (args) { var _a; var index = args.index, parentContainerNode = args.parentContainerNode; var _b = args.droppedNode, children = _b.children, droppedNode = __rest(_b, ["children"]); // console.log('___________START MOVE_________________') // console.log( // 'droppedNode', // parentContainerNode?.data.wbsId, // droppedNode.data.wbsId, // index // ) var isNotDroppingInDescendant = true; // !isDroppingNodeInDescendant({ // index, // droppedNode, // parentContainerNode // }) var droppedOnNode; if (parentContainerNode.children.length === 0) { droppedOnNode = treeDataLocal.nodes.find(function (n) { return n.data.id === parentContainerNode.data.id; }); } else if (index > parentContainerNode.children.length - 1) { var i = treeDataLocal.nodes.findIndex(function (n) { return n.data.id === parentContainerNode.children[index - 1].data.id; }); droppedOnNode = treeDataLocal.nodes[i + 1] || treeDataLocal.nodes[i]; } else { droppedOnNode = treeDataLocal.nodes.find(function (n) { return n.data.id === parentContainerNode.children[index].data.id; }); } var isNotDroppingAtSameIndex = droppedNode.data.id !== ((_a = parentContainerNode.children[index]) === null || _a === void 0 ? void 0 : _a.data.id); var allowMoveNode = isNotDroppingInDescendant && isNotDroppingAtSameIndex; if (allowMoveNode) { setTreeDataLocal(function (treeDataLocal) { var nextDroppedNode = produce(droppedNode, function (draftDroppedNode) { return __assign(__assign({}, draftDroppedNode), { data: __assign(__assign({}, draftDroppedNode.data), { parent: parentContainerNode.data.id }) }); }); var nodes = move({ arr: treeDataLocal.nodes, id: droppedNode.data.id, toId: droppedOnNode.data.id, newElement: nextDroppedNode }); // console.log('AFTER MOVE') // console.log(nodes.map((n, i) => `${n.data.wbsId}, ${i}`)) var nextTreeDataLocal = { nodes: nodes }; return nextTreeDataLocal; }); if (actionsFromServer.moveNode) { actionsFromServer.moveNode({ index: index, nodeKey: droppedNode.data.id, parentKey: parentContainerNode.data.id }); } else { console.warn('insertParent is not implemented. Please provide a insertParent function'); } } // reset node dragging id setNodeDraggingId(''); }; var collapseNode = function (nodeId) { var nextCollapsedNodes = produce(expandedNodes, function (draft) { draft[nodeId].isExpanded = false; }); setExpandedNodes(nextCollapsedNodes); }; var expandNode = function (nodeId) { var nextCollapsedNodes = produce(expandedNodes, function (draft) { draft[nodeId].isExpanded = true; }); setExpandedNodes(nextCollapsedNodes); }; var toggleNode = function (nodeId) { var nextCollapsedNodes = produce(expandedNodes, function (draft) { draft[nodeId].isExpanded = !draft[nodeId].isExpanded; }); setExpandedNodes(nextCollapsedNodes); }; var actions = { addChildNode: addChildNode, collapseNode: collapseNode, expandNode: expandNode, toggleNode: toggleNode, createNewNode: createNewNode, deleteNode: deleteNode, insertSiblingBefore: insertSiblingBefore, insertParent: insertParent, insertSiblingAfter: insertSiblingAfter, moveNode: moveNode, saveNewNode: saveNewNode.saveNewNode, setNodeDraggingId: setNodeDraggingId }; return { actions: actions, activeNode: activeNode, events: { onMouseDownHandle: onMouseDownHandle, onMouseUpHandle: onMouseUpHandle }, components: components, onNodeClicked: onNodeClicked, treeMode: treeMode, treeOptions: treeOptions, state: { expandedNodes: expandedNodes, draggingId: draggingId, newNodeId: newNode, unflattenedTreeData: unflattenedTreeData } }; }; export var DnDTreeContainer = createContainer(useDnDTree); var getUnflattenedTreeData = function (nodes) { return { nodes: arrayToTree(nodes, { dataField: null, id: 'data.id', parentId: 'data.parent' }) }; }; var getDeepestDescendant = function (node) { var childenCount = node.children.length; if (childenCount > 0) { return getDeepestDescendant(node.children[childenCount - 1]); } else { return node.data.id; } }; var getDescendants = function (node, descendants) { if (!node) { return []; } var desc = descendants || []; node.children.map(function (c) { desc.push(c); getDescendants(c, desc); }); return desc; }; var getDescendantsFromFlatNode = function (nodeId, treeData) { var treeNodeUnflattened = null; var getTreeNodeUnflattened = function (nodes) { for (var i = 0; i < nodes.length; i++) { if (treeNodeUnflattened) { break; } if (nodes[i].data.id === nodeId) { treeNodeUnflattened = nodes[i]; break; } else { getTreeNodeUnflattened(nodes[i].children); } } }; getTreeNodeUnflattened(treeData.nodes); return getDescendants(treeNodeUnflattened); }; /** * Returns the position index of the node within the flat treeData list * @param node * @param flatTreeData */ var getNodeIndex = function (node, flatTreeData) { if (typeof node === 'string') { return flatTreeData.nodes.findIndex(function (n) { return n.data.id === node; }); } else { return flatTreeData.nodes.findIndex(function (n) { return n.data.id === node.data.id; }); } }; var isDroppedAsLastChild = function (args) { var index = args.index, parent = args.parent; return parent.children[index] === undefined; }; var branchNodeQty = function (node) { var nodeCount = 1; var descendantsCount = getDescendants(node).length; return descendantsCount + nodeCount; }; var indexAfterNode = function (parentIndex) { return parentIndex + 1; }; var isNodeInserted = function (node, parent) { return node.data.parent !== parent.data.id; }; var arrayToObject = function (args) { var array = args.array, id = args.id, value = args.value; if (typeof value === 'string') { return Object.assign.apply(Object, __spreadArrays([{}], array.map(function (item) { var _a; return (_a = {}, _a[getItemNestedProperty(item, id)] = { data: getItemNestedProperty(item, value) }, _a); }))); } else { return Object.assign.apply(Object, __spreadArrays([{}], array.map(function (item) { var _a; return (_a = {}, _a[getItemNestedProperty(item, id)] = true, _a); }))); } }; var getItemNestedProperty = function (item, nestedProperty) { return nestedProperty.split('.').reduce(function (o, i) { return o[i]; }, item); };