@nocoo/basalt 2.1.0 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/ai/COMPATIBILITY.md +1 -1
  2. package/ai/INTEGRATION.md +17 -1
  3. package/ai/USAGE.md +1 -1
  4. package/ai/registry.json +80 -80
  5. package/dist/assets/brand-mark.d.ts +1 -1
  6. package/dist/assets/brand-mark.js +1 -1
  7. package/dist/assets/brand-mark.js.map +1 -1
  8. package/dist/charts/charts.d.ts +1 -1
  9. package/dist/charts/charts.js +3 -16
  10. package/dist/charts/charts.js.map +1 -1
  11. package/dist/charts/custom-chart.d.ts +1 -1
  12. package/dist/charts/custom-chart.js +3 -16
  13. package/dist/charts/custom-chart.js.map +1 -1
  14. package/dist/charts/grouped-bar.d.ts +1 -1
  15. package/dist/charts/grouped-bar.js +11 -48
  16. package/dist/charts/grouped-bar.js.map +1 -1
  17. package/dist/charts/heatmap-calendar.js +80 -136
  18. package/dist/charts/heatmap-calendar.js.map +1 -1
  19. package/dist/charts/slot-bar.js +2 -12
  20. package/dist/charts/slot-bar.js.map +1 -1
  21. package/dist/charts/stat-card.js +2 -7
  22. package/dist/charts/stat-card.js.map +1 -1
  23. package/dist/charts/timeseries.d.ts +1 -1
  24. package/dist/charts/timeseries.js +3 -16
  25. package/dist/charts/timeseries.js.map +1 -1
  26. package/dist/charts/tooltip.js +13 -51
  27. package/dist/charts/tooltip.js.map +1 -1
  28. package/dist/components/app-shell.js +2 -2
  29. package/dist/components/app-shell.js.map +1 -1
  30. package/dist/components/autocomplete.d.ts +1 -1
  31. package/dist/components/autocomplete.js +3 -16
  32. package/dist/components/autocomplete.js.map +1 -1
  33. package/dist/components/badge.js +6 -6
  34. package/dist/components/badge.js.map +1 -1
  35. package/dist/components/banner.js +28 -28
  36. package/dist/components/banner.js.map +1 -1
  37. package/dist/components/button.js +6 -7
  38. package/dist/components/button.js.map +1 -1
  39. package/dist/components/chat-composer.js +1 -2
  40. package/dist/components/chat-composer.js.map +1 -1
  41. package/dist/components/checkbox.js +50 -82
  42. package/dist/components/checkbox.js.map +1 -1
  43. package/dist/components/code.js +1 -1
  44. package/dist/components/code.js.map +1 -1
  45. package/dist/components/combobox.d.ts +1 -1
  46. package/dist/components/combobox.js +3 -16
  47. package/dist/components/combobox.js.map +1 -1
  48. package/dist/components/context-menu.d.ts +1 -67
  49. package/dist/components/context-menu.js.map +1 -1
  50. package/dist/components/data-table.js +111 -123
  51. package/dist/components/data-table.js.map +1 -1
  52. package/dist/components/date-picker.js +236 -245
  53. package/dist/components/date-picker.js.map +1 -1
  54. package/dist/components/dock.js +15 -16
  55. package/dist/components/dock.js.map +1 -1
  56. package/dist/components/field.js +20 -23
  57. package/dist/components/field.js.map +1 -1
  58. package/dist/components/layer-card.d.ts +1 -1
  59. package/dist/components/layer-card.js +44 -51
  60. package/dist/components/layer-card.js.map +1 -1
  61. package/dist/components/sidebar.js +3 -3
  62. package/dist/components/sidebar.js.map +1 -1
  63. package/dist/components/skeleton-line.d.ts +2 -2
  64. package/dist/components/skeleton-line.js +1 -1
  65. package/dist/components/skeleton-line.js.map +1 -1
  66. package/dist/components/switch.js +46 -78
  67. package/dist/components/switch.js.map +1 -1
  68. package/dist/providers/accent.js +89 -198
  69. package/dist/providers/accent.js.map +1 -1
  70. package/dist/providers/theme.js +46 -144
  71. package/dist/providers/theme.js.map +1 -1
  72. package/dist/styles/standalone.css +12 -24
  73. package/dist/styles/tokens.css +6 -6
  74. package/dist/utils/preference-store.d.ts +16 -0
  75. package/dist/utils/preference-store.js +106 -0
  76. package/dist/utils/preference-store.js.map +1 -0
  77. package/dist/utils/use-checkable-group.d.ts +19 -0
  78. package/dist/utils/use-checkable-group.js +61 -0
  79. package/dist/utils/use-checkable-group.js.map +1 -0
  80. package/package.json +1 -2
@@ -1 +1 @@
1
- {"version":3,"file":"grouped-bar.js","names":[],"sources":["../../src/charts/grouped-bar.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { Bar, CartesianGrid, BarChart as RechartsBar, Tooltip, XAxis, YAxis } from \"recharts\";\nimport {\n\tANIMATION_PROPS,\n\tBAR_RADIUS,\n\tCHART_PLOT_MARGIN,\n\tCHART_PLOT_MARGIN_BARE,\n\tcartesianAxisProps,\n\tchartTooltipProps,\n\tGRID_PROPS,\n\tseriesColor,\n} from \"./config\";\nimport { ChartShell } from \"./frame\";\nimport { ChartLegend } from \"./legend\";\nimport type {\n\tLineChartAxisDomain,\n\tLineChartLegendRenderer,\n\tLineChartNumericKeys,\n\tLineChartTooltipRenderer,\n} from \"./line\";\nimport { type ChartSeriesDescriptor, resolveChartSeries, type XYPoint } from \"./series\";\n\nexport type GroupedBarChartProps<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n> = {\n\t/**\n\t * Dataset array where each record requires an `x` category or time coordinate.\n\t * Any remaining fields with numeric, nullable, or optional number values are inferred as valid series keys.\n\t */\n\tdata: TData[];\n\t/**\n\t * Series descriptors identifying which numeric keys to plot.\n\t * Inferred strictly from numeric keys of `TData` (rejects non-numeric fields, `x`, and typos).\n\t */\n\tseries?: Array<ChartSeriesDescriptor<NoInfer<K>>>;\n\tariaLabel?: string;\n\tclassName?: string;\n\tshowAxes?: boolean;\n\tshowLegend?: boolean;\n\t/**\n\t * Value formatter for Y-axis tick values and fallback tooltip values.\n\t */\n\tvalueFormatter?: (value: number) => string;\n\t/**\n\t * Formatter for X-axis category or time tick labels.\n\t */\n\txValueFormatter?: (value: string | number) => string;\n\t/**\n\t * Custom numerical or keyword bounds for the Y-axis.\n\t */\n\tyDomain?: LineChartAxisDomain;\n\t/**\n\t * Custom legend slot or render function receiving resolved series descriptors.\n\t */\n\tlegend?: ReactNode | LineChartLegendRenderer<K>;\n\t/**\n\t * Custom tooltip content renderer receiving Recharts payload, active status, and coordinate label.\n\t */\n\tcustomTooltip?: LineChartTooltipRenderer;\n\t/**\n\t * Textual summary describing key insights, highs, lows, and keyboard exploration instructions.\n\t * Associated with the chart via useId and aria-describedby.\n\t */\n\tsummary?: ReactNode;\n\t/**\n\t * Accessible tabular or structured data alternative rendered outside the plot area.\n\t */\n\tdataAlternative?: ReactNode;\n\t/**\n\t * Whether the interactive accessibility layer is enabled on the underlying Recharts graphic.\n\t * Enables keyboard exploration with Tab and arrow keys where supported by the underlying chart type.\n\t * Non-interactive compact or decorative charts without tooltip navigation should provide summary or dataAlternative.\n\t * @default true\n\t */\n\taccessibilityLayer?: boolean;\n};\n\nexport function GroupedBarChart<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n>({\n\tdata,\n\tseries,\n\tariaLabel = \"Grouped bar chart\",\n\tclassName,\n\tshowAxes = false,\n\tshowLegend = false,\n\tvalueFormatter,\n\txValueFormatter,\n\tyDomain,\n\tlegend,\n\tcustomTooltip,\n\tsummary,\n\tdataAlternative,\n\taccessibilityLayer,\n}: GroupedBarChartProps<TData, K>) {\n\tconst defaultKeys = [\"y\", \"y2\"] as K[];\n\tconst bars = resolveChartSeries(series, defaultKeys);\n\tconst resolvedLegend =\n\t\tlegend !== undefined ? (\n\t\t\ttypeof legend === \"function\" ? (\n\t\t\t\tlegend({ items: bars })\n\t\t\t) : (\n\t\t\t\tlegend\n\t\t\t)\n\t\t) : showLegend ? (\n\t\t\t<ChartLegend items={bars} shape=\"bar\" />\n\t\t) : undefined;\n\n\treturn (\n\t\t<ChartShell\n\t\t\tariaLabel={ariaLabel}\n\t\t\tclassName={className}\n\t\t\tlegend={resolvedLegend}\n\t\t\tsummary={summary}\n\t\t\tdataAlternative={dataAlternative}\n\t\t\taccessibilityLayer={accessibilityLayer}\n\t\t>\n\t\t\t<RechartsBar data={data} margin={showAxes ? CHART_PLOT_MARGIN : CHART_PLOT_MARGIN_BARE}>\n\t\t\t\t{showAxes ? <CartesianGrid {...GRID_PROPS} /> : null}\n\t\t\t\t<XAxis dataKey=\"x\" {...cartesianAxisProps(!showAxes)} tickFormatter={xValueFormatter} />\n\t\t\t\t<YAxis\n\t\t\t\t\t{...cartesianAxisProps(!showAxes)}\n\t\t\t\t\ttickFormatter={valueFormatter}\n\t\t\t\t\t{...(yDomain ? { domain: yDomain as [number, number] } : {})}\n\t\t\t\t/>\n\t\t\t\t<Tooltip\n\t\t\t\t\t{...chartTooltipProps({\n\t\t\t\t\t\tformatter: valueFormatter,\n\t\t\t\t\t\tcursor: \"bar\",\n\t\t\t\t\t\tcustomTooltip,\n\t\t\t\t\t})}\n\t\t\t\t/>\n\t\t\t\t{bars.map((item, index) => (\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tkey={item.key}\n\t\t\t\t\t\tdataKey={item.key}\n\t\t\t\t\t\tname={item.label ?? item.key}\n\t\t\t\t\t\tfill={seriesColor(item, index)}\n\t\t\t\t\t\tradius={BAR_RADIUS.vertical}\n\t\t\t\t\t\t{...ANIMATION_PROPS}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</RechartsBar>\n\t\t</ChartShell>\n\t);\n}\n"],"mappings":";;;;;;;;AA8EA,SAAgB,EAGd,EACD,SACA,WACA,eAAY,qBACZ,cACA,cAAW,IACX,gBAAa,IACb,mBACA,oBACA,YACA,WACA,kBACA,YACA,oBACA,yBACkC;CAElC,IAAM,IAAO,EAAmB,GAAQ,CADnB,KAAK,IACc,CAAW,GAC7C,IACL,MAAW,KAAA,IAMP,IACH,kBAAC,GAAD;EAAa,OAAO;EAAM,OAAM;CAAO,CAAA,IACpC,KAAA,IAPH,OAAO,KAAW,aACjB,EAAO,EAAE,OAAO,EAAK,CAAC,IAEtB;CAMH,OACC,kBAAC,GAAD;EACY;EACA;EACX,QAAQ;EACC;EACQ;EACG;EAEpB,UAAA,kBAAC,GAAD;GAAmB;GAAM,QAAQ,IAAW,IAAoB;GAAhE,UAAA;IACE,IAAW,kBAAC,GAAD,EAAe,GAAI,EAAa,CAAA,IAAI;IAChD,kBAAC,GAAD;KAAO,SAAQ;KAAI,GAAI,EAAmB,CAAC,CAAQ;KAAG,eAAe;IAAkB,CAAA;IACvF,kBAAC,GAAD;KACC,GAAI,EAAmB,CAAC,CAAQ;KAChC,eAAe;KACf,GAAK,IAAU,EAAE,QAAQ,EAA4B,IAAI,CAAC;IAC1D,CAAA;IACD,kBAAC,GAAD,EACC,GAAI,EAAkB;KACrB,WAAW;KACX,QAAQ;KACR;IACD,CAAC,EACD,CAAA;IACA,EAAK,KAAK,GAAM,MAChB,kBAAC,GAAD;KAEC,SAAS,EAAK;KACd,MAAM,EAAK,SAAS,EAAK;KACzB,MAAM,EAAY,GAAM,CAAK;KAC7B,QAAQ,EAAW;KACnB,GAAI;IACJ,GANK,EAAK,GAMV,CACD;GACW;;CACF,CAAA;AAEd"}
1
+ {"version":3,"file":"grouped-bar.js","names":[],"sources":["../../src/charts/grouped-bar.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { BarChart } from \"./bar\";\nimport type {\n\tLineChartAxisDomain,\n\tLineChartLegendRenderer,\n\tLineChartNumericKeys,\n\tLineChartTooltipRenderer,\n} from \"./line\";\nimport { type ChartSeriesDescriptor, resolveChartSeries, type XYPoint } from \"./series\";\n\nexport type GroupedBarChartProps<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n> = {\n\t/**\n\t * Dataset array where each record requires an `x` category or time coordinate.\n\t * Any remaining fields with numeric, nullable, or optional number values are inferred as valid series keys.\n\t */\n\tdata: TData[];\n\t/**\n\t * Series descriptors identifying which numeric keys to plot.\n\t * Inferred strictly from numeric keys of `TData` (rejects non-numeric fields, `x`, and typos).\n\t */\n\tseries?: Array<ChartSeriesDescriptor<NoInfer<K>>>;\n\tariaLabel?: string;\n\tclassName?: string;\n\tshowAxes?: boolean;\n\tshowLegend?: boolean;\n\t/**\n\t * Value formatter for Y-axis tick values and fallback tooltip values.\n\t */\n\tvalueFormatter?: (value: number) => string;\n\t/**\n\t * Formatter for X-axis category or time tick labels.\n\t */\n\txValueFormatter?: (value: string | number) => string;\n\t/**\n\t * Custom numerical or keyword bounds for the Y-axis.\n\t */\n\tyDomain?: LineChartAxisDomain;\n\t/**\n\t * Custom legend slot or render function receiving resolved series descriptors.\n\t */\n\tlegend?: ReactNode | LineChartLegendRenderer<K>;\n\t/**\n\t * Custom tooltip content renderer receiving Recharts payload, active status, and coordinate label.\n\t */\n\tcustomTooltip?: LineChartTooltipRenderer;\n\t/**\n\t * Textual summary describing key insights, highs, lows, and keyboard exploration instructions.\n\t * Associated with the chart via useId and aria-describedby.\n\t */\n\tsummary?: ReactNode;\n\t/**\n\t * Accessible tabular or structured data alternative rendered outside the plot area.\n\t */\n\tdataAlternative?: ReactNode;\n\t/**\n\t * Whether the interactive accessibility layer is enabled on the underlying Recharts graphic.\n\t * Enables keyboard exploration with Tab and arrow keys where supported by the underlying chart type.\n\t * Non-interactive compact or decorative charts without tooltip navigation should provide summary or dataAlternative.\n\t * @default true\n\t */\n\taccessibilityLayer?: boolean;\n};\n\nexport function GroupedBarChart<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n>({ series, ariaLabel = \"Grouped bar chart\", ...props }: GroupedBarChartProps<TData, K>) {\n\tconst resolvedSeries = resolveChartSeries(series, [\"y\", \"y2\"] as K[]);\n\treturn (\n\t\t<BarChart<TData, K>\n\t\t\tseries={resolvedSeries}\n\t\t\tariaLabel={ariaLabel}\n\t\t\t{...props}\n\t\t\tcolor={undefined}\n\t\t/>\n\t);\n}\n"],"mappings":";;;;;AAkEA,SAAgB,EAGd,EAAE,WAAQ,eAAY,qBAAqB,GAAG,KAAyC;CACxF,IAAM,IAAiB,EAAmB,GAAQ,CAAC,KAAK,IAAI,CAAQ;CACpE,OACC,kBAAC,GAAD;EACC,QAAQ;EACG;EACX,GAAI;EACJ,OAAO,KAAA;CACP,CAAA;AAEH"}
@@ -56,33 +56,9 @@ function _(e, t, n) {
56
56
  return Math.ceil(i * r);
57
57
  }
