@seekalfred/primitives 0.7.1 → 0.7.3

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 (101) hide show
  1. package/README.md +195 -4
  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 +17 -4
  8. package/dist/alert-dialog/alert-dialog.js +26 -2
  9. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  10. package/dist/badge/badge.d.ts +1 -1
  11. package/dist/button/button.d.ts +2 -2
  12. package/dist/calendar/calendar.d.ts +1 -1
  13. package/dist/card/card.d.ts +22 -1
  14. package/dist/card/card.js +60 -7
  15. package/dist/card/card.js.map +1 -1
  16. package/dist/carousel/carousel.d.ts +21 -0
  17. package/dist/carousel/carousel.js +34 -8
  18. package/dist/carousel/carousel.js.map +1 -1
  19. package/dist/copy-button/copy-button.d.ts +18 -2
  20. package/dist/copy-button/copy-button.js +29 -8
  21. package/dist/copy-button/copy-button.js.map +1 -1
  22. package/dist/donut-chart/donut-chart.d.ts +22 -0
  23. package/dist/donut-chart/donut-chart.js +35 -18
  24. package/dist/donut-chart/donut-chart.js.map +1 -1
  25. package/dist/donut-chart/index.d.ts +2 -2
  26. package/dist/empty-state/empty-state.d.ts +21 -0
  27. package/dist/empty-state/empty-state.js +16 -4
  28. package/dist/empty-state/empty-state.js.map +1 -1
  29. package/dist/empty-state/index.d.ts +2 -2
  30. package/dist/empty-state/index.js +2 -2
  31. package/dist/filter-bar/filter-bar.d.ts +4 -1
  32. package/dist/filter-bar/filter-bar.js +53 -9
  33. package/dist/filter-bar/filter-bar.js.map +1 -1
  34. package/dist/index.d.ts +10 -10
  35. package/dist/index.js +7 -7
  36. package/dist/input/input.js +4 -4
  37. package/dist/input/input.js.map +1 -1
  38. package/dist/lib/field-shell.js +10 -3
  39. package/dist/lib/field-shell.js.map +1 -1
  40. package/dist/lib/field-surface.js +22 -1
  41. package/dist/lib/field-surface.js.map +1 -1
  42. package/dist/lib/use-copy-feedback.js +17 -3
  43. package/dist/lib/use-copy-feedback.js.map +1 -1
  44. package/dist/line-chart/index.d.ts +2 -2
  45. package/dist/line-chart/line-chart.d.ts +42 -2
  46. package/dist/line-chart/line-chart.js +143 -14
  47. package/dist/line-chart/line-chart.js.map +1 -1
  48. package/dist/money-input/money-input.d.ts +22 -1
  49. package/dist/money-input/money-input.js +13 -7
  50. package/dist/money-input/money-input.js.map +1 -1
  51. package/dist/morph-select/morph-select.d.ts +19 -1
  52. package/dist/morph-select/morph-select.js +21 -2
  53. package/dist/morph-select/morph-select.js.map +1 -1
  54. package/dist/multi-select/index.d.ts +2 -2
  55. package/dist/multi-select/index.js +2 -2
  56. package/dist/multi-select/multi-select.d.ts +43 -2
  57. package/dist/multi-select/multi-select.js +93 -36
  58. package/dist/multi-select/multi-select.js.map +1 -1
  59. package/dist/password-field/password-field.d.ts +16 -0
  60. package/dist/password-field/password-field.js +37 -8
  61. package/dist/password-field/password-field.js.map +1 -1
  62. package/dist/popover/index.d.ts +2 -2
  63. package/dist/popover/index.js +2 -2
  64. package/dist/popover/popover.d.ts +39 -2
  65. package/dist/popover/popover.js +29 -2
  66. package/dist/popover/popover.js.map +1 -1
  67. package/dist/progress/progress.d.ts +16 -2
  68. package/dist/progress/progress.js +15 -8
  69. package/dist/progress/progress.js.map +1 -1
  70. package/dist/radio-group/index.d.ts +2 -2
  71. package/dist/radio-group/index.js +2 -2
  72. package/dist/radio-group/radio-group.d.ts +33 -3
  73. package/dist/radio-group/radio-group.js +109 -9
  74. package/dist/radio-group/radio-group.js.map +1 -1
  75. package/dist/scroll-area/scroll-area.js +4 -3
  76. package/dist/scroll-area/scroll-area.js.map +1 -1
  77. package/dist/search-field/search-field.d.ts +22 -1
  78. package/dist/search-field/search-field.js +22 -7
  79. package/dist/search-field/search-field.js.map +1 -1
  80. package/dist/select/select.d.ts +1 -1
  81. package/dist/select/select.js +8 -6
  82. package/dist/select/select.js.map +1 -1
  83. package/dist/separator/separator.d.ts +21 -9
  84. package/dist/separator/separator.js +33 -11
  85. package/dist/separator/separator.js.map +1 -1
  86. package/dist/stat-card/index.d.ts +2 -2
  87. package/dist/stat-card/index.js +2 -2
  88. package/dist/stat-card/stat-card.d.ts +34 -1
  89. package/dist/stat-card/stat-card.js +51 -21
  90. package/dist/stat-card/stat-card.js.map +1 -1
  91. package/dist/styles.css +1 -1
  92. package/dist/table/table.d.ts +1 -1
  93. package/dist/time-picker/time-picker.d.ts +36 -1
  94. package/dist/time-picker/time-picker.js +33 -5
  95. package/dist/time-picker/time-picker.js.map +1 -1
  96. package/dist/toast/toast.d.ts +2 -2
  97. package/dist/usage-meter/index.d.ts +2 -2
  98. package/dist/usage-meter/usage-meter.d.ts +30 -0
  99. package/dist/usage-meter/usage-meter.js +56 -38
  100. package/dist/usage-meter/usage-meter.js.map +1 -1
  101. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"donut-chart.js","names":[],"sources":["../../src/donut-chart/donut-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type MouseEvent,\n type PointerEvent,\n} from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { useCountTo } from \"../lib/count-to.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { prefersReducedMotion, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { live, retarget, REVEAL, SETTLE, SNAPPY, type Live } from \"../lib/spring-tween.js\";\n\n/**\n * DonutChart: a few parts of one whole, when the share of each part matters\n * more than a precise comparison.\n *\n * The ring sweeps open the first time it is seen: every segment leaves the top\n * together and each trailing edge follows a beat behind the one before. New\n * data, or a segment shown or hidden, moves every arc from where it is on\n * screen; a leaving segment closes where it sits. The active segment (hover,\n * focus, selection) slides out along its middle and thickens; the rest dim.\n * The centre rolls like a drum between the total and each segment, and every\n * number counts to its new value. Small parts fold into one \"Other\" segment.\n *\n * Plain SVG and CSS; one small spring tween moves the arcs. The legend is the\n * keyboard path (or the ring itself, without a legend), and a visually hidden\n * table carries every value.\n */\nexport interface DonutChartDatum {\n /** Stable identity. A segment whose key survives a data change keeps its place and colour. */\n key: string;\n label: string;\n value: number;\n /** Any CSS colour. Segments default to chart-1 to chart-4 in data order, then neutral steps. */\n color?: string;\n}\n\nexport interface DonutChartProps {\n data: DonutChartDatum[];\n /** What the whole is, such as \"Visits by source\". Names the chart for assistive technology. */\n label: string;\n /** Unit after values, such as \"visits\". */\n unit?: string;\n formatValue?: (value: number) => string;\n /** Centre label at rest, above the total. @default \"Total\" */\n totalLabel?: string;\n /** Diameter in pixels. The chart scales down to fit narrower containers. @default 208 */\n size?: number;\n /** Ring thickness in pixels. @default 24 */\n thickness?: number;\n /** Segments below this share of the total join \"Other\". @default 0.04 */\n groupBelow?: number;\n /** The most segments drawn, counting \"Other\". @default 6 */\n maxSegments?: number;\n /** @default \"Other\" */\n otherLabel?: string;\n /** Controlled selected segment key. Hover and focus preview other segments without changing it. */\n activeKey?: string | null;\n defaultActiveKey?: string | null;\n onActiveChange?: (key: string | null) => void;\n /** Controlled hidden segment keys. A hidden segment closes and the rest of the ring redistributes. */\n hiddenKeys?: string[];\n defaultHiddenKeys?: string[];\n onHiddenKeysChange?: (keys: string[]) => void;\n /** What clicking a legend row does: show or hide its segment, or pin it as selected. @default \"toggle\" */\n legendAction?: \"toggle\" | \"select\";\n /** The synced legend beside or below the ring. @default true */\n legend?: boolean;\n /** @default \"No data yet\" */\n emptyLabel?: string;\n /** Extra classes for the root. Merged last. */\n className?: string;\n}\n\ntype Slice = { key: string; label: string; value: number; color?: string; members?: DonutChartDatum[] };\ntype Arc = { start: number; end: number };\ntype Segment = { start: Live; end: Live; lift: Live };\n\nconst OTHER = \"__other\";\nconst TAU = Math.PI * 2;\n/** Neutral steps after the four series colours, and the quietest step for Other. */\nconst NEUTRAL = [56, 42, 32];\nconst seriesColor = (index: number) =>\n index < 4\n ? `var(--color-chart-${index + 1})`\n : `color-mix(in oklab, var(--color-foreground) ${NEUTRAL[(index - 4) % NEUTRAL.length]}%, var(--color-surface))`;\nconst OTHER_COLOR = \"color-mix(in oklab, var(--color-foreground) 26%, var(--color-surface))\";\n/** Room outside the ring for the lift; growth, margin, the gap between segments and their corner radius, in pixels. */\nconst LIFT = 4;\nconst GROW = 2;\nconst MARGIN = 7;\nconst GAP = 3;\nconst CORNER = 4;\n/** Stagger of the trailing edges on the first sweep. */\nconst STAGGER_MS = 35;\nconst GROUPED = new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 1 });\nconst PERCENT = new Intl.NumberFormat(\"en-US\", { style: \"percent\", maximumFractionDigits: 0 });\nconst shareText = (share: number) => (share > 0 && share < 0.01 ? \"<1%\" : PERCENT.format(share));\nconst n2 = (value: number) => (Math.round(value * 100) / 100).toString();\n\n/** Small parts join one \"Other\" segment, but only when at least two would; grouping a single part hides it for nothing. */\nfunction sliceData(data: DonutChartDatum[], groupBelow: number, maxSegments: number, otherLabel: string): Slice[] {\n const parts = data.filter((item) => item.value > 0);\n const total = parts.reduce((sum, item) => sum + item.value, 0);\n if (!total) return [];\n const ranked = [...parts].sort((a, b) => b.value - a.value);\n const keep = new Set(\n ranked.filter((item, rank) => item.value / total >= groupBelow && rank < maxSegments - 1).map((item) => item.key),\n );\n const rest = parts.filter((item) => !keep.has(item.key));\n if (rest.length < 2) return parts.map((item) => ({ ...item }));\n return [\n ...parts.filter((item) => keep.has(item.key)).map((item) => ({ ...item })),\n { key: OTHER, label: otherLabel, value: rest.reduce((sum, item) => sum + item.value, 0), members: rest },\n ];\n}\n\n/** Turns of the ring for each slice in data order; a hidden slice closes to a point where it sits. */\nfunction layout(slices: Slice[], hidden: Set<string>) {\n const total = slices.reduce((sum, item) => sum + (hidden.has(item.key) ? 0 : item.value), 0) || 1;\n let at = 0;\n return new Map(\n slices.map((item) => {\n const arc = { start: at, end: at + (hidden.has(item.key) ? 0 : item.value / total) };\n at = arc.end;\n return [item.key, arc] as const;\n }),\n );\n}\n\n/** Half the width, in pixels, of a sector's outer edge once the gap is cut; below a couple of pixels a segment fades rather than drawing a hairline. */\nfunction breadth(R: number, t0: number, t1: number, gap: number) {\n const turns = t1 - t0;\n if (turns >= 1 - 1e-6) return Infinity;\n return Math.sin(Math.min(turns * Math.PI, Math.PI / 2)) * R - Math.min(gap / 2, ((1 - turns) * TAU * R) / 2);\n}\n\n/**\n * An annular sector between two turns of the ring, clockwise from the top, with\n * rounded corners. Its sides run parallel to the radius at a fixed distance, so\n * the gap between neighbours is the same width from the inner edge to the outer.\n * A thin sector narrows to a wedge and then to nothing, so a closing one never pops.\n */\nfunction sector(c: number, R: number, r: number, t0: number, t1: number, gap: number, corner: number) {\n const turns = t1 - t0;\n if (turns <= 1e-6) return \"\";\n const at = (radius: number, angle: number) => `${n2(c + radius * Math.cos(angle))} ${n2(c + radius * Math.sin(angle))}`;\n if (turns >= 1 - 1e-6) {\n return `M${at(R, -Math.PI / 2)}A${n2(R)} ${n2(R)} 0 1 1 ${at(R, Math.PI / 2)}A${n2(R)} ${n2(R)} 0 1 1 ${at(R, -Math.PI / 2)}ZM${at(r, -Math.PI / 2)}A${n2(r)} ${n2(r)} 0 1 0 ${at(r, Math.PI / 2)}A${n2(r)} ${n2(r)} 0 1 0 ${at(r, -Math.PI / 2)}Z`;\n }\n // Half the gap on each side; a lone segment closes its gap as the rest of the ring empties, so it becomes a whole ring without a jump.\n const p = Math.min(gap / 2, ((1 - turns) * TAU * R) / 2);\n const a0 = t0 * TAU - Math.PI / 2;\n const a1 = t1 * TAU - Math.PI / 2;\n const half = (a1 - a0) / 2;\n const s = Math.sin(Math.min(half, Math.PI / 2));\n if (s * R <= p + 1e-6) return \"\";\n const side = (t: number, angle: number, sign: number) =>\n `${n2(c + t * Math.cos(angle) - sign * p * Math.sin(angle))} ${n2(c + t * Math.sin(angle) + sign * p * Math.cos(angle))}`;\n const band = (R - r) / 2;\n const ro = Math.max(0, Math.min(corner, band, s >= 0.9999 ? Infinity : (s * R - p) / (1 + s)));\n const dO = Math.asin(Math.min(1, (p + ro) / (R - ro)));\n const tO = Math.sqrt(Math.max(0, (R - ro) ** 2 - (p + ro) ** 2));\n const bigO = a1 - a0 - 2 * dO > Math.PI ? 1 : 0;\n let d = `M${side(tO, a0, 1)}`;\n if (ro > 0.01) d += `A${n2(ro)} ${n2(ro)} 0 0 1 ${at(R, a0 + dO)}`;\n d += `A${n2(R)} ${n2(R)} 0 ${bigO} 1 ${at(R, a1 - dO)}`;\n if (ro > 0.01) d += `A${n2(ro)} ${n2(ro)} 0 0 1 ${side(tO, a1, -1)}`;\n if (s * r > p + 1e-6 || s >= 0.9999) {\n const ri = Math.max(0, Math.min(corner, band, s >= 0.9999 ? Infinity : (s * r - p) / (1 - s)));\n const dI = Math.asin(Math.min(1, (p + ri) / (r + ri)));\n const tI = Math.sqrt(Math.max(0, (r + ri) ** 2 - (p + ri) ** 2));\n const bigI = a1 - a0 - 2 * dI > Math.PI ? 1 : 0;\n d += `L${side(tI, a1, -1)}`;\n if (ri > 0.01) d += `A${n2(ri)} ${n2(ri)} 0 0 1 ${at(r, a1 - dI)}`;\n d += `A${n2(r)} ${n2(r)} 0 ${bigI} 0 ${at(r, a0 + dI)}`;\n if (ri > 0.01) d += `A${n2(ri)} ${n2(ri)} 0 0 1 ${side(tI, a0, 1)}`;\n } else d += `L${at(p / s, (a0 + a1) / 2)}`;\n return `${d}Z`;\n}\n\n/**\n * A number that counts to each new value on the smooth spring. useCountTo counts\n * in whole steps, so a fraction (a share) is counted in ten-thousandths.\n */\nfunction Count({ value, format }: { value: number; format: (value: number) => string }) {\n const fine = !Number.isInteger(value);\n const shown = useCountTo(fine ? value * 10000 : value);\n return <>{format(fine ? shown / 10000 : shown)}</>;\n}\n\nexport const DonutChart = forwardRef<HTMLElement, DonutChartProps>(function DonutChart(\n {\n data,\n label,\n unit = \"\",\n formatValue = (value) => GROUPED.format(value),\n totalLabel = \"Total\",\n size = 208,\n thickness = 24,\n groupBelow = 0.04,\n maxSegments = 6,\n otherLabel = \"Other\",\n activeKey,\n defaultActiveKey = null,\n onActiveChange,\n hiddenKeys,\n defaultHiddenKeys,\n onHiddenKeysChange,\n legendAction = \"toggle\",\n legend = true,\n emptyLabel = \"No data yet\",\n className,\n },\n ref,\n) {\n const figure = useRef<HTMLElement | null>(null);\n const refs = useMemo(() => mergeRefs(ref, figure), [ref]);\n const legendId = useId();\n const slices = sliceData(data, groupBelow, maxSegments, otherLabel);\n\n const [ownHidden, setOwnHidden] = useState<string[]>(defaultHiddenKeys ?? []);\n const hiddenList = hiddenKeys ?? ownHidden;\n const hidden = new Set(hiddenList.filter((key) => slices.some((item) => item.key === key)));\n const visible = slices.filter((item) => !hidden.has(item.key));\n const total = visible.reduce((sum, item) => sum + item.value, 0);\n const signature = slices.map((item) => `${item.key}:${item.value}${hidden.has(item.key) ? \"h\" : \"\"}`).join(\"|\");\n const suffix = unit ? ` ${unit}` : \"\";\n\n // Colours follow a key from the first time it is seen, so a segment keeps its colour through every dataset.\n const [palette, setPalette] = useState(() => new Map(data.map((item, index) => [item.key, index])));\n if (data.some((item) => !palette.has(item.key))) {\n const next = new Map(palette);\n for (const item of data) if (!next.has(item.key)) next.set(item.key, next.size);\n setPalette(next);\n }\n const colorFor = (item: Slice) =>\n item.color ?? (item.key === OTHER ? OTHER_COLOR : seriesColor(palette.get(item.key) ?? 0));\n\n // Leaving segments stay drawn in their old place while they close, then drop out.\n const [drawn, setDrawn] = useState<Slice[]>(slices);\n const [seen, setSeen] = useState(signature);\n if (seen !== signature) {\n setSeen(signature);\n const next = [...slices];\n drawn.forEach((item, index) => {\n if (next.some((slice) => slice.key === item.key)) return;\n const before = drawn\n .slice(0, index)\n .reverse()\n .find((prior) => next.some((slice) => slice.key === prior.key));\n next.splice(before ? next.findIndex((slice) => slice.key === before.key) + 1 : 0, 0, { ...item, value: 0 });\n });\n setDrawn(next);\n }\n\n // Selection is controlled or not; hover and focus preview on top of it.\n const [ownActive, setOwnActive] = useState<string | null>(defaultActiveKey);\n const selected = activeKey !== undefined ? activeKey : ownActive;\n const [preview, setPreview] = useState<string | null>(null);\n const select = (key: string | null) => {\n if (activeKey === undefined) setOwnActive(key);\n onActiveChange?.(key);\n };\n const current = [preview, selected].find((key) => key && visible.some((item) => item.key === key)) ?? null;\n const activeSlice = visible.find((item) => item.key === current) ?? null;\n\n const [announcement, setAnnouncement] = useState(\"\");\n const setHidden = (key: string) => {\n const isHidden = hidden.has(key);\n if (!isHidden && visible.length <= 1) return;\n const next = isHidden ? hiddenList.filter((item) => item !== key) : [...hiddenList, key];\n if (hiddenKeys === undefined) setOwnHidden(next);\n onHiddenKeysChange?.(next);\n const item = slices.find((slice) => slice.key === key);\n const nextTotal = slices.reduce((sum, slice) => sum + (next.includes(slice.key) ? 0 : slice.value), 0);\n if (item) setAnnouncement(`${item.label} ${isHidden ? \"shown\" : \"hidden\"}. ${totalLabel} ${formatValue(nextTotal)}${suffix}.`);\n };\n\n // Geometry in the chart's own units; the SVG scales with its container.\n const c = size / 2;\n const outer = c - MARGIN;\n const inner = Math.max(8, outer - thickness);\n const corner = Math.min(CORNER, thickness / 4);\n const geometry = useRef({ c, outer, inner, corner });\n const segments = useRef(new Map<string, Segment>());\n const svg = useRef<SVGSVGElement>(null);\n\n /** One writer: every path's shape, opacity and lift, straight onto the elements. */\n const paint = useCallback(() => {\n const root = svg.current;\n if (!root) return;\n const { c, outer, inner, corner } = geometry.current;\n for (const node of root.querySelectorAll<SVGPathElement>(\"[data-key]\")) {\n const segment = segments.current.get(node.dataset.key!);\n if (!segment) {\n node.setAttribute(\"d\", \"\");\n continue;\n }\n const start = segment.start.value;\n const end = segment.end.value;\n const lift = Math.max(0, segment.lift.value);\n const R = outer + lift * GROW;\n node.setAttribute(\"d\", sector(c, R, inner - lift * GROW * 0.5, start, end, GAP, corner));\n node.setAttribute(\"fill-opacity\", n2(Math.min(1, Math.max(0, (breadth(R, start, end, GAP) - 1) / 3))));\n const middle = ((start + end) / 2) * TAU - Math.PI / 2;\n node.setAttribute(\"transform\", `translate(${n2(Math.cos(middle) * lift * LIFT)} ${n2(Math.sin(middle) * lift * LIFT)})`);\n }\n }, []);\n useIsomorphicLayoutEffect(() => {\n geometry.current = { c, outer, inner, corner };\n paint();\n });\n useEffect(\n () => () => {\n for (const segment of segments.current.values()) {\n segment.start.stop();\n segment.end.stop();\n segment.lift.stop();\n }\n },\n [],\n );\n\n // The ring sweeps once a third of it is in view.\n const [shown, setShown] = useState(false);\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 const targets = useRef(new Map<string, Arc>());\n const latest = useRef({ drawn, slices });\n useIsomorphicLayoutEffect(() => {\n latest.current = { drawn, slices };\n });\n\n // New data, or a segment shown or hidden, moves every arc from wherever it is on screen.\n useEffect(() => {\n if (!shown) return;\n const { drawn: order, slices: now } = latest.current;\n const goal = layout(now, hidden);\n targets.current = goal;\n const first = segments.current.size === 0;\n const place = (index: number) => {\n const before = order\n .slice(0, index)\n .reverse()\n .find((item) => goal.has(item.key));\n return before ? goal.get(before.key)!.end : 0;\n };\n const drop = (key: string) => {\n if (latest.current.slices.some((item) => item.key === key)) return;\n segments.current.delete(key);\n setDrawn((list) => list.filter((item) => item.key !== key));\n };\n order.forEach((item, index) => {\n const leaving = !goal.has(item.key);\n const to = goal.get(item.key) ?? { start: place(index), end: place(index) };\n let segment = segments.current.get(item.key);\n if (!segment) {\n // A new segment opens from the edge of the one before it, as it is right now.\n const prior = order\n .slice(0, index)\n .reverse()\n .map((entry) => segments.current.get(entry.key))\n .find(Boolean);\n const from = first ? 0 : prior ? prior.end.value : 0;\n segment = { start: live(from), end: live(from), lift: live(0) };\n segments.current.set(item.key, segment);\n }\n if (first) {\n // One sweep: every segment leaves the top together, and each trailing edge follows a beat behind the one before.\n retarget(segment.start, to.start, REVEAL, paint);\n retarget(segment.end, to.end, REVEAL, paint, 40 + index * STAGGER_MS);\n return;\n }\n retarget(segment.start, to.start, SETTLE, paint);\n retarget(segment.end, to.end, SETTLE, paint, 0, leaving ? () => drop(item.key) : undefined);\n });\n paint();\n // The signature stands for the slices and the hidden set.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shown, signature, paint]);\n\n // The active segment slides out along its middle and thickens; the one it replaces settles back.\n useEffect(() => {\n for (const [key, segment] of segments.current) retarget(segment.lift, key === current ? 1 : 0, SNAPPY, paint);\n }, [current, drawn, shown, paint]);\n\n // One pointer handler for the whole ring: the angle picks the segment, so crossing a gap never drops the hover.\n const hit = (event: { clientX: number; clientY: number }) => {\n const box = svg.current?.getBoundingClientRect();\n if (!box?.width) return undefined;\n const scale = size / box.width;\n const x = (event.clientX - box.left) * scale - c;\n const y = (event.clientY - box.top) * scale - c;\n const radius = Math.hypot(x, y);\n if (radius < inner - 6 || radius > c) return null;\n let turn = (Math.atan2(y, x) + Math.PI / 2) / TAU;\n if (turn < 0) turn += 1;\n for (const [key, arc] of targets.current) {\n if (arc.end > arc.start && turn >= arc.start && turn < arc.end && visible.some((item) => item.key === key)) return key;\n }\n return undefined;\n };\n const onRingMove = (event: PointerEvent<SVGSVGElement>) => {\n if (event.pointerType !== \"mouse\") return;\n const key = hit(event);\n if (key !== undefined && key !== preview) setPreview(key);\n };\n const onRingClick = (event: MouseEvent<SVGSVGElement>) => {\n const key = hit(event);\n if (key) select(selected === key ? null : key);\n };\n\n const format = (value: number) => `${formatValue(value)}${suffix}`;\n const describe = (item: Slice) =>\n `${item.label}, ${format(item.value)}, ${shareText(item.value / (total || 1))}${\n item.members ? `. Includes ${item.members.map((member) => member.label).join(\", \")}` : \"\"\n }`;\n\n // Arrow keys walk the segments, in the legend or on the ring itself when there is no legend.\n const rows = useRef<(HTMLButtonElement | null)[]>([]);\n const step = (key: string, index: number, count: number) => {\n const moves: Record<string, number> = {\n ArrowDown: index + 1,\n ArrowRight: index + 1,\n ArrowUp: index - 1,\n ArrowLeft: index - 1,\n Home: 0,\n End: count - 1,\n };\n return moves[key];\n };\n const onLegendKey = (event: KeyboardEvent<HTMLButtonElement>, index: number) => {\n if (event.key === \"Escape\" && selected) {\n event.preventDefault();\n select(null);\n return;\n }\n const next = step(event.key, index, slices.length);\n if (next === undefined) return;\n event.preventDefault();\n rows.current[(next + slices.length) % slices.length]?.focus();\n };\n const onRingKey = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!visible.length) return;\n if (event.key === \"Escape\") {\n event.preventDefault();\n setPreview(null);\n if (selected) select(null);\n return;\n }\n if ((event.key === \"Enter\" || event.key === \" \") && current) {\n event.preventDefault();\n select(selected === current ? null : current);\n return;\n }\n const index = visible.findIndex((item) => item.key === current);\n const next = step(\n event.key,\n index < 0 ? (event.key.endsWith(\"Up\") || event.key.endsWith(\"Left\") ? 0 : -1) : index,\n visible.length,\n );\n if (next === undefined) return;\n event.preventDefault();\n const item = visible[(next + visible.length) % visible.length]!;\n setPreview(item.key);\n setAnnouncement(describe(item));\n };\n const onLegendBlur = (event: FocusEvent<HTMLButtonElement>) => {\n if (!(event.relatedTarget instanceof Node && event.currentTarget.closest(\"ul\")?.contains(event.relatedTarget))) {\n setPreview(null);\n }\n };\n\n // One readout per segment plus the total; the active one rolls in from the side it sits on.\n const identity = activeSlice ? activeSlice.key : \"__total\";\n const readouts = [\n { key: \"__total\", label: total ? totalLabel : emptyLabel, value: total, meta: null as Slice | null },\n ...slices.map((item) => ({ key: item.key, label: item.label, value: item.value, meta: item })),\n ];\n const activeIndex = Math.max(0, readouts.findIndex((item) => item.key === identity));\n const summary = total ? `${label}. ${totalLabel} ${format(total)}. ${visible.map(describe).join(\". \")}.` : `${label}. ${emptyLabel}.`;\n const members = (item: Slice) =>\n item.members\n ? item.members.length > 2\n ? `${item.members\n .slice(0, 2)\n .map((member) => member.label)\n .join(\", \")} and ${item.members.length - 2} more`\n : item.members.map((member) => member.label).join(\" and \")\n : null;\n\n return (\n <figure\n ref={refs}\n data-slot=\"donut-chart\"\n aria-label={label}\n data-legend={legend || undefined}\n className={cn(\"donut-chart box-border font-sans text-foreground\", className)}\n >\n <div\n data-slot=\"donut-chart-ring\"\n className=\"donut-chart-ring\"\n style={{ width: size, \"--inner\": `${((inner * 2) / size) * 100}%` } as CSSProperties}\n tabIndex={legend ? undefined : 0}\n role={legend ? undefined : \"application\"}\n aria-label={legend ? undefined : `${label}. Use the arrow keys to read each segment.`}\n onKeyDown={legend ? undefined : onRingKey}\n onBlur={legend ? undefined : () => setPreview(null)}\n >\n <svg\n ref={svg}\n viewBox={`0 0 ${size} ${size}`}\n aria-hidden=\"true\"\n focusable=\"false\"\n onPointerMove={onRingMove}\n onPointerLeave={() => setPreview(null)}\n onClick={onRingClick}\n data-hovering={current !== null || undefined}\n className=\"block size-full overflow-visible data-[hovering]:cursor-pointer\"\n >\n <circle\n className=\"donut-chart-track\"\n cx={c}\n cy={c}\n r={(outer + inner) / 2}\n strokeWidth={outer - inner}\n data-idle={!shown || !total || undefined}\n />\n {drawn.map((item) => (\n <path\n key={item.key}\n data-key={item.key}\n data-active={item.key === current || undefined}\n data-dim={(current !== null && item.key !== current) || undefined}\n className=\"donut-chart-segment\"\n style={{ \"--slice\": colorFor(item) } as CSSProperties}\n />\n ))}\n </svg>\n <div aria-hidden=\"true\" className=\"donut-chart-center\">\n {readouts.map((item, index) => (\n <span\n key={item.key}\n data-place={Math.sign(index - activeIndex)}\n className=\"donut-chart-readout\"\n >\n <span className=\"block w-full min-h-5 truncate text-xs text-text-secondary\">{item.label}</span>\n <span className=\"block max-w-full min-h-8 truncate text-h3 font-medium tabular-nums\">\n {item.value || item.meta ? <Count value={item.value} format={formatValue} /> : \" \"}\n </span>\n <span className=\"block w-full min-h-5 truncate text-xs tabular-nums text-muted-foreground\">\n {item.meta ? (\n <>\n <Count value={total && !hidden.has(item.key) ? item.value / total : 0} format={shareText} /> of{\" \"}\n {totalLabel.toLowerCase()}\n </>\n ) : (\n unit || \" \"\n )}\n </span>\n </span>\n ))}\n </div>\n </div>\n {legend && slices.length > 0 ? (\n <ul\n id={legendId}\n aria-label={`${label}, segments${legendAction === \"toggle\" ? \". Press a segment to show or hide it\" : \"\"}`}\n onPointerLeave={() => setPreview(null)}\n className=\"m-0 -mb-0.5 grid list-none p-0\"\n >\n {slices.map((item, index) => {\n const off = hidden.has(item.key);\n return (\n <li key={item.key} className=\"overflow-hidden motion-safe:animate-fade-in\">\n <button\n ref={(node) => {\n rows.current[index] = node;\n }}\n type=\"button\"\n data-slot=\"donut-chart-row\"\n aria-pressed={legendAction === \"toggle\" ? !off : selected === item.key}\n aria-label={off ? `${item.label}, hidden` : describe(item)}\n data-active={item.key === current || undefined}\n data-pinned={selected === item.key || undefined}\n data-hidden={off || undefined}\n style={{ \"--slice\": colorFor(item) } as CSSProperties}\n onClick={() => (legendAction === \"toggle\" ? setHidden(item.key) : select(selected === item.key ? null : item.key))}\n onPointerEnter={(event) => {\n if (event.pointerType === \"mouse\") setPreview(off ? null : item.key);\n }}\n onFocus={(event) => {\n if (event.currentTarget.matches(\":focus-visible\")) setPreview(off ? null : item.key);\n }}\n onBlur={onLegendBlur}\n onKeyDown={(event) => onLegendKey(event, index)}\n className=\"donut-chart-row\"\n >\n <span aria-hidden=\"true\" className=\"donut-chart-dot\" />\n <span className=\"grid min-w-0\">\n <span className=\"donut-chart-row-label truncate\">{item.label}</span>\n {item.members ? <span className=\"truncate text-xs text-muted-foreground\">{members(item)}</span> : null}\n </span>\n <span aria-hidden=\"true\" className=\"donut-chart-row-value justify-self-end tabular-nums text-text-secondary\">\n <Count value={item.value} format={formatValue} />\n </span>\n <span aria-hidden=\"true\" className=\"donut-chart-row-value justify-self-end font-medium tabular-nums\">\n <Count value={off || !total ? 0 : item.value / total} format={shareText} />\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n ) : null}\n <p className=\"sr-only\">{summary}</p>\n <p className=\"sr-only\" aria-live=\"polite\">\n {announcement}\n </p>\n {total > 0 ? (\n <div className=\"sr-only\">\n <table>\n <caption>{label}</caption>\n <thead>\n <tr>\n <th scope=\"col\">Segment</th>\n <th scope=\"col\">Value</th>\n <th scope=\"col\">Share</th>\n </tr>\n </thead>\n <tbody>\n {visible\n .flatMap((item) =>\n item.members\n ? item.members.map((member) => ({ key: member.key, label: `${member.label} (${otherLabel})`, value: member.value }))\n : [item],\n )\n .map((item) => (\n <tr key={item.key}>\n <th scope=\"row\">{item.label}</th>\n <td>{format(item.value)}</td>\n <td>{shareText(item.value / total)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : null}\n </figure>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"donut-chart.js","names":[],"sources":["../../src/donut-chart/donut-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type MouseEvent,\n type PointerEvent,\n} from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { useCountTo } from \"../lib/count-to.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { prefersReducedMotion, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { live, retarget, REVEAL, SETTLE, SNAPPY, type Live } from \"../lib/spring-tween.js\";\n\n/**\n * DonutChart: a few parts of one whole, when the share of each part matters\n * more than a precise comparison.\n *\n * The ring sweeps open the first time it is seen: every segment leaves the top\n * together and each trailing edge follows a beat behind the one before. New\n * data, or a segment shown or hidden, moves every arc from where it is on\n * screen; a leaving segment closes where it sits. The active segment (hover,\n * focus, selection) slides out along its middle and thickens; the rest dim.\n * The centre rolls like a drum between the total and each segment, and every\n * number counts to its new value. Small parts fold into one \"Other\" segment.\n * Where the parts do not add up to anything (a rate such as CTR, CPC or ROAS\n * per source), `center` hides the total or puts a figure of the caller's own\n * at rest instead; the segment readouts still roll in on hover and focus.\n *\n * Plain SVG and CSS; one small spring tween moves the arcs. The legend is the\n * keyboard path (or the ring itself, without a legend), and a visually hidden\n * table carries every value.\n */\nexport interface DonutChartDatum {\n /** Stable identity. A segment whose key survives a data change keeps its place and colour. */\n key: string;\n label: string;\n value: number;\n /** Any CSS colour. Segments default to chart-1 to chart-4 in data order, then neutral steps. */\n color?: string;\n}\n\n/** A figure of the caller's own for the centre at rest, in place of the summed total. */\nexport interface DonutChartCenter {\n /** The line above the figure, such as \"Blended CTR\". */\n label: string;\n /**\n * The figure. A number is formatted with `formatValue` and counts to each new\n * value, with `unit` under it; a string is shown as written (`\"2.4%\"`).\n */\n value: number | string;\n}\n\nexport interface DonutChartProps {\n data: DonutChartDatum[];\n /** What the whole is, such as \"Visits by source\". Names the chart for assistive technology. */\n label: string;\n /** Unit after values, such as \"visits\". */\n unit?: string;\n formatValue?: (value: number) => string;\n /** Centre label at rest, above the total. @default \"Total\" */\n totalLabel?: string;\n /**\n * What the centre shows at rest. `\"total\"` sums the visible segments under\n * `totalLabel`. `\"none\"` leaves it empty, for parts whose sum means nothing\n * (CTR, CPC or ROAS by source). An object puts a figure of your own there\n * instead, such as the blended rate. Hovering or focusing a segment still\n * shows that segment's readout either way.\n * @default \"total\"\n */\n center?: \"total\" | \"none\" | DonutChartCenter;\n /** Diameter in pixels. The chart scales down to fit narrower containers. @default 208 */\n size?: number;\n /** Ring thickness in pixels. @default 24 */\n thickness?: number;\n /** Segments below this share of the total join \"Other\". @default 0.04 */\n groupBelow?: number;\n /** The most segments drawn, counting \"Other\". @default 6 */\n maxSegments?: number;\n /** @default \"Other\" */\n otherLabel?: string;\n /** Controlled selected segment key. Hover and focus preview other segments without changing it. */\n activeKey?: string | null;\n defaultActiveKey?: string | null;\n onActiveChange?: (key: string | null) => void;\n /** Controlled hidden segment keys. A hidden segment closes and the rest of the ring redistributes. */\n hiddenKeys?: string[];\n defaultHiddenKeys?: string[];\n onHiddenKeysChange?: (keys: string[]) => void;\n /** What clicking a legend row does: show or hide its segment, or pin it as selected. @default \"toggle\" */\n legendAction?: \"toggle\" | \"select\";\n /** The synced legend beside or below the ring. @default true */\n legend?: boolean;\n /** @default \"No data yet\" */\n emptyLabel?: string;\n /** Extra classes for the root. Merged last. */\n className?: string;\n}\n\ntype Slice = { key: string; label: string; value: number; color?: string; members?: DonutChartDatum[] };\ntype Arc = { start: number; end: number };\ntype Segment = { start: Live; end: Live; lift: Live };\n\nconst OTHER = \"__other\";\nconst TAU = Math.PI * 2;\n/** Neutral steps after the four series colours, and the quietest step for Other. */\nconst NEUTRAL = [56, 42, 32];\nconst seriesColor = (index: number) =>\n index < 4\n ? `var(--color-chart-${index + 1})`\n : `color-mix(in oklab, var(--color-foreground) ${NEUTRAL[(index - 4) % NEUTRAL.length]}%, var(--color-surface))`;\nconst OTHER_COLOR = \"color-mix(in oklab, var(--color-foreground) 26%, var(--color-surface))\";\n/** Room outside the ring for the lift; growth, margin, the gap between segments and their corner radius, in pixels. */\nconst LIFT = 4;\nconst GROW = 2;\nconst MARGIN = 7;\nconst GAP = 3;\nconst CORNER = 4;\n/** Stagger of the trailing edges on the first sweep. */\nconst STAGGER_MS = 35;\nconst GROUPED = new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 1 });\nconst PERCENT = new Intl.NumberFormat(\"en-US\", { style: \"percent\", maximumFractionDigits: 0 });\nconst shareText = (share: number) => (share > 0 && share < 0.01 ? \"<1%\" : PERCENT.format(share));\nconst n2 = (value: number) => (Math.round(value * 100) / 100).toString();\n\n/** Small parts join one \"Other\" segment, but only when at least two would; grouping a single part hides it for nothing. */\nfunction sliceData(data: DonutChartDatum[], groupBelow: number, maxSegments: number, otherLabel: string): Slice[] {\n const parts = data.filter((item) => item.value > 0);\n const total = parts.reduce((sum, item) => sum + item.value, 0);\n if (!total) return [];\n const ranked = [...parts].sort((a, b) => b.value - a.value);\n const keep = new Set(\n ranked.filter((item, rank) => item.value / total >= groupBelow && rank < maxSegments - 1).map((item) => item.key),\n );\n const rest = parts.filter((item) => !keep.has(item.key));\n if (rest.length < 2) return parts.map((item) => ({ ...item }));\n return [\n ...parts.filter((item) => keep.has(item.key)).map((item) => ({ ...item })),\n { key: OTHER, label: otherLabel, value: rest.reduce((sum, item) => sum + item.value, 0), members: rest },\n ];\n}\n\n/** Turns of the ring for each slice in data order; a hidden slice closes to a point where it sits. */\nfunction layout(slices: Slice[], hidden: Set<string>) {\n const total = slices.reduce((sum, item) => sum + (hidden.has(item.key) ? 0 : item.value), 0) || 1;\n let at = 0;\n return new Map(\n slices.map((item) => {\n const arc = { start: at, end: at + (hidden.has(item.key) ? 0 : item.value / total) };\n at = arc.end;\n return [item.key, arc] as const;\n }),\n );\n}\n\n/** Half the width, in pixels, of a sector's outer edge once the gap is cut; below a couple of pixels a segment fades rather than drawing a hairline. */\nfunction breadth(R: number, t0: number, t1: number, gap: number) {\n const turns = t1 - t0;\n if (turns >= 1 - 1e-6) return Infinity;\n return Math.sin(Math.min(turns * Math.PI, Math.PI / 2)) * R - Math.min(gap / 2, ((1 - turns) * TAU * R) / 2);\n}\n\n/**\n * An annular sector between two turns of the ring, clockwise from the top, with\n * rounded corners. Its sides run parallel to the radius at a fixed distance, so\n * the gap between neighbours is the same width from the inner edge to the outer.\n * A thin sector narrows to a wedge and then to nothing, so a closing one never pops.\n */\nfunction sector(c: number, R: number, r: number, t0: number, t1: number, gap: number, corner: number) {\n const turns = t1 - t0;\n if (turns <= 1e-6) return \"\";\n const at = (radius: number, angle: number) => `${n2(c + radius * Math.cos(angle))} ${n2(c + radius * Math.sin(angle))}`;\n if (turns >= 1 - 1e-6) {\n return `M${at(R, -Math.PI / 2)}A${n2(R)} ${n2(R)} 0 1 1 ${at(R, Math.PI / 2)}A${n2(R)} ${n2(R)} 0 1 1 ${at(R, -Math.PI / 2)}ZM${at(r, -Math.PI / 2)}A${n2(r)} ${n2(r)} 0 1 0 ${at(r, Math.PI / 2)}A${n2(r)} ${n2(r)} 0 1 0 ${at(r, -Math.PI / 2)}Z`;\n }\n // Half the gap on each side; a lone segment closes its gap as the rest of the ring empties, so it becomes a whole ring without a jump.\n const p = Math.min(gap / 2, ((1 - turns) * TAU * R) / 2);\n const a0 = t0 * TAU - Math.PI / 2;\n const a1 = t1 * TAU - Math.PI / 2;\n const half = (a1 - a0) / 2;\n const s = Math.sin(Math.min(half, Math.PI / 2));\n if (s * R <= p + 1e-6) return \"\";\n const side = (t: number, angle: number, sign: number) =>\n `${n2(c + t * Math.cos(angle) - sign * p * Math.sin(angle))} ${n2(c + t * Math.sin(angle) + sign * p * Math.cos(angle))}`;\n const band = (R - r) / 2;\n const ro = Math.max(0, Math.min(corner, band, s >= 0.9999 ? Infinity : (s * R - p) / (1 + s)));\n const dO = Math.asin(Math.min(1, (p + ro) / (R - ro)));\n const tO = Math.sqrt(Math.max(0, (R - ro) ** 2 - (p + ro) ** 2));\n const bigO = a1 - a0 - 2 * dO > Math.PI ? 1 : 0;\n let d = `M${side(tO, a0, 1)}`;\n if (ro > 0.01) d += `A${n2(ro)} ${n2(ro)} 0 0 1 ${at(R, a0 + dO)}`;\n d += `A${n2(R)} ${n2(R)} 0 ${bigO} 1 ${at(R, a1 - dO)}`;\n if (ro > 0.01) d += `A${n2(ro)} ${n2(ro)} 0 0 1 ${side(tO, a1, -1)}`;\n if (s * r > p + 1e-6 || s >= 0.9999) {\n const ri = Math.max(0, Math.min(corner, band, s >= 0.9999 ? Infinity : (s * r - p) / (1 - s)));\n const dI = Math.asin(Math.min(1, (p + ri) / (r + ri)));\n const tI = Math.sqrt(Math.max(0, (r + ri) ** 2 - (p + ri) ** 2));\n const bigI = a1 - a0 - 2 * dI > Math.PI ? 1 : 0;\n d += `L${side(tI, a1, -1)}`;\n if (ri > 0.01) d += `A${n2(ri)} ${n2(ri)} 0 0 1 ${at(r, a1 - dI)}`;\n d += `A${n2(r)} ${n2(r)} 0 ${bigI} 0 ${at(r, a0 + dI)}`;\n if (ri > 0.01) d += `A${n2(ri)} ${n2(ri)} 0 0 1 ${side(tI, a0, 1)}`;\n } else d += `L${at(p / s, (a0 + a1) / 2)}`;\n return `${d}Z`;\n}\n\n/**\n * A number that counts to each new value on the smooth spring. useCountTo counts\n * in whole steps, so a fraction (a share) is counted in ten-thousandths.\n */\nfunction Count({ value, format }: { value: number; format: (value: number) => string }) {\n const fine = !Number.isInteger(value);\n const shown = useCountTo(fine ? value * 10000 : value);\n return <>{format(fine ? shown / 10000 : shown)}</>;\n}\n\nexport const DonutChart = forwardRef<HTMLElement, DonutChartProps>(function DonutChart(\n {\n data,\n label,\n unit = \"\",\n formatValue = (value) => GROUPED.format(value),\n totalLabel = \"Total\",\n center = \"total\",\n size = 208,\n thickness = 24,\n groupBelow = 0.04,\n maxSegments = 6,\n otherLabel = \"Other\",\n activeKey,\n defaultActiveKey = null,\n onActiveChange,\n hiddenKeys,\n defaultHiddenKeys,\n onHiddenKeysChange,\n legendAction = \"toggle\",\n legend = true,\n emptyLabel = \"No data yet\",\n className,\n },\n ref,\n) {\n const figure = useRef<HTMLElement | null>(null);\n const refs = useMemo(() => mergeRefs(ref, figure), [ref]);\n const legendId = useId();\n const slices = sliceData(data, groupBelow, maxSegments, otherLabel);\n\n const [ownHidden, setOwnHidden] = useState<string[]>(defaultHiddenKeys ?? []);\n const hiddenList = hiddenKeys ?? ownHidden;\n const hidden = new Set(hiddenList.filter((key) => slices.some((item) => item.key === key)));\n const visible = slices.filter((item) => !hidden.has(item.key));\n const total = visible.reduce((sum, item) => sum + item.value, 0);\n const signature = slices.map((item) => `${item.key}:${item.value}${hidden.has(item.key) ? \"h\" : \"\"}`).join(\"|\");\n const suffix = unit ? ` ${unit}` : \"\";\n\n // Colours follow a key from the first time it is seen, so a segment keeps its colour through every dataset.\n const [palette, setPalette] = useState(() => new Map(data.map((item, index) => [item.key, index])));\n if (data.some((item) => !palette.has(item.key))) {\n const next = new Map(palette);\n for (const item of data) if (!next.has(item.key)) next.set(item.key, next.size);\n setPalette(next);\n }\n const colorFor = (item: Slice) =>\n item.color ?? (item.key === OTHER ? OTHER_COLOR : seriesColor(palette.get(item.key) ?? 0));\n\n // Leaving segments stay drawn in their old place while they close, then drop out.\n const [drawn, setDrawn] = useState<Slice[]>(slices);\n const [seen, setSeen] = useState(signature);\n if (seen !== signature) {\n setSeen(signature);\n const next = [...slices];\n drawn.forEach((item, index) => {\n if (next.some((slice) => slice.key === item.key)) return;\n const before = drawn\n .slice(0, index)\n .reverse()\n .find((prior) => next.some((slice) => slice.key === prior.key));\n next.splice(before ? next.findIndex((slice) => slice.key === before.key) + 1 : 0, 0, { ...item, value: 0 });\n });\n setDrawn(next);\n }\n\n // Selection is controlled or not; hover and focus preview on top of it.\n const [ownActive, setOwnActive] = useState<string | null>(defaultActiveKey);\n const selected = activeKey !== undefined ? activeKey : ownActive;\n const [preview, setPreview] = useState<string | null>(null);\n const select = (key: string | null) => {\n if (activeKey === undefined) setOwnActive(key);\n onActiveChange?.(key);\n };\n const current = [preview, selected].find((key) => key && visible.some((item) => item.key === key)) ?? null;\n const activeSlice = visible.find((item) => item.key === current) ?? null;\n\n const [announcement, setAnnouncement] = useState(\"\");\n const setHidden = (key: string) => {\n const isHidden = hidden.has(key);\n if (!isHidden && visible.length <= 1) return;\n const next = isHidden ? hiddenList.filter((item) => item !== key) : [...hiddenList, key];\n if (hiddenKeys === undefined) setOwnHidden(next);\n onHiddenKeysChange?.(next);\n const item = slices.find((slice) => slice.key === key);\n const nextTotal = slices.reduce((sum, slice) => sum + (next.includes(slice.key) ? 0 : slice.value), 0);\n const totalText =\n center === \"total\" ? ` ${totalLabel} ${formatValue(nextTotal)}${suffix}.` : \"\";\n if (item) setAnnouncement(`${item.label} ${isHidden ? \"shown\" : \"hidden\"}.${totalText}`);\n };\n\n // Geometry in the chart's own units; the SVG scales with its container.\n const c = size / 2;\n const outer = c - MARGIN;\n const inner = Math.max(8, outer - thickness);\n const corner = Math.min(CORNER, thickness / 4);\n const geometry = useRef({ c, outer, inner, corner });\n const segments = useRef(new Map<string, Segment>());\n const svg = useRef<SVGSVGElement>(null);\n\n /** One writer: every path's shape, opacity and lift, straight onto the elements. */\n const paint = useCallback(() => {\n const root = svg.current;\n if (!root) return;\n const { c, outer, inner, corner } = geometry.current;\n for (const node of root.querySelectorAll<SVGPathElement>(\"[data-key]\")) {\n const segment = segments.current.get(node.dataset.key!);\n if (!segment) {\n node.setAttribute(\"d\", \"\");\n continue;\n }\n const start = segment.start.value;\n const end = segment.end.value;\n const lift = Math.max(0, segment.lift.value);\n const R = outer + lift * GROW;\n node.setAttribute(\"d\", sector(c, R, inner - lift * GROW * 0.5, start, end, GAP, corner));\n node.setAttribute(\"fill-opacity\", n2(Math.min(1, Math.max(0, (breadth(R, start, end, GAP) - 1) / 3))));\n const middle = ((start + end) / 2) * TAU - Math.PI / 2;\n node.setAttribute(\"transform\", `translate(${n2(Math.cos(middle) * lift * LIFT)} ${n2(Math.sin(middle) * lift * LIFT)})`);\n }\n }, []);\n useIsomorphicLayoutEffect(() => {\n geometry.current = { c, outer, inner, corner };\n paint();\n });\n useEffect(\n () => () => {\n for (const segment of segments.current.values()) {\n segment.start.stop();\n segment.end.stop();\n segment.lift.stop();\n }\n },\n [],\n );\n\n // The ring sweeps once a third of it is in view.\n const [shown, setShown] = useState(false);\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 const targets = useRef(new Map<string, Arc>());\n const latest = useRef({ drawn, slices });\n useIsomorphicLayoutEffect(() => {\n latest.current = { drawn, slices };\n });\n\n // New data, or a segment shown or hidden, moves every arc from wherever it is on screen.\n useEffect(() => {\n if (!shown) return;\n const { drawn: order, slices: now } = latest.current;\n const goal = layout(now, hidden);\n targets.current = goal;\n const first = segments.current.size === 0;\n const place = (index: number) => {\n const before = order\n .slice(0, index)\n .reverse()\n .find((item) => goal.has(item.key));\n return before ? goal.get(before.key)!.end : 0;\n };\n const drop = (key: string) => {\n if (latest.current.slices.some((item) => item.key === key)) return;\n segments.current.delete(key);\n setDrawn((list) => list.filter((item) => item.key !== key));\n };\n order.forEach((item, index) => {\n const leaving = !goal.has(item.key);\n const to = goal.get(item.key) ?? { start: place(index), end: place(index) };\n let segment = segments.current.get(item.key);\n if (!segment) {\n // A new segment opens from the edge of the one before it, as it is right now.\n const prior = order\n .slice(0, index)\n .reverse()\n .map((entry) => segments.current.get(entry.key))\n .find(Boolean);\n const from = first ? 0 : prior ? prior.end.value : 0;\n segment = { start: live(from), end: live(from), lift: live(0) };\n segments.current.set(item.key, segment);\n }\n if (first) {\n // One sweep: every segment leaves the top together, and each trailing edge follows a beat behind the one before.\n retarget(segment.start, to.start, REVEAL, paint);\n retarget(segment.end, to.end, REVEAL, paint, 40 + index * STAGGER_MS);\n return;\n }\n retarget(segment.start, to.start, SETTLE, paint);\n retarget(segment.end, to.end, SETTLE, paint, 0, leaving ? () => drop(item.key) : undefined);\n });\n paint();\n // The signature stands for the slices and the hidden set.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shown, signature, paint]);\n\n // The active segment slides out along its middle and thickens; the one it replaces settles back.\n useEffect(() => {\n for (const [key, segment] of segments.current) retarget(segment.lift, key === current ? 1 : 0, SNAPPY, paint);\n }, [current, drawn, shown, paint]);\n\n // One pointer handler for the whole ring: the angle picks the segment, so crossing a gap never drops the hover.\n const hit = (event: { clientX: number; clientY: number }) => {\n const box = svg.current?.getBoundingClientRect();\n if (!box?.width) return undefined;\n const scale = size / box.width;\n const x = (event.clientX - box.left) * scale - c;\n const y = (event.clientY - box.top) * scale - c;\n const radius = Math.hypot(x, y);\n if (radius < inner - 6 || radius > c) return null;\n let turn = (Math.atan2(y, x) + Math.PI / 2) / TAU;\n if (turn < 0) turn += 1;\n for (const [key, arc] of targets.current) {\n if (arc.end > arc.start && turn >= arc.start && turn < arc.end && visible.some((item) => item.key === key)) return key;\n }\n return undefined;\n };\n const onRingMove = (event: PointerEvent<SVGSVGElement>) => {\n if (event.pointerType !== \"mouse\") return;\n const key = hit(event);\n if (key !== undefined && key !== preview) setPreview(key);\n };\n const onRingClick = (event: MouseEvent<SVGSVGElement>) => {\n const key = hit(event);\n if (key) select(selected === key ? null : key);\n };\n\n const format = (value: number) => `${formatValue(value)}${suffix}`;\n // A share of the total only means something when the centre IS the total: with\n // `center=\"none\"` or a figure of the caller's own, the parts do not add up.\n const showShare = center === \"total\";\n const shareOf = (value: number) => (showShare ? `, ${shareText(value / (total || 1))}` : \"\");\n const describe = (item: Slice) =>\n `${item.label}, ${format(item.value)}${shareOf(item.value)}${\n item.members ? `. Includes ${item.members.map((member) => member.label).join(\", \")}` : \"\"\n }`;\n\n // Arrow keys walk the segments, in the legend or on the ring itself when there is no legend.\n const rows = useRef<(HTMLButtonElement | null)[]>([]);\n const step = (key: string, index: number, count: number) => {\n const moves: Record<string, number> = {\n ArrowDown: index + 1,\n ArrowRight: index + 1,\n ArrowUp: index - 1,\n ArrowLeft: index - 1,\n Home: 0,\n End: count - 1,\n };\n return moves[key];\n };\n const onLegendKey = (event: KeyboardEvent<HTMLButtonElement>, index: number) => {\n if (event.key === \"Escape\" && selected) {\n event.preventDefault();\n select(null);\n return;\n }\n const next = step(event.key, index, slices.length);\n if (next === undefined) return;\n event.preventDefault();\n rows.current[(next + slices.length) % slices.length]?.focus();\n };\n const onRingKey = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!visible.length) return;\n if (event.key === \"Escape\") {\n event.preventDefault();\n setPreview(null);\n if (selected) select(null);\n return;\n }\n if ((event.key === \"Enter\" || event.key === \" \") && current) {\n event.preventDefault();\n select(selected === current ? null : current);\n return;\n }\n const index = visible.findIndex((item) => item.key === current);\n const next = step(\n event.key,\n index < 0 ? (event.key.endsWith(\"Up\") || event.key.endsWith(\"Left\") ? 0 : -1) : index,\n visible.length,\n );\n if (next === undefined) return;\n event.preventDefault();\n const item = visible[(next + visible.length) % visible.length]!;\n setPreview(item.key);\n setAnnouncement(describe(item));\n };\n const onLegendBlur = (event: FocusEvent<HTMLButtonElement>) => {\n if (!(event.relatedTarget instanceof Node && event.currentTarget.closest(\"ul\")?.contains(event.relatedTarget))) {\n setPreview(null);\n }\n };\n\n // One readout per segment plus the one at rest; the active one rolls in from the side it sits on.\n // At rest: the total, nothing, or the caller's own figure (`center`); with no data, the\n // empty label.\n const rest =\n center === \"total\"\n ? { label: totalLabel, value: total as number | string }\n : center === \"none\"\n ? { label: \" \", value: \"\" }\n : center;\n const identity = activeSlice ? activeSlice.key : \"__total\";\n // A figure of the caller's own always shows, 0 included; the total shows only when there is one.\n const custom = typeof center === \"object\" && total > 0;\n const readouts = [\n {\n key: \"__total\",\n label: total ? rest.label : emptyLabel,\n value: total ? rest.value : 0,\n meta: null as Slice | null,\n custom,\n },\n ...slices.map((item) => ({\n key: item.key,\n label: item.label,\n value: item.value,\n meta: item,\n custom: false,\n })),\n ];\n const activeIndex = Math.max(0, readouts.findIndex((item) => item.key === identity));\n const restValue = (value: number | string) => (typeof value === \"number\" ? format(value) : value);\n // An empty custom figure leaves just its label, not \"Blended CTR . \".\n const restText =\n center === \"total\"\n ? `${totalLabel} ${format(total)}. `\n : center === \"none\"\n ? \"\"\n : `${[center.label, restValue(center.value)].filter(Boolean).join(\" \")}. `;\n const summary = total\n ? `${label}. ${restText}${visible.map(describe).join(\". \")}.`\n : `${label}. ${emptyLabel}.`;\n const members = (item: Slice) =>\n item.members\n ? item.members.length > 2\n ? `${item.members\n .slice(0, 2)\n .map((member) => member.label)\n .join(\", \")} and ${item.members.length - 2} more`\n : item.members.map((member) => member.label).join(\" and \")\n : null;\n\n return (\n <figure\n ref={refs}\n data-slot=\"donut-chart\"\n aria-label={label}\n data-legend={legend || undefined}\n className={cn(\"donut-chart box-border font-sans text-foreground\", className)}\n >\n <div\n data-slot=\"donut-chart-ring\"\n className=\"donut-chart-ring\"\n style={{ width: size, \"--inner\": `${((inner * 2) / size) * 100}%` } as CSSProperties}\n tabIndex={legend ? undefined : 0}\n role={legend ? undefined : \"application\"}\n aria-label={legend ? undefined : `${label}. Use the arrow keys to read each segment.`}\n onKeyDown={legend ? undefined : onRingKey}\n onBlur={legend ? undefined : () => setPreview(null)}\n >\n <svg\n ref={svg}\n viewBox={`0 0 ${size} ${size}`}\n aria-hidden=\"true\"\n focusable=\"false\"\n onPointerMove={onRingMove}\n onPointerLeave={() => setPreview(null)}\n onClick={onRingClick}\n data-hovering={current !== null || undefined}\n className=\"block size-full overflow-visible data-[hovering]:cursor-pointer\"\n >\n <circle\n className=\"donut-chart-track\"\n cx={c}\n cy={c}\n r={(outer + inner) / 2}\n strokeWidth={outer - inner}\n data-idle={!shown || !total || undefined}\n />\n {drawn.map((item) => (\n <path\n key={item.key}\n data-key={item.key}\n data-active={item.key === current || undefined}\n data-dim={(current !== null && item.key !== current) || undefined}\n className=\"donut-chart-segment\"\n style={{ \"--slice\": colorFor(item) } as CSSProperties}\n />\n ))}\n </svg>\n <div\n aria-hidden=\"true\"\n data-center={center === \"total\" || center === \"none\" ? center : \"custom\"}\n className=\"donut-chart-center\"\n >\n {readouts.map((item, index) => (\n <span\n key={item.key}\n data-place={Math.sign(index - activeIndex)}\n className=\"donut-chart-readout\"\n >\n <span className=\"block w-full min-h-5 truncate text-xs text-text-secondary\">{item.label}</span>\n <span className=\"block max-w-full min-h-8 truncate text-h3 font-medium tabular-nums\">\n {typeof item.value === \"string\" ? (\n item.value || \" \"\n ) : item.value || item.meta || item.custom ? (\n <Count value={item.value} format={formatValue} />\n ) : (\n \" \"\n )}\n </span>\n <span className=\"block w-full min-h-5 truncate text-xs tabular-nums text-muted-foreground\">\n {item.meta && showShare ? (\n <>\n <Count\n value={total && !hidden.has(item.key) ? item.meta.value / total : 0}\n format={shareText}\n />{\" \"}\n of {totalLabel.toLowerCase()}\n </>\n ) : typeof item.value === \"number\" ? (\n unit || \" \"\n ) : (\n \" \"\n )}\n </span>\n </span>\n ))}\n </div>\n </div>\n {legend && slices.length > 0 ? (\n <ul\n id={legendId}\n aria-label={`${label}, segments${legendAction === \"toggle\" ? \". Press a segment to show or hide it\" : \"\"}`}\n onPointerLeave={() => setPreview(null)}\n className=\"m-0 -mb-0.5 grid list-none p-0\"\n >\n {slices.map((item, index) => {\n const off = hidden.has(item.key);\n return (\n <li key={item.key} className=\"overflow-hidden motion-safe:animate-fade-in\">\n <button\n ref={(node) => {\n rows.current[index] = node;\n }}\n type=\"button\"\n data-slot=\"donut-chart-row\"\n aria-pressed={legendAction === \"toggle\" ? !off : selected === item.key}\n aria-label={off ? `${item.label}, hidden` : describe(item)}\n data-active={item.key === current || undefined}\n data-pinned={selected === item.key || undefined}\n data-hidden={off || undefined}\n data-share={showShare ? undefined : \"none\"}\n style={{ \"--slice\": colorFor(item) } as CSSProperties}\n onClick={() => (legendAction === \"toggle\" ? setHidden(item.key) : select(selected === item.key ? null : item.key))}\n onPointerEnter={(event) => {\n if (event.pointerType === \"mouse\") setPreview(off ? null : item.key);\n }}\n onFocus={(event) => {\n if (event.currentTarget.matches(\":focus-visible\")) setPreview(off ? null : item.key);\n }}\n onBlur={onLegendBlur}\n onKeyDown={(event) => onLegendKey(event, index)}\n className=\"donut-chart-row\"\n >\n <span aria-hidden=\"true\" className=\"donut-chart-dot\" />\n <span className=\"grid min-w-0\">\n <span className=\"donut-chart-row-label truncate\">{item.label}</span>\n {item.members ? <span className=\"truncate text-xs text-muted-foreground\">{members(item)}</span> : null}\n </span>\n <span aria-hidden=\"true\" className=\"donut-chart-row-value justify-self-end tabular-nums text-text-secondary\">\n <Count value={item.value} format={formatValue} />\n </span>\n {showShare ? (\n <span\n aria-hidden=\"true\"\n className=\"donut-chart-row-value justify-self-end font-medium tabular-nums\"\n >\n <Count value={off || !total ? 0 : item.value / total} format={shareText} />\n </span>\n ) : null}\n </button>\n </li>\n );\n })}\n </ul>\n ) : null}\n <p className=\"sr-only\">{summary}</p>\n <p className=\"sr-only\" aria-live=\"polite\">\n {announcement}\n </p>\n {total > 0 ? (\n <div className=\"sr-only\">\n <table>\n <caption>{label}</caption>\n <thead>\n <tr>\n <th scope=\"col\">Segment</th>\n <th scope=\"col\">Value</th>\n {showShare ? <th scope=\"col\">Share</th> : null}\n </tr>\n </thead>\n <tbody>\n {visible\n .flatMap((item) =>\n item.members\n ? item.members.map((member) => ({ key: member.key, label: `${member.label} (${otherLabel})`, value: member.value }))\n : [item],\n )\n .map((item) => (\n <tr key={item.key}>\n <th scope=\"row\">{item.label}</th>\n <td>{format(item.value)}</td>\n {showShare ? <td>{shareText(item.value / total)}</td> : null}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : null}\n </figure>\n );\n});\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { DonutChart, DonutChartDatum, DonutChartProps } from "./donut-chart.js";
