@uisatin/ui 0.0.0-stage → 0.1.1

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 (334) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -2
  3. package/chunk-2HKBZFIK.js +339 -0
  4. package/chunk-2HKBZFIK.js.map +1 -0
  5. package/chunk-3DINAQLL.js +153 -0
  6. package/chunk-3DINAQLL.js.map +1 -0
  7. package/chunk-3ERGAHKA.js +179 -0
  8. package/chunk-3ERGAHKA.js.map +1 -0
  9. package/chunk-6C7CGJX2.js +86 -0
  10. package/chunk-6C7CGJX2.js.map +1 -0
  11. package/chunk-6QMSWITQ.js +140 -0
  12. package/chunk-6QMSWITQ.js.map +1 -0
  13. package/chunk-6YXUBP27.js +474 -0
  14. package/chunk-6YXUBP27.js.map +1 -0
  15. package/chunk-72OCFAPT.js +175 -0
  16. package/chunk-72OCFAPT.js.map +1 -0
  17. package/chunk-7TXHCRAG.js +51 -0
  18. package/chunk-7TXHCRAG.js.map +1 -0
  19. package/chunk-A5XEM3GF.js +260 -0
  20. package/chunk-A5XEM3GF.js.map +1 -0
  21. package/chunk-AN2RNJLR.js +79 -0
  22. package/chunk-AN2RNJLR.js.map +1 -0
  23. package/chunk-B2K45IUR.js +36 -0
  24. package/chunk-B2K45IUR.js.map +1 -0
  25. package/chunk-B6V4VSGH.js +82 -0
  26. package/chunk-B6V4VSGH.js.map +1 -0
  27. package/chunk-BKZBWPUH.js +59 -0
  28. package/chunk-BKZBWPUH.js.map +1 -0
  29. package/chunk-BNTPPZCK.js +220 -0
  30. package/chunk-BNTPPZCK.js.map +1 -0
  31. package/chunk-BNZVTLHV.js +98 -0
  32. package/chunk-BNZVTLHV.js.map +1 -0
  33. package/chunk-CL2II6BX.js +89 -0
  34. package/chunk-CL2II6BX.js.map +1 -0
  35. package/chunk-D7RZNTFC.js +95 -0
  36. package/chunk-D7RZNTFC.js.map +1 -0
  37. package/chunk-DETGJAIC.js +147 -0
  38. package/chunk-DETGJAIC.js.map +1 -0
  39. package/chunk-DO3TBNSC.js +130 -0
  40. package/chunk-DO3TBNSC.js.map +1 -0
  41. package/chunk-ESC5JCVQ.js +90 -0
  42. package/chunk-ESC5JCVQ.js.map +1 -0
  43. package/chunk-FDKIX4DC.js +55 -0
  44. package/chunk-FDKIX4DC.js.map +1 -0
  45. package/chunk-GICKPMJU.js +239 -0
  46. package/chunk-GICKPMJU.js.map +1 -0
  47. package/chunk-GQA24FYG.js +57 -0
  48. package/chunk-GQA24FYG.js.map +1 -0
  49. package/chunk-H4NU7NPO.js +264 -0
  50. package/chunk-H4NU7NPO.js.map +1 -0
  51. package/chunk-H63FYCMY.js +11 -0
  52. package/chunk-H63FYCMY.js.map +1 -0
  53. package/chunk-HL42K2NH.js +76 -0
  54. package/chunk-HL42K2NH.js.map +1 -0
  55. package/chunk-HNQUX43Z.js +109 -0
  56. package/chunk-HNQUX43Z.js.map +1 -0
  57. package/chunk-HRKPV66B.js +32 -0
  58. package/chunk-HRKPV66B.js.map +1 -0
  59. package/chunk-J3NSDRVH.js +83 -0
  60. package/chunk-J3NSDRVH.js.map +1 -0
  61. package/chunk-JPNWHWAQ.js +151 -0
  62. package/chunk-JPNWHWAQ.js.map +1 -0
  63. package/chunk-KNUWZNFV.js +240 -0
  64. package/chunk-KNUWZNFV.js.map +1 -0
  65. package/chunk-L6NCCSIP.js +213 -0
  66. package/chunk-L6NCCSIP.js.map +1 -0
  67. package/chunk-LLHTVIM3.js +50 -0
  68. package/chunk-LLHTVIM3.js.map +1 -0
  69. package/chunk-LW3TRU5W.js +131 -0
  70. package/chunk-LW3TRU5W.js.map +1 -0
  71. package/chunk-LZMKSFCF.js +79 -0
  72. package/chunk-LZMKSFCF.js.map +1 -0
  73. package/chunk-MR3JDI7E.js +122 -0
  74. package/chunk-MR3JDI7E.js.map +1 -0
  75. package/chunk-NN6PKJWS.js +44 -0
  76. package/chunk-NN6PKJWS.js.map +1 -0
  77. package/chunk-NSRTTYQ4.js +252 -0
  78. package/chunk-NSRTTYQ4.js.map +1 -0
  79. package/chunk-O7F3CIDV.js +45 -0
  80. package/chunk-O7F3CIDV.js.map +1 -0
  81. package/chunk-P4O6TGDR.js +166 -0
  82. package/chunk-P4O6TGDR.js.map +1 -0
  83. package/chunk-PF3YBUKT.js +81 -0
  84. package/chunk-PF3YBUKT.js.map +1 -0
  85. package/chunk-PVOLDO4G.js +47 -0
  86. package/chunk-PVOLDO4G.js.map +1 -0
  87. package/chunk-QS4N4MNP.js +215 -0
  88. package/chunk-QS4N4MNP.js.map +1 -0
  89. package/chunk-QUF2IPOZ.js +98 -0
  90. package/chunk-QUF2IPOZ.js.map +1 -0
  91. package/chunk-RTJ76X5U.js +126 -0
  92. package/chunk-RTJ76X5U.js.map +1 -0
  93. package/chunk-SH6NG6VN.js +40 -0
  94. package/chunk-SH6NG6VN.js.map +1 -0
  95. package/chunk-SHDFUXYV.js +195 -0
  96. package/chunk-SHDFUXYV.js.map +1 -0
  97. package/chunk-TKO44NDI.js +84 -0
  98. package/chunk-TKO44NDI.js.map +1 -0
  99. package/chunk-TVLGZGYN.js +59 -0
  100. package/chunk-TVLGZGYN.js.map +1 -0
  101. package/chunk-TX3H3QMA.js +520 -0
  102. package/chunk-TX3H3QMA.js.map +1 -0
  103. package/chunk-UBNHSZSE.js +88 -0
  104. package/chunk-UBNHSZSE.js.map +1 -0
  105. package/chunk-UC3ZJWOD.js +73 -0
  106. package/chunk-UC3ZJWOD.js.map +1 -0
  107. package/chunk-UGRSWP6U.js +69 -0
  108. package/chunk-UGRSWP6U.js.map +1 -0
  109. package/chunk-VAGIAREV.js +214 -0
  110. package/chunk-VAGIAREV.js.map +1 -0
  111. package/chunk-WTMZT3IT.js +105 -0
  112. package/chunk-WTMZT3IT.js.map +1 -0
  113. package/chunk-WX6BP5ZP.js +39 -0
  114. package/chunk-WX6BP5ZP.js.map +1 -0
  115. package/chunk-X3PQANZO.js +255 -0
  116. package/chunk-X3PQANZO.js.map +1 -0
  117. package/chunk-XILQ7GXE.js +66 -0
  118. package/chunk-XILQ7GXE.js.map +1 -0
  119. package/chunk-XL75JRUA.js +130 -0
  120. package/chunk-XL75JRUA.js.map +1 -0
  121. package/chunk-XZNHT6MO.js +348 -0
  122. package/chunk-XZNHT6MO.js.map +1 -0
  123. package/chunk-Y5AYU7PK.js +44 -0
  124. package/chunk-Y5AYU7PK.js.map +1 -0
  125. package/chunk-YG7JOOLJ.js +28 -0
  126. package/chunk-YG7JOOLJ.js.map +1 -0
  127. package/chunk-YPJYW624.js +195 -0
  128. package/chunk-YPJYW624.js.map +1 -0
  129. package/chunk-YPNKBE5X.js +104 -0
  130. package/chunk-YPNKBE5X.js.map +1 -0
  131. package/chunk-YX5VNRJX.js +24 -0
  132. package/chunk-YX5VNRJX.js.map +1 -0
  133. package/components/accordion.d.ts +27 -0
  134. package/components/accordion.js +4 -0
  135. package/components/accordion.js.map +1 -0
  136. package/components/agent-loading.d.ts +65 -0
  137. package/components/agent-loading.js +4 -0
  138. package/components/agent-loading.js.map +1 -0
  139. package/components/agent-picker.d.ts +82 -0
  140. package/components/agent-picker.js +5 -0
  141. package/components/agent-picker.js.map +1 -0
  142. package/components/agent-suggestion.d.ts +69 -0
  143. package/components/agent-suggestion.js +6 -0
  144. package/components/agent-suggestion.js.map +1 -0
  145. package/components/announcement-bar.d.ts +39 -0
  146. package/components/announcement-bar.js +7 -0
  147. package/components/announcement-bar.js.map +1 -0
  148. package/components/attachment.d.ts +73 -0
  149. package/components/attachment.js +5 -0
  150. package/components/attachment.js.map +1 -0
  151. package/components/avatar.d.ts +34 -0
  152. package/components/avatar.js +6 -0
  153. package/components/avatar.js.map +1 -0
  154. package/components/badge.d.ts +19 -0
  155. package/components/badge.js +4 -0
  156. package/components/badge.js.map +1 -0
  157. package/components/banner-mesh.d.ts +11 -0
  158. package/components/banner-mesh.js +3 -0
  159. package/components/banner-mesh.js.map +1 -0
  160. package/components/banner.d.ts +50 -0
  161. package/components/banner.js +6 -0
  162. package/components/banner.js.map +1 -0
  163. package/components/bar-chart.d.ts +32 -0
  164. package/components/bar-chart.js +11 -0
  165. package/components/bar-chart.js.map +1 -0
  166. package/components/breadcrumb.d.ts +32 -0
  167. package/components/breadcrumb.js +6 -0
  168. package/components/breadcrumb.js.map +1 -0
  169. package/components/button.d.ts +32 -0
  170. package/components/button.js +4 -0
  171. package/components/button.js.map +1 -0
  172. package/components/card.d.ts +28 -0
  173. package/components/card.js +5 -0
  174. package/components/card.js.map +1 -0
  175. package/components/channel-chart.d.ts +14 -0
  176. package/components/channel-chart.js +11 -0
  177. package/components/channel-chart.js.map +1 -0
  178. package/components/chart.d.ts +88 -0
  179. package/components/chart.js +10 -0
  180. package/components/chart.js.map +1 -0
  181. package/components/checkbox.d.ts +17 -0
  182. package/components/checkbox.js +4 -0
  183. package/components/checkbox.js.map +1 -0
  184. package/components/chip.d.ts +54 -0
  185. package/components/chip.js +4 -0
  186. package/components/chip.js.map +1 -0
  187. package/components/citations.d.ts +37 -0
  188. package/components/citations.js +5 -0
  189. package/components/citations.js.map +1 -0
  190. package/components/code-block.d.ts +13 -0
  191. package/components/code-block.js +5 -0
  192. package/components/code-block.js.map +1 -0
  193. package/components/combobox.d.ts +58 -0
  194. package/components/combobox.js +10 -0
  195. package/components/combobox.js.map +1 -0
  196. package/components/command-menu.d.ts +54 -0
  197. package/components/command-menu.js +6 -0
  198. package/components/command-menu.js.map +1 -0
  199. package/components/context-menu.d.ts +46 -0
  200. package/components/context-menu.js +6 -0
  201. package/components/context-menu.js.map +1 -0
  202. package/components/cookie-consent.d.ts +26 -0
  203. package/components/cookie-consent.js +5 -0
  204. package/components/cookie-consent.js.map +1 -0
  205. package/components/date-picker.d.ts +112 -0
  206. package/components/date-picker.js +9 -0
  207. package/components/date-picker.js.map +1 -0
  208. package/components/decision-card.d.ts +65 -0
  209. package/components/decision-card.js +7 -0
  210. package/components/decision-card.js.map +1 -0
  211. package/components/dialog.d.ts +68 -0
  212. package/components/dialog.js +5 -0
  213. package/components/dialog.js.map +1 -0
  214. package/components/donut-chart.d.ts +20 -0
  215. package/components/donut-chart.js +11 -0
  216. package/components/donut-chart.js.map +1 -0
  217. package/components/dot.d.ts +21 -0
  218. package/components/dot.js +4 -0
  219. package/components/dot.js.map +1 -0
  220. package/components/dropdown.d.ts +74 -0
  221. package/components/dropdown.js +5 -0
  222. package/components/dropdown.js.map +1 -0
  223. package/components/empty-state.d.ts +25 -0
  224. package/components/empty-state.js +4 -0
  225. package/components/empty-state.js.map +1 -0
  226. package/components/file-upload.d.ts +78 -0
  227. package/components/file-upload.js +7 -0
  228. package/components/file-upload.js.map +1 -0
  229. package/components/floating.d.ts +30 -0
  230. package/components/floating.js +3 -0
  231. package/components/floating.js.map +1 -0
  232. package/components/form-field.d.ts +35 -0
  233. package/components/form-field.js +4 -0
  234. package/components/form-field.js.map +1 -0
  235. package/components/hover-card.d.ts +30 -0
  236. package/components/hover-card.js +4 -0
  237. package/components/hover-card.js.map +1 -0
  238. package/components/input-otp.d.ts +49 -0
  239. package/components/input-otp.js +8 -0
  240. package/components/input-otp.js.map +1 -0
  241. package/components/input.d.ts +28 -0
  242. package/components/input.js +5 -0
  243. package/components/input.js.map +1 -0
  244. package/components/kbd.d.ts +37 -0
  245. package/components/kbd.js +4 -0
  246. package/components/kbd.js.map +1 -0
  247. package/components/line-chart.d.ts +29 -0
  248. package/components/line-chart.js +12 -0
  249. package/components/line-chart.js.map +1 -0
  250. package/components/message-actions.d.ts +24 -0
  251. package/components/message-actions.js +5 -0
  252. package/components/message-actions.js.map +1 -0
  253. package/components/message.d.ts +40 -0
  254. package/components/message.js +8 -0
  255. package/components/message.js.map +1 -0
  256. package/components/number-input.d.ts +33 -0
  257. package/components/number-input.js +8 -0
  258. package/components/number-input.js.map +1 -0
  259. package/components/pagination.d.ts +45 -0
  260. package/components/pagination.js +6 -0
  261. package/components/pagination.js.map +1 -0
  262. package/components/popover.d.ts +44 -0
  263. package/components/popover.js +4 -0
  264. package/components/popover.js.map +1 -0
  265. package/components/progress.d.ts +24 -0
  266. package/components/progress.js +4 -0
  267. package/components/progress.js.map +1 -0
  268. package/components/prompt-bar.d.ts +63 -0
  269. package/components/prompt-bar.js +6 -0
  270. package/components/prompt-bar.js.map +1 -0
  271. package/components/radio.d.ts +27 -0
  272. package/components/radio.js +4 -0
  273. package/components/radio.js.map +1 -0
  274. package/components/search-bar.d.ts +20 -0
  275. package/components/search-bar.js +5 -0
  276. package/components/search-bar.js.map +1 -0
  277. package/components/segmented-control.d.ts +32 -0
  278. package/components/segmented-control.js +5 -0
  279. package/components/segmented-control.js.map +1 -0
  280. package/components/select.d.ts +97 -0
  281. package/components/select.js +7 -0
  282. package/components/select.js.map +1 -0
  283. package/components/sheet.d.ts +37 -0
  284. package/components/sheet.js +6 -0
  285. package/components/sheet.js.map +1 -0
  286. package/components/sidebar.d.ts +52 -0
  287. package/components/sidebar.js +4 -0
  288. package/components/sidebar.js.map +1 -0
  289. package/components/skeleton.d.ts +18 -0
  290. package/components/skeleton.js +4 -0
  291. package/components/skeleton.js.map +1 -0
  292. package/components/slider.d.ts +57 -0
  293. package/components/slider.js +8 -0
  294. package/components/slider.js.map +1 -0
  295. package/components/sliding-indicator.d.ts +23 -0
  296. package/components/sliding-indicator.js +3 -0
  297. package/components/sliding-indicator.js.map +1 -0
  298. package/components/streaming-text.d.ts +33 -0
  299. package/components/streaming-text.js +4 -0
  300. package/components/streaming-text.js.map +1 -0
  301. package/components/switch.d.ts +15 -0
  302. package/components/switch.js +4 -0
  303. package/components/switch.js.map +1 -0
  304. package/components/table.d.ts +57 -0
  305. package/components/table.js +5 -0
  306. package/components/table.js.map +1 -0
  307. package/components/tabs.d.ts +45 -0
  308. package/components/tabs.js +5 -0
  309. package/components/tabs.js.map +1 -0
  310. package/components/textarea.d.ts +30 -0
  311. package/components/textarea.js +5 -0
  312. package/components/textarea.js.map +1 -0
  313. package/components/thinking.d.ts +64 -0
  314. package/components/thinking.js +5 -0
  315. package/components/thinking.js.map +1 -0
  316. package/components/toast.d.ts +71 -0
  317. package/components/toast.js +5 -0
  318. package/components/toast.js.map +1 -0
  319. package/components/toggle.d.ts +30 -0
  320. package/components/toggle.js +4 -0
  321. package/components/toggle.js.map +1 -0
  322. package/components/tooltip.d.ts +35 -0
  323. package/components/tooltip.js +5 -0
  324. package/components/tooltip.js.map +1 -0
  325. package/index.d.ts +64 -0
  326. package/index.js +231 -0
  327. package/index.js.map +1 -0
  328. package/lib/utils.d.ts +3 -0
  329. package/lib/utils.js +3 -0
  330. package/lib/utils.js.map +1 -0
  331. package/package.json +54 -4
  332. package/styles.css +2 -0
  333. package/theme/create-theme.d.ts +44 -0
  334. package/theme/index.d.ts +1 -0
