@seekalfred/primitives 0.7.1 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +255 -5
  2. package/dist/alert/alert.d.ts +49 -4
  3. package/dist/alert/alert.js +66 -38
  4. package/dist/alert/alert.js.map +1 -1
  5. package/dist/alert/index.d.ts +2 -2
  6. package/dist/alert/index.js +2 -2
  7. package/dist/alert-dialog/alert-dialog.d.ts +31 -4
  8. package/dist/alert-dialog/alert-dialog.js +75 -26
  9. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  10. package/dist/announcement-bar/announcement-bar.d.ts +15 -4
  11. package/dist/announcement-bar/announcement-bar.js +31 -6
  12. package/dist/announcement-bar/announcement-bar.js.map +1 -1
  13. package/dist/avatar/avatar.d.ts +7 -0
  14. package/dist/avatar/avatar.js +33 -12
  15. package/dist/avatar/avatar.js.map +1 -1
  16. package/dist/badge/badge.d.ts +1 -1
  17. package/dist/bar-chart/bar-chart-series.d.ts +75 -0
  18. package/dist/bar-chart/bar-chart-series.js +401 -0
  19. package/dist/bar-chart/bar-chart-series.js.map +1 -0
  20. package/dist/bar-chart/bar-chart.d.ts +25 -1
  21. package/dist/bar-chart/bar-chart.js +26 -1
  22. package/dist/bar-chart/bar-chart.js.map +1 -1
  23. package/dist/bar-chart/index.d.ts +2 -1
  24. package/dist/button/button.d.ts +1 -1
  25. package/dist/calendar/calendar.d.ts +1 -1
  26. package/dist/card/card.d.ts +22 -1
  27. package/dist/card/card.js +60 -7
  28. package/dist/card/card.js.map +1 -1
  29. package/dist/carousel/carousel.d.ts +21 -0
  30. package/dist/carousel/carousel.js +34 -8
  31. package/dist/carousel/carousel.js.map +1 -1
  32. package/dist/chip-group/chip-group.d.ts +11 -0
  33. package/dist/chip-group/chip-group.js +50 -34
  34. package/dist/chip-group/chip-group.js.map +1 -1
  35. package/dist/chip-group/index.d.ts +2 -2
  36. package/dist/copy-button/copy-button.d.ts +37 -5
  37. package/dist/copy-button/copy-button.js +35 -11
  38. package/dist/copy-button/copy-button.js.map +1 -1
  39. package/dist/donut-chart/donut-chart.d.ts +22 -0
  40. package/dist/donut-chart/donut-chart.js +35 -18
  41. package/dist/donut-chart/donut-chart.js.map +1 -1
  42. package/dist/donut-chart/index.d.ts +2 -2
  43. package/dist/empty-state/empty-state.d.ts +21 -0
  44. package/dist/empty-state/empty-state.js +16 -4
  45. package/dist/empty-state/empty-state.js.map +1 -1
  46. package/dist/empty-state/index.d.ts +2 -2
  47. package/dist/empty-state/index.js +2 -2
  48. package/dist/file-drop-zone/file-drop-zone.d.ts +29 -3
  49. package/dist/file-drop-zone/file-drop-zone.js +26 -8
  50. package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
  51. package/dist/filter-bar/filter-bar.d.ts +4 -1
  52. package/dist/filter-bar/filter-bar.js +53 -9
  53. package/dist/filter-bar/filter-bar.js.map +1 -1
  54. package/dist/gauge/gauge.d.ts +18 -1
  55. package/dist/gauge/gauge.js +35 -21
  56. package/dist/gauge/gauge.js.map +1 -1
  57. package/dist/gauge/index.d.ts +2 -2
  58. package/dist/index.d.ts +15 -14
  59. package/dist/index.js +9 -9
  60. package/dist/input/input.d.ts +7 -0
  61. package/dist/input/input.js +7 -5
  62. package/dist/input/input.js.map +1 -1
  63. package/dist/lib/field-shell.js +6 -3
  64. package/dist/lib/field-shell.js.map +1 -1
  65. package/dist/lib/field-surface.js +22 -1
  66. package/dist/lib/field-surface.js.map +1 -1
  67. package/dist/lib/required-marker.js +20 -0
  68. package/dist/lib/required-marker.js.map +1 -0
  69. package/dist/lib/use-copy-feedback.js +17 -3
  70. package/dist/lib/use-copy-feedback.js.map +1 -1
  71. package/dist/line-chart/index.d.ts +2 -2
  72. package/dist/line-chart/line-chart.d.ts +144 -2
  73. package/dist/line-chart/line-chart.js +314 -46
  74. package/dist/line-chart/line-chart.js.map +1 -1
  75. package/dist/money-input/index.d.ts +2 -2
  76. package/dist/money-input/money-input.d.ts +36 -1
  77. package/dist/money-input/money-input.js +30 -14
  78. package/dist/money-input/money-input.js.map +1 -1
  79. package/dist/morph-select/morph-select.d.ts +26 -1
  80. package/dist/morph-select/morph-select.js +28 -6
  81. package/dist/morph-select/morph-select.js.map +1 -1
  82. package/dist/multi-select/index.d.ts +2 -2
  83. package/dist/multi-select/index.js +2 -2
  84. package/dist/multi-select/multi-select.d.ts +74 -2
  85. package/dist/multi-select/multi-select.js +213 -39
  86. package/dist/multi-select/multi-select.js.map +1 -1
  87. package/dist/password-field/password-field.d.ts +16 -0
  88. package/dist/password-field/password-field.js +37 -8
  89. package/dist/password-field/password-field.js.map +1 -1
  90. package/dist/popover/index.d.ts +2 -2
  91. package/dist/popover/index.js +2 -2
  92. package/dist/popover/popover.d.ts +39 -2
  93. package/dist/popover/popover.js +29 -2
  94. package/dist/popover/popover.js.map +1 -1
  95. package/dist/progress/index.d.ts +2 -2
  96. package/dist/progress/index.js +2 -2
  97. package/dist/progress/progress.d.ts +68 -3
  98. package/dist/progress/progress.js +156 -39
  99. package/dist/progress/progress.js.map +1 -1
  100. package/dist/radio-group/index.d.ts +2 -2
  101. package/dist/radio-group/index.js +2 -2
  102. package/dist/radio-group/radio-group.d.ts +33 -3
  103. package/dist/radio-group/radio-group.js +109 -9
  104. package/dist/radio-group/radio-group.js.map +1 -1
  105. package/dist/scroll-area/scroll-area.js +4 -3
  106. package/dist/scroll-area/scroll-area.js.map +1 -1
  107. package/dist/scrub-input/scrub-input.d.ts +11 -1
  108. package/dist/scrub-input/scrub-input.js +22 -10
  109. package/dist/scrub-input/scrub-input.js.map +1 -1
  110. package/dist/search-field/search-field.d.ts +22 -1
  111. package/dist/search-field/search-field.js +22 -7
  112. package/dist/search-field/search-field.js.map +1 -1
  113. package/dist/select/select.d.ts +10 -1
  114. package/dist/select/select.js +11 -8
  115. package/dist/select/select.js.map +1 -1
  116. package/dist/separator/separator.d.ts +21 -9
  117. package/dist/separator/separator.js +33 -11
  118. package/dist/separator/separator.js.map +1 -1
  119. package/dist/sparkline/sparkline.d.ts +27 -2
  120. package/dist/sparkline/sparkline.js +115 -18
  121. package/dist/sparkline/sparkline.js.map +1 -1
  122. package/dist/stat-card/index.d.ts +2 -2
  123. package/dist/stat-card/index.js +2 -2
  124. package/dist/stat-card/stat-card.d.ts +65 -1
  125. package/dist/stat-card/stat-card.js +98 -25
  126. package/dist/stat-card/stat-card.js.map +1 -1
  127. package/dist/styles.css +1 -1
  128. package/dist/table/index.d.ts +2 -2
  129. package/dist/table/index.js +2 -2
  130. package/dist/table/table.d.ts +115 -5
  131. package/dist/table/table.js +374 -94
  132. package/dist/table/table.js.map +1 -1
  133. package/dist/text-shimmer/text-shimmer.d.ts +18 -2
  134. package/dist/text-shimmer/text-shimmer.js +5 -2
  135. package/dist/text-shimmer/text-shimmer.js.map +1 -1
  136. package/dist/textarea/textarea.d.ts +7 -0
  137. package/dist/textarea/textarea.js +3 -1
  138. package/dist/textarea/textarea.js.map +1 -1
  139. package/dist/time-picker/time-picker.d.ts +36 -1
  140. package/dist/time-picker/time-picker.js +33 -5
  141. package/dist/time-picker/time-picker.js.map +1 -1
  142. package/dist/toast/toast.d.ts +2 -2
  143. package/dist/usage-meter/index.d.ts +2 -2
  144. package/dist/usage-meter/usage-meter.d.ts +30 -0
  145. package/dist/usage-meter/usage-meter.js +56 -38
  146. package/dist/usage-meter/usage-meter.js.map +1 -1
  147. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"bar-chart.js","names":[],"sources":["../../src/bar-chart/bar-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n type PointerEvent,\n} from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { prefersReducedMotion, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { live, MORPH, retarget, SMOOTH, SNAPPY, type Live, type Spring } from \"../lib/spring-tween.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * BarChart: one measure across a run of days, weeks or months, when people\n * compare each period against the rest and against the average.\n *\n * The bars grow from the baseline the first time the chart is seen, each a beat\n * after its neighbour, and the dashed average rises with them. A new range zooms\n * the time axis around the latest bar (bars that stay glide to their new slot,\n * new ones grow outward from them, leaving ones sink into the baseline) while\n * the scale and the average slide to fit. Hover, drag or the arrow keys scrub:\n * the scrubbed bar takes the full colour while the rest step back, a cursor\n * glides to it, and the headline swaps from the average to that bar.\n *\n * Plain SVG and CSS; one small spring tween drives the bars, the zoom, the\n * scale, the average and the cursor. A visually hidden table carries every\n * value, and the plot is a slider.\n */\nexport interface BarChartDatum {\n /** Stable identity, such as an ISO date. A bar whose key survives a data change keeps its place. */\n key: string;\n /** Full label for the headline and screen readers, such as \"Tue, Sep 15\". */\n label: string;\n /** Short label under the bar, such as \"T\" or \"Sep 14\". Leave it out to keep the axis quiet. */\n axisLabel?: string;\n value: number;\n}\n\nexport interface BarChartProps {\n data: BarChartDatum[];\n /** What is measured, such as \"Active minutes\". Names the chart for assistive technology. */\n label: string;\n /** The range on show, such as \"Sep 15–21, 2026\". It rests under the headline. */\n period: string;\n /** Unit after each value, such as \"min\". */\n unit?: string;\n /** Headline label at rest, above the average. @default \"Daily average\" */\n averageLabel?: string;\n /** Headline label while a bar is scrubbed. @default \"Total\" */\n valueLabel?: string;\n /** Header of the first column in the data table read by screen readers. @default \"Day\" */\n categoryLabel?: string;\n /** Draws the average as a dashed reference line. @default true */\n showAverage?: boolean;\n /** Plot height in pixels. @default 176 */\n height?: number;\n formatValue?: (value: number) => string;\n /** Extra classes for the root. Merged last. */\n className?: string;\n}\n\n/** Room above the top gridline, the share of each slot a bar fills, its widest size, and the radius of its data end. */\nconst TOP = 12;\nconst FILL = 0.58;\nconst MAX_BAR = 28;\nconst RADIUS = 4;\n/** Stagger between bars on the first reveal (seconds in the source: .035 per bar, at most .36 in all). */\nconst STAGGER_MS = 35;\nconst GROUPED = new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 0 });\nconst clamp = (value: number, low: number, high: number) => Math.min(high, Math.max(low, value));\n/** visualDuration .24s, no bounce: a leaving bar sinks a little faster than a bar grows. */\nconst COLLAPSE: Spring = {\n curve: [\n 0, 0.0484, 0.1552, 0.2816, 0.4069, 0.5207, 0.6189, 0.7009, 0.7676, 0.821, 0.8631, 0.8959, 0.9213,\n 0.9408, 0.9556, 0.9668, 0.9753, 0.9816, 0.9864, 0.9899, 0.9926, 0.9945, 0.996, 0.9971, 0.9978,\n 0.9984, 0.9988, 0.9992, 0.9994, 1,\n ],\n ms: 464,\n};\n\n/** Clean gridlines: the smallest step of 1, 2, 3 or 5 that covers the data in four rows or fewer. */\nfunction scaleFor(max: number) {\n const safe = Math.max(max, 1);\n const magnitude = 10 ** Math.floor(Math.log10(safe / 4));\n for (const factor of [1, 2, 3, 5, 10]) {\n const step = factor * magnitude;\n const rows = Math.ceil(safe / step);\n if (rows <= 4) return { top: rows * step, ticks: Array.from({ length: rows + 1 }, (_, row) => row * step) };\n }\n return { top: safe, ticks: [0, safe] };\n}\n\n/** A column that grows from a square baseline to a softly rounded data end. */\nfunction barPath(x: number, w: number, h: number, base: number) {\n const f = (value: number) => value.toFixed(2);\n if (w <= 0 || h <= 0.01) return `M${f(x)} ${base}H${f(x + Math.max(w, 0))}Z`;\n const r = Math.min(RADIUS, w / 2, h);\n const top = base - h;\n return `M${f(x)} ${base}V${f(top + r)}A${f(r)} ${f(r)} 0 0 1 ${f(x + r)} ${f(top)}H${f(x + w - r)}A${f(r)} ${f(r)} 0 0 1 ${f(x + w)} ${f(top + r)}V${base}Z`;\n}\n\n/** Axis labels that fit once the range has settled: every label, else every second, third and so on, counted back from the latest. */\nfunction fittingLabels(data: BarChartDatum[], width: number, sizes: Record<string, number>) {\n const fitting = new Map<string, number>();\n if (!width) return fitting;\n const count = Math.max(1, data.length);\n const labels = data\n .flatMap((item, index) => {\n const size = item.axisLabel ? sizes[item.axisLabel] : undefined;\n if (size === undefined) return [];\n const natural = width - (data.length - 1 - index + 0.5) * (width / count) - size / 2;\n return [{ key: item.key, size, natural, left: clamp(natural, 0, Math.max(0, width - size)) }];\n })\n .reverse();\n for (let stride = 1; stride <= labels.length; stride++) {\n const picked = labels.filter((_, rank) => rank % stride === 0);\n if (\n stride === labels.length ||\n picked.every((label, rank) => rank === 0 || label.left + label.size + 10 <= picked[rank - 1]!.left)\n ) {\n picked.forEach((label) => fitting.set(label.key, label.left - label.natural));\n break;\n }\n }\n return fitting;\n}\n\nexport const BarChart = forwardRef<HTMLElement, BarChartProps>(function BarChart(\n {\n data,\n label,\n period,\n unit = \"\",\n averageLabel = \"Daily average\",\n valueLabel = \"Total\",\n categoryLabel = \"Day\",\n showAverage = true,\n height = 176,\n formatValue = (value) => GROUPED.format(value),\n className,\n },\n ref,\n) {\n const figure = useRef<HTMLElement | null>(null);\n const refs = useMemo(() => mergeRefs(ref, figure), [ref]);\n const plot = useRef<HTMLDivElement>(null);\n const svg = useRef<SVGSVGElement>(null);\n const gutter = useRef<HTMLDivElement>(null);\n const axis = useRef<HTMLDivElement>(null);\n const ruler = useRef<HTMLSpanElement>(null);\n const titleId = useId();\n const count = Math.max(data.length, 1);\n const last = data.length - 1;\n const average = data.length ? data.reduce((sum, item) => sum + item.value, 0) / data.length : 0;\n const peak = data.reduce((max, item) => Math.max(max, item.value), 0);\n const { top, ticks } = useMemo(() => scaleFor(peak), [peak]);\n const suffix = unit ? ` ${unit}` : \"\";\n\n // Bars that left stay in the DOM while they sink, then go.\n const [leaving, setLeaving] = useState<string[]>([]);\n const previous = useRef(data);\n const [changes, setChanges] = useState(0);\n const [active, setActive] = useState<number | null>(null);\n const signature = data.map((item) => item.key).join(\"|\");\n const [seen, setSeen] = useState(signature);\n if (seen !== signature) {\n setSeen(signature);\n setActive(null);\n setChanges((n) => n + 1);\n }\n const index = active === null ? null : Math.min(active, last);\n const scrubbing = index !== null && last >= 0;\n const shownValue = scrubbing ? data[index]!.value : average;\n const [trend, setTrend] = useState({ value: shownValue, index, valueWay: 1, indexWay: 1 });\n if (trend.value !== shownValue || trend.index !== index) {\n setTrend({\n value: shownValue,\n index,\n valueWay: shownValue === trend.value ? trend.valueWay : shownValue > trend.value ? 1 : -1,\n indexWay: index === null || trend.index === null ? 1 : Math.sign(index - trend.index) || 1,\n });\n }\n\n // Animated state, read only by the painter.\n const width = useRef(0);\n const share = useRef(live(1 / count));\n const scale = useRef(live(top));\n const mean = useRef(live(0));\n const cursor = useRef(live(0));\n const bars = useRef(new Map<string, { place: Live; amount: Live }>());\n const [shown, setShown] = useState(false);\n const [plotWidth, setPlotWidth] = useState(0);\n const [labelSizes, setLabelSizes] = useState<Record<string, number>>({});\n\n /** One writer for every drawn attribute, straight onto the elements. */\n const paint = useCallback(() => {\n const root = svg.current;\n const w = width.current;\n if (!root || !w) return;\n const s = scale.current.value || 1;\n const rowY = (value: number) => Math.round(height - (value / s) * (height - TOP)) + 0.5;\n const slot = w * share.current.value;\n const size = Math.min(slot * FILL, MAX_BAR);\n for (const [key, bar] of bars.current) {\n const node = root.querySelector(`[data-bar=\"${CSS.escape(key)}\"]`);\n const center = w - (bar.place.value + 0.5) * slot;\n node?.setAttribute(\"d\", barPath(center - size / 2, size, Math.max(0, (bar.amount.value / s) * (height - TOP)), height));\n }\n for (const node of root.querySelectorAll<SVGLineElement>(\"[data-grid]\")) {\n const y = String(rowY(Number(node.dataset.grid)));\n node.setAttribute(\"y1\", y);\n node.setAttribute(\"y2\", y);\n }\n const meanY = rowY(mean.current.value);\n const meanLine = root.querySelector(\"[data-part=mean]\");\n meanLine?.setAttribute(\"y1\", String(meanY));\n meanLine?.setAttribute(\"y2\", String(meanY));\n const cx = String(Math.round(w - (cursor.current.value + 0.5) * slot) + 0.5);\n const cross = root.querySelector(\"[data-part=cursor]\");\n cross?.setAttribute(\"x1\", cx);\n cross?.setAttribute(\"x2\", cx);\n // Tick labels ride the scale and make way where the average label sits beside them.\n for (const node of gutter.current?.querySelectorAll<HTMLElement>(\"[data-tick]\") ?? []) {\n const y = rowY(Number(node.dataset.tick));\n node.style.translate = `0 ${y}px`;\n node.style.opacity = showAverage ? String(clamp((Math.abs(y - meanY) - 10) / 6, 0, 1)) : \"1\";\n }\n const meanLabel = gutter.current?.querySelector<HTMLElement>(\"[data-part=mean-label]\");\n if (meanLabel) {\n meanLabel.style.translate = `0 ${meanY}px`;\n const text = meanLabel.querySelector(\"[data-part=mean-text]\");\n if (text) text.textContent = `Avg ${formatValue(Math.round(mean.current.value))}`;\n }\n // Axis labels are centred under their bar and carried by the zoom.\n for (const node of axis.current?.querySelectorAll<HTMLElement>(\"[data-axis]\") ?? []) {\n const place = bars.current.get(node.dataset.axis!)?.place.value ?? 0;\n node.style.translate = `${w - (place + 0.5) * slot - Number(node.dataset.size) / 2 + Number(node.dataset.shift)}px 0`;\n }\n }, [formatValue, height, showAverage]);\n\n const go = useCallback(\n (target: Live, to: number, spring: Spring, delay = 0, done?: () => void) =>\n retarget(target, to, spring, paint, delay, done),\n [paint],\n );\n\n // The chart draws in its own pixels: measure the width and every axis label once, follow resizes.\n const labelTexts = [...new Set(data.map((item) => item.axisLabel).filter((text): text is string => !!text))].join(\"\\n\");\n useIsomorphicLayoutEffect(() => {\n const node = plot.current;\n const measure = ruler.current;\n if (!node || !measure) return;\n const read = () => {\n width.current = node.clientWidth;\n setPlotWidth(node.clientWidth);\n const sizes: Record<string, number> = {};\n measure.querySelectorAll<HTMLElement>(\"[data-text]\").forEach((item) => {\n sizes[item.dataset.text ?? \"\"] = item.offsetWidth;\n });\n setLabelSizes((current) => (Object.keys(sizes).every((text) => current[text] === sizes[text]) ? current : sizes));\n paint();\n };\n read();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(read);\n observer.observe(node);\n return () => observer.disconnect();\n }, [labelTexts, paint]);\n const fitting = useMemo(() => fittingLabels(data, plotWidth, labelSizes), [data, plotWidth, labelSizes]);\n useIsomorphicLayoutEffect(paint);\n\n // The bars grow once the chart is a third in view.\n useEffect(() => {\n const node = figure.current;\n if (!node || prefersReducedMotion() || typeof IntersectionObserver === \"undefined\") {\n setShown(true);\n return;\n }\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry?.isIntersecting) return;\n observer.disconnect();\n setShown(true);\n },\n { threshold: 0.35 },\n );\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n // Every bar to its slot and value. The first reveal runs left to right a beat apart; later, new bars grow outward from the ones that stayed and leaving ones sink.\n const firstReveal = useRef(true);\n useEffect(() => {\n if (!shown) return;\n const first = firstReveal.current;\n firstReveal.current = false;\n const known = new Set(previous.current.map((item) => item.key));\n previous.current = data;\n const incoming = data\n .map((item, at) => ({ key: item.key, place: last - at }))\n .filter((item) => first || !known.has(item.key))\n .sort((a, b) => (first ? b.place - a.place : a.place - b.place));\n const step = Math.min(STAGGER_MS, (first ? 360 : 320) / Math.max(1, incoming.length));\n const delayOf = new Map(incoming.map((item, rank) => [item.key, rank * step]));\n const keys = new Set(data.map((item) => item.key));\n data.forEach((item, at) => {\n const place = last - at;\n let bar = bars.current.get(item.key);\n if (!bar) {\n bar = { place: live(place), amount: live(0) };\n bars.current.set(item.key, bar);\n }\n go(bar.place, place, SMOOTH);\n const fresh = delayOf.has(item.key);\n go(bar.amount, item.value, fresh ? MORPH : SMOOTH, fresh ? delayOf.get(item.key)! : 0);\n });\n const gone = [...bars.current.keys()].filter((key) => !keys.has(key));\n if (gone.length) setLeaving((current) => [...new Set([...current, ...gone])]);\n for (const key of gone) {\n const bar = bars.current.get(key)!;\n go(bar.amount, 0, COLLAPSE, 0, () => {\n bars.current.delete(key);\n setLeaving((current) => current.filter((item) => item !== key));\n });\n }\n // The time axis zooms around the latest bar, and the scale slides to fit.\n go(share.current, 1 / count, SMOOTH);\n go(scale.current, top, SMOOTH);\n // The average rises with the first bars, a beat late, then springs to each new range.\n go(mean.current, average, SMOOTH, first ? 120 : 0);\n // `data` is captured by signature and values; the springs read the rest from refs.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shown, signature, data.map((item) => item.value).join(\",\"), top, average, count]);\n\n // The cursor appears on the first scrubbed bar and glides between the ones after it.\n const wasScrubbing = useRef(false);\n useEffect(() => {\n if (index === null) {\n wasScrubbing.current = false;\n return;\n }\n const place = last - index;\n if (!wasScrubbing.current) {\n wasScrubbing.current = true;\n cursor.current.stop();\n cursor.current.value = place;\n paint();\n return;\n }\n go(cursor.current, place, SNAPPY);\n }, [go, index, last, paint]);\n\n const pointAt = (clientX: number) => {\n const rect = plot.current?.getBoundingClientRect();\n if (!rect?.width) return last;\n const place = Math.floor(((rect.right - clientX) / rect.width) * count);\n return clamp(last - place, 0, last);\n };\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"Escape\" && scrubbing) {\n event.preventDefault();\n setActive(null);\n return;\n }\n const from = index ?? last + 1;\n const page = Math.min(7, count);\n const next = (\n {\n ArrowLeft: from - 1,\n ArrowDown: from - 1,\n ArrowRight: index === null ? last : from + 1,\n ArrowUp: index === null ? last : from + 1,\n PageDown: from - page,\n PageUp: from + page,\n Home: 0,\n End: last,\n } as Record<string, number>\n )[event.key];\n if (next === undefined || last < 0) return;\n event.preventDefault();\n setActive(clamp(next, 0, last));\n };\n\n const scrubbed = scrubbing ? data[index]! : null;\n const reading = scrubbed ?? data[last];\n const valueText = reading ? `${reading.label}: ${formatValue(reading.value)}${suffix}` : \"No data\";\n const highest = data.reduce<BarChartDatum | null>((best, item) => (!best || item.value > best.value ? item : best), null);\n const lowest = data.reduce<BarChartDatum | null>((best, item) => (!best || item.value < best.value ? item : best), null);\n const summary = `${label}, ${period}. ${averageLabel} ${formatValue(Math.round(average))}${suffix}.${\n highest ? ` Highest ${highest.label}, ${formatValue(highest.value)}${suffix}.` : \"\"\n }${lowest && lowest !== highest ? ` Lowest ${lowest.label}, ${formatValue(lowest.value)}${suffix}.` : \"\"}`;\n const headline = formatValue(scrubbing ? data[index]!.value : Math.round(average));\n const barKeys = [...data.map((item) => item.key), ...leaving.filter((key) => !data.some((item) => item.key === key))];\n\n return (\n <figure\n ref={refs}\n data-slot=\"bar-chart\"\n aria-labelledby={titleId}\n className={cn(\"m-0 grid min-w-0 gap-5 box-border font-sans text-foreground\", className)}\n >\n <figcaption id={titleId} className=\"sr-only\">\n {label}, {period}\n </figcaption>\n <div aria-hidden=\"true\" className=\"grid min-w-0 gap-0.5\">\n <span className=\"block min-h-5 text-sm text-text-secondary\">\n <SwapText swapKey={scrubbing ? valueLabel : averageLabel} motion=\"rise\" className=\"block\">\n {scrubbing ? valueLabel : averageLabel}\n </SwapText>\n </span>\n <span data-slot=\"bar-chart-value\" className=\"flex min-w-0 items-baseline gap-1.5 text-h2 font-medium tabular-nums\">\n <SwapText swapKey={headline} dir={trend.valueWay} motion=\"rise\">\n {headline}\n </SwapText>\n {unit ? <span className=\"text-base font-normal text-text-secondary\">{unit}</span> : null}\n </span>\n <span className=\"block min-h-5 text-sm text-muted-foreground\">\n <SwapText\n swapKey={scrubbed ? scrubbed.label : period}\n dir={scrubbing ? trend.indexWay : 1}\n motion=\"rise\"\n className=\"block\"\n >\n {scrubbed ? scrubbed.label : period}\n </SwapText>\n </span>\n </div>\n <div data-slot=\"bar-chart-chart\" data-scrubbing={scrubbing || undefined} data-shown={shown || undefined} className=\"bar-chart-frame\">\n <div ref={plot} className=\"relative min-w-0 [grid-area:1/1]\" style={{ height }}>\n <svg ref={svg} width=\"100%\" height={height} role=\"img\" aria-label={summary} className=\"block overflow-hidden\">\n {ticks.slice(1).map((value) => (\n <line key={value} data-grid={value} className=\"line-chart-grid motion-safe:animate-fade-in\" x1={0} x2=\"100%\" />\n ))}\n <line data-part=\"cursor\" className=\"line-chart-crosshair\" y1={TOP} y2={height} />\n {barKeys.map((key) => (\n <path\n key={key}\n data-bar={key}\n data-active={(scrubbing && data[index]?.key === key) || undefined}\n className=\"bar-chart-bar\"\n />\n ))}\n <line className=\"line-chart-baseline\" x1={0} x2=\"100%\" y1={height - 0.5} y2={height - 0.5} />\n {showAverage ? <line data-part=\"mean\" className=\"bar-chart-mean\" x1={0} x2=\"100%\" /> : null}\n </svg>\n </div>\n <div ref={gutter} aria-hidden=\"true\" className=\"relative min-w-0 [grid-area:1/2]\">\n {ticks.map((value) => (\n <span key={value} data-tick={value} className=\"line-chart-tick motion-safe:animate-fade-in\">\n <span className=\"block -translate-y-1/2 text-xs leading-none tabular-nums text-muted-foreground\">\n {formatValue(value)}\n </span>\n </span>\n ))}\n {showAverage ? (\n <span data-part=\"mean-label\" className=\"line-chart-tick bar-chart-mean-label\">\n <span data-part=\"mean-text\" className=\"block -translate-y-1/2 text-xs leading-none font-medium tabular-nums text-foreground\" />\n </span>\n ) : null}\n </div>\n <div ref={axis} aria-hidden=\"true\" className=\"relative min-w-0 overflow-hidden [grid-area:2/1]\">\n {data.map((item) =>\n item.axisLabel && fitting.has(item.key) ? (\n <span\n key={`${item.key}:${item.axisLabel}`}\n data-axis={item.key}\n data-size={labelSizes[item.axisLabel] ?? 0}\n data-shift={fitting.get(item.key) ?? 0}\n className=\"absolute start-0 top-2 whitespace-nowrap text-xs leading-none tabular-nums text-muted-foreground motion-safe:animate-fade-in\"\n >\n {item.axisLabel}\n </span>\n ) : null,\n )}\n <span ref={ruler} className=\"pointer-events-none invisible absolute start-0 top-0 flex\">\n {labelTexts\n .split(\"\\n\")\n .filter(Boolean)\n .map((text) => (\n <span key={text} data-text={text} className=\"flex-none whitespace-nowrap text-xs leading-none tabular-nums\">\n {text}\n </span>\n ))}\n </span>\n </div>\n {/* The scrubber lies over the plot: hover or drag across it, or focus it and use the arrow keys. */}\n <div\n data-slot=\"bar-chart-scrubber\"\n role=\"slider\"\n tabIndex={0}\n aria-label={`${label}, explore by ${categoryLabel.toLowerCase()}`}\n aria-orientation=\"horizontal\"\n aria-valuemin={1}\n aria-valuemax={Math.max(1, data.length)}\n aria-valuenow={(index ?? last) + 1}\n aria-valuetext={valueText}\n onPointerDown={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"mouse\") event.currentTarget.setPointerCapture?.(event.pointerId);\n setActive(pointAt(event.clientX));\n }}\n onPointerMove={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType === \"mouse\" || event.buttons) setActive(pointAt(event.clientX));\n }}\n onPointerUp={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"mouse\") setActive(null);\n }}\n onPointerCancel={() => setActive(null)}\n onPointerLeave={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType === \"mouse\") setActive(null);\n }}\n onKeyDown={onKeyDown}\n onBlur={() => setActive(null)}\n onFocus={(event) => {\n if (event.currentTarget.matches(\":focus-visible\") && last >= 0) setActive((current) => current ?? last);\n }}\n className=\"bar-chart-scrubber\"\n />\n </div>\n <div className=\"sr-only\">\n <table>\n <caption>\n {label}, {period}\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">{categoryLabel}</th>\n <th scope=\"col\">{label}</th>\n </tr>\n </thead>\n <tbody>\n {data.map((item) => (\n <tr key={item.key}>\n <th scope=\"row\">{item.label}</th>\n <td>\n {formatValue(item.value)}\n {suffix}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n <p className=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\">\n {changes ? `${period}. ${averageLabel} ${formatValue(Math.round(average))}${suffix}.` : \"\"}\n </p>\n </figure>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"bar-chart.js","names":[],"sources":["../../src/bar-chart/bar-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n type PointerEvent,\n} from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { prefersReducedMotion, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { live, MORPH, retarget, SMOOTH, SNAPPY, type Live, type Spring } from \"../lib/spring-tween.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\nimport { SeriesBarChart, type BarChartSeriesProps } from \"./bar-chart-series.js\";\n\nexport type {\n BarChartSeries,\n BarChartSeriesDatum,\n BarChartSeriesProps,\n} from \"./bar-chart-series.js\";\n\n/**\n * BarChart: one measure across a run of days, weeks or months, when people\n * compare each period against the rest and against the average.\n *\n * The bars grow from the baseline the first time the chart is seen, each a beat\n * after its neighbour, and the dashed average rises with them. A new range zooms\n * the time axis around the latest bar (bars that stay glide to their new slot,\n * new ones grow outward from them, leaving ones sink into the baseline) while\n * the scale and the average slide to fit. Hover, drag or the arrow keys scrub:\n * the scrubbed bar takes the full colour while the rest step back, a cursor\n * glides to it, and the headline swaps from the average to that bar.\n *\n * Plain SVG and CSS; one small spring tween drives the bars, the zoom, the\n * scale, the average and the cursor. A visually hidden table carries every\n * value, and the plot is a slider.\n *\n * Pass `series` for the series mode instead: several measures per category,\n * grouped or stacked, on a categorical axis in data order, upright or as\n * horizontal rows, with a legend (see `BarChartSeriesProps`). Without\n * `series` the chart is exactly the one described above.\n */\nexport interface BarChartDatum {\n /** Stable identity, such as an ISO date. A bar whose key survives a data change keeps its place. */\n key: string;\n /** Full label for the headline and screen readers, such as \"Tue, Sep 15\". */\n label: string;\n /** Short label under the bar, such as \"T\" or \"Sep 14\". Leave it out to keep the axis quiet. */\n axisLabel?: string;\n value: number;\n}\n\nexport interface BarChartProps {\n data: BarChartDatum[];\n /** What is measured, such as \"Active minutes\". Names the chart for assistive technology. */\n label: string;\n /** The range on show, such as \"Sep 15–21, 2026\". It rests under the headline. */\n period: string;\n /** Unit after each value, such as \"min\". */\n unit?: string;\n /** Headline label at rest, above the average. @default \"Daily average\" */\n averageLabel?: string;\n /** Headline label while a bar is scrubbed. @default \"Total\" */\n valueLabel?: string;\n /** Header of the first column in the data table read by screen readers. @default \"Day\" */\n categoryLabel?: string;\n /** Draws the average as a dashed reference line. @default true */\n showAverage?: boolean;\n /** Plot height in pixels. @default 176 */\n height?: number;\n formatValue?: (value: number) => string;\n /** Extra classes for the root. Merged last. */\n className?: string;\n /** Not used in this mode: passing `series` switches to the series mode (`BarChartSeriesProps`). */\n series?: undefined;\n}\n\n/** Room above the top gridline, the share of each slot a bar fills, its widest size, and the radius of its data end. */\nconst TOP = 12;\nconst FILL = 0.58;\nconst MAX_BAR = 28;\nconst RADIUS = 4;\n/** Stagger between bars on the first reveal (seconds in the source: .035 per bar, at most .36 in all). */\nconst STAGGER_MS = 35;\nconst GROUPED = new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 0 });\nconst clamp = (value: number, low: number, high: number) => Math.min(high, Math.max(low, value));\n/** visualDuration .24s, no bounce: a leaving bar sinks a little faster than a bar grows. */\nconst COLLAPSE: Spring = {\n curve: [\n 0, 0.0484, 0.1552, 0.2816, 0.4069, 0.5207, 0.6189, 0.7009, 0.7676, 0.821, 0.8631, 0.8959, 0.9213,\n 0.9408, 0.9556, 0.9668, 0.9753, 0.9816, 0.9864, 0.9899, 0.9926, 0.9945, 0.996, 0.9971, 0.9978,\n 0.9984, 0.9988, 0.9992, 0.9994, 1,\n ],\n ms: 464,\n};\n\n/** Clean gridlines: the smallest step of 1, 2, 3 or 5 that covers the data in four rows or fewer. */\nfunction scaleFor(max: number) {\n const safe = Math.max(max, 1);\n const magnitude = 10 ** Math.floor(Math.log10(safe / 4));\n for (const factor of [1, 2, 3, 5, 10]) {\n const step = factor * magnitude;\n const rows = Math.ceil(safe / step);\n if (rows <= 4) return { top: rows * step, ticks: Array.from({ length: rows + 1 }, (_, row) => row * step) };\n }\n return { top: safe, ticks: [0, safe] };\n}\n\n/** A column that grows from a square baseline to a softly rounded data end. */\nfunction barPath(x: number, w: number, h: number, base: number) {\n const f = (value: number) => value.toFixed(2);\n if (w <= 0 || h <= 0.01) return `M${f(x)} ${base}H${f(x + Math.max(w, 0))}Z`;\n const r = Math.min(RADIUS, w / 2, h);\n const top = base - h;\n return `M${f(x)} ${base}V${f(top + r)}A${f(r)} ${f(r)} 0 0 1 ${f(x + r)} ${f(top)}H${f(x + w - r)}A${f(r)} ${f(r)} 0 0 1 ${f(x + w)} ${f(top + r)}V${base}Z`;\n}\n\n/** Axis labels that fit once the range has settled: every label, else every second, third and so on, counted back from the latest. */\nfunction fittingLabels(data: BarChartDatum[], width: number, sizes: Record<string, number>) {\n const fitting = new Map<string, number>();\n if (!width) return fitting;\n const count = Math.max(1, data.length);\n const labels = data\n .flatMap((item, index) => {\n const size = item.axisLabel ? sizes[item.axisLabel] : undefined;\n if (size === undefined) return [];\n const natural = width - (data.length - 1 - index + 0.5) * (width / count) - size / 2;\n return [{ key: item.key, size, natural, left: clamp(natural, 0, Math.max(0, width - size)) }];\n })\n .reverse();\n for (let stride = 1; stride <= labels.length; stride++) {\n const picked = labels.filter((_, rank) => rank % stride === 0);\n if (\n stride === labels.length ||\n picked.every((label, rank) => rank === 0 || label.left + label.size + 10 <= picked[rank - 1]!.left)\n ) {\n picked.forEach((label) => fitting.set(label.key, label.left - label.natural));\n break;\n }\n }\n return fitting;\n}\n\nconst TimeBarChart = forwardRef<HTMLElement, BarChartProps>(function TimeBarChart(\n {\n data,\n label,\n period,\n unit = \"\",\n averageLabel = \"Daily average\",\n valueLabel = \"Total\",\n categoryLabel = \"Day\",\n showAverage = true,\n height = 176,\n formatValue = (value) => GROUPED.format(value),\n className,\n },\n ref,\n) {\n const figure = useRef<HTMLElement | null>(null);\n const refs = useMemo(() => mergeRefs(ref, figure), [ref]);\n const plot = useRef<HTMLDivElement>(null);\n const svg = useRef<SVGSVGElement>(null);\n const gutter = useRef<HTMLDivElement>(null);\n const axis = useRef<HTMLDivElement>(null);\n const ruler = useRef<HTMLSpanElement>(null);\n const titleId = useId();\n const count = Math.max(data.length, 1);\n const last = data.length - 1;\n const average = data.length ? data.reduce((sum, item) => sum + item.value, 0) / data.length : 0;\n const peak = data.reduce((max, item) => Math.max(max, item.value), 0);\n const { top, ticks } = useMemo(() => scaleFor(peak), [peak]);\n const suffix = unit ? ` ${unit}` : \"\";\n\n // Bars that left stay in the DOM while they sink, then go.\n const [leaving, setLeaving] = useState<string[]>([]);\n const previous = useRef(data);\n const [changes, setChanges] = useState(0);\n const [active, setActive] = useState<number | null>(null);\n const signature = data.map((item) => item.key).join(\"|\");\n const [seen, setSeen] = useState(signature);\n if (seen !== signature) {\n setSeen(signature);\n setActive(null);\n setChanges((n) => n + 1);\n }\n const index = active === null ? null : Math.min(active, last);\n const scrubbing = index !== null && last >= 0;\n const shownValue = scrubbing ? data[index]!.value : average;\n const [trend, setTrend] = useState({ value: shownValue, index, valueWay: 1, indexWay: 1 });\n if (trend.value !== shownValue || trend.index !== index) {\n setTrend({\n value: shownValue,\n index,\n valueWay: shownValue === trend.value ? trend.valueWay : shownValue > trend.value ? 1 : -1,\n indexWay: index === null || trend.index === null ? 1 : Math.sign(index - trend.index) || 1,\n });\n }\n\n // Animated state, read only by the painter.\n const width = useRef(0);\n const share = useRef(live(1 / count));\n const scale = useRef(live(top));\n const mean = useRef(live(0));\n const cursor = useRef(live(0));\n const bars = useRef(new Map<string, { place: Live; amount: Live }>());\n const [shown, setShown] = useState(false);\n const [plotWidth, setPlotWidth] = useState(0);\n const [labelSizes, setLabelSizes] = useState<Record<string, number>>({});\n\n /** One writer for every drawn attribute, straight onto the elements. */\n const paint = useCallback(() => {\n const root = svg.current;\n const w = width.current;\n if (!root || !w) return;\n const s = scale.current.value || 1;\n const rowY = (value: number) => Math.round(height - (value / s) * (height - TOP)) + 0.5;\n const slot = w * share.current.value;\n const size = Math.min(slot * FILL, MAX_BAR);\n for (const [key, bar] of bars.current) {\n const node = root.querySelector(`[data-bar=\"${CSS.escape(key)}\"]`);\n const center = w - (bar.place.value + 0.5) * slot;\n node?.setAttribute(\"d\", barPath(center - size / 2, size, Math.max(0, (bar.amount.value / s) * (height - TOP)), height));\n }\n for (const node of root.querySelectorAll<SVGLineElement>(\"[data-grid]\")) {\n const y = String(rowY(Number(node.dataset.grid)));\n node.setAttribute(\"y1\", y);\n node.setAttribute(\"y2\", y);\n }\n const meanY = rowY(mean.current.value);\n const meanLine = root.querySelector(\"[data-part=mean]\");\n meanLine?.setAttribute(\"y1\", String(meanY));\n meanLine?.setAttribute(\"y2\", String(meanY));\n const cx = String(Math.round(w - (cursor.current.value + 0.5) * slot) + 0.5);\n const cross = root.querySelector(\"[data-part=cursor]\");\n cross?.setAttribute(\"x1\", cx);\n cross?.setAttribute(\"x2\", cx);\n // Tick labels ride the scale and make way where the average label sits beside them.\n for (const node of gutter.current?.querySelectorAll<HTMLElement>(\"[data-tick]\") ?? []) {\n const y = rowY(Number(node.dataset.tick));\n node.style.translate = `0 ${y}px`;\n node.style.opacity = showAverage ? String(clamp((Math.abs(y - meanY) - 10) / 6, 0, 1)) : \"1\";\n }\n const meanLabel = gutter.current?.querySelector<HTMLElement>(\"[data-part=mean-label]\");\n if (meanLabel) {\n meanLabel.style.translate = `0 ${meanY}px`;\n const text = meanLabel.querySelector(\"[data-part=mean-text]\");\n if (text) text.textContent = `Avg ${formatValue(Math.round(mean.current.value))}`;\n }\n // Axis labels are centred under their bar and carried by the zoom.\n for (const node of axis.current?.querySelectorAll<HTMLElement>(\"[data-axis]\") ?? []) {\n const place = bars.current.get(node.dataset.axis!)?.place.value ?? 0;\n node.style.translate = `${w - (place + 0.5) * slot - Number(node.dataset.size) / 2 + Number(node.dataset.shift)}px 0`;\n }\n }, [formatValue, height, showAverage]);\n\n const go = useCallback(\n (target: Live, to: number, spring: Spring, delay = 0, done?: () => void) =>\n retarget(target, to, spring, paint, delay, done),\n [paint],\n );\n\n // The chart draws in its own pixels: measure the width and every axis label once, follow resizes.\n const labelTexts = [...new Set(data.map((item) => item.axisLabel).filter((text): text is string => !!text))].join(\"\\n\");\n useIsomorphicLayoutEffect(() => {\n const node = plot.current;\n const measure = ruler.current;\n if (!node || !measure) return;\n const read = () => {\n width.current = node.clientWidth;\n setPlotWidth(node.clientWidth);\n const sizes: Record<string, number> = {};\n measure.querySelectorAll<HTMLElement>(\"[data-text]\").forEach((item) => {\n sizes[item.dataset.text ?? \"\"] = item.offsetWidth;\n });\n setLabelSizes((current) => (Object.keys(sizes).every((text) => current[text] === sizes[text]) ? current : sizes));\n paint();\n };\n read();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(read);\n observer.observe(node);\n return () => observer.disconnect();\n }, [labelTexts, paint]);\n const fitting = useMemo(() => fittingLabels(data, plotWidth, labelSizes), [data, plotWidth, labelSizes]);\n useIsomorphicLayoutEffect(paint);\n\n // The bars grow once the chart is a third in view.\n useEffect(() => {\n const node = figure.current;\n if (!node || prefersReducedMotion() || typeof IntersectionObserver === \"undefined\") {\n setShown(true);\n return;\n }\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry?.isIntersecting) return;\n observer.disconnect();\n setShown(true);\n },\n { threshold: 0.35 },\n );\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n // Every bar to its slot and value. The first reveal runs left to right a beat apart; later, new bars grow outward from the ones that stayed and leaving ones sink.\n const firstReveal = useRef(true);\n useEffect(() => {\n if (!shown) return;\n const first = firstReveal.current;\n firstReveal.current = false;\n const known = new Set(previous.current.map((item) => item.key));\n previous.current = data;\n const incoming = data\n .map((item, at) => ({ key: item.key, place: last - at }))\n .filter((item) => first || !known.has(item.key))\n .sort((a, b) => (first ? b.place - a.place : a.place - b.place));\n const step = Math.min(STAGGER_MS, (first ? 360 : 320) / Math.max(1, incoming.length));\n const delayOf = new Map(incoming.map((item, rank) => [item.key, rank * step]));\n const keys = new Set(data.map((item) => item.key));\n data.forEach((item, at) => {\n const place = last - at;\n let bar = bars.current.get(item.key);\n if (!bar) {\n bar = { place: live(place), amount: live(0) };\n bars.current.set(item.key, bar);\n }\n go(bar.place, place, SMOOTH);\n const fresh = delayOf.has(item.key);\n go(bar.amount, item.value, fresh ? MORPH : SMOOTH, fresh ? delayOf.get(item.key)! : 0);\n });\n const gone = [...bars.current.keys()].filter((key) => !keys.has(key));\n if (gone.length) setLeaving((current) => [...new Set([...current, ...gone])]);\n for (const key of gone) {\n const bar = bars.current.get(key)!;\n go(bar.amount, 0, COLLAPSE, 0, () => {\n bars.current.delete(key);\n setLeaving((current) => current.filter((item) => item !== key));\n });\n }\n // The time axis zooms around the latest bar, and the scale slides to fit.\n go(share.current, 1 / count, SMOOTH);\n go(scale.current, top, SMOOTH);\n // The average rises with the first bars, a beat late, then springs to each new range.\n go(mean.current, average, SMOOTH, first ? 120 : 0);\n // `data` is captured by signature and values; the springs read the rest from refs.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shown, signature, data.map((item) => item.value).join(\",\"), top, average, count]);\n\n // The cursor appears on the first scrubbed bar and glides between the ones after it.\n const wasScrubbing = useRef(false);\n useEffect(() => {\n if (index === null) {\n wasScrubbing.current = false;\n return;\n }\n const place = last - index;\n if (!wasScrubbing.current) {\n wasScrubbing.current = true;\n cursor.current.stop();\n cursor.current.value = place;\n paint();\n return;\n }\n go(cursor.current, place, SNAPPY);\n }, [go, index, last, paint]);\n\n const pointAt = (clientX: number) => {\n const rect = plot.current?.getBoundingClientRect();\n if (!rect?.width) return last;\n const place = Math.floor(((rect.right - clientX) / rect.width) * count);\n return clamp(last - place, 0, last);\n };\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"Escape\" && scrubbing) {\n event.preventDefault();\n setActive(null);\n return;\n }\n const from = index ?? last + 1;\n const page = Math.min(7, count);\n const next = (\n {\n ArrowLeft: from - 1,\n ArrowDown: from - 1,\n ArrowRight: index === null ? last : from + 1,\n ArrowUp: index === null ? last : from + 1,\n PageDown: from - page,\n PageUp: from + page,\n Home: 0,\n End: last,\n } as Record<string, number>\n )[event.key];\n if (next === undefined || last < 0) return;\n event.preventDefault();\n setActive(clamp(next, 0, last));\n };\n\n const scrubbed = scrubbing ? data[index]! : null;\n const reading = scrubbed ?? data[last];\n const valueText = reading ? `${reading.label}: ${formatValue(reading.value)}${suffix}` : \"No data\";\n const highest = data.reduce<BarChartDatum | null>((best, item) => (!best || item.value > best.value ? item : best), null);\n const lowest = data.reduce<BarChartDatum | null>((best, item) => (!best || item.value < best.value ? item : best), null);\n const summary = `${label}, ${period}. ${averageLabel} ${formatValue(Math.round(average))}${suffix}.${\n highest ? ` Highest ${highest.label}, ${formatValue(highest.value)}${suffix}.` : \"\"\n }${lowest && lowest !== highest ? ` Lowest ${lowest.label}, ${formatValue(lowest.value)}${suffix}.` : \"\"}`;\n const headline = formatValue(scrubbing ? data[index]!.value : Math.round(average));\n const barKeys = [...data.map((item) => item.key), ...leaving.filter((key) => !data.some((item) => item.key === key))];\n\n return (\n <figure\n ref={refs}\n data-slot=\"bar-chart\"\n aria-labelledby={titleId}\n className={cn(\"m-0 grid min-w-0 gap-5 box-border font-sans text-foreground\", className)}\n >\n <figcaption id={titleId} className=\"sr-only\">\n {label}, {period}\n </figcaption>\n <div aria-hidden=\"true\" className=\"grid min-w-0 gap-0.5\">\n <span className=\"block min-h-5 text-sm text-text-secondary\">\n <SwapText swapKey={scrubbing ? valueLabel : averageLabel} motion=\"rise\" className=\"block\">\n {scrubbing ? valueLabel : averageLabel}\n </SwapText>\n </span>\n <span data-slot=\"bar-chart-value\" className=\"flex min-w-0 items-baseline gap-1.5 text-h2 font-medium tabular-nums\">\n <SwapText swapKey={headline} dir={trend.valueWay} motion=\"rise\">\n {headline}\n </SwapText>\n {unit ? <span className=\"text-base font-normal text-text-secondary\">{unit}</span> : null}\n </span>\n <span className=\"block min-h-5 text-sm text-muted-foreground\">\n <SwapText\n swapKey={scrubbed ? scrubbed.label : period}\n dir={scrubbing ? trend.indexWay : 1}\n motion=\"rise\"\n className=\"block\"\n >\n {scrubbed ? scrubbed.label : period}\n </SwapText>\n </span>\n </div>\n <div data-slot=\"bar-chart-chart\" data-scrubbing={scrubbing || undefined} data-shown={shown || undefined} className=\"bar-chart-frame\">\n <div ref={plot} className=\"relative min-w-0 [grid-area:1/1]\" style={{ height }}>\n <svg ref={svg} width=\"100%\" height={height} role=\"img\" aria-label={summary} className=\"block overflow-hidden\">\n {ticks.slice(1).map((value) => (\n <line key={value} data-grid={value} className=\"line-chart-grid motion-safe:animate-fade-in\" x1={0} x2=\"100%\" />\n ))}\n <line data-part=\"cursor\" className=\"line-chart-crosshair\" y1={TOP} y2={height} />\n {barKeys.map((key) => (\n <path\n key={key}\n data-bar={key}\n data-active={(scrubbing && data[index]?.key === key) || undefined}\n className=\"bar-chart-bar\"\n />\n ))}\n <line className=\"line-chart-baseline\" x1={0} x2=\"100%\" y1={height - 0.5} y2={height - 0.5} />\n {showAverage ? <line data-part=\"mean\" className=\"bar-chart-mean\" x1={0} x2=\"100%\" /> : null}\n </svg>\n </div>\n <div ref={gutter} aria-hidden=\"true\" className=\"relative min-w-0 [grid-area:1/2]\">\n {ticks.map((value) => (\n <span key={value} data-tick={value} className=\"line-chart-tick motion-safe:animate-fade-in\">\n <span className=\"block -translate-y-1/2 text-xs leading-none tabular-nums text-muted-foreground\">\n {formatValue(value)}\n </span>\n </span>\n ))}\n {showAverage ? (\n <span data-part=\"mean-label\" className=\"line-chart-tick bar-chart-mean-label\">\n <span data-part=\"mean-text\" className=\"block -translate-y-1/2 text-xs leading-none font-medium tabular-nums text-foreground\" />\n </span>\n ) : null}\n </div>\n <div ref={axis} aria-hidden=\"true\" className=\"relative min-w-0 overflow-hidden [grid-area:2/1]\">\n {data.map((item) =>\n item.axisLabel && fitting.has(item.key) ? (\n <span\n key={`${item.key}:${item.axisLabel}`}\n data-axis={item.key}\n data-size={labelSizes[item.axisLabel] ?? 0}\n data-shift={fitting.get(item.key) ?? 0}\n className=\"absolute start-0 top-2 whitespace-nowrap text-xs leading-none tabular-nums text-muted-foreground motion-safe:animate-fade-in\"\n >\n {item.axisLabel}\n </span>\n ) : null,\n )}\n <span ref={ruler} className=\"pointer-events-none invisible absolute start-0 top-0 flex\">\n {labelTexts\n .split(\"\\n\")\n .filter(Boolean)\n .map((text) => (\n <span key={text} data-text={text} className=\"flex-none whitespace-nowrap text-xs leading-none tabular-nums\">\n {text}\n </span>\n ))}\n </span>\n </div>\n {/* The scrubber lies over the plot: hover or drag across it, or focus it and use the arrow keys. */}\n <div\n data-slot=\"bar-chart-scrubber\"\n role=\"slider\"\n tabIndex={0}\n aria-label={`${label}, explore by ${categoryLabel.toLowerCase()}`}\n aria-orientation=\"horizontal\"\n aria-valuemin={1}\n aria-valuemax={Math.max(1, data.length)}\n aria-valuenow={(index ?? last) + 1}\n aria-valuetext={valueText}\n onPointerDown={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"mouse\") event.currentTarget.setPointerCapture?.(event.pointerId);\n setActive(pointAt(event.clientX));\n }}\n onPointerMove={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType === \"mouse\" || event.buttons) setActive(pointAt(event.clientX));\n }}\n onPointerUp={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"mouse\") setActive(null);\n }}\n onPointerCancel={() => setActive(null)}\n onPointerLeave={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType === \"mouse\") setActive(null);\n }}\n onKeyDown={onKeyDown}\n onBlur={() => setActive(null)}\n onFocus={(event) => {\n if (event.currentTarget.matches(\":focus-visible\") && last >= 0) setActive((current) => current ?? last);\n }}\n className=\"bar-chart-scrubber\"\n />\n </div>\n <div className=\"sr-only\">\n <table>\n <caption>\n {label}, {period}\n </caption>\n <thead>\n <tr>\n <th scope=\"col\">{categoryLabel}</th>\n <th scope=\"col\">{label}</th>\n </tr>\n </thead>\n <tbody>\n {data.map((item) => (\n <tr key={item.key}>\n <th scope=\"row\">{item.label}</th>\n <td>\n {formatValue(item.value)}\n {suffix}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n <p className=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\">\n {changes ? `${period}. ${averageLabel} ${formatValue(Math.round(average))}${suffix}.` : \"\"}\n </p>\n </figure>\n );\n});\n\n/**\n * One measure across a run of periods (`BarChartProps`), or, with `series`,\n * several measures per category, grouped or stacked, upright or horizontal\n * (`BarChartSeriesProps`).\n *\n * @example\n * <BarChart label=\"Active minutes\" period=\"Sep 15–21, 2026\" data={week} />\n * <BarChart\n * label=\"Spend by campaign\"\n * series={[{ key: \"search\", label: \"Search\" }, { key: \"social\", label: \"Social\" }]}\n * layout=\"stacked\"\n * orientation=\"horizontal\"\n * data={campaigns}\n * />\n */\nexport const BarChart = forwardRef<HTMLElement, BarChartProps | BarChartSeriesProps>(\n function BarChart(props, ref) {\n return props.series === undefined ? (\n <TimeBarChart ref={ref} {...props} />\n ) : (\n <SeriesBarChart ref={ref} {...props} />\n );\n },\n);\n"],"mappings":";;;;;;;;;;;AAoFA,MAAM,MAAM;AACZ,MAAM,OAAO;AACb,MAAM,UAAU;AAChB,MAAM,SAAS;;AAEf,MAAM,aAAa;AACnB,MAAM,UAAU,IAAI,KAAK,aAAa,SAAS,EAAE,uBAAuB,EAAE,CAAC;AAC3E,MAAM,SAAS,OAAe,KAAa,SAAiB,KAAK,IAAI,MAAM,KAAK,IAAI,KAAK,KAAK,CAAC;;AAE/F,MAAM,WAAmB;CACvB,OAAO;EACL;EAAG;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;EAAO;EAAQ;EAAQ;EAC1F;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;EAAO;EAAQ;EACvF;EAAQ;EAAQ;EAAQ;EAAQ;CAClC;CACA,IAAI;AACN;;AAGA,SAAS,SAAS,KAAa;CAC7B,MAAM,OAAO,KAAK,IAAI,KAAK,CAAC;CAC5B,MAAM,YAAY,MAAM,KAAK,MAAM,KAAK,MAAM,OAAO,CAAC,CAAC;CACvD,KAAK,MAAM,UAAU;EAAC;EAAG;EAAG;EAAG;EAAG;CAAE,GAAG;EACrC,MAAM,OAAO,SAAS;EACtB,MAAM,OAAO,KAAK,KAAK,OAAO,IAAI;EAClC,IAAI,QAAQ,GAAG,OAAO;GAAE,KAAK,OAAO;GAAM,OAAO,MAAM,KAAK,EAAE,QAAQ,OAAO,EAAE,IAAI,GAAG,QAAQ,MAAM,IAAI;EAAE;CAC5G;CACA,OAAO;EAAE,KAAK;EAAM,OAAO,CAAC,GAAG,IAAI;CAAE;AACvC;;AAGA,SAAS,QAAQ,GAAW,GAAW,GAAW,MAAc;CAC9D,MAAM,KAAK,UAAkB,MAAM,QAAQ,CAAC;CAC5C,IAAI,KAAK,KAAK,KAAK,KAAM,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,GAAG,EAAE,IAAI,KAAK,IAAI,GAAG,CAAC,CAAC,EAAE;CAC1E,MAAM,IAAI,KAAK,IAAI,QAAQ,IAAI,GAAG,CAAC;CACnC,MAAM,MAAM,OAAO;CACnB,OAAO,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,KAAK;AAC5J;;AAGA,SAAS,cAAc,MAAuB,OAAe,OAA+B;CAC1F,MAAM,0BAAU,IAAI,IAAoB;CACxC,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM;CACrC,MAAM,SAAS,KACZ,SAAS,MAAM,UAAU;EACxB,MAAM,OAAO,KAAK,YAAY,MAAM,KAAK,aAAa,KAAA;EACtD,IAAI,SAAS,KAAA,GAAW,OAAO,CAAC;EAChC,MAAM,UAAU,SAAS,KAAK,SAAS,IAAI,QAAQ,OAAQ,QAAQ,SAAS,OAAO;EACnF,OAAO,CAAC;GAAE,KAAK,KAAK;GAAK;GAAM;GAAS,MAAM,MAAM,SAAS,GAAG,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC;EAAE,CAAC;CAC9F,CAAC,CAAC,CACD,QAAQ;CACX,KAAK,IAAI,SAAS,GAAG,UAAU,OAAO,QAAQ,UAAU;EACtD,MAAM,SAAS,OAAO,QAAQ,GAAG,SAAS,OAAO,WAAW,CAAC;EAC7D,IACE,WAAW,OAAO,UAClB,OAAO,OAAO,OAAO,SAAS,SAAS,KAAK,MAAM,OAAO,MAAM,OAAO,MAAM,OAAO,OAAO,EAAE,CAAE,IAAI,GAClG;GACA,OAAO,SAAS,UAAU,QAAQ,IAAI,MAAM,KAAK,MAAM,OAAO,MAAM,OAAO,CAAC;GAC5E;EACF;CACF;CACA,OAAO;AACT;AAEA,MAAM,eAAe,WAAuC,SAAS,aACnE,EACE,MACA,OACA,QACA,OAAO,IACP,eAAe,iBACf,aAAa,SACb,gBAAgB,OAChB,cAAc,MACd,SAAS,KACT,eAAe,UAAU,QAAQ,OAAO,KAAK,GAC7C,aAEF,KACA;CACA,MAAM,SAAS,OAA2B,IAAI;CAC9C,MAAM,OAAO,cAAc,UAAU,KAAK,MAAM,GAAG,CAAC,GAAG,CAAC;CACxD,MAAM,OAAO,OAAuB,IAAI;CACxC,MAAM,MAAM,OAAsB,IAAI;CACtC,MAAM,SAAS,OAAuB,IAAI;CAC1C,MAAM,OAAO,OAAuB,IAAI;CACxC,MAAM,QAAQ,OAAwB,IAAI;CAC1C,MAAM,UAAU,MAAM;CACtB,MAAM,QAAQ,KAAK,IAAI,KAAK,QAAQ,CAAC;CACrC,MAAM,OAAO,KAAK,SAAS;CAC3B,MAAM,UAAU,KAAK,SAAS,KAAK,QAAQ,KAAK,SAAS,MAAM,KAAK,OAAO,CAAC,IAAI,KAAK,SAAS;CAC9F,MAAM,OAAO,KAAK,QAAQ,KAAK,SAAS,KAAK,IAAI,KAAK,KAAK,KAAK,GAAG,CAAC;CACpE,MAAM,EAAE,KAAK,UAAU,cAAc,SAAS,IAAI,GAAG,CAAC,IAAI,CAAC;CAC3D,MAAM,SAAS,OAAO,IAAI,SAAS;CAGnC,MAAM,CAAC,SAAS,cAAc,SAAmB,CAAC,CAAC;CACnD,MAAM,WAAW,OAAO,IAAI;CAC5B,MAAM,CAAC,SAAS,cAAc,SAAS,CAAC;CACxC,MAAM,CAAC,QAAQ,aAAa,SAAwB,IAAI;CACxD,MAAM,YAAY,KAAK,KAAK,SAAS,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;CACvD,MAAM,CAAC,MAAM,WAAW,SAAS,SAAS;CAC1C,IAAI,SAAS,WAAW;EACtB,QAAQ,SAAS;EACjB,UAAU,IAAI;EACd,YAAY,MAAM,IAAI,CAAC;CACzB;CACA,MAAM,QAAQ,WAAW,OAAO,OAAO,KAAK,IAAI,QAAQ,IAAI;CAC5D,MAAM,YAAY,UAAU,QAAQ,QAAQ;CAC5C,MAAM,aAAa,YAAY,KAAK,MAAM,CAAE,QAAQ;CACpD,MAAM,CAAC,OAAO,YAAY,SAAS;EAAE,OAAO;EAAY;EAAO,UAAU;EAAG,UAAU;CAAE,CAAC;CACzF,IAAI,MAAM,UAAU,cAAc,MAAM,UAAU,OAChD,SAAS;EACP,OAAO;EACP;EACA,UAAU,eAAe,MAAM,QAAQ,MAAM,WAAW,aAAa,MAAM,QAAQ,IAAI;EACvF,UAAU,UAAU,QAAQ,MAAM,UAAU,OAAO,IAAI,KAAK,KAAK,QAAQ,MAAM,KAAK,KAAK;CAC3F,CAAC;CAIH,MAAM,QAAQ,OAAO,CAAC;CACtB,MAAM,QAAQ,OAAO,KAAK,IAAI,KAAK,CAAC;CACpC,MAAM,QAAQ,OAAO,KAAK,GAAG,CAAC;CAC9B,MAAM,OAAO,OAAO,KAAK,CAAC,CAAC;CAC3B,MAAM,SAAS,OAAO,KAAK,CAAC,CAAC;CAC7B,MAAM,OAAO,uBAAO,IAAI,IAA2C,CAAC;CACpE,MAAM,CAAC,OAAO,YAAY,SAAS,KAAK;CACxC,MAAM,CAAC,WAAW,gBAAgB,SAAS,CAAC;CAC5C,MAAM,CAAC,YAAY,iBAAiB,SAAiC,CAAC,CAAC;;CAGvE,MAAM,QAAQ,kBAAkB;EAC9B,MAAM,OAAO,IAAI;EACjB,MAAM,IAAI,MAAM;EAChB,IAAI,CAAC,QAAQ,CAAC,GAAG;EACjB,MAAM,IAAI,MAAM,QAAQ,SAAS;EACjC,MAAM,QAAQ,UAAkB,KAAK,MAAM,SAAU,QAAQ,KAAM,SAAS,IAAI,IAAI;EACpF,MAAM,OAAO,IAAI,MAAM,QAAQ;EAC/B,MAAM,OAAO,KAAK,IAAI,OAAO,MAAM,OAAO;EAC1C,KAAK,MAAM,CAAC,KAAK,QAAQ,KAAK,SAAS;GACrC,MAAM,OAAO,KAAK,cAAc,cAAc,IAAI,OAAO,GAAG,EAAE,GAAG;GACjE,MAAM,SAAS,KAAK,IAAI,MAAM,QAAQ,MAAO;GAC7C,MAAM,aAAa,KAAK,QAAQ,SAAS,OAAO,GAAG,MAAM,KAAK,IAAI,GAAI,IAAI,OAAO,QAAQ,KAAM,SAAS,IAAI,GAAG,MAAM,CAAC;EACxH;EACA,KAAK,MAAM,QAAQ,KAAK,iBAAiC,aAAa,GAAG;GACvE,MAAM,IAAI,OAAO,KAAK,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC;GAChD,KAAK,aAAa,MAAM,CAAC;GACzB,KAAK,aAAa,MAAM,CAAC;EAC3B;EACA,MAAM,QAAQ,KAAK,KAAK,QAAQ,KAAK;EACrC,MAAM,WAAW,KAAK,cAAc,kBAAkB;EACtD,UAAU,aAAa,MAAM,OAAO,KAAK,CAAC;EAC1C,UAAU,aAAa,MAAM,OAAO,KAAK,CAAC;EAC1C,MAAM,KAAK,OAAO,KAAK,MAAM,KAAK,OAAO,QAAQ,QAAQ,MAAO,IAAI,IAAI,EAAG;EAC3E,MAAM,QAAQ,KAAK,cAAc,oBAAoB;EACrD,OAAO,aAAa,MAAM,EAAE;EAC5B,OAAO,aAAa,MAAM,EAAE;EAE5B,KAAK,MAAM,QAAQ,OAAO,SAAS,iBAA8B,aAAa,KAAK,CAAC,GAAG;GACrF,MAAM,IAAI,KAAK,OAAO,KAAK,QAAQ,IAAI,CAAC;GACxC,KAAK,MAAM,YAAY,KAAK,EAAE;GAC9B,KAAK,MAAM,UAAU,cAAc,OAAO,OAAO,KAAK,IAAI,IAAI,KAAK,IAAI,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI;EAC3F;EACA,MAAM,YAAY,OAAO,SAAS,cAA2B,wBAAwB;EACrF,IAAI,WAAW;GACb,UAAU,MAAM,YAAY,KAAK,MAAM;GACvC,MAAM,OAAO,UAAU,cAAc,uBAAuB;GAC5D,IAAI,MAAM,KAAK,cAAc,OAAO,YAAY,KAAK,MAAM,KAAK,QAAQ,KAAK,CAAC;EAChF;EAEA,KAAK,MAAM,QAAQ,KAAK,SAAS,iBAA8B,aAAa,KAAK,CAAC,GAAG;GACnF,MAAM,QAAQ,KAAK,QAAQ,IAAI,KAAK,QAAQ,IAAK,CAAC,EAAE,MAAM,SAAS;GACnE,KAAK,MAAM,YAAY,GAAG,KAAK,QAAQ,MAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,IAAI,IAAI,OAAO,KAAK,QAAQ,KAAK,EAAE;EAClH;CACF,GAAG;EAAC;EAAa;EAAQ;CAAW,CAAC;CAErC,MAAM,KAAK,aACR,QAAc,IAAY,QAAgB,QAAQ,GAAG,SACpD,SAAS,QAAQ,IAAI,QAAQ,OAAO,OAAO,IAAI,GACjD,CAAC,KAAK,CACR;CAGA,MAAM,aAAa,CAAC,GAAG,IAAI,IAAI,KAAK,KAAK,SAAS,KAAK,SAAS,CAAC,CAAC,QAAQ,SAAyB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;CACtH,gCAAgC;EAC9B,MAAM,OAAO,KAAK;EAClB,MAAM,UAAU,MAAM;EACtB,IAAI,CAAC,QAAQ,CAAC,SAAS;EACvB,MAAM,aAAa;GACjB,MAAM,UAAU,KAAK;GACrB,aAAa,KAAK,WAAW;GAC7B,MAAM,QAAgC,CAAC;GACvC,QAAQ,iBAA8B,aAAa,CAAC,CAAC,SAAS,SAAS;IACrE,MAAM,KAAK,QAAQ,QAAQ,MAAM,KAAK;GACxC,CAAC;GACD,eAAe,YAAa,OAAO,KAAK,KAAK,CAAC,CAAC,OAAO,SAAS,QAAQ,UAAU,MAAM,KAAK,IAAI,UAAU,KAAM;GAChH,MAAM;EACR;EACA,KAAK;EACL,IAAI,OAAO,mBAAmB,aAAa;EAC3C,MAAM,WAAW,IAAI,eAAe,IAAI;EACxC,SAAS,QAAQ,IAAI;EACrB,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,YAAY,KAAK,CAAC;CACtB,MAAM,UAAU,cAAc,cAAc,MAAM,WAAW,UAAU,GAAG;EAAC;EAAM;EAAW;CAAU,CAAC;CACvG,0BAA0B,KAAK;CAG/B,gBAAgB;EACd,MAAM,OAAO,OAAO;EACpB,IAAI,CAAC,QAAQ,qBAAqB,KAAK,OAAO,yBAAyB,aAAa;GAClF,SAAS,IAAI;GACb;EACF;EACA,MAAM,WAAW,IAAI,sBAClB,CAAC,WAAW;GACX,IAAI,CAAC,OAAO,gBAAgB;GAC5B,SAAS,WAAW;GACpB,SAAS,IAAI;EACf,GACA,EAAE,WAAW,IAAK,CACpB;EACA,SAAS,QAAQ,IAAI;EACrB,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,CAAC;CAGL,MAAM,cAAc,OAAO,IAAI;CAC/B,gBAAgB;EACd,IAAI,CAAC,OAAO;EACZ,MAAM,QAAQ,YAAY;EAC1B,YAAY,UAAU;EACtB,MAAM,QAAQ,IAAI,IAAI,SAAS,QAAQ,KAAK,SAAS,KAAK,GAAG,CAAC;EAC9D,SAAS,UAAU;EACnB,MAAM,WAAW,KACd,KAAK,MAAM,QAAQ;GAAE,KAAK,KAAK;GAAK,OAAO,OAAO;EAAG,EAAE,CAAC,CACxD,QAAQ,SAAS,SAAS,CAAC,MAAM,IAAI,KAAK,GAAG,CAAC,CAAC,CAC/C,MAAM,GAAG,MAAO,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAM;EACjE,MAAM,OAAO,KAAK,IAAI,aAAa,QAAQ,MAAM,OAAO,KAAK,IAAI,GAAG,SAAS,MAAM,CAAC;EACpF,MAAM,UAAU,IAAI,IAAI,SAAS,KAAK,MAAM,SAAS,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC;EAC7E,MAAM,OAAO,IAAI,IAAI,KAAK,KAAK,SAAS,KAAK,GAAG,CAAC;EACjD,KAAK,SAAS,MAAM,OAAO;GACzB,MAAM,QAAQ,OAAO;GACrB,IAAI,MAAM,KAAK,QAAQ,IAAI,KAAK,GAAG;GACnC,IAAI,CAAC,KAAK;IACR,MAAM;KAAE,OAAO,KAAK,KAAK;KAAG,QAAQ,KAAK,CAAC;IAAE;IAC5C,KAAK,QAAQ,IAAI,KAAK,KAAK,GAAG;GAChC;GACA,GAAG,IAAI,OAAO,OAAO,MAAM;GAC3B,MAAM,QAAQ,QAAQ,IAAI,KAAK,GAAG;GAClC,GAAG,IAAI,QAAQ,KAAK,OAAO,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,IAAI,KAAK,GAAG,IAAK,CAAC;EACvF,CAAC;EACD,MAAM,OAAO,CAAC,GAAG,KAAK,QAAQ,KAAK,CAAC,CAAC,CAAC,QAAQ,QAAQ,CAAC,KAAK,IAAI,GAAG,CAAC;EACpE,IAAI,KAAK,QAAQ,YAAY,YAAY,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC;EAC5E,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,MAAM,KAAK,QAAQ,IAAI,GAAG;GAChC,GAAG,IAAI,QAAQ,GAAG,UAAU,SAAS;IACnC,KAAK,QAAQ,OAAO,GAAG;IACvB,YAAY,YAAY,QAAQ,QAAQ,SAAS,SAAS,GAAG,CAAC;GAChE,CAAC;EACH;EAEA,GAAG,MAAM,SAAS,IAAI,OAAO,MAAM;EACnC,GAAG,MAAM,SAAS,KAAK,MAAM;EAE7B,GAAG,KAAK,SAAS,SAAS,QAAQ,QAAQ,MAAM,CAAC;CAGnD,GAAG;EAAC;EAAO;EAAW,KAAK,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,KAAK,GAAG;EAAG;EAAK;EAAS;CAAK,CAAC;CAGpF,MAAM,eAAe,OAAO,KAAK;CACjC,gBAAgB;EACd,IAAI,UAAU,MAAM;GAClB,aAAa,UAAU;GACvB;EACF;EACA,MAAM,QAAQ,OAAO;EACrB,IAAI,CAAC,aAAa,SAAS;GACzB,aAAa,UAAU;GACvB,OAAO,QAAQ,KAAK;GACpB,OAAO,QAAQ,QAAQ;GACvB,MAAM;GACN;EACF;EACA,GAAG,OAAO,SAAS,OAAO,MAAM;CAClC,GAAG;EAAC;EAAI;EAAO;EAAM;CAAK,CAAC;CAE3B,MAAM,WAAW,YAAoB;EACnC,MAAM,OAAO,KAAK,SAAS,sBAAsB;EACjD,IAAI,CAAC,MAAM,OAAO,OAAO;EACzB,MAAM,QAAQ,KAAK,OAAQ,KAAK,QAAQ,WAAW,KAAK,QAAS,KAAK;EACtE,OAAO,MAAM,OAAO,OAAO,GAAG,IAAI;CACpC;CACA,MAAM,aAAa,UAAyC;EAC1D,IAAI,MAAM,QAAQ,YAAY,WAAW;GACvC,MAAM,eAAe;GACrB,UAAU,IAAI;GACd;EACF;EACA,MAAM,OAAO,SAAS,OAAO;EAC7B,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK;EAC9B,MAAM,OACJ;GACE,WAAW,OAAO;GAClB,WAAW,OAAO;GAClB,YAAY,UAAU,OAAO,OAAO,OAAO;GAC3C,SAAS,UAAU,OAAO,OAAO,OAAO;GACxC,UAAU,OAAO;GACjB,QAAQ,OAAO;GACf,MAAM;GACN,KAAK;EACP,EACA,MAAM;EACR,IAAI,SAAS,KAAA,KAAa,OAAO,GAAG;EACpC,MAAM,eAAe;EACrB,UAAU,MAAM,MAAM,GAAG,IAAI,CAAC;CAChC;CAEA,MAAM,WAAW,YAAY,KAAK,SAAU;CAC5C,MAAM,UAAU,YAAY,KAAK;CACjC,MAAM,YAAY,UAAU,GAAG,QAAQ,MAAM,IAAI,YAAY,QAAQ,KAAK,IAAI,WAAW;CACzF,MAAM,UAAU,KAAK,QAA8B,MAAM,SAAU,CAAC,QAAQ,KAAK,QAAQ,KAAK,QAAQ,OAAO,MAAO,IAAI;CACxH,MAAM,SAAS,KAAK,QAA8B,MAAM,SAAU,CAAC,QAAQ,KAAK,QAAQ,KAAK,QAAQ,OAAO,MAAO,IAAI;CACvH,MAAM,UAAU,GAAG,MAAM,IAAI,OAAO,IAAI,aAAa,GAAG,YAAY,KAAK,MAAM,OAAO,CAAC,IAAI,OAAO,GAChG,UAAU,YAAY,QAAQ,MAAM,IAAI,YAAY,QAAQ,KAAK,IAAI,OAAO,KAAK,KAChF,UAAU,WAAW,UAAU,WAAW,OAAO,MAAM,IAAI,YAAY,OAAO,KAAK,IAAI,OAAO,KAAK;CACtG,MAAM,WAAW,YAAY,YAAY,KAAK,MAAM,CAAE,QAAQ,KAAK,MAAM,OAAO,CAAC;CACjF,MAAM,UAAU,CAAC,GAAG,KAAK,KAAK,SAAS,KAAK,GAAG,GAAG,GAAG,QAAQ,QAAQ,QAAQ,CAAC,KAAK,MAAM,SAAS,KAAK,QAAQ,GAAG,CAAC,CAAC;CAEpH,OACE,qBAAC,UAAD;EACE,KAAK;EACL,aAAU;EACV,mBAAiB;EACjB,WAAW,GAAG,+DAA+D,SAAS;EAJxF,UAAA;GAME,qBAAC,cAAD;IAAY,IAAI;IAAS,WAAU;IAAnC,UAAA;KACG;KAAM;KAAG;IACA;;GACZ,qBAAC,OAAD;IAAK,eAAY;IAAO,WAAU;IAAlC,UAAA;KACE,oBAAC,QAAD;MAAM,WAAU;MACd,UAAA,oBAAC,UAAD;OAAU,SAAS,YAAY,aAAa;OAAc,QAAO;OAAO,WAAU;OAC/E,UAAA,YAAY,aAAa;MAClB,CAAA;KACN,CAAA;KACN,qBAAC,QAAD;MAAM,aAAU;MAAkB,WAAU;MAA5C,UAAA,CACE,oBAAC,UAAD;OAAU,SAAS;OAAU,KAAK,MAAM;OAAU,QAAO;OACtD,UAAA;MACO,CAAA,GACT,OAAO,oBAAC,QAAD;OAAM,WAAU;OAA6C,UAAA;MAAW,CAAA,IAAI,IAChF;;KACN,oBAAC,QAAD;MAAM,WAAU;MACd,UAAA,oBAAC,UAAD;OACE,SAAS,WAAW,SAAS,QAAQ;OACrC,KAAK,YAAY,MAAM,WAAW;OAClC,QAAO;OACP,WAAU;OAET,UAAA,WAAW,SAAS,QAAQ;MACrB,CAAA;KACN,CAAA;IACH;;GACL,qBAAC,OAAD;IAAK,aAAU;IAAkB,kBAAgB,aAAa,KAAA;IAAW,cAAY,SAAS,KAAA;IAAW,WAAU;IAAnH,UAAA;KACE,oBAAC,OAAD;MAAK,KAAK;MAAM,WAAU;MAAmC,OAAO,EAAE,OAAO;MAC3E,UAAA,qBAAC,OAAD;OAAK,KAAK;OAAK,OAAM;OAAe;OAAQ,MAAK;OAAM,cAAY;OAAS,WAAU;OAAtF,UAAA;QACG,MAAM,MAAM,CAAC,CAAC,CAAC,KAAK,UACnB,oBAAC,QAAD;SAAkB,aAAW;SAAO,WAAU;SAA8C,IAAI;SAAG,IAAG;QAAQ,GAAnG,KAAmG,CAC/G;QACD,oBAAC,QAAD;SAAM,aAAU;SAAS,WAAU;SAAuB,IAAI;SAAK,IAAI;QAAS,CAAA;QAC/E,QAAQ,KAAK,QACZ,oBAAC,QAAD;SAEE,YAAU;SACV,eAAc,aAAa,KAAK,MAAM,EAAE,QAAQ,OAAQ,KAAA;SACxD,WAAU;QACX,GAJM,GAIN,CACF;QACD,oBAAC,QAAD;SAAM,WAAU;SAAsB,IAAI;SAAG,IAAG;SAAO,IAAI,SAAS;SAAK,IAAI,SAAS;QAAM,CAAA;QAC3F,cAAc,oBAAC,QAAD;SAAM,aAAU;SAAO,WAAU;SAAiB,IAAI;SAAG,IAAG;QAAQ,CAAA,IAAI;OACpF;;KACF,CAAA;KACL,qBAAC,OAAD;MAAK,KAAK;MAAQ,eAAY;MAAO,WAAU;MAA/C,UAAA,CACG,MAAM,KAAK,UACV,oBAAC,QAAD;OAAkB,aAAW;OAAO,WAAU;OAC5C,UAAA,oBAAC,QAAD;QAAM,WAAU;QACb,UAAA,YAAY,KAAK;OACd,CAAA;MACF,GAJK,KAIL,CACP,GACA,cACC,oBAAC,QAAD;OAAM,aAAU;OAAa,WAAU;OACrC,UAAA,oBAAC,QAAD;QAAM,aAAU;QAAY,WAAU;OAAwF,CAAA;MAC1H,CAAA,IACJ,IACD;;KACL,qBAAC,OAAD;MAAK,KAAK;MAAM,eAAY;MAAO,WAAU;MAA7C,UAAA,CACG,KAAK,KAAK,SACT,KAAK,aAAa,QAAQ,IAAI,KAAK,GAAG,IACpC,oBAAC,QAAD;OAEE,aAAW,KAAK;OAChB,aAAW,WAAW,KAAK,cAAc;OACzC,cAAY,QAAQ,IAAI,KAAK,GAAG,KAAK;OACrC,WAAU;OAET,UAAA,KAAK;MACF,GAPC,GAAG,KAAK,IAAI,GAAG,KAAK,WAOrB,IACJ,IACN,GACA,oBAAC,QAAD;OAAM,KAAK;OAAO,WAAU;OACzB,UAAA,WACE,MAAM,IAAI,CAAC,CACX,OAAO,OAAO,CAAC,CACf,KAAK,SACJ,oBAAC,QAAD;QAAiB,aAAW;QAAM,WAAU;QACzC,UAAA;OACG,GAFK,IAEL,CACP;MACC,CAAA,CACH;;KAEL,oBAAC,OAAD;MACE,aAAU;MACV,MAAK;MACL,UAAU;MACV,cAAY,GAAG,MAAM,eAAe,cAAc,YAAY;MAC9D,oBAAiB;MACjB,iBAAe;MACf,iBAAe,KAAK,IAAI,GAAG,KAAK,MAAM;MACtC,kBAAgB,SAAS,QAAQ;MACjC,kBAAgB;MAChB,gBAAgB,UAAwC;OACtD,IAAI,MAAM,gBAAgB,SAAS,MAAM,cAAc,oBAAoB,MAAM,SAAS;OAC1F,UAAU,QAAQ,MAAM,OAAO,CAAC;MAClC;MACA,gBAAgB,UAAwC;OACtD,IAAI,MAAM,gBAAgB,WAAW,MAAM,SAAS,UAAU,QAAQ,MAAM,OAAO,CAAC;MACtF;MACA,cAAc,UAAwC;OACpD,IAAI,MAAM,gBAAgB,SAAS,UAAU,IAAI;MACnD;MACA,uBAAuB,UAAU,IAAI;MACrC,iBAAiB,UAAwC;OACvD,IAAI,MAAM,gBAAgB,SAAS,UAAU,IAAI;MACnD;MACW;MACX,cAAc,UAAU,IAAI;MAC5B,UAAU,UAAU;OAClB,IAAI,MAAM,cAAc,QAAQ,gBAAgB,KAAK,QAAQ,GAAG,WAAW,YAAY,WAAW,IAAI;MACxG;MACA,WAAU;KACX,CAAA;IACE;;GACL,oBAAC,OAAD;IAAK,WAAU;IACb,UAAA,qBAAC,SAAD,EAAA,UAAA;KACE,qBAAC,WAAD,EAAA,UAAA;MACG;MAAM;MAAG;KACH,EAAA,CAAA;KACT,oBAAC,SAAD,EAAA,UACE,qBAAC,MAAD,EAAA,UAAA,CACE,oBAAC,MAAD;MAAI,OAAM;MAAO,UAAA;KAAkB,CAAA,GACnC,oBAAC,MAAD;MAAI,OAAM;MAAO,UAAA;KAAU,CAAA,CACzB,EAAA,CAAA,EACC,CAAA;KACP,oBAAC,SAAD,EAAA,UACG,KAAK,KAAK,SACT,qBAAC,MAAD,EAAA,UAAA,CACE,oBAAC,MAAD;MAAI,OAAM;MAAO,UAAA,KAAK;KAAU,CAAA,GAChC,qBAAC,MAAD,EAAA,UAAA,CACG,YAAY,KAAK,KAAK,GACtB,MACC,EAAA,CAAA,CACF,EAAA,GANK,KAAK,GAMV,CACL,EACI,CAAA;IACF,EAAA,CAAA;GACJ,CAAA;GACL,oBAAC,KAAD;IAAG,WAAU;IAAU,aAAU;IAAS,eAAY;IACnD,UAAA,UAAU,GAAG,OAAO,IAAI,aAAa,GAAG,YAAY,KAAK,MAAM,OAAO,CAAC,IAAI,OAAO,KAAK;GACvF,CAAA;EACG;;AAEZ,CAAC;;;;;;;;;;;;;;;;AAiBD,MAAa,WAAW,WACtB,SAAS,SAAS,OAAO,KAAK;CAC5B,OAAO,MAAM,WAAW,KAAA,IACtB,oBAAC,cAAD;EAAmB;EAAK,GAAI;CAAQ,CAAA,IAEpC,oBAAC,gBAAD;EAAqB;EAAK,GAAI;CAAQ,CAAA;AAE1C,CACF"}