2
- export { DonutChart, type DonutChartDatum, type DonutChartProps };
1
+ import { DonutChart, DonutChartCenter, DonutChartDatum, DonutChartProps } from "./donut-chart.js";
2
+ export { DonutChart, type DonutChartCenter, type DonutChartDatum, type DonutChartProps };
@@ -18,6 +18,12 @@ export declare const emptyStateVariants: (props?: import("class-variance-authori
18
18
  export declare const emptyStateTitleVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
19
19
  export declare const emptyStateTextVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
20
20
  export declare const emptyStateActionVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
21
+ /**
22
+ * The illustration's frame. It centres what it is given and keeps an image or
23
+ * SVG inside the box at its own aspect ratio; the illustration's SIZE is the
24
+ * caller's, because the artwork sets it, not the empty state.
25
+ */
26
+ export declare const emptyStateImageVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
21
27
  /**
22
28
  * An empty list's message.
23
29
  *
@@ -45,6 +51,21 @@ export interface EmptyStateProps extends Omit<ComponentPropsWithoutRef<"div">, "
45
51
  * swaps in place, and the same one re-rendering stays still. Mark it `aria-hidden`.
46
52
  */
47
53
  icon?: ReactNode;
54
+ /**
55
+ * An illustration above the icon and the title: an `<img>`, an inline SVG or any
56
+ * element. It is the caller's artwork, so the empty state does not hide it from
57
+ * assistive technology, and the caller names it:
58
+ * - an `<img>` takes an `alt`, or `alt=""` when it only decorates what the title
59
+ * already says;
60
+ * - an inline `<svg>` has no name of its own, so give it `role="img"` and an
61
+ * `aria-label`, or `aria-hidden="true"` when it is decorative.
62
+ *
63
+ * The slot reserves no space, so give the artwork its intrinsic size (`width` and
64
+ * `height` on an `<img>` or `<svg>`): without it the layout jumps when an image
65
+ * loads. Omitted from the DOM when absent; `false` (as in `cond && <img />`)
66
+ * counts as absent.
67
+ */
68
+ image?: ReactNode;
48
69
  /** A next step under the text: a button or a link. Omitted from the DOM when absent. */
49
70
  action?: ReactNode;
50
71
  /** Extra classes for the box. Merged last, so they win conflicts with ours. */
@@ -24,16 +24,23 @@ const emptyStateVariants = cva(["flex flex-col items-center px-5 py-12 text-cent
24
24
  const emptyStateTitleVariants = cva("m-0 mt-5 text-base leading-(--leading-normal) font-medium text-foreground");
25
25
  const emptyStateTextVariants = cva("mx-auto mt-0 mb-0 max-w-72 text-sm leading-(--leading-normal) text-pretty text-text-secondary");
26
26
  const emptyStateActionVariants = cva("mt-5 flex flex-wrap justify-center gap-3");
27
+ /**
28
+ * The illustration's frame. It centres what it is given and keeps an image or
29
+ * SVG inside the box at its own aspect ratio; the illustration's SIZE is the
30
+ * caller's, because the artwork sets it, not the empty state.
31
+ */
32
+ const emptyStateImageVariants = cva("flex max-w-full justify-center [&>img]:h-auto [&>img]:max-w-full [&>svg]:h-auto [&>svg]:max-w-full");
27
33
  /** The name an icon swaps under: a different component swaps; the same one re-rendering does not. */
28
34
  function iconKey(icon) {
29
35
  if (!isValidElement(icon)) return "icon";
30
36
  const type = icon.type;
31
37
  return typeof type === "string" ? type : type.displayName ?? type.name ?? "icon";
32
38
  }
33
- const EmptyState = forwardRef(function EmptyState({ title, description, children, icon, action, className, ...props }, ref) {
39
+ const EmptyState = forwardRef(function EmptyState({ title, description, children, icon, image, action, className, ...props }, ref) {
34
40
  const frame = useRef(null);
35
41
  const copy = useRef(null);
36
42
  const message = description ?? children;
43
+ const hasImage = image != null && typeof image !== "boolean";
37
44
  const text = (value) => typeof value === "string" ? /* @__PURE__ */ jsx(SwapText, {
38
45
  swapKey: value,
39
46
  className: "block",
@@ -46,10 +53,15 @@ const EmptyState = forwardRef(function EmptyState({ title, description, children
46
53
  "data-slot": "empty-state",
47
54
  className: cn(emptyStateVariants(), className),
48
55
  children: [
56
+ !hasImage ? null : /* @__PURE__ */ jsx("div", {
57
+ "data-slot": "empty-state-image",
58
+ className: emptyStateImageVariants(),
59
+ children: image
60
+ }),
49
61
  icon === void 0 || icon === null ? null : /* @__PURE__ */ jsx("div", {
50
62
  "data-slot": "empty-state-icon",
51
63
  "aria-hidden": "true",
52
- className: "grid size-12 place-items-center rounded-circle border-(length:--border-width) border-border bg-muted text-text-secondary motion-safe:animate-empty-state-icon [&_svg]:size-6",
64
+ className: cnAll("grid size-12 place-items-center rounded-circle border-(length:--border-width) border-border bg-muted text-text-secondary motion-safe:animate-empty-state-icon [&_svg]:size-6", hasImage && "mt-5"),
53
65
  children: /* @__PURE__ */ jsx(SwapText, {
54
66
  swapKey: iconKey(icon),
55
67
  motion: "glyph",
@@ -70,7 +82,7 @@ const EmptyState = forwardRef(function EmptyState({ title, description, children
70
82
  children: text(title)
71
83
  }), message == null ? null : /* @__PURE__ */ jsx("p", {
72
84
  "data-slot": "empty-state-text",
73
- className: cnAll(emptyStateTextVariants(), title != null ? "mt-2" : icon != null && "mt-5"),
85
+ className: cnAll(emptyStateTextVariants(), title != null ? "mt-2" : (icon != null || hasImage) && "mt-5"),
74
86
  children: text(message)
75
87
  })]
76
88
  })
@@ -84,6 +96,6 @@ const EmptyState = forwardRef(function EmptyState({ title, description, children
84
96
  });
85
97
  });
86
98
  //#endregion
87
- export { EmptyState, emptyStateActionVariants, emptyStateTextVariants, emptyStateTitleVariants, emptyStateVariants };
99
+ export { EmptyState, emptyStateActionVariants, emptyStateImageVariants, emptyStateTextVariants, emptyStateTitleVariants, emptyStateVariants };
88
100
 
89
101
  //# sourceMappingURL=empty-state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"empty-state.js","names":[],"sources":["../../src/empty-state/empty-state.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport {\n forwardRef,\n isValidElement,\n useRef,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from \"react\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { useHeightFollow } from \"../lib/height-follow.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * EmptyState: what a list shows when it has nothing to show. An icon in a\n * circle, a short title, a one- or two-line description and an optional next\n * step, centred on the page surface with room to breathe.\n *\n * Presentational, so there is no React Aria base. It exists because every\n * list has an empty case, and without a shared component each screen picks its\n * own padding, border and alignment, so \"nothing here\" looks different on\n * every page.\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 emptyStateVariants = cva([\n \"flex flex-col items-center px-5 py-12 text-center\",\n // The reset in @seekalfred/styles is opt-in, so a consumer may load none.\n // Its own text colour too, so it reads the same on any page background.\n \"box-border font-sans text-foreground\",\n]);\n\nexport const emptyStateTitleVariants = cva(\n \"m-0 mt-5 text-base leading-(--leading-normal) font-medium text-foreground\",\n);\n\nexport const emptyStateTextVariants = cva(\n \"mx-auto mt-0 mb-0 max-w-72 text-sm leading-(--leading-normal) text-pretty text-text-secondary\",\n);\n\nexport const emptyStateActionVariants = cva(\"mt-5 flex flex-wrap justify-center gap-3\");\n\n/**\n * An empty list's message.\n *\n * @example\n * <EmptyState\n * icon={<Folder aria-hidden=\"true\" />}\n * title=\"No saved views\"\n * description=\"Save a set of filters as a view to come back to it later.\"\n * action={<Button>Create view</Button>}\n * />\n */\nexport interface EmptyStateProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"title\"> {\n /**\n * A short heading above the description. When it is set, `description` (or\n * `children`) is the supporting text under it.\n */\n title?: ReactNode;\n /** One or two lines of supporting text. `children` is the same slot and works the same way. */\n description?: ReactNode;\n /** Why the list is empty, in one short statement. The message when there is no `description`. */\n children?: ReactNode;\n /**\n * A glyph above the message, inside a circle. It settles in once when the state first appears\n * (small and transparent to rest over 480ms); a different icon component later\n * swaps in place, and the same one re-rendering stays still. Mark it `aria-hidden`.\n */\n icon?: ReactNode;\n /** A next step under the text: a button or a link. Omitted from the DOM when absent. */\n action?: ReactNode;\n /** Extra classes for the box. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/** The name an icon swaps under: a different component swaps; the same one re-rendering does not. */\nfunction iconKey(icon: ReactNode) {\n if (!isValidElement(icon)) return \"icon\";\n const type = icon.type as string | { displayName?: string; name?: string };\n return typeof type === \"string\" ? type : (type.displayName ?? type.name ?? \"icon\");\n}\n\nexport const EmptyState = forwardRef<HTMLDivElement, EmptyStateProps>(function EmptyState(\n { title, description, children, icon, action, className, ...props },\n ref,\n) {\n const frame = useRef<HTMLDivElement>(null);\n const copy = useRef<HTMLDivElement>(null);\n const message = description ?? children;\n const text = (value: ReactNode) =>\n typeof value === \"string\" ? (\n <SwapText swapKey={value} className=\"block\">\n {value}\n </SwapText>\n ) : (\n value\n );\n // The copy column follows its height when the title or the message changes.\n useHeightFollow(\n frame,\n copy,\n `${typeof title === \"string\" ? title : \"\"}\\n${typeof message === \"string\" ? message : \"\"}`,\n );\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"empty-state\"\n className={cn(emptyStateVariants(), className)}\n >\n {icon === undefined || icon === null ? null : (\n <div\n data-slot=\"empty-state-icon\"\n aria-hidden=\"true\"\n className=\"grid size-12 place-items-center rounded-circle border-(length:--border-width) border-border bg-muted text-text-secondary motion-safe:animate-empty-state-icon [&_svg]:size-6\"\n >\n <SwapText swapKey={iconKey(icon)} motion=\"glyph\" className=\"grid place-items-center\">\n {icon}\n </SwapText>\n </div>\n )}\n <div ref={frame} data-slot=\"empty-state-copy\" className=\"height-follow w-full\">\n <div ref={copy} className=\"flow-root\">\n {title == null ? null : (\n <h3 data-slot=\"empty-state-title\" className={emptyStateTitleVariants()}>\n {text(title)}\n </h3>\n )}\n {message == null ? null : (\n <p\n data-slot=\"empty-state-text\"\n className={cnAll(\n emptyStateTextVariants(),\n // Under the title it sits close; straight under the icon it clears it.\n title != null ? \"mt-2\" : icon != null && \"mt-5\",\n )}\n >\n {text(message)}\n </p>\n )}\n </div>\n </div>\n {action == null ? null : (\n <div data-slot=\"empty-state-action\" className={emptyStateActionVariants()}>\n {action}\n </div>\n )}\n </div>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,qBAAqB,IAAI,CACpC,qDAGA,sCACF,CAAC;AAED,MAAa,0BAA0B,IACrC,2EACF;AAEA,MAAa,yBAAyB,IACpC,+FACF;AAEA,MAAa,2BAA2B,IAAI,0CAA0C;;AAoCtF,SAAS,QAAQ,MAAiB;CAChC,IAAI,CAAC,eAAe,IAAI,GAAG,OAAO;CAClC,MAAM,OAAO,KAAK;CAClB,OAAO,OAAO,SAAS,WAAW,OAAQ,KAAK,eAAe,KAAK,QAAQ;AAC7E;AAEA,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,OAAO,aAAa,UAAU,MAAM,QAAQ,WAAW,GAAG,SAC5D,KACA;CACA,MAAM,QAAQ,OAAuB,IAAI;CACzC,MAAM,OAAO,OAAuB,IAAI;CACxC,MAAM,UAAU,eAAe;CAC/B,MAAM,QAAQ,UACZ,OAAO,UAAU,WACf,oBAAC,UAAD;EAAU,SAAS;EAAO,WAAU;EACjC,UAAA;CACO,CAAA,IAEV;CAGJ,gBACE,OACA,MACA,GAAG,OAAO,UAAU,WAAW,QAAQ,GAAG,IAAI,OAAO,YAAY,WAAW,UAAU,IACxF;CACA,OACE,qBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;EAJ/C,UAAA;GAMG,SAAS,KAAA,KAAa,SAAS,OAAO,OACrC,oBAAC,OAAD;IACE,aAAU;IACV,eAAY;IACZ,WAAU;IAEV,UAAA,oBAAC,UAAD;KAAU,SAAS,QAAQ,IAAI;KAAG,QAAO;KAAQ,WAAU;KACxD,UAAA;IACO,CAAA;GACP,CAAA;GAEP,oBAAC,OAAD;IAAK,KAAK;IAAO,aAAU;IAAmB,WAAU;IACtD,UAAA,qBAAC,OAAD;KAAK,KAAK;KAAM,WAAU;KAA1B,UAAA,CACG,SAAS,OAAO,OACf,oBAAC,MAAD;MAAI,aAAU;MAAoB,WAAW,wBAAwB;MAClE,UAAA,KAAK,KAAK;KACT,CAAA,GAEL,WAAW,OAAO,OACjB,oBAAC,KAAD;MACE,aAAU;MACV,WAAW,MACT,uBAAuB,GAEvB,SAAS,OAAO,SAAS,QAAQ,QAAQ,MAC3C;MAEC,UAAA,KAAK,OAAO;KACZ,CAAA,CAEF;;GACF,CAAA;GACJ,UAAU,OAAO,OAChB,oBAAC,OAAD;IAAK,aAAU;IAAqB,WAAW,yBAAyB;IACrE,UAAA;GACE,CAAA;EAEJ;;AAET,CAAC"}
1
+ {"version":3,"file":"empty-state.js","names":[],"sources":["../../src/empty-state/empty-state.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport {\n forwardRef,\n isValidElement,\n useRef,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from \"react\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport { useHeightFollow } from \"../lib/height-follow.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * EmptyState: what a list shows when it has nothing to show. An icon in a\n * circle, a short title, a one- or two-line description and an optional next\n * step, centred on the page surface with room to breathe.\n *\n * Presentational, so there is no React Aria base. It exists because every\n * list has an empty case, and without a shared component each screen picks its\n * own padding, border and alignment, so \"nothing here\" looks different on\n * every page.\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 emptyStateVariants = cva([\n \"flex flex-col items-center px-5 py-12 text-center\",\n // The reset in @seekalfred/styles is opt-in, so a consumer may load none.\n // Its own text colour too, so it reads the same on any page background.\n \"box-border font-sans text-foreground\",\n]);\n\nexport const emptyStateTitleVariants = cva(\n \"m-0 mt-5 text-base leading-(--leading-normal) font-medium text-foreground\",\n);\n\nexport const emptyStateTextVariants = cva(\n \"mx-auto mt-0 mb-0 max-w-72 text-sm leading-(--leading-normal) text-pretty text-text-secondary\",\n);\n\nexport const emptyStateActionVariants = cva(\"mt-5 flex flex-wrap justify-center gap-3\");\n\n/**\n * The illustration's frame. It centres what it is given and keeps an image or\n * SVG inside the box at its own aspect ratio; the illustration's SIZE is the\n * caller's, because the artwork sets it, not the empty state.\n */\nexport const emptyStateImageVariants = cva(\n \"flex max-w-full justify-center [&>img]:h-auto [&>img]:max-w-full [&>svg]:h-auto [&>svg]:max-w-full\",\n);\n\n/**\n * An empty list's message.\n *\n * @example\n * <EmptyState\n * icon={<Folder aria-hidden=\"true\" />}\n * title=\"No saved views\"\n * description=\"Save a set of filters as a view to come back to it later.\"\n * action={<Button>Create view</Button>}\n * />\n */\nexport interface EmptyStateProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"title\"> {\n /**\n * A short heading above the description. When it is set, `description` (or\n * `children`) is the supporting text under it.\n */\n title?: ReactNode;\n /** One or two lines of supporting text. `children` is the same slot and works the same way. */\n description?: ReactNode;\n /** Why the list is empty, in one short statement. The message when there is no `description`. */\n children?: ReactNode;\n /**\n * A glyph above the message, inside a circle. It settles in once when the state first appears\n * (small and transparent to rest over 480ms); a different icon component later\n * swaps in place, and the same one re-rendering stays still. Mark it `aria-hidden`.\n */\n icon?: ReactNode;\n /**\n * An illustration above the icon and the title: an `<img>`, an inline SVG or any\n * element. It is the caller's artwork, so the empty state does not hide it from\n * assistive technology, and the caller names it:\n * - an `<img>` takes an `alt`, or `alt=\"\"` when it only decorates what the title\n * already says;\n * - an inline `<svg>` has no name of its own, so give it `role=\"img\"` and an\n * `aria-label`, or `aria-hidden=\"true\"` when it is decorative.\n *\n * The slot reserves no space, so give the artwork its intrinsic size (`width` and\n * `height` on an `<img>` or `<svg>`): without it the layout jumps when an image\n * loads. Omitted from the DOM when absent; `false` (as in `cond && <img />`)\n * counts as absent.\n */\n image?: ReactNode;\n /** A next step under the text: a button or a link. Omitted from the DOM when absent. */\n action?: ReactNode;\n /** Extra classes for the box. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/** The name an icon swaps under: a different component swaps; the same one re-rendering does not. */\nfunction iconKey(icon: ReactNode) {\n if (!isValidElement(icon)) return \"icon\";\n const type = icon.type as string | { displayName?: string; name?: string };\n return typeof type === \"string\" ? type : (type.displayName ?? type.name ?? \"icon\");\n}\n\nexport const EmptyState = forwardRef<HTMLDivElement, EmptyStateProps>(function EmptyState(\n { title, description, children, icon, image, action, className, ...props },\n ref,\n) {\n const frame = useRef<HTMLDivElement>(null);\n const copy = useRef<HTMLDivElement>(null);\n const message = description ?? children;\n // `cond && <img />` hands over `false` when the condition fails: absent, not\n // an empty slot with its spacing.\n const hasImage = image != null && typeof image !== \"boolean\";\n const text = (value: ReactNode) =>\n typeof value === \"string\" ? (\n <SwapText swapKey={value} className=\"block\">\n {value}\n </SwapText>\n ) : (\n value\n );\n // The copy column follows its height when the title or the message changes.\n useHeightFollow(\n frame,\n copy,\n `${typeof title === \"string\" ? title : \"\"}\\n${typeof message === \"string\" ? message : \"\"}`,\n );\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"empty-state\"\n className={cn(emptyStateVariants(), className)}\n >\n {!hasImage ? null : (\n <div data-slot=\"empty-state-image\" className={emptyStateImageVariants()}>\n {image}\n </div>\n )}\n {icon === undefined || icon === null ? null : (\n <div\n data-slot=\"empty-state-icon\"\n aria-hidden=\"true\"\n className={cnAll(\n \"grid size-12 place-items-center rounded-circle border-(length:--border-width) border-border bg-muted text-text-secondary motion-safe:animate-empty-state-icon [&_svg]:size-6\",\n // Under an illustration it clears it by the same step as the title.\n hasImage && \"mt-5\",\n )}\n >\n <SwapText swapKey={iconKey(icon)} motion=\"glyph\" className=\"grid place-items-center\">\n {icon}\n </SwapText>\n </div>\n )}\n <div ref={frame} data-slot=\"empty-state-copy\" className=\"height-follow w-full\">\n <div ref={copy} className=\"flow-root\">\n {title == null ? null : (\n <h3 data-slot=\"empty-state-title\" className={emptyStateTitleVariants()}>\n {text(title)}\n </h3>\n )}\n {message == null ? null : (\n <p\n data-slot=\"empty-state-text\"\n className={cnAll(\n emptyStateTextVariants(),\n // Under the title it sits close; straight under the icon or the\n // illustration it clears it.\n title != null ? \"mt-2\" : (icon != null || hasImage) && \"mt-5\",\n )}\n >\n {text(message)}\n </p>\n )}\n </div>\n </div>\n {action == null ? null : (\n <div data-slot=\"empty-state-action\" className={emptyStateActionVariants()}>\n {action}\n </div>\n )}\n </div>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,qBAAqB,IAAI,CACpC,qDAGA,sCACF,CAAC;AAED,MAAa,0BAA0B,IACrC,2EACF;AAEA,MAAa,yBAAyB,IACpC,+FACF;AAEA,MAAa,2BAA2B,IAAI,0CAA0C;;;;;;AAOtF,MAAa,0BAA0B,IACrC,oGACF;;AAmDA,SAAS,QAAQ,MAAiB;CAChC,IAAI,CAAC,eAAe,IAAI,GAAG,OAAO;CAClC,MAAM,OAAO,KAAK;CAClB,OAAO,OAAO,SAAS,WAAW,OAAQ,KAAK,eAAe,KAAK,QAAQ;AAC7E;AAEA,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,OAAO,aAAa,UAAU,MAAM,OAAO,QAAQ,WAAW,GAAG,SACnE,KACA;CACA,MAAM,QAAQ,OAAuB,IAAI;CACzC,MAAM,OAAO,OAAuB,IAAI;CACxC,MAAM,UAAU,eAAe;CAG/B,MAAM,WAAW,SAAS,QAAQ,OAAO,UAAU;CACnD,MAAM,QAAQ,UACZ,OAAO,UAAU,WACf,oBAAC,UAAD;EAAU,SAAS;EAAO,WAAU;EACjC,UAAA;CACO,CAAA,IAEV;CAGJ,gBACE,OACA,MACA,GAAG,OAAO,UAAU,WAAW,QAAQ,GAAG,IAAI,OAAO,YAAY,WAAW,UAAU,IACxF;CACA,OACE,qBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;EAJ/C,UAAA;GAMG,CAAC,WAAW,OACX,oBAAC,OAAD;IAAK,aAAU;IAAoB,WAAW,wBAAwB;IACnE,UAAA;GACE,CAAA;GAEN,SAAS,KAAA,KAAa,SAAS,OAAO,OACrC,oBAAC,OAAD;IACE,aAAU;IACV,eAAY;IACZ,WAAW,MACT,gLAEA,YAAY,MACd;IAEA,UAAA,oBAAC,UAAD;KAAU,SAAS,QAAQ,IAAI;KAAG,QAAO;KAAQ,WAAU;KACxD,UAAA;IACO,CAAA;GACP,CAAA;GAEP,oBAAC,OAAD;IAAK,KAAK;IAAO,aAAU;IAAmB,WAAU;IACtD,UAAA,qBAAC,OAAD;KAAK,KAAK;KAAM,WAAU;KAA1B,UAAA,CACG,SAAS,OAAO,OACf,oBAAC,MAAD;MAAI,aAAU;MAAoB,WAAW,wBAAwB;MAClE,UAAA,KAAK,KAAK;KACT,CAAA,GAEL,WAAW,OAAO,OACjB,oBAAC,KAAD;MACE,aAAU;MACV,WAAW,MACT,uBAAuB,GAGvB,SAAS,OAAO,UAAU,QAAQ,QAAQ,aAAa,MACzD;MAEC,UAAA,KAAK,OAAO;KACZ,CAAA,CAEF;;GACF,CAAA;GACJ,UAAU,OAAO,OAChB,oBAAC,OAAD;IAAK,aAAU;IAAqB,WAAW,yBAAyB;IACrE,UAAA;GACE,CAAA;EAEJ;;AAET,CAAC"}
@@ -1,2 +1,2 @@
1
- import { EmptyState, EmptyStateProps, emptyStateActionVariants, emptyStateTextVariants, emptyStateTitleVariants, emptyStateVariants } from "./empty-state.js";
2
- export { EmptyState, type EmptyStateProps, emptyStateActionVariants, emptyStateTextVariants, emptyStateTitleVariants, emptyStateVariants };
1
+ import { EmptyState, EmptyStateProps, emptyStateActionVariants, emptyStateImageVariants, emptyStateTextVariants, emptyStateTitleVariants, emptyStateVariants } from "./empty-state.js";
2
+ export { EmptyState, type EmptyStateProps, emptyStateActionVariants, emptyStateImageVariants, emptyStateTextVariants, emptyStateTitleVariants, emptyStateVariants };
@@ -1,2 +1,2 @@
1
- import { EmptyState, emptyStateActionVariants, emptyStateTextVariants, emptyStateTitleVariants, emptyStateVariants } from "./empty-state.js";
2
- export { EmptyState, emptyStateActionVariants, emptyStateTextVariants, emptyStateTitleVariants, emptyStateVariants };
1
+ import { EmptyState, emptyStateActionVariants, emptyStateImageVariants, emptyStateTextVariants, emptyStateTitleVariants, emptyStateVariants } from "./empty-state.js";
2
+ export { EmptyState, emptyStateActionVariants, emptyStateImageVariants, emptyStateTextVariants, emptyStateTitleVariants, emptyStateVariants };
@@ -33,7 +33,10 @@ import { Key } from "react-aria-components";
33
33
  * Accessibility comes from the parts: the panel is a dialog named by its
34
34
  * trigger, the trigger carries `aria-expanded`, focus moves into the panel and
35
35
  * is contained there, Escape closes it and focus returns to the trigger, and
36
- * each list is a real menu with arrow keys and type-ahead.
36
+ * each list is a real menu with arrow keys and type-ahead. Removing a chip, by
37
+ * keyboard or pointer, moves focus to the chip that takes its place (the one
38
+ * before it when it was last); removing the last chip, or Clear all, moves it to
39
+ * the Add filter button.
37
40
  */
38
41
  /** The toolbar: one pill with a light border on the surface. */
39
42
  export declare const filterBarVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
@@ -45,7 +45,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
45
45
  * Accessibility comes from the parts: the panel is a dialog named by its
46
46
  * trigger, the trigger carries `aria-expanded`, focus moves into the panel and
47
47
  * is contained there, Escape closes it and focus returns to the trigger, and
48
- * each list is a real menu with arrow keys and type-ahead.
48
+ * each list is a real menu with arrow keys and type-ahead. Removing a chip, by
49
+ * keyboard or pointer, moves focus to the chip that takes its place (the one
50
+ * before it when it was last); removing the last chip, or Clear all, moves it to
51
+ * the Add filter button.
49
52
  */
50
53
  /** The toolbar: one pill with a light border on the surface. */
51
54
  const filterBarVariants = cva([
@@ -140,6 +143,23 @@ function Slot({ leaving, ready, onDone, id, children }) {
140
143
  children
141
144
  });
142
145
  }
146
+ /**
147
+ * A chip's remove button. It registers itself with the bar under its chip's id
148
+ * through a ref callback that stays the same across renders, so React does not
149
+ * detach and re-attach it on every commit.
150
+ */
151
+ function ChipRemove({ id, label, register, onPress }) {
152
+ const ref = useCallback((node) => register(id, node), [register, id]);
153
+ return /* @__PURE__ */ jsx(Button$1, {
154
+ ref,
155
+ variant: "ghost",
156
+ size: "sm",
157
+ "aria-label": label,
158
+ onPress,
159
+ className: "h-6 rounded-full px-1 data-[focus-visible]:-outline-offset-(--focus-ring-width)",
160
+ children: /* @__PURE__ */ jsx(CrossGlyph, {})
161
+ });
162
+ }
143
163
  /** A chip's text: a changed value swaps (the new text rises in) and the chip's width springs to it. */
144
164
  function ChipText({ text }) {
145
165
  const content = useRef(null);
@@ -321,8 +341,30 @@ const FilterBar = forwardRef(function FilterBar({ inline, end, filters, onClearA
321
341
  }] : []]);
322
342
  const frame = useRef(null);
323
343
  const row = useRef(null);
324
- useHeightFollow(frame, row, chips.map((chip) => chip.id).join());
344
+ const chipKey = chips.map((chip) => chip.id).join();
345
+ useHeightFollow(frame, row, chipKey);
346
+ const removeButtons = useRef(/* @__PURE__ */ new Map());
347
+ const registerRemove = useCallback((id, node) => {
348
+ if (node) removeButtons.current.set(id, node);
349
+ else removeButtons.current.delete(id);
350
+ }, []);
351
+ const addButton = useRef(null);
352
+ const focusAfter = useRef(null);
353
+ const noteFocusAfter = (after) => {
354
+ focusAfter.current = after;
355
+ requestAnimationFrame(() => {
356
+ if (focusAfter.current === after) focusAfter.current = null;
357
+ });
358
+ };
359
+ useIsomorphicLayoutEffect(() => {
360
+ const after = focusAfter.current;
361
+ if (after === null) return;
362
+ focusAfter.current = null;
363
+ const next = after === "add" || chips.length === 0 ? null : chips[Math.min(after, chips.length - 1)];
364
+ (next ? removeButtons.current.get(next.id) : addButton.current)?.focus();
365
+ }, [chipKey]);
325
366
  const clearAll = () => {
367
+ noteFocusAfter("add");
326
368
  if (onClearAll) onClearAll();
327
369
  else for (const c of chips) c.clear();
328
370
  };
@@ -355,13 +397,14 @@ const FilterBar = forwardRef(function FilterBar({ inline, end, filters, onClearA
355
397
  children: /* @__PURE__ */ jsxs("span", {
356
398
  "data-slot": "filter-bar-chip",
357
399
  className: filterBarChipVariants(),
358
- children: [/* @__PURE__ */ jsx(ChipText, { text: item.chip.text }), /* @__PURE__ */ jsx(Button$1, {
359
- variant: "ghost",
360
- size: "sm",
361
- "aria-label": removeLabel(item.chip.text),
362
- onPress: item.chip.clear,
363
- className: "h-6 px-1",
364
- children: /* @__PURE__ */ jsx(CrossGlyph, {})
400
+ children: [/* @__PURE__ */ jsx(ChipText, { text: item.chip.text }), /* @__PURE__ */ jsx(ChipRemove, {
401
+ id: item.id,
402
+ label: removeLabel(item.chip.text),
403
+ register: registerRemove,
404
+ onPress: () => {
405
+ noteFocusAfter(chips.findIndex((chip) => chip.id === item.id));
406
+ item.chip.clear();
407
+ }
365
408
  })]
366
409
  })
367
410
  }, item.id) : /* @__PURE__ */ jsx(Slot, {
@@ -389,6 +432,7 @@ const FilterBar = forwardRef(function FilterBar({ inline, end, filters, onClearA
389
432
  isOpen: open,
390
433
  onOpenChange: setOpen,
391
434
  children: [/* @__PURE__ */ jsx(Button$1, {
435
+ ref: addButton,
392
436
  variant: "outlineNeutral",
393
437
  size: "sm",
394
438
  startIcon: /* @__PURE__ */ jsx(PlusGlyph, {}),