@@ -0,0 +1,213 @@
1
+ import { Input } from './chunk-YPNKBE5X.js';
2
+ import { TooltipBubble } from './chunk-B6V4VSGH.js';
3
+ import { cn } from './chunk-H63FYCMY.js';
4
+ import * as React from 'react';
5
+ import { Slider as Slider$1 } from 'radix-ui';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ var decimals = (n) => (String(n).split(".")[1] ?? "").length;
9
+ function Slider({
10
+ value: valueProp,
11
+ defaultValue,
12
+ onValueChange,
13
+ onValueCommit,
14
+ min = 0,
15
+ max = 100,
16
+ step = 1,
17
+ minStepsBetweenThumbs = 0,
18
+ label,
19
+ showValue = true,
20
+ inputs = false,
21
+ tooltip = false,
22
+ orientation = "horizontal",
23
+ formatValue = String,
24
+ disabled = false,
25
+ name,
26
+ id,
27
+ "aria-label": ariaLabel,
28
+ className
29
+ }) {
30
+ const autoId = React.useId();
31
+ const sliderId = id ?? autoId;
32
+ const labelId = `${sliderId}-label`;
33
+ const vertical = orientation === "vertical";
34
+ const range = Array.isArray(valueProp ?? defaultValue);
35
+ const toArray = (v) => v == null ? range ? [min, max] : [min] : Array.isArray(v) ? [...v] : [v];
36
+ const toOut = (a) => range ? [a[0], a[1]] : a[0];
37
+ const [uncontrolled, setUncontrolled] = React.useState(() => toArray(defaultValue));
38
+ const values = valueProp != null ? toArray(valueProp) : uncontrolled;
39
+ const change = (next) => {
40
+ if (valueProp == null) setUncontrolled(next);
41
+ onValueChange?.(toOut(next));
42
+ };
43
+ const [dragging, setDragging] = React.useState(false);
44
+ const [keying, setKeying] = React.useState(false);
45
+ const [focused, setFocused] = React.useState(null);
46
+ const keyTimer = React.useRef(void 0);
47
+ React.useEffect(() => {
48
+ if (!dragging) return;
49
+ const end = () => setDragging(false);
50
+ window.addEventListener("pointerup", end);
51
+ window.addEventListener("pointercancel", end);
52
+ return () => {
53
+ window.removeEventListener("pointerup", end);
54
+ window.removeEventListener("pointercancel", end);
55
+ };
56
+ }, [dragging]);
57
+ React.useEffect(() => () => clearTimeout(keyTimer.current), []);
58
+ const readout = range ? `${formatValue(values[0])} \u2013 ${formatValue(values[1])}` : formatValue(values[0]);
59
+ const thumbLabel = (i) => range ? i === 0 ? "From" : "To" : void 0;
60
+ const fieldLabel = (i) => range ? i === 0 ? "From" : "To" : "Value";
61
+ const commitTyped = (i, typed) => {
62
+ const places = decimals(step);
63
+ let v = Math.round((typed - min) / step) * step + min;
64
+ v = Number(Math.min(max, Math.max(min, v)).toFixed(places));
65
+ const gap = minStepsBetweenThumbs * step;
66
+ if (range) v = i === 0 ? Math.min(v, values[1] - gap) : Math.max(v, values[0] + gap);
67
+ const next = values.map((x, j) => j === i ? v : x);
68
+ change(next);
69
+ onValueCommit?.(toOut(next));
70
+ };
71
+ const field = (i) => /* @__PURE__ */ jsx(
72
+ SliderInput,
73
+ {
74
+ value: values[i],
75
+ disabled,
76
+ "aria-label": fieldLabel(i),
77
+ onCommit: (v) => commitTyped(i, v)
78
+ },
79
+ i
80
+ );
81
+ const slider = /* @__PURE__ */ jsxs(
82
+ Slider$1.Root,
83
+ {
84
+ id: sliderId,
85
+ "data-slot": "slider",
86
+ value: values,
87
+ onValueChange: change,
88
+ onValueCommit: (v) => onValueCommit?.(toOut(v)),
89
+ min,
90
+ max,
91
+ step,
92
+ minStepsBetweenThumbs,
93
+ orientation,
94
+ disabled,
95
+ name,
96
+ "aria-labelledby": label ? labelId : void 0,
97
+ onPointerDown: () => !disabled && setDragging(true),
98
+ className: cn(
99
+ "relative flex shrink-0 touch-none items-center select-none data-[disabled]:cursor-not-allowed",
100
+ vertical ? "h-40 w-5 flex-col" : "h-5 w-full min-w-0 flex-1"
101
+ ),
102
+ children: [
103
+ /* @__PURE__ */ jsx(
104
+ Slider$1.Track,
105
+ {
106
+ className: cn("relative grow cursor-pointer overflow-hidden rounded-full bg-bg-muted", vertical ? "w-1" : "h-1"),
107
+ children: /* @__PURE__ */ jsx(Slider$1.Range, { className: cn("absolute rounded-full bg-action-primary-bg", vertical ? "w-full" : "h-full") })
108
+ }
109
+ ),
110
+ values.map((v, i) => /* @__PURE__ */ jsx(
111
+ Slider$1.Thumb,
112
+ {
113
+ "aria-label": thumbLabel(i) ?? (label ? void 0 : ariaLabel),
114
+ "aria-labelledby": !range && label ? labelId : void 0,
115
+ "aria-valuetext": formatValue(v),
116
+ onFocus: () => setFocused(i),
117
+ onBlur: () => {
118
+ setFocused(null);
119
+ setKeying(false);
120
+ },
121
+ onKeyDown: (e) => {
122
+ if (!/^(Arrow|Page|Home|End)/.test(e.key)) return;
123
+ setKeying(true);
124
+ clearTimeout(keyTimer.current);
125
+ keyTimer.current = setTimeout(() => setKeying(false), 1e3);
126
+ },
127
+ className: cn(
128
+ "relative block size-4 cursor-grab rounded-full border border-border-strong bg-bg-popover outline-none active:cursor-grabbing",
129
+ "[box-shadow:0_1px_3px_0_var(--effect-shadow-sm)]",
130
+ "transition-[border-color,box-shadow] duration-(--duration-fast) ease-(--ease-out)",
131
+ "hover:border-text-subtle focus-visible:[box-shadow:0_0_0_2px_var(--action-focus-ring)]",
132
+ "data-[disabled]:cursor-not-allowed data-[disabled]:hover:border-border-strong"
133
+ ),
134
+ children: tooltip && /* @__PURE__ */ jsx(
135
+ TooltipBubble,
136
+ {
137
+ "aria-hidden": true,
138
+ label: formatValue(v),
139
+ "data-visible": (dragging || keying) && focused === i ? "" : void 0,
140
+ className: cn(
141
+ "pointer-events-none absolute tabular-nums opacity-0 transition-[opacity,translate] duration-(--duration-fast) ease-(--ease-out) data-[visible]:opacity-100",
142
+ vertical ? "top-1/2 left-full ml-2 -translate-x-0.5 -translate-y-1/2 data-[visible]:translate-x-0" : "bottom-full left-1/2 mb-2 -translate-x-1/2 translate-y-0.5 data-[visible]:translate-y-0"
143
+ )
144
+ }
145
+ )
146
+ },
147
+ i
148
+ ))
149
+ ]
150
+ }
151
+ );
152
+ const head = (label || showValue && !inputs) && /* @__PURE__ */ jsxs("div", { className: cn("flex h-[18px] items-center justify-between gap-3 whitespace-nowrap", vertical && "min-w-full"), children: [
153
+ label ? /* @__PURE__ */ jsx("span", { id: labelId, className: "text-sm leading-[18px] font-medium text-text-default", children: label }) : /* @__PURE__ */ jsx("span", {}),
154
+ showValue && !inputs && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-sm leading-[18px] text-text-muted tabular-nums", children: readout })
155
+ ] });
156
+ return /* @__PURE__ */ jsxs(
157
+ "div",
158
+ {
159
+ "data-slot": "slider-field",
160
+ "data-orientation": orientation,
161
+ "data-disabled": disabled || void 0,
162
+ className: cn("flex flex-col gap-2", vertical ? "w-max items-center" : "w-full", className),
163
+ children: [
164
+ head,
165
+ vertical ? /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-center gap-2", disabled && "opacity-50"), children: [
166
+ inputs && range && field(1),
167
+ slider,
168
+ inputs && field(0)
169
+ ] }) : /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-3", disabled && "opacity-50"), children: [
170
+ inputs && range && field(0),
171
+ slider,
172
+ inputs && field(range ? 1 : 0)
173
+ ] })
174
+ ]
175
+ }
176
+ );
177
+ }
178
+ function SliderInput({
179
+ value,
180
+ disabled,
181
+ onCommit,
182
+ "aria-label": ariaLabel
183
+ }) {
184
+ const [draft, setDraft] = React.useState(null);
185
+ const commit = () => {
186
+ if (draft == null) return;
187
+ const n = Number(draft.replace(",", "."));
188
+ setDraft(null);
189
+ if (draft.trim() !== "" && Number.isFinite(n) && n !== value) onCommit(n);
190
+ };
191
+ return /* @__PURE__ */ jsx(
192
+ Input,
193
+ {
194
+ size: "sm",
195
+ inputMode: "decimal",
196
+ "aria-label": ariaLabel,
197
+ disabled,
198
+ value: draft ?? String(value),
199
+ onChange: (e) => setDraft(e.target.value),
200
+ onBlur: commit,
201
+ onKeyDown: (e) => {
202
+ if (e.key === "Enter") commit();
203
+ if (e.key === "Escape") setDraft(null);
204
+ },
205
+ wrapperClassName: "w-16 shrink-0",
206
+ className: "[&_input]:text-center [&_input]:tabular-nums"
207
+ }
208
+ );
209
+ }
210
+
211
+ export { Slider };
212
+ //# sourceMappingURL=chunk-L6NCCSIP.js.map
213
+ //# sourceMappingURL=chunk-L6NCCSIP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/slider.tsx"],"names":["SliderPrimitive"],"mappings":"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","file":"chunk-L6NCCSIP.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Slider as SliderPrimitive } from 'radix-ui'\nimport { cn } from '../lib/utils'\nimport { Input } from './input'\nimport { TooltipBubble } from './tooltip'\n\n/**\n * Slider, 1:1 with the uiKit `Slider` set (4025:3621): a 4px bg/muted track with the picked\n * part in action/primary/bg, and 16px bg/popover thumbs with a border/strong ring and the\n * shadow-sm drop (0 1 3). Hover turns the ring text/subtle, keyboard focus adds a 2px focus\n * ring, disabled fades the whole slider to 50%. 20px tall, so the thumbs have room.\n *\n * One value or a range: pass a number or a `[from, to]` pair as `value` / `defaultValue`.\n *\n * Around it, all optional:\n * - `label` on the left above the track and, on the same line at the right, the picked value\n * (`showValue`, on by default, \"40\" or \"20 – 80\", through `formatValue`).\n * - `inputs`: small number fields beside the track to type a value: the range's from on the\n * left and to on the right, a single value on the right. They take the readout's place.\n * A typed value is clamped, snapped to `step` and kept in order on Enter or blur; Escape\n * puts the current value back.\n * - `tooltip`: the value in a Tooltip bubble over the thumb while it is dragged or moved with\n * the keys.\n * - `orientation=\"vertical\"`: the track runs bottom to top (160px unless styled), the inputs\n * go above (to) and below (from or the single value).\n *\n * Built on the Radix Slider: arrow keys, Page Up/Down, Home/End, a native form value (`name`)\n * and a `slider` role per thumb, with `formatValue` as its spoken value.\n */\nexport interface SliderProps {\n /** A number, or a `[from, to]` pair for a range. */\n value?: number | [number, number]\n defaultValue?: number | [number, number]\n /** Every change while it moves. Gets a number or a pair, as given. */\n onValueChange?: (value: number | [number, number]) => void\n /** Once, when a drag ends or a key or a typed value changes it. */\n onValueCommit?: (value: number | [number, number]) => void\n min?: number\n max?: number\n step?: number\n /** Smallest gap between the range's thumbs, in steps. */\n minStepsBetweenThumbs?: number\n label?: React.ReactNode\n /** The picked value at the right of the label's line. Hidden while `inputs` are on. */\n showValue?: boolean\n /** Small number fields beside the track to type the value. */\n inputs?: boolean\n /** The value in a bubble over the thumb while it moves. */\n tooltip?: boolean\n orientation?: 'horizontal' | 'vertical'\n /** How a value reads in the readout, the bubble, the fields' neighbours and to screen readers. */\n formatValue?: (value: number) => string\n disabled?: boolean\n /** Form field name, for a native form submit. */\n name?: string\n id?: string\n /** Names the slider when there is no `label`. */\n 'aria-label'?: string\n /** Classes for the outer wrapper. */\n className?: string\n}\n\nconst decimals = (n: number) => (String(n).split('.')[1] ?? '').length\n\nexport function Slider({\n value: valueProp,\n defaultValue,\n onValueChange,\n onValueCommit,\n min = 0,\n max = 100,\n step = 1,\n minStepsBetweenThumbs = 0,\n label,\n showValue = true,\n inputs = false,\n tooltip = false,\n orientation = 'horizontal',\n formatValue = String,\n disabled = false,\n name,\n id,\n 'aria-label': ariaLabel,\n className,\n}: SliderProps) {\n const autoId = React.useId()\n const sliderId = id ?? autoId\n const labelId = `${sliderId}-label`\n const vertical = orientation === 'vertical'\n\n const range = Array.isArray(valueProp ?? defaultValue)\n const toArray = (v: number | [number, number] | undefined) => (v == null ? (range ? [min, max] : [min]) : Array.isArray(v) ? [...v] : [v])\n const toOut = (a: number[]) => (range ? ([a[0], a[1]] as [number, number]) : a[0])\n\n const [uncontrolled, setUncontrolled] = React.useState(() => toArray(defaultValue))\n const values = valueProp != null ? toArray(valueProp) : uncontrolled\n const change = (next: number[]) => {\n if (valueProp == null) setUncontrolled(next)\n onValueChange?.(toOut(next))\n }\n\n // The bubble shows on the thumb that moves: while a pointer drags it, or for a moment after a key.\n const [dragging, setDragging] = React.useState(false)\n const [keying, setKeying] = React.useState(false)\n const [focused, setFocused] = React.useState<number | null>(null)\n const keyTimer = React.useRef<ReturnType<typeof setTimeout>>(undefined)\n React.useEffect(() => {\n if (!dragging) return\n const end = () => setDragging(false)\n window.addEventListener('pointerup', end)\n window.addEventListener('pointercancel', end)\n return () => {\n window.removeEventListener('pointerup', end)\n window.removeEventListener('pointercancel', end)\n }\n }, [dragging])\n React.useEffect(() => () => clearTimeout(keyTimer.current), [])\n\n const readout = range ? `${formatValue(values[0])} – ${formatValue(values[1])}` : formatValue(values[0])\n const thumbLabel = (i: number) => (range ? (i === 0 ? 'From' : 'To') : undefined)\n const fieldLabel = (i: number) => (range ? (i === 0 ? 'From' : 'To') : 'Value')\n\n // A typed value: clamped, snapped to the step and kept on its side of the other thumb.\n const commitTyped = (i: number, typed: number) => {\n const places = decimals(step)\n let v = Math.round((typed - min) / step) * step + min\n v = Number(Math.min(max, Math.max(min, v)).toFixed(places))\n const gap = minStepsBetweenThumbs * step\n if (range) v = i === 0 ? Math.min(v, values[1] - gap) : Math.max(v, values[0] + gap)\n const next = values.map((x, j) => (j === i ? v : x))\n change(next)\n onValueCommit?.(toOut(next))\n }\n\n const field = (i: number) => (\n <SliderInput\n key={i}\n value={values[i]}\n disabled={disabled}\n aria-label={fieldLabel(i)}\n onCommit={(v) => commitTyped(i, v)}\n />\n )\n\n const slider = (\n <SliderPrimitive.Root\n id={sliderId}\n data-slot=\"slider\"\n value={values}\n onValueChange={change}\n onValueCommit={(v) => onValueCommit?.(toOut(v))}\n min={min}\n max={max}\n step={step}\n minStepsBetweenThumbs={minStepsBetweenThumbs}\n orientation={orientation}\n disabled={disabled}\n name={name}\n aria-labelledby={label ? labelId : undefined}\n onPointerDown={() => !disabled && setDragging(true)}\n className={cn(\n 'relative flex shrink-0 touch-none items-center select-none data-[disabled]:cursor-not-allowed',\n vertical ? 'h-40 w-5 flex-col' : 'h-5 w-full min-w-0 flex-1',\n )}\n >\n <SliderPrimitive.Track\n className={cn('relative grow cursor-pointer overflow-hidden rounded-full bg-bg-muted', vertical ? 'w-1' : 'h-1')}\n >\n <SliderPrimitive.Range className={cn('absolute rounded-full bg-action-primary-bg', vertical ? 'w-full' : 'h-full')} />\n </SliderPrimitive.Track>\n {values.map((v, i) => (\n <SliderPrimitive.Thumb\n key={i}\n aria-label={thumbLabel(i) ?? (label ? undefined : ariaLabel)}\n aria-labelledby={!range && label ? labelId : undefined}\n aria-valuetext={formatValue(v)}\n onFocus={() => setFocused(i)}\n onBlur={() => {\n setFocused(null)\n setKeying(false)\n }}\n onKeyDown={(e) => {\n if (!/^(Arrow|Page|Home|End)/.test(e.key)) return\n setKeying(true)\n clearTimeout(keyTimer.current)\n keyTimer.current = setTimeout(() => setKeying(false), 1000)\n }}\n className={cn(\n 'relative block size-4 cursor-grab rounded-full border border-border-strong bg-bg-popover outline-none active:cursor-grabbing',\n '[box-shadow:0_1px_3px_0_var(--effect-shadow-sm)]',\n 'transition-[border-color,box-shadow] duration-(--duration-fast) ease-(--ease-out)',\n 'hover:border-text-subtle focus-visible:[box-shadow:0_0_0_2px_var(--action-focus-ring)]',\n 'data-[disabled]:cursor-not-allowed data-[disabled]:hover:border-border-strong',\n )}\n >\n {tooltip && (\n <TooltipBubble\n aria-hidden\n label={formatValue(v)}\n data-visible={(dragging || keying) && focused === i ? '' : undefined}\n className={cn(\n 'pointer-events-none absolute tabular-nums opacity-0 transition-[opacity,translate] duration-(--duration-fast) ease-(--ease-out) data-[visible]:opacity-100',\n vertical\n ? 'top-1/2 left-full ml-2 -translate-x-0.5 -translate-y-1/2 data-[visible]:translate-x-0'\n : 'bottom-full left-1/2 mb-2 -translate-x-1/2 translate-y-0.5 data-[visible]:translate-y-0',\n )}\n />\n )}\n </SliderPrimitive.Thumb>\n ))}\n </SliderPrimitive.Root>\n )\n\n const head = (label || (showValue && !inputs)) && (\n <div className={cn('flex h-[18px] items-center justify-between gap-3 whitespace-nowrap', vertical && 'min-w-full')}>\n {label ? (\n <span id={labelId} className=\"text-sm leading-[18px] font-medium text-text-default\">\n {label}\n </span>\n ) : (\n <span />\n )}\n {showValue && !inputs && (\n <span aria-hidden className=\"text-sm leading-[18px] text-text-muted tabular-nums\">\n {readout}\n </span>\n )}\n </div>\n )\n\n return (\n <div\n data-slot=\"slider-field\"\n data-orientation={orientation}\n data-disabled={disabled || undefined}\n className={cn('flex flex-col gap-2', vertical ? 'w-max items-center' : 'w-full', className)}\n >\n {head}\n {vertical ? (\n <div className={cn('flex flex-col items-center gap-2', disabled && 'opacity-50')}>\n {inputs && range && field(1)}\n {slider}\n {inputs && field(0)}\n </div>\n ) : (\n <div className={cn('flex items-center gap-3', disabled && 'opacity-50')}>\n {inputs && range && field(0)}\n {slider}\n {inputs && field(range ? 1 : 0)}\n </div>\n )}\n </div>\n )\n}\n\n/** A small Input for one value: free typing, then committed on Enter or blur; Escape undoes. */\nfunction SliderInput({\n value,\n disabled,\n onCommit,\n 'aria-label': ariaLabel,\n}: {\n value: number\n disabled?: boolean\n onCommit: (value: number) => void\n 'aria-label': string\n}) {\n const [draft, setDraft] = React.useState<string | null>(null)\n const commit = () => {\n if (draft == null) return\n const n = Number(draft.replace(',', '.'))\n setDraft(null)\n if (draft.trim() !== '' && Number.isFinite(n) && n !== value) onCommit(n)\n }\n return (\n <Input\n size=\"sm\"\n inputMode=\"decimal\"\n aria-label={ariaLabel}\n disabled={disabled}\n value={draft ?? String(value)}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={commit}\n onKeyDown={(e) => {\n if (e.key === 'Enter') commit()\n if (e.key === 'Escape') setDraft(null)\n }}\n wrapperClassName=\"w-16 shrink-0\"\n className=\"[&_input]:text-center [&_input]:tabular-nums\"\n />\n )\n}\n"]}
@@ -0,0 +1,50 @@
1
+ import { cn } from './chunk-H63FYCMY.js';
2
+ import * as React from 'react';
3
+ import { RadioGroup as RadioGroup$1 } from 'radix-ui';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ function RadioGroup({ className, ...props }) {
7
+ return /* @__PURE__ */ jsx(RadioGroup$1.Root, { "data-slot": "radio-group", className: cn("grid gap-3", className), ...props });
8
+ }
9
+ function Radio({ className, label, description, placement = "left", id, disabled, ...props }) {
10
+ const autoId = React.useId();
11
+ const radioId = id ?? autoId;
12
+ const labelId = label ? `${radioId}-label` : void 0;
13
+ const descriptionId = description ? `${radioId}-description` : void 0;
14
+ const circle = /* @__PURE__ */ jsx(
15
+ RadioGroup$1.Item,
16
+ {
17
+ id: radioId,
18
+ "data-slot": "radio",
19
+ "aria-labelledby": labelId,
20
+ "aria-describedby": descriptionId,
21
+ disabled,
22
+ className: cn("peer satin-radio", className),
23
+ ...props,
24
+ children: /* @__PURE__ */ jsx(RadioGroup$1.Indicator, { className: "satin-radio-dot" })
25
+ }
26
+ );
27
+ if (!label && !description) return circle;
28
+ return (
29
+ // The whole field is the <label>: the control, its label and its description all toggle it.
30
+ /* @__PURE__ */ jsxs(
31
+ "label",
32
+ {
33
+ htmlFor: radioId,
34
+ "data-slot": "radio-field",
35
+ className: cn("flex cursor-pointer items-start gap-2 has-disabled:cursor-not-allowed has-data-disabled:cursor-not-allowed [&>:first-child]:mt-0.5", placement === "right" && "flex-row-reverse justify-between"),
36
+ children: [
37
+ circle,
38
+ /* @__PURE__ */ jsxs("div", { className: "grid min-w-0 gap-0.5 text-base text-text-default peer-data-disabled:text-text-subtle peer-data-disabled:*:text-text-subtle", children: [
39
+ label && /* @__PURE__ */ jsx("span", { id: labelId, className: "block leading-5 font-medium", children: label }),
40
+ description && /* @__PURE__ */ jsx("span", { id: descriptionId, className: "block leading-5 text-text-muted", children: description })
41
+ ] })
42
+ ]
43
+ }
44
+ )
45
+ );
46
+ }
47
+
48
+ export { Radio, RadioGroup };
49
+ //# sourceMappingURL=chunk-LLHTVIM3.js.map
50
+ //# sourceMappingURL=chunk-LLHTVIM3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/radio.tsx"],"names":["RadioGroupPrimitive"],"mappings":";;;;;AAqBO,SAAS,UAAA,CAAW,EAAE,SAAA,EAAW,GAAG,OAAM,EAAoB;AACnE,EAAA,uBAAO,GAAA,CAACA,YAAA,CAAoB,IAAA,EAApB,EAAyB,WAAA,EAAU,aAAA,EAAc,SAAA,EAAW,EAAA,CAAG,YAAA,EAAc,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AAC9G;AAWO,SAAS,KAAA,CAAM,EAAE,SAAA,EAAW,KAAA,EAAO,WAAA,EAAa,SAAA,GAAY,MAAA,EAAQ,EAAA,EAAI,QAAA,EAAU,GAAG,KAAA,EAAM,EAAe;AAC/G,EAAA,MAAM,SAAe,KAAA,CAAA,KAAA,EAAM;AAC3B,EAAA,MAAM,UAAU,EAAA,IAAM,MAAA;AACtB,EAAA,MAAM,OAAA,GAAU,KAAA,GAAQ,CAAA,EAAG,OAAO,CAAA,MAAA,CAAA,GAAW,MAAA;AAC7C,EAAA,MAAM,aAAA,GAAgB,WAAA,GAAc,CAAA,EAAG,OAAO,CAAA,YAAA,CAAA,GAAiB,MAAA;AAE/D,EAAA,MAAM,MAAA,mBACJ,GAAA;AAAA,IAACA,YAAA,CAAoB,IAAA;AAAA,IAApB;AAAA,MACC,EAAA,EAAI,OAAA;AAAA,MACJ,WAAA,EAAU,OAAA;AAAA,MACV,iBAAA,EAAiB,OAAA;AAAA,MACjB,kBAAA,EAAkB,aAAA;AAAA,MAClB,QAAA;AAAA,MACA,SAAA,EAAW,EAAA,CAAG,kBAAA,EAAoB,SAAS,CAAA;AAAA,MAC1C,GAAG,KAAA;AAAA,MAEJ,QAAA,kBAAA,GAAA,CAACA,YAAA,CAAoB,SAAA,EAApB,EAA8B,WAAU,iBAAA,EAAkB;AAAA;AAAA,GAC7D;AAGF,EAAA,IAAI,CAAC,KAAA,IAAS,CAAC,WAAA,EAAa,OAAO,MAAA;AAEnC,EAAA;AAAA;AAAA,oBAEE,IAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAS,OAAA;AAAA,QACT,WAAA,EAAU,aAAA;AAAA,QAEV,SAAA,EAAW,EAAA,CAAG,oIAAA,EAAsI,SAAA,KAAc,WAAW,kCAAkC,CAAA;AAAA,QAE9M,QAAA,EAAA;AAAA,UAAA,MAAA;AAAA,0BAED,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,4HAAA,EACZ,QAAA,EAAA;AAAA,YAAA,KAAA,wBACE,MAAA,EAAA,EAAK,EAAA,EAAI,OAAA,EAAS,SAAA,EAAU,+BAC1B,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,YAED,+BACC,GAAA,CAAC,MAAA,EAAA,EAAK,IAAI,aAAA,EAAe,SAAA,EAAU,mCAChC,QAAA,EAAA,WAAA,EACH;AAAA,WAAA,EAEJ;AAAA;AAAA;AAAA;AACF;AAEJ","file":"chunk-LLHTVIM3.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { RadioGroup as RadioGroupPrimitive } from 'radix-ui'\nimport { cn } from '../lib/utils'\n\n/**\n * Radio, 1:1 with the uiKit `_radioBase` (852:22410) and `Radio` (852:22426) sets: a 16px\n * circle, checked as a solid primary fill with a 6px dot, with an optional label (14/20 medium,\n * text/default) and description (14/20 regular, muted) 2px apart, 8px from the circle, on either\n * side. Disabled turns both texts text/subtle. The look of every state is\n * styles/components/radio.css.\n *\n * A Radio lives in a RadioGroup (Radix): the group owns the value, so `checked` (Figma's\n * property) follows from the group's `value` / `defaultValue`, and arrow keys move between\n * options. With a label or description the whole field is a `<label>`, so a click anywhere on\n * it (circle, label, description) picks the option, and hovering it hovers the circle; the label\n * names the circle, the description is wired with `aria-describedby`.\n */\nexport interface RadioGroupProps extends React.ComponentProps<typeof RadioGroupPrimitive.Root> {}\n\nexport function RadioGroup({ className, ...props }: RadioGroupProps) {\n return <RadioGroupPrimitive.Root data-slot=\"radio-group\" className={cn('grid gap-3', className)} {...props} />\n}\n\nexport interface RadioProps extends React.ComponentProps<typeof RadioGroupPrimitive.Item> {\n label?: React.ReactNode\n description?: React.ReactNode\n /** Which side of the text the circle sits on. */\n placement?: 'left' | 'right'\n /** Show a state without interacting, for state sheets. */\n 'data-force'?: 'hover' | 'focus' | 'pressed'\n}\n\nexport function Radio({ className, label, description, placement = 'left', id, disabled, ...props }: RadioProps) {\n const autoId = React.useId()\n const radioId = id ?? autoId\n const labelId = label ? `${radioId}-label` : undefined\n const descriptionId = description ? `${radioId}-description` : undefined\n\n const circle = (\n <RadioGroupPrimitive.Item\n id={radioId}\n data-slot=\"radio\"\n aria-labelledby={labelId}\n aria-describedby={descriptionId}\n disabled={disabled}\n className={cn('peer satin-radio', className)}\n {...props}\n >\n <RadioGroupPrimitive.Indicator className=\"satin-radio-dot\" />\n </RadioGroupPrimitive.Item>\n )\n\n if (!label && !description) return circle\n\n return (\n // The whole field is the <label>: the control, its label and its description all toggle it.\n <label\n htmlFor={radioId}\n data-slot=\"radio-field\"\n // The 16px circle sits on the middle of the label's first 20px line.\n className={cn('flex cursor-pointer items-start gap-2 has-disabled:cursor-not-allowed has-data-disabled:cursor-not-allowed [&>:first-child]:mt-0.5', placement === 'right' && 'flex-row-reverse justify-between')}\n >\n {circle}\n {/* Disabled (on the item or the whole group) turns both texts text/subtle. */}\n <div className=\"grid min-w-0 gap-0.5 text-base text-text-default peer-data-disabled:text-text-subtle peer-data-disabled:*:text-text-subtle\">\n {label && (\n <span id={labelId} className=\"block leading-5 font-medium\">\n {label}\n </span>\n )}\n {description && (\n <span id={descriptionId} className=\"block leading-5 text-text-muted\">\n {description}\n </span>\n )}\n </div>\n </label>\n )\n}\n"]}
@@ -0,0 +1,131 @@
1
+ import { useFieldChrome, FieldChrome } from './chunk-72OCFAPT.js';
2
+ import { cn } from './chunk-H63FYCMY.js';
3
+ import * as React from 'react';
4
+ import { Minus, Plus } from 'iconoir-react';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+
7
+ var decimals = (n) => (String(n).split(".")[1] ?? "").length;
8
+ function NumberInput({
9
+ value: valueProp,
10
+ defaultValue = 0,
11
+ onValueChange,
12
+ min = -Infinity,
13
+ max = Infinity,
14
+ step = 1,
15
+ size = "md",
16
+ formatValue = String,
17
+ label,
18
+ hint,
19
+ error,
20
+ disabled,
21
+ id,
22
+ name,
23
+ wrapperClassName,
24
+ className,
25
+ ...props
26
+ }) {
27
+ const chrome = useFieldChrome({ id, disabled, error, hint });
28
+ const [inner, setInner] = React.useState(defaultValue);
29
+ const value = valueProp ?? inner;
30
+ const [draft, setDraft] = React.useState(null);
31
+ const clamp = (n) => {
32
+ const snapped = Number.isFinite(min) ? Math.round((n - min) / step) * step + min : Math.round(n / step) * step;
33
+ return Number(Math.min(max, Math.max(min, snapped)).toFixed(decimals(step)));
34
+ };
35
+ const commit = (n) => {
36
+ const next = clamp(n);
37
+ setDraft(null);
38
+ if (next === value) return;
39
+ if (valueProp == null) setInner(next);
40
+ onValueChange?.(next);
41
+ };
42
+ const by = (k) => commit(value + k * step);
43
+ const atMin = value <= min;
44
+ const atMax = value >= max;
45
+ const btn = cn(
46
+ "grid size-6 shrink-0 place-items-center rounded-md text-text-muted outline-none",
47
+ "transition-[background-color,color,opacity] duration-(--duration-fast) ease-(--ease-out)",
48
+ "hover:bg-bg-hover hover:text-text-default focus-visible:[box-shadow:0_0_0_2px_var(--action-focus-ring)]",
49
+ "disabled:pointer-events-none disabled:opacity-40 [&_svg]:size-4 [&_svg]:[stroke-width:1.875]"
50
+ );
51
+ return /* @__PURE__ */ jsx(FieldChrome, { label, chrome, className: wrapperClassName, children: /* @__PURE__ */ jsxs(
52
+ "div",
53
+ {
54
+ "data-slot": "number-input",
55
+ "data-size": size,
56
+ "data-filled": true,
57
+ "data-invalid": chrome.invalid || void 0,
58
+ "data-disabled": chrome.disabled || void 0,
59
+ className: cn("satin-field gap-1 px-1", className),
60
+ children: [
61
+ /* @__PURE__ */ jsx(
62
+ "button",
63
+ {
64
+ type: "button",
65
+ tabIndex: -1,
66
+ "aria-label": "Decrease",
67
+ "aria-controls": chrome.controlId,
68
+ disabled: chrome.disabled || atMin,
69
+ onClick: () => by(-1),
70
+ className: btn,
71
+ children: /* @__PURE__ */ jsx(Minus, { "aria-hidden": true })
72
+ }
73
+ ),
74
+ /* @__PURE__ */ jsx(
75
+ "input",
76
+ {
77
+ id: chrome.controlId,
78
+ name,
79
+ role: "spinbutton",
80
+ inputMode: "decimal",
81
+ "aria-valuenow": value,
82
+ "aria-valuemin": Number.isFinite(min) ? min : void 0,
83
+ "aria-valuemax": Number.isFinite(max) ? max : void 0,
84
+ "aria-valuetext": formatValue(value),
85
+ "aria-invalid": chrome.invalid || void 0,
86
+ "aria-describedby": chrome.describedBy,
87
+ "aria-label": props["aria-label"],
88
+ disabled: chrome.disabled,
89
+ value: draft ?? formatValue(value),
90
+ onChange: (e) => setDraft(e.target.value),
91
+ onBlur: () => draft != null && commit(Number(draft.replace(",", ".").replace(/[^\d.-]/g, "")) || 0),
92
+ onKeyDown: (e) => {
93
+ const keys = {
94
+ ArrowUp: () => by(1),
95
+ ArrowDown: () => by(-1),
96
+ PageUp: () => by(10),
97
+ PageDown: () => by(-10),
98
+ Home: () => Number.isFinite(min) && commit(min),
99
+ End: () => Number.isFinite(max) && commit(max),
100
+ Enter: () => draft != null && commit(Number(draft.replace(",", ".").replace(/[^\d.-]/g, "")) || 0),
101
+ Escape: () => setDraft(null)
102
+ };
103
+ if (keys[e.key]) {
104
+ e.preventDefault();
105
+ keys[e.key]();
106
+ }
107
+ },
108
+ className: "satin-field-input text-center tabular-nums"
109
+ }
110
+ ),
111
+ /* @__PURE__ */ jsx(
112
+ "button",
113
+ {
114
+ type: "button",
115
+ tabIndex: -1,
116
+ "aria-label": "Increase",
117
+ "aria-controls": chrome.controlId,
118
+ disabled: chrome.disabled || atMax,
119
+ onClick: () => by(1),
120
+ className: btn,
121
+ children: /* @__PURE__ */ jsx(Plus, { "aria-hidden": true })
122
+ }
123
+ )
124
+ ]
125
+ }
126
+ ) });
127
+ }
128
+
129
+ export { NumberInput };
130
+ //# sourceMappingURL=chunk-LW3TRU5W.js.map
131
+ //# sourceMappingURL=chunk-LW3TRU5W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/number-input.tsx"],"names":[],"mappings":"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","file":"chunk-LW3TRU5W.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Minus, Plus } from 'iconoir-react'\nimport { cn } from '../lib/utils'\nimport { FieldChrome, useFieldChrome } from './select'\n\n/**\n * NumberInput, 1:1 with the uiKit design (🆕 input otp & number): Input's field skin and sizes\n * (sm 28, md 32, lg 36) with − and + at its ends and the value centred, tabular. The buttons are\n * 24px squares (radius-md, hover bg/hover, text/muted → text/default); at `min` or `max` the one\n * that would cross it fades to 40% and stops.\n *\n * It's a spinbutton: ArrowUp / ArrowDown step, PageUp / PageDown step ten times, Home / End jump\n * to `min` / `max`. Typing is free while focused; Enter or leaving the field clamps it, snaps it to\n * `step` and commits; Escape puts the last value back.\n */\nexport interface NumberInputProps {\n value?: number\n defaultValue?: number\n onValueChange?: (value: number) => void\n min?: number\n max?: number\n step?: number\n size?: 'sm' | 'md' | 'lg'\n /** How the value reads in the field and to screen readers. */\n formatValue?: (value: number) => string\n label?: React.ReactNode\n hint?: React.ReactNode\n error?: React.ReactNode | boolean\n disabled?: boolean\n id?: string\n name?: string\n 'aria-label'?: string\n /** Classes for the outer wrapper (label + field + hint). `className` styles the field. */\n wrapperClassName?: string\n className?: string\n}\n\nconst decimals = (n: number) => (String(n).split('.')[1] ?? '').length\n\nexport function NumberInput({\n value: valueProp,\n defaultValue = 0,\n onValueChange,\n min = -Infinity,\n max = Infinity,\n step = 1,\n size = 'md',\n formatValue = String,\n label,\n hint,\n error,\n disabled,\n id,\n name,\n wrapperClassName,\n className,\n ...props\n}: NumberInputProps) {\n const chrome = useFieldChrome({ id, disabled, error, hint })\n const [inner, setInner] = React.useState(defaultValue)\n const value = valueProp ?? inner\n const [draft, setDraft] = React.useState<string | null>(null)\n\n const clamp = (n: number) => {\n const snapped = Number.isFinite(min) ? Math.round((n - min) / step) * step + min : Math.round(n / step) * step\n return Number(Math.min(max, Math.max(min, snapped)).toFixed(decimals(step)))\n }\n const commit = (n: number) => {\n const next = clamp(n)\n setDraft(null)\n if (next === value) return\n if (valueProp == null) setInner(next)\n onValueChange?.(next)\n }\n const by = (k: number) => commit(value + k * step)\n\n const atMin = value <= min\n const atMax = value >= max\n const btn = cn(\n 'grid size-6 shrink-0 place-items-center rounded-md text-text-muted outline-none',\n 'transition-[background-color,color,opacity] duration-(--duration-fast) ease-(--ease-out)',\n 'hover:bg-bg-hover hover:text-text-default focus-visible:[box-shadow:0_0_0_2px_var(--action-focus-ring)]',\n 'disabled:pointer-events-none disabled:opacity-40 [&_svg]:size-4 [&_svg]:[stroke-width:1.875]',\n )\n\n return (\n <FieldChrome label={label} chrome={chrome} className={wrapperClassName}>\n <div\n data-slot=\"number-input\"\n data-size={size}\n data-filled\n data-invalid={chrome.invalid || undefined}\n data-disabled={chrome.disabled || undefined}\n className={cn('satin-field gap-1 px-1', className)}\n >\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label=\"Decrease\"\n aria-controls={chrome.controlId}\n disabled={chrome.disabled || atMin}\n onClick={() => by(-1)}\n className={btn}\n >\n <Minus aria-hidden />\n </button>\n <input\n id={chrome.controlId}\n name={name}\n role=\"spinbutton\"\n inputMode=\"decimal\"\n aria-valuenow={value}\n aria-valuemin={Number.isFinite(min) ? min : undefined}\n aria-valuemax={Number.isFinite(max) ? max : undefined}\n aria-valuetext={formatValue(value)}\n aria-invalid={chrome.invalid || undefined}\n aria-describedby={chrome.describedBy}\n aria-label={props['aria-label']}\n disabled={chrome.disabled}\n value={draft ?? formatValue(value)}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={() => draft != null && commit(Number(draft.replace(',', '.').replace(/[^\\d.-]/g, '')) || 0)}\n onKeyDown={(e) => {\n const keys: Record<string, () => void> = {\n ArrowUp: () => by(1),\n ArrowDown: () => by(-1),\n PageUp: () => by(10),\n PageDown: () => by(-10),\n Home: () => Number.isFinite(min) && commit(min),\n End: () => Number.isFinite(max) && commit(max),\n Enter: () => draft != null && commit(Number(draft.replace(',', '.').replace(/[^\\d.-]/g, '')) || 0),\n Escape: () => setDraft(null),\n }\n if (keys[e.key]) {\n e.preventDefault()\n keys[e.key]()\n }\n }}\n className=\"satin-field-input text-center tabular-nums\"\n />\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label=\"Increase\"\n aria-controls={chrome.controlId}\n disabled={chrome.disabled || atMax}\n onClick={() => by(1)}\n className={btn}\n >\n <Plus aria-hidden />\n </button>\n </div>\n </FieldChrome>\n )\n}\n"]}
@@ -0,0 +1,79 @@
1
+ import { ChartCard, ChartTooltip, defaultFormat, ChartLegendRow } from './chunk-YPJYW624.js';
2
+ import { cn } from './chunk-H63FYCMY.js';
3
+ import * as React from 'react';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ var seriesColor = (i) => `var(--chart-${i % 5 + 1})`;
7
+ function ChannelCard({
8
+ data,
9
+ formatValue = defaultFormat,
10
+ highlight,
11
+ total,
12
+ ...props
13
+ }) {
14
+ const [hovered, setHovered] = React.useState(null);
15
+ const [anchor, setAnchor] = React.useState(null);
16
+ const active = hovered ?? highlight ?? null;
17
+ const sum = data.reduce((n, d) => n + d.value, 0);
18
+ const last = data.length - 1;
19
+ return /* @__PURE__ */ jsx(ChartCard, { total: total ?? formatValue(sum), ...props, children: /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col gap-4", onPointerLeave: () => setHovered(null), children: [
20
+ /* @__PURE__ */ jsx(
21
+ "div",
22
+ {
23
+ "aria-hidden": true,
24
+ className: "relative grid h-3.5 gap-x-0.5",
25
+ style: { gridTemplateColumns: data.map((d) => `minmax(0, ${Math.max(d.value, 0)}fr)`).join(" ") },
26
+ children: data.map((d, i) => /* @__PURE__ */ jsx(
27
+ "span",
28
+ {
29
+ ref: active === i ? setAnchor : void 0,
30
+ onPointerEnter: () => setHovered(i),
31
+ className: cn(
32
+ "relative rounded-[1.5px] transition-opacity duration-(--duration-fast) ease-(--ease-out)",
33
+ i === 0 && "rounded-l-[4px]",
34
+ i === last && "rounded-r-[4px]",
35
+ active !== null && active !== i && "opacity-15"
36
+ ),
37
+ style: { background: seriesColor(i) },
38
+ children: active === i && /* @__PURE__ */ jsx(ChartTooltip, { anchor, sideOffset: 8, label: d.tooltipLabel ?? d.label, value: formatValue(d.value) })
39
+ },
40
+ d.label
41
+ ))
42
+ }
43
+ ),
44
+ /* @__PURE__ */ jsx(
45
+ "ul",
46
+ {
47
+ className: "flex flex-col gap-0.5 @min-[560px]:grid @min-[560px]:grid-flow-col @min-[560px]:auto-cols-fr @min-[560px]:gap-x-6",
48
+ style: { gridTemplateRows: `repeat(${Math.ceil(data.length / 2)}, auto)` },
49
+ "aria-label": props.title,
50
+ children: data.map((d, i) => /* @__PURE__ */ jsx(
51
+ "li",
52
+ {
53
+ tabIndex: 0,
54
+ onPointerEnter: () => setHovered(i),
55
+ onFocus: () => setHovered(i),
56
+ onBlur: () => setHovered(null),
57
+ "aria-label": `${d.label}: ${formatValue(d.value)}, ${Math.round(d.value / (sum || 1) * 100)}%`,
58
+ className: "rounded-md focus-visible:outline-none focus-visible:[box-shadow:0_0_0_2px_var(--action-focus-ring)]",
59
+ children: /* @__PURE__ */ jsx(
60
+ ChartLegendRow,
61
+ {
62
+ color: seriesColor(i),
63
+ label: d.label,
64
+ value: formatValue(d.value),
65
+ active: active === i,
66
+ dimmed: active !== null && active !== i
67
+ }
68
+ )
69
+ },
70
+ d.label
71
+ ))
72
+ }
73
+ )
74
+ ] }) });
75
+ }
76
+
77
+ export { ChannelCard };
78
+ //# sourceMappingURL=chunk-LZMKSFCF.js.map
79
+ //# sourceMappingURL=chunk-LZMKSFCF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/channel-chart.tsx"],"names":[],"mappings":"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","file":"chunk-LZMKSFCF.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { cn } from '../lib/utils'\nimport { ChartCard, ChartLegendRow, ChartTooltip, defaultFormat, type ChartCardBaseProps } from './chart'\n\n/**\n * Channel chart, 1:1 with the uiKit `chart/channelCard` (3608:18797), in the shared card\n * frame (chart.tsx).\n *\n * ChannelCard: the KPI (the sum unless given) with its change badge, a 14px stacked bar of fr\n * segments 2px apart in chart/1–5 (4px outer corners, 1.5px inner), and a legend row per\n * channel (8px swatch, label, value, 32 high, 2px apart), in two columns from 560px. Hover (or `highlight`) fades the\n * other segments to 15% and the other rows to 45%, and fills the row bg/muted.\n */\nexport interface ChannelCardProps extends ChartCardBaseProps {}\n\nconst seriesColor = (i: number) => `var(--chart-${(i % 5) + 1})`\n\nexport function ChannelCard({\n data,\n formatValue = defaultFormat,\n highlight,\n total,\n ...props\n}: ChannelCardProps) {\n const [hovered, setHovered] = React.useState<number | null>(null)\n const [anchor, setAnchor] = React.useState<HTMLElement | null>(null)\n const active = hovered ?? highlight ?? null\n const sum = data.reduce((n, d) => n + d.value, 0)\n const last = data.length - 1\n\n return (\n <ChartCard total={total ?? formatValue(sum)} {...props}>\n <div className=\"relative flex flex-col gap-4\" onPointerLeave={() => setHovered(null)}>\n <div\n aria-hidden\n className=\"relative grid h-3.5 gap-x-0.5\"\n style={{ gridTemplateColumns: data.map((d) => `minmax(0, ${Math.max(d.value, 0)}fr)`).join(' ') }}\n >\n {data.map((d, i) => (\n <span\n key={d.label}\n ref={active === i ? setAnchor : undefined}\n onPointerEnter={() => setHovered(i)}\n className={cn(\n 'relative rounded-[1.5px] transition-opacity duration-(--duration-fast) ease-(--ease-out)',\n i === 0 && 'rounded-l-[4px]',\n i === last && 'rounded-r-[4px]',\n active !== null && active !== i && 'opacity-15',\n )}\n style={{ background: seriesColor(i) }}\n >\n {active === i && (\n <ChartTooltip anchor={anchor} sideOffset={8} label={d.tooltipLabel ?? d.label} value={formatValue(d.value)} />\n )}\n </span>\n ))}\n </div>\n <ul\n className=\"flex flex-col gap-0.5 @min-[560px]:grid @min-[560px]:grid-flow-col @min-[560px]:auto-cols-fr @min-[560px]:gap-x-6\"\n style={{ gridTemplateRows: `repeat(${Math.ceil(data.length / 2)}, auto)` }}\n aria-label={props.title}\n >\n {data.map((d, i) => (\n <li\n key={d.label}\n tabIndex={0}\n onPointerEnter={() => setHovered(i)}\n onFocus={() => setHovered(i)}\n onBlur={() => setHovered(null)}\n aria-label={`${d.label}: ${formatValue(d.value)}, ${Math.round((d.value / (sum || 1)) * 100)}%`}\n className=\"rounded-md focus-visible:outline-none focus-visible:[box-shadow:0_0_0_2px_var(--action-focus-ring)]\"\n >\n <ChartLegendRow\n color={seriesColor(i)}\n label={d.label}\n value={formatValue(d.value)}\n active={active === i}\n dimmed={active !== null && active !== i}\n />\n </li>\n ))}\n </ul>\n </div>\n </ChartCard>\n )\n}\n"]}