58
58
  function v(e) {
59
- if ("data" in e) {
60
- let { colorScale: t = f.green, valueFormatter: n = (e) => e.toLocaleString(), metricLabel: r = "Value", cellSize: i = 12, cellGap: a = 2, locale: o = "en-US", weekdayLabels: s, monthLabels: c, lessLabel: l = "Less", moreLabel: d = "More", ariaLabel: p = "Heatmap calendar", className: m } = e;
61
- return /* @__PURE__ */ u(b, {
62
- data: e.data,
63
- year: e.year,
64
- colorScale: t,
65
- valueFormatter: n,
66
- metricLabel: r,
67
- cellSize: i,
68
- cellGap: a,
69
- locale: o,
70
- weekdayLabels: s,
71
- monthLabels: c,
72
- lessLabel: l,
73
- moreLabel: d,
74
- ariaLabel: p,
75
- className: m
76
- });
77
- }
78
- let { ariaLabel: t = "Heatmap calendar", className: n } = e;
79
- return /* @__PURE__ */ u(y, {
80
- values: e.values,
81
- ariaLabel: t,
82
- className: n
83
- });
59
+ return "data" in e ? /* @__PURE__ */ u(b, { ...e }) : /* @__PURE__ */ u(y, { ...e });
84
60
  }
85
- function y({ values: o, ariaLabel: s, className: f }) {
61
+ function y({ values: o, ariaLabel: s = "Heatmap calendar", className: f }) {
86
62
  let [p, m] = l(0), [h, g] = l(null), _ = c(null), v = c([]), y = c(!1);
87
63
  a(() => {
88
64
  let e = y.current;
@@ -107,20 +83,7 @@ function y({ values: o, ariaLabel: s, className: f }) {
107
83
  else if (e.key === "End") n = o.length - 1;
108
84
  else return;
109
85
  e.preventDefault(), m(n), g(n), v.current[n]?.focus();
110
- };
111
- return o.length === 0 ? /* @__PURE__ */ u("div", {
112
- ref: _,
113
- tabIndex: -1,
114
- role: "region",
115
- "aria-label": s,
116
- onFocus: () => {
117
- y.current = !0;
118
- },
119
- onBlur: (e) => {
120
- e.currentTarget.contains(e.relatedTarget) || (y.current = !1);
121
- },
122
- className: e("grid grid-cols-7 gap-1 p-0.5 outline-none", f)
123
- }) : /* @__PURE__ */ u(r, { children: /* @__PURE__ */ u("div", {
86
+ }, x = /* @__PURE__ */ u("div", {
124
87
  ref: _,
125
88
  tabIndex: -1,
126
89
  role: "region",
@@ -166,35 +129,30 @@ function y({ values: o, ariaLabel: s, className: f }) {
166
129
  ]
167
130
  }) })]
168
131
  }, r))
169
- }) });
132
+ });
133
+ return o.length === 0 ? x : /* @__PURE__ */ u(r, { children: x });
170
134
  }
