enable updates are more efficient

This commit is contained in:
Nate Kelley 2025-04-02 14:26:30 -06:00
parent f8be3fa007
commit 1feb7558c2
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
11 changed files with 115 additions and 56 deletions

View File

@ -6,18 +6,36 @@ export interface ChartHoverBarPluginOptions {
isDarkMode?: boolean;
}
declare module 'chart.js' {
interface Chart {
$pluginHoverBarManager: {
enabled: boolean;
};
}
}
export const ChartHoverBarPlugin: Plugin<ChartType, ChartHoverBarPluginOptions> = {
id: 'tooltipHoverBar',
afterInit: (chart) => {
//@ts-ignore
const chartType = chart.config.type as ChartType;
// Store whether this is a bar chart to avoid checking on every draw
chart.$pluginHoverBarManager = {
enabled:
chartType === 'bar' ||
(chartType === 'line' && chart.data.datasets.some((dataset) => dataset.type === 'bar')) //this line is for combo chart
};
},
beforeDraw: (chart, args, options) => {
// Early return if not a bar chart (check only once during initialization)
if (!chart.$pluginHoverBarManager.enabled) return;
const {
ctx,
tooltip,
chartArea: { top, bottom },
scales: { x }
} = chart;
const chartType = (chart.config as any).type as ChartType;
if (chartType !== 'bar') return;
const tooltipActive = tooltip?.getActiveElements();

View File

@ -6,39 +6,54 @@ export interface ChartHoverLinePluginOptions {
lineDash?: number[];
}
declare module 'chart.js' {
interface Chart {
$pluginHoverLineManager: {
enabled: boolean;
};
}
}
export const ChartHoverLinePlugin: Plugin<ChartType, ChartHoverLinePluginOptions> = {
id: 'tooltipHoverLine',
afterInit: (chart) => {
//@ts-ignore
const chartType = chart.config.type as ChartType;
chart.$pluginHoverLineManager = {
enabled: chartType === 'line'
};
},
beforeDraw: (chart, args, options) => {
if (!chart.$pluginHoverLineManager.enabled) return;
const {
ctx,
tooltip,
chartArea: { top, bottom }
} = chart;
const chartType = (chart.config as any).type as ChartType;
if (chartType === 'line') {
const tooltipActive = tooltip?.getActiveElements();
if (tooltipActive && tooltipActive.length) {
const activePoint = tooltipActive[0];
const x = activePoint.element.x;
const topY = top;
const bottomY = bottom;
const tooltipActive = tooltip?.getActiveElements();
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = options.lineWidth || 1;
ctx.strokeStyle = options.lineColor;
if (options.lineDash) {
ctx.setLineDash(options.lineDash);
}
ctx.stroke();
ctx.restore();
if (tooltipActive && tooltipActive.length) {
const activePoint = tooltipActive[0];
const x = activePoint.element.x;
const topY = top;
const bottomY = bottom;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = options.lineWidth || 1;
ctx.strokeStyle = options.lineColor;
if (options.lineDash) {
ctx.setLineDash(options.lineDash);
}
ctx.stroke();
ctx.restore();
}
},
defaults: {
lineWidth: 1,
lineColor: 'rgba(0,0,0,0.22)',

View File

@ -10,11 +10,23 @@ declare module 'chart.js' {
interface PluginOptionsByType<TType extends ChartType> {
hoverScatter?: ChartHoverScatterPluginOptions | false;
}
interface Chart {
$pluginHoverScatterManager: {
enabled: boolean;
};
}
}
export const ChartHoverScatterPlugin: Plugin<ChartType, ChartHoverScatterPluginOptions> = {
id: 'tooltipHoverScatter',
afterInit: (chart) => {
//@ts-ignore
const chartType = chart.config.type as ChartType;
chart.$pluginHoverScatterManager = {
enabled: chartType === 'scatter' || chartType === 'bubble'
};
},
defaults: {
color: 'rgba(0,0,0,0.6)',
lineWidth: 0.65,
@ -22,11 +34,9 @@ export const ChartHoverScatterPlugin: Plugin<ChartType, ChartHoverScatterPluginO
},
beforeDraw: (chart, args, options) => {
const { ctx, chartArea } = chart;
if (!chart.$pluginHoverScatterManager.enabled) return;
// Only show crosshair for scatter and bubble charts
const type = (chart.config as any).type as ChartType;
if (type !== 'scatter' && type !== 'bubble') return;
const { ctx, chartArea } = chart;
// Get mouse position from chart
const activeElements = chart.getActiveElements();

View File

@ -29,6 +29,8 @@ export const ChartTotalizerPlugin: Plugin<ChartType, ChartTotalizerPluginOptions
beforeUpdate: (_chart, args, options) => {
if (options?.enabled === false) return;
console.log('here');
const chart = _chart as TotalizerChart;
const stackTotals: Record<string, number> = {};
const seriesTotals: number[] = [];

View File

@ -29,7 +29,9 @@ export const getLegendItems = ({
//@ts-ignore
const globalType: ChartType = (chartRef.current?.config.type as 'pie') || ChartType.Bar;
const isPieChart = globalType === ChartType.Pie;
const data = chartRef.current?.data!;
const data = chartRef.current?.data;
if (!data) return [];
if (isPieChart) {
const labels: string[] = data.labels as string[];

View File

@ -13,9 +13,10 @@ import { DeepPartial } from 'utility-types';
import type { ScaleChartOptions, Scale, GridLineOptions } from 'chart.js';
import { useXAxisTitle } from '../../../commonHelpers/useXAxisTitle';
import { useIsStacked } from './useIsStacked';
import { formatLabel, isNumericColumnType } from '@/lib';
import { formatLabel, isNumericColumnType, truncateText } from '@/lib';
import isDate from 'lodash/isDate';
import { Chart as ChartJS } from 'chart.js';
import { truncate } from 'lodash';
const DEFAULT_X_AXIS_TICK_CALLBACK = ChartJS.defaults.scales.category.ticks.callback;
@ -88,7 +89,6 @@ export const useXAxis = ({
if (isComboChart && columnSettings) {
const allYAxisKeys = [...selectedAxis.y, ...((selectedAxis as ComboChartAxis).y2 || [])];
console.log(allYAxisKeys, columnSettings);
const atLeastOneLineVisualization = allYAxisKeys.some(
(y) =>
columnSettings[y]?.columnVisualization === 'line' ||
@ -134,7 +134,7 @@ export const useXAxis = ({
const xKey = selectedAxis.x[0];
const xColumnLabelFormat = xAxisColumnFormats[xKey];
const res = formatLabel(rawValue, xColumnLabelFormat);
return DEFAULT_X_AXIS_TICK_CALLBACK.call(this, res, index, this.getLabels() as any);
return truncateText(res, 20);
}
return DEFAULT_X_AXIS_TICK_CALLBACK.call(this, value, index, this.getLabels() as any);

View File

@ -6,7 +6,8 @@ import {
getDefaultDateOptions,
getDefaultDayOfWeekOptions,
getDefaultMonthOptions,
getDefaultQuarterOptions
getDefaultQuarterOptions,
NO_FORMATTING_ITEM
} from './dateConfig';
import first from 'lodash/last';
import { formatDate, getNow } from '@/lib/date';
@ -47,26 +48,31 @@ export const EditDateFormat: React.FC<{
}, [dateFormat, defaultOptions, useAlternateFormats]);
const selectedOption = useMemo(() => {
if (dateFormat === '') return NO_FORMATTING_ITEM;
return selectOptions.find((option) => option.value === dateFormat) || first(selectOptions)!;
}, [dateFormat, selectOptions]);
const onChange = useMemoizedFn((value: string) => {
onUpdateColumnConfig({
dateFormat: value as IColumnLabelFormat['dateFormat']
});
const onChange = useMemoizedFn((value: IColumnLabelFormat['dateFormat']) => {
if (value === NO_FORMATTING_ITEM.value) {
onUpdateColumnConfig({
dateFormat: ''
});
} else {
onUpdateColumnConfig({
dateFormat: value
});
}
});
return (
<LabelAndInput label="Date format">
<div className="w-full overflow-hidden">
<Select
key={convertNumberTo}
className="w-full!"
items={selectOptions}
value={selectedOption.value}
onChange={onChange}
/>
</div>
<Select
key={convertNumberTo}
className="w-full!"
items={selectOptions}
value={selectedOption.value}
onChange={onChange}
/>
</LabelAndInput>
);
});

View File

@ -26,6 +26,7 @@ import isEmpty from 'lodash/isEmpty';
import { useGetCurrencies } from '@/api/buster_rest/nextjs/currency';
import { cn } from '@/lib/classMerge';
import { useUpdateMetricChart } from '@/context/Metrics';
import { ErrorBoundary } from '@/components/ui/error';
export const SelectAxisDropdownContent: React.FC<{
columnSetting: IBusterMetricChartConfig['columnSettings'][string];
@ -427,11 +428,13 @@ const LabelSettings: React.FC<{
if (ComponentsLoop.length === 0) return null;
return (
<div className={`${className} flex w-full flex-col space-y-3 overflow-hidden p-3`}>
{ComponentsLoop.map(({ key, Component }) => {
return <React.Fragment key={key}>{Component}</React.Fragment>;
})}
</div>
<ErrorBoundary>
<div className={`${className} flex w-full flex-col space-y-3 overflow-hidden p-3`}>
{ComponentsLoop.map(({ key, Component }) => {
return <React.Fragment key={key}>{Component}</React.Fragment>;
})}
</div>
</ErrorBoundary>
);
};

View File

@ -1,15 +1,18 @@
import { formatDate } from '@/lib';
const NO_FORMATTING_ID = 'NO_FORMATTING_👻';
export const NO_FORMATTING_ITEM = {
label: 'No Formatting',
value: NO_FORMATTING_ID
};
export const getDefaultDateOptions = (now: Date) => {
return [
{
label: 'Auto Format',
value: 'auto'
},
{
label: 'No Formatting',
value: ''
},
NO_FORMATTING_ITEM,
{
label: formatDate({
date: now,

View File

@ -43,7 +43,7 @@ const VerticalBarContainer: React.FC<{
}> = React.memo(({ showBar, isCompletedStream, status }) => {
return (
<div className="ml-2 flex w-5 min-w-5 flex-col items-center pt-0.5">
<StatusIndicator status={'loading'} />
<StatusIndicator status={status} />
<VerticalBar show={showBar} isCompletedStream={isCompletedStream} />
</div>
);

View File

@ -81,6 +81,6 @@ export const calculateTextWidth = (text: string, font: string): number => {
export const truncateText = (text: string, characters: number) => {
if (text.length <= characters) return text;
const truncatedText = text.slice(0, characters) + '...';
const truncatedText = (String(text || '') || '').slice(0, characters) + '...';
return truncatedText;
};