From 1cf88971ebafb7500ced5c1c8a6986fd245d61f6 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Fri, 18 Apr 2025 12:30:24 -0600 Subject: [PATCH] start building timescale --- .../plugins/chartjs-plugin-tick-duplicate.ts | 66 ++++++++++++++----- .../stories/BusterChart.LineChart.stories.tsx | 6 +- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-tick-duplicate.ts b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-tick-duplicate.ts index 30b7dee5a..4353a66d3 100644 --- a/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-tick-duplicate.ts +++ b/web/src/components/ui/charts/BusterChartJS/core/plugins/chartjs-plugin-tick-duplicate.ts @@ -7,6 +7,7 @@ declare module 'chart.js' { export const ChartJSTickDuplicatePlugin: Plugin = { id: 'chartjs-plugin-tick-duplicate', afterBuildTicks(chart) { + console.log('afterBuildTicks'); const scale = chart.scales['x']; if (!scale || scale.type !== 'time') return; @@ -14,7 +15,7 @@ export const ChartJSTickDuplicatePlugin: Plugin = { const displayFormat = scale.options.time.displayFormats?.month || 'MMM'; const tickCallback = scale.options.ticks?.callback; - const allTicks = scale._generate(); // raw ticks + const allTicks = scale._generate(); // raw generated ticks const values = allTicks.map((t) => t.value ?? t); const seenLabels = new Set(); @@ -26,7 +27,6 @@ export const ChartJSTickDuplicatePlugin: Plugin = { let label; try { if (typeof tickCallback === 'function') { - // ✅ this is the KEY FIX: preserve `this` as the scale label = tickCallback.call(scale, value, i, values); } else { label = adapter.format(value, displayFormat); @@ -49,23 +49,57 @@ export const ChartJSTickDuplicatePlugin: Plugin = { const max = scale.max ?? Math.max(...values); const spacing = (max - min) / (unique.length - 1); - scale._customTicks = unique.map((u, i) => ({ - value: min + spacing * i, + scale.ticks = unique.map((u, i) => ({ + value: min + i * spacing, label: u.label })); - - scale.ticks = scale._customTicks; - }, - - beforeDraw(chart) { - const scale = chart.scales['x']; - if (!scale?._customTicks) return; - - scale.ticks = scale._customTicks.map((t) => ({ - ...t, - label: t.label - })); } }; export default ChartJSTickDuplicatePlugin; + +import { TimeScale } from 'chart.js'; + +export class DeduplicatedTimeScale extends TimeScale { + static id = 'time'; + static defaults = { + ...TimeScale.defaults + }; + generateTicks() { + const baseTicks = super.generateTicks(); // Chart.js handles spacing, maxTicksLimit, etc. + const tickCallback = this.options.ticks?.callback; + const format = this._adapter.format; + const displayFormat = this.options.time?.displayFormats?.month || 'MMM'; + + const seenLabels = new Set(); + const dedupedTicks = []; + + const values = baseTicks.map((t) => t.value); + + for (let i = 0; i < baseTicks.length; i++) { + const tick = baseTicks[i]; + + let label; + try { + if (typeof tickCallback === 'function') { + // Call with same context Chart.js uses + label = tickCallback.call(this, tick.value, i, values); + } else { + label = format(tick.value, displayFormat); + } + } catch (err) { + label = '???'; + console.warn('Tick callback error at index', i, err); + } + + const stringLabel = String(label); + + if (!seenLabels.has(stringLabel)) { + seenLabels.add(stringLabel); + dedupedTicks.push(tick); // original tick object (value + major flag) + } + } + + return dedupedTicks; + } +} diff --git a/web/src/components/ui/charts/stories/BusterChart.LineChart.stories.tsx b/web/src/components/ui/charts/stories/BusterChart.LineChart.stories.tsx index 4094eb8b4..abb770af0 100644 --- a/web/src/components/ui/charts/stories/BusterChart.LineChart.stories.tsx +++ b/web/src/components/ui/charts/stories/BusterChart.LineChart.stories.tsx @@ -332,13 +332,17 @@ export const FixedDateFormat_TimeIntervalTest_UnevenMonthsAutoUnit_BUSTED: Story export const FixedDateFormat_TimeIntervalTest_Days_WithForcedUnit: Story = { args: { ...AutoDateFormat_TimeIntervalTest_Days_WithForcedUnit.args, + data: Array.from({ length: 131 }, (_, i) => ({ + date: dayjs('2024-01-01').add(i, 'day').toISOString(), + sales: addNoise(i * 15 + 55, 10) + })), xAxisTimeInterval: 'day', columnLabelFormats: { ...AutoDateFormat_TimeIntervalTest_Days_WithForcedUnit.args!.columnLabelFormats, date: { columnType: 'date', style: 'date', - dateFormat: 'MMM DD' + dateFormat: 'MMM DD, YYYY' } satisfies IColumnLabelFormat } }