diff --git a/src/components/widgets/BaseWidget.tsx b/src/components/widgets/BaseWidget.tsx index 8ae09ad..e3463ec 100644 --- a/src/components/widgets/BaseWidget.tsx +++ b/src/components/widgets/BaseWidget.tsx @@ -2,6 +2,8 @@ import { Button } from 'antd' import { memo, ReactNode, useMemo } from 'react' import { CloseOutlined, SettingOutlined } from '@ant-design/icons' +import { makeDisplayValue } from '@utils' + import '@styles/widgets/base.less' export type WidgetSettings = { @@ -19,13 +21,7 @@ export type WidgetSettings = { const emptyNumber = '----' -const defaultFormatter = (v: any) => { - if (typeof v === 'undefined' || v === null || String(v) === 'NaN') return emptyNumber - if (Number.isNaN(+v)) return v - const f = parseFloat(v) - if (Number.isFinite(v)) return f.toFixed(2) - return `${f < 0 ? '-' : ''}\u221E` -} +const defaultFormatter = makeDisplayValue({ def: emptyNumber, fixed: 2 }) export const defaultSettings: WidgetSettings = { unit: '----', diff --git a/src/pages/Telemetry/TelemetryView/cursorRender.jsx b/src/pages/Telemetry/TelemetryView/cursorRender.jsx index c6cd306..d788a02 100644 --- a/src/pages/Telemetry/TelemetryView/cursorRender.jsx +++ b/src/pages/Telemetry/TelemetryView/cursorRender.jsx @@ -1,39 +1,34 @@ -import { BarChartOutlined, LineChartOutlined, DotChartOutlined, AreaChartOutlined, BorderOuterOutlined } from '@ant-design/icons' -import { Grid, GridItem } from '@components/Grid' import { Fragment } from 'react' +import { Grid, GridItem } from '@components/Grid' +import { getChartIcon, makeDisplayValue } from '@utils' + +const defaultFormater = makeDisplayValue({ def: '---', fixed: 2 }) +const defaultValueRender = (v, unit) => ( + <>{defaultFormater(v)} {unit ?? ''} +) + export const cursorRender = (group, data) => { const d = data.length > 0 ? data[0] : {} if (group.charts.length <= 0) return <> const firstChart = group.charts[0] const y = firstChart.y(d) - const yValue = firstChart.yAxis.format?.(y) ?? `${(+y).toFixed(2)} ${firstChart.yAxis.unit ?? ''}` - const yFormat = (chart) => { + const yValue = firstChart.yAxis.format?.(y) ?? defaultValueRender(y, chart.yAxis.unit) + const xFormat = (chart) => { const v = chart.x(d) - return chart.xAxis.format?.(v) ?? `${(+v)?.toFixed(2)} ${chart.xAxis.unit ?? ''}` + return chart.xAxis.format?.(v) ?? defaultValueRender(v, chart.xAxis.unit) } return ( {yValue} {group.charts.map((chart, i) => { - let Icon - switch (chart.type) { - case 'needle': Icon = BarChartOutlined; break - case 'line': Icon = LineChartOutlined; break - case 'point': Icon = DotChartOutlined; break - case 'area': Icon = AreaChartOutlined; break - case 'rect_area': Icon = BorderOuterOutlined; break - } - - const d = data[0] - return ( - + {getChartIcon(chart)} {chart.shortLabel || chart.label} - {yFormat(chart)} + {xFormat(chart)} ) })} diff --git a/src/pages/Telemetry/TelemetryView/index.jsx b/src/pages/Telemetry/TelemetryView/index.jsx index 5459fec..9739a77 100755 --- a/src/pages/Telemetry/TelemetryView/index.jsx +++ b/src/pages/Telemetry/TelemetryView/index.jsx @@ -38,7 +38,7 @@ const { Option } = Select const yAxis = { type: 'time', accessor: (d) => new Date(d.date), - format: (d) => formatDate(d), + format: (d) => formatDate(d, undefined, 'YYYY-MM-DD HH:mm:ss'), } const dash = [7, 3] @@ -251,7 +251,7 @@ const TelemetryView = memo(() => { yDomain={domain} yTicks={{ visible: true, - format: (d) => formatDate(d) + format: (d) => formatDate(d, 'YYYY-MM-DD') }} plugins={{ menu: { enabled: false }, diff --git a/src/utils/functions/chart.ts b/src/utils/functions/chart.tsx similarity index 50% rename from src/utils/functions/chart.ts rename to src/utils/functions/chart.tsx index 607e3cd..2c1e790 100644 --- a/src/utils/functions/chart.ts +++ b/src/utils/functions/chart.tsx @@ -1,3 +1,8 @@ +import { BarChartOutlined, LineChartOutlined, DotChartOutlined, AreaChartOutlined, BorderOuterOutlined, } from '@ant-design/icons' +import { AntdIconProps } from '@ant-design/icons/lib/components/AntdIcon' + +import { ChartDataset } from '@components/d3' + export const makePointsOptimizator = >(isEquals: (a: DataType, b: DataType) => boolean) => (points: DataType[]) => { if (!Array.isArray(points) || points.length < 3) return points @@ -17,3 +22,16 @@ export const getDistance = (x1: number, y1: number, x2: number, y2: number, type return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) } + +export const getChartIcon = (chart: ChartDataset, options: Omit) => { + let Icon + switch (chart.type) { + case 'needle': Icon = BarChartOutlined; break + case 'line': Icon = LineChartOutlined; break + case 'point': Icon = DotChartOutlined; break + case 'area': Icon = AreaChartOutlined; break + case 'rect_area': Icon = BorderOuterOutlined; break + } + + return +} diff --git a/src/utils/functions/numbers.ts b/src/utils/functions/numbers.tsx similarity index 58% rename from src/utils/functions/numbers.ts rename to src/utils/functions/numbers.tsx index 0624780..c60a46e 100644 --- a/src/utils/functions/numbers.ts +++ b/src/utils/functions/numbers.tsx @@ -1,3 +1,5 @@ +import { ReactNode } from 'react' + export const getPrecision = (number: number, def: string = '-', fixed: number = 2): string => Number.isFinite(number) ? number.toFixed(fixed) : def /** @@ -7,7 +9,7 @@ export const getPrecision = (number: number, def: string = '-', fixed: number = * @param max Максимальное значение * @returns Функция, ограничивающая значение в диапазоне [`min`; `max`] */ -export const limitValue = (min: T, max: T) => (value: T) => { +export const limitValue = (min: T, max: T) => (value: T) => { if (value > max) return max if (value < min) return min return value @@ -22,3 +24,25 @@ export const limitValue = (min: T, max: T) => (value: T) => { * @returns Массив чисел в диапазоне от `start` до `end` */ export const range = (end: number, start: number = 0) => Array.from({ length: end - start }, (_, i) => start + i) + +export type DisplayValueOptions = { + def?: ReactNode + inf?: ReactNode | ((v: number) => ReactNode) + fixed?: number +} + +export const makeDisplayValue = ({ + def = '----', + inf = (v) => `${v < 0 ? '-' : ''}\u221E`, + fixed = 2 +}: DisplayValueOptions) => (v: unknown): ReactNode => { + if (typeof v === 'undefined' || v === null || String(v) === 'NaN') return def + let f = Number(v) + if (typeof v === 'string') { + if (Number.isNaN(+v)) return v + f = parseFloat(v) + } + + if (Number.isFinite(f)) return f.toFixed(fixed) + return typeof inf === 'function' ? inf(f) : inf +}