@rtkelly-labs/design-system 0.0.0-bootstrap.0 → 0.16.0-dev.0.37dedac

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 (284) hide show
  1. package/CHANGELOG.md +347 -0
  2. package/LICENSE +21 -0
  3. package/README.md +317 -2
  4. package/dist/components/AlertDialog.js +95 -0
  5. package/dist/components/AlertDialog.js.map +1 -0
  6. package/dist/components/AppShell.js +261 -0
  7. package/dist/components/AppShell.js.map +1 -0
  8. package/dist/components/AsciiDivider.js +8 -0
  9. package/dist/components/AsciiDivider.js.map +1 -0
  10. package/dist/components/AsciiGauge.js +130 -0
  11. package/dist/components/AsciiGauge.js.map +1 -0
  12. package/dist/components/Avatar.js +87 -0
  13. package/dist/components/Avatar.js.map +1 -0
  14. package/dist/components/Badge.js +25 -0
  15. package/dist/components/Badge.js.map +1 -0
  16. package/dist/components/BarChart.js +288 -0
  17. package/dist/components/BarChart.js.map +1 -0
  18. package/dist/components/BracketText.js +23 -0
  19. package/dist/components/BracketText.js.map +1 -0
  20. package/dist/components/BulletChart.js +50 -0
  21. package/dist/components/BulletChart.js.map +1 -0
  22. package/dist/components/Button.js +130 -0
  23. package/dist/components/Button.js.map +1 -0
  24. package/dist/components/Card.js +181 -0
  25. package/dist/components/Card.js.map +1 -0
  26. package/dist/components/ChartTooltip.js +54 -0
  27. package/dist/components/ChartTooltip.js.map +1 -0
  28. package/dist/components/Checkbox.js +55 -0
  29. package/dist/components/Checkbox.js.map +1 -0
  30. package/dist/components/ColophonCard.js +122 -0
  31. package/dist/components/ColophonCard.js.map +1 -0
  32. package/dist/components/DataTable.js +299 -0
  33. package/dist/components/DataTable.js.map +1 -0
  34. package/dist/components/Divider.js +42 -0
  35. package/dist/components/Divider.js.map +1 -0
  36. package/dist/components/Drawer.js +81 -0
  37. package/dist/components/Drawer.js.map +1 -0
  38. package/dist/components/EmptyState.js +39 -0
  39. package/dist/components/EmptyState.js.map +1 -0
  40. package/dist/components/ErrorSummary.js +122 -0
  41. package/dist/components/ErrorSummary.js.map +1 -0
  42. package/dist/components/Fieldset.js +37 -0
  43. package/dist/components/Fieldset.js.map +1 -0
  44. package/dist/components/Glyph.js +60 -0
  45. package/dist/components/Glyph.js.map +1 -0
  46. package/dist/components/Input.js +111 -0
  47. package/dist/components/Input.js.map +1 -0
  48. package/dist/components/LinkProvider.js +76 -0
  49. package/dist/components/LinkProvider.js.map +1 -0
  50. package/dist/components/Menu.js +144 -0
  51. package/dist/components/Menu.js.map +1 -0
  52. package/dist/components/MetadataGrid.js +184 -0
  53. package/dist/components/MetadataGrid.js.map +1 -0
  54. package/dist/components/MobileNav.js +120 -0
  55. package/dist/components/MobileNav.js.map +1 -0
  56. package/dist/components/Modal.js +79 -0
  57. package/dist/components/Modal.js.map +1 -0
  58. package/dist/components/NerdIcon.js +177 -0
  59. package/dist/components/NerdIcon.js.map +1 -0
  60. package/dist/components/NoteBlock.js +84 -0
  61. package/dist/components/NoteBlock.js.map +1 -0
  62. package/dist/components/PageHeader.js +34 -0
  63. package/dist/components/PageHeader.js.map +1 -0
  64. package/dist/components/PageTitle.js +21 -0
  65. package/dist/components/PageTitle.js.map +1 -0
  66. package/dist/components/Pagination.js +96 -0
  67. package/dist/components/Pagination.js.map +1 -0
  68. package/dist/components/Popover.js +87 -0
  69. package/dist/components/Popover.js.map +1 -0
  70. package/dist/components/Progress.js +83 -0
  71. package/dist/components/Progress.js.map +1 -0
  72. package/dist/components/RadioGroup.js +74 -0
  73. package/dist/components/RadioGroup.js.map +1 -0
  74. package/dist/components/ReportDocument.js +75 -0
  75. package/dist/components/ReportDocument.js.map +1 -0
  76. package/dist/components/SectionContainer.js +18 -0
  77. package/dist/components/SectionContainer.js.map +1 -0
  78. package/dist/components/Select.js +199 -0
  79. package/dist/components/Select.js.map +1 -0
  80. package/dist/components/SiteFooter.js +29 -0
  81. package/dist/components/SiteFooter.js.map +1 -0
  82. package/dist/components/SiteHeader.js +59 -0
  83. package/dist/components/SiteHeader.js.map +1 -0
  84. package/dist/components/SiteNav.js +141 -0
  85. package/dist/components/SiteNav.js.map +1 -0
  86. package/dist/components/Skeleton.js +51 -0
  87. package/dist/components/Skeleton.js.map +1 -0
  88. package/dist/components/SocialIcon.js +77 -0
  89. package/dist/components/SocialIcon.js.map +1 -0
  90. package/dist/components/Sparkline.js +104 -0
  91. package/dist/components/Sparkline.js.map +1 -0
  92. package/dist/components/Spinner.js +65 -0
  93. package/dist/components/Spinner.js.map +1 -0
  94. package/dist/components/StatCard.js +53 -0
  95. package/dist/components/StatCard.js.map +1 -0
  96. package/dist/components/StatusPage.js +99 -0
  97. package/dist/components/StatusPage.js.map +1 -0
  98. package/dist/components/Swatch.js +55 -0
  99. package/dist/components/Swatch.js.map +1 -0
  100. package/dist/components/Switch.js +53 -0
  101. package/dist/components/Switch.js.map +1 -0
  102. package/dist/components/TLDR.js +53 -0
  103. package/dist/components/TLDR.js.map +1 -0
  104. package/dist/components/Table.js +155 -0
  105. package/dist/components/Table.js.map +1 -0
  106. package/dist/components/Tabs.js +325 -0
  107. package/dist/components/Tabs.js.map +1 -0
  108. package/dist/components/Tag.js +19 -0
  109. package/dist/components/Tag.js.map +1 -0
  110. package/dist/components/TelemetryGauge.js +112 -0
  111. package/dist/components/TelemetryGauge.js.map +1 -0
  112. package/dist/components/ThemeProvider.js +87 -0
  113. package/dist/components/ThemeProvider.js.map +1 -0
  114. package/dist/components/TickerTape.js +196 -0
  115. package/dist/components/TickerTape.js.map +1 -0
  116. package/dist/components/Toast.js +200 -0
  117. package/dist/components/Toast.js.map +1 -0
  118. package/dist/components/Tooltip.js +69 -0
  119. package/dist/components/Tooltip.js.map +1 -0
  120. package/dist/components/TrackCard.js +206 -0
  121. package/dist/components/TrackCard.js.map +1 -0
  122. package/dist/components/blog/BlogPost.js +116 -0
  123. package/dist/components/blog/BlogPost.js.map +1 -0
  124. package/dist/components/blog/LoremIpsumPost.js +62 -0
  125. package/dist/components/blog/LoremIpsumPost.js.map +1 -0
  126. package/dist/components/blog/author.js +25 -0
  127. package/dist/components/blog/author.js.map +1 -0
  128. package/dist/components/booleanControl.js +50 -0
  129. package/dist/components/booleanControl.js.map +1 -0
  130. package/dist/components/dialogPopupRef.js +28 -0
  131. package/dist/components/dialogPopupRef.js.map +1 -0
  132. package/dist/components/dialogSurface.js +75 -0
  133. package/dist/components/dialogSurface.js.map +1 -0
  134. package/dist/components/docs/AnchorHeading.js +80 -0
  135. package/dist/components/docs/AnchorHeading.js.map +1 -0
  136. package/dist/components/docs/Breadcrumbs.js +26 -0
  137. package/dist/components/docs/Breadcrumbs.js.map +1 -0
  138. package/dist/components/docs/CodeBlock.js +63 -0
  139. package/dist/components/docs/CodeBlock.js.map +1 -0
  140. package/dist/components/docs/CodeTabs.js +71 -0
  141. package/dist/components/docs/CodeTabs.js.map +1 -0
  142. package/dist/components/docs/DocPager.js +28 -0
  143. package/dist/components/docs/DocPager.js.map +1 -0
  144. package/dist/components/docs/DocsHeader.js +124 -0
  145. package/dist/components/docs/DocsHeader.js.map +1 -0
  146. package/dist/components/docs/DocsLayout.js +61 -0
  147. package/dist/components/docs/DocsLayout.js.map +1 -0
  148. package/dist/components/docs/DocsLinkProvider.js +25 -0
  149. package/dist/components/docs/DocsLinkProvider.js.map +1 -0
  150. package/dist/components/docs/DocsSidebar.js +147 -0
  151. package/dist/components/docs/DocsSidebar.js.map +1 -0
  152. package/dist/components/docs/Prose.js +56 -0
  153. package/dist/components/docs/Prose.js.map +1 -0
  154. package/dist/components/docs/TableOfContents.js +71 -0
  155. package/dist/components/docs/TableOfContents.js.map +1 -0
  156. package/dist/components/docs/codeTabsStore.js +75 -0
  157. package/dist/components/docs/codeTabsStore.js.map +1 -0
  158. package/dist/components/docs/figures/ActivityGrid.js +116 -0
  159. package/dist/components/docs/figures/ActivityGrid.js.map +1 -0
  160. package/dist/components/docs/figures/BeforeAfter.js +64 -0
  161. package/dist/components/docs/figures/BeforeAfter.js.map +1 -0
  162. package/dist/components/docs/figures/ChangeSummary.js +74 -0
  163. package/dist/components/docs/figures/ChangeSummary.js.map +1 -0
  164. package/dist/components/docs/figures/Decision.js +34 -0
  165. package/dist/components/docs/figures/Decision.js.map +1 -0
  166. package/dist/components/docs/figures/DrawingSheet.js +37 -0
  167. package/dist/components/docs/figures/DrawingSheet.js.map +1 -0
  168. package/dist/components/docs/figures/FigureFrame.js +226 -0
  169. package/dist/components/docs/figures/FigureFrame.js.map +1 -0
  170. package/dist/components/docs/figures/FileChangeTree.js +134 -0
  171. package/dist/components/docs/figures/FileChangeTree.js.map +1 -0
  172. package/dist/components/docs/figures/FlowDiagram.js +82 -0
  173. package/dist/components/docs/figures/FlowDiagram.js.map +1 -0
  174. package/dist/components/docs/figures/GanttChart.js +79 -0
  175. package/dist/components/docs/figures/GanttChart.js.map +1 -0
  176. package/dist/components/docs/figures/IndexedSection.js +45 -0
  177. package/dist/components/docs/figures/IndexedSection.js.map +1 -0
  178. package/dist/components/docs/figures/MetricStrip.js +23 -0
  179. package/dist/components/docs/figures/MetricStrip.js.map +1 -0
  180. package/dist/components/docs/figures/Steps.js +58 -0
  181. package/dist/components/docs/figures/Steps.js.map +1 -0
  182. package/dist/components/docs/figures/SwatchCompare.js +42 -0
  183. package/dist/components/docs/figures/SwatchCompare.js.map +1 -0
  184. package/dist/components/docs/figures/TermGrid.js +22 -0
  185. package/dist/components/docs/figures/TermGrid.js.map +1 -0
  186. package/dist/components/docs/figures/Terminal.js +37 -0
  187. package/dist/components/docs/figures/Terminal.js.map +1 -0
  188. package/dist/components/docs/figures/Timeline.js +61 -0
  189. package/dist/components/docs/figures/Timeline.js.map +1 -0
  190. package/dist/components/docs/figures/TokenTable.js +42 -0
  191. package/dist/components/docs/figures/TokenTable.js.map +1 -0
  192. package/dist/components/docs/figures/TreeDiagram.js +76 -0
  193. package/dist/components/docs/figures/TreeDiagram.js.map +1 -0
  194. package/dist/components/docs/figures/UptimeStrip.js +68 -0
  195. package/dist/components/docs/figures/UptimeStrip.js.map +1 -0
  196. package/dist/components/docs/figures/index.js +24 -0
  197. package/dist/components/docs/figures/index.js.map +1 -0
  198. package/dist/components/docs/index.js +15 -0
  199. package/dist/components/docs/index.js.map +1 -0
  200. package/dist/components/docs/mdxComponents.js +108 -0
  201. package/dist/components/docs/mdxComponents.js.map +1 -0
  202. package/dist/components/experiments/DesignSandbox.js +206 -0
  203. package/dist/components/experiments/DesignSandbox.js.map +1 -0
  204. package/dist/components/experiments/ExperimentsView.js +138 -0
  205. package/dist/components/experiments/ExperimentsView.js.map +1 -0
  206. package/dist/components/fieldFrame.js +200 -0
  207. package/dist/components/fieldFrame.js.map +1 -0
  208. package/dist/components/floatingSurface.js +35 -0
  209. package/dist/components/floatingSurface.js.map +1 -0
  210. package/dist/components/marketing/CTASection.js +47 -0
  211. package/dist/components/marketing/CTASection.js.map +1 -0
  212. package/dist/components/marketing/FeatureGrid.js +49 -0
  213. package/dist/components/marketing/FeatureGrid.js.map +1 -0
  214. package/dist/components/marketing/Hero.js +44 -0
  215. package/dist/components/marketing/Hero.js.map +1 -0
  216. package/dist/components/marketing/PricingGrid.js +65 -0
  217. package/dist/components/marketing/PricingGrid.js.map +1 -0
  218. package/dist/components/marketing/SectionGrid.js +51 -0
  219. package/dist/components/marketing/SectionGrid.js.map +1 -0
  220. package/dist/components/paginationRange.js +28 -0
  221. package/dist/components/paginationRange.js.map +1 -0
  222. package/dist/components/portalTheme.js +14 -0
  223. package/dist/components/portalTheme.js.map +1 -0
  224. package/dist/components/saas/SaasLandingPage.js +201 -0
  225. package/dist/components/saas/SaasLandingPage.js.map +1 -0
  226. package/dist/components/siteNavContext.js +24 -0
  227. package/dist/components/siteNavContext.js.map +1 -0
  228. package/dist/components/slides/Slide.js +84 -0
  229. package/dist/components/slides/Slide.js.map +1 -0
  230. package/dist/components/slides/SlideDeck.js +228 -0
  231. package/dist/components/slides/SlideDeck.js.map +1 -0
  232. package/dist/components/themeInitScript.js +16 -0
  233. package/dist/components/themeInitScript.js.map +1 -0
  234. package/dist/components/themeScope.js +9 -0
  235. package/dist/components/themeScope.js.map +1 -0
  236. package/dist/components/toastLifetime.js +21 -0
  237. package/dist/components/toastLifetime.js.map +1 -0
  238. package/dist/fonts/DSSymbols-OFL.txt +98 -0
  239. package/dist/fonts/DSSymbols-Regular.woff2 +0 -0
  240. package/dist/fonts/SymbolsNerdFontMono-Regular.woff2 +0 -0
  241. package/dist/hooks/useActiveHeading.js +59 -0
  242. package/dist/hooks/useActiveHeading.js.map +1 -0
  243. package/dist/hooks/useCopyToClipboard.js +58 -0
  244. package/dist/hooks/useCopyToClipboard.js.map +1 -0
  245. package/dist/index.d.ts +6394 -0
  246. package/dist/index.js +117 -0
  247. package/dist/index.js.map +1 -0
  248. package/dist/lib/accentClasses.js +87 -0
  249. package/dist/lib/accentClasses.js.map +1 -0
  250. package/dist/lib/recipe.js +33 -0
  251. package/dist/lib/recipe.js.map +1 -0
  252. package/dist/lib/slug.js +39 -0
  253. package/dist/lib/slug.js.map +1 -0
  254. package/dist/lib/theme.js +76 -0
  255. package/dist/lib/theme.js.map +1 -0
  256. package/dist/lib/tokenRules.js +51 -0
  257. package/dist/lib/tokenRules.js.map +1 -0
  258. package/dist/prose.css +766 -0
  259. package/dist/styles.css +203 -0
  260. package/dist/theme/ansi.js +77 -0
  261. package/dist/theme/ansi.js.map +1 -0
  262. package/dist/theme/contrast.js +342 -0
  263. package/dist/theme/contrast.js.map +1 -0
  264. package/dist/theme/levels.js +234 -0
  265. package/dist/theme/levels.js.map +1 -0
  266. package/dist/theme/media.js +115 -0
  267. package/dist/theme/media.js.map +1 -0
  268. package/dist/theme/recommended.js +105 -0
  269. package/dist/theme/recommended.js.map +1 -0
  270. package/dist/theme.css +538 -0
  271. package/dist/tokens.js +1 -0
  272. package/dist/tokens.js.map +1 -0
  273. package/package.json +209 -6
  274. package/skills/design-system-usage/SKILL.md +75 -0
  275. package/terminal/rtk-midnight.alacritty.toml +33 -0
  276. package/terminal/rtk-midnight.ghostty.conf +24 -0
  277. package/terminal/rtk-midnight.itermcolors +167 -0
  278. package/terminal/rtk-midnight.windows-terminal.json +23 -0
  279. package/terminal/rtk-sketch.alacritty.toml +33 -0
  280. package/terminal/rtk-sketch.ghostty.conf +24 -0
  281. package/terminal/rtk-sketch.itermcolors +167 -0
  282. package/terminal/rtk-sketch.windows-terminal.json +23 -0
  283. package/tokens/palette.midnight.tokens.json +477 -0
  284. package/tokens/palette.sketch.tokens.json +477 -0
