fix: barchart, piechart flash issue
This commit is contained in:
@@ -3,7 +3,13 @@ import { Chart } from '@gpustack/core-ui';
|
|||||||
import { formatLargeNumber } from '@gpustack/core-ui/utils';
|
import { formatLargeNumber } from '@gpustack/core-ui/utils';
|
||||||
import { Empty, Spin, theme } from 'antd';
|
import { Empty, Spin, theme } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useEffect, useMemo, useRef } from 'react';
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
export interface BarSeriesItem {
|
export interface BarSeriesItem {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -50,6 +56,27 @@ const BarChart: React.FC<BarChartProps> = (props) => {
|
|||||||
const chartRef = useRef<{ chart: any } | null>(null);
|
const chartRef = useRef<{ chart: any } | null>(null);
|
||||||
const generateCoolColors = useCoolColors();
|
const generateCoolColors = useCoolColors();
|
||||||
|
|
||||||
|
// ECharts reads the DOM width at init time; with a "100%" width it can pick
|
||||||
|
// up a stale/tiny value while the flex child's layout is still resolving,
|
||||||
|
// rendering every bar squeezed at the left edge until its internal (throttled)
|
||||||
|
// ResizeObserver corrects it ~100ms later — a visible blue-sliver flash. We
|
||||||
|
// measure the container ourselves via a callback ref (which fires during
|
||||||
|
// commit, before paint) and feed ECharts an explicit pixel width, so the very
|
||||||
|
// first render is already correct. No gating, so the chart mounts with no
|
||||||
|
// extra delay; the ResizeObserver keeps the width in sync afterwards.
|
||||||
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
||||||
|
const [measuredWidth, setMeasuredWidth] = useState(0);
|
||||||
|
const measureRef = useCallback((node: HTMLDivElement | null) => {
|
||||||
|
resizeObserverRef.current?.disconnect();
|
||||||
|
if (!node) return;
|
||||||
|
setMeasuredWidth(node.clientWidth);
|
||||||
|
resizeObserverRef.current = new ResizeObserver(() => {
|
||||||
|
setMeasuredWidth(node.clientWidth);
|
||||||
|
});
|
||||||
|
resizeObserverRef.current.observe(node);
|
||||||
|
}, []);
|
||||||
|
useEffect(() => () => resizeObserverRef.current?.disconnect(), []);
|
||||||
|
|
||||||
const dynamicColors = useMemo(
|
const dynamicColors = useMemo(
|
||||||
() => generateCoolColors(seriesData.length),
|
() => generateCoolColors(seriesData.length),
|
||||||
[seriesData.length, generateCoolColors]
|
[seriesData.length, generateCoolColors]
|
||||||
@@ -289,12 +316,15 @@ const BarChart: React.FC<BarChartProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width, height, position: 'relative' }}>
|
<div
|
||||||
|
ref={measureRef}
|
||||||
|
style={{ width: width || '100%', height, position: 'relative' }}
|
||||||
|
>
|
||||||
<Chart
|
<Chart
|
||||||
ref={chartRef as any}
|
ref={chartRef as any}
|
||||||
options={options as any}
|
options={options as any}
|
||||||
height={height}
|
height={height}
|
||||||
width={width || '100%'}
|
width={measuredWidth || width || '100%'}
|
||||||
/>
|
/>
|
||||||
{loading && (
|
{loading && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -2,7 +2,13 @@ import useCoolColors from '@/hooks/use-cool-colors';
|
|||||||
import { Chart } from '@gpustack/core-ui';
|
import { Chart } from '@gpustack/core-ui';
|
||||||
import { formatLargeNumber } from '@gpustack/core-ui/utils';
|
import { formatLargeNumber } from '@gpustack/core-ui/utils';
|
||||||
import { Empty, Spin, theme } from 'antd';
|
import { Empty, Spin, theme } from 'antd';
|
||||||
import React, { useMemo, useRef } from 'react';
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
export interface PieChartItem {
|
export interface PieChartItem {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -37,6 +43,26 @@ const PieChart: React.FC<PieChartProps> = ({
|
|||||||
const chartRef = useRef<{ chart: any } | null>(null);
|
const chartRef = useRef<{ chart: any } | null>(null);
|
||||||
const generateCoolColors = useCoolColors();
|
const generateCoolColors = useCoolColors();
|
||||||
|
|
||||||
|
// ECharts reads the DOM width at init time; with a "100%" width it can pick
|
||||||
|
// up a stale/tiny value while layout is still resolving, briefly rendering
|
||||||
|
// the donut undersized before its internal (throttled) ResizeObserver
|
||||||
|
// corrects it — a visible flash. We measure the container via a callback ref
|
||||||
|
// (fires during commit, before paint) and feed ECharts an explicit pixel
|
||||||
|
// width so the first render is already correct. The ResizeObserver keeps it
|
||||||
|
// in sync afterwards.
|
||||||
|
const resizeObserverRef = useRef<ResizeObserver | null>(null);
|
||||||
|
const [measuredWidth, setMeasuredWidth] = useState(0);
|
||||||
|
const measureRef = useCallback((node: HTMLDivElement | null) => {
|
||||||
|
resizeObserverRef.current?.disconnect();
|
||||||
|
if (!node) return;
|
||||||
|
setMeasuredWidth(node.clientWidth);
|
||||||
|
resizeObserverRef.current = new ResizeObserver(() => {
|
||||||
|
setMeasuredWidth(node.clientWidth);
|
||||||
|
});
|
||||||
|
resizeObserverRef.current.observe(node);
|
||||||
|
}, []);
|
||||||
|
useEffect(() => () => resizeObserverRef.current?.disconnect(), []);
|
||||||
|
|
||||||
const colors = useMemo(() => {
|
const colors = useMemo(() => {
|
||||||
const generatedColors = generateCoolColors(data.length + colorOffset);
|
const generatedColors = generateCoolColors(data.length + colorOffset);
|
||||||
return generatedColors.slice(colorOffset);
|
return generatedColors.slice(colorOffset);
|
||||||
@@ -142,12 +168,12 @@ const PieChart: React.FC<PieChartProps> = ({
|
|||||||
const displayTotal = total ?? data.reduce((sum, item) => sum + item.value, 0);
|
const displayTotal = total ?? data.reduce((sum, item) => sum + item.value, 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width, height, position: 'relative' }}>
|
<div ref={measureRef} style={{ width, height, position: 'relative' }}>
|
||||||
<Chart
|
<Chart
|
||||||
ref={chartRef as any}
|
ref={chartRef as any}
|
||||||
options={options as any}
|
options={options as any}
|
||||||
height={height}
|
height={height}
|
||||||
width={width}
|
width={measuredWidth || width}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
Reference in New Issue
Block a user