UNPKG

@neo4j-ndl/react-charts

Version:

React implementation of charts from Neo4j Design System

149 lines 6.6 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.useLegendSelection = useLegendSelection; exports.useLegendHoverHighlight = useLegendHoverHighlight; const echarts_1 = require("echarts"); const react_1 = require("react"); const legend_utils_1 = require("../utils/legend-utils"); const threshold_1 = require("../utils/threshold"); const use_chart_refs_1 = require("./use-chart-refs"); const LEGEND_HOVER_HIGHLIGHT_DELAY_MS = 80; const LEGEND_SELECTION_EVENT_TYPES = [ 'legendselectchanged', 'legendselectall', 'legendselected', 'legendunselected', ]; const getAllSeriesSelected = (series) => Object.fromEntries(series.map((s) => { var _a; return [(_a = s.name) !== null && _a !== void 0 ? _a : '', true]; })); const hasSelectionForCurrentSeries = (selection, series) => { const selectedNames = Object.keys(selection); return (selectedNames.length === series.length && series.every((s) => { var _a; return Object.prototype.hasOwnProperty.call(selection, (_a = s.name) !== null && _a !== void 0 ? _a : ''); })); }; const filterThresholdLineSelection = (selected) => Object.fromEntries(Object.entries(selected).filter(([key]) => { var _a; return !((_a = (0, threshold_1.isThresholdLine)(key)) !== null && _a !== void 0 ? _a : false); })); const clearTimeoutRef = (timeoutRef) => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } }; const createLegendSelectionEventHandler = (chart, eventType, setSelectedSeries) => (params) => { var _a; if (typeof params !== 'object' || params === null || !('selected' in params) || params.selected === null) { return; } if (eventType === 'legendselectchanged') { (0, legend_utils_1.resetAllSeriesHighlight)(chart); } setSelectedSeries(filterThresholdLineSelection((_a = params.selected) !== null && _a !== void 0 ? _a : {})); }; /** * Owns React legend selection and keeps it synchronized with ECharts. * * Current series changes preserve existing selection by name, new series default * to visible, removed series are dropped, and ECharts legend events update the * React state used to render legend items. */ function useLegendSelection({ series }) { const { chartEchartRef, legendSelectedRef } = (0, use_chart_refs_1.useChartRefsContext)(); const initialSelected = (0, react_1.useMemo)(() => getAllSeriesSelected(series), [series]); const [selectedSeries, setSelectedSeries] = (0, react_1.useState)(initialSelected); const renderedSelectedSeries = hasSelectionForCurrentSeries(selectedSeries, series) ? selectedSeries : initialSelected; (0, react_1.useEffect)(() => { var _a, _b, _c; const prev = (_a = legendSelectedRef.current) !== null && _a !== void 0 ? _a : {}; const next = {}; for (const s of series) { const name = (_b = s.name) !== null && _b !== void 0 ? _b : ''; next[name] = (_c = prev[name]) !== null && _c !== void 0 ? _c : true; } setSelectedSeries(next); }, [legendSelectedRef, series]); (0, react_1.useEffect)(() => { legendSelectedRef.current = selectedSeries; }, [legendSelectedRef, selectedSeries]); (0, react_1.useEffect)(() => { if (chartEchartRef.current === null) { return; } const chart = (0, echarts_1.getInstanceByDom)(chartEchartRef.current); if (!chart) { return; } const eventHandlers = LEGEND_SELECTION_EVENT_TYPES.map((eventType) => { const handler = createLegendSelectionEventHandler(chart, eventType, setSelectedSeries); chart.on(eventType, handler); return { eventType, handler }; }); return () => { eventHandlers.forEach(({ eventType, handler }) => { chart.off(eventType, handler); }); }; }, [chartEchartRef]); return { renderedSelectedSeries }; } /** * Creates a delayed ECharts highlighter for legend item hover state. * * Legend hover moves often happen as a quick leave/enter pair between adjacent * items. Keeping separate highlight and downplay timers lets the next hover * cancel the previous opposite action, preventing the chart from flickering * between emphasized and reset states. Deselected legend items are ignored so * hidden series do not get emphasized through hover. */ function useLegendHoverHighlight({ selectedSeries, series, }) { const { chartEchartRef } = (0, use_chart_refs_1.useChartRefsContext)(); const highlightTimeOut = (0, react_1.useRef)(null); const downplayTimeOut = (0, react_1.useRef)(null); (0, react_1.useEffect)(() => { return () => { clearTimeoutRef(highlightTimeOut); clearTimeoutRef(downplayTimeOut); }; }, []); const toggleHighlight = (0, react_1.useCallback)((seriesToUpdate, shouldHighlight) => { const isDeselected = seriesToUpdate.name === undefined ? false : !selectedSeries[seriesToUpdate.name]; if (isDeselected) { return; } const timeoutToClear = shouldHighlight ? downplayTimeOut : highlightTimeOut; const timeoutToSet = shouldHighlight ? highlightTimeOut : downplayTimeOut; if (timeoutToClear.current) { clearTimeout(timeoutToClear.current); } timeoutToSet.current = setTimeout(() => { (0, legend_utils_1.highlightOrDownplaySeries)(chartEchartRef, series, selectedSeries, [seriesToUpdate], shouldHighlight ? 'highlight' : 'downplay'); }, LEGEND_HOVER_HIGHLIGHT_DELAY_MS); }, [chartEchartRef, selectedSeries, series]); return { toggleHighlight }; } //# sourceMappingURL=use-legend-interactions.js.map