@@ -0,0 +1,288 @@
1
+ "use client";
2
+
3
+ // src/components/BarChart.tsx
4
+ import { AxisBottom, AxisLeft } from "@visx/axis";
5
+ import { GridColumns, GridRows } from "@visx/grid";
6
+ import { Group } from "@visx/group";
7
+ import { ParentSize } from "@visx/responsive";
8
+ import { scaleBand, scaleLinear } from "@visx/scale";
9
+ import { Bar } from "@visx/shape";
10
+ import { forwardRef } from "react";
11
+ import { cn } from "../lib/recipe.js";
12
+ import { accentVar } from "../lib/theme.js";
13
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
14
+ var DEFAULT_VERTICAL_MARGIN = { top: 28, right: 24, bottom: 44, left: 48 };
15
+ var DEFAULT_HORIZONTAL_MARGIN = { top: 20, right: 48, bottom: 36, left: 80 };
16
+ function BarChartCanvas({
17
+ data,
18
+ width = 500,
19
+ height = 260,
20
+ orientation = "vertical",
21
+ accent = "primary",
22
+ showGrid = true,
23
+ showValues = true,
24
+ emptyMessage = "NO DATA",
25
+ ariaLabel,
26
+ margin: customMargin,
27
+ onBarClick,
28
+ className,
29
+ svgRef,
30
+ ...props
31
+ }) {
32
+ const isHorizontal = orientation === "horizontal";
33
+ const margin = customMargin ?? (isHorizontal ? DEFAULT_HORIZONTAL_MARGIN : DEFAULT_VERTICAL_MARGIN);
34
+ const xMax = Math.max(0, width - margin.left - margin.right);
35
+ const yMax = Math.max(0, height - margin.top - margin.bottom);
36
+ if (!data || data.length === 0) {
37
+ return /* @__PURE__ */ jsx(
38
+ "svg",
39
+ {
40
+ ref: svgRef,
41
+ "data-slot": "barchart",
42
+ width,
43
+ height,
44
+ viewBox: `0 0 ${width} ${height}`,
45
+ role: "img",
46
+ "aria-label": ariaLabel ?? "Empty chart",
47
+ className: cn(
48
+ "w-full max-w-full overflow-visible border-2 border-edge-strong bg-surface-base",
49
+ className
50
+ ),
51
+ ...props,
52
+ children: /* @__PURE__ */ jsxs(
53
+ "text",
54
+ {
55
+ x: width / 2,
56
+ y: height / 2,
57
+ textAnchor: "middle",
58
+ dominantBaseline: "central",
59
+ fill: "var(--ds-text-muted)",
60
+ fontFamily: "var(--ds-font-mono)",
61
+ fontSize: 12,
62
+ fontWeight: "bold",
63
+ children: [
64
+ "[ ",
65
+ emptyMessage,
66
+ " ]"
67
+ ]
68
+ }
69
+ )
70
+ }
71
+ );
72
+ }
73
+ const values = data.map((d) => d.value);
74
+ const maxValue = Math.max(1, ...values);
75
+ const categoryScale = scaleBand({
76
+ domain: data.map((d) => d.label),
77
+ range: isHorizontal ? [0, yMax] : [0, xMax],
78
+ padding: 0.3
79
+ });
80
+ const valueScale = scaleLinear({
81
+ domain: [0, maxValue],
82
+ range: isHorizontal ? [0, xMax] : [yMax, 0],
83
+ nice: true
84
+ });
85
+ return /* @__PURE__ */ jsx(
86
+ "svg",
87
+ {
88
+ ref: svgRef,
89
+ "data-slot": "barchart",
90
+ width,
91
+ height,
92
+ viewBox: `0 0 ${width} ${height}`,
93
+ role: "img",
94
+ "aria-label": ariaLabel ?? `Bar chart with ${data.length} categories`,
95
+ className: cn(
96
+ "w-full max-w-full overflow-visible border-2 border-edge-strong bg-surface-base",
97
+ className
98
+ ),
99
+ ...props,
100
+ children: /* @__PURE__ */ jsxs(Group, { left: margin.left, top: margin.top, children: [
101
+ showGrid && (isHorizontal ? /* @__PURE__ */ jsx(
102
+ GridColumns,
103
+ {
104
+ scale: valueScale,
105
+ height: yMax,
106
+ stroke: "var(--ds-border-subtle)",
107
+ strokeWidth: 1
108
+ }
109
+ ) : /* @__PURE__ */ jsx(
110
+ GridRows,
111
+ {
112
+ scale: valueScale,
113
+ width: xMax,
114
+ stroke: "var(--ds-border-subtle)",
115
+ strokeWidth: 1
116
+ }
117
+ )),
118
+ data.map((d, index) => {
119
+ const barAccent = d.accent ?? accent;
120
+ const fillColor = accentVar(barAccent);
121
+ if (isHorizontal) {
122
+ const barY2 = categoryScale(d.label) ?? 0;
123
+ const barHeight2 = categoryScale.bandwidth();
124
+ const barWidth2 = Math.max(0, valueScale(d.value));
125
+ return /* @__PURE__ */ jsxs(Group, { children: [
126
+ /* @__PURE__ */ jsx(
127
+ Bar,
128
+ {
129
+ x: 0,
130
+ y: barY2,
131
+ width: barWidth2,
132
+ height: barHeight2,
133
+ fill: fillColor,
134
+ stroke: "var(--ds-border-strong)",
135
+ strokeWidth: 2,
136
+ className: "cursor-pointer hover:opacity-80",
137
+ onClick: () => onBarClick?.(d, index)
138
+ }
139
+ ),
140
+ showValues && /* @__PURE__ */ jsx(
141
+ "text",
142
+ {
143
+ x: barWidth2 + 6,
144
+ y: barY2 + barHeight2 / 2,
145
+ dominantBaseline: "central",
146
+ fill: "var(--ds-text-primary)",
147
+ fontFamily: "var(--ds-font-mono)",
148
+ fontSize: 11,
149
+ fontWeight: "bold",
150
+ children: d.value
151
+ }
152
+ )
153
+ ] }, `bar-h-${d.label}-${index}`);
154
+ }
155
+ const barX = categoryScale(d.label) ?? 0;
156
+ const barWidth = categoryScale.bandwidth();
157
+ const barHeight = Math.max(0, yMax - (valueScale(d.value) ?? 0));
158
+ const barY = yMax - barHeight;
159
+ return /* @__PURE__ */ jsxs(Group, { children: [
160
+ /* @__PURE__ */ jsx(
161
+ Bar,
162
+ {
163
+ x: barX,
164
+ y: barY,
165
+ width: barWidth,
166
+ height: barHeight,
167
+ fill: fillColor,
168
+ stroke: "var(--ds-border-strong)",
169
+ strokeWidth: 2,
170
+ className: "cursor-pointer hover:opacity-80",
171
+ onClick: () => onBarClick?.(d, index)
172
+ }
173
+ ),
174
+ showValues && /* @__PURE__ */ jsx(
175
+ "text",
176
+ {
177
+ x: barX + barWidth / 2,
178
+ y: barY - 6,
179
+ textAnchor: "middle",
180
+ fill: "var(--ds-text-primary)",
181
+ fontFamily: "var(--ds-font-mono)",
182
+ fontSize: 11,
183
+ fontWeight: "bold",
184
+ children: d.value
185
+ }
186
+ )
187
+ ] }, `bar-v-${d.label}-${index}`);
188
+ }),
189
+ isHorizontal ? /* @__PURE__ */ jsxs(Fragment, { children: [
190
+ /* @__PURE__ */ jsx(
191
+ AxisLeft,
192
+ {
193
+ scale: categoryScale,
194
+ stroke: "var(--ds-border-strong)",
195
+ strokeWidth: 2,
196
+ tickStroke: "var(--ds-border-strong)",
197
+ tickLength: 4,
198
+ tickLabelProps: {
199
+ fill: "var(--ds-text-secondary)",
200
+ fontFamily: "var(--ds-font-mono)",
201
+ fontSize: 11,
202
+ textAnchor: "end",
203
+ dominantBaseline: "central",
204
+ dx: "-0.25em"
205
+ }
206
+ }
207
+ ),
208
+ /* @__PURE__ */ jsx(
209
+ AxisBottom,
210
+ {
211
+ top: yMax,
212
+ scale: valueScale,
213
+ stroke: "var(--ds-border-strong)",
214
+ strokeWidth: 2,
215
+ tickStroke: "var(--ds-border-strong)",
216
+ tickLength: 4,
217
+ tickLabelProps: {
218
+ fill: "var(--ds-text-secondary)",
219
+ fontFamily: "var(--ds-font-mono)",
220
+ fontSize: 10,
221
+ textAnchor: "middle",
222
+ dominantBaseline: "hanging",
223
+ dy: "0.25em"
224
+ }
225
+ }
226
+ )
227
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
228
+ /* @__PURE__ */ jsx(
229
+ AxisBottom,
230
+ {
231
+ top: yMax,
232
+ scale: categoryScale,
233
+ stroke: "var(--ds-border-strong)",
234
+ strokeWidth: 2,
235
+ tickStroke: "var(--ds-border-strong)",
236
+ tickLength: 4,
237
+ tickLabelProps: {
238
+ fill: "var(--ds-text-secondary)",
239
+ fontFamily: "var(--ds-font-mono)",
240
+ fontSize: 11,
241
+ textAnchor: "middle",
242
+ dominantBaseline: "hanging",
243
+ dy: "0.25em"
244
+ }
245
+ }
246
+ ),
247
+ /* @__PURE__ */ jsx(
248
+ AxisLeft,
249
+ {
250
+ scale: valueScale,
251
+ stroke: "var(--ds-border-strong)",
252
+ strokeWidth: 2,
253
+ tickStroke: "var(--ds-border-strong)",
254
+ tickLength: 4,
255
+ tickLabelProps: {
256
+ fill: "var(--ds-text-secondary)",
257
+ fontFamily: "var(--ds-font-mono)",
258
+ fontSize: 10,
259
+ textAnchor: "end",
260
+ dominantBaseline: "central",
261
+ dx: "-0.25em"
262
+ }
263
+ }
264
+ )
265
+ ] })
266
+ ] })
267
+ }
268
+ );
269
+ }
270
+ var BarChart = forwardRef(function BarChart2({ responsive = false, ...props }, ref) {
271
+ if (responsive) {
272
+ return /* @__PURE__ */ jsx(ParentSize, { className: cn("w-full", props.className), children: ({ width: parentWidth }) => /* @__PURE__ */ jsx(
273
+ BarChartCanvas,
274
+ {
275
+ ...props,
276
+ width: parentWidth > 0 ? parentWidth : props.width ?? 500,
277
+ svgRef: ref
278
+ }
279
+ ) });
280
+ }
281
+ return /* @__PURE__ */ jsx(BarChartCanvas, { ...props, svgRef: ref });
282
+ });
283
+ BarChart.displayName = "BarChart";
284
+ export {
285
+ BarChart,
286
+ ParentSize as ResponsiveChartContainer
287
+ };
288
+ //# sourceMappingURL=BarChart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/BarChart.tsx"],"sourcesContent":["'use client';\n\nimport { AxisBottom, AxisLeft } from '@visx/axis';\nimport { GridColumns, GridRows } from '@visx/grid';\nimport { Group } from '@visx/group';\nimport { ParentSize } from '@visx/responsive';\nimport { scaleBand, scaleLinear } from '@visx/scale';\nimport { Bar } from '@visx/shape';\nimport { type ForwardedRef, forwardRef, type SVGProps } from 'react';\nimport { cn } from '../lib/recipe';\nimport { type AccentToken, accentVar } from '../lib/theme';\n\nexport { ParentSize as ResponsiveChartContainer };\n\nexport interface BarChartDatum {\n label: string;\n value: number;\n accent?: AccentToken;\n}\n\nexport interface BarChartProps extends Omit<SVGProps<SVGSVGElement>, 'data'> {\n /** Categorical data points with labels and numeric values. */\n data: BarChartDatum[];\n /** SVG coordinate width (default 500). */\n width?: number;\n /** SVG coordinate height (default 260). */\n height?: number;\n /** Whether chart width dynamically tracks parent container width (default false). */\n responsive?: boolean;\n /** Bar layout orientation (default 'vertical'). */\n orientation?: 'vertical' | 'horizontal';\n /** Default accent token applied to bars without their own accent (default 'primary'). */\n accent?: AccentToken;\n /** Whether to render background coordinate grid lines (default true). */\n showGrid?: boolean;\n /** Whether to render numeric value labels beside or above bars (default true). */\n showValues?: boolean;\n /** Custom fallback message when dataset is empty (default 'NO DATA'). */\n emptyMessage?: string;\n /** Screen reader accessibility label. */\n ariaLabel?: string;\n /** Inset padding between outer canvas and plotting coordinates. */\n margin?: { top: number; right: number; bottom: number; left: number };\n /** Optional click callback for interactive bar inspection. */\n onBarClick?: (datum: BarChartDatum, index: number) => void;\n}\n\nconst DEFAULT_VERTICAL_MARGIN = { top: 28, right: 24, bottom: 44, left: 48 };\nconst DEFAULT_HORIZONTAL_MARGIN = { top: 20, right: 48, bottom: 36, left: 80 };\n\ninterface InternalCanvasProps extends Omit<BarChartProps, 'responsive'> {\n svgRef?: ForwardedRef<SVGSVGElement>;\n}\n\nfunction BarChartCanvas({\n data,\n width = 500,\n height = 260,\n orientation = 'vertical',\n accent = 'primary',\n showGrid = true,\n showValues = true,\n emptyMessage = 'NO DATA',\n ariaLabel,\n margin: customMargin,\n onBarClick,\n className,\n svgRef,\n ...props\n}: InternalCanvasProps) {\n const isHorizontal = orientation === 'horizontal';\n const margin =\n customMargin ?? (isHorizontal ? DEFAULT_HORIZONTAL_MARGIN : DEFAULT_VERTICAL_MARGIN);\n\n const xMax = Math.max(0, width - margin.left - margin.right);\n const yMax = Math.max(0, height - margin.top - margin.bottom);\n\n if (!data || data.length === 0) {\n return (\n <svg\n ref={svgRef}\n data-slot=\"barchart\"\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n role=\"img\"\n aria-label={ariaLabel ?? 'Empty chart'}\n className={cn(\n 'w-full max-w-full overflow-visible border-2 border-edge-strong bg-surface-base',\n className,\n )}\n {...props}\n >\n <text\n x={width / 2}\n y={height / 2}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fill=\"var(--ds-text-muted)\"\n fontFamily=\"var(--ds-font-mono)\"\n fontSize={12}\n fontWeight=\"bold\"\n >\n [ {emptyMessage} ]\n </text>\n </svg>\n );\n }\n\n const values = data.map((d) => d.value);\n const maxValue = Math.max(1, ...values);\n\n const categoryScale = scaleBand<string>({\n domain: data.map((d) => d.label),\n range: isHorizontal ? [0, yMax] : [0, xMax],\n padding: 0.3,\n });\n\n const valueScale = scaleLinear<number>({\n domain: [0, maxValue],\n range: isHorizontal ? [0, xMax] : [yMax, 0],\n nice: true,\n });\n\n return (\n <svg\n ref={svgRef}\n data-slot=\"barchart\"\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n role=\"img\"\n aria-label={ariaLabel ?? `Bar chart with ${data.length} categories`}\n className={cn(\n 'w-full max-w-full overflow-visible border-2 border-edge-strong bg-surface-base',\n className,\n )}\n {...props}\n >\n <Group left={margin.left} top={margin.top}>\n {showGrid &&\n (isHorizontal ? (\n <GridColumns\n scale={valueScale}\n height={yMax}\n stroke=\"var(--ds-border-subtle)\"\n strokeWidth={1}\n />\n ) : (\n <GridRows\n scale={valueScale}\n width={xMax}\n stroke=\"var(--ds-border-subtle)\"\n strokeWidth={1}\n />\n ))}\n\n {data.map((d, index) => {\n const barAccent = d.accent ?? accent;\n const fillColor = accentVar(barAccent);\n\n if (isHorizontal) {\n const barY = categoryScale(d.label) ?? 0;\n const barHeight = categoryScale.bandwidth();\n const barWidth = Math.max(0, valueScale(d.value));\n\n return (\n <Group key={`bar-h-${d.label}-${index}`}>\n <Bar\n x={0}\n y={barY}\n width={barWidth}\n height={barHeight}\n fill={fillColor}\n stroke=\"var(--ds-border-strong)\"\n strokeWidth={2}\n className=\"cursor-pointer hover:opacity-80\"\n onClick={() => onBarClick?.(d, index)}\n />\n {showValues && (\n <text\n x={barWidth + 6}\n y={barY + barHeight / 2}\n dominantBaseline=\"central\"\n fill=\"var(--ds-text-primary)\"\n fontFamily=\"var(--ds-font-mono)\"\n fontSize={11}\n fontWeight=\"bold\"\n >\n {d.value}\n </text>\n )}\n </Group>\n );\n }\n\n const barX = categoryScale(d.label) ?? 0;\n const barWidth = categoryScale.bandwidth();\n const barHeight = Math.max(0, yMax - (valueScale(d.value) ?? 0));\n const barY = yMax - barHeight;\n\n return (\n <Group key={`bar-v-${d.label}-${index}`}>\n <Bar\n x={barX}\n y={barY}\n width={barWidth}\n height={barHeight}\n fill={fillColor}\n stroke=\"var(--ds-border-strong)\"\n strokeWidth={2}\n className=\"cursor-pointer hover:opacity-80\"\n onClick={() => onBarClick?.(d, index)}\n />\n {showValues && (\n <text\n x={barX + barWidth / 2}\n y={barY - 6}\n textAnchor=\"middle\"\n fill=\"var(--ds-text-primary)\"\n fontFamily=\"var(--ds-font-mono)\"\n fontSize={11}\n fontWeight=\"bold\"\n >\n {d.value}\n </text>\n )}\n </Group>\n );\n })}\n\n {isHorizontal ? (\n <>\n <AxisLeft\n scale={categoryScale}\n stroke=\"var(--ds-border-strong)\"\n strokeWidth={2}\n tickStroke=\"var(--ds-border-strong)\"\n tickLength={4}\n tickLabelProps={{\n fill: 'var(--ds-text-secondary)',\n fontFamily: 'var(--ds-font-mono)',\n fontSize: 11,\n textAnchor: 'end',\n dominantBaseline: 'central',\n dx: '-0.25em',\n }}\n />\n <AxisBottom\n top={yMax}\n scale={valueScale}\n stroke=\"var(--ds-border-strong)\"\n strokeWidth={2}\n tickStroke=\"var(--ds-border-strong)\"\n tickLength={4}\n tickLabelProps={{\n fill: 'var(--ds-text-secondary)',\n fontFamily: 'var(--ds-font-mono)',\n fontSize: 10,\n textAnchor: 'middle',\n dominantBaseline: 'hanging',\n dy: '0.25em',\n }}\n />\n </>\n ) : (\n <>\n <AxisBottom\n top={yMax}\n scale={categoryScale}\n stroke=\"var(--ds-border-strong)\"\n strokeWidth={2}\n tickStroke=\"var(--ds-border-strong)\"\n tickLength={4}\n tickLabelProps={{\n fill: 'var(--ds-text-secondary)',\n fontFamily: 'var(--ds-font-mono)',\n fontSize: 11,\n textAnchor: 'middle',\n dominantBaseline: 'hanging',\n dy: '0.25em',\n }}\n />\n <AxisLeft\n scale={valueScale}\n stroke=\"var(--ds-border-strong)\"\n strokeWidth={2}\n tickStroke=\"var(--ds-border-strong)\"\n tickLength={4}\n tickLabelProps={{\n fill: 'var(--ds-text-secondary)',\n fontFamily: 'var(--ds-font-mono)',\n fontSize: 10,\n textAnchor: 'end',\n dominantBaseline: 'central',\n dx: '-0.25em',\n }}\n />\n </>\n )}\n </Group>\n </svg>\n );\n}\n\n/**\n * Brutalist categorical bar chart built with Visx primitives.\n *\n * Emits high-contrast, deterministic SVG geometry with 2px borders, theme-driven fills,\n * and monospace axis coordinates. Supports vertical and horizontal layouts and\n * responds dynamically to theme ladder swaps without canvas re-renders.\n */\nexport const BarChart = forwardRef<SVGSVGElement, BarChartProps>(function BarChart(\n { responsive = false, ...props },\n ref,\n) {\n if (responsive) {\n return (\n <ParentSize className={cn('w-full', props.className)}>\n {({ width: parentWidth }) => (\n <BarChartCanvas\n {...props}\n width={parentWidth > 0 ? parentWidth : (props.width ?? 500)}\n svgRef={ref}\n />\n )}\n </ParentSize>\n );\n }\n\n return <BarChartCanvas {...props} svgRef={ref} />;\n});\n\nBarChart.displayName = 'BarChart';\n"],"mappings":";;;AAEA,SAAS,YAAY,gBAAgB;AACrC,SAAS,aAAa,gBAAgB;AACtC,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAC3B,SAAS,WAAW,mBAAmB;AACvC,SAAS,WAAW;AACpB,SAA4B,kBAAiC;AAC7D,SAAS,UAAU;AACnB,SAA2B,iBAAiB;AAqEtC,SAyJI,UAzJJ,KAcE,YAdF;AAhCN,IAAM,0BAA0B,EAAE,KAAK,IAAI,OAAO,IAAI,QAAQ,IAAI,MAAM,GAAG;AAC3E,IAAM,4BAA4B,EAAE,KAAK,IAAI,OAAO,IAAI,QAAQ,IAAI,MAAM,GAAG;AAM7E,SAAS,eAAe;AAAA,EACtB;AAAA,EACA,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,cAAc;AAAA,EACd,SAAS;AAAA,EACT,WAAW;AAAA,EACX,aAAa;AAAA,EACb,eAAe;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAwB;AACtB,QAAM,eAAe,gBAAgB;AACrC,QAAM,SACJ,iBAAiB,eAAe,4BAA4B;AAE9D,QAAM,OAAO,KAAK,IAAI,GAAG,QAAQ,OAAO,OAAO,OAAO,KAAK;AAC3D,QAAM,OAAO,KAAK,IAAI,GAAG,SAAS,OAAO,MAAM,OAAO,MAAM;AAE5D,MAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,aAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA,SAAS,OAAO,KAAK,IAAI,MAAM;AAAA,QAC/B,MAAK;AAAA,QACL,cAAY,aAAa;AAAA,QACzB,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA,QAEJ;AAAA,UAAC;AAAA;AAAA,YACC,GAAG,QAAQ;AAAA,YACX,GAAG,SAAS;AAAA,YACZ,YAAW;AAAA,YACX,kBAAiB;AAAA,YACjB,MAAK;AAAA,YACL,YAAW;AAAA,YACX,UAAU;AAAA,YACV,YAAW;AAAA,YACZ;AAAA;AAAA,cACI;AAAA,cAAa;AAAA;AAAA;AAAA,QAClB;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,QAAM,SAAS,KAAK,IAAI,CAAC,MAAM,EAAE,KAAK;AACtC,QAAM,WAAW,KAAK,IAAI,GAAG,GAAG,MAAM;AAEtC,QAAM,gBAAgB,UAAkB;AAAA,IACtC,QAAQ,KAAK,IAAI,CAAC,MAAM,EAAE,KAAK;AAAA,IAC/B,OAAO,eAAe,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,IAAI;AAAA,IAC1C,SAAS;AAAA,EACX,CAAC;AAED,QAAM,aAAa,YAAoB;AAAA,IACrC,QAAQ,CAAC,GAAG,QAAQ;AAAA,IACpB,OAAO,eAAe,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC;AAAA,IAC1C,MAAM;AAAA,EACR,CAAC;AAED,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,aAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,SAAS,OAAO,KAAK,IAAI,MAAM;AAAA,MAC/B,MAAK;AAAA,MACL,cAAY,aAAa,kBAAkB,KAAK,MAAM;AAAA,MACtD,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ,+BAAC,SAAM,MAAM,OAAO,MAAM,KAAK,OAAO,KACnC;AAAA,qBACE,eACC;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,QAAO;AAAA,YACP,aAAa;AAAA;AAAA,QACf,IAEA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,OAAO;AAAA,YACP,QAAO;AAAA,YACP,aAAa;AAAA;AAAA,QACf;AAAA,QAGH,KAAK,IAAI,CAAC,GAAG,UAAU;AACtB,gBAAM,YAAY,EAAE,UAAU;AAC9B,gBAAM,YAAY,UAAU,SAAS;AAErC,cAAI,cAAc;AAChB,kBAAMA,QAAO,cAAc,EAAE,KAAK,KAAK;AACvC,kBAAMC,aAAY,cAAc,UAAU;AAC1C,kBAAMC,YAAW,KAAK,IAAI,GAAG,WAAW,EAAE,KAAK,CAAC;AAEhD,mBACE,qBAAC,SACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,GAAG;AAAA,kBACH,GAAGF;AAAA,kBACH,OAAOE;AAAA,kBACP,QAAQD;AAAA,kBACR,MAAM;AAAA,kBACN,QAAO;AAAA,kBACP,aAAa;AAAA,kBACb,WAAU;AAAA,kBACV,SAAS,MAAM,aAAa,GAAG,KAAK;AAAA;AAAA,cACtC;AAAA,cACC,cACC;AAAA,gBAAC;AAAA;AAAA,kBACC,GAAGC,YAAW;AAAA,kBACd,GAAGF,QAAOC,aAAY;AAAA,kBACtB,kBAAiB;AAAA,kBACjB,MAAK;AAAA,kBACL,YAAW;AAAA,kBACX,UAAU;AAAA,kBACV,YAAW;AAAA,kBAEV,YAAE;AAAA;AAAA,cACL;AAAA,iBAvBQ,SAAS,EAAE,KAAK,IAAI,KAAK,EAyBrC;AAAA,UAEJ;AAEA,gBAAM,OAAO,cAAc,EAAE,KAAK,KAAK;AACvC,gBAAM,WAAW,cAAc,UAAU;AACzC,gBAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,WAAW,EAAE,KAAK,KAAK,EAAE;AAC/D,gBAAM,OAAO,OAAO;AAEpB,iBACE,qBAAC,SACC;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,GAAG;AAAA,gBACH,GAAG;AAAA,gBACH,OAAO;AAAA,gBACP,QAAQ;AAAA,gBACR,MAAM;AAAA,gBACN,QAAO;AAAA,gBACP,aAAa;AAAA,gBACb,WAAU;AAAA,gBACV,SAAS,MAAM,aAAa,GAAG,KAAK;AAAA;AAAA,YACtC;AAAA,YACC,cACC;AAAA,cAAC;AAAA;AAAA,gBACC,GAAG,OAAO,WAAW;AAAA,gBACrB,GAAG,OAAO;AAAA,gBACV,YAAW;AAAA,gBACX,MAAK;AAAA,gBACL,YAAW;AAAA,gBACX,UAAU;AAAA,gBACV,YAAW;AAAA,gBAEV,YAAE;AAAA;AAAA,YACL;AAAA,eAvBQ,SAAS,EAAE,KAAK,IAAI,KAAK,EAyBrC;AAAA,QAEJ,CAAC;AAAA,QAEA,eACC,iCACE;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,QAAO;AAAA,cACP,aAAa;AAAA,cACb,YAAW;AAAA,cACX,YAAY;AAAA,cACZ,gBAAgB;AAAA,gBACd,MAAM;AAAA,gBACN,YAAY;AAAA,gBACZ,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,kBAAkB;AAAA,gBAClB,IAAI;AAAA,cACN;AAAA;AAAA,UACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,OAAO;AAAA,cACP,QAAO;AAAA,cACP,aAAa;AAAA,cACb,YAAW;AAAA,cACX,YAAY;AAAA,cACZ,gBAAgB;AAAA,gBACd,MAAM;AAAA,gBACN,YAAY;AAAA,gBACZ,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,kBAAkB;AAAA,gBAClB,IAAI;AAAA,cACN;AAAA;AAAA,UACF;AAAA,WACF,IAEA,iCACE;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,OAAO;AAAA,cACP,QAAO;AAAA,cACP,aAAa;AAAA,cACb,YAAW;AAAA,cACX,YAAY;AAAA,cACZ,gBAAgB;AAAA,gBACd,MAAM;AAAA,gBACN,YAAY;AAAA,gBACZ,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,kBAAkB;AAAA,gBAClB,IAAI;AAAA,cACN;AAAA;AAAA,UACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,QAAO;AAAA,cACP,aAAa;AAAA,cACb,YAAW;AAAA,cACX,YAAY;AAAA,cACZ,gBAAgB;AAAA,gBACd,MAAM;AAAA,gBACN,YAAY;AAAA,gBACZ,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,kBAAkB;AAAA,gBAClB,IAAI;AAAA,cACN;AAAA;AAAA,UACF;AAAA,WACF;AAAA,SAEJ;AAAA;AAAA,EACF;AAEJ;AASO,IAAM,WAAW,WAAyC,SAASE,UACxE,EAAE,aAAa,OAAO,GAAG,MAAM,GAC/B,KACA;AACA,MAAI,YAAY;AACd,WACE,oBAAC,cAAW,WAAW,GAAG,UAAU,MAAM,SAAS,GAChD,WAAC,EAAE,OAAO,YAAY,MACrB;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,OAAO,cAAc,IAAI,cAAe,MAAM,SAAS;AAAA,QACvD,QAAQ;AAAA;AAAA,IACV,GAEJ;AAAA,EAEJ;AAEA,SAAO,oBAAC,kBAAgB,GAAG,OAAO,QAAQ,KAAK;AACjD,CAAC;AAED,SAAS,cAAc;","names":["barY","barHeight","barWidth","BarChart"]}
@@ -0,0 +1,23 @@
1
+ // src/components/BracketText.tsx
2
+ import { cn } from "../lib/recipe.js";
3
+ import { accentTextClass } from "../lib/accentClasses.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ function BracketText({
6
+ children,
7
+ accent,
8
+ className = "",
9
+ ...props
10
+ }) {
11
+ const accentClass = accent ? accentTextClass(accent) : "";
12
+ return /* @__PURE__ */ jsxs("span", { "data-slot": "bracket-text", className: cn(accentClass, className), ...props, children: [
13
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "[" }),
14
+ " ",
15
+ children,
16
+ " ",
17
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "]" })
18
+ ] });
19
+ }
20
+ export {
21
+ BracketText
22
+ };
23
+ //# sourceMappingURL=BracketText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/BracketText.tsx"],"sourcesContent":["import type { ReactNode, HTMLAttributes } from 'react';\nimport type { AccentToken } from '../lib/theme';\nimport { cn } from '../lib/recipe';\nimport { accentTextClass } from '../lib/accentClasses';\n\nexport interface BracketTextProps extends HTMLAttributes<HTMLSpanElement> {\n children: ReactNode;\n /**\n * Semantic emphasis. Roles only — `'white'` was still accepted here after\n * #138 removed the hue vocabulary from the component API, because the union\n * named it explicitly and the class map was typed `Record<string, string>`.\n * An appearance name in a component API is the defect ADR 0001 exists to\n * prevent; omit `accent` for the default ink.\n */\n accent?: AccentToken;\n className?: string;\n}\n\nexport function BracketText({\n children,\n accent,\n className = '',\n ...props\n}: BracketTextProps) {\n // No per-render object: the map lives in `lib/accentClasses`, written once.\n const accentClass = accent ? accentTextClass(accent) : '';\n\n return (\n <span data-slot=\"bracket-text\" className={cn(accentClass, className)} {...props}>\n <span aria-hidden=\"true\">\n [\n </span>{' '}\n {children}{' '}\n <span aria-hidden=\"true\">\n ]\n </span>\n </span>\n );\n}\n"],"mappings":";AAEA,SAAS,UAAU;AACnB,SAAS,uBAAuB;AAyB5B,SACE,KADF;AAVG,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,GAAG;AACL,GAAqB;AAEnB,QAAM,cAAc,SAAS,gBAAgB,MAAM,IAAI;AAEvD,SACE,qBAAC,UAAK,aAAU,gBAAe,WAAW,GAAG,aAAa,SAAS,GAAI,GAAG,OACxE;AAAA,wBAAC,UAAK,eAAY,QAAO,eAEzB;AAAA,IAAQ;AAAA,IACP;AAAA,IAAU;AAAA,IACX,oBAAC,UAAK,eAAY,QAAO,eAEzB;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,50 @@
1
+ // src/components/BulletChart.tsx
2
+ import { Bullet as MicroBullet } from "@microcharts/react/bullet";
3
+ import { forwardRef } from "react";
4
+ import { cn } from "../lib/recipe.js";
5
+ import { accentVar } from "../lib/theme.js";
6
+ import { jsx } from "react/jsx-runtime";
7
+ var BulletChart = forwardRef(function BulletChart2({
8
+ value,
9
+ target,
10
+ bands,
11
+ accent = "primary",
12
+ width = 140,
13
+ height = 20,
14
+ title,
15
+ summary,
16
+ label = "none",
17
+ className,
18
+ ...props
19
+ }, ref) {
20
+ const barColor = accentVar(accent);
21
+ return /* @__PURE__ */ jsx(
22
+ "div",
23
+ {
24
+ ref,
25
+ "data-slot": "bullet-chart",
26
+ className: cn("inline-flex items-center font-mono", className),
27
+ ...props,
28
+ children: /* @__PURE__ */ jsx(
29
+ MicroBullet,
30
+ {
31
+ value,
32
+ target,
33
+ bands,
34
+ color: barColor,
35
+ width,
36
+ height,
37
+ title,
38
+ summary,
39
+ label,
40
+ className: "overflow-visible"
41
+ }
42
+ )
43
+ }
44
+ );
45
+ });
46
+ BulletChart.displayName = "BulletChart";
47
+ export {
48
+ BulletChart
49
+ };
50
+ //# sourceMappingURL=BulletChart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/BulletChart.tsx"],"sourcesContent":["import { Bullet as MicroBullet } from '@microcharts/react/bullet';\nimport { forwardRef, type HTMLAttributes } from 'react';\nimport { cn } from '../lib/recipe';\nimport { type AccentToken, accentVar } from '../lib/theme';\n\nexport interface BulletChartProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'color'> {\n /** Current actual metric value. */\n value: number;\n /** Target or benchmark value to compare against. */\n target: number;\n /** Qualitative comparative range thresholds (e.g. [40, 70, 100]). */\n bands?: number[];\n /** Accent role token controlling the main value bar color (default 'primary'). */\n accent?: AccentToken;\n /** Display width in pixels (default 140). */\n width?: number;\n /** Display height in pixels (default 20). */\n height?: number;\n /** Accessible chart title. */\n title?: string;\n /** Accessible prose summary. */\n summary?: string;\n /** How to format numeric labels ('none' | 'value' | 'both'). */\n label?: 'none' | 'value' | 'both';\n}\n\n/**\n * High-density KPI benchmark comparison indicator.\n *\n * Wraps @microcharts/react Bullet to render a qualitative target-versus-actual\n * comparison. Sits cleanly inside StatCards, table cells, or dashboard summaries\n * with zero client JS runtime requirements and theme ladder token awareness.\n */\nexport const BulletChart = forwardRef<HTMLDivElement, BulletChartProps>(function BulletChart(\n {\n value,\n target,\n bands,\n accent = 'primary',\n width = 140,\n height = 20,\n title,\n summary,\n label = 'none',\n className,\n ...props\n },\n ref,\n) {\n const barColor = accentVar(accent);\n\n return (\n <div\n ref={ref}\n data-slot=\"bullet-chart\"\n className={cn('inline-flex items-center font-mono', className)}\n {...props}\n >\n <MicroBullet\n value={value}\n target={target}\n bands={bands}\n color={barColor}\n width={width}\n height={height}\n title={title}\n summary={summary}\n label={label}\n className=\"overflow-visible\"\n />\n </div>\n );\n});\n\nBulletChart.displayName = 'BulletChart';\n"],"mappings":";AAAA,SAAS,UAAU,mBAAmB;AACtC,SAAS,kBAAuC;AAChD,SAAS,UAAU;AACnB,SAA2B,iBAAiB;AAuDtC;AAzBC,IAAM,cAAc,WAA6C,SAASA,aAC/E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,GAAG;AACL,GACA,KACA;AACA,QAAM,WAAW,UAAU,MAAM;AAEjC,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAU;AAAA,MACV,WAAW,GAAG,sCAAsC,SAAS;AAAA,MAC5D,GAAG;AAAA,MAEJ;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO;AAAA,UACP;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAU;AAAA;AAAA,MACZ;AAAA;AAAA,EACF;AAEJ,CAAC;AAED,YAAY,cAAc;","names":["BulletChart"]}
@@ -0,0 +1,130 @@
1
+ "use client";
2
+
3
+ // src/components/Button.tsx
4
+ import { forwardRef } from "react";
5
+ import { recipe } from "../lib/recipe.js";
6
+ import { Spinner } from "./Spinner.js";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ var PRESS = "shadow-hard-md not-disabled:not-aria-disabled:hover:shadow-hard-lg not-disabled:not-aria-disabled:active:translate-x-1 not-disabled:not-aria-disabled:active:translate-y-1 not-disabled:not-aria-disabled:active:shadow-none";
9
+ var DISABLED = "disabled:cursor-not-allowed disabled:border-edge-subtle disabled:bg-surface-sunken disabled:text-content-muted disabled:shadow-none";
10
+ var PRIMARY = `bg-accent-primary text-content-inverse border-edge-strong ${PRESS}`;
11
+ var SECONDARY = `bg-accent-secondary text-content-inverse border-edge-strong ${PRESS}`;
12
+ var TERTIARY = `bg-accent-tertiary text-content-inverse border-edge-strong ${PRESS}`;
13
+ var INVERSE = `bg-content-primary text-content-inverse border-content-primary ${PRESS}`;
14
+ var button = recipe({
15
+ base: `font-mono font-bold uppercase border-2 transition-all duration-200 ${DISABLED}`,
16
+ variants: {
17
+ size: {
18
+ sm: "px-4 py-2 text-sm",
19
+ md: "px-6 py-3 text-base",
20
+ lg: "px-8 py-4 text-lg"
21
+ },
22
+ variant: {
23
+ primary: PRIMARY,
24
+ secondary: SECONDARY,
25
+ tertiary: TERTIARY,
26
+ inverse: INVERSE,
27
+ default: PRIMARY
28
+ },
29
+ /**
30
+ * An anchor is not `inline-flex` by default and carries an underline, so the
31
+ * two forms would otherwise lay out and read differently from identical
32
+ * props. A variant rather than a prepended string so a caller's own
33
+ * `className` still resolves against it.
34
+ */
35
+ asLink: {
36
+ true: "inline-flex items-center justify-center no-underline"
37
+ },
38
+ /**
39
+ * Held in its press: the `:active` offset with the shadow gone, over the
40
+ * variant's own fill. Enabled is raised, disabled is sunken and muted, and
41
+ * pending is the one in between — this button was pressed and has not let
42
+ * go. `relative` is the box the spinner is centred in.
43
+ */
44
+ pending: {
45
+ true: "relative cursor-progress translate-x-1 translate-y-1 shadow-none"
46
+ }
47
+ },
48
+ defaultVariants: {
49
+ variant: "tertiary",
50
+ size: "md"
51
+ }
52
+ });
53
+ var Button = forwardRef(
54
+ function Button2(props, ref) {
55
+ const {
56
+ children,
57
+ variant = "tertiary",
58
+ size = "md",
59
+ bracketed = false,
60
+ className,
61
+ ...rest
62
+ } = props;
63
+ const label = bracketed ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center justify-center gap-2", children: [
64
+ /* @__PURE__ */ jsx("span", { className: "select-none", "aria-hidden": "true", children: "[" }),
65
+ /* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-2", children }),
66
+ /* @__PURE__ */ jsx("span", { className: "select-none", "aria-hidden": "true", children: "]" })
67
+ ] }) : /* @__PURE__ */ jsx("span", { className: "inline-flex items-center justify-center gap-2", children });
68
+ if (typeof props.href === "string") {
69
+ const {
70
+ pending: _pending,
71
+ pendingLabel: _pendingLabel,
72
+ ...anchorProps
73
+ } = rest;
74
+ return /* @__PURE__ */ jsx(
75
+ "a",
76
+ {
77
+ "data-slot": "button",
78
+ ref,
79
+ ...anchorProps,
80
+ rel: anchorProps.rel ?? (anchorProps.target === "_blank" ? "noopener noreferrer" : void 0),
81
+ className: button({ variant, size, asLink: true, class: className }),
82
+ children: label
83
+ }
84
+ );
85
+ }
86
+ const {
87
+ pending,
88
+ pendingLabel = "Working",
89
+ ...buttonProps
90
+ } = rest;
91
+ const isPending = pending === true && !buttonProps.disabled;
92
+ const element = /* @__PURE__ */ jsx(
93
+ "button",
94
+ {
95
+ "data-slot": "button",
96
+ ref,
97
+ ...buttonProps,
98
+ "aria-disabled": isPending ? true : buttonProps["aria-disabled"],
99
+ onClick: isPending ? preventActivation : buttonProps.onClick,
100
+ "data-pending": isPending ? "" : void 0,
101
+ className: button({ variant, size, pending: isPending, class: className }),
102
+ children: isPending ? /* @__PURE__ */ jsxs(Fragment, { children: [
103
+ /* @__PURE__ */ jsx("span", { className: "inline-flex opacity-0", children: label }),
104
+ /* @__PURE__ */ jsx(
105
+ Spinner,
106
+ {
107
+ size: size === "sm" ? "sm" : "md",
108
+ accent: "current",
109
+ role: void 0,
110
+ "aria-hidden": "true",
111
+ className: "absolute inset-0"
112
+ }
113
+ )
114
+ ] }) : label
115
+ }
116
+ );
117
+ if (pending === void 0) return element;
118
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
119
+ element,
120
+ /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", "data-slot": "button-status", children: isPending ? pendingLabel : "" })
121
+ ] });
122
+ }
123
+ );
124
+ function preventActivation(event) {
125
+ event.preventDefault();
126
+ }
127
+ export {
128
+ Button
129
+ };
130
+ //# sourceMappingURL=Button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/Button.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef } from 'react';\nimport type { Ref } from 'react';\nimport type {\n AnchorHTMLAttributes,\n ButtonHTMLAttributes,\n DetailedHTMLProps,\n MouseEvent,\n ReactNode,\n} from 'react';\nimport { recipe } from '../lib/recipe';\nimport { Spinner } from './Spinner';\n\ninterface ButtonOwnProps {\n /**\n * The label. Written in caps at the call site rather than upper-cased in\n * CSS, so a screen reader is given the words rather than the shouting, and\n * so an acronym that is already caps does not read as emphasis.\n */\n children: ReactNode;\n /**\n * `primary`, `secondary` and `tertiary` use the corresponding accent role,\n * which changes with the theme level. `inverse` uses the primary content\n * role as its background for maximum contrast. `default` is identical to\n * `primary`. Omitted, the variant is `tertiary`.\n */\n variant?: ButtonVariant;\n /**\n * Padding and type scale, not a semantic weight. `md` is the default and the\n * one to reach for; `sm` is for dense rows such as a table's row actions,\n * and `lg` for a page's single primary action. Hit area is the constraint\n * that decides — `sm` is still 44px tall at the `web` Medium.\n */\n size?: 'sm' | 'md' | 'lg';\n /**\n * Wraps the label in `[ ]`. The brutalist cue for \"this one does something\"\n * — worth it for a form's submit or a destructive confirm, noise on every\n * button in a toolbar. It is punctuation in the label, not a border.\n */\n bracketed?: boolean;\n /**\n * Merged into the recipe's own classes. Reach for it to place the button —\n * `w-full`, a grid slot — rather than to restyle it; a colour set here is\n * what `pnpm check:tokens` exists to find.\n */\n className?: string;\n}\n\n/**\n * The accents a filled Button may take.\n *\n * Narrower than `AccentToken` on purpose: `quiet` is missing because\n * `check:contrast` gates `text.inverse` against `accent.primary`, `secondary`\n * and `tertiary` only. A quiet fill with inverse text on it is not measured, so\n * it is not offered.\n */\nexport type ButtonVariant =\n | 'primary'\n | 'secondary'\n | 'tertiary'\n | 'inverse'\n | 'default';\n\n/**\n * The `<button>` form. `href?: never` is what makes the union below\n * discriminate: without it TypeScript would accept `href` here and silently\n * drop it onto an element that ignores it.\n */\nexport type ButtonElementProps = ButtonOwnProps &\n DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> & {\n href?: never;\n /**\n * The request this button started is in flight. Reach for it instead of\n * `disabled` while a submit is being sent.\n *\n * `disabled` removes a focused button from the tab order and drops focus\n * to `<body>`, so a keyboard user is thrown back to the top of the page\n * at the moment they need to hear what happened. A pending button keeps\n * focus and its tab stop, reports `aria-disabled=\"true\"` instead of the\n * `disabled` attribute, and ignores activation: a click, Enter, Space,\n * or Enter in one of its form's fields does not run `onClick` and does\n * not submit the form again.\n *\n * It shows a spinner in place of the label, over the label's own box, so\n * the button keeps its width; the label stays in the accessibility tree,\n * so the accessible name does not change while it has focus. The fill is\n * kept and the button sits sunk into its press — the same offset and no\n * shadow as `:active` — which is distinct from the sunken, muted\n * `disabled` treatment.\n *\n * Pass it from the first render — `pending={saving}`, not\n * `pending={saving || undefined}` — so the live region that announces\n * `pendingLabel` is in the page before it has anything to say. `disabled`\n * wins over `pending` when both are set.\n */\n pending?: boolean;\n /**\n * What is being waited for, as a screen reader hears it when `pending`\n * turns on — \"Signing in\", not \"Loading\". It is written into a visually\n * hidden `role=\"status\"` region beside the button, which exists whenever\n * `pending` is passed at all and is empty until it is `true`.\n */\n pendingLabel?: string;\n };\n\n/** The `<a>` form. Passing `href` selects it; there is no `as` prop to remember. */\nexport type ButtonLinkProps = ButtonOwnProps &\n DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement> & {\n /**\n * Where it navigates — and the discriminant. Supplying it renders an `<a>`\n * wearing the button's styling, which is what a keyboard user, a screen\n * reader and a middle click all need from something that navigates. Omit\n * it for anything that acts on the current page.\n */\n href: string;\n /**\n * Not offered on the anchor form. A link's work is the browser's\n * navigation, which the page cannot hold open, and `aria-disabled` on a\n * link is a link that still navigates. A control that starts a request\n * is a `<button>`.\n */\n pending?: never;\n /** Not offered on the anchor form; see `pending`. */\n pendingLabel?: never;\n };\n\nexport type ButtonProps = ButtonElementProps | ButtonLinkProps;\n\n/**\n * The press affordance — offset shadow that collapses as the control moves into\n * it. Shared rather than repeated so the four accents cannot drift apart.\n *\n * Hover and press are gated on `not-disabled:` so a disabled button does not\n * lift under the pointer or sink when clicked: it would be promising an action\n * it will not take. `not-disabled` rather than `enabled`, because `:enabled`\n * matches form elements only and the anchor form would lose its hover.\n *\n * `not-aria-disabled:` is the same gate for a pending button, which is\n * `aria-disabled` rather than `:disabled` so that it keeps focus. Spelled out\n * in full rather than built from a constant: Tailwind reads the source text,\n * and a template literal emits no CSS.\n */\nconst PRESS =\n 'shadow-hard-md not-disabled:not-aria-disabled:hover:shadow-hard-lg ' +\n 'not-disabled:not-aria-disabled:active:translate-x-1 ' +\n 'not-disabled:not-aria-disabled:active:translate-y-1 ' +\n 'not-disabled:not-aria-disabled:active:shadow-none';\n\n/**\n * The disabled treatment, the one `Select`'s trigger and the text fields wear:\n * a sunken ground, the subtle edge, muted ink and no shadow. Before #252 a\n * disabled button rendered exactly like an enabled one — it looked pressable\n * and did nothing. A request in flight is `pending`, not this. `disabled:`\n * matches the `<button>` form only, which is right: an anchor cannot be\n * disabled.\n */\nconst DISABLED =\n 'disabled:cursor-not-allowed disabled:border-edge-subtle disabled:bg-surface-sunken ' +\n 'disabled:text-content-muted disabled:shadow-none';\n\n/**\n * One constant per rendered form, aliased by every name that resolves to it.\n *\n * `default` and `primary` share the same constant and render identically.\n */\nconst PRIMARY = `bg-accent-primary text-content-inverse border-edge-strong ${PRESS}`;\nconst SECONDARY = `bg-accent-secondary text-content-inverse border-edge-strong ${PRESS}`;\nconst TERTIARY = `bg-accent-tertiary text-content-inverse border-edge-strong ${PRESS}`;\nconst INVERSE = `bg-content-primary text-content-inverse border-content-primary ${PRESS}`;\n\nconst button = recipe({\n base: `font-mono font-bold uppercase border-2 transition-all duration-200 ${DISABLED}`,\n variants: {\n size: {\n sm: 'px-4 py-2 text-sm',\n md: 'px-6 py-3 text-base',\n lg: 'px-8 py-4 text-lg',\n },\n variant: {\n primary: PRIMARY,\n secondary: SECONDARY,\n tertiary: TERTIARY,\n inverse: INVERSE,\n default: PRIMARY,\n },\n /**\n * An anchor is not `inline-flex` by default and carries an underline, so the\n * two forms would otherwise lay out and read differently from identical\n * props. A variant rather than a prepended string so a caller's own\n * `className` still resolves against it.\n */\n asLink: {\n true: 'inline-flex items-center justify-center no-underline',\n },\n /**\n * Held in its press: the `:active` offset with the shadow gone, over the\n * variant's own fill. Enabled is raised, disabled is sunken and muted, and\n * pending is the one in between — this button was pressed and has not let\n * go. `relative` is the box the spinner is centred in.\n */\n pending: {\n true: 'relative cursor-progress translate-x-1 translate-y-1 shadow-none',\n },\n },\n defaultVariants: {\n variant: 'tertiary',\n size: 'md',\n },\n});\n\n/**\n * A button, or a link that looks like one.\n *\n * The distinction is not cosmetic and is not the caller's styling preference:\n * a control that *navigates* must be an `<a>`, or it loses middle-click,\n * open-in-new-tab, the status-bar URL preview, and its announcement as a link\n * to a screen reader. Marketing CTAs are overwhelmingly navigation — \"Get\n * started\" goes somewhere — so a button-only component pushes every landing\n * page into either nesting an anchor inside a button (invalid HTML) or\n * re-styling the whole thing by hand.\n *\n * Passing `href` selects the anchor form, and TypeScript then offers anchor\n * attributes (`target`, `rel`, `download`) and withdraws button-only ones\n * (`disabled`, `type`) — the latter being the point, since `disabled` does\n * nothing whatsoever on an anchor.\n *\n * ```tsx\n * <Button onClick={save}>SAVE</Button>\n * <Button href=\"/pricing\" variant=\"tertiary\" bracketed size=\"lg\">SEE PRICING</Button>\n * ```\n */\n/**\n * Forwards its ref to whichever element it renders.\n *\n * `ButtonProps` is a union, so the ref's type is the union of both targets —\n * `HTMLButtonElement | HTMLAnchorElement`. A caller who knows which form they\n * asked for can narrow it; one who does not is told the truth, which is that it\n * depends on `href`.\n *\n * This is what `<Tooltip.Trigger render={<Button />} />` needs: Base UI's\n * composition merges props *and a ref* onto the element it is given, and a\n * component that drops the ref silently breaks positioning, focus return and\n * outside-click detection in every primitive that wraps it.\n */\nexport const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>(\n function Button(props, ref) {\n const {\n children,\n variant = 'tertiary',\n size = 'md',\n bracketed = false,\n className,\n ...rest\n } = props;\n\n const label = bracketed ? (\n <span className=\"inline-flex items-center justify-center gap-2\">\n <span className=\"select-none\" aria-hidden=\"true\">\n [\n </span>\n <span className=\"inline-flex items-center gap-2\">{children}</span>\n <span className=\"select-none\" aria-hidden=\"true\">\n ]\n </span>\n </span>\n ) : (\n <span className=\"inline-flex items-center justify-center gap-2\">{children}</span>\n );\n\n if (typeof props.href === 'string') {\n // `pending` is `never` here; taken out so a stray `undefined` is not spread.\n const {\n pending: _pending,\n pendingLabel: _pendingLabel,\n ...anchorProps\n } = rest as Omit<ButtonLinkProps, keyof ButtonOwnProps>;\n return (\n <a data-slot=\"button\"\n ref={ref as Ref<HTMLAnchorElement>}\n {...anchorProps}\n // A `target=\"_blank\"` document can reach back through `window.opener`\n // unless told otherwise. Modern browsers imply `noopener`, but not\n // every renderer this package ships into is a modern browser, and an\n // explicit `rel` costs nothing. An explicit `rel` from the caller wins.\n rel={\n anchorProps.rel ??\n (anchorProps.target === '_blank' ? 'noopener noreferrer' : undefined)\n }\n className={button({ variant, size, asLink: true, class: className })}\n >\n {label}\n </a>\n );\n }\n\n const {\n pending,\n pendingLabel = 'Working',\n ...buttonProps\n } = rest as Omit<ButtonElementProps, keyof ButtonOwnProps>;\n const isPending = pending === true && !buttonProps.disabled;\n\n const element = (\n <button data-slot=\"button\"\n ref={ref as Ref<HTMLButtonElement>}\n {...buttonProps}\n // The `disabled` attribute is what drops focus, so a pending button says\n // it is unavailable through ARIA and refuses activation here instead.\n // Enter and Space on a focused button, and Enter in a text field of its\n // form (implicit submission), all reach a button as a `click`; cancelling\n // it cancels the submit it would have sent. Base UI's Button does the\n // same under `focusableWhenDisabled`.\n aria-disabled={isPending ? true : buttonProps['aria-disabled']}\n onClick={isPending ? preventActivation : buttonProps.onClick}\n data-pending={isPending ? '' : undefined}\n className={button({ variant, size, pending: isPending, class: className })}\n >\n {isPending ? (\n <>\n {/* Transparent, not removed: it holds the width and the name. */}\n <span className=\"inline-flex opacity-0\">{label}</span>\n <Spinner\n // Sized to the label's line box: `sm` in a small button, `md` —\n // about a capital's height and a half — in the others.\n size={size === 'sm' ? 'sm' : 'md'}\n accent=\"current\"\n // The status region beside the button announces the wait. This is\n // the drawing only; a second live region inside a button, whose\n // children are presentational, would be read unreliably or twice.\n role={undefined}\n aria-hidden=\"true\"\n className=\"absolute inset-0\"\n />\n </>\n ) : (\n label\n )}\n </button>\n );\n\n if (pending === undefined) return element;\n\n return (\n <>\n {element}\n {/*\n * Outside the button because a button's children are presentational: a\n * live region inside one is flattened into its name. Present from the\n * first render and filled only while pending, which is what makes the\n * insertion an announcement rather than a region appearing already full.\n */}\n <span role=\"status\" className=\"sr-only\" data-slot=\"button-status\">\n {isPending ? pendingLabel : ''}\n </span>\n </>\n );\n});\n\n/** A pending button's click: cancelled, so neither `onClick` nor a submit runs. */\nfunction preventActivation(event: MouseEvent<HTMLButtonElement>) {\n event.preventDefault();\n}\n"],"mappings":"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","names":["Button"]}