@craftzbay/ui 0.9.0 → 0.11.0

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 (208) hide show
  1. package/README.md +82 -10
  2. package/dist-lib/__tests__/helpers/color.d.ts +9 -0
  3. package/dist-lib/__tests__/helpers/theme.d.ts +18 -0
  4. package/dist-lib/components/ui/Accordion.js +71 -0
  5. package/dist-lib/components/ui/Accordion.js.map +1 -0
  6. package/dist-lib/components/ui/Alert.d.ts +17 -1
  7. package/dist-lib/components/ui/Alert.js +79 -0
  8. package/dist-lib/components/ui/Alert.js.map +1 -0
  9. package/dist-lib/components/ui/Avatar.d.ts +3 -3
  10. package/dist-lib/components/ui/Avatar.js +94 -0
  11. package/dist-lib/components/ui/Avatar.js.map +1 -0
  12. package/dist-lib/components/ui/Badge.d.ts +4 -2
  13. package/dist-lib/components/ui/Badge.js +76 -0
  14. package/dist-lib/components/ui/Badge.js.map +1 -0
  15. package/dist-lib/components/ui/Breadcrumbs.d.ts +2 -0
  16. package/dist-lib/components/ui/Breadcrumbs.js +52 -0
  17. package/dist-lib/components/ui/Breadcrumbs.js.map +1 -0
  18. package/dist-lib/components/ui/Button.d.ts +6 -4
  19. package/dist-lib/components/ui/Button.js +131 -0
  20. package/dist-lib/components/ui/Button.js.map +1 -0
  21. package/dist-lib/components/ui/Calendar.d.ts +7 -1
  22. package/dist-lib/components/ui/Calendar.js +139 -0
  23. package/dist-lib/components/ui/Calendar.js.map +1 -0
  24. package/dist-lib/components/ui/Card.d.ts +3 -1
  25. package/dist-lib/components/ui/Card.js +97 -0
  26. package/dist-lib/components/ui/Card.js.map +1 -0
  27. package/dist-lib/components/ui/Carousel.d.ts +11 -7
  28. package/dist-lib/components/ui/Carousel.js +174 -0
  29. package/dist-lib/components/ui/Carousel.js.map +1 -0
  30. package/dist-lib/components/ui/Chart.d.ts +68 -7
  31. package/dist-lib/components/ui/Chart.js +487 -0
  32. package/dist-lib/components/ui/Chart.js.map +1 -0
  33. package/dist-lib/components/ui/Checkbox.js +70 -0
  34. package/dist-lib/components/ui/Checkbox.js.map +1 -0
  35. package/dist-lib/components/ui/Combobox.d.ts +9 -0
  36. package/dist-lib/components/ui/Combobox.js +195 -0
  37. package/dist-lib/components/ui/Combobox.js.map +1 -0
  38. package/dist-lib/components/ui/CommandPalette.d.ts +9 -6
  39. package/dist-lib/components/ui/CommandPalette.js +179 -0
  40. package/dist-lib/components/ui/CommandPalette.js.map +1 -0
  41. package/dist-lib/components/ui/ContextMenu.d.ts +7 -2
  42. package/dist-lib/components/ui/ContextMenu.js +165 -0
  43. package/dist-lib/components/ui/ContextMenu.js.map +1 -0
  44. package/dist-lib/components/ui/DataGrid.d.ts +17 -4
  45. package/dist-lib/components/ui/DataGrid.js +99 -0
  46. package/dist-lib/components/ui/DataGrid.js.map +1 -0
  47. package/dist-lib/components/ui/DatePicker.d.ts +16 -13
  48. package/dist-lib/components/ui/DatePicker.js +193 -0
  49. package/dist-lib/components/ui/DatePicker.js.map +1 -0
  50. package/dist-lib/components/ui/DesignSystemProvider.d.ts +102 -41
  51. package/dist-lib/components/ui/DesignSystemProvider.js +135 -0
  52. package/dist-lib/components/ui/DesignSystemProvider.js.map +1 -0
  53. package/dist-lib/components/ui/Dialog.d.ts +19 -4
  54. package/dist-lib/components/ui/Dialog.js +165 -0
  55. package/dist-lib/components/ui/Dialog.js.map +1 -0
  56. package/dist-lib/components/ui/Drawer.d.ts +23 -2
  57. package/dist-lib/components/ui/Drawer.js +132 -0
  58. package/dist-lib/components/ui/Drawer.js.map +1 -0
  59. package/dist-lib/components/ui/DropdownMenu.js +169 -0
  60. package/dist-lib/components/ui/DropdownMenu.js.map +1 -0
  61. package/dist-lib/components/ui/EmptyState.d.ts +2 -0
  62. package/dist-lib/components/ui/EmptyState.js +46 -0
  63. package/dist-lib/components/ui/EmptyState.js.map +1 -0
  64. package/dist-lib/components/ui/ErrorState.d.ts +13 -2
  65. package/dist-lib/components/ui/ErrorState.js +69 -0
  66. package/dist-lib/components/ui/ErrorState.js.map +1 -0
  67. package/dist-lib/components/ui/FileUpload.d.ts +3 -0
  68. package/dist-lib/components/ui/FileUpload.js +111 -0
  69. package/dist-lib/components/ui/FileUpload.js.map +1 -0
  70. package/dist-lib/components/ui/Form.d.ts +16 -2
  71. package/dist-lib/components/ui/Form.js +131 -0
  72. package/dist-lib/components/ui/Form.js.map +1 -0
  73. package/dist-lib/components/ui/Icon.d.ts +1 -1
  74. package/dist-lib/components/ui/Icon.js +32 -0
  75. package/dist-lib/components/ui/Icon.js.map +1 -0
  76. package/dist-lib/components/ui/IconButton.d.ts +3 -3
  77. package/dist-lib/components/ui/IconButton.js +64 -0
  78. package/dist-lib/components/ui/IconButton.js.map +1 -0
  79. package/dist-lib/components/ui/Input.d.ts +9 -4
  80. package/dist-lib/components/ui/Input.js +149 -0
  81. package/dist-lib/components/ui/Input.js.map +1 -0
  82. package/dist-lib/components/ui/Kbd.js +24 -0
  83. package/dist-lib/components/ui/Kbd.js.map +1 -0
  84. package/dist-lib/components/ui/MultiSelect.d.ts +3 -1
  85. package/dist-lib/components/ui/MultiSelect.js +210 -0
  86. package/dist-lib/components/ui/MultiSelect.js.map +1 -0
  87. package/dist-lib/components/ui/Pagination.d.ts +23 -0
  88. package/dist-lib/components/ui/Pagination.js +153 -0
  89. package/dist-lib/components/ui/Pagination.js.map +1 -0
  90. package/dist-lib/components/ui/Popover.js +35 -0
  91. package/dist-lib/components/ui/Popover.js.map +1 -0
  92. package/dist-lib/components/ui/Progress.d.ts +3 -3
  93. package/dist-lib/components/ui/Progress.js +100 -0
  94. package/dist-lib/components/ui/Progress.js.map +1 -0
  95. package/dist-lib/components/ui/RadioGroup.d.ts +1 -1
  96. package/dist-lib/components/ui/RadioGroup.js +81 -0
  97. package/dist-lib/components/ui/RadioGroup.js.map +1 -0
  98. package/dist-lib/components/ui/RelativeTime.d.ts +19 -0
  99. package/dist-lib/components/ui/RelativeTime.js +31 -0
  100. package/dist-lib/components/ui/RelativeTime.js.map +1 -0
  101. package/dist-lib/components/ui/ScrollArea.d.ts +9 -1
  102. package/dist-lib/components/ui/ScrollArea.js +60 -0
  103. package/dist-lib/components/ui/ScrollArea.js.map +1 -0
  104. package/dist-lib/components/ui/Select.d.ts +6 -1
  105. package/dist-lib/components/ui/Select.js +139 -0
  106. package/dist-lib/components/ui/Select.js.map +1 -0
  107. package/dist-lib/components/ui/Separator.js +26 -0
  108. package/dist-lib/components/ui/Separator.js.map +1 -0
  109. package/dist-lib/components/ui/Sheet.d.ts +10 -1
  110. package/dist-lib/components/ui/Sheet.js +136 -0
  111. package/dist-lib/components/ui/Sheet.js.map +1 -0
  112. package/dist-lib/components/ui/Sidebar.d.ts +29 -14
  113. package/dist-lib/components/ui/Sidebar.js +212 -0
  114. package/dist-lib/components/ui/Sidebar.js.map +1 -0
  115. package/dist-lib/components/ui/Skeleton.d.ts +13 -2
  116. package/dist-lib/components/ui/Skeleton.js +29 -0
  117. package/dist-lib/components/ui/Skeleton.js.map +1 -0
  118. package/dist-lib/components/ui/Slider.js +64 -0
  119. package/dist-lib/components/ui/Slider.js.map +1 -0
  120. package/dist-lib/components/ui/Snackbar.d.ts +1 -1
  121. package/dist-lib/components/ui/Snackbar.js +57 -0
  122. package/dist-lib/components/ui/Snackbar.js.map +1 -0
  123. package/dist-lib/components/ui/Spinner.js +54 -0
  124. package/dist-lib/components/ui/Spinner.js.map +1 -0
  125. package/dist-lib/components/ui/Stepper.d.ts +6 -0
  126. package/dist-lib/components/ui/Stepper.js +111 -0
  127. package/dist-lib/components/ui/Stepper.js.map +1 -0
  128. package/dist-lib/components/ui/Switch.js +80 -0
  129. package/dist-lib/components/ui/Switch.js.map +1 -0
  130. package/dist-lib/components/ui/Table.d.ts +41 -5
  131. package/dist-lib/components/ui/Table.js +179 -0
  132. package/dist-lib/components/ui/Table.js.map +1 -0
  133. package/dist-lib/components/ui/Tabs.d.ts +1 -1
  134. package/dist-lib/components/ui/Tabs.js +91 -0
  135. package/dist-lib/components/ui/Tabs.js.map +1 -0
  136. package/dist-lib/components/ui/TagInput.d.ts +17 -2
  137. package/dist-lib/components/ui/TagInput.js +111 -0
  138. package/dist-lib/components/ui/TagInput.js.map +1 -0
  139. package/dist-lib/components/ui/Textarea.js +88 -0
  140. package/dist-lib/components/ui/Textarea.js.map +1 -0
  141. package/dist-lib/components/ui/Timeline.d.ts +16 -6
  142. package/dist-lib/components/ui/Timeline.js +56 -0
  143. package/dist-lib/components/ui/Timeline.js.map +1 -0
  144. package/dist-lib/components/ui/Toast.d.ts +1 -1
  145. package/dist-lib/components/ui/Toast.js +158 -0
  146. package/dist-lib/components/ui/Toast.js.map +1 -0
  147. package/dist-lib/components/ui/Tooltip.d.ts +4 -1
  148. package/dist-lib/components/ui/Tooltip.js +60 -0
  149. package/dist-lib/components/ui/Tooltip.js.map +1 -0
  150. package/dist-lib/components/ui/TopNav.d.ts +7 -3
  151. package/dist-lib/components/ui/TopNav.js +62 -0
  152. package/dist-lib/components/ui/TopNav.js.map +1 -0
  153. package/dist-lib/components/ui/Tree.d.ts +34 -7
  154. package/dist-lib/components/ui/Tree.js +170 -0
  155. package/dist-lib/components/ui/Tree.js.map +1 -0
  156. package/dist-lib/hooks/use-debounce.d.ts +19 -0
  157. package/dist-lib/hooks/use-debounce.js +36 -0
  158. package/dist-lib/hooks/use-debounce.js.map +1 -0
  159. package/dist-lib/hooks/use-delayed-loading.d.ts +21 -0
  160. package/dist-lib/hooks/use-delayed-loading.js +20 -0
  161. package/dist-lib/hooks/use-delayed-loading.js.map +1 -0
  162. package/dist-lib/hooks/use-field-ids.d.ts +30 -0
  163. package/dist-lib/hooks/use-field-ids.js +18 -0
  164. package/dist-lib/hooks/use-field-ids.js.map +1 -0
  165. package/dist-lib/hooks/use-media-query.d.ts +3 -1
  166. package/dist-lib/hooks/use-media-query.js +22 -0
  167. package/dist-lib/hooks/use-media-query.js.map +1 -0
  168. package/dist-lib/hooks/use-modifier-key.d.ts +13 -0
  169. package/dist-lib/hooks/use-modifier-key.js +19 -0
  170. package/dist-lib/hooks/use-modifier-key.js.map +1 -0
  171. package/dist-lib/hooks/use-strings.d.ts +5 -0
  172. package/dist-lib/hooks/use-strings.js +12 -0
  173. package/dist-lib/hooks/use-strings.js.map +1 -0
  174. package/dist-lib/hooks/use-toast.d.ts +7 -2
  175. package/dist-lib/hooks/use-toast.js +67 -0
  176. package/dist-lib/hooks/use-toast.js.map +1 -0
  177. package/dist-lib/icon.d.ts +8 -0
  178. package/dist-lib/icon.js +7 -0
  179. package/dist-lib/icon.js.map +1 -0
  180. package/dist-lib/icons/index.d.ts +80 -88
  181. package/dist-lib/icons/index.js +163 -0
  182. package/dist-lib/icons/index.js.map +1 -0
  183. package/dist-lib/illustrations/index.d.ts +1 -0
  184. package/dist-lib/illustrations/index.js +114 -0
  185. package/dist-lib/illustrations/index.js.map +1 -0
  186. package/dist-lib/index.d.ts +68 -59
  187. package/dist-lib/index.js +288 -4530
  188. package/dist-lib/index.js.map +1 -1
  189. package/dist-lib/lib/format.d.ts +65 -0
  190. package/dist-lib/lib/format.js +95 -0
  191. package/dist-lib/lib/format.js.map +1 -0
  192. package/dist-lib/lib/return-focus.d.ts +11 -0
  193. package/dist-lib/lib/return-focus.js +12 -0
  194. package/dist-lib/lib/return-focus.js.map +1 -0
  195. package/dist-lib/lib/strings.d.ts +190 -0
  196. package/dist-lib/lib/strings.js +141 -0
  197. package/dist-lib/lib/strings.js.map +1 -0
  198. package/dist-lib/lib/strings.mn.d.ts +3 -0
  199. package/dist-lib/lib/strings.mn.js +115 -0
  200. package/dist-lib/lib/strings.mn.js.map +1 -0
  201. package/dist-lib/lib/utils.d.ts +0 -1
  202. package/dist-lib/lib/utils.js +10 -0
  203. package/dist-lib/lib/utils.js.map +1 -0
  204. package/dist-lib/styles.css +1 -1
  205. package/dist-lib/theme.css +840 -0
  206. package/package.json +38 -8
  207. package/dist-lib/index.cjs +0 -8
  208. package/dist-lib/index.cjs.map +0 -1
