From a8613e6b759d31eabf2bf59216e96ed64300e2a0 Mon Sep 17 00:00:00 2001 From: goodmice Date: Wed, 10 Aug 2022 15:22:57 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BA=D0=BE=D1=80=D1=80=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D1=80=D0=B5=D0=B4=D0=B0=D0=BA?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B8?= =?UTF-8?q?=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BA=20=D0=B3?= =?UTF-8?q?=D1=80=D0=B0=D1=84=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/d3/D3MonitoringChartEditor.tsx | 109 ++++++++---------- src/components/d3/D3MonitoringCharts.tsx | 51 ++++---- .../d3/D3MonitoringGroupsEditor.tsx | 81 +++++++------ .../d3/plugins/D3HorizontalCursor.tsx | 4 +- src/components/d3/plugins/D3Tooltip.tsx | 2 +- src/pages/Telemetry/TelemetryView/index.jsx | 7 +- 6 files changed, 128 insertions(+), 126 deletions(-) diff --git a/src/components/d3/D3MonitoringChartEditor.tsx b/src/components/d3/D3MonitoringChartEditor.tsx index 3bf55d1..02bad6e 100644 --- a/src/components/d3/D3MonitoringChartEditor.tsx +++ b/src/components/d3/D3MonitoringChartEditor.tsx @@ -1,4 +1,4 @@ -import { Button, Form, FormItemProps, Input, InputNumber, Select, Space, Tooltip, Typography } from 'antd' +import { Button, Form, FormItemProps, Input, InputNumber, Select, Tooltip } from 'antd' import { CSSProperties, memo, useCallback, useEffect, useMemo, useState } from 'react' import { ColorPicker, Color } from '../ColorPicker' @@ -17,87 +17,68 @@ const lineTypes = [ { value: 'needle', label: 'Иглы' }, ] -export type D3MonitoringChartEditorProps = Omit, HTMLDivElement>, 'onChange'> & { - chart?: ExtendedChartDataset | null +export type D3MonitoringChartEditorProps = { + chart: ExtendedChartDataset onChange: (value: ExtendedChartDataset) => boolean } const _D3MonitoringChartEditor = ({ chart: value, onChange, - - style, - ...other }: D3MonitoringChartEditorProps) => { - const [domain, setDomain] = useState({}) - const [color, setColor] = useState() - const [form] = Form.useForm() - const onDomainChange = useCallback((mm: MinMax) => { - setDomain((prev) => ({ - min: ('min' in mm) ? mm.min : prev?.min, - max: ('max' in mm) ? mm.max : prev?.max, - })) - }, []) - - const onColorChange = useCallback((color: Color) => setColor((prev) => color ?? prev), []) - - useEffect(() => { - if (value?.type) { - form.setFieldsValue(value) - } else { - form.resetFields() - } - setColor(value?.color ? new Color(String(value.color)) : undefined) - setDomain(value?.xDomain ?? {}) - }, [value, form]) - - const onSave = useCallback(() => { - if (!value) return + const onSave = useCallback((props: Partial>) => { const values = form.getFieldsValue() const newValue = { ...value, - color, - xDomain: domain, ...values, + ...props } onChange(newValue) - }, [form, domain, color, value]) + }, [value]) - const divStyle: CSSProperties = useMemo(() => ({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-between', - alignItems: 'center', - ...style, - }), [style]) + const onDomainChange = useCallback((mm: MinMax) => { + onSave({ xDomain: { + min: ('min' in mm) ? mm.min : value.xDomain?.min, + max: ('max' in mm) ? mm.max : value.xDomain?.max, + }}) + }, [value]) + + const onColorChange = useCallback((color: Color) => { + onSave({ color: color.toHexString() }) + }, [value]) + + useEffect(() => { + if (value.type) + form.setFieldsValue(value) + else + form.resetFields() + }, [value, form]) return ( -
-
- - - - - - - - onDomainChange({ min })} placeholder={'Мин'} /> - onDomainChange({ max })} placeholder={'Макс'} /> - - - - - -
+
+ + onSave({ label: e.target.value })} /> + onSave({ shortLabel: e.target.value })} /> + + + + + onDomainChange({ min })} placeholder={'Мин'} /> + onDomainChange({ max })} placeholder={'Макс'} /> + + + + + ) } diff --git a/src/components/d3/D3MonitoringCharts.tsx b/src/components/d3/D3MonitoringCharts.tsx index 9e7ce25..379078a 100644 --- a/src/components/d3/D3MonitoringCharts.tsx +++ b/src/components/d3/D3MonitoringCharts.tsx @@ -184,31 +184,27 @@ const _D3MonitoringCharts = >({ const [settingsVisible, setSettingsVisible] = useState(true) useEffect(() => { - if (!chartName) { - setDatasets(datasetGroups) - return - } - let datasets: ExtendedChartDataset[][] = [] - let needInsert = false invokeWebApiWrapperAsync( async () => { - const sets = await UserSettingsService.get(chartName) - needInsert = !sets - datasets = sets ?? datasetGroups + let sets = chartName ? await UserSettingsService.get(chartName) : null + if (typeof sets === 'string') + sets = JSON.parse(sets) + if (Array.isArray(sets)) { + setDatasets(sets) + } else if (Array.isArray(datasetGroups)) { + setDatasets(datasetGroups) + if (chartName) { + invokeWebApiWrapperAsync( + async () => await UserSettingsService.insert(chartName, datasetGroups), + undefined, + 'Не удалось сохранить настройки графиков' + ) + } + } }, undefined, 'Не удалось загрузить настройки графиков' ) - setDatasets(datasets) - if (needInsert) { - invokeWebApiWrapperAsync( - async () => { - await UserSettingsService.insert(chartName, datasets) - }, - undefined, - 'Не удалось сохранить настройки графиков' - ) - } }, [datasetGroups, chartName]) const offset = usePartialProps(_offset, defaultOffsets) @@ -242,7 +238,7 @@ const _D3MonitoringCharts = >({ groupLeft: (i: number) => (groupWidth + spaceBetweenGroups) * i, axisTop: (i: number, count: number) => axisHeight * (maxChartCount - count + i + 1) }) - }, [groups, height, offset]) + }, [groups, width, height, offset, axisHeight, spaceBetweenGroups]) const yAxis = useMemo(() => { if (!data) return @@ -258,7 +254,7 @@ const _D3MonitoringCharts = >({ const out: [string | number, ChartDomain][] = group.charts.map((chart) => { const mm = { ...chart.xDomain } let domain: Required = { min: 0, max: 100 } - if (mm.min && mm.max) { + if (!Number.isNaN((mm.min ?? NaN) + (mm.max ?? NaN))) { domain = mm as Required } else if (data) { const [min, max] = d3.extent(data, chart.x) @@ -291,9 +287,18 @@ const _D3MonitoringCharts = >({ ), [chartArea, axesArea]) const onGroupsChange = useCallback((sets: ExtendedChartDataset[][]) => { + if (chartName) { + invokeWebApiWrapperAsync( + async () => { + await UserSettingsService.update(chartName, sets) + }, + undefined, + 'Не удалось сохранить параметры графиков' + ) + } setDatasets(sets) setSettingsVisible(false) - }, []) + }, [chartName]) useEffect(() => { if (isDev()) { @@ -345,7 +350,7 @@ const _D3MonitoringCharts = >({ animDurationMs, ...dataset, yAxis: dataset.yAxis ?? yAxisConfig, - y: getByAccessor(dataset.yAxis.accessor ?? yAxisConfig.accessor), + y: getByAccessor(dataset.yAxis?.accessor ?? yAxisConfig.accessor), x: getByAccessor(dataset.xAxis?.accessor), } ) diff --git a/src/components/d3/D3MonitoringGroupsEditor.tsx b/src/components/d3/D3MonitoringGroupsEditor.tsx index d08ed14..c392b58 100644 --- a/src/components/d3/D3MonitoringGroupsEditor.tsx +++ b/src/components/d3/D3MonitoringGroupsEditor.tsx @@ -1,10 +1,11 @@ -import { Key, memo, useCallback, useEffect, useMemo, useState } from 'react' -import { Divider, Modal, Tooltip, Tree } from 'antd' +import { CSSProperties, Key, memo, useCallback, useEffect, useMemo, useState } from 'react' +import { Divider, Empty, Modal, Tooltip, Tree } from 'antd' import { getChartIcon } from '@utils' -import { ChartGroup, ExtendedChartDataset } from './D3MonitoringCharts' +import { ExtendedChartDataset } from './D3MonitoringCharts' import D3MonitoringChartEditor from './D3MonitoringChartEditor' +import { notify } from '../factory' export type D3MonitoringGroupsEditorProps = { visible?: boolean @@ -14,30 +15,30 @@ export type D3MonitoringGroupsEditorProps = { name?: string } -const moveToPos = (arr: T[], pos: number, newPos: number): T[] => { - if (pos === newPos) return arr - if (newPos === -1) return [arr[pos], ...arr.slice(0, pos), ...arr.slice(pos + 1)] - if (newPos === arr.length) return [...arr.slice(0, pos), ...arr.slice(pos + 1), arr[pos]] - const newArray = [] - for (let i = 0; i < arr.length; i++) { - if (i == newPos) newArray.push(arr[pos]) - if (i !== pos) newArray.push(arr[i]) - } - return newArray -} - const getChartLabel = (chart: ExtendedChartDataset) => ( {getChartIcon(chart)} {chart.label} -) +) + +const divStyle: CSSProperties = { + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + alignItems: 'center', + flexGrow: 1, +} + +const getNodePos = (node: any): { group: number, chart?: number } => { + const out = node.pos.split('-').map(Number) + return { group: out[1], chart: out[2] } +} const _D3MonitoringGroupsEditor = ({ visible, groups: oldGroups, onChange, onCancel, - name, }: D3MonitoringGroupsEditorProps) => { const [groups, setGroups] = useState[][]>([]) const [expand, setExpand] = useState([]) @@ -48,22 +49,30 @@ const _D3MonitoringGroupsEditor = ({ const onModalOk = useCallback(() => onChange(groups), [groups]) const onDrop = useCallback((info: any) => { - const { dragNode, dropPosition, dropToGap } = info + const { dragNode, dropPosition, node } = info - const nodes = dragNode.pos.split('-') - const groupPos = Number(nodes[1]) + const targetNodes = getNodePos(node) + const dragNodes = getNodePos(dragNode) + const groupPos = dragNodes.group if (!Number.isFinite(groupPos)) return setGroups((prev) => { - if (dropToGap) { - if (nodes.length < 3) - return moveToPos(prev, groupPos, dropPosition) - } else { - if (nodes.length < 3) return prev - const chartPos = Number(nodes[2]) - if (Number.isFinite(chartPos)) { - prev[groupPos] = moveToPos(prev[groupPos], chartPos, dropPosition) - return [ ...prev ] + if (typeof dragNodes.chart === 'undefined') { + const groups = [ ...prev ] + const movedGroups = groups.splice(dragNodes.group, 1) + groups.splice(Math.max(dropPosition - 1, 0), 0, ...movedGroups) + return groups + } else if (Number.isFinite(dragNodes.chart)) { + if (groupPos !== targetNodes.group) { + const dragKey = prev[dragNodes.group][dragNodes.chart].key + if (prev[targetNodes.group].find((chart) => chart.key === dragKey)) { + notify('График с данным ключом уже существует в этой группе. Перемещение невозможно', 'warning') + return prev + } } + const groups = [ ...prev ] + const charts = groups[groupPos].splice(dragNodes.chart, 1) + groups[targetNodes.group].splice(Math.max(dropPosition - 1, 0), 0, ...charts) + return groups } return prev }) @@ -108,7 +117,7 @@ const _D3MonitoringGroupsEditor = ({ return ( ({ title={'Настройка групп графиков'} >
-
+
setExpand(keys)} expandedKeys={expand} @@ -130,7 +139,13 @@ const _D3MonitoringGroupsEditor = ({ />
- chart={selectedChart} style={{ flexGrow: 1 }} onChange={onChartChange} /> +
+ {selectedChart ? ( + chart={selectedChart} onChange={onChartChange} /> + ) : ( + + )} +
) diff --git a/src/components/d3/plugins/D3HorizontalCursor.tsx b/src/components/d3/plugins/D3HorizontalCursor.tsx index 2a3f212..3b99157 100644 --- a/src/components/d3/plugins/D3HorizontalCursor.tsx +++ b/src/components/d3/plugins/D3HorizontalCursor.tsx @@ -1,4 +1,4 @@ -import { CSSProperties, ReactNode, SVGProps, useEffect, useMemo, useRef, useState } from 'react' +import { CSSProperties, ReactNode, SVGProps, useCallback, useEffect, useMemo, useRef, useState } from 'react' import * as d3 from 'd3' import { useD3MouseZone } from '@components/d3/D3MouseZone' @@ -104,7 +104,7 @@ const _D3HorizontalCursor = ({ if (!unsubscribe() && isDev()) console.warn('Не удалось отвязать эвент') } - }, [subscribe]) + }, [subscribe, fixed, mouseState.visible]) useEffect(() => { if (!zone || !getXLine) return diff --git a/src/components/d3/plugins/D3Tooltip.tsx b/src/components/d3/plugins/D3Tooltip.tsx index 8622d59..86e9f9c 100644 --- a/src/components/d3/plugins/D3Tooltip.tsx +++ b/src/components/d3/plugins/D3Tooltip.tsx @@ -110,7 +110,7 @@ function _D3Tooltip>({ if (!unsubscribe() && isDev()) console.warn('Не удалось отвязать эвент') } - }, [visible]) + }, [subscribe, visible]) useEffect(() => { if (!tooltipRef.current || !zoneRect || fixed) return diff --git a/src/pages/Telemetry/TelemetryView/index.jsx b/src/pages/Telemetry/TelemetryView/index.jsx index 7d5791c..8110c32 100755 --- a/src/pages/Telemetry/TelemetryView/index.jsx +++ b/src/pages/Telemetry/TelemetryView/index.jsx @@ -49,7 +49,6 @@ const makeDataset = (label, shortLabel, color, key, unit, other) => ({ label, shortLabel, color, - yAxis, xAxis: { type: 'linear', accessor: key, @@ -171,7 +170,7 @@ const TelemetryView = memo(() => { useEffect(() => { const subscribtion = saubSubject$.pipe( buffer(saubSubject$.pipe(throttleTime(700))) - ).subscribe((data) => handleDataSaub(data.flat())) + ).subscribe((data) => handleDataSaub(data.flat().filter(Boolean))) return () => subscribtion.unsubscribe() }, [saubSubject$]) @@ -179,7 +178,7 @@ const TelemetryView = memo(() => { useEffect(() => { const subscribtion = spinSubject$.pipe( buffer(spinSubject$.pipe(throttleTime(700))) - ).subscribe((data) => handleDataSpin(data.flat())) + ).subscribe((data) => handleDataSpin(data.flat().filter(Boolean))) return () => subscribtion.unsubscribe() }, [spinSubject$]) @@ -294,9 +293,11 @@ const TelemetryView = memo(() => { formatDate(d, 'YYYY-MM-DD')