171
- function b({ data: f, year: v, colorScale: y, valueFormatter: b, metricLabel: x, cellSize: S, cellGap: C, locale: w, weekdayLabels: T, monthLabels: E, lessLabel: D, moreLabel: O, ariaLabel: k, className: A }) {
172
- let j = T ?? p(w), M = E ?? m(w), N = o(), P = c(null), { weeks: F, dataMap: I, maxValue: L, labels: R, validDays: z, dateToValidIndex: B } = s(() => {
173
- let e = h(v), t = /* @__PURE__ */ new Map(), n = 0;
174
- for (let e of f) t.set(e.date, e.value), e.value > n && (n = e.value);
135
+ function b({ data: v, year: y, colorScale: b = f.green, valueFormatter: x = (e) => e.toLocaleString(), metricLabel: S = "Value", cellSize: C = 12, cellGap: w = 2, locale: T = "en-US", weekdayLabels: E, monthLabels: D, lessLabel: O = "Less", moreLabel: k = "More", ariaLabel: A = "Heatmap calendar", className: j }) {
136
+ let M = E ?? p(T), N = D ?? m(T), P = o(), F = c(null), { weeks: I, dataMap: L, maxValue: R, labels: z, validDays: B, dateToValidIndex: V } = s(() => {
137
+ let e = h(y), t = /* @__PURE__ */ new Map(), n = 0;
138
+ for (let e of v) t.set(e.date, e.value), e.value > n && (n = e.value);
175
139
  let r = [], i = -1;
176
140
  e.forEach((e, t) => {
177
- let n = e.find((e) => e.getFullYear() === v);
141
+ let n = e.find((e) => e.getFullYear() === y);
178
142
  if (n) {
179
143
  let e = n.getMonth();
180
144
  e !== i && (r.push({
181
- month: M[e],
145
+ month: N[e],
182
146
  weekIndex: t
183
147
  }), i = e);
184
148
  }
185
149
  });
186
150
  let a = [], o = /* @__PURE__ */ new Map();
187
- return e.forEach((e, r) => {
188
- e.forEach((e, i) => {
189
- if (e.getFullYear() === v) {
190
- let s = g(e), c = t.get(s) ?? 0, l = _(c, n, y);
191
- o.set(s, a.length), a.push({
192
- dateStr: s,
193
- value: c,
194
- weekIndex: r,
195
- dayIndex: i,
196
- colorIndex: l
197
- });
151
+ return e.forEach((e) => {
152
+ e.forEach((e) => {
153
+ if (e.getFullYear() === y) {
154
+ let t = g(e);
155
+ o.set(t, a.length), a.push(t);
198
156
  }
199
157
  });
200
158
  }), {
@@ -206,80 +164,72 @@ function b({ data: f, year: v, colorScale: y, valueFormatter: b, metricLabel: x,
206
164
  dateToValidIndex: o
207
165
  };
208
166
  }, [
209
- f,
210
- M,
211
167
  v,
168
+ N,
212
169
  y
213
- ]), [V, H] = l(() => z[0]?.dateStr ?? `${v}-01-01`), [U, W] = l(null), G = c(/* @__PURE__ */ new Map()), K = c(!1);
170
+ ]), [H, U] = l(() => B[0] ?? `${y}-01-01`), [W, G] = l(null), K = c(/* @__PURE__ */ new Map()), q = c(!1);
214
171
  a(() => {
215
- let e = K.current;
216
- if (z.length === 0) H(""), W(null), e && P.current?.focus();
217
- else if (!B.has(V)) {
218
- let t = z[0]?.dateStr ?? "";
219
- H(t), e && G.current.get(t)?.focus();
172
+ let e = q.current;
173
+ if (B.length === 0) U(""), G(null), e && F.current?.focus();
174
+ else if (!V.has(H)) {
175
+ let t = B[0] ?? "";
176
+ U(t), e && K.current.get(t)?.focus();
220
177
  }
221
178
  }, [
222
- z,
223
179
  B,
224
- V
225
- ]);
226
- let q = s(() => {
227
- let e = B.get(V);
228
- return e === void 0 ? z[0] : z[e];
229
- }, [
230
180
  V,
231
- B,
232
- z
233
- ]), J = (e) => {
234
- e && (H(e.dateStr), W(e.dateStr), G.current.get(e.dateStr)?.focus());
235
- }, Y = (e, t) => {
236
- if (z.length === 0) return;
237
- let n = B.get(t.dateStr) ?? 0;
181
+ H
182
+ ]);
183
+ let J = V.has(H) ? H : B[0], Y = (e) => {
184
+ e && (U(e), G(e), K.current.get(e)?.focus());
185
+ }, X = (e, t) => {
186
+ if (B.length === 0) return;
187
+ let n = V.get(t) ?? 0;
238
188
  if (e.key === "Escape") {
239
- W(null);
189
+ G(null);
240
190
  return;
241
191
  }
242
192
  if (e.key === "ArrowDown") {
243
193
  e.preventDefault();
244
- let t = z[n + 1];
245
- t && J(t);
194
+ let t = B[n + 1];
195
+ t && Y(t);
246
196
  } else if (e.key === "ArrowUp") {
247
197
  e.preventDefault();
248
- let t = z[n - 1];
249
- t && J(t);
198
+ let t = B[n - 1];
199
+ t && Y(t);
250
200
  } else if (e.key === "ArrowRight") {
251
201
  e.preventDefault();
252
202
  let t = n + 7;
253
- t < z.length ? J(z[t]) : J(z[z.length - 1]);
203
+ t < B.length ? Y(B[t]) : Y(B[B.length - 1]);
254
204
  } else if (e.key === "ArrowLeft") {
255
205
  e.preventDefault();
256
206
  let t = n - 7;
257
- J(t >= 0 ? z[t] : z[0]);
258
- } else e.key === "Home" ? (e.preventDefault(), J(z[0])) : e.key === "End" && (e.preventDefault(), J(z[z.length - 1]));
207
+ Y(t >= 0 ? B[t] : B[0]);
208
+ } else e.key === "Home" ? (e.preventDefault(), Y(B[0])) : e.key === "End" && (e.preventDefault(), Y(B[B.length - 1]));
259
209
  };
260
210
  return /* @__PURE__ */ u("div", {
261
- ref: P,
211
+ ref: F,
262
212
  tabIndex: -1,
263
213
  role: "region",
264
- "aria-label": `${k} scrollable view`,
214
+ "aria-label": `${A} scrollable view`,
265
215
  onFocus: () => {
266
- K.current = !0;
216
+ q.current = !0;
267
217
  },
268
218
  onBlur: (e) => {
269
- e.currentTarget.contains(e.relatedTarget) || (K.current = !1);
219
+ e.currentTarget.contains(e.relatedTarget) || (q.current = !1);
270
220
  },
271
- className: e("overflow-x-auto rounded-md outline-none focus-visible:ring-1 focus-visible:ring-basalt-ring", A),
221
+ className: e("overflow-x-auto rounded-md outline-none focus-visible:ring-1 focus-visible:ring-basalt-ring", j),
272
222
  children: /* @__PURE__ */ u(r, { children: /* @__PURE__ */ d("div", {
273
223
  className: "inline-block p-1",
274
224
  role: "group",
275
- "aria-label": k,
225
+ "aria-label": A,
276
226
  children: [
277
227
  /* @__PURE__ */ u("div", {
278
228
  className: "relative mb-1 h-4 text-xs text-basalt-muted-foreground",
279
229
  style: { marginLeft: 30 },
280
- children: R.map((e) => /* @__PURE__ */ u("div", {
230
+ children: z.map((e) => /* @__PURE__ */ u("div", {
281
231
  className: "absolute",
282
- style: { left: e.weekIndex * (S + C) },
232
+ style: { left: e.weekIndex * (C + w) },
283
233
  children: e.month
284
234
  }, `${e.month}-${e.weekIndex}`))
285
235
  }),
@@ -289,83 +239,77 @@ function b({ data: f, year: v, colorScale: y, valueFormatter: b, metricLabel: x,
289
239
  className: "mr-1 flex flex-col text-xs text-basalt-muted-foreground select-none",
290
240
  style: { width: 30 },
291
241
  "aria-hidden": "true",
292
- children: j.map((e, t) => /* @__PURE__ */ u("div", {
242
+ children: M.map((e, t) => /* @__PURE__ */ u("div", {
293
243
  style: {
294
- height: S + C,
295
- lineHeight: `${S + C}px`,
244
+ height: C + w,
245
+ lineHeight: `${C + w}px`,
296
246
  visibility: t % 2 == 1 ? "visible" : "hidden"
297
247
  },
298
248
  children: e
299
249
  }, `${e}-${t}`))
300
250
  }), /* @__PURE__ */ u("div", {
301
251
  className: "flex",
302
- style: { gap: C },
303
- children: F.map((e, r) => /* @__PURE__ */ u("div", {
252
+ style: { gap: w },
253
+ children: I.map((e, r) => /* @__PURE__ */ u("div", {
304
254
  className: "flex flex-col",
305
- style: { gap: C },
306
- children: e.map((e, a) => {
307
- let o = g(e);
308
- if (e.getFullYear() !== v) return /* @__PURE__ */ u("div", {
255
+ style: { gap: w },
256
+ children: e.map((e, r) => {
257
+ let a = g(e);
258
+ if (e.getFullYear() !== y) return /* @__PURE__ */ u("div", {
309
259
  style: {
310
- width: S,
311
- height: S,
260
+ width: C,
261
+ height: C,
312
262
  visibility: "hidden"
313
263
  },
314
264
  "aria-hidden": "true"
315
- }, a);
316
- let s = I.get(o) ?? 0, c = _(s, L, y), l = q?.dateStr === o, f = b(s, o), p = `${o}, ${x}: ${f}`, m = U === o, h = {
317
- dateStr: o,
318
- value: s,
319
- weekIndex: r,
320
- dayIndex: a,
321
- colorIndex: c
322
- };
265
+ }, r);
266
+ let o = L.get(a) ?? 0, s = _(o, R, b), c = J === a, l = x(o, a), f = `${a}, ${S}: ${l}`;
323
267
  return /* @__PURE__ */ d(t, {
324
- open: m,
268
+ open: W === a,
325
269
  onOpenChange: (e) => {
326
- W(e ? o : (e) => e === o ? document.activeElement === G.current.get(o) ? e : null : e);
270
+ G(e ? a : (e) => e === a ? document.activeElement === K.current.get(a) ? e : null : e);
327
271
  },
328
272
  children: [/* @__PURE__ */ u(i, {
329
273
  asChild: !0,
330
274
  children: /* @__PURE__ */ u("button", {
331
275
  ref: (e) => {
332
- e ? G.current.set(o, e) : G.current.delete(o);
276
+ e ? K.current.set(a, e) : K.current.delete(a);
333
277
  },
334
278
  type: "button",
335
- "aria-label": p,
336
- tabIndex: l ? 0 : -1,
279
+ "aria-label": f,
280
+ tabIndex: c ? 0 : -1,
337
281
  onFocus: () => {
338
- H(o), W(o);
282
+ U(a), G(a);
339
283
  },
340
284
  onBlur: () => {
341
- W((e) => e === o ? null : e);
285
+ G((e) => e === a ? null : e);
342
286
  },
343
- onKeyDown: (e) => Y(e, h),
287
+ onKeyDown: (e) => X(e, a),
344
288
  className: "box-border m-0 cursor-pointer rounded-sm border-0 p-0 transition-colors hover:ring-1 hover:ring-basalt-foreground focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-basalt-ring",
345
289
  style: {
346
- width: S,
347
- height: S,
348
- backgroundColor: y[c]
290
+ width: C,
291
+ height: C,
292
+ backgroundColor: b[s]
349
293
  }
350
294
  })
351
295
  }), /* @__PURE__ */ u(n, {
352
- id: `${N}-${o}`,
296
+ id: `${P}-${a}`,
353
297
  children: /* @__PURE__ */ d("div", {
354
298
  className: "text-sm",
355
299
  children: [/* @__PURE__ */ u("div", {
356
300
  className: "font-medium",
357
- children: o
301
+ children: a
358
302
  }), /* @__PURE__ */ d("div", {
359
303
  className: "text-basalt-muted-foreground",
360
304
  children: [
361
- x,
305
+ S,
362
306
  ": ",
363
- f
307
+ l
364
308
  ]
365
309
  })]
366
310
  })
367
311
  })]
368
- }, a);
312
+ }, r);
369
313
  })
370
314
  }, r))
371
315
  })]
@@ -374,16 +318,16 @@ function b({ data: f, year: v, colorScale: y, valueFormatter: b, metricLabel: x,
374
318
  className: "mt-2 flex items-center justify-end gap-1 text-xs text-basalt-muted-foreground select-none",
375
319
  "aria-hidden": "true",
376
320
  children: [
377
- /* @__PURE__ */ u("span", { children: D }),
378
- y.map((e) => /* @__PURE__ */ u("div", {
321
+ /* @__PURE__ */ u("span", { children: O }),
322
+ b.map((e) => /* @__PURE__ */ u("div", {
379
323
  className: "rounded-sm",
380
324
  style: {
381
- width: S,
382
- height: S,
325
+ width: C,
326
+ height: C,
383
327
  backgroundColor: e
384
328
  }
385
329
  }, e)),
386
- /* @__PURE__ */ u("span", { children: O })
330
+ /* @__PURE__ */ u("span", { children: k })
387
331
  ]
388
332
  })
389
333
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"heatmap-calendar.js","names":[],"sources":["../../src/charts/heatmap-calendar.tsx"],"sourcesContent":["import { type KeyboardEvent, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../components/tooltip\";\nimport { cn } from \"../utils/cn\";\n\nexport type HeatmapDataPoint = { date: string; value: number };\n\nexport const heatmapColorScales = {\n\tgreen: [\n\t\t\"hsl(var(--basalt-muted))\",\n\t\t\"hsl(var(--basalt-heatmap-green-1))\",\n\t\t\"hsl(var(--basalt-heatmap-green-2))\",\n\t\t\"hsl(var(--basalt-heatmap-green-3))\",\n\t\t\"hsl(var(--basalt-heatmap-green-4))\",\n\t],\n\tred: [\n\t\t\"hsl(var(--basalt-muted))\",\n\t\t\"hsl(var(--basalt-heatmap-red-1))\",\n\t\t\"hsl(var(--basalt-heatmap-red-2))\",\n\t\t\"hsl(var(--basalt-heatmap-red-3))\",\n\t\t\"hsl(var(--basalt-heatmap-red-4))\",\n\t],\n\tblue: [\n\t\t\"hsl(var(--basalt-muted))\",\n\t\t\"hsl(var(--basalt-heatmap-blue-1))\",\n\t\t\"hsl(var(--basalt-heatmap-blue-2))\",\n\t\t\"hsl(var(--basalt-heatmap-blue-3))\",\n\t\t\"hsl(var(--basalt-heatmap-blue-4))\",\n\t],\n\torange: [\n\t\t\"hsl(var(--basalt-muted))\",\n\t\t\"hsl(var(--basalt-heatmap-orange-1))\",\n\t\t\"hsl(var(--basalt-heatmap-orange-2))\",\n\t\t\"hsl(var(--basalt-heatmap-orange-3))\",\n\t\t\"hsl(var(--basalt-heatmap-orange-4))\",\n\t],\n} as const;\n\nfunction weekdayNames(locale: string) {\n\treturn Array.from({ length: 7 }, (_, index) =>\n\t\tnew Intl.DateTimeFormat(locale, { weekday: \"short\" }).format(new Date(2023, 0, 1 + index)),\n\t);\n}\n\nfunction monthNames(locale: string) {\n\treturn Array.from({ length: 12 }, (_, index) =>\n\t\tnew Intl.DateTimeFormat(locale, { month: \"short\" }).format(new Date(2023, index, 1)),\n\t);\n}\n\nfunction getYearWeeks(year: number): Date[][] {\n\tconst weeks: Date[][] = [];\n\tconst startDate = new Date(year, 0, 1);\n\tconst endDate = new Date(year, 11, 31);\n\tconst firstDay = new Date(startDate);\n\tfirstDay.setDate(firstDay.getDate() - firstDay.getDay());\n\tlet currentDate = new Date(firstDay);\n\tlet currentWeek: Date[] = [];\n\twhile (currentDate <= endDate || currentWeek.length > 0) {\n\t\tif (currentWeek.length === 7) {\n\t\t\tweeks.push(currentWeek);\n\t\t\tcurrentWeek = [];\n\t\t}\n\t\tif (currentDate > endDate) {\n\t\t\tbreak;\n\t\t}\n\t\tcurrentWeek.push(new Date(currentDate));\n\t\tcurrentDate = new Date(currentDate);\n\t\tcurrentDate.setDate(currentDate.getDate() + 1);\n\t}\n\tif (currentWeek.length > 0) {\n\t\tweeks.push(currentWeek);\n\t}\n\treturn weeks;\n}\n\nfunction formatDate(date: Date): string {\n\tconst year = date.getFullYear();\n\tconst month = String(date.getMonth() + 1).padStart(2, \"0\");\n\tconst day = String(date.getDate()).padStart(2, \"0\");\n\treturn `${year}-${month}-${day}`;\n}\n\nfunction getColorIndex(value: number, maxValue: number, colorScale: readonly string[]): number {\n\tif (value === 0) {\n\t\treturn 0;\n\t}\n\tconst levels = colorScale.length - 1;\n\tconst normalized = Math.min(value / maxValue, 1);\n\treturn Math.ceil(normalized * levels);\n}\n\nexport type HeatmapCalendarValuesProps = {\n\t/**\n\t * Numeric values sequence to display in a compact 7-column grid.\n\t */\n\tvalues: number[];\n\t/**\n\t * Accessible name describing the heatmap purpose or data.\n\t * @default \"Heatmap calendar\"\n\t */\n\tariaLabel?: string;\n\t/**\n\t * Additional CSS class names for the outer container.\n\t */\n\tclassName?: string;\n};\n\nexport type HeatmapCalendarYearProps = {\n\t/**\n\t * Dataset of daily metrics with ISO date strings (\"YYYY-MM-DD\") and numeric values.\n\t */\n\tdata: HeatmapDataPoint[];\n\t/**\n\t * Calendar year to render.\n\t */\n\tyear: number;\n\t/**\n\t * Array of color values forming the intensity gradient from lowest to highest.\n\t * @default heatmapColorScales.green\n\t */\n\tcolorScale?: readonly string[];\n\t/**\n\t * Formatter for tooltip value display and accessible label.\n\t * @default (value) => value.toLocaleString()\n\t */\n\tvalueFormatter?: (value: number, date: string) => string;\n\t/**\n\t * Name of the metric for accessible labeling and tooltips.\n\t * @default \"Value\"\n\t */\n\tmetricLabel?: string;\n\t/**\n\t * Pixel width and height of each day cell.\n\t * @default 12\n\t */\n\tcellSize?: number;\n\t/**\n\t * Pixel gap between adjacent cells.\n\t * @default 2\n\t */\n\tcellGap?: number;\n\t/**\n\t * BCP 47 locale tag used for weekday and month label formatting.\n\t * @default \"en-US\"\n\t */\n\tlocale?: string;\n\t/**\n\t * Explicit custom weekday labels override.\n\t */\n\tweekdayLabels?: string[];\n\t/**\n\t * Explicit custom month labels override.\n\t */\n\tmonthLabels?: string[];\n\t/**\n\t * Label text for the lower end of the color scale legend.\n\t * @default \"Less\"\n\t */\n\tlessLabel?: string;\n\t/**\n\t * Label text for the upper end of the color scale legend.\n\t * @default \"More\"\n\t */\n\tmoreLabel?: string;\n\t/**\n\t * Accessible name describing the yearly heatmap dataset.\n\t * @default \"Heatmap calendar\"\n\t */\n\tariaLabel?: string;\n\t/**\n\t * Additional CSS class names for the outer scrollable container.\n\t */\n\tclassName?: string;\n};\n\nexport type HeatmapCalendarProps = HeatmapCalendarValuesProps | HeatmapCalendarYearProps;\n\nexport function HeatmapCalendar(props: HeatmapCalendarProps) {\n\tif (\"data\" in props) {\n\t\tconst {\n\t\t\tcolorScale = heatmapColorScales.green,\n\t\t\tvalueFormatter = (value) => value.toLocaleString(),\n\t\t\tmetricLabel = \"Value\",\n\t\t\tcellSize = 12,\n\t\t\tcellGap = 2,\n\t\t\tlocale = \"en-US\",\n\t\t\tweekdayLabels,\n\t\t\tmonthLabels,\n\t\t\tlessLabel = \"Less\",\n\t\t\tmoreLabel = \"More\",\n\t\t\tariaLabel = \"Heatmap calendar\",\n\t\t\tclassName,\n\t\t} = props;\n\t\treturn (\n\t\t\t<YearHeatmap\n\t\t\t\tdata={props.data}\n\t\t\t\tyear={props.year}\n\t\t\t\tcolorScale={colorScale}\n\t\t\t\tvalueFormatter={valueFormatter}\n\t\t\t\tmetricLabel={metricLabel}\n\t\t\t\tcellSize={cellSize}\n\t\t\t\tcellGap={cellGap}\n\t\t\t\tlocale={locale}\n\t\t\t\tweekdayLabels={weekdayLabels}\n\t\t\t\tmonthLabels={monthLabels}\n\t\t\t\tlessLabel={lessLabel}\n\t\t\t\tmoreLabel={moreLabel}\n\t\t\t\tariaLabel={ariaLabel}\n\t\t\t\tclassName={className}\n\t\t\t/>\n\t\t);\n\t}\n\tconst { ariaLabel = \"Heatmap calendar\", className } = props;\n\treturn <ValuesHeatmap values={props.values} ariaLabel={ariaLabel} className={className} />;\n}\n\nfunction ValuesHeatmap({\n\tvalues,\n\tariaLabel,\n\tclassName,\n}: {\n\tvalues: number[];\n\tariaLabel: string;\n\tclassName?: string;\n}) {\n\tconst [activeIdx, setActiveIdx] = useState(0);\n\tconst [openTooltipIdx, setOpenTooltipIdx] = useState<number | null>(null);\n\tconst containerRef = useRef<HTMLDivElement | null>(null);\n\tconst cellRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\tconst isFocusedInsideRef = useRef(false);\n\n\t// When values array shrinks, empties, or restores: if an item or container currently held focus,\n\t// restore focus to the new valid index or empty container without stealing focus from outside.\n\tuseEffect(() => {\n\t\tconst hadFocus = isFocusedInsideRef.current;\n\t\tif (values.length === 0) {\n\t\t\tsetActiveIdx(0);\n\t\t\tsetOpenTooltipIdx(null);\n\t\t\tif (hadFocus) {\n\t\t\t\tcontainerRef.current?.focus();\n\t\t\t}\n\t\t} else if (activeIdx >= values.length) {\n\t\t\tconst next = values.length - 1;\n\t\t\tsetActiveIdx(next);\n\t\t\tif (hadFocus) {\n\t\t\t\tcellRefs.current[next]?.focus();\n\t\t\t}\n\t\t} else if (hadFocus) {\n\t\t\tcellRefs.current[activeIdx]?.focus();\n\t\t}\n\t}, [values.length, activeIdx]);\n\n\tconst handleKeyDown = (e: KeyboardEvent<HTMLButtonElement>, currentIndex: number) => {\n\t\tif (values.length === 0) return;\n\n\t\tif (e.key === \"Escape\") {\n\t\t\tsetOpenTooltipIdx(null);\n\t\t\treturn;\n\t\t}\n\n\t\tlet nextIdx = currentIndex;\n\t\tif (e.key === \"ArrowRight\") {\n\t\t\tnextIdx = Math.min(values.length - 1, currentIndex + 1);\n\t\t} else if (e.key === \"ArrowLeft\") {\n\t\t\tnextIdx = Math.max(0, currentIndex - 1);\n\t\t} else if (e.key === \"ArrowDown\") {\n\t\t\tnextIdx = Math.min(values.length - 1, currentIndex + 7);\n\t\t} else if (e.key === \"ArrowUp\") {\n\t\t\tnextIdx = Math.max(0, currentIndex - 7);\n\t\t} else if (e.key === \"Home\") {\n\t\t\tnextIdx = 0;\n\t\t} else if (e.key === \"End\") {\n\t\t\tnextIdx = values.length - 1;\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\te.preventDefault();\n\t\tsetActiveIdx(nextIdx);\n\t\tsetOpenTooltipIdx(nextIdx);\n\t\tcellRefs.current[nextIdx]?.focus();\n\t};\n\n\tif (values.length === 0) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\ttabIndex={-1}\n\t\t\t\trole=\"region\"\n\t\t\t\taria-label={ariaLabel}\n\t\t\t\tonFocus={() => {\n\t\t\t\t\tisFocusedInsideRef.current = true;\n\t\t\t\t}}\n\t\t\t\tonBlur={(e) => {\n\t\t\t\t\tif (!e.currentTarget.contains(e.relatedTarget)) {\n\t\t\t\t\t\tisFocusedInsideRef.current = false;\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\tclassName={cn(\"grid grid-cols-7 gap-1 p-0.5 outline-none\", className)}\n\t\t\t/>\n\t\t);\n\t}\n\n\treturn (\n\t\t<TooltipProvider>\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\ttabIndex={-1}\n\t\t\t\trole=\"region\"\n\t\t\t\taria-label={ariaLabel}\n\t\t\t\tonFocus={() => {\n\t\t\t\t\tisFocusedInsideRef.current = true;\n\t\t\t\t}}\n\t\t\t\tonBlur={(e) => {\n\t\t\t\t\tif (!e.currentTarget.contains(e.relatedTarget)) {\n\t\t\t\t\t\tisFocusedInsideRef.current = false;\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\tclassName={cn(\"grid grid-cols-7 gap-1 p-0.5 outline-none\", className)}\n\t\t\t>\n\t\t\t\t{values.map((value, index) => {\n\t\t\t\t\tconst isCurrent = index === activeIdx;\n\t\t\t\t\tconst isOpen = openTooltipIdx === index;\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<Tooltip\n\t\t\t\t\t\t\tkey={index}\n\t\t\t\t\t\t\topen={isOpen}\n\t\t\t\t\t\t\tonOpenChange={(next) => {\n\t\t\t\t\t\t\t\tif (next) {\n\t\t\t\t\t\t\t\t\tsetOpenTooltipIdx(index);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tsetOpenTooltipIdx((curr) => {\n\t\t\t\t\t\t\t\t\t\tif (curr !== index) return curr;\n\t\t\t\t\t\t\t\t\t\t// If this cell currently holds document focus, ignore scroll dismiss\n\t\t\t\t\t\t\t\t\t\tconst isCellFocused =\n\t\t\t\t\t\t\t\t\t\t\tcellRefs.current[index] !== null &&\n\t\t\t\t\t\t\t\t\t\t\tdocument.activeElement === cellRefs.current[index];\n\t\t\t\t\t\t\t\t\t\treturn isCellFocused ? curr : null;\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tref={(el) => {\n\t\t\t\t\t\t\t\t\t\tcellRefs.current[index] = el;\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\ttabIndex={isCurrent ? 0 : -1}\n\t\t\t\t\t\t\t\t\tonFocus={() => {\n\t\t\t\t\t\t\t\t\t\tsetActiveIdx(index);\n\t\t\t\t\t\t\t\t\t\tsetOpenTooltipIdx(index);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonBlur={() => {\n\t\t\t\t\t\t\t\t\t\tsetOpenTooltipIdx((curr) => (curr === index ? null : curr));\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonKeyDown={(e) => handleKeyDown(e, index)}\n\t\t\t\t\t\t\t\t\taria-label={`Position ${index + 1}: ${value}`}\n\t\t\t\t\t\t\t\t\tclassName=\"box-border m-0 h-3 w-3 cursor-pointer rounded-sm border-0 p-0 transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-basalt-ring\"\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tbackgroundColor: `hsl(var(--basalt-chart-5) / ${0.2 + Math.min(4, Math.max(0, value)) * 0.15})`,\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t<TooltipContent>\n\t\t\t\t\t\t\t\t<div className=\"text-xs\">\n\t\t\t\t\t\t\t\t\tPosition {index + 1}: {value}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</TooltipContent>\n\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t</TooltipProvider>\n\t);\n}\n\ninterface YearDayCell {\n\tdateStr: string;\n\tvalue: number;\n\tweekIndex: number;\n\tdayIndex: number;\n\tcolorIndex: number;\n}\n\nfunction YearHeatmap({\n\tdata,\n\tyear,\n\tcolorScale,\n\tvalueFormatter,\n\tmetricLabel,\n\tcellSize,\n\tcellGap,\n\tlocale,\n\tweekdayLabels,\n\tmonthLabels,\n\tlessLabel,\n\tmoreLabel,\n\tariaLabel,\n\tclassName,\n}: {\n\tdata: HeatmapDataPoint[];\n\tyear: number;\n\tcolorScale: readonly string[];\n\tvalueFormatter: (value: number, date: string) => string;\n\tmetricLabel: string;\n\tcellSize: number;\n\tcellGap: number;\n\tlocale: string;\n\tweekdayLabels?: string[];\n\tmonthLabels?: string[];\n\tlessLabel: string;\n\tmoreLabel: string;\n\tariaLabel: string;\n\tclassName?: string;\n}) {\n\tconst weekdays = weekdayLabels ?? weekdayNames(locale);\n\tconst months = monthLabels ?? monthNames(locale);\n\tconst calendarId = useId();\n\tconst scrollRegionRef = useRef<HTMLDivElement | null>(null);\n\n\tconst { weeks, dataMap, maxValue, labels, validDays, dateToValidIndex } = useMemo(() => {\n\t\tconst weeks = getYearWeeks(year);\n\t\tconst dataMap = new Map<string, number>();\n\t\tlet maxValue = 0;\n\t\tfor (const point of data) {\n\t\t\tdataMap.set(point.date, point.value);\n\t\t\tif (point.value > maxValue) {\n\t\t\t\tmaxValue = point.value;\n\t\t\t}\n\t\t}\n\n\t\tconst labels: { month: string; weekIndex: number }[] = [];\n\t\tlet lastMonth = -1;\n\t\tweeks.forEach((week, weekIndex) => {\n\t\t\tconst firstDayOfWeek = week.find((date) => date.getFullYear() === year);\n\t\t\tif (firstDayOfWeek) {\n\t\t\t\tconst month = firstDayOfWeek.getMonth();\n\t\t\t\tif (month !== lastMonth) {\n\t\t\t\t\tlabels.push({ month: months[month], weekIndex });\n\t\t\t\t\tlastMonth = month;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tconst validDays: YearDayCell[] = [];\n\t\tconst dateToValidIndex = new Map<string, number>();\n\n\t\tweeks.forEach((week, weekIndex) => {\n\t\t\tweek.forEach((date, dayIndex) => {\n\t\t\t\tif (date.getFullYear() === year) {\n\t\t\t\t\tconst dateStr = formatDate(date);\n\t\t\t\t\tconst value = dataMap.get(dateStr) ?? 0;\n\t\t\t\t\tconst colorIndex = getColorIndex(value, maxValue, colorScale);\n\t\t\t\t\tdateToValidIndex.set(dateStr, validDays.length);\n\t\t\t\t\tvalidDays.push({\n\t\t\t\t\t\tdateStr,\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tweekIndex,\n\t\t\t\t\t\tdayIndex,\n\t\t\t\t\t\tcolorIndex,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\treturn { weeks, dataMap, maxValue, labels, validDays, dateToValidIndex };\n\t}, [data, months, year, colorScale]);\n\n\tconst [activeDate, setActiveDate] = useState<string>(() => {\n\t\treturn validDays[0]?.dateStr ?? `${year}-01-01`;\n\t});\n\tconst [openTooltipDate, setOpenTooltipDate] = useState<string | null>(null);\n\n\tconst cellRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\n\tconst isYearFocusedInsideRef = useRef(false);\n\n\t// If year or validDays change: if a cell held focus, restore focus to the clamped\n\t// active date or scroll container; if focus was outside, do NOT steal focus.\n\tuseEffect(() => {\n\t\tconst hadFocus = isYearFocusedInsideRef.current;\n\t\tif (validDays.length === 0) {\n\t\t\tsetActiveDate(\"\");\n\t\t\tsetOpenTooltipDate(null);\n\t\t\tif (hadFocus) {\n\t\t\t\tscrollRegionRef.current?.focus();\n\t\t\t}\n\t\t} else if (!dateToValidIndex.has(activeDate)) {\n\t\t\tconst nextDate = validDays[0]?.dateStr ?? \"\";\n\t\t\tsetActiveDate(nextDate);\n\t\t\tif (hadFocus) {\n\t\t\t\tcellRefs.current.get(nextDate)?.focus();\n\t\t\t}\n\t\t}\n\t}, [validDays, dateToValidIndex, activeDate]);\n\n\tconst activeDay = useMemo(() => {\n\t\tconst idx = dateToValidIndex.get(activeDate);\n\t\treturn idx !== undefined ? validDays[idx] : validDays[0];\n\t}, [activeDate, dateToValidIndex, validDays]);\n\n\tconst navigateToDay = (targetDay: YearDayCell | undefined) => {\n\t\tif (!targetDay) return;\n\t\tsetActiveDate(targetDay.dateStr);\n\t\tsetOpenTooltipDate(targetDay.dateStr);\n\t\tconst btn = cellRefs.current.get(targetDay.dateStr);\n\t\tbtn?.focus();\n\t};\n\n\tconst handleCellKeyDown = (e: KeyboardEvent<HTMLButtonElement>, cell: YearDayCell) => {\n\t\tif (validDays.length === 0) return;\n\n\t\tconst currentIdx = dateToValidIndex.get(cell.dateStr) ?? 0;\n\n\t\tif (e.key === \"Escape\") {\n\t\t\tsetOpenTooltipDate(null);\n\t\t\treturn;\n\t\t}\n\n\t\tif (e.key === \"ArrowDown\") {\n\t\t\te.preventDefault();\n\t\t\t// Visually next day (down in the column)\n\t\t\tconst nextDay = validDays[currentIdx + 1];\n\t\t\tif (nextDay) {\n\t\t\t\tnavigateToDay(nextDay);\n\t\t\t}\n\t\t} else if (e.key === \"ArrowUp\") {\n\t\t\te.preventDefault();\n\t\t\t// Visually previous day (up in the column)\n\t\t\tconst prevDay = validDays[currentIdx - 1];\n\t\t\tif (prevDay) {\n\t\t\t\tnavigateToDay(prevDay);\n\t\t\t}\n\t\t} else if (e.key === \"ArrowRight\") {\n\t\t\te.preventDefault();\n\t\t\t// Visually next week (right in the row: +7 days by index)\n\t\t\tconst targetIdx = currentIdx + 7;\n\t\t\tif (targetIdx < validDays.length) {\n\t\t\t\tnavigateToDay(validDays[targetIdx]);\n\t\t\t} else {\n\t\t\t\tnavigateToDay(validDays[validDays.length - 1]);\n\t\t\t}\n\t\t} else if (e.key === \"ArrowLeft\") {\n\t\t\te.preventDefault();\n\t\t\t// Visually previous week (left in the row: -7 days by index)\n\t\t\tconst targetIdx = currentIdx - 7;\n\t\t\tif (targetIdx >= 0) {\n\t\t\t\tnavigateToDay(validDays[targetIdx]);\n\t\t\t} else {\n\t\t\t\tnavigateToDay(validDays[0]);\n\t\t\t}\n\t\t} else if (e.key === \"Home\") {\n\t\t\te.preventDefault();\n\t\t\tnavigateToDay(validDays[0]);\n\t\t} else if (e.key === \"End\") {\n\t\t\te.preventDefault();\n\t\t\tnavigateToDay(validDays[validDays.length - 1]);\n\t\t}\n\t};\n\n\tconst labelWidth = 30;\n\n\treturn (\n\t\t<div\n\t\t\tref={scrollRegionRef}\n\t\t\ttabIndex={-1}\n\t\t\trole=\"region\"\n\t\t\taria-label={`${ariaLabel} scrollable view`}\n\t\t\tonFocus={() => {\n\t\t\t\tisYearFocusedInsideRef.current = true;\n\t\t\t}}\n\t\t\tonBlur={(e) => {\n\t\t\t\tif (!e.currentTarget.contains(e.relatedTarget)) {\n\t\t\t\t\tisYearFocusedInsideRef.current = false;\n\t\t\t\t}\n\t\t\t}}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-x-auto rounded-md outline-none focus-visible:ring-1 focus-visible:ring-basalt-ring\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<TooltipProvider>\n\t\t\t\t<div className=\"inline-block p-1\" role=\"group\" aria-label={ariaLabel}>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"relative mb-1 h-4 text-xs text-basalt-muted-foreground\"\n\t\t\t\t\t\tstyle={{ marginLeft: labelWidth }}\n\t\t\t\t\t>\n\t\t\t\t\t\t{labels.map((label) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={`${label.month}-${label.weekIndex}`}\n\t\t\t\t\t\t\t\tclassName=\"absolute\"\n\t\t\t\t\t\t\t\tstyle={{ left: label.weekIndex * (cellSize + cellGap) }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{label.month}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"flex\">\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"mr-1 flex flex-col text-xs text-basalt-muted-foreground select-none\"\n\t\t\t\t\t\t\tstyle={{ width: labelWidth }}\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{weekdays.map((day, index) => (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={`${day}-${index}`}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\theight: cellSize + cellGap,\n\t\t\t\t\t\t\t\t\t\tlineHeight: `${cellSize + cellGap}px`,\n\t\t\t\t\t\t\t\t\t\tvisibility: index % 2 === 1 ? \"visible\" : \"hidden\",\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{day}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex\" style={{ gap: cellGap }}>\n\t\t\t\t\t\t\t{weeks.map((week, weekIndex) => (\n\t\t\t\t\t\t\t\t<div key={weekIndex} className=\"flex flex-col\" style={{ gap: cellGap }}>\n\t\t\t\t\t\t\t\t\t{week.map((date, dayIndex) => {\n\t\t\t\t\t\t\t\t\t\tconst dateStr = formatDate(date);\n\t\t\t\t\t\t\t\t\t\tconst isCurrentYear = date.getFullYear() === year;\n\t\t\t\t\t\t\t\t\t\tif (!isCurrentYear) {\n\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={dayIndex}\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: cellSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvisibility: \"hidden\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tconst value = dataMap.get(dateStr) ?? 0;\n\t\t\t\t\t\t\t\t\t\tconst colorIndex = getColorIndex(value, maxValue, colorScale);\n\t\t\t\t\t\t\t\t\t\tconst isSelected = activeDay?.dateStr === dateStr;\n\t\t\t\t\t\t\t\t\t\tconst formattedValue = valueFormatter(value, dateStr);\n\t\t\t\t\t\t\t\t\t\tconst labelText = `${dateStr}, ${metricLabel}: ${formattedValue}`;\n\t\t\t\t\t\t\t\t\t\tconst isOpen = openTooltipDate === dateStr;\n\t\t\t\t\t\t\t\t\t\tconst currentCell: YearDayCell = {\n\t\t\t\t\t\t\t\t\t\t\tdateStr,\n\t\t\t\t\t\t\t\t\t\t\tvalue,\n\t\t\t\t\t\t\t\t\t\t\tweekIndex,\n\t\t\t\t\t\t\t\t\t\t\tdayIndex,\n\t\t\t\t\t\t\t\t\t\t\tcolorIndex,\n\t\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t<Tooltip\n\t\t\t\t\t\t\t\t\t\t\t\tkey={dayIndex}\n\t\t\t\t\t\t\t\t\t\t\t\topen={isOpen}\n\t\t\t\t\t\t\t\t\t\t\t\tonOpenChange={(next) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (next) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipDate(dateStr);\n\t\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipDate((curr) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (curr !== dateStr) return curr;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// If this cell currently holds document focus, ignore scroll dismiss\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst isCellFocused =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdocument.activeElement === cellRefs.current.get(dateStr);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn isCellFocused ? curr : null;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tref={(el) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (el) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcellRefs.current.set(dateStr, el);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcellRefs.current.delete(dateStr);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label={labelText}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttabIndex={isSelected ? 0 : -1}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonFocus={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetActiveDate(dateStr);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipDate(dateStr);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonBlur={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipDate((curr) => (curr === dateStr ? null : curr));\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonKeyDown={(e) => handleCellKeyDown(e, currentCell)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"box-border m-0 cursor-pointer rounded-sm border-0 p-0 transition-colors hover:ring-1 hover:ring-basalt-foreground focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-basalt-ring\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: cellSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbackgroundColor: colorScale[colorIndex],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t\t\t\t\t\t<TooltipContent id={`${calendarId}-${dateStr}`}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-sm\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"font-medium\">{dateStr}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{metricLabel}: {formattedValue}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</TooltipContent>\n\t\t\t\t\t\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"mt-2 flex items-center justify-end gap-1 text-xs text-basalt-muted-foreground select-none\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span>{lessLabel}</span>\n\t\t\t\t\t\t{colorScale.map((color) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={color}\n\t\t\t\t\t\t\t\tclassName=\"rounded-sm\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\twidth: cellSize,\n\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\tbackgroundColor: color,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t<span>{moreLabel}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</TooltipProvider>\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"heatmap-calendar.js","names":[],"sources":["../../src/charts/heatmap-calendar.tsx"],"sourcesContent":["import { type KeyboardEvent, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../components/tooltip\";\nimport { cn } from \"../utils/cn\";\n\nexport type HeatmapDataPoint = { date: string; value: number };\n\nexport const heatmapColorScales = {\n\tgreen: [\n\t\t\"hsl(var(--basalt-muted))\",\n\t\t\"hsl(var(--basalt-heatmap-green-1))\",\n\t\t\"hsl(var(--basalt-heatmap-green-2))\",\n\t\t\"hsl(var(--basalt-heatmap-green-3))\",\n\t\t\"hsl(var(--basalt-heatmap-green-4))\",\n\t],\n\tred: [\n\t\t\"hsl(var(--basalt-muted))\",\n\t\t\"hsl(var(--basalt-heatmap-red-1))\",\n\t\t\"hsl(var(--basalt-heatmap-red-2))\",\n\t\t\"hsl(var(--basalt-heatmap-red-3))\",\n\t\t\"hsl(var(--basalt-heatmap-red-4))\",\n\t],\n\tblue: [\n\t\t\"hsl(var(--basalt-muted))\",\n\t\t\"hsl(var(--basalt-heatmap-blue-1))\",\n\t\t\"hsl(var(--basalt-heatmap-blue-2))\",\n\t\t\"hsl(var(--basalt-heatmap-blue-3))\",\n\t\t\"hsl(var(--basalt-heatmap-blue-4))\",\n\t],\n\torange: [\n\t\t\"hsl(var(--basalt-muted))\",\n\t\t\"hsl(var(--basalt-heatmap-orange-1))\",\n\t\t\"hsl(var(--basalt-heatmap-orange-2))\",\n\t\t\"hsl(var(--basalt-heatmap-orange-3))\",\n\t\t\"hsl(var(--basalt-heatmap-orange-4))\",\n\t],\n} as const;\n\nfunction weekdayNames(locale: string) {\n\treturn Array.from({ length: 7 }, (_, index) =>\n\t\tnew Intl.DateTimeFormat(locale, { weekday: \"short\" }).format(new Date(2023, 0, 1 + index)),\n\t);\n}\n\nfunction monthNames(locale: string) {\n\treturn Array.from({ length: 12 }, (_, index) =>\n\t\tnew Intl.DateTimeFormat(locale, { month: \"short\" }).format(new Date(2023, index, 1)),\n\t);\n}\n\nfunction getYearWeeks(year: number): Date[][] {\n\tconst weeks: Date[][] = [];\n\tconst startDate = new Date(year, 0, 1);\n\tconst endDate = new Date(year, 11, 31);\n\tconst firstDay = new Date(startDate);\n\tfirstDay.setDate(firstDay.getDate() - firstDay.getDay());\n\tlet currentDate = new Date(firstDay);\n\tlet currentWeek: Date[] = [];\n\twhile (currentDate <= endDate || currentWeek.length > 0) {\n\t\tif (currentWeek.length === 7) {\n\t\t\tweeks.push(currentWeek);\n\t\t\tcurrentWeek = [];\n\t\t}\n\t\tif (currentDate > endDate) {\n\t\t\tbreak;\n\t\t}\n\t\tcurrentWeek.push(new Date(currentDate));\n\t\tcurrentDate = new Date(currentDate);\n\t\tcurrentDate.setDate(currentDate.getDate() + 1);\n\t}\n\tif (currentWeek.length > 0) {\n\t\tweeks.push(currentWeek);\n\t}\n\treturn weeks;\n}\n\nfunction formatDate(date: Date): string {\n\tconst year = date.getFullYear();\n\tconst month = String(date.getMonth() + 1).padStart(2, \"0\");\n\tconst day = String(date.getDate()).padStart(2, \"0\");\n\treturn `${year}-${month}-${day}`;\n}\n\nfunction getColorIndex(value: number, maxValue: number, colorScale: readonly string[]): number {\n\tif (value === 0) {\n\t\treturn 0;\n\t}\n\tconst levels = colorScale.length - 1;\n\tconst normalized = Math.min(value / maxValue, 1);\n\treturn Math.ceil(normalized * levels);\n}\n\nexport type HeatmapCalendarValuesProps = {\n\t/**\n\t * Numeric values sequence to display in a compact 7-column grid.\n\t */\n\tvalues: number[];\n\t/**\n\t * Accessible name describing the heatmap purpose or data.\n\t * @default \"Heatmap calendar\"\n\t */\n\tariaLabel?: string;\n\t/**\n\t * Additional CSS class names for the outer container.\n\t */\n\tclassName?: string;\n};\n\nexport type HeatmapCalendarYearProps = {\n\t/**\n\t * Dataset of daily metrics with ISO date strings (\"YYYY-MM-DD\") and numeric values.\n\t */\n\tdata: HeatmapDataPoint[];\n\t/**\n\t * Calendar year to render.\n\t */\n\tyear: number;\n\t/**\n\t * Array of color values forming the intensity gradient from lowest to highest.\n\t * @default heatmapColorScales.green\n\t */\n\tcolorScale?: readonly string[];\n\t/**\n\t * Formatter for tooltip value display and accessible label.\n\t * @default (value) => value.toLocaleString()\n\t */\n\tvalueFormatter?: (value: number, date: string) => string;\n\t/**\n\t * Name of the metric for accessible labeling and tooltips.\n\t * @default \"Value\"\n\t */\n\tmetricLabel?: string;\n\t/**\n\t * Pixel width and height of each day cell.\n\t * @default 12\n\t */\n\tcellSize?: number;\n\t/**\n\t * Pixel gap between adjacent cells.\n\t * @default 2\n\t */\n\tcellGap?: number;\n\t/**\n\t * BCP 47 locale tag used for weekday and month label formatting.\n\t * @default \"en-US\"\n\t */\n\tlocale?: string;\n\t/**\n\t * Explicit custom weekday labels override.\n\t */\n\tweekdayLabels?: string[];\n\t/**\n\t * Explicit custom month labels override.\n\t */\n\tmonthLabels?: string[];\n\t/**\n\t * Label text for the lower end of the color scale legend.\n\t * @default \"Less\"\n\t */\n\tlessLabel?: string;\n\t/**\n\t * Label text for the upper end of the color scale legend.\n\t * @default \"More\"\n\t */\n\tmoreLabel?: string;\n\t/**\n\t * Accessible name describing the yearly heatmap dataset.\n\t * @default \"Heatmap calendar\"\n\t */\n\tariaLabel?: string;\n\t/**\n\t * Additional CSS class names for the outer scrollable container.\n\t */\n\tclassName?: string;\n};\n\nexport type HeatmapCalendarProps = HeatmapCalendarValuesProps | HeatmapCalendarYearProps;\n\nexport function HeatmapCalendar(props: HeatmapCalendarProps) {\n\tif (\"data\" in props) {\n\t\treturn <YearHeatmap {...props} />;\n\t}\n\treturn <ValuesHeatmap {...props} />;\n}\n\nfunction ValuesHeatmap({\n\tvalues,\n\tariaLabel = \"Heatmap calendar\",\n\tclassName,\n}: HeatmapCalendarValuesProps) {\n\tconst [activeIdx, setActiveIdx] = useState(0);\n\tconst [openTooltipIdx, setOpenTooltipIdx] = useState<number | null>(null);\n\tconst containerRef = useRef<HTMLDivElement | null>(null);\n\tconst cellRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\tconst isFocusedInsideRef = useRef(false);\n\n\t// When values array shrinks, empties, or restores: if an item or container currently held focus,\n\t// restore focus to the new valid index or empty container without stealing focus from outside.\n\tuseEffect(() => {\n\t\tconst hadFocus = isFocusedInsideRef.current;\n\t\tif (values.length === 0) {\n\t\t\tsetActiveIdx(0);\n\t\t\tsetOpenTooltipIdx(null);\n\t\t\tif (hadFocus) {\n\t\t\t\tcontainerRef.current?.focus();\n\t\t\t}\n\t\t} else if (activeIdx >= values.length) {\n\t\t\tconst next = values.length - 1;\n\t\t\tsetActiveIdx(next);\n\t\t\tif (hadFocus) {\n\t\t\t\tcellRefs.current[next]?.focus();\n\t\t\t}\n\t\t} else if (hadFocus) {\n\t\t\tcellRefs.current[activeIdx]?.focus();\n\t\t}\n\t}, [values.length, activeIdx]);\n\n\tconst handleKeyDown = (e: KeyboardEvent<HTMLButtonElement>, currentIndex: number) => {\n\t\tif (values.length === 0) return;\n\n\t\tif (e.key === \"Escape\") {\n\t\t\tsetOpenTooltipIdx(null);\n\t\t\treturn;\n\t\t}\n\n\t\tlet nextIdx = currentIndex;\n\t\tif (e.key === \"ArrowRight\") {\n\t\t\tnextIdx = Math.min(values.length - 1, currentIndex + 1);\n\t\t} else if (e.key === \"ArrowLeft\") {\n\t\t\tnextIdx = Math.max(0, currentIndex - 1);\n\t\t} else if (e.key === \"ArrowDown\") {\n\t\t\tnextIdx = Math.min(values.length - 1, currentIndex + 7);\n\t\t} else if (e.key === \"ArrowUp\") {\n\t\t\tnextIdx = Math.max(0, currentIndex - 7);\n\t\t} else if (e.key === \"Home\") {\n\t\t\tnextIdx = 0;\n\t\t} else if (e.key === \"End\") {\n\t\t\tnextIdx = values.length - 1;\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\te.preventDefault();\n\t\tsetActiveIdx(nextIdx);\n\t\tsetOpenTooltipIdx(nextIdx);\n\t\tcellRefs.current[nextIdx]?.focus();\n\t};\n\n\tconst container = (\n\t\t<div\n\t\t\tref={containerRef}\n\t\t\ttabIndex={-1}\n\t\t\trole=\"region\"\n\t\t\taria-label={ariaLabel}\n\t\t\tonFocus={() => {\n\t\t\t\tisFocusedInsideRef.current = true;\n\t\t\t}}\n\t\t\tonBlur={(e) => {\n\t\t\t\tif (!e.currentTarget.contains(e.relatedTarget)) {\n\t\t\t\t\tisFocusedInsideRef.current = false;\n\t\t\t\t}\n\t\t\t}}\n\t\t\tclassName={cn(\"grid grid-cols-7 gap-1 p-0.5 outline-none\", className)}\n\t\t>\n\t\t\t{values.map((value, index) => {\n\t\t\t\tconst isCurrent = index === activeIdx;\n\t\t\t\tconst isOpen = openTooltipIdx === index;\n\t\t\t\treturn (\n\t\t\t\t\t<Tooltip\n\t\t\t\t\t\tkey={index}\n\t\t\t\t\t\topen={isOpen}\n\t\t\t\t\t\tonOpenChange={(next) => {\n\t\t\t\t\t\t\tif (next) {\n\t\t\t\t\t\t\t\tsetOpenTooltipIdx(index);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsetOpenTooltipIdx((curr) => {\n\t\t\t\t\t\t\t\t\tif (curr !== index) return curr;\n\t\t\t\t\t\t\t\t\t// If this cell currently holds document focus, ignore scroll dismiss\n\t\t\t\t\t\t\t\t\tconst isCellFocused =\n\t\t\t\t\t\t\t\t\t\tcellRefs.current[index] !== null &&\n\t\t\t\t\t\t\t\t\t\tdocument.activeElement === cellRefs.current[index];\n\t\t\t\t\t\t\t\t\treturn isCellFocused ? curr : null;\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tref={(el) => {\n\t\t\t\t\t\t\t\t\tcellRefs.current[index] = el;\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\ttabIndex={isCurrent ? 0 : -1}\n\t\t\t\t\t\t\t\tonFocus={() => {\n\t\t\t\t\t\t\t\t\tsetActiveIdx(index);\n\t\t\t\t\t\t\t\t\tsetOpenTooltipIdx(index);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tonBlur={() => {\n\t\t\t\t\t\t\t\t\tsetOpenTooltipIdx((curr) => (curr === index ? null : curr));\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tonKeyDown={(e) => handleKeyDown(e, index)}\n\t\t\t\t\t\t\t\taria-label={`Position ${index + 1}: ${value}`}\n\t\t\t\t\t\t\t\tclassName=\"box-border m-0 h-3 w-3 cursor-pointer rounded-sm border-0 p-0 transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-basalt-ring\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tbackgroundColor: `hsl(var(--basalt-chart-5) / ${0.2 + Math.min(4, Math.max(0, value)) * 0.15})`,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t<TooltipContent>\n\t\t\t\t\t\t\t<div className=\"text-xs\">\n\t\t\t\t\t\t\t\tPosition {index + 1}: {value}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</TooltipContent>\n\t\t\t\t\t</Tooltip>\n\t\t\t\t);\n\t\t\t})}\n\t\t</div>\n\t);\n\n\tif (values.length === 0) {\n\t\treturn container;\n\t}\n\n\treturn <TooltipProvider>{container}</TooltipProvider>;\n}\n\nfunction YearHeatmap({\n\tdata,\n\tyear,\n\tcolorScale = heatmapColorScales.green,\n\tvalueFormatter = (value) => value.toLocaleString(),\n\tmetricLabel = \"Value\",\n\tcellSize = 12,\n\tcellGap = 2,\n\tlocale = \"en-US\",\n\tweekdayLabels,\n\tmonthLabels,\n\tlessLabel = \"Less\",\n\tmoreLabel = \"More\",\n\tariaLabel = \"Heatmap calendar\",\n\tclassName,\n}: HeatmapCalendarYearProps) {\n\tconst weekdays = weekdayLabels ?? weekdayNames(locale);\n\tconst months = monthLabels ?? monthNames(locale);\n\tconst calendarId = useId();\n\tconst scrollRegionRef = useRef<HTMLDivElement | null>(null);\n\n\tconst { weeks, dataMap, maxValue, labels, validDays, dateToValidIndex } = useMemo(() => {\n\t\tconst weeks = getYearWeeks(year);\n\t\tconst dataMap = new Map<string, number>();\n\t\tlet maxValue = 0;\n\t\tfor (const point of data) {\n\t\t\tdataMap.set(point.date, point.value);\n\t\t\tif (point.value > maxValue) {\n\t\t\t\tmaxValue = point.value;\n\t\t\t}\n\t\t}\n\n\t\tconst labels: { month: string; weekIndex: number }[] = [];\n\t\tlet lastMonth = -1;\n\t\tweeks.forEach((week, weekIndex) => {\n\t\t\tconst firstDayOfWeek = week.find((date) => date.getFullYear() === year);\n\t\t\tif (firstDayOfWeek) {\n\t\t\t\tconst month = firstDayOfWeek.getMonth();\n\t\t\t\tif (month !== lastMonth) {\n\t\t\t\t\tlabels.push({ month: months[month], weekIndex });\n\t\t\t\t\tlastMonth = month;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tconst validDays: string[] = [];\n\t\tconst dateToValidIndex = new Map<string, number>();\n\n\t\tweeks.forEach((week) => {\n\t\t\tweek.forEach((date) => {\n\t\t\t\tif (date.getFullYear() === year) {\n\t\t\t\t\tconst dateStr = formatDate(date);\n\t\t\t\t\tdateToValidIndex.set(dateStr, validDays.length);\n\t\t\t\t\tvalidDays.push(dateStr);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\treturn { weeks, dataMap, maxValue, labels, validDays, dateToValidIndex };\n\t}, [data, months, year]);\n\n\tconst [activeDate, setActiveDate] = useState<string>(() => {\n\t\treturn validDays[0] ?? `${year}-01-01`;\n\t});\n\tconst [openTooltipDate, setOpenTooltipDate] = useState<string | null>(null);\n\n\tconst cellRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\n\tconst isYearFocusedInsideRef = useRef(false);\n\n\t// If year or validDays change: if a cell held focus, restore focus to the clamped\n\t// active date or scroll container; if focus was outside, do NOT steal focus.\n\tuseEffect(() => {\n\t\tconst hadFocus = isYearFocusedInsideRef.current;\n\t\tif (validDays.length === 0) {\n\t\t\tsetActiveDate(\"\");\n\t\t\tsetOpenTooltipDate(null);\n\t\t\tif (hadFocus) {\n\t\t\t\tscrollRegionRef.current?.focus();\n\t\t\t}\n\t\t} else if (!dateToValidIndex.has(activeDate)) {\n\t\t\tconst nextDate = validDays[0] ?? \"\";\n\t\t\tsetActiveDate(nextDate);\n\t\t\tif (hadFocus) {\n\t\t\t\tcellRefs.current.get(nextDate)?.focus();\n\t\t\t}\n\t\t}\n\t}, [validDays, dateToValidIndex, activeDate]);\n\n\tconst selectedDate = dateToValidIndex.has(activeDate) ? activeDate : validDays[0];\n\n\tconst navigateToDay = (targetDate: string | undefined) => {\n\t\tif (!targetDate) return;\n\t\tsetActiveDate(targetDate);\n\t\tsetOpenTooltipDate(targetDate);\n\t\tconst btn = cellRefs.current.get(targetDate);\n\t\tbtn?.focus();\n\t};\n\n\tconst handleCellKeyDown = (e: KeyboardEvent<HTMLButtonElement>, dateStr: string) => {\n\t\tif (validDays.length === 0) return;\n\n\t\tconst currentIdx = dateToValidIndex.get(dateStr) ?? 0;\n\n\t\tif (e.key === \"Escape\") {\n\t\t\tsetOpenTooltipDate(null);\n\t\t\treturn;\n\t\t}\n\n\t\tif (e.key === \"ArrowDown\") {\n\t\t\te.preventDefault();\n\t\t\t// Visually next day (down in the column)\n\t\t\tconst nextDay = validDays[currentIdx + 1];\n\t\t\tif (nextDay) {\n\t\t\t\tnavigateToDay(nextDay);\n\t\t\t}\n\t\t} else if (e.key === \"ArrowUp\") {\n\t\t\te.preventDefault();\n\t\t\t// Visually previous day (up in the column)\n\t\t\tconst prevDay = validDays[currentIdx - 1];\n\t\t\tif (prevDay) {\n\t\t\t\tnavigateToDay(prevDay);\n\t\t\t}\n\t\t} else if (e.key === \"ArrowRight\") {\n\t\t\te.preventDefault();\n\t\t\t// Visually next week (right in the row: +7 days by index)\n\t\t\tconst targetIdx = currentIdx + 7;\n\t\t\tif (targetIdx < validDays.length) {\n\t\t\t\tnavigateToDay(validDays[targetIdx]);\n\t\t\t} else {\n\t\t\t\tnavigateToDay(validDays[validDays.length - 1]);\n\t\t\t}\n\t\t} else if (e.key === \"ArrowLeft\") {\n\t\t\te.preventDefault();\n\t\t\t// Visually previous week (left in the row: -7 days by index)\n\t\t\tconst targetIdx = currentIdx - 7;\n\t\t\tif (targetIdx >= 0) {\n\t\t\t\tnavigateToDay(validDays[targetIdx]);\n\t\t\t} else {\n\t\t\t\tnavigateToDay(validDays[0]);\n\t\t\t}\n\t\t} else if (e.key === \"Home\") {\n\t\t\te.preventDefault();\n\t\t\tnavigateToDay(validDays[0]);\n\t\t} else if (e.key === \"End\") {\n\t\t\te.preventDefault();\n\t\t\tnavigateToDay(validDays[validDays.length - 1]);\n\t\t}\n\t};\n\n\tconst labelWidth = 30;\n\n\treturn (\n\t\t<div\n\t\t\tref={scrollRegionRef}\n\t\t\ttabIndex={-1}\n\t\t\trole=\"region\"\n\t\t\taria-label={`${ariaLabel} scrollable view`}\n\t\t\tonFocus={() => {\n\t\t\t\tisYearFocusedInsideRef.current = true;\n\t\t\t}}\n\t\t\tonBlur={(e) => {\n\t\t\t\tif (!e.currentTarget.contains(e.relatedTarget)) {\n\t\t\t\t\tisYearFocusedInsideRef.current = false;\n\t\t\t\t}\n\t\t\t}}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-x-auto rounded-md outline-none focus-visible:ring-1 focus-visible:ring-basalt-ring\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<TooltipProvider>\n\t\t\t\t<div className=\"inline-block p-1\" role=\"group\" aria-label={ariaLabel}>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"relative mb-1 h-4 text-xs text-basalt-muted-foreground\"\n\t\t\t\t\t\tstyle={{ marginLeft: labelWidth }}\n\t\t\t\t\t>\n\t\t\t\t\t\t{labels.map((label) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={`${label.month}-${label.weekIndex}`}\n\t\t\t\t\t\t\t\tclassName=\"absolute\"\n\t\t\t\t\t\t\t\tstyle={{ left: label.weekIndex * (cellSize + cellGap) }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{label.month}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"flex\">\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"mr-1 flex flex-col text-xs text-basalt-muted-foreground select-none\"\n\t\t\t\t\t\t\tstyle={{ width: labelWidth }}\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{weekdays.map((day, index) => (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={`${day}-${index}`}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\theight: cellSize + cellGap,\n\t\t\t\t\t\t\t\t\t\tlineHeight: `${cellSize + cellGap}px`,\n\t\t\t\t\t\t\t\t\t\tvisibility: index % 2 === 1 ? \"visible\" : \"hidden\",\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{day}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex\" style={{ gap: cellGap }}>\n\t\t\t\t\t\t\t{weeks.map((week, weekIndex) => (\n\t\t\t\t\t\t\t\t<div key={weekIndex} className=\"flex flex-col\" style={{ gap: cellGap }}>\n\t\t\t\t\t\t\t\t\t{week.map((date, dayIndex) => {\n\t\t\t\t\t\t\t\t\t\tconst dateStr = formatDate(date);\n\t\t\t\t\t\t\t\t\t\tconst isCurrentYear = date.getFullYear() === year;\n\t\t\t\t\t\t\t\t\t\tif (!isCurrentYear) {\n\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={dayIndex}\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: cellSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvisibility: \"hidden\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tconst value = dataMap.get(dateStr) ?? 0;\n\t\t\t\t\t\t\t\t\t\tconst colorIndex = getColorIndex(value, maxValue, colorScale);\n\t\t\t\t\t\t\t\t\t\tconst isSelected = selectedDate === dateStr;\n\t\t\t\t\t\t\t\t\t\tconst formattedValue = valueFormatter(value, dateStr);\n\t\t\t\t\t\t\t\t\t\tconst labelText = `${dateStr}, ${metricLabel}: ${formattedValue}`;\n\t\t\t\t\t\t\t\t\t\tconst isOpen = openTooltipDate === dateStr;\n\n\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t<Tooltip\n\t\t\t\t\t\t\t\t\t\t\t\tkey={dayIndex}\n\t\t\t\t\t\t\t\t\t\t\t\topen={isOpen}\n\t\t\t\t\t\t\t\t\t\t\t\tonOpenChange={(next) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (next) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipDate(dateStr);\n\t\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipDate((curr) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (curr !== dateStr) return curr;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// If this cell currently holds document focus, ignore scroll dismiss\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst isCellFocused =\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdocument.activeElement === cellRefs.current.get(dateStr);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn isCellFocused ? curr : null;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tref={(el) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (el) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcellRefs.current.set(dateStr, el);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcellRefs.current.delete(dateStr);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taria-label={labelText}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttabIndex={isSelected ? 0 : -1}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonFocus={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetActiveDate(dateStr);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipDate(dateStr);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonBlur={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetOpenTooltipDate((curr) => (curr === dateStr ? null : curr));\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonKeyDown={(e) => handleCellKeyDown(e, dateStr)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"box-border m-0 cursor-pointer rounded-sm border-0 p-0 transition-colors hover:ring-1 hover:ring-basalt-foreground focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-basalt-ring\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: cellSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbackgroundColor: colorScale[colorIndex],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t\t\t\t\t\t<TooltipContent id={`${calendarId}-${dateStr}`}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-sm\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"font-medium\">{dateStr}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{metricLabel}: {formattedValue}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</TooltipContent>\n\t\t\t\t\t\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"mt-2 flex items-center justify-end gap-1 text-xs text-basalt-muted-foreground select-none\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span>{lessLabel}</span>\n\t\t\t\t\t\t{colorScale.map((color) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={color}\n\t\t\t\t\t\t\t\tclassName=\"rounded-sm\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\twidth: cellSize,\n\t\t\t\t\t\t\t\t\theight: cellSize,\n\t\t\t\t\t\t\t\t\tbackgroundColor: color,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t<span>{moreLabel}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</TooltipProvider>\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
@@ -8,17 +8,7 @@ import { jsx as d, jsxs as f } from "react/jsx-runtime";
8
8
  import { Bar as p, BarChart as m, XAxis as h, YAxis as g } from "recharts";
9
9
  //#region src/charts/slot-bar.tsx
10
10
  function _(e) {
11
- if ("items" in e) {
12
- let { ariaLabel: t = "Slot bar chart", heightClass: n = "h-6", gapClass: r = "gap-px", emptyClass: i = "bg-basalt-muted", className: a } = e;
13
- return /* @__PURE__ */ d(v, {
14
- items: e.items,
15
- ariaLabel: t,
16
- heightClass: n,
17
- gapClass: r,
18
- emptyClass: i,
19
- className: a
20
- });
21
- }
11
+ if ("items" in e) return /* @__PURE__ */ d(v, { ...e });
22
12
  let { series: t, ariaLabel: n = "Slot bar chart", className: r, summary: i, dataAlternative: _, accessibilityLayer: y } = e, b = l(t, ["y"]);
23
13
  return /* @__PURE__ */ d(u, {
24
14
  ariaLabel: n,
@@ -45,7 +35,7 @@ function _(e) {
45
35
  })
46
36
  });
47
37
  }
48
- function v({ items: a, ariaLabel: o, heightClass: s, gapClass: c, emptyClass: l, className: u }) {
38
+ function v({ items: a, ariaLabel: o = "Slot bar chart", heightClass: s = "h-6", gapClass: c = "gap-px", emptyClass: l = "bg-basalt-muted", className: u }) {
49
39
  if (a.length === 0) return null;
50
40
  let p = a.some((e) => e.label);
51
41
  function m(t) {
@@ -1 +1 @@
1
- {"version":3,"file":"slot-bar.js","names":[],"sources":["../../src/charts/slot-bar.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { Bar, BarChart as RechartsBar, XAxis, YAxis } from \"recharts\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../components/tooltip\";\nimport { cn } from \"../utils/cn\";\nimport { ANIMATION_PROPS, BAR_RADIUS, cartesianAxisProps, seriesColor } from \"./config\";\nimport { ChartFrame } from \"./frame\";\nimport type { LineChartNumericKeys } from \"./line\";\nimport { type ChartSeriesDescriptor, resolveChartSeries, type XYPoint } from \"./series\";\n\nexport type SlotBarItem = {\n\tcolor: string;\n\theight?: number;\n\tlabel?: string;\n};\n\nexport type SlotBarItemsProps = {\n\titems: SlotBarItem[];\n\tariaLabel?: string;\n\theightClass?: string;\n\tgapClass?: string;\n\temptyClass?: string;\n\tclassName?: string;\n};\n\nexport type SlotBarDataProps<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n> = {\n\t/**\n\t * Dataset array where each record requires an `x` category or time coordinate.\n\t * Any remaining fields with numeric, nullable, or optional number values are inferred as valid series keys.\n\t */\n\tdata: TData[];\n\t/**\n\t * Series descriptors identifying which numeric keys to plot.\n\t * Inferred strictly from numeric keys of `TData` (rejects non-numeric fields, `x`, and typos).\n\t */\n\tseries?: Array<ChartSeriesDescriptor<NoInfer<K>>>;\n\tariaLabel?: string;\n\tclassName?: string;\n\t/**\n\t * Textual summary describing key insights, highs, lows, and keyboard exploration instructions.\n\t * Associated with the chart via useId and aria-describedby.\n\t */\n\tsummary?: ReactNode;\n\t/**\n\t * Accessible tabular or structured data alternative rendered outside the plot area.\n\t */\n\tdataAlternative?: ReactNode;\n\t/**\n\t * Whether the interactive accessibility layer is enabled on the underlying Recharts graphic.\n\t * Enables keyboard exploration with Tab and arrow keys where supported by the underlying chart type.\n\t * Non-interactive compact or decorative charts without tooltip navigation should provide summary or dataAlternative.\n\t * @default true\n\t */\n\taccessibilityLayer?: boolean;\n};\n\nexport type SlotBarChartProps<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n> = SlotBarItemsProps | SlotBarDataProps<TData, K>;\n\nexport function SlotBarChart<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n>(props: SlotBarChartProps<TData, K>) {\n\tif (\"items\" in props) {\n\t\tconst {\n\t\t\tariaLabel = \"Slot bar chart\",\n\t\t\theightClass = \"h-6\",\n\t\t\tgapClass = \"gap-px\",\n\t\t\temptyClass = \"bg-basalt-muted\",\n\t\t\tclassName,\n\t\t} = props;\n\t\treturn (\n\t\t\t<SlotItemBars\n\t\t\t\titems={props.items}\n\t\t\t\tariaLabel={ariaLabel}\n\t\t\t\theightClass={heightClass}\n\t\t\t\tgapClass={gapClass}\n\t\t\t\temptyClass={emptyClass}\n\t\t\t\tclassName={className}\n\t\t\t/>\n\t\t);\n\t}\n\tconst {\n\t\tseries,\n\t\tariaLabel = \"Slot bar chart\",\n\t\tclassName,\n\t\tsummary,\n\t\tdataAlternative,\n\t\taccessibilityLayer,\n\t} = props;\n\tconst bars = resolveChartSeries(series, [\"y\" as K]);\n\treturn (\n\t\t<ChartFrame\n\t\t\tariaLabel={ariaLabel}\n\t\t\tclassName={className}\n\t\t\tsummary={summary}\n\t\t\tdataAlternative={dataAlternative}\n\t\t\taccessibilityLayer={accessibilityLayer}\n\t\t>\n\t\t\t<RechartsBar data={props.data}>\n\t\t\t\t<XAxis dataKey=\"x\" {...cartesianAxisProps(true)} />\n\t\t\t\t<YAxis {...cartesianAxisProps(true)} />\n\t\t\t\t{bars.map((item, index) => (\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tkey={item.key}\n\t\t\t\t\t\tdataKey={item.key}\n\t\t\t\t\t\tname={item.label ?? item.key}\n\t\t\t\t\t\tfill={seriesColor(item, index)}\n\t\t\t\t\t\tradius={BAR_RADIUS.vertical}\n\t\t\t\t\t\t{...ANIMATION_PROPS}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</RechartsBar>\n\t\t</ChartFrame>\n\t);\n}\n\nfunction SlotItemBars({\n\titems,\n\tariaLabel,\n\theightClass,\n\tgapClass,\n\temptyClass,\n\tclassName,\n}: {\n\titems: SlotBarItem[];\n\tariaLabel: string;\n\theightClass: string;\n\tgapClass: string;\n\temptyClass: string;\n\tclassName?: string;\n}) {\n\tif (items.length === 0) {\n\t\treturn null;\n\t}\n\tconst hasTooltips = items.some((item) => item.label);\n\tfunction renderBar(item: SlotBarItem) {\n\t\tconst heightRatio = item.height ?? 1;\n\t\tconst isEmpty = heightRatio <= 0;\n\t\tconst isTailwindColor = item.color.startsWith(\"bg-\");\n\t\tconst heightPercent = isEmpty ? 100 : Math.max(heightRatio * 100, 10);\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"w-full rounded-sm\",\n\t\t\t\t\tisEmpty ? emptyClass : isTailwindColor ? item.color : undefined,\n\t\t\t\t)}\n\t\t\t\tstyle={{\n\t\t\t\t\theight: `${heightPercent}%`,\n\t\t\t\t\t...(isEmpty || isTailwindColor ? {} : { backgroundColor: item.color }),\n\t\t\t\t}}\n\t\t\t\tdata-testid=\"slot-bar\"\n\t\t\t/>\n\t\t);\n\t}\n\tconst content = items.map((item, index) => {\n\t\tconst bar =\n\t\t\thasTooltips && item.label ? (\n\t\t\t\t<Tooltip>\n\t\t\t\t\t<TooltipTrigger asChild>{renderBar(item)}</TooltipTrigger>\n\t\t\t\t\t<TooltipContent side=\"top\">\n\t\t\t\t\t\t<p>{item.label}</p>\n\t\t\t\t\t</TooltipContent>\n\t\t\t\t</Tooltip>\n\t\t\t) : (\n\t\t\t\trenderBar(item)\n\t\t\t);\n\t\treturn (\n\t\t\t<div\n\t\t\t\tkey={`${item.label ?? item.color}-${index}`}\n\t\t\t\tclassName=\"flex min-h-0 min-w-0 flex-1 items-end self-stretch\"\n\t\t\t>\n\t\t\t\t{bar}\n\t\t\t</div>\n\t\t);\n\t});\n\tconst container = (\n\t\t<div\n\t\t\tclassName={cn(\"flex w-full items-end\", heightClass, gapClass, className)}\n\t\t\trole=\"img\"\n\t\t\taria-label={ariaLabel}\n\t\t>\n\t\t\t{content}\n\t\t</div>\n\t);\n\tif (hasTooltips) {\n\t\treturn <TooltipProvider delayDuration={0}>{container}</TooltipProvider>;\n\t}\n\treturn container;\n}\n"],"mappings":";;;;;;;;;AA+DA,SAAgB,EAGd,GAAoC;CACrC,IAAI,WAAW,GAAO;EACrB,IAAM,EACL,eAAY,kBACZ,iBAAc,OACd,cAAW,UACX,gBAAa,mBACb,iBACG;EACJ,OACC,kBAAC,GAAD;GACC,OAAO,EAAM;GACF;GACE;GACH;GACE;GACD;EACX,CAAA;CAEH;CACA,IAAM,EACL,WACA,eAAY,kBACZ,cACA,YACA,oBACA,0BACG,GACE,IAAO,EAAmB,GAAQ,CAAC,GAAQ,CAAC;CAClD,OACC,kBAAC,GAAD;EACY;EACA;EACF;EACQ;EACG;EAEpB,UAAA,kBAAC,GAAD;GAAa,MAAM,EAAM;GAAzB,UAAA;IACC,kBAAC,GAAD;KAAO,SAAQ;KAAI,GAAI,EAAmB,EAAI;IAAI,CAAA;IAClD,kBAAC,GAAD,EAAO,GAAI,EAAmB,EAAI,EAAI,CAAA;IACrC,EAAK,KAAK,GAAM,MAChB,kBAAC,GAAD;KAEC,SAAS,EAAK;KACd,MAAM,EAAK,SAAS,EAAK;KACzB,MAAM,EAAY,GAAM,CAAK;KAC7B,QAAQ,EAAW;KACnB,GAAI;IACJ,GANK,EAAK,GAMV,CACD;GACW;;CACF,CAAA;AAEd;AAEA,SAAS,EAAa,EACrB,UACA,cACA,gBACA,aACA,eACA,gBAQE;CACF,IAAI,EAAM,WAAW,GACpB,OAAO;CAER,IAAM,IAAc,EAAM,MAAM,MAAS,EAAK,KAAK;CACnD,SAAS,EAAU,GAAmB;EACrC,IAAM,IAAc,EAAK,UAAU,GAC7B,IAAU,KAAe,GACzB,IAAkB,EAAK,MAAM,WAAW,KAAK,GAC7C,IAAgB,IAAU,MAAM,KAAK,IAAI,IAAc,KAAK,EAAE;EACpE,OACC,kBAAC,OAAD;GACC,WAAW,EACV,qBACA,IAAU,IAAa,IAAkB,EAAK,QAAQ,KAAA,CACvD;GACA,OAAO;IACN,QAAQ,GAAG,EAAc;IACzB,GAAI,KAAW,IAAkB,CAAC,IAAI,EAAE,iBAAiB,EAAK,MAAM;GACrE;GACA,eAAY;EACZ,CAAA;CAEH;CACA,IAAM,IAAU,EAAM,KAAK,GAAM,MAAU;EAC1C,IAAM,IACL,KAAe,EAAK,QACnB,kBAAC,GAAD,EAAA,UAAA,CACC,kBAAC,GAAD;GAAgB,SAAA;GAAS,UAAA,EAAU,CAAI;EAAkB,CAAA,GACzD,kBAAC,GAAD;GAAgB,MAAK;GACpB,UAAA,kBAAC,KAAD,EAAA,UAAI,EAAK,MAAS,CAAA;EACH,CAAA,CACR,EAAA,CAAA,IAET,EAAU,CAAI;EAEhB,OACC,kBAAC,OAAD;GAEC,WAAU;GAET,UAAA;EACG,GAJC,GAAG,EAAK,SAAS,EAAK,MAAM,GAAG,GAIhC;CAEP,CAAC,GACK,IACL,kBAAC,OAAD;EACC,WAAW,EAAG,yBAAyB,GAAa,GAAU,CAAS;EACvE,MAAK;EACL,cAAY;EAEX,UAAA;CACG,CAAA;CAKN,OAHI,IACI,kBAAC,GAAD;EAAiB,eAAe;EAAI,UAAA;CAA2B,CAAA,IAEhE;AACR"}
1
+ {"version":3,"file":"slot-bar.js","names":[],"sources":["../../src/charts/slot-bar.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { Bar, BarChart as RechartsBar, XAxis, YAxis } from \"recharts\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../components/tooltip\";\nimport { cn } from \"../utils/cn\";\nimport { ANIMATION_PROPS, BAR_RADIUS, cartesianAxisProps, seriesColor } from \"./config\";\nimport { ChartFrame } from \"./frame\";\nimport type { LineChartNumericKeys } from \"./line\";\nimport { type ChartSeriesDescriptor, resolveChartSeries, type XYPoint } from \"./series\";\n\nexport type SlotBarItem = {\n\tcolor: string;\n\theight?: number;\n\tlabel?: string;\n};\n\nexport type SlotBarItemsProps = {\n\titems: SlotBarItem[];\n\tariaLabel?: string;\n\theightClass?: string;\n\tgapClass?: string;\n\temptyClass?: string;\n\tclassName?: string;\n};\n\nexport type SlotBarDataProps<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n> = {\n\t/**\n\t * Dataset array where each record requires an `x` category or time coordinate.\n\t * Any remaining fields with numeric, nullable, or optional number values are inferred as valid series keys.\n\t */\n\tdata: TData[];\n\t/**\n\t * Series descriptors identifying which numeric keys to plot.\n\t * Inferred strictly from numeric keys of `TData` (rejects non-numeric fields, `x`, and typos).\n\t */\n\tseries?: Array<ChartSeriesDescriptor<NoInfer<K>>>;\n\tariaLabel?: string;\n\tclassName?: string;\n\t/**\n\t * Textual summary describing key insights, highs, lows, and keyboard exploration instructions.\n\t * Associated with the chart via useId and aria-describedby.\n\t */\n\tsummary?: ReactNode;\n\t/**\n\t * Accessible tabular or structured data alternative rendered outside the plot area.\n\t */\n\tdataAlternative?: ReactNode;\n\t/**\n\t * Whether the interactive accessibility layer is enabled on the underlying Recharts graphic.\n\t * Enables keyboard exploration with Tab and arrow keys where supported by the underlying chart type.\n\t * Non-interactive compact or decorative charts without tooltip navigation should provide summary or dataAlternative.\n\t * @default true\n\t */\n\taccessibilityLayer?: boolean;\n};\n\nexport type SlotBarChartProps<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n> = SlotBarItemsProps | SlotBarDataProps<TData, K>;\n\nexport function SlotBarChart<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n>(props: SlotBarChartProps<TData, K>) {\n\tif (\"items\" in props) {\n\t\treturn <SlotItemBars {...props} />;\n\t}\n\tconst {\n\t\tseries,\n\t\tariaLabel = \"Slot bar chart\",\n\t\tclassName,\n\t\tsummary,\n\t\tdataAlternative,\n\t\taccessibilityLayer,\n\t} = props;\n\tconst bars = resolveChartSeries(series, [\"y\" as K]);\n\treturn (\n\t\t<ChartFrame\n\t\t\tariaLabel={ariaLabel}\n\t\t\tclassName={className}\n\t\t\tsummary={summary}\n\t\t\tdataAlternative={dataAlternative}\n\t\t\taccessibilityLayer={accessibilityLayer}\n\t\t>\n\t\t\t<RechartsBar data={props.data}>\n\t\t\t\t<XAxis dataKey=\"x\" {...cartesianAxisProps(true)} />\n\t\t\t\t<YAxis {...cartesianAxisProps(true)} />\n\t\t\t\t{bars.map((item, index) => (\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tkey={item.key}\n\t\t\t\t\t\tdataKey={item.key}\n\t\t\t\t\t\tname={item.label ?? item.key}\n\t\t\t\t\t\tfill={seriesColor(item, index)}\n\t\t\t\t\t\tradius={BAR_RADIUS.vertical}\n\t\t\t\t\t\t{...ANIMATION_PROPS}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</RechartsBar>\n\t\t</ChartFrame>\n\t);\n}\n\nfunction SlotItemBars({\n\titems,\n\tariaLabel = \"Slot bar chart\",\n\theightClass = \"h-6\",\n\tgapClass = \"gap-px\",\n\temptyClass = \"bg-basalt-muted\",\n\tclassName,\n}: SlotBarItemsProps) {\n\tif (items.length === 0) {\n\t\treturn null;\n\t}\n\tconst hasTooltips = items.some((item) => item.label);\n\tfunction renderBar(item: SlotBarItem) {\n\t\tconst heightRatio = item.height ?? 1;\n\t\tconst isEmpty = heightRatio <= 0;\n\t\tconst isTailwindColor = item.color.startsWith(\"bg-\");\n\t\tconst heightPercent = isEmpty ? 100 : Math.max(heightRatio * 100, 10);\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"w-full rounded-sm\",\n\t\t\t\t\tisEmpty ? emptyClass : isTailwindColor ? item.color : undefined,\n\t\t\t\t)}\n\t\t\t\tstyle={{\n\t\t\t\t\theight: `${heightPercent}%`,\n\t\t\t\t\t...(isEmpty || isTailwindColor ? {} : { backgroundColor: item.color }),\n\t\t\t\t}}\n\t\t\t\tdata-testid=\"slot-bar\"\n\t\t\t/>\n\t\t);\n\t}\n\tconst content = items.map((item, index) => {\n\t\tconst bar =\n\t\t\thasTooltips && item.label ? (\n\t\t\t\t<Tooltip>\n\t\t\t\t\t<TooltipTrigger asChild>{renderBar(item)}</TooltipTrigger>\n\t\t\t\t\t<TooltipContent side=\"top\">\n\t\t\t\t\t\t<p>{item.label}</p>\n\t\t\t\t\t</TooltipContent>\n\t\t\t\t</Tooltip>\n\t\t\t) : (\n\t\t\t\trenderBar(item)\n\t\t\t);\n\t\treturn (\n\t\t\t<div\n\t\t\t\tkey={`${item.label ?? item.color}-${index}`}\n\t\t\t\tclassName=\"flex min-h-0 min-w-0 flex-1 items-end self-stretch\"\n\t\t\t>\n\t\t\t\t{bar}\n\t\t\t</div>\n\t\t);\n\t});\n\tconst container = (\n\t\t<div\n\t\t\tclassName={cn(\"flex w-full items-end\", heightClass, gapClass, className)}\n\t\t\trole=\"img\"\n\t\t\taria-label={ariaLabel}\n\t\t>\n\t\t\t{content}\n\t\t</div>\n\t);\n\tif (hasTooltips) {\n\t\treturn <TooltipProvider delayDuration={0}>{container}</TooltipProvider>;\n\t}\n\treturn container;\n}\n"],"mappings":";;;;;;;;;AA+DA,SAAgB,EAGd,GAAoC;CACrC,IAAI,WAAW,GACd,OAAO,kBAAC,GAAD,EAAc,GAAI,EAAQ,CAAA;CAElC,IAAM,EACL,WACA,eAAY,kBACZ,cACA,YACA,oBACA,0BACG,GACE,IAAO,EAAmB,GAAQ,CAAC,GAAQ,CAAC;CAClD,OACC,kBAAC,GAAD;EACY;EACA;EACF;EACQ;EACG;EAEpB,UAAA,kBAAC,GAAD;GAAa,MAAM,EAAM;GAAzB,UAAA;IACC,kBAAC,GAAD;KAAO,SAAQ;KAAI,GAAI,EAAmB,EAAI;IAAI,CAAA;IAClD,kBAAC,GAAD,EAAO,GAAI,EAAmB,EAAI,EAAI,CAAA;IACrC,EAAK,KAAK,GAAM,MAChB,kBAAC,GAAD;KAEC,SAAS,EAAK;KACd,MAAM,EAAK,SAAS,EAAK;KACzB,MAAM,EAAY,GAAM,CAAK;KAC7B,QAAQ,EAAW;KACnB,GAAI;IACJ,GANK,EAAK,GAMV,CACD;GACW;;CACF,CAAA;AAEd;AAEA,SAAS,EAAa,EACrB,UACA,eAAY,kBACZ,iBAAc,OACd,cAAW,UACX,gBAAa,mBACb,gBACqB;CACrB,IAAI,EAAM,WAAW,GACpB,OAAO;CAER,IAAM,IAAc,EAAM,MAAM,MAAS,EAAK,KAAK;CACnD,SAAS,EAAU,GAAmB;EACrC,IAAM,IAAc,EAAK,UAAU,GAC7B,IAAU,KAAe,GACzB,IAAkB,EAAK,MAAM,WAAW,KAAK,GAC7C,IAAgB,IAAU,MAAM,KAAK,IAAI,IAAc,KAAK,EAAE;EACpE,OACC,kBAAC,OAAD;GACC,WAAW,EACV,qBACA,IAAU,IAAa,IAAkB,EAAK,QAAQ,KAAA,CACvD;GACA,OAAO;IACN,QAAQ,GAAG,EAAc;IACzB,GAAI,KAAW,IAAkB,CAAC,IAAI,EAAE,iBAAiB,EAAK,MAAM;GACrE;GACA,eAAY;EACZ,CAAA;CAEH;CACA,IAAM,IAAU,EAAM,KAAK,GAAM,MAAU;EAC1C,IAAM,IACL,KAAe,EAAK,QACnB,kBAAC,GAAD,EAAA,UAAA,CACC,kBAAC,GAAD;GAAgB,SAAA;GAAS,UAAA,EAAU,CAAI;EAAkB,CAAA,GACzD,kBAAC,GAAD;GAAgB,MAAK;GACpB,UAAA,kBAAC,KAAD,EAAA,UAAI,EAAK,MAAS,CAAA;EACH,CAAA,CACR,EAAA,CAAA,IAET,EAAU,CAAI;EAEhB,OACC,kBAAC,OAAD;GAEC,WAAU;GAET,UAAA;EACG,GAJC,GAAG,EAAK,SAAS,EAAK,MAAM,GAAG,GAIhC;CAEP,CAAC,GACK,IACL,kBAAC,OAAD;EACC,WAAW,EAAG,yBAAyB,GAAa,GAAU,CAAS;EACvE,MAAK;EACL,cAAY;EAEX,UAAA;CACG,CAAA;CAKN,OAHI,IACI,kBAAC,GAAD;EAAiB,eAAe;EAAI,UAAA;CAA2B,CAAA,IAEhE;AACR"}
@@ -65,14 +65,9 @@ function a({ label: a, title: o, value: s, subtitle: c, icon: l, iconColor: u =
65
65
  }) : null]
66
66
  }) : null
67
67
  ] });
68
- return C ? /* @__PURE__ */ n("div", {
69
- className: e("rounded-basalt-md border border-basalt-border bg-basalt-secondary p-4", _),
70
- role: "group",
71
- "aria-label": O,
72
- children: k
73
- }) : /* @__PURE__ */ n("div", {
68
+ return /* @__PURE__ */ n("div", {
74
69
  className: e("rounded-basalt-md border border-basalt-border bg-basalt-secondary p-4", _),
75
- role: "img",
70
+ role: C ? "group" : "img",
76
71
  "aria-label": O,
77
72
  children: k
78
73
  });