@@ -0,0 +1,153 @@
1
+ "use client";
2
+ import { jsxs as d, jsx as i } from "react/jsx-runtime";
3
+ import { forwardRef as A, useId as B, useMemo as E } from "react";
4
+ import { ChevronsLeft as T, ChevronLeft as V, ChevronRight as _, ChevronsRight as $ } from "../../icons/index.js";
5
+ import { cn as g } from "../../lib/utils.js";
6
+ import { useStrings as q } from "../../hooks/use-strings.js";
7
+ import { defaultStrings as z, formatString as h } from "../../lib/strings.js";
8
+ import { Select as D, SelectTrigger as G, SelectContent as H, SelectItem as K } from "./Select.js";
9
+ function y(r) {
10
+ return {
11
+ showing: (e, t, c) => h(r.showing, { from: e, to: t, total: c }),
12
+ rowsPerPage: r.rowsPerPage,
13
+ first: r.first,
14
+ last: r.last,
15
+ prev: r.prev,
16
+ next: r.next,
17
+ page: (e) => h(r.page, { n: e }),
18
+ perPage: (e) => h(r.perPage, { n: e }),
19
+ nav: r.nav
20
+ };
21
+ }
22
+ const ee = y(
23
+ z.pagination
24
+ );
25
+ function Q(r, e, t = 7) {
26
+ if (e <= t) return Array.from({ length: e }, (l, f) => f + 1);
27
+ const c = 1, s = [], a = Math.max(2, r - c), u = Math.min(e - 1, r + c);
28
+ s.push(1), a > 2 && s.push("gap");
29
+ for (let l = a; l <= u; l++) s.push(l);
30
+ return u < e - 1 && s.push("gap"), s.push(e), s;
31
+ }
32
+ const U = A(function({
33
+ page: e,
34
+ pageCount: t,
35
+ onPageChange: c,
36
+ totalItems: s,
37
+ pageSize: a,
38
+ pageSizeOptions: u,
39
+ onPageSizeChange: l,
40
+ showJump: f = !0,
41
+ labels: P,
42
+ className: j,
43
+ "aria-label": C,
44
+ ...L
45
+ }, M) {
46
+ const R = q(), o = { ...y(R.pagination), ...P }, v = B(), S = E(() => Q(e, t), [e, t]), x = !s || t <= 0, N = a === void 0 ? void 0 : x ? 0 : (e - 1) * a + 1, p = a === void 0 || s === void 0 ? void 0 : x ? 0 : Math.min(e * a, s), k = e <= 1 || t <= 0, w = e >= t, m = (n) => {
47
+ n < 1 || n > t || n === e || c(n);
48
+ };
49
+ return /* @__PURE__ */ d(
50
+ "nav",
51
+ {
52
+ ref: M,
53
+ "aria-label": C ?? o.nav,
54
+ className: g("flex flex-wrap items-center justify-between gap-3", j),
55
+ ...L,
56
+ children: [
57
+ /* @__PURE__ */ d("div", { className: "text-foreground-muted flex items-center gap-3 text-sm", children: [
58
+ s !== void 0 && a !== void 0 && N !== void 0 && p !== void 0 && /* @__PURE__ */ i("span", { className: "tabular", children: o.showing(N, p, s) }),
59
+ u && l && a !== void 0 && /* @__PURE__ */ d("div", { className: "flex items-center gap-2", children: [
60
+ /* @__PURE__ */ i("label", { htmlFor: v, className: "sr-only", children: o.rowsPerPage }),
61
+ /* @__PURE__ */ d(D, { value: String(a), onValueChange: (n) => l(Number(n)), children: [
62
+ /* @__PURE__ */ i(G, { id: v, size: "sm", className: "w-[7.5rem] whitespace-nowrap" }),
63
+ /* @__PURE__ */ i(H, { children: u.map((n) => /* @__PURE__ */ i(K, { value: String(n), children: o.perPage(n) }, n)) })
64
+ ] })
65
+ ] })
66
+ ] }),
67
+ /* @__PURE__ */ d("ul", { className: "flex items-center gap-1", children: [
68
+ f && /* @__PURE__ */ i("li", { className: "hidden sm:block", children: /* @__PURE__ */ i(
69
+ "button",
70
+ {
71
+ type: "button",
72
+ "aria-label": o.first,
73
+ disabled: k,
74
+ onClick: () => m(1),
75
+ className: b,
76
+ children: /* @__PURE__ */ i(T, { className: "size-4", "aria-hidden": !0 })
77
+ }
78
+ ) }),
79
+ /* @__PURE__ */ i("li", { children: /* @__PURE__ */ i(
80
+ "button",
81
+ {
82
+ type: "button",
83
+ "aria-label": o.prev,
84
+ disabled: k,
85
+ onClick: () => m(e - 1),
86
+ className: b,
87
+ children: /* @__PURE__ */ i(V, { className: "size-4", "aria-hidden": !0 })
88
+ }
89
+ ) }),
90
+ /* @__PURE__ */ d("li", { className: "tabular text-foreground-muted px-2 text-sm sm:hidden", "aria-current": "page", children: [
91
+ e,
92
+ " / ",
93
+ t
94
+ ] }),
95
+ S.map(
96
+ (n, F) => n === "gap" ? /* @__PURE__ */ i("li", { className: "text-foreground-subtle hidden px-2 sm:block", children: "…" }, `gap-${F}`) : /* @__PURE__ */ i("li", { className: "hidden sm:block", children: /* @__PURE__ */ i(
97
+ "button",
98
+ {
99
+ type: "button",
100
+ "aria-label": o.page(n),
101
+ "aria-current": n === e ? "page" : void 0,
102
+ onClick: () => m(n),
103
+ className: g(
104
+ "tabular inline-flex size-8 items-center justify-center rounded-md text-sm outline-none",
105
+ "transition-colors duration-[var(--duration-fast)]",
106
+ "focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2",
107
+ n === e ? "bg-accent text-on-accent font-medium" : "text-foreground-muted hover:bg-background-muted hover:text-foreground"
108
+ ),
109
+ children: n
110
+ }
111
+ ) }, n)
112
+ ),
113
+ /* @__PURE__ */ i("li", { children: /* @__PURE__ */ i(
114
+ "button",
115
+ {
116
+ type: "button",
117
+ "aria-label": o.next,
118
+ disabled: w,
119
+ onClick: () => m(e + 1),
120
+ className: b,
121
+ children: /* @__PURE__ */ i(_, { className: "size-4", "aria-hidden": !0 })
122
+ }
123
+ ) }),
124
+ f && /* @__PURE__ */ i("li", { className: "hidden sm:block", children: /* @__PURE__ */ i(
125
+ "button",
126
+ {
127
+ type: "button",
128
+ "aria-label": o.last,
129
+ disabled: w,
130
+ onClick: () => m(t),
131
+ className: b,
132
+ children: /* @__PURE__ */ i($, { className: "size-4", "aria-hidden": !0 })
133
+ }
134
+ ) })
135
+ ] })
136
+ ]
137
+ }
138
+ );
139
+ });
140
+ U.displayName = "Pagination";
141
+ const b = g(
142
+ "inline-flex size-8 items-center justify-center rounded-md text-foreground-muted",
143
+ "transition-colors duration-[var(--duration-fast)]",
144
+ "hover:bg-background-muted hover:text-foreground",
145
+ "disabled:opacity-50 disabled:pointer-events-none",
146
+ "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
147
+ );
148
+ export {
149
+ U as Pagination,
150
+ ee as defaultPaginationLabels,
151
+ y as paginationLabelsFromStrings
152
+ };
153
+ //# sourceMappingURL=Pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pagination.js","sources":["../../../src/components/ui/Pagination.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, useId, useMemo, type HTMLAttributes } from 'react';\nimport { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\nimport { defaultStrings, formatString, type UiStrings } from '@/lib/strings';\nimport { Select, SelectContent, SelectItem, SelectTrigger } from './Select';\n\nexport interface PaginationLabels {\n /** Summary line. Receives the computed range. */\n showing: (from: number, to: number, total: number) => string;\n /** sr-only label for the page-size select. */\n rowsPerPage: string;\n first: string;\n last: string;\n prev: string;\n next: string;\n /** aria-label for a numbered page button. */\n page: (n: number) => string;\n /** Suffix in the page-size options, e.g. \"20 / page\". */\n perPage: (n: number) => string;\n /** aria-label of the nav landmark. */\n nav: string;\n}\n\n/** Build the label set from a strings object (context → functions). */\nexport function paginationLabelsFromStrings(s: UiStrings['pagination']): PaginationLabels {\n return {\n showing: (from, to, total) => formatString(s.showing, { from, to, total }),\n rowsPerPage: s.rowsPerPage,\n first: s.first,\n last: s.last,\n prev: s.prev,\n next: s.next,\n page: (n) => formatString(s.page, { n }),\n perPage: (n) => formatString(s.perPage, { n }),\n nav: s.nav,\n };\n}\n\n/** English defaults — derived from `defaultStrings.pagination`. */\nexport const defaultPaginationLabels: PaginationLabels = paginationLabelsFromStrings(\n defaultStrings.pagination,\n);\n\nexport interface PaginationProps extends HTMLAttributes<HTMLElement> {\n /** 1-indexed current page. */\n page: number;\n /** Total number of pages. Set to 0 to hide page numbers. */\n pageCount: number;\n /** Called with the new page when navigating. */\n onPageChange: (page: number) => void;\n /** Total item count, for the \"Showing 1-20 of 200\" hint. Omit to hide. */\n totalItems?: number;\n /** Items shown per page (controlled if `onPageSizeChange` is provided). */\n pageSize?: number;\n /** Options for the page-size select. */\n pageSizeOptions?: number[];\n /** Called when the user picks a new page size. */\n onPageSizeChange?: (size: number) => void;\n /** Show first/last (« ») jump buttons. Default true. */\n showJump?: boolean;\n /** Override any of the visible / accessible strings (i18n). */\n labels?: Partial<PaginationLabels>;\n}\n\nfunction pageRange(current: number, total: number, max = 7): (number | 'gap')[] {\n if (total <= max) return Array.from({ length: total }, (_, i) => i + 1);\n const window = 1;\n const result: (number | 'gap')[] = [];\n const start = Math.max(2, current - window);\n const end = Math.min(total - 1, current + window);\n\n result.push(1);\n if (start > 2) result.push('gap');\n for (let i = start; i <= end; i++) result.push(i);\n if (end < total - 1) result.push('gap');\n result.push(total);\n return result;\n}\n\n/**\n * Numbered pagination with prev/next, first/last jumps, page-size selector,\n * and item-count summary.\n *\n * @example\n * <Pagination page={page} pageCount={20} onPageChange={setPage}\n * totalItems={400} pageSize={20}\n * pageSizeOptions={[10, 20, 50]} onPageSizeChange={setSize} />\n *\n * @do Place the count summary on the left and controls on the right.\n * @dont Show numbers when there are >100 pages — use a \"jump to\" input instead.\n */\nexport const Pagination = forwardRef<HTMLElement, PaginationProps>(function Pagination(\n {\n page,\n pageCount,\n onPageChange,\n totalItems,\n pageSize,\n pageSizeOptions,\n onPageSizeChange,\n showJump = true,\n labels: labelsProp,\n className,\n 'aria-label': ariaLabel,\n ...props\n },\n ref,\n) {\n const strings = useStrings();\n const labels = { ...paginationLabelsFromStrings(strings.pagination), ...labelsProp };\n const pageSizeId = useId();\n const pages = useMemo(() => pageRange(page, pageCount), [page, pageCount]);\n\n const empty = !totalItems || pageCount <= 0;\n const from = pageSize === undefined ? undefined : empty ? 0 : (page - 1) * pageSize + 1;\n const to =\n pageSize === undefined || totalItems === undefined\n ? undefined\n : empty\n ? 0\n : Math.min(page * pageSize, totalItems);\n const atStart = page <= 1 || pageCount <= 0;\n const atEnd = page >= pageCount;\n\n const goto = (p: number) => {\n if (p < 1 || p > pageCount || p === page) return;\n onPageChange(p);\n };\n\n return (\n <nav\n ref={ref}\n aria-label={ariaLabel ?? labels.nav}\n className={cn('flex flex-wrap items-center justify-between gap-3', className)}\n {...props}\n >\n <div className=\"text-foreground-muted flex items-center gap-3 text-sm\">\n {totalItems !== undefined &&\n pageSize !== undefined &&\n from !== undefined &&\n to !== undefined && (\n <span className=\"tabular\">{labels.showing(from, to, totalItems)}</span>\n )}\n {pageSizeOptions && onPageSizeChange && pageSize !== undefined && (\n <div className=\"flex items-center gap-2\">\n <label htmlFor={pageSizeId} className=\"sr-only\">\n {labels.rowsPerPage}\n </label>\n <Select value={String(pageSize)} onValueChange={(v) => onPageSizeChange(Number(v))}>\n <SelectTrigger id={pageSizeId} size=\"sm\" className=\"w-[7.5rem] whitespace-nowrap\" />\n <SelectContent>\n {pageSizeOptions.map((s) => (\n <SelectItem key={s} value={String(s)}>\n {labels.perPage(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n </div>\n\n <ul className=\"flex items-center gap-1\">\n {showJump && (\n <li className=\"hidden sm:block\">\n <button\n type=\"button\"\n aria-label={labels.first}\n disabled={atStart}\n onClick={() => goto(1)}\n className={navButtonClass}\n >\n <ChevronsLeft className=\"size-4\" aria-hidden />\n </button>\n </li>\n )}\n <li>\n <button\n type=\"button\"\n aria-label={labels.prev}\n disabled={atStart}\n onClick={() => goto(page - 1)}\n className={navButtonClass}\n >\n <ChevronLeft className=\"size-4\" aria-hidden />\n </button>\n </li>\n {/* Below sm the page list collapses to \"current / total\". */}\n <li className=\"tabular text-foreground-muted px-2 text-sm sm:hidden\" aria-current=\"page\">\n {page} / {pageCount}\n </li>\n {pages.map((p, i) =>\n p === 'gap' ? (\n <li key={`gap-${i}`} className=\"text-foreground-subtle hidden px-2 sm:block\">\n …\n </li>\n ) : (\n <li key={p} className=\"hidden sm:block\">\n <button\n type=\"button\"\n aria-label={labels.page(p)}\n aria-current={p === page ? 'page' : undefined}\n onClick={() => goto(p)}\n className={cn(\n 'tabular inline-flex size-8 items-center justify-center rounded-md text-sm outline-none',\n 'transition-colors duration-[var(--duration-fast)]',\n 'focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2',\n p === page\n ? 'bg-accent text-on-accent font-medium'\n : 'text-foreground-muted hover:bg-background-muted hover:text-foreground',\n )}\n >\n {p}\n </button>\n </li>\n ),\n )}\n <li>\n <button\n type=\"button\"\n aria-label={labels.next}\n disabled={atEnd}\n onClick={() => goto(page + 1)}\n className={navButtonClass}\n >\n <ChevronRight className=\"size-4\" aria-hidden />\n </button>\n </li>\n {showJump && (\n <li className=\"hidden sm:block\">\n <button\n type=\"button\"\n aria-label={labels.last}\n disabled={atEnd}\n onClick={() => goto(pageCount)}\n className={navButtonClass}\n >\n <ChevronsRight className=\"size-4\" aria-hidden />\n </button>\n </li>\n )}\n </ul>\n </nav>\n );\n});\nPagination.displayName = 'Pagination';\n\nconst navButtonClass = cn(\n 'inline-flex size-8 items-center justify-center rounded-md text-foreground-muted',\n 'transition-colors duration-[var(--duration-fast)]',\n 'hover:bg-background-muted hover:text-foreground',\n 'disabled:opacity-50 disabled:pointer-events-none',\n 'outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n);\n"],"names":["paginationLabelsFromStrings","s","from","to","total","formatString","n","defaultPaginationLabels","defaultStrings","pageRange","current","max","_","i","window","result","start","end","Pagination","forwardRef","page","pageCount","onPageChange","totalItems","pageSize","pageSizeOptions","onPageSizeChange","showJump","labelsProp","className","ariaLabel","props","ref","strings","useStrings","labels","pageSizeId","useId","pages","useMemo","empty","atStart","atEnd","goto","p","jsxs","cn","jsx","Select","v","SelectTrigger","SelectContent","SelectItem","navButtonClass","ChevronsLeft","ChevronLeft","ChevronRight","ChevronsRight"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ "use client";
2
+ import { jsx as e } from "react/jsx-runtime";
3
+ import { forwardRef as s } from "react";
4
+ import * as o from "@radix-ui/react-popover";
5
+ import { cn as d } from "../../lib/utils.js";
6
+ const v = o.Root, g = o.Trigger, u = o.Anchor, b = o.Close, p = s(function({ className: t, align: r = "center", sideOffset: i = 4, ...n }, a) {
7
+ return /* @__PURE__ */ e(o.Portal, { children: /* @__PURE__ */ e(
8
+ o.Content,
9
+ {
10
+ ref: a,
11
+ align: r,
12
+ sideOffset: i,
13
+ className: d(
14
+ "border-border bg-popover text-popover-foreground z-[var(--z-popover)] max-w-sm min-w-72 rounded-lg border p-4 shadow-md",
15
+ "focus-visible:ring-ring focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
16
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
17
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
18
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
19
+ "data-[side=bottom]:slide-in-from-top-1 data-[side=top]:slide-in-from-bottom-1",
20
+ "data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1",
21
+ t
22
+ ),
23
+ ...n
24
+ }
25
+ ) });
26
+ });
27
+ p.displayName = "PopoverContent";
28
+ export {
29
+ v as Popover,
30
+ u as PopoverAnchor,
31
+ b as PopoverClose,
32
+ p as PopoverContent,
33
+ g as PopoverTrigger
34
+ };
35
+ //# sourceMappingURL=Popover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Popover.js","sources":["../../../src/components/ui/Popover.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from 'react';\nimport * as PopoverPrimitive from '@radix-ui/react-popover';\nimport { cn } from '@/lib/utils';\n\nexport const Popover = PopoverPrimitive.Root;\nexport const PopoverTrigger = PopoverPrimitive.Trigger;\nexport const PopoverAnchor = PopoverPrimitive.Anchor;\nexport const PopoverClose = PopoverPrimitive.Close;\n\nexport const PopoverContent = forwardRef<\n ElementRef<typeof PopoverPrimitive.Content>,\n ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>\n>(function PopoverContent({ className, align = 'center', sideOffset = 4, ...props }, ref) {\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n align={align}\n sideOffset={sideOffset}\n className={cn(\n 'border-border bg-popover text-popover-foreground z-[var(--z-popover)] max-w-sm min-w-72 rounded-lg border p-4 shadow-md',\n 'focus-visible:ring-ring focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2',\n 'data-[state=open]:animate-in data-[state=closed]:animate-out',\n 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',\n 'data-[side=bottom]:slide-in-from-top-1 data-[side=top]:slide-in-from-bottom-1',\n 'data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1',\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n );\n});\nPopoverContent.displayName = 'PopoverContent';\n\n/**\n * @example\n * <Popover>\n * <PopoverTrigger asChild><Button variant=\"outline\">Options</Button></PopoverTrigger>\n * <PopoverContent>…</PopoverContent>\n * </Popover>\n *\n * @do Reserve Popover for low-stakes, transient UI — pickers, mini-forms.\n * @dont Use Popover for navigation menus — that's DropdownMenu.\n */\n"],"names":["Popover","PopoverPrimitive","PopoverTrigger","PopoverAnchor","PopoverClose","PopoverContent","forwardRef","className","align","sideOffset","props","ref","jsx","cn"],"mappings":";;;;;AAMO,MAAMA,IAAUC,EAAiB,MAC3BC,IAAiBD,EAAiB,SAClCE,IAAgBF,EAAiB,QACjCG,IAAeH,EAAiB,OAEhCI,IAAiBC,EAG5B,SAAwB,EAAE,WAAAC,GAAW,OAAAC,IAAQ,UAAU,YAAAC,IAAa,GAAG,GAAGC,EAAA,GAASC,GAAK;AACxF,SACE,gBAAAC,EAACX,EAAiB,QAAjB,EACC,UAAA,gBAAAW;AAAA,IAACX,EAAiB;AAAA,IAAjB;AAAA,MACC,KAAAU;AAAA,MACA,OAAAH;AAAA,MACA,YAAAC;AAAA,MACA,WAAWI;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACAN;AAAA,MAAA;AAAA,MAED,GAAGG;AAAA,IAAA;AAAA,EAAA,GAER;AAEJ,CAAC;AACDL,EAAe,cAAc;"}
@@ -1,7 +1,7 @@
1
1
  import { ComponentPropsWithoutRef, SVGProps } from 'react';
