@neo4j-ndl/react-charts
Version:
React implementation of charts from Neo4j Design System
200 lines • 9.54 kB
JavaScript
/**
*
* 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/>.
*/
import { getInstanceByDom, } from 'echarts';
import { useCallback } from 'react';
const useLegendVisibility = (chartRef, selectedSeries) => {
const setOnlyVisible = useCallback((name) => {
if (chartRef.current === null) {
return;
}
const chart = getInstanceByDom(chartRef.current);
chart === null || chart === void 0 ? void 0 : chart.dispatchAction({
type: 'legendSelect',
name: name,
});
const otherNames = Object.keys(selectedSeries).filter((n) => n !== name);
otherNames.forEach((name) => {
if (name.startsWith('thresholdLine')) {
return;
}
chart === null || chart === void 0 ? void 0 : chart.dispatchAction({
type: 'legendUnSelect',
name: name,
});
});
}, [chartRef, selectedSeries]);
const setAllVisible = useCallback(() => {
if (chartRef.current === null) {
return;
}
const chart = getInstanceByDom(chartRef.current);
chart === null || chart === void 0 ? void 0 : chart.dispatchAction({
type: 'legendAllSelect',
});
}, [chartRef]);
/**
* Toggle the visibility of a legend series
*
* The toggle logic depends on the current state of the series:
* - If only the legend series that was selected is visible: show all series
* - If all series are visible: hide all except the series that was selected
* - Otherwise: toggle the legend series
*
* The isOnlyVisible and isAllSeriesSelected states can't be calculated in this
* function since different legend types require different logic.
*/
const toggleLegendVisibility = useCallback((name, isAllSeriesSelected, isOnlyVisible) => {
if (chartRef.current === null || name === undefined) {
return;
}
if (isOnlyVisible) {
setAllVisible();
}
else if (isAllSeriesSelected) {
setOnlyVisible(name);
}
else {
const chart = getInstanceByDom(chartRef.current);
chart === null || chart === void 0 ? void 0 : chart.dispatchAction({
name: name,
type: 'legendToggleSelect',
});
}
}, [chartRef, setOnlyVisible, setAllVisible]);
return { setAllVisible, setOnlyVisible, toggleLegendVisibility };
};
const isThresholdLine = (name) => name === null || name === void 0 ? void 0 : name.startsWith('thresholdLine');
/**
* Get the computed element width, including both paddings and margins
*/
const getComputedElementWidth = (element) => {
const elementRect = element.getBoundingClientRect();
const elementComputedStyle = window.getComputedStyle(element);
const elementMarginLeft = parseFloat(elementComputedStyle.marginLeft);
const elementMarginRight = parseFloat(elementComputedStyle.marginRight);
return elementRect.width + elementMarginLeft + elementMarginRight;
};
export const resetAllSeriesHighlight = (chart) => {
const option = chart.getOption();
const seriesList = option.series || [];
seriesList.forEach((_, seriesIndex) => {
chart.dispatchAction({
seriesIndex,
type: 'downplay',
});
});
};
const highlightOrDownplayLineAndBarSeries = (chart, currentSeriesToDispatch, action, currentChartOptions) => {
var _a;
const isDownplay = action === 'downplay';
const allChartOptionSeries = (currentChartOptions === null || currentChartOptions === void 0 ? void 0 : currentChartOptions.series) ||
[];
// Change emphasis state to add opacity on the non highlighted items
chart === null || chart === void 0 ? void 0 : chart.setOption({
series: allChartOptionSeries === null || allChartOptionSeries === void 0 ? void 0 : allChartOptionSeries.map((seriesItem) => (Object.assign(Object.assign({}, seriesItem), { emphasis: Object.assign(Object.assign({}, seriesItem.emphasis), { disabled: isDownplay }) }))),
});
const seriesWithSeriesIndex = (_a = allChartOptionSeries === null || allChartOptionSeries === void 0 ? void 0 : allChartOptionSeries
// Since the series index does not exist in the data we extract it from the series index
.map((seriesItem, index) => (Object.assign(Object.assign({}, seriesItem), { seriesIndex: index })))) === null || _a === void 0 ? void 0 : _a.filter((seriesItem) => currentSeriesToDispatch.some((series) => series.name === seriesItem.name));
if (!seriesWithSeriesIndex.length) {
return;
}
chart === null || chart === void 0 ? void 0 : chart.dispatchAction({
seriesIndex: seriesWithSeriesIndex.map((series) => series.seriesIndex),
type: action,
});
};
/**
* Highlight or downplay pie chart dataset item
*/
const highlightOrDownplayPieSlices = (chart, currentSeriesToDispatch, action, currentChartOptions) => {
var _a;
const isDownplay = action === 'downplay';
const allChartOptionSeries = (currentChartOptions === null || currentChartOptions === void 0 ? void 0 : currentChartOptions.series) ||
[];
// Change blur state to add opacity on the non highlighted items in the pie chart
chart === null || chart === void 0 ? void 0 : chart.setOption({
series: allChartOptionSeries === null || allChartOptionSeries === void 0 ? void 0 : allChartOptionSeries.map((seriesItem) => (Object.assign(Object.assign({}, seriesItem), { blur: {
itemStyle: {
opacity: isDownplay ? 1 : 0.3,
},
label: {
opacity: isDownplay ? 1 : 0.3,
},
// ...(seriesItem.blur as Record<string, unknown>),
} }))),
});
// The dataIndex that we want to dispatch can be found in the dataset[0] source array, where the first item is the name of the series
const chartOptionDataset = currentChartOptions === null || currentChartOptions === void 0 ? void 0 : currentChartOptions.dataset;
const datasetArray = Array.isArray(chartOptionDataset)
? chartOptionDataset
: [chartOptionDataset];
const sourceArray = (_a = datasetArray === null || datasetArray === void 0 ? void 0 : datasetArray[0]) === null || _a === void 0 ? void 0 : _a.source;
// Find the dataIndexes that we want to dispatch
const currentDataSeriesIndexes = currentSeriesToDispatch.map((series) => {
const index = (sourceArray === null || sourceArray === void 0 ? void 0 : sourceArray.findIndex((item) => item[0] === series.name)) - 1;
return index;
});
if (action === 'highlight') {
// Downplay other data indexes to make sure the other slices are not highlighted
const otherSeriesIndexes = sourceArray
.map((_, index) => index)
.filter((dataIndex) => !currentDataSeriesIndexes.includes(dataIndex));
chart === null || chart === void 0 ? void 0 : chart.dispatchAction({
seriesIndex: [0], // pie charts only has one series so seriesIndex is always 0
dataIndex: otherSeriesIndexes,
type: 'downplay',
});
}
chart === null || chart === void 0 ? void 0 : chart.dispatchAction({
seriesIndex: [0], // pie charts only has one series so otherSeriesIndexes is always 0
dataIndex: currentDataSeriesIndexes,
type: action,
});
};
/**
* Highlight or downplay the series in the chart to toggle between emphasized and non-emphasized state
*/
const highlightOrDownplaySeries = (chartRef, series, currentSelectedSeries, currentSeriesToDispatch, action) => {
var _a, _b;
const hasOnlyOneSeries = series.length === 1;
const isOnlyOneSeriesSelected = Object.values(currentSelectedSeries).filter(Boolean).length === 1;
if (isOnlyOneSeriesSelected ||
currentSeriesToDispatch.length === 0 ||
chartRef.current === null ||
hasOnlyOneSeries) {
return;
}
const chart = getInstanceByDom(chartRef.current);
if (!chart) {
return;
}
const currentChartOptions = chart.getOption();
const seriesType = (_b = (_a = currentChartOptions === null || currentChartOptions === void 0 ? void 0 : currentChartOptions.series) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.type;
if (seriesType === 'pie') {
highlightOrDownplayPieSlices(chart, currentSeriesToDispatch, action, currentChartOptions);
}
else {
highlightOrDownplayLineAndBarSeries(chart, currentSeriesToDispatch, action, currentChartOptions);
}
};
export { useLegendVisibility, isThresholdLine, getComputedElementWidth, highlightOrDownplaySeries, };
//# sourceMappingURL=legend-utils.js.map