Косметические улучшения кода

This commit is contained in:
goodmice 2022-08-15 12:49:11 +05:00
parent 8ade639e0e
commit 1de4e8d909
2 changed files with 34 additions and 13 deletions

View File

@ -1,9 +1,9 @@
import { memo, useEffect, useMemo, useState } from 'react' import { CSSProperties, memo, useEffect, useMemo, useState } from 'react'
import * as d3 from 'd3' import * as d3 from 'd3'
import { Grid, GridItem } from '@components/Grid' import { Grid, GridItem } from '@components/Grid'
import { formatDate, makeDisplayValue } from '@utils'
import { TelemetryDataSaubDto } from '@api' import { TelemetryDataSaubDto } from '@api'
import { formatDate } from '@utils'
type LimitChartData = { type LimitChartData = {
id: number id: number
@ -77,6 +77,8 @@ export type D3MonitoringLimitChartProps<DataType> = {
const tooltipWidth = 270 const tooltipWidth = 270
const tooltipHeight = 120 const tooltipHeight = 120
const displayValue = makeDisplayValue()
const _D3MonitoringLimitChart = <DataType extends TelemetryDataSaubDto>({ const _D3MonitoringLimitChart = <DataType extends TelemetryDataSaubDto>({
yAxis, yAxis,
data: chartData, data: chartData,
@ -85,7 +87,7 @@ const _D3MonitoringLimitChart = <DataType extends TelemetryDataSaubDto>({
left, left,
top, top,
regulators, regulators,
zoneWidth = 0 zoneWidth = 0,
}: D3MonitoringLimitChartProps<DataType>) => { }: D3MonitoringLimitChartProps<DataType>) => {
const [ref, setRef] = useState<SVGGElement | null>(null) const [ref, setRef] = useState<SVGGElement | null>(null)
const [selected, setSelected] = useState<LimitChartData & { x: number, y: number, visible: boolean }>() const [selected, setSelected] = useState<LimitChartData & { x: number, y: number, visible: boolean }>()
@ -108,22 +110,37 @@ const _D3MonitoringLimitChart = <DataType extends TelemetryDataSaubDto>({
const y = yAxis(d.dateStart) - tooltipHeight const y = yAxis(d.dateStart) - tooltipHeight
setSelected({ ...d, y, x: -tooltipWidth - 10, visible: true }) setSelected({ ...d, y, x: -tooltipWidth - 10, visible: true })
}) })
.on('mouseout', (_, d) => setSelected((pre) => pre ? ({ ...pre, visible: false }) : undefined)) .on('mouseout', () => setSelected((pre) => pre ? ({ ...pre, visible: false }) : undefined))
}, [yAxis, data, ref, width]) }, [yAxis, data, ref, width])
const zoneY1 = useMemo(() => yAxis && selected ? yAxis(selected.dateStart) : 0, [yAxis, selected]) const zoneY1 = useMemo(() => yAxis && selected ? yAxis(selected.dateStart) : 0, [yAxis, selected])
const zoneY2 = useMemo(() => yAxis && selected ? yAxis(selected.dateEnd) : 0, [yAxis, selected]) const zoneY2 = useMemo(() => yAxis && selected ? yAxis(selected.dateEnd) : 0, [yAxis, selected])
const tooltipStyle = useMemo(() => ({ transition: 'opacity .1s ease-in-out', opacity: selected?.visible ? 1 : 0 }), [selected]) const opacityStyle: CSSProperties = useMemo(() => ({
transition: 'opacity .1s ease-in-out',
opacity: selected?.visible ? 1 : 0,
}), [selected])
const tooltipStyle: CSSProperties = useMemo(() => ({
...opacityStyle,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}), [opacityStyle])
return ( return (
<g transform={`translate(${left}, ${top})`} stroke={'#333'} strokeWidth={1} fill={'none'}> <g transform={`translate(${left}, ${top})`} stroke={'#333'} strokeWidth={1} fill={'none'}>
<g ref={setRef} > <g ref={setRef} >
<g className={'bars'} strokeWidth={0} /> <g className={'bars'} strokeWidth={0} />
{selected && ( {selected && (
<g strokeDasharray={'6, 3, 3, 3'} style={tooltipStyle} stroke={regulators[selected.id].color}> <g
<line x1={-zoneWidth} y1={zoneY1} y2={zoneY1} /> style={opacityStyle}
<line x1={-zoneWidth} y1={zoneY2} y2={zoneY2} /> pointerEvents={'none'}
strokeDasharray={'6, 3, 3, 3'}
stroke={regulators[selected.id].color}
>
<line x1={-zoneWidth} x2={0} y1={zoneY1} y2={zoneY1} />
<line x1={-zoneWidth} x2={0} y1={zoneY2} y2={zoneY2} />
<rect <rect
opacity={0.1} opacity={0.1}
stroke={'none'} stroke={'none'}
@ -139,17 +156,17 @@ const _D3MonitoringLimitChart = <DataType extends TelemetryDataSaubDto>({
<rect x={0} y={0} width={width} height={height} /> <rect x={0} y={0} width={width} height={height} />
{selected && ( {selected && (
<foreignObject width={tooltipWidth} height={tooltipHeight} x={selected.x} y={selected.y} pointerEvents={'none'}> <foreignObject width={tooltipWidth} height={tooltipHeight} x={selected.x} y={selected.y} pointerEvents={'none'}>
<div className={'tooltip bottom'} style={{ ...tooltipStyle, display: 'flex', flexDirection: 'column', alignItems: 'center'}}> <div className={'tooltip bottom'} style={tooltipStyle}>
<span>Ограничивающий параметр</span> <span>Ограничивающий параметр</span>
<span>{regulators[selected.id].label}</span> <span>{regulators[selected.id].label}</span>
<Grid style={{ margin: 0 }}> <Grid style={{ margin: 0, padding: 0 }}>
<GridItem row={1} col={1}>Начало:</GridItem> <GridItem row={1} col={1}>Начало:</GridItem>
<GridItem row={1} col={2}>{formatDate(selected.dateStart)}</GridItem> <GridItem row={1} col={2}>{formatDate(selected.dateStart)}</GridItem>
<GridItem row={1} col={3}>{selected.depthStart?.toFixed(2) ?? '---'}</GridItem> <GridItem row={1} col={3}>{displayValue(selected.depthStart)}</GridItem>
<GridItem row={1} col={4}>м.</GridItem> <GridItem row={1} col={4}>м.</GridItem>
<GridItem row={2} col={1}>Конец:</GridItem> <GridItem row={2} col={1}>Конец:</GridItem>
<GridItem row={2} col={2}>{formatDate(selected.dateEnd)}</GridItem> <GridItem row={2} col={2}>{formatDate(selected.dateEnd)}</GridItem>
<GridItem row={2} col={3}>{selected.depthEnd?.toFixed(2) ?? '---'}</GridItem> <GridItem row={2} col={3}>{displayValue(selected.depthEnd)}</GridItem>
<GridItem row={2} col={4}>м.</GridItem> <GridItem row={2} col={4}>м.</GridItem>
</Grid> </Grid>
</div> </div>

View File

@ -35,7 +35,11 @@ export const makeDisplayValue = ({
def = '----', def = '----',
inf = (v) => `${v < 0 ? '-' : ''}\u221E`, inf = (v) => `${v < 0 ? '-' : ''}\u221E`,
fixed = 2 fixed = 2
}: DisplayValueOptions) => (v: unknown): ReactNode => { }: DisplayValueOptions = {
def: '----',
inf: (v) => `${v < 0 ? '-' : ''}\u221E`,
fixed: 2
}) => (v: unknown): ReactNode => {
if (typeof v === 'undefined' || v === null || String(v) === 'NaN') return def if (typeof v === 'undefined' || v === null || String(v) === 'NaN') return def
let f = Number(v) let f = Number(v)
if (typeof v === 'string') { if (typeof v === 'string') {