diff --git a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-bar.tsx b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-bar.tsx index 36d7773e9..792f89c59 100644 --- a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-bar.tsx +++ b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-bar.tsx @@ -6,18 +6,36 @@ export interface ChartHoverBarPluginOptions { isDarkMode?: boolean; } +declare module 'chart.js' { + interface Chart { + $pluginHoverBarManager: { + enabled: boolean; + }; + } +} + export const ChartHoverBarPlugin: Plugin = { 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(); diff --git a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-line.tsx b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-line.tsx index be470ee59..4b58c0779 100644 --- a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-line.tsx +++ b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-line.tsx @@ -6,39 +6,54 @@ export interface ChartHoverLinePluginOptions { lineDash?: number[]; } +declare module 'chart.js' { + interface Chart { + $pluginHoverLineManager: { + enabled: boolean; + }; + } +} + export const ChartHoverLinePlugin: Plugin = { 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)', diff --git a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-scatter.tsx b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-scatter.tsx index 530af7cf5..546d186b6 100644 --- a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-scatter.tsx +++ b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-hover-scatter.tsx @@ -10,11 +10,23 @@ declare module 'chart.js' { interface PluginOptionsByType { hoverScatter?: ChartHoverScatterPluginOptions | false; } + + interface Chart { + $pluginHoverScatterManager: { + enabled: boolean; + }; + } } export const ChartHoverScatterPlugin: Plugin = { 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 { - 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(); diff --git a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-totalizer.ts b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-totalizer.ts index 4676fa892..8e0e44ea8 100644 --- a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-totalizer.ts +++ b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-totalizer.ts @@ -29,6 +29,8 @@ export const ChartTotalizerPlugin: Plugin { if (options?.enabled === false) return; + console.log('here'); + const chart = _chart as TotalizerChart; const stackTotals: Record = {}; const seriesTotals: number[] = []; diff --git a/web/src/components/ui/charts/BusterChartJS/hooks/useBusterChartJSLegend/helper.ts b/web/src/components/ui/charts/BusterChartJS/hooks/useBusterChartJSLegend/helper.ts index de445f0da..a765720a0 100644 --- a/web/src/components/ui/charts/BusterChartJS/hooks/useBusterChartJSLegend/helper.ts +++ b/web/src/components/ui/charts/BusterChartJS/hooks/useBusterChartJSLegend/helper.ts @@ -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[]; diff --git a/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useXAxis.ts b/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useXAxis.ts index f7b2b03d4..97450bb49 100644 --- a/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useXAxis.ts +++ b/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useXAxis.ts @@ -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); diff --git a/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/EditDateFormat.tsx b/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/EditDateFormat.tsx index 1df336bad..df168b531 100644 --- a/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/EditDateFormat.tsx +++ b/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/EditDateFormat.tsx @@ -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 ( -
- ); }); diff --git a/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/SelectAxisDropdownContent.tsx b/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/SelectAxisDropdownContent.tsx index 053a55748..3a52e1e33 100644 --- a/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/SelectAxisDropdownContent.tsx +++ b/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/SelectAxisDropdownContent.tsx @@ -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 ( -
- {ComponentsLoop.map(({ key, Component }) => { - return {Component}; - })} -
+ +
+ {ComponentsLoop.map(({ key, Component }) => { + return {Component}; + })} +
+
); }; diff --git a/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/dateConfig.ts b/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/dateConfig.ts index ec6e4dfc8..2f134d8d2 100644 --- a/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/dateConfig.ts +++ b/web/src/controllers/MetricController/MetricViewChart/MetricEditController/MetricStylingApp/StylingAppVisualize/SelectAxis/SelectAxisColumnContent/dateConfig.ts @@ -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, diff --git a/web/src/controllers/ReasoningController/ReasoningMessages/BarContainer.tsx b/web/src/controllers/ReasoningController/ReasoningMessages/BarContainer.tsx index e2e895a6e..86cabfca5 100644 --- a/web/src/controllers/ReasoningController/ReasoningMessages/BarContainer.tsx +++ b/web/src/controllers/ReasoningController/ReasoningMessages/BarContainer.tsx @@ -43,7 +43,7 @@ const VerticalBarContainer: React.FC<{ }> = React.memo(({ showBar, isCompletedStream, status }) => { return (
- +
); diff --git a/web/src/lib/text.ts b/web/src/lib/text.ts index 4d0740bd1..5d10b7b5d 100644 --- a/web/src/lib/text.ts +++ b/web/src/lib/text.ts @@ -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; };