From 852abd548284a953b9b7890882a8625831c410fd Mon Sep 17 00:00:00 2001 From: Mikhail Chechnev Date: Fri, 14 Feb 2025 13:48:36 +0300 Subject: [PATCH] =?UTF-8?q?WEBAPP:=20P8PChart=20-=20=D0=BE=D0=B1=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20onClick-=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8,=20=D0=B7=D0=BD=D0=B0?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=20=D1=83=D0=BC?= =?UTF-8?q?=D0=BE=D0=BB=D1=87=D0=B0=D0=BD=D0=B8=D1=8E=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20options,=20labels,=20datasets,=20=D0=BD=D0=B5=D0=BE=D0=B1?= =?UTF-8?q?=D1=8F=D0=B7=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D1=8C=20labels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- app/components/p8p_chart.js | 34 +++++++++++++++++++--------------- app/panels/samples/chart.js | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 48a5928..a74a831 100644 --- a/README.md +++ b/README.md @@ -1767,7 +1767,7 @@ const MyPanel = () => { `title` - необязательный, строка, заголовок графика, если не указано - заголовок не отображается\ `legendPosition` - необязательный, строка, расположение легенды, может принимать значения `left|right|top|bottom`, если не указано - легенда не отображается\ `options` - необязательный, объект, дополнительные параметры графика, формат и допустимый состав атрибутов определены в документации к библиотеке [ChartJS](https://www.chartjs.org/docs/latest/), будет объединён с параметрами графика уже зафиксированными в компоненте `P8PChart` (см. `useEffect` при подключении компонента к старице в его исходном коде, параметры графика, зафиксированные в компоненте, имеют более высокий приоритет по сравнению с данным свойством) -`labels` - обязательный, массив строк, список меток для значений графика\ +`labels` - необязательный, массив строк, список меток для значений графика\ `datasets` - необязательный, массив объектов, данные для отображения на диаграмме, каждый элемент массива - серия данных для отображения, содержит объекты вида `{label: <ЗАГОЛОВОК_СЕРИИ>, borderColor: <ЦВЕТ_ГРАНИЦЫ_СЕРИИ_НА_ГРАФИКЕ>, backgroundColor: <ЦВЕТ_ЗАЛИВКИ_СЕРИИ_НА_ГРАФИКЕ>, data: <МАССИВ_ЗНАЧЕНИЙ_СЕРИИ_ДАННЫХ>, items: <МАССИВ_ОБЪЕКТОВ_ПРОИЗВОЛЬНОЙ_СТРУКТУРЫ_ДЛЯ_ОПИСАНИЯ_СЕРИИ_ДАННЫХ>}`\ `onClick` - необязательный, функция, будет вызвана при нажатии на элемент графика, сигнатура функции `f({datasetIndex, itemIndex, item})`, результат функции не интерпретируется. Функции будет передан объект, поле `datasetIndex` которого, будет содержать индекс серии данных, `itemIndex` - индекс элемента серии данных, а `item` - описание элмента данных серии, на котором было зафиксировано нажатие.\ `style` - необязательный, объект, стили, которые будут применены к контейнеру `div` графика @@ -1859,7 +1859,7 @@ const STYLES = { //Пример: Графики "P8PChart" const Chart = ({ title }) => { //Собственное состояние - график - const [chart, setChart] = useState({ loaded: false, labels: [], datasets: [] }); + const [chart, setChart] = useState({ loaded: false }); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx); diff --git a/app/components/p8p_chart.js b/app/components/p8p_chart.js index a56e687..32c6efa 100644 --- a/app/components/p8p_chart.js +++ b/app/components/p8p_chart.js @@ -7,7 +7,7 @@ //Подключение библиотек //--------------------- -import React, { useEffect, useRef } from "react"; //Классы React +import React, { useCallback, useEffect, useRef } from "react"; //Классы React import PropTypes from "prop-types"; //Контроль свойств компонента import Chart from "chart.js/auto"; //Диаграммы и графики @@ -37,23 +37,26 @@ const P8P_CHART_DATASET_SHAPE = PropTypes.shape({ //----------- //График -const P8PChart = ({ type, title, legendPosition, options, labels, datasets, onClick, style }) => { +const P8PChart = ({ type, title, legendPosition, options = {}, labels = [], datasets = [], onClick, style }) => { //Ссылки на DOM const chartCanvasRef = useRef(null); const chartRef = useRef(null); //Обработка нажатия на элемент графика - const handleClick = e => { - const bar = chartRef.current.getElementsAtEventForMode(e, "nearest", { intersect: true }, true)[0]; - if (onClick && bar) - onClick({ - datasetIndex: bar.datasetIndex, - itemIndex: bar.index, - item: chartRef.current.data.datasets[bar.datasetIndex].items - ? chartRef.current.data.datasets[bar.datasetIndex].items[bar.index] - : null - }); - }; + const handleClick = useCallback( + e => { + const bar = chartRef.current.getElementsAtEventForMode(e, "nearest", { intersect: true }, true)[0]; + if (onClick && bar) + onClick({ + datasetIndex: bar.datasetIndex, + itemIndex: bar.index, + item: chartRef.current.data.datasets[bar.datasetIndex].items + ? chartRef.current.data.datasets[bar.datasetIndex].items[bar.index] + : null + }); + }, + [onClick] + ); //При подключении к старнице useEffect(() => { @@ -89,9 +92,10 @@ const P8PChart = ({ type, title, legendPosition, options, labels, datasets, onCl if (chartRef.current) { chartRef.current.data.labels = [...labels]; chartRef.current.data.datasets = [...datasets]; + chartRef.current.options.onClick = handleClick; chartRef.current.update(); } - }, [datasets, labels]); + }, [datasets, labels, handleClick]); //Генерация содержимого return ( @@ -107,7 +111,7 @@ P8PChart.propTypes = { title: PropTypes.string, legendPosition: PropTypes.string, options: PropTypes.object, - labels: PropTypes.arrayOf(PropTypes.string).isRequired, + labels: PropTypes.arrayOf(PropTypes.string), datasets: PropTypes.arrayOf(P8P_CHART_DATASET_SHAPE), onClick: PropTypes.func, style: PropTypes.object diff --git a/app/panels/samples/chart.js b/app/panels/samples/chart.js index 8c5ef52..5f2adb8 100644 --- a/app/panels/samples/chart.js +++ b/app/panels/samples/chart.js @@ -33,7 +33,7 @@ const STYLES = { //Пример: Графики "P8PChart" const Chart = ({ title }) => { //Собственное состояние - график - const [chart, setChart] = useState({ loaded: false, labels: [], datasets: [] }); + const [chart, setChart] = useState({ loaded: false }); //Подключение к контексту взаимодействия с сервером const { executeStored } = useContext(BackEndСtx);