@nocoo/basalt 2.1.2 → 2.1.8
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.
- package/ai/INTEGRATION.md +19 -1
- package/ai/USAGE.md +1 -1
- package/ai/registry.json +81 -81
- package/dist/charts/charts.d.ts +1 -1
- package/dist/charts/charts.js +3 -16
- package/dist/charts/charts.js.map +1 -1
- package/dist/charts/custom-chart.d.ts +1 -1
- package/dist/charts/custom-chart.js +3 -16
- package/dist/charts/custom-chart.js.map +1 -1
- package/dist/charts/grouped-bar.d.ts +1 -1
- package/dist/charts/grouped-bar.js +11 -48
- package/dist/charts/grouped-bar.js.map +1 -1
- package/dist/charts/heatmap-calendar.js +80 -136
- package/dist/charts/heatmap-calendar.js.map +1 -1
- package/dist/charts/slot-bar.js +2 -12
- package/dist/charts/slot-bar.js.map +1 -1
- package/dist/charts/stat-card.js +2 -7
- package/dist/charts/stat-card.js.map +1 -1
- package/dist/charts/timeseries.d.ts +1 -1
- package/dist/charts/timeseries.js +3 -16
- package/dist/charts/timeseries.js.map +1 -1
- package/dist/charts/tooltip.js +13 -51
- package/dist/charts/tooltip.js.map +1 -1
- package/dist/components/autocomplete.d.ts +1 -1
- package/dist/components/autocomplete.js +3 -16
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/banner.js +28 -28
- package/dist/components/banner.js.map +1 -1
- package/dist/components/button.js +8 -9
- package/dist/components/button.js.map +1 -1
- package/dist/components/chat-composer.js +1 -2
- package/dist/components/chat-composer.js.map +1 -1
- package/dist/components/checkbox.js +50 -82
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/code.js +1 -1
- package/dist/components/code.js.map +1 -1
- package/dist/components/combobox.d.ts +1 -1
- package/dist/components/combobox.js +3 -16
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-dialog.js +1 -1
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/context-menu.d.ts +1 -67
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/data-table.js +112 -124
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.js +236 -245
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/delete-resource.js +1 -1
- package/dist/components/delete-resource.js.map +1 -1
- package/dist/components/dock.js +15 -16
- package/dist/components/dock.js.map +1 -1
- package/dist/components/field.js +20 -23
- package/dist/components/field.js.map +1 -1
- package/dist/components/layer-card.d.ts +1 -1
- package/dist/components/layer-card.js +44 -51
- package/dist/components/layer-card.js.map +1 -1
- package/dist/components/skeleton-line.d.ts +2 -2
- package/dist/components/skeleton-line.js +1 -1
- package/dist/components/skeleton-line.js.map +1 -1
- package/dist/components/switch.js +46 -78
- package/dist/components/switch.js.map +1 -1
- package/dist/components/toggle-group.js +2 -2
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle.js +1 -1
- package/dist/components/toggle.js.map +1 -1
- package/dist/components/tooltip.js +14 -13
- package/dist/components/tooltip.js.map +1 -1
- package/dist/providers/accent.js +89 -198
- package/dist/providers/accent.js.map +1 -1
- package/dist/providers/theme.js +46 -144
- package/dist/providers/theme.js.map +1 -1
- package/dist/styles/standalone.css +37 -13
- package/dist/utils/preference-store.d.ts +16 -0
- package/dist/utils/preference-store.js +106 -0
- package/dist/utils/preference-store.js.map +1 -0
- package/dist/utils/selection-indicator.js +1 -1
- package/dist/utils/selection-indicator.js.map +1 -1
- package/dist/utils/use-checkable-group.d.ts +19 -0
- package/dist/utils/use-checkable-group.js +61 -0
- package/dist/utils/use-checkable-group.js.map +1 -0
- package/package.json +2 -3
|
@@ -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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"}
|
package/dist/charts/slot-bar.js
CHANGED
|
@@ -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\
|
|
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"}
|
package/dist/charts/stat-card.js
CHANGED
|
@@ -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
|
|
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
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stat-card.js","names":[],"sources":["../../src/charts/stat-card.tsx"],"sourcesContent":["import type { LucideIcon } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport type StatCardProps = {\n\t/**\n\t * Secondary heading label for the metric card (overridden by title).\n\t */\n\tlabel?: string;\n\t/**\n\t * Primary heading title for the metric card (takes precedence over label).\n\t */\n\ttitle?: string;\n\t/**\n\t * Metric value, formatted via toLocaleString() if a number is provided.\n\t * Overridden by the status slot if provided.\n\t */\n\tvalue: string | number;\n\t/**\n\t * Supporting descriptive subtitle text rendered below value.\n\t */\n\tsubtitle?: string;\n\t/**\n\t * Optional decorative Lucide icon component.\n\t */\n\ticon?: LucideIcon;\n\t/**\n\t * Color class applied to the icon wrapper.\n\t * @default \"text-basalt-muted-foreground\"\n\t */\n\ticonColor?: string;\n\t/**\n\t * Structured percentage trend value and optional label.\n\t * Overridden by the trendContent slot if provided.\n\t */\n\ttrend?: { value: number; label?: string };\n\t/**\n\t * Optional header action or interactive element (e.g. tooltip trigger or button).\n\t * Renders alongside the title/icon. Switches card role to group for accessible descendants.\n\t */\n\taction?: ReactNode;\n\t/**\n\t * Optional custom status replacement slot (e.g. loading skeleton, error state, or empty state).\n\t * Takes precedence over the default value display and excludes value from accessible name.\n\t * Subtitle and trend remain caller-managed; when error/empty state is shown, callers should omit trend.\n\t */\n\tstatus?: ReactNode;\n\t/**\n\t * Optional custom trend replacement slot (e.g. custom badge, sparkline, or localized trend indicator).\n\t * Takes precedence over the default trend calculation and excludes default trend from accessible name.\n\t */\n\ttrendContent?: ReactNode;\n\t/**\n\t * Optional custom content rendered below the metric value and above or alongside the trend.\n\t */\n\tchildren?: ReactNode;\n\t/**\n\t * Explicit accessible label overriding automatic accessible name calculation.\n\t */\n\tariaLabel?: string;\n\t/**\n\t * Additional CSS class names for styling the card root.\n\t */\n\tclassName?: string;\n};\n\nfunction hasSlotContent(slot: ReactNode | undefined): boolean {\n\treturn slot !== undefined && slot !== null && slot !== false;\n}\n\nexport function StatCard({\n\tlabel,\n\ttitle,\n\tvalue,\n\tsubtitle,\n\ticon: Icon,\n\ticonColor = \"text-basalt-muted-foreground\",\n\ttrend,\n\taction,\n\tstatus,\n\ttrendContent,\n\tchildren,\n\tariaLabel,\n\tclassName,\n}: StatCardProps) {\n\tconst heading = title ?? label;\n\tconst hasStatus = hasSlotContent(status);\n\tconst hasTrendContent = hasSlotContent(trendContent);\n\tconst hasAction = hasSlotContent(action);\n\tconst hasChildren = hasSlotContent(children);\n\tconst isInteractive = hasAction || hasStatus || hasTrendContent || hasChildren;\n\tconst hasHeaderEnd = hasAction || Boolean(Icon);\n\n\tconst display = typeof value === \"number\" ? value.toLocaleString() : value;\n\tconst isPositiveTrend = trend && trend.value > 0;\n\tconst isNegativeTrend = trend && trend.value < 0;\n\n\t// Accessible name: If custom status or custom trendContent is provided, do not report\n\t// the overridden hidden default value or default trend to assistive technology.\n\tconst named =\n\t\tariaLabel ??\n\t\t[\n\t\t\theading,\n\t\t\t!hasStatus ? display : null,\n\t\t\tsubtitle,\n\t\t\t!hasTrendContent && trend\n\t\t\t\t? `${isPositiveTrend ? \"+\" : \"\"}${trend.value}% ${trend.label ?? \"\"}`\n\t\t\t\t: null,\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(\" \")\n\t\t\t.trim();\n\n\tconst cardContent = (\n\t\t<>\n\t\t\t<div className=\"flex items-start justify-between gap-3\">\n\t\t\t\t<div className=\"min-w-0 flex-1 space-y-1\">\n\t\t\t\t\t<p className=\"text-xs text-basalt-muted-foreground md:text-sm\">{heading}</p>\n\t\t\t\t\t{hasStatus ? (\n\t\t\t\t\t\t<div className=\"pt-0.5\">{status}</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p className=\"font-display text-xl font-semibold tracking-tight text-basalt-foreground md:text-2xl\">\n\t\t\t\t\t\t\t{display}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{subtitle ? <p className=\"text-xs text-basalt-muted-foreground\">{subtitle}</p> : null}\n\t\t\t\t</div>\n\t\t\t\t{hasHeaderEnd ? (\n\t\t\t\t\t<div className=\"flex items-center gap-2 shrink-0\">\n\t\t\t\t\t\t{hasAction ? <div className=\"flex items-center\">{action}</div> : null}\n\t\t\t\t\t\t{Icon ? (\n\t\t\t\t\t\t\t<div className={cn(\"rounded-md bg-basalt-card p-2\", iconColor)}>\n\t\t\t\t\t\t\t\t<Icon className=\"h-5 w-5\" strokeWidth={1.5} />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</div>\n\t\t\t\t) : null}\n\t\t\t</div>\n\n\t\t\t{hasChildren ? <div className=\"mt-2\">{children}</div> : null}\n\n\t\t\t{hasTrendContent ? (\n\t\t\t\t<div className=\"mt-3 flex items-center gap-1 text-xs\">{trendContent}</div>\n\t\t\t) : trend ? (\n\t\t\t\t<div className=\"mt-3 flex items-center gap-1 text-xs\">\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"font-medium\",\n\t\t\t\t\t\t\tisPositiveTrend && \"text-basalt-heatmap-green-4\",\n\t\t\t\t\t\t\tisNegativeTrend && \"text-basalt-destructive\",\n\t\t\t\t\t\t\t!isPositiveTrend && !isNegativeTrend && \"text-basalt-muted-foreground\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{`${isPositiveTrend ? \"+\" : \"\"}${trend.value}%`}\n\t\t\t\t\t</span>\n\t\t\t\t\t{trend.label ? <span className=\"text-basalt-muted-foreground\">{trend.label}</span> : null}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t</>\n\t);\n\n\
|
|
1
|
+
{"version":3,"file":"stat-card.js","names":[],"sources":["../../src/charts/stat-card.tsx"],"sourcesContent":["import type { LucideIcon } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport type StatCardProps = {\n\t/**\n\t * Secondary heading label for the metric card (overridden by title).\n\t */\n\tlabel?: string;\n\t/**\n\t * Primary heading title for the metric card (takes precedence over label).\n\t */\n\ttitle?: string;\n\t/**\n\t * Metric value, formatted via toLocaleString() if a number is provided.\n\t * Overridden by the status slot if provided.\n\t */\n\tvalue: string | number;\n\t/**\n\t * Supporting descriptive subtitle text rendered below value.\n\t */\n\tsubtitle?: string;\n\t/**\n\t * Optional decorative Lucide icon component.\n\t */\n\ticon?: LucideIcon;\n\t/**\n\t * Color class applied to the icon wrapper.\n\t * @default \"text-basalt-muted-foreground\"\n\t */\n\ticonColor?: string;\n\t/**\n\t * Structured percentage trend value and optional label.\n\t * Overridden by the trendContent slot if provided.\n\t */\n\ttrend?: { value: number; label?: string };\n\t/**\n\t * Optional header action or interactive element (e.g. tooltip trigger or button).\n\t * Renders alongside the title/icon. Switches card role to group for accessible descendants.\n\t */\n\taction?: ReactNode;\n\t/**\n\t * Optional custom status replacement slot (e.g. loading skeleton, error state, or empty state).\n\t * Takes precedence over the default value display and excludes value from accessible name.\n\t * Subtitle and trend remain caller-managed; when error/empty state is shown, callers should omit trend.\n\t */\n\tstatus?: ReactNode;\n\t/**\n\t * Optional custom trend replacement slot (e.g. custom badge, sparkline, or localized trend indicator).\n\t * Takes precedence over the default trend calculation and excludes default trend from accessible name.\n\t */\n\ttrendContent?: ReactNode;\n\t/**\n\t * Optional custom content rendered below the metric value and above or alongside the trend.\n\t */\n\tchildren?: ReactNode;\n\t/**\n\t * Explicit accessible label overriding automatic accessible name calculation.\n\t */\n\tariaLabel?: string;\n\t/**\n\t * Additional CSS class names for styling the card root.\n\t */\n\tclassName?: string;\n};\n\nfunction hasSlotContent(slot: ReactNode | undefined): boolean {\n\treturn slot !== undefined && slot !== null && slot !== false;\n}\n\nexport function StatCard({\n\tlabel,\n\ttitle,\n\tvalue,\n\tsubtitle,\n\ticon: Icon,\n\ticonColor = \"text-basalt-muted-foreground\",\n\ttrend,\n\taction,\n\tstatus,\n\ttrendContent,\n\tchildren,\n\tariaLabel,\n\tclassName,\n}: StatCardProps) {\n\tconst heading = title ?? label;\n\tconst hasStatus = hasSlotContent(status);\n\tconst hasTrendContent = hasSlotContent(trendContent);\n\tconst hasAction = hasSlotContent(action);\n\tconst hasChildren = hasSlotContent(children);\n\tconst isInteractive = hasAction || hasStatus || hasTrendContent || hasChildren;\n\tconst hasHeaderEnd = hasAction || Boolean(Icon);\n\n\tconst display = typeof value === \"number\" ? value.toLocaleString() : value;\n\tconst isPositiveTrend = trend && trend.value > 0;\n\tconst isNegativeTrend = trend && trend.value < 0;\n\n\t// Accessible name: If custom status or custom trendContent is provided, do not report\n\t// the overridden hidden default value or default trend to assistive technology.\n\tconst named =\n\t\tariaLabel ??\n\t\t[\n\t\t\theading,\n\t\t\t!hasStatus ? display : null,\n\t\t\tsubtitle,\n\t\t\t!hasTrendContent && trend\n\t\t\t\t? `${isPositiveTrend ? \"+\" : \"\"}${trend.value}% ${trend.label ?? \"\"}`\n\t\t\t\t: null,\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(\" \")\n\t\t\t.trim();\n\n\tconst cardContent = (\n\t\t<>\n\t\t\t<div className=\"flex items-start justify-between gap-3\">\n\t\t\t\t<div className=\"min-w-0 flex-1 space-y-1\">\n\t\t\t\t\t<p className=\"text-xs text-basalt-muted-foreground md:text-sm\">{heading}</p>\n\t\t\t\t\t{hasStatus ? (\n\t\t\t\t\t\t<div className=\"pt-0.5\">{status}</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p className=\"font-display text-xl font-semibold tracking-tight text-basalt-foreground md:text-2xl\">\n\t\t\t\t\t\t\t{display}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{subtitle ? <p className=\"text-xs text-basalt-muted-foreground\">{subtitle}</p> : null}\n\t\t\t\t</div>\n\t\t\t\t{hasHeaderEnd ? (\n\t\t\t\t\t<div className=\"flex items-center gap-2 shrink-0\">\n\t\t\t\t\t\t{hasAction ? <div className=\"flex items-center\">{action}</div> : null}\n\t\t\t\t\t\t{Icon ? (\n\t\t\t\t\t\t\t<div className={cn(\"rounded-md bg-basalt-card p-2\", iconColor)}>\n\t\t\t\t\t\t\t\t<Icon className=\"h-5 w-5\" strokeWidth={1.5} />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</div>\n\t\t\t\t) : null}\n\t\t\t</div>\n\n\t\t\t{hasChildren ? <div className=\"mt-2\">{children}</div> : null}\n\n\t\t\t{hasTrendContent ? (\n\t\t\t\t<div className=\"mt-3 flex items-center gap-1 text-xs\">{trendContent}</div>\n\t\t\t) : trend ? (\n\t\t\t\t<div className=\"mt-3 flex items-center gap-1 text-xs\">\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"font-medium\",\n\t\t\t\t\t\t\tisPositiveTrend && \"text-basalt-heatmap-green-4\",\n\t\t\t\t\t\t\tisNegativeTrend && \"text-basalt-destructive\",\n\t\t\t\t\t\t\t!isPositiveTrend && !isNegativeTrend && \"text-basalt-muted-foreground\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{`${isPositiveTrend ? \"+\" : \"\"}${trend.value}%`}\n\t\t\t\t\t</span>\n\t\t\t\t\t{trend.label ? <span className=\"text-basalt-muted-foreground\">{trend.label}</span> : null}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t</>\n\t);\n\n\treturn (\n\t\t// biome-ignore lint/a11y/useAriaPropsSupportedByRole: dynamic role is \"group\" | \"img\", both support aria-label\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"rounded-basalt-md border border-basalt-border bg-basalt-secondary p-4\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\trole={isInteractive ? \"group\" : \"img\"}\n\t\t\taria-label={named}\n\t\t>\n\t\t\t{cardContent}\n\t\t</div>\n\t);\n}\n\nexport type StatGridProps = {\n\tchildren: ReactNode;\n\tcolumns?: 2 | 3 | 4;\n\tclassName?: string;\n};\n\nexport function StatGrid({ children, columns = 4, className }: StatGridProps) {\n\tconst gridCols = {\n\t\t2: \"grid-cols-1 sm:grid-cols-2\",\n\t\t3: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\",\n\t\t4: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-4\",\n\t};\n\treturn <div className={cn(\"grid gap-3 md:gap-4\", gridCols[columns], className)}>{children}</div>;\n}\n"],"mappings":";;;;AAkEA,SAAS,EAAe,GAAsC;CAC7D,OAAO,KAA+B,QAAQ,MAAS;AACxD;AAEA,SAAgB,EAAS,EACxB,UACA,UACA,UACA,aACA,MAAM,GACN,eAAY,gCACZ,UACA,WACA,WACA,iBACA,aACA,cACA,gBACiB;CACjB,IAAM,IAAU,KAAS,GACnB,IAAY,EAAe,CAAM,GACjC,IAAkB,EAAe,CAAY,GAC7C,IAAY,EAAe,CAAM,GACjC,IAAc,EAAe,CAAQ,GACrC,IAAgB,KAAa,KAAa,KAAmB,GAC7D,IAAe,KAAa,EAAQ,GAEpC,IAAU,OAAO,KAAU,WAAW,EAAM,eAAe,IAAI,GAC/D,IAAkB,KAAS,EAAM,QAAQ,GACzC,IAAkB,KAAS,EAAM,QAAQ,GAIzC,IACL,KACA;EACC;EACC,IAAsB,OAAV;EACb;EACA,CAAC,KAAmB,IACjB,GAAG,IAAkB,MAAM,KAAK,EAAM,MAAM,IAAI,EAAM,SAAS,OAC/D;CACJ,CAAC,CACC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CAAC,CACT,KAAK,GAEF,IACL,kBAAA,GAAA,EAAA,UAAA;EACC,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACC,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACC,kBAAC,KAAD;MAAG,WAAU;MAAmD,UAAA;KAAW,CAAA;KAC1E,IACA,kBAAC,OAAD;MAAK,WAAU;MAAU,UAAA;KAAY,CAAA,IAErC,kBAAC,KAAD;MAAG,WAAU;MACX,UAAA;KACC,CAAA;KAEH,IAAW,kBAAC,KAAD;MAAG,WAAU;MAAwC,UAAA;KAAY,CAAA,IAAI;IAC7E;GACJ,CAAA,GAAA,IACA,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,IAAY,kBAAC,OAAD;KAAK,WAAU;KAAqB,UAAA;IAAY,CAAA,IAAI,MAChE,IACA,kBAAC,OAAD;KAAK,WAAW,EAAG,iCAAiC,CAAS;KAC5D,UAAA,kBAAC,GAAD;MAAM,WAAU;MAAU,aAAa;KAAM,CAAA;IACzC,CAAA,IACF,IACA;GACF,CAAA,IAAA,IACA;;EAEJ,IAAc,kBAAC,OAAD;GAAK,WAAU;GAAQ;EAAc,CAAA,IAAI;EAEvD,IACA,kBAAC,OAAD;GAAK,WAAU;GAAwC,UAAA;EAAkB,CAAA,IACtE,IACH,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACC,kBAAC,QAAD;IACC,WAAW,EACV,eACA,KAAmB,+BACnB,KAAmB,2BACnB,CAAC,KAAmB,CAAC,KAAmB,8BACzC;IAEC,UAAA,GAAG,IAAkB,MAAM,KAAK,EAAM,MAAM;GACxC,CAAA,GACL,EAAM,QAAQ,kBAAC,QAAD;IAAM,WAAU;IAAgC,UAAA,EAAM;GAAY,CAAA,IAAI,IACjF;EACF,CAAA,IAAA;CACH,EAAA,CAAA;CAGH,OAEC,kBAAC,OAAD;EACC,WAAW,EACV,yEACA,CACD;EACA,MAAM,IAAgB,UAAU;EAChC,cAAY;EAEX,UAAA;CACG,CAAA;AAEP;AAQA,SAAgB,EAAS,EAAE,aAAU,aAAU,GAAG,gBAA4B;CAM7E,OAAO,kBAAC,OAAD;EAAK,WAAW,EAAG,uBAAuB;GAJhD,GAAG;GACH,GAAG;GACH,GAAG;EAE6C,EAAS,IAAU,CAAS;EAAI;CAAc,CAAA;AAChG"}
|
|
@@ -58,4 +58,4 @@ export type TimeseriesProps<TData extends {
|
|
|
58
58
|
};
|
|
59
59
|
export declare function Timeseries<TData extends {
|
|
60
60
|
x: string | number;
|
|
61
|
-
} = XYPoint, K extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string>({
|
|
61
|
+
} = XYPoint, K extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string>({ ariaLabel, ...props }: TimeseriesProps<TData, K>): import("react").JSX.Element;
|
|
@@ -2,23 +2,10 @@
|
|
|
2
2
|
import { LineChart as e } from "./line.js";
|
|
3
3
|
import { jsx as t } from "react/jsx-runtime";
|
|
4
4
|
//#region src/charts/timeseries.tsx
|
|
5
|
-
function n({
|
|
5
|
+
function n({ ariaLabel: n = "Timeseries", ...r }) {
|
|
6
6
|
return /* @__PURE__ */ t(e, {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
ariaLabel: i,
|
|
10
|
-
className: a,
|
|
11
|
-
showAxes: o,
|
|
12
|
-
showLegend: s,
|
|
13
|
-
color: c,
|
|
14
|
-
valueFormatter: l,
|
|
15
|
-
xValueFormatter: u,
|
|
16
|
-
yDomain: d,
|
|
17
|
-
legend: f,
|
|
18
|
-
customTooltip: p,
|
|
19
|
-
summary: m,
|
|
20
|
-
dataAlternative: h,
|
|
21
|
-
accessibilityLayer: g
|
|
7
|
+
ariaLabel: n,
|
|
8
|
+
...r
|
|
22
9
|
});
|
|
23
10
|
}
|
|
24
11
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeseries.js","names":[],"sources":["../../src/charts/timeseries.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport {\n\tLineChart,\n\ttype LineChartAxisDomain,\n\ttype LineChartLegendRenderer,\n\ttype LineChartNumericKeys,\n\ttype LineChartTooltipRenderer,\n} from \"./line\";\nimport type { ChartSeriesDescriptor, XYPoint } from \"./series\";\n\nexport type TimeseriesProps<\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\tcolor?: string;\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 Timeseries<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n>({
|
|
1
|
+
{"version":3,"file":"timeseries.js","names":[],"sources":["../../src/charts/timeseries.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport {\n\tLineChart,\n\ttype LineChartAxisDomain,\n\ttype LineChartLegendRenderer,\n\ttype LineChartNumericKeys,\n\ttype LineChartTooltipRenderer,\n} from \"./line\";\nimport type { ChartSeriesDescriptor, XYPoint } from \"./series\";\n\nexport type TimeseriesProps<\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\tcolor?: string;\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 Timeseries<\n\tTData extends { x: string | number } = XYPoint,\n\tK extends LineChartNumericKeys<TData> & string = LineChartNumericKeys<TData> & string,\n>({ ariaLabel = \"Timeseries\", ...props }: TimeseriesProps<TData, K>) {\n\treturn <LineChart<TData, K> ariaLabel={ariaLabel} {...props} />;\n}\n"],"mappings":";;;;AAmEA,SAAgB,EAGd,EAAE,eAAY,cAAc,GAAG,KAAoC;CACpE,OAAO,kBAAC,GAAD;EAAgC;EAAW,GAAI;CAAQ,CAAA;AAC/D"}
|
package/dist/charts/tooltip.js
CHANGED
|
@@ -135,9 +135,9 @@ function f({ label: n = "Total", value: r, unit: a, formatter: o, style: s, ...c
|
|
|
135
135
|
})]
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
|
-
function p({ active: n, payload:
|
|
139
|
-
if (!n || !
|
|
140
|
-
let
|
|
138
|
+
function p({ active: n, payload: i, label: a, formatter: l }) {
|
|
139
|
+
if (!n || !i?.length) return null;
|
|
140
|
+
let d = l ?? o, f = a == null || a === "" ? void 0 : String(a);
|
|
141
141
|
return /* @__PURE__ */ t("div", {
|
|
142
142
|
"data-testid": "chart-tooltip",
|
|
143
143
|
style: {
|
|
@@ -150,7 +150,7 @@ function p({ active: n, payload: l, label: u, formatter: d }) {
|
|
|
150
150
|
minWidth: 128,
|
|
151
151
|
padding: "8px 12px"
|
|
152
152
|
},
|
|
153
|
-
children: [
|
|
153
|
+
children: [f ? /* @__PURE__ */ e("p", {
|
|
154
154
|
style: {
|
|
155
155
|
color: "hsl(var(--basalt-popover-foreground))",
|
|
156
156
|
fontSize: r,
|
|
@@ -159,59 +159,21 @@ function p({ active: n, payload: l, label: u, formatter: d }) {
|
|
|
159
159
|
lineHeight: 1.2,
|
|
160
160
|
margin: "0 0 8px"
|
|
161
161
|
},
|
|
162
|
-
children:
|
|
162
|
+
children: f
|
|
163
163
|
}) : null, /* @__PURE__ */ e("div", {
|
|
164
164
|
style: {
|
|
165
165
|
display: "flex",
|
|
166
166
|
flexDirection: "column",
|
|
167
167
|
gap: 4
|
|
168
168
|
},
|
|
169
|
-
children:
|
|
170
|
-
let
|
|
171
|
-
return /* @__PURE__ */
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
lineHeight: "20px",
|
|
178
|
-
minWidth: 0
|
|
179
|
-
},
|
|
180
|
-
children: [
|
|
181
|
-
/* @__PURE__ */ e("span", {
|
|
182
|
-
"aria-hidden": "true",
|
|
183
|
-
style: {
|
|
184
|
-
background: c(n),
|
|
185
|
-
borderRadius: 999,
|
|
186
|
-
boxShadow: "0 0 0 1px hsl(var(--basalt-popover-foreground) / 0.12)",
|
|
187
|
-
flexShrink: 0,
|
|
188
|
-
height: a,
|
|
189
|
-
width: a
|
|
190
|
-
}
|
|
191
|
-
}),
|
|
192
|
-
u ? /* @__PURE__ */ e("span", {
|
|
193
|
-
style: {
|
|
194
|
-
color: "hsl(var(--basalt-muted-foreground))",
|
|
195
|
-
flex: 1,
|
|
196
|
-
minWidth: 0,
|
|
197
|
-
overflow: "hidden",
|
|
198
|
-
textOverflow: "ellipsis",
|
|
199
|
-
whiteSpace: "nowrap"
|
|
200
|
-
},
|
|
201
|
-
children: u
|
|
202
|
-
}) : null,
|
|
203
|
-
/* @__PURE__ */ e("span", {
|
|
204
|
-
style: {
|
|
205
|
-
color: "hsl(var(--basalt-popover-foreground))",
|
|
206
|
-
flexShrink: 0,
|
|
207
|
-
fontVariantNumeric: "tabular-nums",
|
|
208
|
-
fontWeight: 600,
|
|
209
|
-
marginLeft: u ? 0 : "auto"
|
|
210
|
-
},
|
|
211
|
-
children: Number.isFinite(l) ? f(l) : String(o ?? "—")
|
|
212
|
-
})
|
|
213
|
-
]
|
|
214
|
-
}, d);
|
|
169
|
+
children: i.map((t, n) => {
|
|
170
|
+
let r = t.value, i = typeof r == "number" ? r : typeof r == "string" ? Number(r) : NaN, a = s(t), o = `${String(t.dataKey ?? t.name ?? n)}-${String(n)}`;
|
|
171
|
+
return /* @__PURE__ */ e(u, {
|
|
172
|
+
label: a,
|
|
173
|
+
color: c(t),
|
|
174
|
+
value: Number.isFinite(i) ? d(i) : String(r ?? "—"),
|
|
175
|
+
"data-testid": void 0
|
|
176
|
+
}, o);
|
|
215
177
|
})
|
|
216
178
|
})]
|
|
217
179
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.js","names":[],"sources":["../../src/charts/tooltip.tsx"],"sourcesContent":["import type { ComponentProps, ReactNode } from \"react\";\n\nconst HIDDEN_SERIES_KEYS = new Set([\"y\", \"y2\", \"y3\", \"value\", \"target\"]);\nconst TITLE_SIZE = 12;\nconst BODY_SIZE = 12;\nconst DOT_SIZE = 8;\n\nexport type ChartTooltipItem = {\n\tname?: string;\n\tvalue?: number | string;\n\tcolor?: string;\n\tfill?: string;\n\tstroke?: string;\n\tdataKey?: string | number;\n};\n\nexport type ChartTooltipContentProps = {\n\tactive?: boolean;\n\tpayload?: readonly ChartTooltipItem[];\n\tlabel?: string | number;\n\tformatter?: (value: number) => string;\n};\n\nexport interface ChartTooltipRowProps extends Omit<ComponentProps<\"div\">, \"children\"> {\n\t/**\n\t * Series or metric name displayed beside the color dot.\n\t * Preserves numeric `0` and React nodes; nullish values omit the label element.\n\t */\n\tlabel?: ReactNode;\n\t/**\n\t * Display value of the point or metric.\n\t * Finite numbers are automatically formatted via `formatter` or `formatChartNumber`.\n\t * `null` or `undefined` renders placeholder \"—\". ReactNode values are passed through.\n\t */\n\tvalue?: ReactNode;\n\t/**\n\t * Dot swatch indicator color. Defaults to `--basalt-chart-1`.\n\t */\n\tcolor?: string;\n\t/**\n\t * Optional unit suffix (e.g., \"ms\", \"req/s\", \"%\") rendered adjacent to valid values.\n\t * Omitted when `value` evaluates to placeholder \"—\".\n\t */\n\tunit?: ReactNode;\n\t/**\n\t * Custom numeric value formatter overriding `formatChartNumber`.\n\t */\n\tformatter?: (value: number) => string;\n\t/**\n\t * When true, suppresses rendering of the decorative color dot.\n\t * @default false\n\t */\n\thideIndicator?: boolean;\n}\n\nexport interface ChartTooltipSummaryProps extends Omit<ComponentProps<\"div\">, \"children\"> {\n\t/**\n\t * Summary row heading label.\n\t * Preserves numeric `0` and custom nodes.\n\t * @default \"Total\"\n\t */\n\tlabel?: ReactNode;\n\t/**\n\t * Aggregated metric value. Finite numbers are formatted; nullish renders \"—\".\n\t */\n\tvalue?: ReactNode;\n\t/**\n\t * Optional unit suffix appended to the summary value.\n\t */\n\tunit?: ReactNode;\n\t/**\n\t * Custom numeric formatter for the summary value.\n\t */\n\tformatter?: (value: number) => string;\n}\n\nexport interface ChartTooltipDividerProps extends Omit<ComponentProps<\"hr\">, \"children\"> {}\n\nexport function formatChartNumber(value: number): string {\n\tif (!Number.isFinite(value)) {\n\t\treturn \"—\";\n\t}\n\treturn new Intl.NumberFormat(undefined, {\n\t\tmaximumFractionDigits: Number.isInteger(value) ? 0 : 1,\n\t}).format(value);\n}\n\nfunction seriesLabel(item: ChartTooltipItem): string | undefined {\n\tconst name = item.name == null ? \"\" : String(item.name);\n\tconst key = item.dataKey == null ? \"\" : String(item.dataKey);\n\tif (HIDDEN_SERIES_KEYS.has(name) || HIDDEN_SERIES_KEYS.has(key)) {\n\t\treturn undefined;\n\t}\n\treturn name || undefined;\n}\n\nfunction seriesSwatch(item: ChartTooltipItem): string {\n\treturn item.color ?? item.fill ?? item.stroke ?? \"hsl(var(--basalt-chart-1))\";\n}\n\nfunction formatTooltipValue(val: ReactNode, formatter?: (value: number) => string): ReactNode {\n\tif (val == null) {\n\t\treturn \"—\";\n\t}\n\tif (typeof val === \"number\") {\n\t\treturn formatter ? formatter(val) : formatChartNumber(val);\n\t}\n\treturn val;\n}\n\nexport function ChartTooltipRow({\n\tlabel,\n\tvalue,\n\tcolor,\n\tunit,\n\tformatter,\n\thideIndicator = false,\n\tstyle,\n\t...rest\n}: ChartTooltipRowProps) {\n\tconst formattedValue = formatTooltipValue(value, formatter);\n\tconst hasLabel = label != null;\n\tconst swatchColor = color ?? \"hsl(var(--basalt-chart-1))\";\n\n\treturn (\n\t\t<div\n\t\t\tdata-testid=\"chart-tooltip-row\"\n\t\t\tstyle={{\n\t\t\t\talignItems: \"center\",\n\t\t\t\tdisplay: \"flex\",\n\t\t\t\tfontSize: BODY_SIZE,\n\t\t\t\tgap: 8,\n\t\t\t\tlineHeight: \"20px\",\n\t\t\t\tminWidth: 0,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t\t{...rest}\n\t\t>\n\t\t\t{!hideIndicator ? (\n\t\t\t\t<span\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackground: swatchColor,\n\t\t\t\t\t\tborderRadius: 999,\n\t\t\t\t\t\tboxShadow: \"0 0 0 1px hsl(var(--basalt-popover-foreground) / 0.12)\",\n\t\t\t\t\t\tflexShrink: 0,\n\t\t\t\t\t\theight: DOT_SIZE,\n\t\t\t\t\t\twidth: DOT_SIZE,\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{hasLabel ? (\n\t\t\t\t<span\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tcolor: \"hsl(var(--basalt-muted-foreground))\",\n\t\t\t\t\t\tflex: 1,\n\t\t\t\t\t\tminWidth: 0,\n\t\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\ttextOverflow: \"ellipsis\",\n\t\t\t\t\t\twhiteSpace: \"nowrap\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{label}\n\t\t\t\t</span>\n\t\t\t) : null}\n\t\t\t<span\n\t\t\t\tstyle={{\n\t\t\t\t\tcolor: \"hsl(var(--basalt-popover-foreground))\",\n\t\t\t\t\tflexShrink: 0,\n\t\t\t\t\tfontVariantNumeric: \"tabular-nums\",\n\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\tmarginLeft: hasLabel ? 0 : \"auto\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{formattedValue}\n\t\t\t\t{unit != null && formattedValue !== \"—\" ? (\n\t\t\t\t\t<span style={{ fontWeight: 400, marginLeft: 2 }}>{unit}</span>\n\t\t\t\t) : null}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n\nexport function ChartTooltipDivider({ style, ...rest }: ChartTooltipDividerProps) {\n\treturn (\n\t\t<hr\n\t\t\taria-orientation=\"horizontal\"\n\t\t\tdata-testid=\"chart-tooltip-divider\"\n\t\t\tstyle={{\n\t\t\t\tbackgroundColor: \"hsl(var(--basalt-border) / 0.55)\",\n\t\t\t\tborder: 0,\n\t\t\t\theight: 1,\n\t\t\t\tmargin: \"4px 0\",\n\t\t\t\twidth: \"100%\",\n\t\t\t\t...style,\n\t\t\t}}\n\t\t\t{...rest}\n\t\t/>\n\t);\n}\n\nexport function ChartTooltipSummary({\n\tlabel = \"Total\",\n\tvalue,\n\tunit,\n\tformatter,\n\tstyle,\n\t...rest\n}: ChartTooltipSummaryProps) {\n\tconst formattedValue = formatTooltipValue(value, formatter);\n\tconst hasLabel = label != null;\n\n\treturn (\n\t\t<div\n\t\t\tdata-testid=\"chart-tooltip-summary\"\n\t\t\tstyle={{\n\t\t\t\talignItems: \"center\",\n\t\t\t\tdisplay: \"flex\",\n\t\t\t\tfontSize: BODY_SIZE,\n\t\t\t\tgap: 8,\n\t\t\t\tlineHeight: \"20px\",\n\t\t\t\tminWidth: 0,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t\t{...rest}\n\t\t>\n\t\t\t{hasLabel ? (\n\t\t\t\t<span\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tcolor: \"hsl(var(--basalt-muted-foreground))\",\n\t\t\t\t\t\tflex: 1,\n\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\tminWidth: 0,\n\t\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\ttextOverflow: \"ellipsis\",\n\t\t\t\t\t\twhiteSpace: \"nowrap\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{label}\n\t\t\t\t</span>\n\t\t\t) : null}\n\t\t\t<span\n\t\t\t\tstyle={{\n\t\t\t\t\tcolor: \"hsl(var(--basalt-popover-foreground))\",\n\t\t\t\t\tflexShrink: 0,\n\t\t\t\t\tfontVariantNumeric: \"tabular-nums\",\n\t\t\t\t\tfontWeight: 700,\n\t\t\t\t\tmarginLeft: hasLabel ? 0 : \"auto\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{formattedValue}\n\t\t\t\t{unit != null && formattedValue !== \"—\" ? (\n\t\t\t\t\t<span style={{ fontWeight: 500, marginLeft: 2 }}>{unit}</span>\n\t\t\t\t) : null}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n\nexport function ChartTooltipContent({\n\tactive,\n\tpayload,\n\tlabel,\n\tformatter,\n}: ChartTooltipContentProps) {\n\tif (!active || !payload?.length) {\n\t\treturn null;\n\t}\n\tconst format = formatter ?? formatChartNumber;\n\tconst title = label == null || label === \"\" ? undefined : String(label);\n\treturn (\n\t\t<div\n\t\t\tdata-testid=\"chart-tooltip\"\n\t\t\tstyle={{\n\t\t\t\tbackground: \"hsl(var(--basalt-popover))\",\n\t\t\t\tborder: \"1px solid hsl(var(--basalt-border) / 0.55)\",\n\t\t\t\tborderRadius: 10,\n\t\t\t\tboxShadow: \"0 12px 28px -8px rgb(0 0 0 / 0.28), 0 4px 10px -4px rgb(0 0 0 / 0.16)\",\n\t\t\t\tcolor: \"hsl(var(--basalt-popover-foreground))\",\n\t\t\t\tmaxWidth: 220,\n\t\t\t\tminWidth: 128,\n\t\t\t\tpadding: \"8px 12px\",\n\t\t\t}}\n\t\t>\n\t\t\t{title ? (\n\t\t\t\t<p\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tcolor: \"hsl(var(--basalt-popover-foreground))\",\n\t\t\t\t\t\tfontSize: TITLE_SIZE,\n\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\tletterSpacing: \"-0.01em\",\n\t\t\t\t\t\tlineHeight: 1.2,\n\t\t\t\t\t\tmargin: \"0 0 8px\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</p>\n\t\t\t) : null}\n\t\t\t<div style={{ display: \"flex\", flexDirection: \"column\", gap: 4 }}>\n\t\t\t\t{payload.map((item, index) => {\n\t\t\t\t\tconst raw = item.value;\n\t\t\t\t\tconst numeric =\n\t\t\t\t\t\ttypeof raw === \"number\" ? raw : typeof raw === \"string\" ? Number(raw) : Number.NaN;\n\t\t\t\t\tconst labelText = seriesLabel(item);\n\t\t\t\t\tconst key = `${String(item.dataKey ?? item.name ?? index)}-${String(index)}`;\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\talignItems: \"center\",\n\t\t\t\t\t\t\t\tdisplay: \"flex\",\n\t\t\t\t\t\t\t\tfontSize: BODY_SIZE,\n\t\t\t\t\t\t\t\tgap: 8,\n\t\t\t\t\t\t\t\tlineHeight: \"20px\",\n\t\t\t\t\t\t\t\tminWidth: 0,\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tbackground: seriesSwatch(item),\n\t\t\t\t\t\t\t\t\tborderRadius: 999,\n\t\t\t\t\t\t\t\t\tboxShadow: \"0 0 0 1px hsl(var(--basalt-popover-foreground) / 0.12)\",\n\t\t\t\t\t\t\t\t\tflexShrink: 0,\n\t\t\t\t\t\t\t\t\theight: DOT_SIZE,\n\t\t\t\t\t\t\t\t\twidth: DOT_SIZE,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{labelText ? (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\tcolor: \"hsl(var(--basalt-muted-foreground))\",\n\t\t\t\t\t\t\t\t\t\tflex: 1,\n\t\t\t\t\t\t\t\t\t\tminWidth: 0,\n\t\t\t\t\t\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\t\t\t\t\ttextOverflow: \"ellipsis\",\n\t\t\t\t\t\t\t\t\t\twhiteSpace: \"nowrap\",\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{labelText}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tcolor: \"hsl(var(--basalt-popover-foreground))\",\n\t\t\t\t\t\t\t\t\tflexShrink: 0,\n\t\t\t\t\t\t\t\t\tfontVariantNumeric: \"tabular-nums\",\n\t\t\t\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\t\t\t\tmarginLeft: labelText ? 0 : \"auto\",\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{Number.isFinite(numeric) ? format(numeric) : String(raw ?? \"—\")}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tooltip.js","names":[],"sources":["../../src/charts/tooltip.tsx"],"sourcesContent":["import type { ComponentProps, ReactNode } from \"react\";\n\nconst HIDDEN_SERIES_KEYS = new Set([\"y\", \"y2\", \"y3\", \"value\", \"target\"]);\nconst TITLE_SIZE = 12;\nconst BODY_SIZE = 12;\nconst DOT_SIZE = 8;\n\nexport type ChartTooltipItem = {\n\tname?: string;\n\tvalue?: number | string;\n\tcolor?: string;\n\tfill?: string;\n\tstroke?: string;\n\tdataKey?: string | number;\n};\n\nexport type ChartTooltipContentProps = {\n\tactive?: boolean;\n\tpayload?: readonly ChartTooltipItem[];\n\tlabel?: string | number;\n\tformatter?: (value: number) => string;\n};\n\nexport interface ChartTooltipRowProps extends Omit<ComponentProps<\"div\">, \"children\"> {\n\t/**\n\t * Series or metric name displayed beside the color dot.\n\t * Preserves numeric `0` and React nodes; nullish values omit the label element.\n\t */\n\tlabel?: ReactNode;\n\t/**\n\t * Display value of the point or metric.\n\t * Finite numbers are automatically formatted via `formatter` or `formatChartNumber`.\n\t * `null` or `undefined` renders placeholder \"—\". ReactNode values are passed through.\n\t */\n\tvalue?: ReactNode;\n\t/**\n\t * Dot swatch indicator color. Defaults to `--basalt-chart-1`.\n\t */\n\tcolor?: string;\n\t/**\n\t * Optional unit suffix (e.g., \"ms\", \"req/s\", \"%\") rendered adjacent to valid values.\n\t * Omitted when `value` evaluates to placeholder \"—\".\n\t */\n\tunit?: ReactNode;\n\t/**\n\t * Custom numeric value formatter overriding `formatChartNumber`.\n\t */\n\tformatter?: (value: number) => string;\n\t/**\n\t * When true, suppresses rendering of the decorative color dot.\n\t * @default false\n\t */\n\thideIndicator?: boolean;\n}\n\nexport interface ChartTooltipSummaryProps extends Omit<ComponentProps<\"div\">, \"children\"> {\n\t/**\n\t * Summary row heading label.\n\t * Preserves numeric `0` and custom nodes.\n\t * @default \"Total\"\n\t */\n\tlabel?: ReactNode;\n\t/**\n\t * Aggregated metric value. Finite numbers are formatted; nullish renders \"—\".\n\t */\n\tvalue?: ReactNode;\n\t/**\n\t * Optional unit suffix appended to the summary value.\n\t */\n\tunit?: ReactNode;\n\t/**\n\t * Custom numeric formatter for the summary value.\n\t */\n\tformatter?: (value: number) => string;\n}\n\nexport interface ChartTooltipDividerProps extends Omit<ComponentProps<\"hr\">, \"children\"> {}\n\nexport function formatChartNumber(value: number): string {\n\tif (!Number.isFinite(value)) {\n\t\treturn \"—\";\n\t}\n\treturn new Intl.NumberFormat(undefined, {\n\t\tmaximumFractionDigits: Number.isInteger(value) ? 0 : 1,\n\t}).format(value);\n}\n\nfunction seriesLabel(item: ChartTooltipItem): string | undefined {\n\tconst name = item.name == null ? \"\" : String(item.name);\n\tconst key = item.dataKey == null ? \"\" : String(item.dataKey);\n\tif (HIDDEN_SERIES_KEYS.has(name) || HIDDEN_SERIES_KEYS.has(key)) {\n\t\treturn undefined;\n\t}\n\treturn name || undefined;\n}\n\nfunction seriesSwatch(item: ChartTooltipItem): string {\n\treturn item.color ?? item.fill ?? item.stroke ?? \"hsl(var(--basalt-chart-1))\";\n}\n\nfunction formatTooltipValue(val: ReactNode, formatter?: (value: number) => string): ReactNode {\n\tif (val == null) {\n\t\treturn \"—\";\n\t}\n\tif (typeof val === \"number\") {\n\t\treturn formatter ? formatter(val) : formatChartNumber(val);\n\t}\n\treturn val;\n}\n\nexport function ChartTooltipRow({\n\tlabel,\n\tvalue,\n\tcolor,\n\tunit,\n\tformatter,\n\thideIndicator = false,\n\tstyle,\n\t...rest\n}: ChartTooltipRowProps) {\n\tconst formattedValue = formatTooltipValue(value, formatter);\n\tconst hasLabel = label != null;\n\tconst swatchColor = color ?? \"hsl(var(--basalt-chart-1))\";\n\n\treturn (\n\t\t<div\n\t\t\tdata-testid=\"chart-tooltip-row\"\n\t\t\tstyle={{\n\t\t\t\talignItems: \"center\",\n\t\t\t\tdisplay: \"flex\",\n\t\t\t\tfontSize: BODY_SIZE,\n\t\t\t\tgap: 8,\n\t\t\t\tlineHeight: \"20px\",\n\t\t\t\tminWidth: 0,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t\t{...rest}\n\t\t>\n\t\t\t{!hideIndicator ? (\n\t\t\t\t<span\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackground: swatchColor,\n\t\t\t\t\t\tborderRadius: 999,\n\t\t\t\t\t\tboxShadow: \"0 0 0 1px hsl(var(--basalt-popover-foreground) / 0.12)\",\n\t\t\t\t\t\tflexShrink: 0,\n\t\t\t\t\t\theight: DOT_SIZE,\n\t\t\t\t\t\twidth: DOT_SIZE,\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{hasLabel ? (\n\t\t\t\t<span\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tcolor: \"hsl(var(--basalt-muted-foreground))\",\n\t\t\t\t\t\tflex: 1,\n\t\t\t\t\t\tminWidth: 0,\n\t\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\ttextOverflow: \"ellipsis\",\n\t\t\t\t\t\twhiteSpace: \"nowrap\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{label}\n\t\t\t\t</span>\n\t\t\t) : null}\n\t\t\t<span\n\t\t\t\tstyle={{\n\t\t\t\t\tcolor: \"hsl(var(--basalt-popover-foreground))\",\n\t\t\t\t\tflexShrink: 0,\n\t\t\t\t\tfontVariantNumeric: \"tabular-nums\",\n\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\tmarginLeft: hasLabel ? 0 : \"auto\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{formattedValue}\n\t\t\t\t{unit != null && formattedValue !== \"—\" ? (\n\t\t\t\t\t<span style={{ fontWeight: 400, marginLeft: 2 }}>{unit}</span>\n\t\t\t\t) : null}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n\nexport function ChartTooltipDivider({ style, ...rest }: ChartTooltipDividerProps) {\n\treturn (\n\t\t<hr\n\t\t\taria-orientation=\"horizontal\"\n\t\t\tdata-testid=\"chart-tooltip-divider\"\n\t\t\tstyle={{\n\t\t\t\tbackgroundColor: \"hsl(var(--basalt-border) / 0.55)\",\n\t\t\t\tborder: 0,\n\t\t\t\theight: 1,\n\t\t\t\tmargin: \"4px 0\",\n\t\t\t\twidth: \"100%\",\n\t\t\t\t...style,\n\t\t\t}}\n\t\t\t{...rest}\n\t\t/>\n\t);\n}\n\nexport function ChartTooltipSummary({\n\tlabel = \"Total\",\n\tvalue,\n\tunit,\n\tformatter,\n\tstyle,\n\t...rest\n}: ChartTooltipSummaryProps) {\n\tconst formattedValue = formatTooltipValue(value, formatter);\n\tconst hasLabel = label != null;\n\n\treturn (\n\t\t<div\n\t\t\tdata-testid=\"chart-tooltip-summary\"\n\t\t\tstyle={{\n\t\t\t\talignItems: \"center\",\n\t\t\t\tdisplay: \"flex\",\n\t\t\t\tfontSize: BODY_SIZE,\n\t\t\t\tgap: 8,\n\t\t\t\tlineHeight: \"20px\",\n\t\t\t\tminWidth: 0,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t\t{...rest}\n\t\t>\n\t\t\t{hasLabel ? (\n\t\t\t\t<span\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tcolor: \"hsl(var(--basalt-muted-foreground))\",\n\t\t\t\t\t\tflex: 1,\n\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\tminWidth: 0,\n\t\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\ttextOverflow: \"ellipsis\",\n\t\t\t\t\t\twhiteSpace: \"nowrap\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{label}\n\t\t\t\t</span>\n\t\t\t) : null}\n\t\t\t<span\n\t\t\t\tstyle={{\n\t\t\t\t\tcolor: \"hsl(var(--basalt-popover-foreground))\",\n\t\t\t\t\tflexShrink: 0,\n\t\t\t\t\tfontVariantNumeric: \"tabular-nums\",\n\t\t\t\t\tfontWeight: 700,\n\t\t\t\t\tmarginLeft: hasLabel ? 0 : \"auto\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{formattedValue}\n\t\t\t\t{unit != null && formattedValue !== \"—\" ? (\n\t\t\t\t\t<span style={{ fontWeight: 500, marginLeft: 2 }}>{unit}</span>\n\t\t\t\t) : null}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n\nexport function ChartTooltipContent({\n\tactive,\n\tpayload,\n\tlabel,\n\tformatter,\n}: ChartTooltipContentProps) {\n\tif (!active || !payload?.length) {\n\t\treturn null;\n\t}\n\tconst format = formatter ?? formatChartNumber;\n\tconst title = label == null || label === \"\" ? undefined : String(label);\n\treturn (\n\t\t<div\n\t\t\tdata-testid=\"chart-tooltip\"\n\t\t\tstyle={{\n\t\t\t\tbackground: \"hsl(var(--basalt-popover))\",\n\t\t\t\tborder: \"1px solid hsl(var(--basalt-border) / 0.55)\",\n\t\t\t\tborderRadius: 10,\n\t\t\t\tboxShadow: \"0 12px 28px -8px rgb(0 0 0 / 0.28), 0 4px 10px -4px rgb(0 0 0 / 0.16)\",\n\t\t\t\tcolor: \"hsl(var(--basalt-popover-foreground))\",\n\t\t\t\tmaxWidth: 220,\n\t\t\t\tminWidth: 128,\n\t\t\t\tpadding: \"8px 12px\",\n\t\t\t}}\n\t\t>\n\t\t\t{title ? (\n\t\t\t\t<p\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tcolor: \"hsl(var(--basalt-popover-foreground))\",\n\t\t\t\t\t\tfontSize: TITLE_SIZE,\n\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\tletterSpacing: \"-0.01em\",\n\t\t\t\t\t\tlineHeight: 1.2,\n\t\t\t\t\t\tmargin: \"0 0 8px\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</p>\n\t\t\t) : null}\n\t\t\t<div style={{ display: \"flex\", flexDirection: \"column\", gap: 4 }}>\n\t\t\t\t{payload.map((item, index) => {\n\t\t\t\t\tconst raw = item.value;\n\t\t\t\t\tconst numeric =\n\t\t\t\t\t\ttypeof raw === \"number\" ? raw : typeof raw === \"string\" ? Number(raw) : Number.NaN;\n\t\t\t\t\tconst labelText = seriesLabel(item);\n\t\t\t\t\tconst key = `${String(item.dataKey ?? item.name ?? index)}-${String(index)}`;\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<ChartTooltipRow\n\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\tlabel={labelText}\n\t\t\t\t\t\t\tcolor={seriesSwatch(item)}\n\t\t\t\t\t\t\tvalue={Number.isFinite(numeric) ? format(numeric) : String(raw ?? \"—\")}\n\t\t\t\t\t\t\tdata-testid={undefined}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
|
|
@@ -48,4 +48,4 @@ export type AutocompleteProps = Omit<ComponentPropsWithoutRef<"div">, "children"
|
|
|
48
48
|
*/
|
|
49
49
|
size?: InputSize;
|
|
50
50
|
};
|
|
51
|
-
export declare function Autocomplete({
|
|
51
|
+
export declare function Autocomplete({ placeholder, ...rest }: AutocompleteProps): import("react").JSX.Element;
|
|
@@ -2,24 +2,11 @@
|
|
|
2
2
|
import { TypeaheadField as e } from "./typeahead-field.js";
|
|
3
3
|
import { jsx as t } from "react/jsx-runtime";
|
|
4
4
|
//#region src/components/autocomplete.tsx
|
|
5
|
-
function n({
|
|
5
|
+
function n({ placeholder: n = "Search…", ...r }) {
|
|
6
6
|
return /* @__PURE__ */ t(e, {
|
|
7
|
-
|
|
8
|
-
value: r,
|
|
9
|
-
defaultValue: i,
|
|
10
|
-
onValueChange: a,
|
|
11
|
-
name: o,
|
|
12
|
-
placeholder: s,
|
|
13
|
-
disabled: c,
|
|
14
|
-
loading: l,
|
|
15
|
-
size: u,
|
|
16
|
-
className: d,
|
|
17
|
-
id: f,
|
|
7
|
+
placeholder: n,
|
|
18
8
|
allowFreeform: !0,
|
|
19
|
-
|
|
20
|
-
"aria-describedby": m,
|
|
21
|
-
"aria-invalid": h,
|
|
22
|
-
...g
|
|
9
|
+
...r
|
|
23
10
|
});
|
|
24
11
|
}
|
|
25
12
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete.js","names":[],"sources":["../../src/components/autocomplete.tsx"],"sourcesContent":["import type { ComponentPropsWithoutRef } from \"react\";\nimport type { InputSize } from \"./input\";\nimport { TypeaheadField, type TypeaheadItem } from \"./typeahead-field\";\n\nexport type AutocompleteItem = TypeaheadItem;\n\nexport type AutocompleteProps = Omit<\n\tComponentPropsWithoutRef<\"div\">,\n\t\"children\" | \"defaultValue\" | \"onChange\"\n> & {\n\t/**\n\t * Suggestions shown as the query matches. Values outside the list are allowed.\n\t *\n\t * Note: When focus leaves the field (via pointer click or Tab/Shift+Tab navigation),\n\t * any uncommitted text or matching suggestion is committed once without stealing focus\n\t * back to the input, allowing natural focus traversal.\n\t */\n\titems: AutocompleteItem[];\n\t/**\n\t * The controlled value.\n\t */\n\tvalue?: string;\n\t/**\n\t * The uncontrolled initial value.\n\t */\n\tdefaultValue?: string;\n\t/**\n\t * Called when the value changes, including freeform text.\n\t */\n\tonValueChange?: (value: string) => void;\n\t/**\n\t * Native form field name.\n\t */\n\tname?: string;\n\t/**\n\t * Placeholder shown when the field is empty.\n\t * @default Search…\n\t */\n\tplaceholder?: string;\n\t/**\n\t * Disable the field.\n\t * @default false\n\t */\n\tdisabled?: boolean;\n\t/**\n\t * Disable the field and mark it busy.\n\t * @default false\n\t */\n\tloading?: boolean;\n\t/**\n\t * The visual size of the field.\n\t * @default default\n\t */\n\tsize?: InputSize;\n};\n\nexport function Autocomplete({
|
|
1
|
+
{"version":3,"file":"autocomplete.js","names":[],"sources":["../../src/components/autocomplete.tsx"],"sourcesContent":["import type { ComponentPropsWithoutRef } from \"react\";\nimport type { InputSize } from \"./input\";\nimport { TypeaheadField, type TypeaheadItem } from \"./typeahead-field\";\n\nexport type AutocompleteItem = TypeaheadItem;\n\nexport type AutocompleteProps = Omit<\n\tComponentPropsWithoutRef<\"div\">,\n\t\"children\" | \"defaultValue\" | \"onChange\"\n> & {\n\t/**\n\t * Suggestions shown as the query matches. Values outside the list are allowed.\n\t *\n\t * Note: When focus leaves the field (via pointer click or Tab/Shift+Tab navigation),\n\t * any uncommitted text or matching suggestion is committed once without stealing focus\n\t * back to the input, allowing natural focus traversal.\n\t */\n\titems: AutocompleteItem[];\n\t/**\n\t * The controlled value.\n\t */\n\tvalue?: string;\n\t/**\n\t * The uncontrolled initial value.\n\t */\n\tdefaultValue?: string;\n\t/**\n\t * Called when the value changes, including freeform text.\n\t */\n\tonValueChange?: (value: string) => void;\n\t/**\n\t * Native form field name.\n\t */\n\tname?: string;\n\t/**\n\t * Placeholder shown when the field is empty.\n\t * @default Search…\n\t */\n\tplaceholder?: string;\n\t/**\n\t * Disable the field.\n\t * @default false\n\t */\n\tdisabled?: boolean;\n\t/**\n\t * Disable the field and mark it busy.\n\t * @default false\n\t */\n\tloading?: boolean;\n\t/**\n\t * The visual size of the field.\n\t * @default default\n\t */\n\tsize?: InputSize;\n};\n\nexport function Autocomplete({ placeholder = \"Search…\", ...rest }: AutocompleteProps) {\n\treturn <TypeaheadField placeholder={placeholder} allowFreeform {...rest} />;\n}\n"],"mappings":";;;;AAwDA,SAAgB,EAAa,EAAE,iBAAc,WAAW,GAAG,KAA2B;CACrF,OAAO,kBAAC,GAAD;EAA6B;EAAa,eAAA;EAAc,GAAI;CAAO,CAAA;AAC3E"}
|