From c5d924f98691355c478d498807acca5c6210024f Mon Sep 17 00:00:00 2001 From: Mikhail Chechnev Date: Fri, 20 Oct 2023 14:23:22 +0300 Subject: [PATCH] =?UTF-8?q?WEB=20APP:=20=D0=9A=D0=BE=D0=BC=D0=BF=D0=BE?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D1=82=20"=D0=93=D1=80=D0=B0=D1=84=D0=B8?= =?UTF-8?q?=D0=BA"=20(P8PChart)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/p8p_chart.js | 120 ++++++++++++++++++++++++++++++++++++ app/core/client.js | 4 +- package-lock.json | 107 ++++++++++++++++++-------------- package.json | 1 + 4 files changed, 186 insertions(+), 46 deletions(-) create mode 100644 app/components/p8p_chart.js diff --git a/app/components/p8p_chart.js b/app/components/p8p_chart.js new file mode 100644 index 0000000..beda7be --- /dev/null +++ b/app/components/p8p_chart.js @@ -0,0 +1,120 @@ +/* + Парус 8 - Панели мониторинга + Компонент: График +*/ + +//--------------------- +//Подключение библиотек +//--------------------- + +import React, { useEffect, useRef } from "react"; //Классы React +import PropTypes from "prop-types"; //Контроль свойств компонента +import Chart from "chart.js/auto"; //Диаграммы и графики + +//--------- +//Константы +//--------- + +//Виды графиков +const P8P_CHART_TYPE = { + BAR: "small", + LINE: "line", + PIE: "pie", + DOUGHNUT: "doughnut" +}; + +//Структура элемента набора данных +const P8P_CHART_DATASET_SHAPE = PropTypes.shape({ + label: PropTypes.string.isRequired, + borderColor: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), + backgroundColor: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), + data: PropTypes.arrayOf(PropTypes.number).isRequired, + items: PropTypes.arrayOf(PropTypes.object).isRequired +}); + +//----------- +//Тело модуля +//----------- + +//График +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 + }); + }; + + //При подключении к старнице + useEffect(() => { + if (!chartRef.current) { + const ctx = chartCanvasRef.current.getContext("2d"); + chartRef.current = new Chart(ctx, { + type, + data: { labels: [...labels], datasets: [...datasets] }, + options: { + ...options, + ...{ + responsive: true, + plugins: { + legend: { + display: legendPosition ? true : false, + position: legendPosition + }, + title: { + display: title ? true : false, + text: title + } + } + }, + onClick: handleClick + } + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + //При обновлении данных + useEffect(() => { + if (chartRef.current) { + chartRef.current.data.labels = [...labels]; + chartRef.current.data.datasets = [...datasets]; + chartRef.current.update(); + } + }, [datasets, labels]); + + //Генерация содержимого + return ( +
+ +
+ ); +}; + +//Контроль свойств - График +P8PChart.propTypes = { + type: PropTypes.string.isRequired, + title: PropTypes.string, + legendPosition: PropTypes.string, + options: PropTypes.object, + labels: PropTypes.arrayOf(PropTypes.string).isRequired, + datasets: PropTypes.arrayOf(P8P_CHART_DATASET_SHAPE), + onClick: PropTypes.func, + style: PropTypes.object +}; + +//---------------- +//Интерфейс модуля +//---------------- + +export { P8P_CHART_TYPE, P8PChart }; diff --git a/app/core/client.js b/app/core/client.js index 5d1d924..8d37fa1 100644 --- a/app/core/client.js +++ b/app/core/client.js @@ -42,7 +42,9 @@ const XML_ALWAYS_ARRAY_PATHS = [ "XRESPOND.XPAYLOAD.XGANTT_DEF.taskAttributes", "XRESPOND.XPAYLOAD.XGANTT_DEF.taskColors", "XRESPOND.XPAYLOAD.XGANTT_TASKS", - "XRESPOND.XPAYLOAD.XGANTT_TASKS.dependencies" + "XRESPOND.XPAYLOAD.XGANTT_TASKS.dependencies", + "XRESPOND.XPAYLOAD.XCHART.labels", + "XRESPOND.XPAYLOAD.XCHART.datasets" ]; //Типовой постфикс тега для массива (при переводе XML -> JSON) diff --git a/package-lock.json b/package-lock.json index 842c0bd..b442801 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@emotion/styled": "^11.11.0", "@mui/material": "^5.14.4", "babel-loader": "^9.1.3", + "chart.js": "^4.4.0", "dayjs": "^1.11.9", "eslint": "^8.46.0", "eslint-plugin-react": "^7.33.1", @@ -49,11 +50,11 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.22.10", + "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" }, "engines": { @@ -98,11 +99,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -138,20 +139,20 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -236,9 +237,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -265,11 +266,11 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -278,9 +279,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", - "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -394,31 +395,31 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", - "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.10", - "@babel/types": "^7.22.10", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -427,12 +428,12 @@ } }, "node_modules/@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -747,6 +748,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "node_modules/@mui/base": { "version": "5.0.0-beta.10", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.10.tgz", @@ -1680,6 +1686,17 @@ "node": ">=4" } }, + "node_modules/chart.js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz", + "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=7" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", diff --git a/package.json b/package.json index 3adfc27..e4dc471 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@emotion/styled": "^11.11.0", "@mui/material": "^5.14.4", "babel-loader": "^9.1.3", + "chart.js": "^4.4.0", "dayjs": "^1.11.9", "eslint": "^8.46.0", "eslint-plugin-react": "^7.33.1",