UNPKG

@neo4j-ndl/react

Version:

React implementation of Neo4j Design System

262 lines 13.2 kB
"use strict"; /** * * Copyright (c) "Neo4j" * Neo4j Sweden AB [http://neo4j.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.useManagedNodeState = useManagedNodeState; const react_1 = require("react"); const utils_1 = require("./utils"); const isMultiSelectKeyPressed = (evt) => (!utils_1.isMac && evt.ctrlKey) || (utils_1.isMac && evt.metaKey); const isClickingOnTextInput = (e) => { if (!(e.target instanceof HTMLElement)) { return false; } return (e.target.isContentEditable || ['INPUT', 'TEXTAREA'].includes(e.target.tagName)); }; function useManagedNodeState({ selected, setSelected, gesture, interactionMode, setInteractionMode, mouseEventCallbacks, nvlGraph, highlightedNodeIds, highlightedRelationshipIds, }) { const panOnSpace = (0, react_1.useCallback)((evt) => { // We could check the gesture here // but it doesn't really matter if we switch to panning mode even in single select if (interactionMode === 'select' && evt.key === ' ') { setInteractionMode('pan'); } }, [interactionMode, setInteractionMode]); const stopPanningReleaseSpace = (0, react_1.useCallback)((evt) => { if (interactionMode === 'pan' && evt.key === ' ') { setInteractionMode('select'); } }, [interactionMode, setInteractionMode]); (0, react_1.useEffect)(() => { document.addEventListener('keydown', panOnSpace); document.addEventListener('keyup', stopPanningReleaseSpace); return () => { document.removeEventListener('keydown', panOnSpace); document.removeEventListener('keyup', stopPanningReleaseSpace); }; }, [panOnSpace, stopPanningReleaseSpace]); const { onBoxSelect: originalBoxSelect, onLassoSelect: originalOnLassoSelect, onLassoStarted, onBoxStarted, onPan = true, onHover, onHoverNodeMargin, onNodeClick: originalOnNodeClick, onRelationshipClick: originalOnRelationshipClick, onDragStart: originalOnDragStart, onDragEnd: originalOnDragEnd, onDrawEnded: originalOnDrawEnded, onDrawStarted: originalOnDrawStarted, onCanvasClick: originalOnCanvasClick, onNodeDoubleClick: originalOnNodeDoubleClick, onRelationshipDoubleClick: originalOnRelationshipDoubleClick, } = mouseEventCallbacks; const onCanvasClick = (0, react_1.useCallback)((evt) => { if (isClickingOnTextInput(evt)) { return; } // Clear selection setSelected({ nodeIds: [], relationshipIds: [] }); if (typeof originalOnCanvasClick === 'function') { originalOnCanvasClick(evt); } }, [originalOnCanvasClick, setSelected]); const onDragStart = (0, react_1.useCallback)((draggedNodes, evt) => { setInteractionMode('drag'); // Update Selection const draggedNodeIds = draggedNodes.map((n) => n.id); if (selected.nodeIds.length === 0 || isMultiSelectKeyPressed(evt)) { setSelected({ nodeIds: draggedNodeIds, relationshipIds: selected.relationshipIds, }); return; } setSelected({ nodeIds: draggedNodeIds, relationshipIds: selected.relationshipIds, }); if (typeof originalOnDragStart === 'function') { originalOnDragStart(draggedNodes, evt); } }, [setSelected, originalOnDragStart, selected, setInteractionMode]); const onDragEnd = (0, react_1.useCallback)((draggedNodes, evt) => { if (typeof originalOnDragEnd === 'function') { originalOnDragEnd(draggedNodes, evt); } setInteractionMode('select'); }, [originalOnDragEnd, setInteractionMode]); // wrapping so that there's a function passed, even if the original function is undefined const onDrawStarted = (0, react_1.useCallback)((evt) => { if (typeof originalOnDrawStarted === 'function') { originalOnDrawStarted(evt); } }, [originalOnDrawStarted]); const onDrawEnded = (0, react_1.useCallback)((newRelationshipToAdd, newTargetNodeToAdd, event) => { if (typeof originalOnDrawEnded === 'function') { originalOnDrawEnded(newRelationshipToAdd, newTargetNodeToAdd, event); } }, [originalOnDrawEnded]); const onNodeClick = (0, react_1.useCallback)((node, hitElements, evt) => { if (isClickingOnTextInput(evt)) { return; } if (isMultiSelectKeyPressed(evt)) { // if not selected, add to selection, otherwise deselect const isNodeSelected = selected.nodeIds.includes(node.id); if (isNodeSelected) { const newSelectedNodeIds = selected.nodeIds.filter((id) => id !== node.id); setSelected({ nodeIds: newSelectedNodeIds, relationshipIds: selected.relationshipIds, }); } else { const newSelectedNodeIds = [...selected.nodeIds, node.id]; setSelected({ nodeIds: newSelectedNodeIds, relationshipIds: selected.relationshipIds, }); } } else { setSelected({ nodeIds: [node.id], relationshipIds: [] }); } if (typeof originalOnNodeClick === 'function') { originalOnNodeClick(node, hitElements, evt); } }, [setSelected, selected, originalOnNodeClick]); const onRelationshipClick = (0, react_1.useCallback)((relationship, hitElements, evt) => { if (isClickingOnTextInput(evt)) { return; } if (isMultiSelectKeyPressed(evt)) { // if not selected, add to selection, otherwise deselect const isRelationshipSelected = selected.relationshipIds.includes(relationship.id); if (isRelationshipSelected) { const newSelectedRelIds = selected.relationshipIds.filter((id) => id !== relationship.id); setSelected({ nodeIds: selected.nodeIds, relationshipIds: newSelectedRelIds, }); } else { const newSelectedRelIds = [ ...selected.relationshipIds, relationship.id, ]; setSelected({ nodeIds: selected.nodeIds, relationshipIds: newSelectedRelIds, }); } } else { setSelected({ nodeIds: [], relationshipIds: [relationship.id] }); } if (typeof originalOnRelationshipClick === 'function') { originalOnRelationshipClick(relationship, hitElements, evt); } }, [setSelected, selected, originalOnRelationshipClick]); const onNodeDoubleClick = (0, react_1.useCallback)((node, hitElements, evt) => { if (isClickingOnTextInput(evt)) { return; } if (typeof originalOnNodeDoubleClick === 'function') { originalOnNodeDoubleClick(node, hitElements, evt); } }, [originalOnNodeDoubleClick]); const onRelationshipDoubleClick = (0, react_1.useCallback)((relationship, hitElements, evt) => { if (isClickingOnTextInput(evt)) { return; } if (typeof originalOnRelationshipDoubleClick === 'function') { originalOnRelationshipDoubleClick(relationship, hitElements, evt); } }, [originalOnRelationshipDoubleClick]); const onMultiSelect = (0, react_1.useCallback)((nodes, rels, evt) => { const nodeIds = nodes.map((n) => n.id); const relationshipIds = rels.map((r) => r.id); if (isMultiSelectKeyPressed(evt)) { const currentlySelectedNodeIds = selected.nodeIds; const currentlySelectedRelIds = selected.relationshipIds; // Toggle selection: items that are already selected should be removed from selection, // those that are not should be added const xor = (a, b) => [ ...new Set([...a, ...b].filter((id) => !a.includes(id) || !b.includes(id))), ]; const newSelectedNodeIds = xor(currentlySelectedNodeIds, nodeIds); const newSelectedRelIds = xor(currentlySelectedRelIds, relationshipIds); setSelected({ nodeIds: newSelectedNodeIds, relationshipIds: newSelectedRelIds, }); } else { // REPLACE selection setSelected({ nodeIds: nodeIds, relationshipIds: relationshipIds }); } }, [setSelected, selected]); const onLassoSelect = (0, react_1.useCallback)(({ nodes, rels }, evt) => { onMultiSelect(nodes, rels, evt); if (typeof originalOnLassoSelect === 'function') { originalOnLassoSelect({ nodes, rels }, evt); } }, [onMultiSelect, originalOnLassoSelect]); const onBoxSelect = (0, react_1.useCallback)(({ nodes, rels }, evt) => { onMultiSelect(nodes, rels, evt); if (typeof originalBoxSelect === 'function') { originalBoxSelect({ nodes, rels }, evt); } }, [onMultiSelect, originalBoxSelect]); const isDrawMode = interactionMode === 'draw'; const isSelectMode = interactionMode === 'select'; const shouldEnableBox = isSelectMode && gesture === 'box'; const shouldEnableLasso = isSelectMode && gesture === 'lasso'; const shouldEnablePan = interactionMode === 'pan' || (isSelectMode && gesture === 'single'); const canDrag = interactionMode === 'drag' || interactionMode === 'select'; const wrappedMouseEventCallbacks = (0, react_1.useMemo)(() => { var _a; return (Object.assign(Object.assign({}, mouseEventCallbacks), { onBoxSelect: shouldEnableBox ? onBoxSelect : false, onBoxStarted: shouldEnableBox ? onBoxStarted : false, onCanvasClick: isSelectMode ? onCanvasClick : false, onDragEnd: canDrag ? onDragEnd : false, onDragStart: canDrag ? onDragStart : false, onDrawEnded: isDrawMode ? onDrawEnded : false, onDrawStarted: isDrawMode ? onDrawStarted : false, onHover: isSelectMode ? onHover : false, onHoverNodeMargin: isDrawMode ? onHoverNodeMargin : false, onLassoSelect: shouldEnableLasso ? onLassoSelect : false, onLassoStarted: shouldEnableLasso ? onLassoStarted : false, onNodeClick: isSelectMode ? onNodeClick : false, onNodeDoubleClick: isSelectMode ? onNodeDoubleClick : false, onPan: shouldEnablePan ? onPan : false, onRelationshipClick: isSelectMode ? onRelationshipClick : false, onRelationshipDoubleClick: isSelectMode ? onRelationshipDoubleClick : false, onZoom: (_a = mouseEventCallbacks.onZoom) !== null && _a !== void 0 ? _a : true })); }, [ canDrag, shouldEnableBox, shouldEnableLasso, shouldEnablePan, isDrawMode, isSelectMode, mouseEventCallbacks, onBoxSelect, onBoxStarted, onCanvasClick, onDragEnd, onDragStart, onDrawEnded, onDrawStarted, onHover, onHoverNodeMargin, onLassoSelect, onLassoStarted, onNodeClick, onNodeDoubleClick, onPan, onRelationshipClick, onRelationshipDoubleClick, ]); const selectionSet = (0, react_1.useMemo)(() => ({ nodeIds: new Set(selected.nodeIds), relIds: new Set(selected.relationshipIds), }), [selected]); const highlightedNodeSet = (0, react_1.useMemo)(() => highlightedNodeIds !== undefined ? new Set(highlightedNodeIds) : null, [highlightedNodeIds]); const highlightedRelSet = (0, react_1.useMemo)(() => highlightedRelationshipIds !== undefined ? new Set(highlightedRelationshipIds) : null, [highlightedRelationshipIds]); const nodesWithState = (0, react_1.useMemo)(() => nvlGraph.nodes.map((n) => (Object.assign(Object.assign({}, n), { disabled: highlightedNodeSet ? !highlightedNodeSet.has(n.id) : false, selected: selectionSet.nodeIds.has(n.id) }))), [nvlGraph.nodes, selectionSet, highlightedNodeSet]); const relsWithState = (0, react_1.useMemo)(() => nvlGraph.rels.map((r) => (Object.assign(Object.assign({}, r), { disabled: highlightedRelSet ? !highlightedRelSet.has(r.id) : false, selected: selectionSet.relIds.has(r.id) }))), [nvlGraph.rels, selectionSet, highlightedRelSet]); return { nodesWithState, relsWithState, wrappedMouseEventCallbacks }; } //# sourceMappingURL=use-managed-node-state.js.map