@neo4j-ndl/react
Version:
React implementation of Neo4j Design System
289 lines • 15 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_NVL_OPTIONS = void 0;
exports.GraphVisualization = GraphVisualization;
const jsx_runtime_1 = require("react/jsx-runtime");
/**
*
* 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/>.
*/
const base_1 = require("@neo4j-ndl/base");
const react_1 = require("@neo4j-ndl/react");
const react_2 = require("@neo4j-nvl/react");
const classnames_1 = __importDefault(require("classnames"));
const react_3 = __importStar(require("react"));
const graph_visualization_buttons_1 = require("./graph-visualization-buttons");
const graph_visualization_context_1 = require("./graph-visualization-context");
const graph_visualization_sidepanel_1 = require("./graph-visualization-sidepanel");
const map_to_nvl_graph_1 = require("./map-to-nvl-graph");
const single_selection_sidepanel_contents_1 = require("./sidepanel-components/single-selection-sidepanel-contents");
const use_managed_selection_state_1 = require("./use-managed-selection-state");
const use_semi_controlled_state_1 = require("./use-semi-controlled-state");
const PLACEMENTS = {
'bottom-left': 'ndl-graph-visualization-interaction-island ndl-bottom-left',
'bottom-right': 'ndl-graph-visualization-interaction-island ndl-bottom-right',
'top-left': 'ndl-graph-visualization-interaction-island ndl-top-left',
'top-right': 'ndl-graph-visualization-interaction-island ndl-top-right',
};
const InteractionIsland = ({ children, className, placement, }) => {
return (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(PLACEMENTS[placement], className), children: children });
};
exports.DEFAULT_NVL_OPTIONS = {
disableTelemetry: true,
disableWebGL: true,
maxZoom: 3,
minZoom: 0.05,
relationshipThreshold: 0.55,
useWebGL: false,
};
const DEFAULT_COMPONENTS = {
bottomLeftIsland: null,
bottomRightIsland: ((0, jsx_runtime_1.jsxs)(react_1.IconButtonArray, { orientation: "vertical", isFloating: true, children: [(0, jsx_runtime_1.jsx)(graph_visualization_buttons_1.ZoomInButton, {}), " ", (0, jsx_runtime_1.jsx)(graph_visualization_buttons_1.ZoomOutButton, {}), " ", (0, jsx_runtime_1.jsx)(graph_visualization_buttons_1.ZoomToFitButton, {})] })),
topLeftIsland: null,
topRightIsland: ((0, jsx_runtime_1.jsxs)("div", { className: "ndl-graph-visualization-default-download-group", children: [(0, jsx_runtime_1.jsx)(graph_visualization_buttons_1.DownloadButton, {}), " ", (0, jsx_runtime_1.jsx)(graph_visualization_buttons_1.ToggleSidePanelButton, {})] })),
};
/**
* A comprehensive graph visualization component for rendering Neo4j-style graphs with interactive features.
*
* @remarks
* This component provides a complete graph visualization solution with built-in interaction modes,
* selection handling, zoom controls, and an optional sidepanel. It wraps the NVL (Neo4j Visualization Library)
* with additional UI controls and state management.
*
* The component supports various interaction modes including single selection, box selection, and lasso selection.
* It also provides customizable UI islands for placing controls at different corners of the visualization.
*
* @example
* Basic usage:
* ```tsx
* <GraphVisualization
* nodes={nodes}
* rels={relationships}
* selected={selection}
* setSelected={setSelection}
* />
* ```
*
* @example
* With custom controls:
* ```tsx
* <GraphVisualization
* nodes={nodes}
* rels={relationships}
* topRightIsland={<CustomControls />}
* sidepanel={{
* contents: <CustomSidepanel />,
* isSidePanelOpen: true,
* setIsSidePanelOpen: setSidepanelOpen
* sidePanelWidth: 400,
* onSidePanelResize: setSidepanelWidth,
* }}
* />
* ```
*
* @param props - {@link GraphVisualizationProps}
* @returns A React component that renders an interactive graph visualization
*
* @alpha
*/
function GraphVisualization(_a) {
var _b, _c;
var { nvlRef: rawNvlRef, nvlCallbacks, nvlOptions, sidepanel: rawSidepanel, nodes: rawNodes, rels: rawRels, topLeftIsland = DEFAULT_COMPONENTS.topLeftIsland, topRightIsland = DEFAULT_COMPONENTS.topRightIsland, bottomLeftIsland = DEFAULT_COMPONENTS.bottomLeftIsland, bottomRightIsland = DEFAULT_COMPONENTS.bottomRightIsland, gesture = 'single', setGesture, selected: rawSelected, setSelected: rawSetSelected, interactionMode: rawInteractionMode, setInteractionMode: rawSetInteractionMode, mouseEventCallbacks = {}, className, style, htmlAttributes, ref, as } = _a, restProps = __rest(_a, ["nvlRef", "nvlCallbacks", "nvlOptions", "sidepanel", "nodes", "rels", "topLeftIsland", "topRightIsland", "bottomLeftIsland", "bottomRightIsland", "gesture", "setGesture", "selected", "setSelected", "interactionMode", "setInteractionMode", "mouseEventCallbacks", "className", "style", "htmlAttributes", "ref", "as"]);
const nvlRef = (0, react_3.useMemo)(() => rawNvlRef !== null && rawNvlRef !== void 0 ? rawNvlRef : react_3.default.createRef(), [rawNvlRef]);
const instanceId = (0, react_3.useId)();
// Respect NDL theme changes
const { theme } = (0, react_1.useNeedleTheme)();
const { bg, border } = base_1.tokens.theme[theme].palette.neutral;
// key required for nvl react responsiveness
const [key, setKey] = (0, react_3.useState)(0);
(0, react_3.useEffect)(() => {
setKey((prevKey) => prevKey + 1);
}, [theme]);
// Semi-control interaction mode and selection state
const [interactionMode, setInteractionMode] = (0, use_semi_controlled_state_1.useSemicontrolledState)(rawInteractionMode !== null && rawInteractionMode !== void 0 ? rawInteractionMode : 'select', rawSetInteractionMode);
const [selected, setSelected] = (0, use_semi_controlled_state_1.useSemicontrolledState)(rawSelected !== null && rawSelected !== void 0 ? rawSelected : { nodeIds: [], relationshipIds: [] }, rawSetSelected);
const nvlGraph = (0, react_3.useMemo)(() => (0, map_to_nvl_graph_1.mapToNvlGraph)(rawNodes, rawRels), [rawNodes, rawRels]);
const { nodesWithSelection, relsWithSelection, wrappedMouseEventCallbacks } = (0, use_managed_selection_state_1.useManagedSelectionState)({
gesture,
interactionMode,
mouseEventCallbacks,
nvlGraph,
selected,
setInteractionMode,
setSelected,
});
const [isSidePanelOpen, setIsSidePanelOpen] = (0, use_semi_controlled_state_1.useSemicontrolledState)((_b = rawSidepanel === null || rawSidepanel === void 0 ? void 0 : rawSidepanel.isSidePanelOpen) !== null && _b !== void 0 ? _b : true, rawSidepanel === null || rawSidepanel === void 0 ? void 0 : rawSidepanel.setIsSidePanelOpen);
const [sidePanelWidth, setSidePanelWidth] = (0, use_semi_controlled_state_1.useSemicontrolledState)((_c = rawSidepanel === null || rawSidepanel === void 0 ? void 0 : rawSidepanel.sidePanelWidth) !== null && _c !== void 0 ? _c : 400, rawSidepanel === null || rawSidepanel === void 0 ? void 0 : rawSidepanel.onSidePanelResize);
const sidepanel = (0, react_3.useMemo)(() => {
if (rawSidepanel === undefined) {
return {
children: (0, jsx_runtime_1.jsx)(single_selection_sidepanel_contents_1.SingleSelectionSidepanelContents, {}),
isSidePanelOpen,
onSidePanelResize: setSidePanelWidth,
setIsSidePanelOpen,
sidePanelWidth,
};
}
return rawSidepanel;
}, [
rawSidepanel,
isSidePanelOpen,
setIsSidePanelOpen,
sidePanelWidth,
setSidePanelWidth,
]);
const Component = as !== null && as !== void 0 ? as : 'div';
return ((0, jsx_runtime_1.jsx)(Component, Object.assign({ ref: ref, className: (0, classnames_1.default)('ndl-graph-visualization-container', className), style: style }, htmlAttributes, { children: (0, jsx_runtime_1.jsxs)(graph_visualization_context_1.GraphVisualizationContext.Provider, { value: {
gesture,
interactionMode,
nvlGraph,
nvlInstance: nvlRef,
selected,
setGesture,
sidepanel,
}, children: [(0, jsx_runtime_1.jsxs)("div", { className: "ndl-graph-visualization", children: [(0, jsx_runtime_1.jsx)(react_2.InteractiveNvlWrapper, Object.assign({ layout: "d3Force", nodes: nodesWithSelection, rels: relsWithSelection, nvlOptions: Object.assign(Object.assign(Object.assign({}, exports.DEFAULT_NVL_OPTIONS), { instanceId, styling: {
defaultRelationshipColor: border.strongest,
dropShadowColor: border.weak,
selectedInnerBorderColor: bg.default,
} }), nvlOptions), nvlCallbacks: Object.assign({ onLayoutComputing(isComputing) {
var _a;
if (!isComputing) {
(_a = nvlRef.current) === null || _a === void 0 ? void 0 : _a.fit(nvlRef.current.getNodes().map((neighbors) => neighbors.id), { noPan: true });
}
} }, nvlCallbacks), mouseEventCallbacks: wrappedMouseEventCallbacks, ref: nvlRef }, restProps), key), topLeftIsland !== null && ((0, jsx_runtime_1.jsx)(InteractionIsland, { placement: "top-left", children: topLeftIsland })), topRightIsland !== null && ((0, jsx_runtime_1.jsx)(InteractionIsland, { placement: "top-right", children: topRightIsland })), bottomLeftIsland !== null && ((0, jsx_runtime_1.jsx)(InteractionIsland, { placement: "bottom-left", children: bottomLeftIsland })), bottomRightIsland !== null && ((0, jsx_runtime_1.jsx)(InteractionIsland, { placement: "bottom-right", children: bottomRightIsland }))] }), sidepanel && (0, jsx_runtime_1.jsx)(graph_visualization_sidepanel_1.GraphVisualizationSidepanel, { sidepanel: sidepanel })] }) })));
}
/**
* Zoom in button for use within a GraphVisualization component.
*
* @remarks
* Increases zoom level by 1.3x
*
* @alpha
*/
GraphVisualization.ZoomInButton = graph_visualization_buttons_1.ZoomInButton;
/**
* Zoom out button for use within a GraphVisualization component.
*
* @remarks
* Decreases zoom level by 0.7x
*
* @alpha
*/
GraphVisualization.ZoomOutButton = graph_visualization_buttons_1.ZoomOutButton;
/**
* Zoom to fit button for use within a GraphVisualization component.
*
* @remarks
* Automatically adjusts zoom and pan to show all nodes and relationships
*
* @alpha
*/
GraphVisualization.ZoomToFitButton = graph_visualization_buttons_1.ZoomToFitButton;
/**
* Sidepanel toggle button for use within a GraphVisualization component.
*
* @remarks
* Shows or hides the sidepanel containing node and relationship details.
* Requires a sidepanel to be configured in the GraphVisualization component.
*
* @throws Error when used without a configured sidepanel
*
* @alpha
*/
GraphVisualization.ToggleSidePanelButton = graph_visualization_buttons_1.ToggleSidePanelButton;
/**
* Download button for use within a GraphVisualization component.
*
* @remarks
* Opens a dropdown menu with download options. Currently supports PNG format.
* Captures the current visualization state including zoom level.
*
* @alpha
*/
GraphVisualization.DownloadButton = graph_visualization_buttons_1.DownloadButton;
/**
* Box selection button for use within a GraphVisualization component.
*
* @remarks
* Enables box selection mode where dragging creates a rectangular selection area.
* Disabled unless the `setGesture` callback is passed.
* Shows active state when enabled and supports keyboard shortcut 'B'.
*
* @alpha
*/
GraphVisualization.BoxSelectButton = graph_visualization_buttons_1.BoxSelectButton;
/**
* Lasso selection button for use within a GraphVisualization component.
*
* @remarks
* Enables lasso selection mode where dragging creates a free-form selection area.
* Disabled unless the `setGesture` callback is passed.
* Shows active state when enabled and supports keyboard shortcut 'L'.
*
* @alpha
*/
GraphVisualization.LassoSelectButton = graph_visualization_buttons_1.LassoSelectButton;
/**
* Single node selection button for use within a GraphVisualization component.
*
* @remarks
* Enables single selection mode where clicking selects individual nodes or relationships.
* Shows active state when enabled and supports keyboard shortcut 'S'.
*
* @alpha
*/
GraphVisualization.SingleSelectButton = graph_visualization_buttons_1.SingleSelectButton;
//# sourceMappingURL=graph-visualization.js.map