2
2
  import * as ProgressPrimitive from '@radix-ui/react-progress';
3
3
  export interface ProgressProps extends Omit<ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>, 'value'> {
4
- /** 0100. Omit for indeterminate. */
4
+ /** 0–`max` (default 100). Clamped. Omit for indeterminate. */
5
5
  value?: number | null;
6
6
  /** Bar height. */
7
7
  size?: 'sm' | 'md' | 'lg';
@@ -22,8 +22,8 @@ export declare const Progress: import('react').ForwardRefExoticComponent<Progres
22
22
  export interface ProgressCircleProps extends SVGProps<SVGSVGElement> {
23
23
  /** 0–100. Omit for indeterminate. */
24
24
  value?: number;
25
- /** Pixel size of the SVG. */
26
- size?: number;
25
+ /** `sm` 24 / `md` 36 / `lg` 48, or an explicit pixel size. */
26
+ size?: 'sm' | 'md' | 'lg' | number;
27
27
  /** Stroke thickness. */
28
28
  thickness?: number;
29
29
  /** Visible label for screen readers. */
@@ -0,0 +1,100 @@
1
+ "use client";
2
+ import { jsx as c, jsxs as N } from "react/jsx-runtime";
3
+ import { forwardRef as h } from "react";
4
+ import * as u from "@radix-ui/react-progress";
5
+ import { cn as m } from "../../lib/utils.js";
6
+ const b = { sm: "h-1", md: "h-1.5", lg: "h-2" }, v = {
7
+ accent: "bg-accent",
8
+ success: "bg-success",
9
+ warning: "bg-warning",
10
+ danger: "bg-danger"
11
+ }, w = h(
12
+ function({ className: o, value: s, max: t = 100, size: l = "md", tone: d = "accent", ...f }, g) {
13
+ const e = s == null || Number.isNaN(s), r = t > 0 ? t : 100, n = e ? 0 : Math.min(r, Math.max(0, s)), a = n / r * 100;
14
+ return /* @__PURE__ */ c(
15
+ u.Root,
16
+ {
17
+ ref: g,
18
+ value: e ? void 0 : n,
19
+ max: r,
20
+ className: m(
21
+ "bg-background-muted relative w-full overflow-hidden rounded-full",
22
+ b[l],
23
+ o
24
+ ),
25
+ ...f,
26
+ children: /* @__PURE__ */ c(
27
+ u.Indicator,
28
+ {
29
+ className: m(
30
+ "h-full w-full flex-1 transition-transform",
31
+ v[d],
32
+ e && "animate-progress-indeterminate origin-left"
33
+ ),
34
+ "data-motion-keep": e ? "" : void 0,
35
+ style: e ? void 0 : { transform: `translateX(-${100 - a}%)` }
36
+ }
37
+ )
38
+ }
39
+ );
40
+ }
41
+ );
42
+ w.displayName = "Progress";
43
+ const x = { sm: 24, md: 36, lg: 48 }, y = {
44
+ accent: "stroke-accent",
45
+ success: "stroke-success",
46
+ warning: "stroke-warning",
47
+ danger: "stroke-danger"
48
+ }, M = h(
49
+ function({ value: o, size: s = "md", thickness: t = 3, className: l, tone: d = "accent", ...f }, g) {
50
+ const e = typeof s == "number" ? s : x[s], r = o === void 0 || Number.isNaN(o), n = r ? 0 : Math.min(100, Math.max(0, o)), a = (e - t) / 2, i = 2 * Math.PI * a, k = r ? 0 : i - n / 100 * i;
51
+ return /* @__PURE__ */ N(
52
+ "svg",
53
+ {
54
+ ref: g,
55
+ width: e,
56
+ height: e,
57
+ viewBox: `0 0 ${e} ${e}`,
58
+ role: "progressbar",
59
+ "aria-valuemin": 0,
60
+ "aria-valuemax": 100,
61
+ "aria-valuenow": r ? void 0 : n,
62
+ "data-motion-keep": r ? "" : void 0,
63
+ className: m("shrink-0", r && "animate-spin", l),
64
+ ...f,
65
+ children: [
66
+ /* @__PURE__ */ c(
67
+ "circle",
68
+ {
69
+ cx: e / 2,
70
+ cy: e / 2,
71
+ r: a,
72
+ strokeWidth: t,
73
+ className: "stroke-background-muted fill-none"
74
+ }
75
+ ),
76
+ /* @__PURE__ */ c(
77
+ "circle",
78
+ {
79
+ cx: e / 2,
80
+ cy: e / 2,
81
+ r: a,
82
+ strokeWidth: t,
83
+ strokeLinecap: "round",
84
+ strokeDasharray: i,
85
+ strokeDashoffset: r ? i * 0.7 : k,
86
+ className: m(y[d], "fill-none transition-[stroke-dashoffset]"),
87
+ transform: `rotate(-90 ${e / 2} ${e / 2})`
88
+ }
89
+ )
90
+ ]
91
+ }
92
+ );
93
+ }
94
+ );
95
+ M.displayName = "ProgressCircle";
96
+ export {
97
+ w as Progress,
98
+ M as ProgressCircle
99
+ };
100
+ //# sourceMappingURL=Progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Progress.js","sources":["../../../src/components/ui/Progress.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type ComponentPropsWithoutRef, type ElementRef, type SVGProps } from 'react';\nimport * as ProgressPrimitive from '@radix-ui/react-progress';\nimport { cn } from '@/lib/utils';\n\n/* -----------------------------------------------------------------------------\n * Linear — bar that fills left-to-right.\n * Circular — SVG ring. Both support determinate (value 0..100) and\n * indeterminate (`value` omitted) states.\n * --------------------------------------------------------------------------- */\n\nexport interface ProgressProps extends Omit<\n ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>,\n 'value'\n> {\n /** 0–`max` (default 100). Clamped. Omit for indeterminate. */\n value?: number | null;\n /** Bar height. */\n size?: 'sm' | 'md' | 'lg';\n /** Visual tone. Use `success`/`danger` to colour-code completion state. */\n tone?: 'accent' | 'success' | 'warning' | 'danger';\n}\n\nconst heightMap = { sm: 'h-1', md: 'h-1.5', lg: 'h-2' } as const;\nconst fillTone = {\n accent: 'bg-accent',\n success: 'bg-success',\n warning: 'bg-warning',\n danger: 'bg-danger',\n} as const;\n\n/**\n * Linear progress bar.\n *\n * @example\n * <Progress value={uploadPct} aria-label=\"Upload progress\" />\n * <Progress aria-label=\"Loading\" /> // indeterminate\n *\n * @do Always pair Progress with an `aria-label` describing what is progressing.\n * @dont Use a Progress for an unknown-completion task — use a Spinner.\n */\nexport const Progress = forwardRef<ElementRef<typeof ProgressPrimitive.Root>, ProgressProps>(\n function Progress({ className, value, max = 100, size = 'md', tone = 'accent', ...props }, ref) {\n const indeterminate = value === undefined || value === null || Number.isNaN(value);\n const safeMax = max > 0 ? max : 100;\n const clamped = indeterminate ? 0 : Math.min(safeMax, Math.max(0, value as number));\n const pct = (clamped / safeMax) * 100;\n return (\n <ProgressPrimitive.Root\n ref={ref}\n value={indeterminate ? undefined : clamped}\n max={safeMax}\n className={cn(\n 'bg-background-muted relative w-full overflow-hidden rounded-full',\n heightMap[size],\n className,\n )}\n {...props}\n >\n <ProgressPrimitive.Indicator\n className={cn(\n 'h-full w-full flex-1 transition-transform',\n fillTone[tone],\n indeterminate && 'animate-progress-indeterminate origin-left',\n )}\n data-motion-keep={indeterminate ? '' : undefined}\n style={indeterminate ? undefined : { transform: `translateX(-${100 - pct}%)` }}\n />\n </ProgressPrimitive.Root>\n );\n },\n);\nProgress.displayName = 'Progress';\n\nexport interface ProgressCircleProps extends SVGProps<SVGSVGElement> {\n /** 0–100. Omit for indeterminate. */\n value?: number;\n /** `sm` 24 / `md` 36 / `lg` 48, or an explicit pixel size. */\n size?: 'sm' | 'md' | 'lg' | number;\n /** Stroke thickness. */\n thickness?: number;\n /** Visible label for screen readers. */\n 'aria-label': string;\n tone?: 'accent' | 'success' | 'warning' | 'danger';\n}\n\nconst circleSize = { sm: 24, md: 36, lg: 48 } as const;\n\nconst circleTone = {\n accent: 'stroke-accent',\n success: 'stroke-success',\n warning: 'stroke-warning',\n danger: 'stroke-danger',\n} as const;\n\n/**\n * Circular progress ring. Pairs with a numeric label for percentage-style\n * indicators.\n *\n * @example\n * <ProgressCircle value={72} aria-label=\"Storage used\" />\n */\nexport const ProgressCircle = forwardRef<SVGSVGElement, ProgressCircleProps>(\n function ProgressCircle(\n { value, size: sizeProp = 'md', thickness = 3, className, tone = 'accent', ...props },\n ref,\n ) {\n const size = typeof sizeProp === 'number' ? sizeProp : circleSize[sizeProp];\n const isIndeterminate = value === undefined || Number.isNaN(value);\n const clamped = isIndeterminate ? 0 : Math.min(100, Math.max(0, value as number));\n const radius = (size - thickness) / 2;\n const circumference = 2 * Math.PI * radius;\n const offset = isIndeterminate ? 0 : circumference - (clamped / 100) * circumference;\n\n return (\n <svg\n ref={ref}\n width={size}\n height={size}\n viewBox={`0 0 ${size} ${size}`}\n role=\"progressbar\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={isIndeterminate ? undefined : clamped}\n data-motion-keep={isIndeterminate ? '' : undefined}\n className={cn('shrink-0', isIndeterminate && 'animate-spin', className)}\n {...props}\n >\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n strokeWidth={thickness}\n className=\"stroke-background-muted fill-none\"\n />\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n strokeWidth={thickness}\n strokeLinecap=\"round\"\n strokeDasharray={circumference}\n strokeDashoffset={isIndeterminate ? circumference * 0.7 : offset}\n className={cn(circleTone[tone], 'fill-none transition-[stroke-dashoffset]')}\n transform={`rotate(-90 ${size / 2} ${size / 2})`}\n />\n </svg>\n );\n },\n);\nProgressCircle.displayName = 'ProgressCircle';\n"],"names":["heightMap","fillTone","Progress","forwardRef","className","value","max","size","tone","props","ref","indeterminate","safeMax","clamped","pct","jsx","ProgressPrimitive","cn","circleSize","circleTone","ProgressCircle","sizeProp","thickness","isIndeterminate","radius","circumference","offset","jsxs"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { ComponentPropsWithoutRef, ReactNode } from 'react';
2
2
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
3
3
  export interface RadioGroupProps extends ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> {
4
- /** Lay out the radios horizontally (default) or vertically. */
4
+ /** Lay out the radios vertically (default) or horizontally. Also drives arrow-key navigation. */
5
5
  orientation?: 'horizontal' | 'vertical';
6
6
  }
7
7
  export declare const RadioGroup: import('react').ForwardRefExoticComponent<RadioGroupProps & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,81 @@
1
+ "use client";
2
+ import { jsx as e, jsxs as c } from "react/jsx-runtime";
3
+ import { forwardRef as f, useId as h } from "react";
4
+ import * as d from "@radix-ui/react-radio-group";
5
+ import { cn as i } from "../../lib/utils.js";
6
+ const y = f(
7
+ function({ className: a, orientation: r = "vertical", ...o }, t) {
8
+ return /* @__PURE__ */ e(
9
+ d.Root,
10
+ {
11
+ ref: t,
12
+ orientation: r,
13
+ className: i(
14
+ "flex gap-3",
15
+ r === "vertical" ? "flex-col" : "flex-row flex-wrap",
16
+ a
17
+ ),
18
+ ...o
19
+ }
20
+ );
21
+ }
22
+ );
23
+ y.displayName = "RadioGroup";
24
+ const N = f(
25
+ function({
26
+ className: a,
27
+ label: r,
28
+ description: o,
29
+ hideLabel: t,
30
+ id: m,
31
+ disabled: l,
32
+ "aria-describedby": b,
33
+ ...p
34
+ }, x) {
35
+ const v = h(), s = m ?? v, n = o ? `${s}-desc` : void 0, g = [n, b].filter(Boolean).join(" ") || void 0;
36
+ return /* @__PURE__ */ c("div", { className: i("flex items-start gap-2.5", a), children: [
37
+ /* @__PURE__ */ e(
38
+ d.Item,
39
+ {
40
+ ref: x,
41
+ id: s,
42
+ disabled: l,
43
+ "aria-describedby": g,
44
+ className: i(
45
+ "bg-card relative mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full border",
46
+ // WCAG 2.5.8: 16px visual box, ≥24px hit area via an invisible inset halo.
47
+ 'before:absolute before:-inset-1.5 before:rounded-full before:content-[""]',
48
+ "transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]",
49
+ "focus-visible:ring-ring focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
50
+ "data-[state=checked]:border-accent",
51
+ "disabled:cursor-not-allowed disabled:opacity-50",
52
+ "border-border-input aria-invalid:border-danger"
53
+ ),
54
+ ...p,
55
+ children: /* @__PURE__ */ e(d.Indicator, { className: "flex items-center justify-center", children: /* @__PURE__ */ e("span", { className: "bg-accent size-2 rounded-full", "aria-hidden": !0 }) })
56
+ }
57
+ ),
58
+ r && /* @__PURE__ */ c("div", { className: "flex flex-col gap-0.5", children: [
59
+ /* @__PURE__ */ e(
60
+ "label",
61
+ {
62
+ htmlFor: s,
63
+ className: i(
64
+ "text-foreground text-sm select-none",
65
+ l && "cursor-not-allowed opacity-50",
66
+ t && "sr-only"
67
+ ),
68
+ children: r
69
+ }
70
+ ),
71
+ o && /* @__PURE__ */ e("p", { id: n, className: "text-foreground-subtle text-xs", children: o })
72
+ ] })
73
+ ] });
74
+ }
75
+ );
76
+ N.displayName = "RadioItem";
77
+ export {
78
+ y as RadioGroup,
79
+ N as RadioItem
80
+ };
81
+ //# sourceMappingURL=RadioGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RadioGroup.js","sources":["../../../src/components/ui/RadioGroup.tsx"],"sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useId,\n type ComponentPropsWithoutRef,\n type ElementRef,\n type ReactNode,\n} from 'react';\nimport * as RadioGroupPrimitive from '@radix-ui/react-radio-group';\nimport { cn } from '@/lib/utils';\n\nexport interface RadioGroupProps extends ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> {\n /** Lay out the radios vertically (default) or horizontally. Also drives arrow-key navigation. */\n orientation?: 'horizontal' | 'vertical';\n}\n\nexport const RadioGroup = forwardRef<ElementRef<typeof RadioGroupPrimitive.Root>, RadioGroupProps>(\n function RadioGroup({ className, orientation = 'vertical', ...props }, ref) {\n return (\n <RadioGroupPrimitive.Root\n ref={ref}\n orientation={orientation}\n className={cn(\n 'flex gap-3',\n orientation === 'vertical' ? 'flex-col' : 'flex-row flex-wrap',\n className,\n )}\n {...props}\n />\n );\n },\n);\nRadioGroup.displayName = 'RadioGroup';\n\nexport interface RadioItemProps extends Omit<\n ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>,\n 'asChild'\n> {\n label?: ReactNode;\n description?: ReactNode;\n hideLabel?: boolean;\n}\n\n/**\n * One radio option with inline label + description.\n *\n * @example\n * <RadioGroup defaultValue=\"weekly\">\n * <RadioItem value=\"daily\" label=\"Daily\" description=\"Every morning at 9am\" />\n * <RadioItem value=\"weekly\" label=\"Weekly\" description=\"Monday mornings\" />\n * <RadioItem value=\"never\" label=\"Never\" />\n * </RadioGroup>\n *\n * @do Pair every RadioItem with a label — naked radios are unreachable for\n * screen reader users.\n * @dont Use RadioGroup for binary choices — use Switch.\n */\nexport const RadioItem = forwardRef<ElementRef<typeof RadioGroupPrimitive.Item>, RadioItemProps>(\n function RadioItem(\n {\n className,\n label,\n description,\n hideLabel,\n id,\n disabled,\n 'aria-describedby': ariaDescribedby,\n ...props\n },\n ref,\n ) {\n const autoId = useId();\n const fieldId = id ?? autoId;\n const descId = description ? `${fieldId}-desc` : undefined;\n const describedBy = [descId, ariaDescribedby].filter(Boolean).join(' ') || undefined;\n\n return (\n <div className={cn('flex items-start gap-2.5', className)}>\n <RadioGroupPrimitive.Item\n ref={ref}\n id={fieldId}\n disabled={disabled}\n aria-describedby={describedBy}\n className={cn(\n 'bg-card relative mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full border',\n // WCAG 2.5.8: 16px visual box, ≥24px hit area via an invisible inset halo.\n 'before:absolute before:-inset-1.5 before:rounded-full before:content-[\"\"]',\n 'transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out)]',\n 'focus-visible:ring-ring focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2',\n 'data-[state=checked]:border-accent',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n 'border-border-input aria-invalid:border-danger',\n )}\n {...props}\n >\n <RadioGroupPrimitive.Indicator className=\"flex items-center justify-center\">\n <span className=\"bg-accent size-2 rounded-full\" aria-hidden />\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n\n {label && (\n <div className=\"flex flex-col gap-0.5\">\n <label\n htmlFor={fieldId}\n className={cn(\n 'text-foreground text-sm select-none',\n disabled && 'cursor-not-allowed opacity-50',\n hideLabel && 'sr-only',\n )}\n >\n {label}\n </label>\n {description && (\n <p id={descId} className=\"text-foreground-subtle text-xs\">\n {description}\n </p>\n )}\n </div>\n )}\n </div>\n );\n },\n);\nRadioItem.displayName = 'RadioItem';\n"],"names":["RadioGroup","forwardRef","className","orientation","props","ref","jsx","RadioGroupPrimitive","cn","RadioItem","label","description","hideLabel","id","disabled","ariaDescribedby","autoId","useId","fieldId","descId","describedBy","jsxs"],"mappings":";;;;;AAiBO,MAAMA,IAAaC;AAAA,EACxB,SAAoB,EAAE,WAAAC,GAAW,aAAAC,IAAc,YAAY,GAAGC,EAAA,GAASC,GAAK;AAC1E,WACE,gBAAAC;AAAA,MAACC,EAAoB;AAAA,MAApB;AAAA,QACC,KAAAF;AAAA,QACA,aAAAF;AAAA,QACA,WAAWK;AAAA,UACT;AAAA,UACAL,MAAgB,aAAa,aAAa;AAAA,UAC1CD;AAAA,QAAA;AAAA,QAED,GAAGE;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AACAJ,EAAW,cAAc;AAyBlB,MAAMS,IAAYR;AAAA,EACvB,SACE;AAAA,IACE,WAAAC;AAAA,IACA,OAAAQ;AAAA,IACA,aAAAC;AAAA,IACA,WAAAC;AAAA,IACA,IAAAC;AAAA,IACA,UAAAC;AAAA,IACA,oBAAoBC;AAAA,IACpB,GAAGX;AAAA,EAAA,GAELC,GACA;AACA,UAAMW,IAASC,EAAA,GACTC,IAAUL,KAAMG,GAChBG,IAASR,IAAc,GAAGO,CAAO,UAAU,QAC3CE,IAAc,CAACD,GAAQJ,CAAe,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK;AAE3E,6BACG,OAAA,EAAI,WAAWP,EAAG,4BAA4BN,CAAS,GACtD,UAAA;AAAA,MAAA,gBAAAI;AAAA,QAACC,EAAoB;AAAA,QAApB;AAAA,UACC,KAAAF;AAAA,UACA,IAAIa;AAAA,UACJ,UAAAJ;AAAA,UACA,oBAAkBM;AAAA,UAClB,WAAWZ;AAAA,YACT;AAAA;AAAA,YAEA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UAAA;AAAA,UAED,GAAGJ;AAAA,UAEJ,UAAA,gBAAAE,EAACC,EAAoB,WAApB,EAA8B,WAAU,oCACvC,UAAA,gBAAAD,EAAC,QAAA,EAAK,WAAU,iCAAgC,eAAW,GAAA,CAAC,EAAA,CAC9D;AAAA,QAAA;AAAA,MAAA;AAAA,MAGDI,KACC,gBAAAW,EAAC,OAAA,EAAI,WAAU,yBACb,UAAA;AAAA,QAAA,gBAAAf;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,SAASY;AAAA,YACT,WAAWV;AAAA,cACT;AAAA,cACAM,KAAY;AAAA,cACZF,KAAa;AAAA,YAAA;AAAA,YAGd,UAAAF;AAAA,UAAA;AAAA,QAAA;AAAA,QAEFC,KACC,gBAAAL,EAAC,KAAA,EAAE,IAAIa,GAAQ,WAAU,kCACtB,UAAAR,EAAA,CACH;AAAA,MAAA,EAAA,CAEJ;AAAA,IAAA,GAEJ;AAAA,EAEJ;AACF;AACAF,EAAU,cAAc;"}
@@ -0,0 +1,19 @@
1
+ import { TimeHTMLAttributes } from 'react';
2
+ import { FormatDateOptions } from '../../lib/format.js';
3
+ export interface RelativeTimeProps extends Omit<TimeHTMLAttributes<HTMLTimeElement>, 'dateTime'> {
4
+ /** The instant to describe. */
5
+ date: Date | number | string;
6
+ /** Reference "now". Default `Date.now()` at render — pass one for stable SSR/tests. */
7
+ now?: Date | number;
8
+ /** Options for the absolute `title` (tooltip). */
9
+ absolute?: FormatDateOptions;
10
+ }
11
+ /**
12
+ * `<time>` showing "5 min ago" with the absolute timestamp as `title` and a
13
+ * machine-readable `dateTime`. Strings come from `UiStrings.relativeTime`.
14
+ * Beyond 30 days it falls back to the absolute date so text never goes stale.
15
+ *
16
+ * @example
17
+ * <RelativeTime date={post.createdAt} />
18
+ */
19
+ export declare const RelativeTime: import('react').ForwardRefExoticComponent<RelativeTimeProps & import('react').RefAttributes<HTMLTimeElement>>;
@@ -0,0 +1,31 @@
1
+ "use client";
2
+ import { jsx as N } from "react/jsx-runtime";
3
+ import { forwardRef as h } from "react";
4
+ import { formatDate as v } from "../../lib/format.js";
5
+ import { formatString as a } from "../../lib/strings.js";
6
+ import { useStrings as M } from "../../hooks/use-strings.js";
7
+ import { cn as b } from "../../lib/utils.js";
8
+ const f = 6e4, c = 60 * f, m = 24 * c, R = h(function({ date: n, now: o, absolute: d, className: g, ...p }, T) {
9
+ const t = M().relativeTime, s = n instanceof Date ? n : new Date(n), D = o === void 0 ? Date.now() : o instanceof Date ? o.getTime() : o, l = s.getTime() - D, e = Math.abs(l), r = l > 0, u = v(s, d);
10
+ let i;
11
+ return e < f ? i = t.justNow : e < c ? i = a(r ? t.inMinutes : t.minutesAgo, {
12
+ n: Math.round(e / f)
13
+ }) : e < m ? i = a(r ? t.inHours : t.hoursAgo, {
14
+ n: Math.round(e / c)
15
+ }) : e < 30 * m ? i = a(r ? t.inDays : t.daysAgo, { n: Math.round(e / m) }) : i = u, /* @__PURE__ */ N(
16
+ "time",
17
+ {
18
+ ref: T,
19
+ dateTime: Number.isNaN(s.getTime()) ? void 0 : s.toISOString(),
20
+ title: u,
21
+ className: b("tabular", g),
22
+ ...p,
23
+ children: i
24
+ }
25
+ );
26
+ });
27
+ R.displayName = "RelativeTime";
28
+ export {
29
+ R as RelativeTime
30
+ };
31
+ //# sourceMappingURL=RelativeTime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelativeTime.js","sources":["../../../src/components/ui/RelativeTime.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type TimeHTMLAttributes } from 'react';\nimport { formatDate, type FormatDateOptions } from '@/lib/format';\nimport { formatString } from '@/lib/strings';\nimport { useStrings } from '@/hooks/use-strings';\nimport { cn } from '@/lib/utils';\n\nexport interface RelativeTimeProps extends Omit<TimeHTMLAttributes<HTMLTimeElement>, 'dateTime'> {\n /** The instant to describe. */\n date: Date | number | string;\n /** Reference \"now\". Default `Date.now()` at render — pass one for stable SSR/tests. */\n now?: Date | number;\n /** Options for the absolute `title` (tooltip). */\n absolute?: FormatDateOptions;\n}\n\nconst MIN = 60_000;\nconst HOUR = 60 * MIN;\nconst DAY = 24 * HOUR;\n\n/**\n * `<time>` showing \"5 min ago\" with the absolute timestamp as `title` and a\n * machine-readable `dateTime`. Strings come from `UiStrings.relativeTime`.\n * Beyond 30 days it falls back to the absolute date so text never goes stale.\n *\n * @example\n * <RelativeTime date={post.createdAt} />\n */\nexport const RelativeTime = forwardRef<HTMLTimeElement, RelativeTimeProps>(function RelativeTime(\n { date, now, absolute, className, ...props },\n ref,\n) {\n const strings = useStrings().relativeTime;\n const d = date instanceof Date ? date : new Date(date);\n const ref_ = now === undefined ? Date.now() : now instanceof Date ? now.getTime() : now;\n const diff = d.getTime() - ref_;\n const abs = Math.abs(diff);\n const future = diff > 0;\n const title = formatDate(d, absolute);\n\n let label: string;\n if (abs < MIN) label = strings.justNow;\n else if (abs < HOUR)\n label = formatString(future ? strings.inMinutes : strings.minutesAgo, {\n n: Math.round(abs / MIN),\n });\n else if (abs < DAY)\n label = formatString(future ? strings.inHours : strings.hoursAgo, {\n n: Math.round(abs / HOUR),\n });\n else if (abs < 30 * DAY)\n label = formatString(future ? strings.inDays : strings.daysAgo, { n: Math.round(abs / DAY) });\n else label = title;\n\n return (\n <time\n ref={ref}\n dateTime={Number.isNaN(d.getTime()) ? undefined : d.toISOString()}\n title={title}\n className={cn('tabular', className)}\n {...props}\n >\n {label}\n </time>\n );\n});\nRelativeTime.displayName = 'RelativeTime';\n"],"names":["MIN","HOUR","DAY","RelativeTime","forwardRef","date","now","absolute","className","props","ref","strings","useStrings","d","ref_","diff","abs","future","title","formatDate","label","formatString","jsx","cn"],"mappings":";;;;;;;AAiBA,MAAMA,IAAM,KACNC,IAAO,KAAKD,GACZE,IAAM,KAAKD,GAUJE,IAAeC,EAA+C,SACzE,EAAE,MAAAC,GAAM,KAAAC,GAAK,UAAAC,GAAU,WAAAC,GAAW,GAAGC,EAAA,GACrCC,GACA;AACA,QAAMC,IAAUC,IAAa,cACvBC,IAAIR,aAAgB,OAAOA,IAAO,IAAI,KAAKA,CAAI,GAC/CS,IAAOR,MAAQ,SAAY,KAAK,QAAQA,aAAe,OAAOA,EAAI,QAAA,IAAYA,GAC9ES,IAAOF,EAAE,QAAA,IAAYC,GACrBE,IAAM,KAAK,IAAID,CAAI,GACnBE,IAASF,IAAO,GAChBG,IAAQC,EAAWN,GAAGN,CAAQ;AAEpC,MAAIa;AACJ,SAAIJ,IAAMhB,IAAKoB,IAAQT,EAAQ,UACtBK,IAAMf,IACbmB,IAAQC,EAAaJ,IAASN,EAAQ,YAAYA,EAAQ,YAAY;AAAA,IACpE,GAAG,KAAK,MAAMK,IAAMhB,CAAG;AAAA,EAAA,CACxB,IACMgB,IAAMd,IACbkB,IAAQC,EAAaJ,IAASN,EAAQ,UAAUA,EAAQ,UAAU;AAAA,IAChE,GAAG,KAAK,MAAMK,IAAMf,CAAI;AAAA,EAAA,CACzB,IACMe,IAAM,KAAKd,IAClBkB,IAAQC,EAAaJ,IAASN,EAAQ,SAASA,EAAQ,SAAS,EAAE,GAAG,KAAK,MAAMK,IAAMd,CAAG,GAAG,IACzFkB,IAAQF,GAGX,gBAAAI;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAZ;AAAA,MACA,UAAU,OAAO,MAAMG,EAAE,SAAS,IAAI,SAAYA,EAAE,YAAA;AAAA,MACpD,OAAAK;AAAA,MACA,WAAWK,EAAG,WAAWf,CAAS;AAAA,MACjC,GAAGC;AAAA,MAEH,UAAAW;AAAA,IAAA;AAAA,EAAA;AAGP,CAAC;AACDjB,EAAa,cAAc;"}
@@ -1,3 +1,4 @@
1
+ import { ComponentPropsWithoutRef } from 'react';
1
2
  import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
2
3
  /**
3
4
  * Custom scroll container with consistent styled scrollbars across OS / browsers.
@@ -9,5 +10,12 @@ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
9
10
  * <div className="p-3">…long content…</div>
10
11
  * </ScrollArea>
11
12
  */
12
- export declare const ScrollArea: import('react').ForwardRefExoticComponent<Omit<ScrollAreaPrimitive.ScrollAreaProps & import('react').RefAttributes<HTMLDivElement>, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
13
+ export interface ScrollAreaProps extends ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> {
14
+ /**
15
+ * Accessible name of the viewport (a focusable `group` so keyboard users
16
+ * can scroll it). Defaults to `strings.scrollArea.region`.
17
+ */
18
+ viewportLabel?: string;
19
+ }
20
+ export declare const ScrollArea: import('react').ForwardRefExoticComponent<ScrollAreaProps & import('react').RefAttributes<HTMLDivElement>>;
13
21
  export declare const ScrollBar: import('react').ForwardRefExoticComponent<Omit<ScrollAreaPrimitive.ScrollAreaScrollbarProps & import('react').RefAttributes<HTMLDivElement>, "ref"> & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,60 @@
1
+ "use client";
2
+ import { jsxs as d, jsx as l } from "react/jsx-runtime";
3
+ import { forwardRef as s } from "react";
4
+ import * as o from "@radix-ui/react-scroll-area";
5
+ import { cn as n } from "../../lib/utils.js";
6
+ import { useStrings as m } from "../../hooks/use-strings.js";
7
+ const p = s(
8
+ function({ className: e, children: r, viewportLabel: a, ...i }, f) {
9
+ const u = m();
10
+ return /* @__PURE__ */ d(
11
+ o.Root,
12
+ {
13
+ ref: f,
14
+ className: n("relative overflow-hidden", e),
15
+ ...i,
16
+ children: [
17
+ /* @__PURE__ */ l(
18
+ o.Viewport,
19
+ {
20
+ role: "group",
21
+ "aria-label": a ?? u.scrollArea.region,
22
+ tabIndex: 0,
23
+ className: n(
24
+ "h-full w-full rounded-[inherit]",
25
+ "focus-visible:ring-ring focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2"
26
+ ),
27
+ children: r
28
+ }
29
+ ),
30
+ /* @__PURE__ */ l(t, {}),
31
+ /* @__PURE__ */ l(o.Corner, {})
32
+ ]
33
+ }
34
+ );
35
+ }
36
+ );
37
+ p.displayName = "ScrollArea";
38
+ const t = s(function({ className: e, orientation: r = "vertical", ...a }, i) {
39
+ return /* @__PURE__ */ l(
40
+ o.ScrollAreaScrollbar,
41
+ {
42
+ ref: i,
43
+ orientation: r,
44
+ className: n(
45
+ "flex touch-none transition-colors duration-[var(--duration-fast)] select-none",
46
+ r === "vertical" && "h-full w-2 border-l border-l-transparent p-px",
47
+ r === "horizontal" && "h-2 flex-col border-t border-t-transparent p-px",
48
+ e
49
+ ),
50
+ ...a,
51
+ children: /* @__PURE__ */ l(o.ScrollAreaThumb, { className: "bg-border-strong relative flex-1 rounded-full" })
52
+ }
53
+ );
54
+ });
55
+ t.displayName = "ScrollBar";
56
+ export {
57
+ p as ScrollArea,
58
+ t as ScrollBar
59
+ };
60
+ //# sourceMappingURL=ScrollArea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScrollArea.js","sources":["../../../src/components/ui/ScrollArea.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from 'react';\nimport * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\n\n/**\n * Custom scroll container with consistent styled scrollbars across OS / browsers.\n * Use when the default OS scrollbar would visually clash (sidebars, command\n * palettes, code panes). Falls back to native scroll inside the viewport.\n *\n * @example\n * <ScrollArea className=\"h-64 rounded-md border border-border\">\n * <div className=\"p-3\">…long content…</div>\n * </ScrollArea>\n */\nexport interface ScrollAreaProps extends ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> {\n /**\n * Accessible name of the viewport (a focusable `group` so keyboard users\n * can scroll it). Defaults to `strings.scrollArea.region`.\n */\n viewportLabel?: string;\n}\n\nexport const ScrollArea = forwardRef<ElementRef<typeof ScrollAreaPrimitive.Root>, ScrollAreaProps>(\n function ScrollArea({ className, children, viewportLabel, ...props }, ref) {\n const strings = useStrings();\n return (\n <ScrollAreaPrimitive.Root\n ref={ref}\n className={cn('relative overflow-hidden', className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n role=\"group\"\n aria-label={viewportLabel ?? strings.scrollArea.region}\n tabIndex={0}\n className={cn(\n 'h-full w-full rounded-[inherit]',\n 'focus-visible:ring-ring focus-visible:ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-offset-2',\n )}\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n );\n },\n);\nScrollArea.displayName = 'ScrollArea';\n\nexport const ScrollBar = forwardRef<\n ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,\n ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>\n>(function ScrollBar({ className, orientation = 'vertical', ...props }, ref) {\n return (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n ref={ref}\n orientation={orientation}\n className={cn(\n 'flex touch-none transition-colors duration-[var(--duration-fast)] select-none',\n orientation === 'vertical' && 'h-full w-2 border-l border-l-transparent p-px',\n orientation === 'horizontal' && 'h-2 flex-col border-t border-t-transparent p-px',\n className,\n )}\n {...props}\n >\n <ScrollAreaPrimitive.ScrollAreaThumb className=\"bg-border-strong relative flex-1 rounded-full\" />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n );\n});\nScrollBar.displayName = 'ScrollBar';\n"],"names":["ScrollArea","forwardRef","className","children","viewportLabel","props","ref","strings","useStrings","jsxs","ScrollAreaPrimitive","cn","jsx","ScrollBar","orientation"],"mappings":";;;;;;AAyBO,MAAMA,IAAaC;AAAA,EACxB,SAAoB,EAAE,WAAAC,GAAW,UAAAC,GAAU,eAAAC,GAAe,GAAGC,EAAA,GAASC,GAAK;AACzE,UAAMC,IAAUC,EAAA;AAChB,WACE,gBAAAC;AAAA,MAACC,EAAoB;AAAA,MAApB;AAAA,QACC,KAAAJ;AAAA,QACA,WAAWK,EAAG,4BAA4BT,CAAS;AAAA,QAClD,GAAGG;AAAA,QAEJ,UAAA;AAAA,UAAA,gBAAAO;AAAA,YAACF,EAAoB;AAAA,YAApB;AAAA,cACC,MAAK;AAAA,cACL,cAAYN,KAAiBG,EAAQ,WAAW;AAAA,cAChD,UAAU;AAAA,cACV,WAAWI;AAAA,gBACT;AAAA,gBACA;AAAA,cAAA;AAAA,cAGD,UAAAR;AAAA,YAAA;AAAA,UAAA;AAAA,4BAEFU,GAAA,EAAU;AAAA,UACX,gBAAAD,EAACF,EAAoB,QAApB,CAAA,CAA2B;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGlC;AACF;AACAV,EAAW,cAAc;AAElB,MAAMa,IAAYZ,EAGvB,SAAmB,EAAE,WAAAC,GAAW,aAAAY,IAAc,YAAY,GAAGT,EAAA,GAASC,GAAK;AAC3E,SACE,gBAAAM;AAAA,IAACF,EAAoB;AAAA,IAApB;AAAA,MACC,KAAAJ;AAAA,MACA,aAAAQ;AAAA,MACA,WAAWH;AAAA,QACT;AAAA,QACAG,MAAgB,cAAc;AAAA,QAC9BA,MAAgB,gBAAgB;AAAA,QAChCZ;AAAA,MAAA;AAAA,MAED,GAAGG;AAAA,MAEJ,UAAA,gBAAAO,EAACF,EAAoB,iBAApB,EAAoC,WAAU,gDAAA,CAAgD;AAAA,IAAA;AAAA,EAAA;AAGrG,CAAC;AACDG,EAAU,cAAc;"}