forked from ddrilling/asb_cloud_front
Добавлен механизм автоматического скалирования графиков на группах вертикальных графиков
This commit is contained in:
parent
d5a7d947c6
commit
719180392e
@ -27,16 +27,32 @@ import D3MouseZone from './D3MouseZone'
|
|||||||
import { getByAccessor, getChartClass, getGroupClass, getTicks } from './functions'
|
import { getByAccessor, getChartClass, getGroupClass, getTicks } from './functions'
|
||||||
import { renderArea, renderLine, renderNeedle, renderPoint } from './renders'
|
import { renderArea, renderLine, renderNeedle, renderPoint } from './renders'
|
||||||
|
|
||||||
|
const roundTo = (v: number, to: number = 50) => {
|
||||||
|
if (to == 0) return v
|
||||||
|
if (v < 0) return Math.round(v / to) * to
|
||||||
|
return Math.ceil(v / to) * to
|
||||||
|
}
|
||||||
|
|
||||||
|
const calculateDomain = (mm: MinMax, round: number = 100): Required<MinMax> => {
|
||||||
|
let min = roundTo(mm.min ?? 0, round)
|
||||||
|
let max = roundTo(mm.max ?? round, round)
|
||||||
|
if (min - max < round) {
|
||||||
|
const mid = (min + max) / 2
|
||||||
|
min = mid - round
|
||||||
|
max = mid + round
|
||||||
|
}
|
||||||
|
return { min, max }
|
||||||
|
}
|
||||||
|
|
||||||
type AxisScale = d3.ScaleTime<number, number, never> | d3.ScaleLinear<number, number, never>
|
type AxisScale = d3.ScaleTime<number, number, never> | d3.ScaleLinear<number, number, never>
|
||||||
|
|
||||||
type ExtendedChartDataset<DataType> = ChartDataset<DataType> & {
|
type ExtendedChartDataset<DataType> = ChartDataset<DataType> & {
|
||||||
yDomain: MinMax
|
xDomain: MinMax
|
||||||
hideXAxis?: boolean
|
hideXAxis?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtendedChartRegistry<DataType> = ExtendedChartDataset<DataType> & {
|
type ExtendedChartRegistry<DataType> = ExtendedChartDataset<DataType> & {
|
||||||
(): d3.Selection<SVGGElement, DataType, any, any>
|
(): d3.Selection<SVGGElement, DataType, any, any>
|
||||||
scale: d3.ScaleLinear<number, number>
|
|
||||||
y: (value: any) => number
|
y: (value: any) => number
|
||||||
x: (value: any) => number
|
x: (value: any) => number
|
||||||
}
|
}
|
||||||
@ -110,7 +126,7 @@ export type ChartSizes = ChartOffset & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const axisHeight = 20
|
const axisHeight = 20
|
||||||
const space = 20
|
const space = 30
|
||||||
|
|
||||||
const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
||||||
width: givenWidth = '100%',
|
width: givenWidth = '100%',
|
||||||
@ -188,6 +204,32 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
}
|
}
|
||||||
), [chartArea, axesArea])
|
), [chartArea, axesArea])
|
||||||
|
|
||||||
|
const chartDomains: Record<string, {
|
||||||
|
scale: d3.ScaleLinear<number, number>,
|
||||||
|
domain: Required<MinMax>,
|
||||||
|
}>[] = useMemo(() => {
|
||||||
|
return groups.map((group) => {
|
||||||
|
const out = group.charts.map((chart) => {
|
||||||
|
const mm = { ...chart.xDomain }
|
||||||
|
let domain: Required<MinMax> = { min: 0, max: 100 }
|
||||||
|
if (mm.min && mm.max) {
|
||||||
|
domain = mm as Required<MinMax>
|
||||||
|
} else if (data) {
|
||||||
|
const [min, max] = d3.extent(data, chart.x)
|
||||||
|
domain = calculateDomain({ min, max, ...mm }, 100)
|
||||||
|
}
|
||||||
|
return [chart.key, {
|
||||||
|
scale: d3.scaleLinear().domain([domain.min, domain.max]),
|
||||||
|
domain,
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
return Object.fromEntries(out)
|
||||||
|
})
|
||||||
|
}, [groups, data])
|
||||||
|
|
||||||
|
console.log(chartDomains)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
datasetGroups.forEach((sets, i) => {
|
datasetGroups.forEach((sets, i) => {
|
||||||
@ -228,15 +270,6 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
group.charts[chartIdx]().selectAll('*').remove()
|
group.charts[chartIdx]().selectAll('*').remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
const yDomain = {
|
|
||||||
min: 0,
|
|
||||||
max: 10,
|
|
||||||
...dataset.yDomain,
|
|
||||||
}
|
|
||||||
|
|
||||||
const scale = d3.scaleLinear()
|
|
||||||
.domain([yDomain.min, yDomain.max])
|
|
||||||
|
|
||||||
// Пересоздаём график
|
// Пересоздаём график
|
||||||
const newChart: ExtendedChartRegistry<DataType> = Object.assign(
|
const newChart: ExtendedChartRegistry<DataType> = Object.assign(
|
||||||
() => group().select('.' + getChartClass(dataset.key)) as d3.Selection<SVGGElement, DataType, any, unknown>,
|
() => group().select('.' + getChartClass(dataset.key)) as d3.Selection<SVGGElement, DataType, any, unknown>,
|
||||||
@ -248,8 +281,6 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
animDurationMs,
|
animDurationMs,
|
||||||
...dataset,
|
...dataset,
|
||||||
yAxis: dataset.yAxis ?? yAxisConfig,
|
yAxis: dataset.yAxis ?? yAxisConfig,
|
||||||
yDomain,
|
|
||||||
scale,
|
|
||||||
y: getByAccessor(dataset.yAxis.accessor ?? yAxisConfig.accessor),
|
y: getByAccessor(dataset.yAxis.accessor ?? yAxisConfig.accessor),
|
||||||
x: getByAccessor(dataset.xAxis?.accessor),
|
x: getByAccessor(dataset.xAxis?.accessor),
|
||||||
}
|
}
|
||||||
@ -288,7 +319,7 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
.attr('class', (g) => `charts-group ${getGroupClass(g.key)}`)
|
.attr('class', (g) => `charts-group ${getGroupClass(g.key)}`)
|
||||||
.attr('transform', (g) => `translate(${sizes.groupLeft(g.key)}, 0)`)
|
.attr('transform', (g) => `translate(${sizes.groupLeft(g.key)}, 0)`)
|
||||||
|
|
||||||
actualAxesGroups.each(function(group) {
|
actualAxesGroups.each(function(group, i) {
|
||||||
const groupAxes = d3.select(this)
|
const groupAxes = d3.select(this)
|
||||||
const chartsData = group.charts.filter((chart) => !chart.hideXAxis)
|
const chartsData = group.charts.filter((chart) => !chart.hideXAxis)
|
||||||
const charts = groupAxes.selectChildren().data(chartsData)
|
const charts = groupAxes.selectChildren().data(chartsData)
|
||||||
@ -302,17 +333,18 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
.style('color', (d) => d.color ?? null)
|
.style('color', (d) => d.color ?? null)
|
||||||
|
|
||||||
actualCharts.each(function (chart, j) {
|
actualCharts.each(function (chart, j) {
|
||||||
let axis = d3.axisTop(chart.scale.range([0, sizes.groupWidth]))
|
let axis = d3.axisTop(chartDomains[i][chart.key].scale.range([0, sizes.groupWidth]))
|
||||||
|
const domain = chartDomains[i][chart.key].domain
|
||||||
|
|
||||||
if (j === chartsData.length - 1) {
|
if (j === chartsData.length - 1) {
|
||||||
axis = axis
|
axis = axis
|
||||||
.ticks(5)
|
.ticks(5)
|
||||||
.tickSize(-sizes.chartsHeight)
|
.tickSize(-sizes.chartsHeight)
|
||||||
.tickFormat((d, i) => i === 0 || i === 5 ? String(d) : '')
|
.tickFormat((d, i) => i === 0 || i === 5 ? String(d) : '')
|
||||||
.tickValues(getTicks(chart.yDomain, 5))
|
.tickValues(getTicks(domain, 5))
|
||||||
} else {
|
} else {
|
||||||
axis = axis.ticks(1)
|
axis = axis.ticks(1)
|
||||||
.tickValues(getTicks(chart.yDomain, 1))
|
.tickValues(getTicks(domain, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
d3.select(this).call(axis as any)
|
d3.select(this).call(axis as any)
|
||||||
@ -335,7 +367,7 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
.attr('stroke', j === chartsData.length - 1 ? 'gray' : 'currentColor')
|
.attr('stroke', j === chartsData.length - 1 ? 'gray' : 'currentColor')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, [groups, groupScales, sizes, space])
|
}, [groups, groupScales, sizes, space, chartDomains])
|
||||||
|
|
||||||
useEffect(() => { // Рисуем ось Y
|
useEffect(() => { // Рисуем ось Y
|
||||||
if (!yAxis) return
|
if (!yAxis) return
|
||||||
@ -361,7 +393,7 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data || !yAxis) return
|
if (!data || !yAxis) return
|
||||||
|
|
||||||
groups.forEach((group) => {
|
groups.forEach((group, i) => {
|
||||||
group()
|
group()
|
||||||
.attr('transform', `translate(${sizes.groupLeft(group.key)}, 0)`)
|
.attr('transform', `translate(${sizes.groupLeft(group.key)}, 0)`)
|
||||||
.attr('clip-path', `url(#chart-clip)`)
|
.attr('clip-path', `url(#chart-clip)`)
|
||||||
@ -377,7 +409,7 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
let chartData = data
|
let chartData = data
|
||||||
if (!chartData) return
|
if (!chartData) return
|
||||||
|
|
||||||
const xAxis = chart.scale.range([0, sizes.groupWidth])
|
const xAxis = chartDomains[i][chart.key].scale.range([0, sizes.groupWidth])
|
||||||
|
|
||||||
switch (chart.type) {
|
switch (chart.type) {
|
||||||
case 'needle':
|
case 'needle':
|
||||||
@ -397,12 +429,12 @@ const _D3MonitoringCharts = <DataType extends Record<string, unknown>>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (chart.point)
|
if (chart.point)
|
||||||
renderPoint<DataType>(chart.scale, yAxis, chart, chartData, true)
|
renderPoint<DataType>(xAxis, yAxis, chart, chartData, true)
|
||||||
|
|
||||||
chart.afterDraw?.(chart)
|
chart.afterDraw?.(chart)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, [data, groups, groupScales, height, offset, sizes])
|
}, [data, groups, groupScales, height, offset, sizes, chartDomains])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoaderPortal
|
<LoaderPortal
|
||||||
|
Loading…
Reference in New Issue
Block a user