@@ -1,2 +1,3 @@
1
+ import { BarChartSeries, BarChartSeriesDatum, BarChartSeriesProps } from "./bar-chart-series.js";
1
2
  import { BarChart, BarChartDatum, BarChartProps } from "./bar-chart.js";
2
- export { BarChart, type BarChartDatum, type BarChartProps };
3
+ export { BarChart, type BarChartDatum, type BarChartProps, type BarChartSeries, type BarChartSeriesDatum, type BarChartSeriesProps };
@@ -3,7 +3,7 @@ import { ReactNode } from "react";
3
3
  import { ButtonProps, LinkProps } from "react-aria-components";
4
4
  //#region src/button/button.d.ts
5
5
  export declare const buttonVariants: (props?: ({
6
- variant?: "link" | "outline" | "primary" | "ai" | "secondary" | "outlineNeutral" | "ghost" | "destructive" | null | undefined;
6
+ variant?: "primary" | "ai" | "secondary" | "outline" | "outlineNeutral" | "ghost" | "destructive" | "link" | null | undefined;
7
7
  size?: "icon" | "sm" | "md" | "lg" | null | undefined;
8
8
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
9
9
  /**
@@ -50,7 +50,7 @@ export declare const calendarClearVariants: (props?: import("class-variance-auth
50
50
  * is a strikethrough.
51
51
  */
52
52
  export declare const calendarCellVariants: (props?: ({
53
- selection?: "single" | "range" | "band" | null | undefined;
53
+ selection?: "range" | "single" | "band" | null | undefined;
54
54
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
55
55
  interface CalendarSharedProps {
56
56
  /**
@@ -28,7 +28,10 @@ import { ComponentPropsWithoutRef } from "react";
28
28
  * source text, so a class assembled at runtime is invisible to it and would
29
29
  * simply never be emitted.
30
30
  */
31
- export declare const cardVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
31
+ export declare const cardVariants: (props?: ({
32
+ flush?: boolean | null | undefined;
33
+ highlighted?: boolean | null | undefined;
34
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
32
35
  /**
33
36
  * Padding is inline-only on the sections and block-only on the shell.
34
37
  *
@@ -60,6 +63,24 @@ export interface CardProps extends ComponentPropsWithoutRef<"div"> {
60
63
  * Leave it off for a card that is not a target: a static card should not move.
61
64
  */
62
65
  interactive?: boolean;
66
+ /**
67
+ * A featured card: a brand gradient edge in place of the hairline. Use it for the
68
+ * one card a view wants the eye to land on (a recommended action, the suggested
69
+ * plan), never for several at once. The edge is decoration, so say WHY the card
70
+ * is featured in its content too: colour alone does not reach a screen reader.
71
+ * @default false
72
+ */
73
+ highlighted?: boolean;
74
+ /**
75
+ * Edge-to-edge content: no shell padding and no gap between the parts, and the
76
+ * content is clipped to the rounded border. The outline, radius, surface and
77
+ * shadow stay. For a table, list or full-bleed chart that must meet the card's
78
+ * top and bottom. The header and footer keep a 24px block inset so their text
79
+ * clears the corner curve, and every section keeps its inline padding: a
80
+ * section's fill already reaches the side edges.
81
+ * @default false
82
+ */
83
+ flush?: boolean;
63
84
  }
64
85
  export declare const Card: import("react").ForwardRefExoticComponent<CardProps & import("react").RefAttributes<HTMLDivElement>>;
65
86
  /** Title and description, stacked. */
package/dist/card/card.js CHANGED
@@ -33,11 +33,59 @@ import { jsx } from "react/jsx-runtime";
33
33
  * simply never be emitted.
34
34
  */
35
35
  const cardVariants = cva([
36
- "flex flex-col gap-6",
37
- "rounded-panel border-(length:--border-width) border-border py-6",
36
+ "flex flex-col",
37
+ "rounded-panel border-(length:--border-width) border-border",
38
38
  "bg-card text-card-foreground shadow-surface",
39
39
  "box-border font-sans"
40
- ]);
40
+ ], {
41
+ variants: {
42
+ /**
43
+ * The block padding and the gap live here, not in the base, so `flush`
44
+ * REPLACES them rather than relying on a merge to drop them: the
45
+ * exported `cardVariants({ flush: true })` is correct on its own.
46
+ *
47
+ * Flush keeps the outline, radius, surface and shadow, and clips its
48
+ * content to the inside of the rounded border: without the clip a filled
49
+ * child (a table header, a chart) shows square corners over the curve and
50
+ * covers the left and right hairline. `overflow: clip`, not `hidden`, so
51
+ * the card does not become a scroll container and a sticky table header
52
+ * inside it still sticks to the page. The sections keep their inline
53
+ * padding, so text and a section's fill stay clear of the corner curve
54
+ * (the fill still meets the side edges: background paints the padding).
55
+ *
56
+ * The header and footer hold text, so under flush they keep the 24px
57
+ * block inset the shell gave up. Without it the title and the footer line
58
+ * sat against the top and bottom borders, inside the corner curve, and
59
+ * the clip cut their first glyphs. The inset is a custom property both
60
+ * sections read, set per variant (never left undefined), so a nested
61
+ * default card resets it, and a section's own `py-*` still wins by merge.
62
+ * CardContent stays block-flush: a table or chart meets the edges.
63
+ */
64
+ flush: {
65
+ false: "gap-6 py-6 [--card-section-py:--spacing(0)]",
66
+ true: "gap-0 py-0 overflow-clip [--card-section-py:--spacing(6)]"
67
+ },
68
+ /**
69
+ * A featured card: the hairline is replaced by a brand gradient edge,
70
+ * painted by the `card-highlighted` utility in styles.css. The border
71
+ * stays (transparent), so a highlighted card has exactly the box of a
72
+ * plain one and a list of mixed cards does not shift.
73
+ */
74
+ highlighted: {
75
+ false: "",
76
+ true: "card-highlighted border-transparent"
77
+ }
78
+ },
79
+ compoundVariants: [{
80
+ flush: true,
81
+ highlighted: true,
82
+ class: "card-highlighted-flush"
83
+ }],
84
+ defaultVariants: {
85
+ flush: false,
86
+ highlighted: false
87
+ }
88
+ });
41
89
  /**
42
90
  * Padding is inline-only on the sections and block-only on the shell.
43
91
  *
@@ -45,18 +93,20 @@ const cardVariants = cva([
45
93
  * consumer run a divider or a full-bleed image edge to edge, which a single
46
94
  * `p-6` on the shell would make impossible.
47
95
  */
48
- const cardHeaderVariants = cva("flex flex-col gap-1.5 px-6");
96
+ const cardHeaderVariants = cva("flex flex-col gap-1.5 px-6 py-(--card-section-py)");
49
97
  const cardTitleVariants = cva(["text-base leading-(--leading-none) font-semibold", "m-0"]);
50
98
  const cardDescriptionVariants = cva(["text-sm leading-(--leading-normal) text-muted-foreground"]);
51
99
  const cardContentVariants = cva("px-6");
52
- const cardFooterVariants = cva("flex items-center px-6");
53
- const Card = forwardRef(function Card({ className, interactive = false, onPointerEnter, onPointerLeave, ...props }, ref) {
100
+ const cardFooterVariants = cva("flex items-center px-6 py-(--card-section-py)");
101
+ const Card = forwardRef(function Card({ className, interactive = false, highlighted = false, flush = false, onPointerEnter, onPointerLeave, ...props }, ref) {
54
102
  const [pointed, setPointed] = useState(false);
55
103
  return /* @__PURE__ */ jsx("div", {
56
104
  ref,
57
105
  ...props,
58
106
  "data-slot": "card",
59
107
  "data-interactive": interactive || void 0,
108
+ "data-highlighted": highlighted || void 0,
109
+ "data-flush": flush || void 0,
60
110
  "data-pointed": interactive && pointed || void 0,
61
111
  onPointerEnter: (event) => {
62
112
  onPointerEnter?.(event);
@@ -66,7 +116,10 @@ const Card = forwardRef(function Card({ className, interactive = false, onPointe
66
116
  onPointerLeave?.(event);
67
117
  setPointed(false);
68
118
  },
69
- className: cnAll(cardVariants(), interactive && "card-lift", className)
119
+ className: cnAll(cardVariants({
120
+ flush,
121
+ highlighted
122
+ }), interactive && "card-lift", className)
70
123
  });
71
124
  });
72
125
  const CardHeader = forwardRef(function CardHeader({ className, ...props }, ref) {
@@ -1 +1 @@
1
- {"version":3,"file":"card.js","names":[],"sources":["../../src/card/card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useState, type ComponentPropsWithoutRef } from \"react\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\n\n/**\n * Card, ported from the legacy shadcn component.\n *\n * Presentational, so there is no React Aria base -- React Aria ships no card,\n * and a container that is not focusable, selectable or interactive needs no\n * behaviour to inherit.\n *\n * Five parts, from six. The census (24 consumer files, the most-used\n * container in the app) reads: Card 55, CardContent 40, CardHeader 21,\n * CardTitle 19, CardDescription 8, CardFooter 2, **CardAction 0**.\n *\n * Dropping `CardAction` takes three things with it, which is most of the\n * legacy header's complexity: the `grid`, the `grid-rows-[auto_auto]`, and\n * the `has-data-[slot=card-action]:grid-cols-[1fr_auto]` rule that existed\n * only to reserve a column for it. With no action to place, a header is a\n * stack.\n *\n * Two more legacy rules are gone. `[.border-b]:pb-6` and `[.border-t]:pt-6`\n * added padding when the CONSUMER happened to add a border class -- clever,\n * but action at a distance through a class name we do not own, and the census\n * found zero application call sites relying on it (one call site passes\n * `border-b-0`, which removes a border it never had). `@container/card-header`\n * named a container nothing queried.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const cardVariants = cva([\n \"flex flex-col gap-6\",\n \"rounded-panel border-(length:--border-width) border-border py-6\",\n \"bg-card text-card-foreground shadow-surface\",\n\n // box-border and font-sans are not boilerplate. The reset in\n // @seekalfred/styles is OPT-IN, so a consumer may load none: content-box\n // would add the padding to any width they set (the Alert shipped\n // overflowing its container by 46px), and an unstyled document renders\n // text in a serif (the Accordion shipped its panel that way). Declaring\n // the family HERE rather than on each part means one declaration covers\n // the whole subtree by inheritance.\n \"box-border font-sans\",\n]);\n\n/**\n * Padding is inline-only on the sections and block-only on the shell.\n *\n * That split is load-bearing rather than stylistic: it is what lets a\n * consumer run a divider or a full-bleed image edge to edge, which a single\n * `p-6` on the shell would make impossible.\n */\nexport const cardHeaderVariants = cva(\"flex flex-col gap-1.5 px-6\");\n\nexport const cardTitleVariants = cva([\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"text-base leading-(--leading-none) font-semibold\",\n // The user-agent <h3> ships a 1em block margin and font-size 1.17em. The\n // legacy component was a <div> and never met either.\n \"m-0\",\n]);\n\nexport const cardDescriptionVariants = cva([\n \"text-sm leading-(--leading-normal) text-muted-foreground\",\n]);\n\nexport const cardContentVariants = cva(\"px-6\");\n\nexport const cardFooterVariants = cva(\"flex items-center px-6\");\n\n/**\n * A content container.\n *\n * @example\n * <Card>\n * <CardHeader>\n * <CardTitle>Learning Progress</CardTitle>\n * <CardDescription>Across all active courses.</CardDescription>\n * </CardHeader>\n * <CardContent>Twelve of eighteen modules complete.</CardContent>\n * </Card>\n */\nexport interface CardProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Make the card respond to a pointer: it floats 2px on a spring while it is pointed\n * at, its border darkens and its shadow deepens. Mouse and pen only; touch never lifts.\n * Leave it off for a card that is not a target: a static card should not move.\n */\n interactive?: boolean;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n { className, interactive = false, onPointerEnter, onPointerLeave, ...props },\n ref,\n) {\n // Pointed at by a mouse or pen. Touch never lifts, so it is read from the pointer, not :hover (which a tap leaves stuck).\n const [pointed, setPointed] = useState(false);\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card\"\n data-interactive={interactive || undefined}\n data-pointed={(interactive && pointed) || undefined}\n onPointerEnter={(event) => {\n onPointerEnter?.(event);\n if (interactive && event.pointerType !== \"touch\") setPointed(true);\n }}\n onPointerLeave={(event) => {\n onPointerLeave?.(event);\n setPointed(false);\n }}\n className={cnAll(cardVariants(), interactive && \"card-lift\", className)}\n />\n );\n});\n\n/** Title and description, stacked. */\nexport type CardHeaderProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(function CardHeader(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-header\"\n className={cn(cardHeaderVariants(), className)}\n />\n );\n});\n\n/**\n * The card's heading.\n *\n * A REAL heading, unlike the legacy `<div class=\"font-semibold\">`. None of\n * the 19 legacy call sites wrapped one, so 19 card titles were invisible to\n * screen-reader heading navigation -- the same shape of finding as the Avatar\n * port's missing `alt`, and the migration is when it gets answered.\n */\nexport interface CardTitleProps extends ComponentPropsWithoutRef<\"h3\"> {\n /**\n * Heading level.\n *\n * `3` by default, because a card almost always sits under a section\n * heading. It is a prop rather than a constant because the correct level\n * is a fact about the PAGE, and a fixed one would be wrong somewhere; the\n * level changes semantics only, never the rendered size.\n *\n * @default 3\n */\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nexport const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(function CardTitle(\n { className, level = 3, ...props },\n ref,\n) {\n // Static union, not a template literal: `h${level}` is a string React\n // accepts but TypeScript cannot narrow to an intrinsic element, and the\n // cast that silences it would also accept `h7`.\n const Heading = ({ 1: \"h1\", 2: \"h2\", 3: \"h3\", 4: \"h4\", 5: \"h5\", 6: \"h6\" } as const)[level];\n\n return (\n <Heading\n ref={ref}\n {...props}\n data-slot=\"card-title\"\n className={cn(cardTitleVariants(), className)}\n />\n );\n});\n\n/** Secondary text under the title. */\nexport type CardDescriptionProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardDescription = forwardRef<HTMLDivElement, CardDescriptionProps>(\n function CardDescription({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-description\"\n className={cn(cardDescriptionVariants(), className)}\n />\n );\n },\n);\n\n/** The card's body. */\nexport type CardContentProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardContent = forwardRef<HTMLDivElement, CardContentProps>(function CardContent(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-content\"\n className={cn(cardContentVariants(), className)}\n />\n );\n});\n\n/** Actions or metadata at the foot of the card. */\nexport type CardFooterProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(function CardFooter(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-footer\"\n className={cn(cardFooterVariants(), className)}\n />\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,eAAe,IAAI;CAC9B;CACA;CACA;CASA;AACF,CAAC;;;;;;;;AASD,MAAa,qBAAqB,IAAI,4BAA4B;AAElE,MAAa,oBAAoB,IAAI,CAGnC,oDAGA,KACF,CAAC;AAED,MAAa,0BAA0B,IAAI,CACzC,0DACF,CAAC;AAED,MAAa,sBAAsB,IAAI,MAAM;AAE7C,MAAa,qBAAqB,IAAI,wBAAwB;AAuB9D,MAAa,OAAO,WAAsC,SAAS,KACjE,EAAE,WAAW,cAAc,OAAO,gBAAgB,gBAAgB,GAAG,SACrE,KACA;CAEA,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,oBAAkB,eAAe,KAAA;EACjC,gBAAe,eAAe,WAAY,KAAA;EAC1C,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,IAAI,eAAe,MAAM,gBAAgB,SAAS,WAAW,IAAI;EACnE;EACA,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,WAAW,KAAK;EAClB;EACA,WAAW,MAAM,aAAa,GAAG,eAAe,aAAa,SAAS;CACvE,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC;AAwBD,MAAa,YAAY,WAA+C,SAAS,UAC/E,EAAE,WAAW,QAAQ,GAAG,GAAG,SAC3B,KACA;CAIA,MAAM,UAAW;EAAE,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;CAAK,EAAY;CAEpF,OACE,oBAAC,SAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,kBAAkB,GAAG,SAAS;CAC7C,CAAA;AAEL,CAAC;AAKD,MAAa,kBAAkB,WAC7B,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAS,KAAK;CACrD,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,wBAAwB,GAAG,SAAS;CACnD,CAAA;AAEL,CACF;AAKA,MAAa,cAAc,WAA6C,SAAS,YAC/E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;CAC/C,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC"}
1
+ {"version":3,"file":"card.js","names":[],"sources":["../../src/card/card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useState, type ComponentPropsWithoutRef } from \"react\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\n\n/**\n * Card, ported from the legacy shadcn component.\n *\n * Presentational, so there is no React Aria base -- React Aria ships no card,\n * and a container that is not focusable, selectable or interactive needs no\n * behaviour to inherit.\n *\n * Five parts, from six. The census (24 consumer files, the most-used\n * container in the app) reads: Card 55, CardContent 40, CardHeader 21,\n * CardTitle 19, CardDescription 8, CardFooter 2, **CardAction 0**.\n *\n * Dropping `CardAction` takes three things with it, which is most of the\n * legacy header's complexity: the `grid`, the `grid-rows-[auto_auto]`, and\n * the `has-data-[slot=card-action]:grid-cols-[1fr_auto]` rule that existed\n * only to reserve a column for it. With no action to place, a header is a\n * stack.\n *\n * Two more legacy rules are gone. `[.border-b]:pb-6` and `[.border-t]:pt-6`\n * added padding when the CONSUMER happened to add a border class -- clever,\n * but action at a distance through a class name we do not own, and the census\n * found zero application call sites relying on it (one call site passes\n * `border-b-0`, which removes a border it never had). `@container/card-header`\n * named a container nothing queried.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const cardVariants = cva(\n [\n \"flex flex-col\",\n \"rounded-panel border-(length:--border-width) border-border\",\n \"bg-card text-card-foreground shadow-surface\",\n\n // box-border and font-sans are not boilerplate. The reset in\n // @seekalfred/styles is OPT-IN, so a consumer may load none: content-box\n // would add the padding to any width they set (the Alert shipped\n // overflowing its container by 46px), and an unstyled document renders\n // text in a serif (the Accordion shipped its panel that way). Declaring\n // the family HERE rather than on each part means one declaration covers\n // the whole subtree by inheritance.\n \"box-border font-sans\",\n ],\n {\n variants: {\n /**\n * The block padding and the gap live here, not in the base, so `flush`\n * REPLACES them rather than relying on a merge to drop them: the\n * exported `cardVariants({ flush: true })` is correct on its own.\n *\n * Flush keeps the outline, radius, surface and shadow, and clips its\n * content to the inside of the rounded border: without the clip a filled\n * child (a table header, a chart) shows square corners over the curve and\n * covers the left and right hairline. `overflow: clip`, not `hidden`, so\n * the card does not become a scroll container and a sticky table header\n * inside it still sticks to the page. The sections keep their inline\n * padding, so text and a section's fill stay clear of the corner curve\n * (the fill still meets the side edges: background paints the padding).\n *\n * The header and footer hold text, so under flush they keep the 24px\n * block inset the shell gave up. Without it the title and the footer line\n * sat against the top and bottom borders, inside the corner curve, and\n * the clip cut their first glyphs. The inset is a custom property both\n * sections read, set per variant (never left undefined), so a nested\n * default card resets it, and a section's own `py-*` still wins by merge.\n * CardContent stays block-flush: a table or chart meets the edges.\n */\n flush: {\n false: \"gap-6 py-6 [--card-section-py:--spacing(0)]\",\n true: \"gap-0 py-0 overflow-clip [--card-section-py:--spacing(6)]\",\n },\n /**\n * A featured card: the hairline is replaced by a brand gradient edge,\n * painted by the `card-highlighted` utility in styles.css. The border\n * stays (transparent), so a highlighted card has exactly the box of a\n * plain one and a list of mixed cards does not shift.\n */\n highlighted: {\n false: \"\",\n true: \"card-highlighted border-transparent\",\n },\n },\n compoundVariants: [\n // The flush clip would cut away the gradient edge, which is painted in\n // the border area outside the padding box. See card-highlighted-flush.\n { flush: true, highlighted: true, class: \"card-highlighted-flush\" },\n ],\n defaultVariants: { flush: false, highlighted: false },\n },\n);\n\n/**\n * Padding is inline-only on the sections and block-only on the shell.\n *\n * That split is load-bearing rather than stylistic: it is what lets a\n * consumer run a divider or a full-bleed image edge to edge, which a single\n * `p-6` on the shell would make impossible.\n */\nexport const cardHeaderVariants = cva(\"flex flex-col gap-1.5 px-6 py-(--card-section-py)\");\n\nexport const cardTitleVariants = cva([\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"text-base leading-(--leading-none) font-semibold\",\n // The user-agent <h3> ships a 1em block margin and font-size 1.17em. The\n // legacy component was a <div> and never met either.\n \"m-0\",\n]);\n\nexport const cardDescriptionVariants = cva([\n \"text-sm leading-(--leading-normal) text-muted-foreground\",\n]);\n\nexport const cardContentVariants = cva(\"px-6\");\n\nexport const cardFooterVariants = cva(\"flex items-center px-6 py-(--card-section-py)\");\n\n/**\n * A content container.\n *\n * @example\n * <Card>\n * <CardHeader>\n * <CardTitle>Learning Progress</CardTitle>\n * <CardDescription>Across all active courses.</CardDescription>\n * </CardHeader>\n * <CardContent>Twelve of eighteen modules complete.</CardContent>\n * </Card>\n */\nexport interface CardProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Make the card respond to a pointer: it floats 2px on a spring while it is pointed\n * at, its border darkens and its shadow deepens. Mouse and pen only; touch never lifts.\n * Leave it off for a card that is not a target: a static card should not move.\n */\n interactive?: boolean;\n /**\n * A featured card: a brand gradient edge in place of the hairline. Use it for the\n * one card a view wants the eye to land on (a recommended action, the suggested\n * plan), never for several at once. The edge is decoration, so say WHY the card\n * is featured in its content too: colour alone does not reach a screen reader.\n * @default false\n */\n highlighted?: boolean;\n /**\n * Edge-to-edge content: no shell padding and no gap between the parts, and the\n * content is clipped to the rounded border. The outline, radius, surface and\n * shadow stay. For a table, list or full-bleed chart that must meet the card's\n * top and bottom. The header and footer keep a 24px block inset so their text\n * clears the corner curve, and every section keeps its inline padding: a\n * section's fill already reaches the side edges.\n * @default false\n */\n flush?: boolean;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n {\n className,\n interactive = false,\n highlighted = false,\n flush = false,\n onPointerEnter,\n onPointerLeave,\n ...props\n },\n ref,\n) {\n // Pointed at by a mouse or pen. Touch never lifts, so it is read from the pointer, not :hover (which a tap leaves stuck).\n const [pointed, setPointed] = useState(false);\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card\"\n data-interactive={interactive || undefined}\n data-highlighted={highlighted || undefined}\n data-flush={flush || undefined}\n data-pointed={(interactive && pointed) || undefined}\n onPointerEnter={(event) => {\n onPointerEnter?.(event);\n if (interactive && event.pointerType !== \"touch\") setPointed(true);\n }}\n onPointerLeave={(event) => {\n onPointerLeave?.(event);\n setPointed(false);\n }}\n className={cnAll(cardVariants({ flush, highlighted }), interactive && \"card-lift\", className)}\n />\n );\n});\n\n/** Title and description, stacked. */\nexport type CardHeaderProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(function CardHeader(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-header\"\n className={cn(cardHeaderVariants(), className)}\n />\n );\n});\n\n/**\n * The card's heading.\n *\n * A REAL heading, unlike the legacy `<div class=\"font-semibold\">`. None of\n * the 19 legacy call sites wrapped one, so 19 card titles were invisible to\n * screen-reader heading navigation -- the same shape of finding as the Avatar\n * port's missing `alt`, and the migration is when it gets answered.\n */\nexport interface CardTitleProps extends ComponentPropsWithoutRef<\"h3\"> {\n /**\n * Heading level.\n *\n * `3` by default, because a card almost always sits under a section\n * heading. It is a prop rather than a constant because the correct level\n * is a fact about the PAGE, and a fixed one would be wrong somewhere; the\n * level changes semantics only, never the rendered size.\n *\n * @default 3\n */\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nexport const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(function CardTitle(\n { className, level = 3, ...props },\n ref,\n) {\n // Static union, not a template literal: `h${level}` is a string React\n // accepts but TypeScript cannot narrow to an intrinsic element, and the\n // cast that silences it would also accept `h7`.\n const Heading = ({ 1: \"h1\", 2: \"h2\", 3: \"h3\", 4: \"h4\", 5: \"h5\", 6: \"h6\" } as const)[level];\n\n return (\n <Heading\n ref={ref}\n {...props}\n data-slot=\"card-title\"\n className={cn(cardTitleVariants(), className)}\n />\n );\n});\n\n/** Secondary text under the title. */\nexport type CardDescriptionProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardDescription = forwardRef<HTMLDivElement, CardDescriptionProps>(\n function CardDescription({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-description\"\n className={cn(cardDescriptionVariants(), className)}\n />\n );\n },\n);\n\n/** The card's body. */\nexport type CardContentProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardContent = forwardRef<HTMLDivElement, CardContentProps>(function CardContent(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-content\"\n className={cn(cardContentVariants(), className)}\n />\n );\n});\n\n/** Actions or metadata at the foot of the card. */\nexport type CardFooterProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(function CardFooter(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-footer\"\n className={cn(cardFooterVariants(), className)}\n />\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,eAAe,IAC1B;CACE;CACA;CACA;CASA;AACF,GACA;CACE,UAAU;;;;;;;;;;;;;;;;;;;;;;;EAuBR,OAAO;GACL,OAAO;GACP,MAAM;EACR;;;;;;;EAOA,aAAa;GACX,OAAO;GACP,MAAM;EACR;CACF;CACA,kBAAkB,CAGhB;EAAE,OAAO;EAAM,aAAa;EAAM,OAAO;CAAyB,CACpE;CACA,iBAAiB;EAAE,OAAO;EAAO,aAAa;CAAM;AACtD,CACF;;;;;;;;AASA,MAAa,qBAAqB,IAAI,mDAAmD;AAEzF,MAAa,oBAAoB,IAAI,CAGnC,oDAGA,KACF,CAAC;AAED,MAAa,0BAA0B,IAAI,CACzC,0DACF,CAAC;AAED,MAAa,sBAAsB,IAAI,MAAM;AAE7C,MAAa,qBAAqB,IAAI,+CAA+C;AAyCrF,MAAa,OAAO,WAAsC,SAAS,KACjE,EACE,WACA,cAAc,OACd,cAAc,OACd,QAAQ,OACR,gBACA,gBACA,GAAG,SAEL,KACA;CAEA,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,oBAAkB,eAAe,KAAA;EACjC,oBAAkB,eAAe,KAAA;EACjC,cAAY,SAAS,KAAA;EACrB,gBAAe,eAAe,WAAY,KAAA;EAC1C,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,IAAI,eAAe,MAAM,gBAAgB,SAAS,WAAW,IAAI;EACnE;EACA,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,WAAW,KAAK;EAClB;EACA,WAAW,MAAM,aAAa;GAAE;GAAO;EAAY,CAAC,GAAG,eAAe,aAAa,SAAS;CAC7F,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC;AAwBD,MAAa,YAAY,WAA+C,SAAS,UAC/E,EAAE,WAAW,QAAQ,GAAG,GAAG,SAC3B,KACA;CAIA,MAAM,UAAW;EAAE,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;CAAK,EAAY;CAEpF,OACE,oBAAC,SAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,kBAAkB,GAAG,SAAS;CAC7C,CAAA;AAEL,CAAC;AAKD,MAAa,kBAAkB,WAC7B,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAS,KAAK;CACrD,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,wBAAwB,GAAG,SAAS;CACnD,CAAA;AAEL,CACF;AAKA,MAAa,cAAc,WAA6C,SAAS,YAC/E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;CAC/C,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC"}
@@ -16,6 +16,12 @@ import { ReactNode } from "react";
16
16
  * rotation pauses on hover, keyboard focus and touch, while the carousel is off
17
17
  * screen or the tab is hidden, and is unavailable under reduced motion (where
18
18
  * scrolling also jumps instead of gliding).
19
+ *
20
+ * `loop` wraps the ends: next on the last slide goes to the first, previous on
21
+ * the first to the last, from the controls and the arrow keys alike (a swipe
22
+ * or drag still stops at the end of the track). `peek={false}` shows one slide
23
+ * at a time across the full width, with no neighbours in view, for a viewer
24
+ * such as a single creative asset.
19
25
  */
20
26
  export interface CarouselProps {
21
27
  /** Names the carousel for assistive technology, such as "From the Harbour suites". */
@@ -35,6 +41,21 @@ export interface CarouselProps {
35
41
  interval?: number;
36
42
  /** Starts rotating on mount. Off by default, so motion begins with the viewer. @default false */
37
43
  autoplay?: boolean;
44
+ /**
45
+ * Wraps around at the ends: next on the last slide goes to the first and
46
+ * previous on the first goes to the last, from the controls and the arrow
47
+ * keys. The controls are then never disabled. Home and End still go to the
48
+ * first and last slide; a swipe or drag stops at the end of the track.
49
+ * @default false
50
+ */
51
+ loop?: boolean;
52
+ /**
53
+ * Neighbouring slides peek in at the sides. `false` shows one slide at a
54
+ * time across the full width (`slideSize` is ignored), with the controls
55
+ * spanning it too.
56
+ * @default true
57
+ */
58
+ peek?: boolean;
38
59
  /** Names of the controls, for translation. */
39
60
  labels?: {
40
61
  /** @default "Previous slide" */
@@ -9,6 +9,15 @@ import { jsx, jsxs } from "react/jsx-runtime";
9
9
  const SLOP = 6;
10
10
  /** Release speed in px/s that always moves at least one slide. */
11
11
  const FLICK = 360;
12
+ /**
13
+ * How long a glide may take before the carousel stops waiting for it: one slide,
14
+ * plus more per extra slide (a `loop` wrap crosses them all), capped.
15
+ */
16
+ const BACKSTOP = {
17
+ base: 1e3,
18
+ perSlide: 250,
19
+ max: 3e3
20
+ };
12
21
  /** Where a flick would come to rest: velocity (px/s) projected with a paging deceleration rate. */
13
22
  const project = (velocity, rate = .995) => velocity / 1e3 * rate / (1 - rate);
14
23
  const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
@@ -53,7 +62,7 @@ const Chevron = ({ back }) => /* @__PURE__ */ jsx("svg", {
53
62
  "aria-hidden": "true",
54
63
  children: /* @__PURE__ */ jsx("path", { d: back ? "M10 3.5 5.5 8l4.5 4.5" : "M6 3.5 10.5 8 6 12.5" })
55
64
  });
56
- const Carousel = forwardRef(function Carousel({ label, children, index: controlledIndex, defaultIndex = 0, onIndexChange, slideSize, slideLabel = defaultSlideLabel, interval, autoplay = false, labels, className }, ref) {
65
+ const Carousel = forwardRef(function Carousel({ label, children, index: controlledIndex, defaultIndex = 0, onIndexChange, slideSize, slideLabel = defaultSlideLabel, interval, autoplay = false, loop = false, peek = true, labels, className }, ref) {
57
66
  const slides = Children.toArray(children);
58
67
  const count = slides.length;
59
68
  const last = Math.max(0, count - 1);
@@ -121,7 +130,9 @@ const Carousel = forwardRef(function Carousel({ label, children, index: controll
121
130
  }
122
131
  travelling.current = i;
123
132
  clearTimeout(backstop.current);
124
- backstop.current = setTimeout(() => travelling.current = null, 1e3);
133
+ const distance = Math.abs(box.scrollLeft - left) / (step() || 1);
134
+ const wait = Math.min(BACKSTOP.max, BACKSTOP.base + BACKSTOP.perSlide * Math.max(0, distance - 1));
135
+ backstop.current = setTimeout(() => travelling.current = null, wait);
125
136
  box.scrollTo({
126
137
  left,
127
138
  behavior: "smooth"
@@ -158,6 +169,17 @@ const Carousel = forwardRef(function Carousel({ label, children, index: controll
158
169
  }
159
170
  }, [index]);
160
171
  useEffect(() => () => clearTimeout(backstop.current), []);
172
+ useEffect(() => {
173
+ const box = viewport.current;
174
+ if (!box) return;
175
+ const onScrollEnd = () => {
176
+ if (travelling.current === null) return;
177
+ travelling.current = null;
178
+ clearTimeout(backstop.current);
179
+ };
180
+ box.addEventListener("scrollend", onScrollEnd);
181
+ return () => box.removeEventListener("scrollend", onScrollEnd);
182
+ }, []);
161
183
  function onScroll() {
162
184
  const position = paint();
163
185
  const box = viewport.current;
@@ -170,6 +192,8 @@ const Carousel = forwardRef(function Carousel({ label, children, index: controll
170
192
  if (next !== target.current) commit(next, true);
171
193
  }
172
194
  const advance = () => select(index >= last ? 0 : index + 1, false);
195
+ /** The slide `by` steps from `from`: clamped at the ends, or around them with `loop`. */
196
+ const stepFrom = (from, by) => loop ? (from + by + count) % count : clamp(from + by, 0, last);
173
197
  useEffect(() => {
174
198
  const node = root.current;
175
199
  if (!interval || !node) return;
@@ -251,7 +275,7 @@ const Carousel = forwardRef(function Carousel({ label, children, index: controll
251
275
  const node = event.target;
252
276
  if (node.closest("input, textarea, select, [contenteditable]")) return;
253
277
  const from = target.current;
254
- const to = event.key === "ArrowLeft" ? from - 1 : event.key === "ArrowRight" ? from + 1 : event.key === "Home" ? 0 : event.key === "End" ? last : null;
278
+ const to = event.key === "ArrowLeft" ? stepFrom(from, -1) : event.key === "ArrowRight" ? stepFrom(from, 1) : event.key === "Home" ? 0 : event.key === "End" ? last : null;
255
279
  if (to === null) return;
256
280
  event.preventDefault();
257
281
  const next = clamp(to, 0, last);
@@ -272,8 +296,10 @@ const Carousel = forwardRef(function Carousel({ label, children, index: controll
272
296
  "data-slot": "carousel",
273
297
  "aria-roledescription": "carousel",
274
298
  "aria-label": label,
299
+ "data-loop": loop || void 0,
300
+ "data-peek": peek ? void 0 : "false",
275
301
  className: cn("carousel", className),
276
- style: slideSize ? { "--carousel-slide-size": slideSize } : void 0,
302
+ style: slideSize && peek ? { "--carousel-slide-size": slideSize } : void 0,
277
303
  onKeyDown,
278
304
  onFocus: (event) => setFocusPaused(event.target !== play.current && event.target.matches(":focus-visible")),
279
305
  onBlur: (event) => {
@@ -359,16 +385,16 @@ const Carousel = forwardRef(function Carousel({ label, children, index: controll
359
385
  children: [/* @__PURE__ */ jsx("button", {
360
386
  type: "button",
361
387
  "aria-label": text.previous,
362
- "aria-disabled": index === 0,
388
+ "aria-disabled": !loop && index === 0,
363
389
  className: "carousel-control",
364
- onClick: () => target.current > 0 && select(target.current - 1),
390
+ onClick: () => (loop || target.current > 0) && select(stepFrom(target.current, -1)),
365
391
  children: /* @__PURE__ */ jsx(Chevron, { back: true })
366
392
  }), /* @__PURE__ */ jsx("button", {
367
393
  type: "button",
368
394
  "aria-label": text.next,
369
- "aria-disabled": index === last,
395
+ "aria-disabled": !loop && index === last,
370
396
  className: "carousel-control",
371
- onClick: () => target.current < last && select(target.current + 1),
397
+ onClick: () => (loop || target.current < last) && select(stepFrom(target.current, 1)),
372
398
  children: /* @__PURE__ */ jsx(Chevron, {})
373
399
  })]
374
400
  })