@evinvest/uikit 0.19.0 → 0.20.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 (267) hide show
  1. package/README.md +17 -5
  2. package/dist/components/accordion.js +136 -0
  3. package/dist/components/accordion.js.map +1 -0
  4. package/dist/components/alert-dialog.js +175 -0
  5. package/dist/components/alert-dialog.js.map +1 -0
  6. package/dist/components/alert.js +49 -0
  7. package/dist/components/alert.js.map +1 -0
  8. package/dist/components/avatar.js +54 -0
  9. package/dist/components/avatar.js.map +1 -0
  10. package/dist/components/badge.js +25 -0
  11. package/dist/components/badge.js.map +1 -0
  12. package/dist/components/band.js +75 -0
  13. package/dist/components/band.js.map +1 -0
  14. package/dist/components/breadcrumb.js +132 -0
  15. package/dist/components/breadcrumb.js.map +1 -0
  16. package/dist/components/button-group.js +61 -0
  17. package/dist/components/button-group.js.map +1 -0
  18. package/dist/components/button.js +55 -0
  19. package/dist/components/button.js.map +1 -0
  20. package/dist/components/calendar.js +226 -0
  21. package/dist/components/calendar.js.map +1 -0
  22. package/dist/components/card.js +98 -0
  23. package/dist/components/card.js.map +1 -0
  24. package/dist/components/carousel.js +262 -0
  25. package/dist/components/carousel.js.map +1 -0
  26. package/dist/components/chart.js +151 -0
  27. package/dist/components/chart.js.map +1 -0
  28. package/dist/components/checkbox.js +68 -0
  29. package/dist/components/checkbox.js.map +1 -0
  30. package/dist/components/collapsible.js +60 -0
  31. package/dist/components/collapsible.js.map +1 -0
  32. package/dist/components/command.js +274 -0
  33. package/dist/components/command.js.map +1 -0
  34. package/dist/components/container.js +18 -0
  35. package/dist/components/container.js.map +1 -0
  36. package/dist/components/context-menu.js +337 -0
  37. package/dist/components/context-menu.js.map +1 -0
  38. package/dist/components/date-time-picker.js +301 -0
  39. package/dist/components/date-time-picker.js.map +1 -0
  40. package/dist/components/dialog.js +207 -0
  41. package/dist/components/dialog.js.map +1 -0
  42. package/dist/components/drawer.js +351 -0
  43. package/dist/components/drawer.js.map +1 -0
  44. package/dist/components/dropdown-menu.js +356 -0
  45. package/dist/components/dropdown-menu.js.map +1 -0
  46. package/dist/components/empty.js +91 -0
  47. package/dist/components/empty.js.map +1 -0
  48. package/dist/components/field-context.js +30 -0
  49. package/dist/components/field-context.js.map +1 -0
  50. package/dist/components/field.js +216 -0
  51. package/dist/components/field.js.map +1 -0
  52. package/dist/components/footer.js +89 -0
  53. package/dist/components/footer.js.map +1 -0
  54. package/dist/components/form.js +101 -0
  55. package/dist/components/form.js.map +1 -0
  56. package/dist/components/hover-card.js +90 -0
  57. package/dist/components/hover-card.js.map +1 -0
  58. package/dist/components/info-tip.js +161 -0
  59. package/dist/components/info-tip.js.map +1 -0
  60. package/dist/components/input-group.js +124 -0
  61. package/dist/components/input-group.js.map +1 -0
  62. package/dist/components/input-otp.js +120 -0
  63. package/dist/components/input-otp.js.map +1 -0
  64. package/dist/components/input.js +26 -0
  65. package/dist/components/input.js.map +1 -0
  66. package/dist/components/item.js +167 -0
  67. package/dist/components/item.js.map +1 -0
  68. package/dist/components/kbd.js +29 -0
  69. package/dist/components/kbd.js.map +1 -0
  70. package/dist/components/label.js +11 -0
  71. package/dist/components/label.js.map +1 -0
  72. package/dist/components/logo.js +41 -0
  73. package/dist/components/logo.js.map +1 -0
  74. package/dist/components/menubar.js +330 -0
  75. package/dist/components/menubar.js.map +1 -0
  76. package/dist/components/native-select.js +62 -0
  77. package/dist/components/native-select.js.map +1 -0
  78. package/dist/components/navigation-menu.js +204 -0
  79. package/dist/components/navigation-menu.js.map +1 -0
  80. package/dist/components/pagination.js +138 -0
  81. package/dist/components/pagination.js.map +1 -0
  82. package/dist/components/popover.js +104 -0
  83. package/dist/components/popover.js.map +1 -0
  84. package/dist/components/progress.js +30 -0
  85. package/dist/components/progress.js.map +1 -0
  86. package/dist/components/radio-group.js +113 -0
  87. package/dist/components/radio-group.js.map +1 -0
  88. package/dist/components/resizable.js +193 -0
  89. package/dist/components/resizable.js.map +1 -0
  90. package/dist/components/scroll-area.js +40 -0
  91. package/dist/components/scroll-area.js.map +1 -0
  92. package/dist/components/section-descriptor.js +88 -0
  93. package/dist/components/section-descriptor.js.map +1 -0
  94. package/dist/components/select-chevron.js +25 -0
  95. package/dist/components/select-chevron.js.map +1 -0
  96. package/dist/components/select.js +288 -0
  97. package/dist/components/select.js.map +1 -0
  98. package/dist/components/separator.js +31 -0
  99. package/dist/components/separator.js.map +1 -0
  100. package/dist/components/sheet.js +200 -0
  101. package/dist/components/sheet.js.map +1 -0
  102. package/dist/components/sidebar.js +326 -0
  103. package/dist/components/sidebar.js.map +1 -0
  104. package/dist/components/skeleton.js +18 -0
  105. package/dist/components/skeleton.js.map +1 -0
  106. package/dist/components/slider.js +138 -0
  107. package/dist/components/slider.js.map +1 -0
  108. package/dist/components/sonner.js +319 -0
  109. package/dist/components/sonner.js.map +1 -0
  110. package/dist/components/spinner.js +23 -0
  111. package/dist/components/spinner.js.map +1 -0
  112. package/dist/components/status-screen.js +145 -0
  113. package/dist/components/status-screen.js.map +1 -0
  114. package/dist/components/switch.js +54 -0
  115. package/dist/components/switch.js.map +1 -0
  116. package/dist/components/table.js +124 -0
  117. package/dist/components/table.js.map +1 -0
  118. package/dist/components/tabs.js +133 -0
  119. package/dist/components/tabs.js.map +1 -0
  120. package/dist/components/terminal.js +206 -0
  121. package/dist/components/terminal.js.map +1 -0
  122. package/dist/components/textarea.js +22 -0
  123. package/dist/components/textarea.js.map +1 -0
  124. package/dist/components/toggle-group.js +105 -0
  125. package/dist/components/toggle-group.js.map +1 -0
  126. package/dist/components/toggle.js +53 -0
  127. package/dist/components/toggle.js.map +1 -0
  128. package/dist/components/tooltip.js +102 -0
  129. package/dist/components/tooltip.js.map +1 -0
  130. package/dist/generated/accent.js +27 -0
  131. package/dist/generated/accent.js.map +1 -0
  132. package/dist/generated/accordion.js +13 -0
  133. package/dist/generated/accordion.js.map +1 -0
  134. package/dist/generated/alert-dialog.js +15 -0
  135. package/dist/generated/alert-dialog.js.map +1 -0
  136. package/dist/generated/alert.js +16 -0
  137. package/dist/generated/alert.js.map +1 -0
  138. package/dist/generated/avatar.js +9 -0
  139. package/dist/generated/avatar.js.map +1 -0
  140. package/dist/generated/badge.js +13 -0
  141. package/dist/generated/badge.js.map +1 -0
  142. package/dist/generated/band.js +39 -0
  143. package/dist/generated/band.js.map +1 -0
  144. package/dist/generated/breadcrumb.js +15 -0
  145. package/dist/generated/breadcrumb.js.map +1 -0
  146. package/dist/generated/button-group.js +14 -0
  147. package/dist/generated/button-group.js.map +1 -0
  148. package/dist/generated/button.js +30 -0
  149. package/dist/generated/button.js.map +1 -0
  150. package/dist/generated/calendar.js +29 -0
  151. package/dist/generated/calendar.js.map +1 -0
  152. package/dist/generated/card.js +17 -0
  153. package/dist/generated/card.js.map +1 -0
  154. package/dist/generated/carousel.js +31 -0
  155. package/dist/generated/carousel.js.map +1 -0
  156. package/dist/generated/chart.js +9 -0
  157. package/dist/generated/chart.js.map +1 -0
  158. package/dist/generated/checkbox.js +7 -0
  159. package/dist/generated/checkbox.js.map +1 -0
  160. package/dist/generated/command.js +27 -0
  161. package/dist/generated/command.js.map +1 -0
  162. package/dist/generated/container.js +5 -0
  163. package/dist/generated/container.js.map +1 -0
  164. package/dist/generated/context-menu.js +19 -0
  165. package/dist/generated/context-menu.js.map +1 -0
  166. package/dist/generated/date-time-picker.js +15 -0
  167. package/dist/generated/date-time-picker.js.map +1 -0
  168. package/dist/generated/dialog.js +17 -0
  169. package/dist/generated/dialog.js.map +1 -0
  170. package/dist/generated/drawer.js +26 -0
  171. package/dist/generated/drawer.js.map +1 -0
  172. package/dist/generated/dropdown-menu.js +21 -0
  173. package/dist/generated/dropdown-menu.js.map +1 -0
  174. package/dist/generated/empty.js +20 -0
  175. package/dist/generated/empty.js.map +1 -0
  176. package/dist/generated/field.js +33 -0
  177. package/dist/generated/field.js.map +1 -0
  178. package/dist/generated/focus.js +5 -0
  179. package/dist/generated/focus.js.map +1 -0
  180. package/dist/generated/form.js +11 -0
  181. package/dist/generated/form.js.map +1 -0
  182. package/dist/generated/hover-card.js +5 -0
  183. package/dist/generated/hover-card.js.map +1 -0
  184. package/dist/generated/input-group.js +29 -0
  185. package/dist/generated/input-group.js.map +1 -0
  186. package/dist/generated/input-otp.js +15 -0
  187. package/dist/generated/input-otp.js.map +1 -0
  188. package/dist/generated/input.js +11 -0
  189. package/dist/generated/input.js.map +1 -0
  190. package/dist/generated/item.js +40 -0
  191. package/dist/generated/item.js.map +1 -0
  192. package/dist/generated/kbd.js +7 -0
  193. package/dist/generated/kbd.js.map +1 -0
  194. package/dist/generated/label.js +5 -0
  195. package/dist/generated/label.js.map +1 -0
  196. package/dist/generated/menubar.js +27 -0
  197. package/dist/generated/menubar.js.map +1 -0
  198. package/dist/generated/navigation-menu.js +19 -0
  199. package/dist/generated/navigation-menu.js.map +1 -0
  200. package/dist/generated/popover.js +5 -0
  201. package/dist/generated/popover.js.map +1 -0
  202. package/dist/generated/progress.js +7 -0
  203. package/dist/generated/progress.js.map +1 -0
  204. package/dist/generated/radio-group.js +7 -0
  205. package/dist/generated/radio-group.js.map +1 -0
  206. package/dist/generated/resizable.js +11 -0
  207. package/dist/generated/resizable.js.map +1 -0
  208. package/dist/generated/scroll-area.js +14 -0
  209. package/dist/generated/scroll-area.js.map +1 -0
  210. package/dist/generated/select.js +15 -0
  211. package/dist/generated/select.js.map +1 -0
  212. package/dist/generated/separator.js +10 -0
  213. package/dist/generated/separator.js.map +1 -0
  214. package/dist/generated/sheet.js +25 -0
  215. package/dist/generated/sheet.js.map +1 -0
  216. package/dist/generated/sidebar.js +41 -0
  217. package/dist/generated/sidebar.js.map +1 -0
  218. package/dist/generated/skeleton.js +5 -0
  219. package/dist/generated/skeleton.js.map +1 -0
  220. package/dist/generated/slider.js +11 -0
  221. package/dist/generated/slider.js.map +1 -0
  222. package/dist/generated/sonner.js +30 -0
  223. package/dist/generated/sonner.js.map +1 -0
  224. package/dist/generated/switch.js +7 -0
  225. package/dist/generated/switch.js.map +1 -0
  226. package/dist/generated/table.js +21 -0
  227. package/dist/generated/table.js.map +1 -0
  228. package/dist/generated/tabs.js +11 -0
  229. package/dist/generated/tabs.js.map +1 -0
  230. package/dist/generated/terminal.js +70 -0
  231. package/dist/generated/terminal.js.map +1 -0
  232. package/dist/generated/textarea.js +11 -0
  233. package/dist/generated/textarea.js.map +1 -0
  234. package/dist/generated/toggle.js +18 -0
  235. package/dist/generated/toggle.js.map +1 -0
  236. package/dist/generated/tooltip.js +5 -0
  237. package/dist/generated/tooltip.js.map +1 -0
  238. package/dist/index.d.ts +43 -10
  239. package/dist/index.js +73 -8805
  240. package/dist/lib/cn.js +12 -0
  241. package/dist/lib/cn.js.map +1 -0
  242. package/dist/lib/focus.js +5 -0
  243. package/dist/lib/focus.js.map +1 -0
  244. package/dist/primitives/dismissable-layer.js +35 -0
  245. package/dist/primitives/dismissable-layer.js.map +1 -0
  246. package/dist/primitives/focus-scope.js +58 -0
  247. package/dist/primitives/focus-scope.js.map +1 -0
  248. package/dist/primitives/merge-refs.js +13 -0
  249. package/dist/primitives/merge-refs.js.map +1 -0
  250. package/dist/primitives/portal.js +19 -0
  251. package/dist/primitives/portal.js.map +1 -0
  252. package/dist/primitives/presence.js +45 -0
  253. package/dist/primitives/presence.js.map +1 -0
  254. package/dist/primitives/slot.js +39 -0
  255. package/dist/primitives/slot.js.map +1 -0
  256. package/dist/primitives/use-controllable-state.js +24 -0
  257. package/dist/primitives/use-controllable-state.js.map +1 -0
  258. package/dist/primitives/use-floating.js +128 -0
  259. package/dist/primitives/use-floating.js.map +1 -0
  260. package/dist/primitives/use-hover-intent.js +50 -0
  261. package/dist/primitives/use-hover-intent.js.map +1 -0
  262. package/dist/primitives/use-roving-focus.js +40 -0
  263. package/dist/primitives/use-roving-focus.js.map +1 -0
  264. package/dist/primitives/walk-elements.js +13 -0
  265. package/dist/primitives/walk-elements.js.map +1 -0
  266. package/package.json +5 -4
  267. package/dist/index.js.map +0 -1
@@ -0,0 +1,319 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import {
6
+ TOAST_BASE,
7
+ TOAST_CLOSE,
8
+ TOAST_CONTENT,
9
+ TOAST_TITLE,
10
+ TOASTER_BASE,
11
+ positionClasses,
12
+ toastVariantClasses
13
+ } from "../generated/sonner.js";
14
+ const DEFAULT_DURATION = 4e3;
15
+ const SWIPE_THRESHOLD = 45;
16
+ const SWIPE_VELOCITY = 0.11;
17
+ const SWIPE_FLING_MS = 200;
18
+ const store = /* @__PURE__ */ (() => {
19
+ let toasts = [];
20
+ let id = 0;
21
+ const listeners = /* @__PURE__ */ new Set();
22
+ const emit = () => {
23
+ for (const listener of listeners) listener(toasts);
24
+ };
25
+ const add = (message, opts = {}) => {
26
+ const next = {
27
+ id: ++id,
28
+ message,
29
+ description: opts.description,
30
+ duration: opts.duration ?? DEFAULT_DURATION,
31
+ variant: opts.variant ?? "neutral",
32
+ state: "open"
33
+ };
34
+ toasts = [...toasts, next];
35
+ emit();
36
+ return next.id;
37
+ };
38
+ const dismiss = (toastId) => {
39
+ toasts = toasts.map(
40
+ (t) => t.id === toastId ? { ...t, state: "closing" } : t
41
+ );
42
+ emit();
43
+ };
44
+ const remove = (toastId) => {
45
+ toasts = toasts.filter((t) => t.id !== toastId);
46
+ emit();
47
+ };
48
+ const subscribe = (listener) => {
49
+ listeners.add(listener);
50
+ listener(toasts);
51
+ return () => {
52
+ listeners.delete(listener);
53
+ };
54
+ };
55
+ return { add, dismiss, remove, subscribe };
56
+ })();
57
+ const toast = Object.assign(
58
+ (message, opts) => store.add(message, opts),
59
+ {
60
+ positive: (message, opts) => store.add(message, { ...opts, variant: "positive" }),
61
+ error: (message, opts) => store.add(message, { ...opts, variant: "error" }),
62
+ info: (message, opts) => store.add(message, { ...opts, variant: "info" }),
63
+ warn: (message, opts) => store.add(message, { ...opts, variant: "warn" }),
64
+ dismiss: (id) => store.dismiss(id)
65
+ }
66
+ );
67
+ const VISIBLE_TOASTS = 3;
68
+ const GAP = 14;
69
+ function ToastItem({
70
+ toast: t,
71
+ index,
72
+ total,
73
+ offset,
74
+ paused,
75
+ reportHeight,
76
+ removeHeight
77
+ }) {
78
+ const ref = React.useRef(null);
79
+ const [height, setHeight] = React.useState(0);
80
+ const remainingRef = React.useRef(t.duration);
81
+ const startedRef = React.useRef(0);
82
+ const flingRef = React.useRef(
83
+ void 0
84
+ );
85
+ const dragRef = React.useRef(
86
+ null
87
+ );
88
+ React.useLayoutEffect(() => {
89
+ const el = ref.current;
90
+ if (!el) return;
91
+ el.style.height = "auto";
92
+ const h = el.getBoundingClientRect().height;
93
+ el.style.height = "";
94
+ if (h) {
95
+ setHeight(h);
96
+ reportHeight(t.id, h);
97
+ }
98
+ }, [t.id, t.message, t.description, reportHeight]);
99
+ React.useEffect(() => () => removeHeight(t.id), [t.id, removeHeight]);
100
+ const mountedClosing = React.useRef(t.state === "closing");
101
+ React.useEffect(() => {
102
+ if (mountedClosing.current) store.remove(t.id);
103
+ }, [t.id]);
104
+ React.useEffect(() => {
105
+ if (t.duration === Infinity || t.state === "closing" || paused) return;
106
+ startedRef.current = Date.now();
107
+ const id = setTimeout(() => store.dismiss(t.id), remainingRef.current);
108
+ return () => {
109
+ clearTimeout(id);
110
+ remainingRef.current = Math.max(
111
+ 0,
112
+ remainingRef.current - (Date.now() - startedRef.current)
113
+ );
114
+ };
115
+ }, [paused, t.id, t.duration, t.state]);
116
+ React.useEffect(
117
+ () => () => {
118
+ if (flingRef.current) clearTimeout(flingRef.current);
119
+ },
120
+ []
121
+ );
122
+ const onTransitionEnd = (e) => {
123
+ if (t.state === "closing" && e.propertyName === "transform") {
124
+ store.remove(t.id);
125
+ }
126
+ };
127
+ const onPointerDown = (e) => {
128
+ const el = ref.current;
129
+ if (!el || e.button > 0 || t.state === "closing") return;
130
+ if (e.target.closest('[data-slot="toast-close"]')) return;
131
+ dragRef.current = { x: e.clientX, time: e.timeStamp, dx: 0 };
132
+ el.setAttribute("data-swiping", "true");
133
+ try {
134
+ el.setPointerCapture?.(e.pointerId);
135
+ } catch {
136
+ }
137
+ };
138
+ const onPointerMove = (e) => {
139
+ const drag = dragRef.current;
140
+ const el = ref.current;
141
+ if (!drag || !el) return;
142
+ const dx = e.clientX - drag.x;
143
+ drag.dx = dx;
144
+ const dist = el.offsetWidth || 256;
145
+ el.style.setProperty("--swipe-x", `${dx}px`);
146
+ el.style.opacity = `${Math.max(0, 1 - Math.abs(dx) / dist)}`;
147
+ };
148
+ const onPointerEnd = (e) => {
149
+ const drag = dragRef.current;
150
+ const el = ref.current;
151
+ if (!drag || !el) return;
152
+ dragRef.current = null;
153
+ el.removeAttribute("data-swiping");
154
+ const dt = e.timeStamp - drag.time;
155
+ const velocity = dt > 0 ? Math.abs(drag.dx) / dt : 0;
156
+ if (drag.dx !== 0 && (Math.abs(drag.dx) >= SWIPE_THRESHOLD || velocity > SWIPE_VELOCITY)) {
157
+ el.style.setProperty("--swipe-x", `${drag.dx > 0 ? 150 : -150}%`);
158
+ el.style.opacity = "0";
159
+ const remove = () => store.remove(t.id);
160
+ el.addEventListener("transitionend", remove, { once: true });
161
+ flingRef.current = setTimeout(remove, SWIPE_FLING_MS + 50);
162
+ } else {
163
+ el.style.setProperty("--swipe-x", "0px");
164
+ el.style.opacity = "1";
165
+ el.addEventListener(
166
+ "transitionend",
167
+ () => {
168
+ el.style.removeProperty("--swipe-x");
169
+ el.style.opacity = "";
170
+ },
171
+ { once: true }
172
+ );
173
+ }
174
+ };
175
+ return /* @__PURE__ */ jsxs(
176
+ "li",
177
+ {
178
+ ref,
179
+ role: "status",
180
+ "aria-live": "polite",
181
+ "data-slot": "toast",
182
+ "data-variant": t.variant,
183
+ "data-state": t.state === "closing" ? "closed" : "open",
184
+ "data-front": index === 0,
185
+ "data-visible": index < VISIBLE_TOASTS,
186
+ onTransitionEnd,
187
+ onPointerDown,
188
+ onPointerMove,
189
+ onPointerUp: onPointerEnd,
190
+ onPointerCancel: onPointerEnd,
191
+ style: {
192
+ "--index": index,
193
+ "--z-index": total - index,
194
+ "--offset": `${offset}px`,
195
+ "--initial-height": `${height}px`
196
+ },
197
+ className: cn(
198
+ TOAST_BASE,
199
+ "touch-pan-y select-none",
200
+ toastVariantClasses[t.variant]
201
+ ),
202
+ children: [
203
+ /* @__PURE__ */ jsxs("div", { className: TOAST_CONTENT, children: [
204
+ /* @__PURE__ */ jsx("div", { className: TOAST_TITLE, children: t.message }),
205
+ t.description ? /* @__PURE__ */ jsx("div", { className: "text-ink-soft text-sm", children: t.description }) : null
206
+ ] }),
207
+ /* @__PURE__ */ jsx(
208
+ "button",
209
+ {
210
+ type: "button",
211
+ "aria-label": "Close",
212
+ "data-slot": "toast-close",
213
+ onClick: () => store.dismiss(t.id),
214
+ className: TOAST_CLOSE,
215
+ children: /* @__PURE__ */ jsx(
216
+ "svg",
217
+ {
218
+ xmlns: "http://www.w3.org/2000/svg",
219
+ width: "16",
220
+ height: "16",
221
+ viewBox: "0 0 24 24",
222
+ fill: "none",
223
+ stroke: "currentColor",
224
+ strokeWidth: "2",
225
+ strokeLinecap: "round",
226
+ strokeLinejoin: "round",
227
+ "aria-hidden": "true",
228
+ children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" })
229
+ }
230
+ )
231
+ }
232
+ )
233
+ ]
234
+ }
235
+ );
236
+ }
237
+ function Toaster({
238
+ position = "bottom-right",
239
+ className,
240
+ ...props
241
+ }) {
242
+ const [toasts, setToasts] = React.useState([]);
243
+ const [heights, setHeights] = React.useState(/* @__PURE__ */ new Map());
244
+ const [paused, setPaused] = React.useState(false);
245
+ React.useEffect(() => store.subscribe(setToasts), []);
246
+ const onEnter = () => setPaused(true);
247
+ const onLeave = (e) => {
248
+ if (!e.currentTarget.contains(e.relatedTarget)) {
249
+ setPaused(false);
250
+ }
251
+ };
252
+ const reportHeight = React.useCallback((id, h) => {
253
+ setHeights((prev) => {
254
+ if (prev.get(id) === h) return prev;
255
+ const next = new Map(prev);
256
+ next.set(id, h);
257
+ return next;
258
+ });
259
+ }, []);
260
+ const removeHeight = React.useCallback((id) => {
261
+ setHeights((prev) => {
262
+ if (!prev.has(id)) return prev;
263
+ const next = new Map(prev);
264
+ next.delete(id);
265
+ return next;
266
+ });
267
+ }, []);
268
+ const yPosition = position.startsWith("top") ? "top" : "bottom";
269
+ const ordered = [...toasts].reverse();
270
+ const known = [...heights.values()];
271
+ const frontHeight = ordered[0] ? heights.get(ordered[0].id) ?? (known.length ? Math.max(...known) : 0) : 0;
272
+ let sum = 0;
273
+ const offsets = ordered.map((t, i) => {
274
+ const o = sum + i * GAP;
275
+ sum += heights.get(t.id) ?? 0;
276
+ return o;
277
+ });
278
+ return /* @__PURE__ */ jsx(
279
+ "ol",
280
+ {
281
+ "data-slot": "toaster",
282
+ "data-position": position,
283
+ "data-y-position": yPosition,
284
+ "data-stack": "",
285
+ style: {
286
+ "--front-height": `${frontHeight}px`,
287
+ "--gap": `${GAP}px`
288
+ },
289
+ className: cn(
290
+ TOASTER_BASE,
291
+ positionClasses[position],
292
+ className
293
+ ),
294
+ ...props,
295
+ onPointerOver: onEnter,
296
+ onPointerOut: onLeave,
297
+ onFocus: onEnter,
298
+ onBlur: onLeave,
299
+ children: ordered.map((t, i) => /* @__PURE__ */ jsx(
300
+ ToastItem,
301
+ {
302
+ toast: t,
303
+ index: i,
304
+ total: ordered.length,
305
+ offset: offsets[i],
306
+ paused,
307
+ reportHeight,
308
+ removeHeight
309
+ },
310
+ t.id
311
+ ))
312
+ }
313
+ );
314
+ }
315
+ export {
316
+ Toaster,
317
+ toast
318
+ };
319
+ //# sourceMappingURL=sonner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/sonner.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport {\n TOAST_BASE,\n TOAST_CLOSE,\n TOAST_CONTENT,\n TOAST_TITLE,\n TOASTER_BASE,\n positionClasses,\n toastVariantClasses,\n type ToastPosition,\n type ToastVariant,\n} from \"../generated/sonner\";\n\nexport type { ToastPosition, ToastVariant };\n\n/**\n * Lifecycle phase of a toast. It is added `\"open\"` (plays the enter keyframe);\n * {@link ToastFn.dismiss} flips it to `\"closing\"` (slides it out) and the live\n * node is dropped once the exit `transitionend` fires. Mirrors Rust's\n * `ToastState` enum and the `data-state` the shared `tokens.css` keys on.\n */\nexport type ToastState = \"open\" | \"closing\";\n\nexport interface ToastOptions {\n description?: React.ReactNode;\n /**\n * Auto-dismiss delay in ms (default 4000). Pass `Infinity` for a **persistent**\n * toast that never times out — it stays until the close button, a swipe, or\n * `toast.dismiss(id)` removes it. Hovering the stack also pauses the countdown.\n */\n duration?: number;\n variant?: ToastVariant;\n}\n\nexport interface Toast {\n id: number;\n message: React.ReactNode;\n description?: React.ReactNode;\n duration: number;\n variant: ToastVariant;\n state: ToastState;\n}\n\nconst DEFAULT_DURATION = 4000;\n\n// Swipe-to-dismiss tuning (pointer physics, TS-only — Rust stays render-only).\n// Values from Sonner: a horizontal drag past 45px, or a flick faster than\n// 0.11px/ms, flings the toast off; anything short snaps back.\nconst SWIPE_THRESHOLD = 45;\nconst SWIPE_VELOCITY = 0.11;\nconst SWIPE_FLING_MS = 200;\n\n/**\n * Module-level toast store: a plain observable holding the live toast array.\n * `toast(...)` mutates it and notifies subscribers; the {@link Toaster}\n * component is the sole subscriber. This is the JS-native global the Rust\n * mirror cannot have — Dioxus has no clean cross-component mutable singleton,\n * so it uses a `ToasterProvider` + `use_toaster()` hook instead.\n */\nconst store = (() => {\n let toasts: Toast[] = [];\n let id = 0;\n const listeners = new Set<(toasts: Toast[]) => void>();\n\n const emit = () => {\n for (const listener of listeners) listener(toasts);\n };\n\n const add = (message: React.ReactNode, opts: ToastOptions = {}): number => {\n const next: Toast = {\n id: ++id,\n message,\n description: opts.description,\n duration: opts.duration ?? DEFAULT_DURATION,\n variant: opts.variant ?? \"neutral\",\n state: \"open\",\n };\n toasts = [...toasts, next];\n emit();\n return next.id;\n };\n\n // Begins the exit animation: flip the toast to `closing` (`data-state=closed`)\n // and keep it mounted so the exit keyframe can play. The live node is dropped\n // by `remove`, wired to the exit `transitionend` — no timer matched to the CSS.\n // A toast dismissed before it has ever painted mounts closed and so never\n // transitions; `ToastItem` drops that one on mount instead.\n const dismiss = (toastId: number) => {\n toasts = toasts.map((t) =>\n t.id === toastId ? { ...t, state: \"closing\" as const } : t,\n );\n emit();\n };\n\n // Drops a toast outright. Wired to the exit `transitionend`; the guard that it\n // is actually `closing` (a restack transition fires too) lives at the call\n // site in {@link ToastItem}, alongside the mount-closed path.\n const remove = (toastId: number) => {\n toasts = toasts.filter((t) => t.id !== toastId);\n emit();\n };\n\n const subscribe = (listener: (toasts: Toast[]) => void): (() => void) => {\n listeners.add(listener);\n listener(toasts);\n return () => {\n listeners.delete(listener);\n };\n };\n\n return { add, dismiss, remove, subscribe };\n})();\n\nexport interface ToastFn {\n (message: React.ReactNode, opts?: ToastOptions): number;\n positive: (message: React.ReactNode, opts?: ToastOptions) => number;\n error: (message: React.ReactNode, opts?: ToastOptions) => number;\n info: (message: React.ReactNode, opts?: ToastOptions) => number;\n warn: (message: React.ReactNode, opts?: ToastOptions) => number;\n dismiss: (id: number) => void;\n}\n\n/**\n * Global toast handle. `toast(msg)` enqueues a neutral toast; the `.positive`,\n * `.error`, `.info` and `.warn` helpers pin the variant; `toast.dismiss(id)`\n * removes one early. Mirrors Rust's `use_toaster()` handle methods.\n */\nexport const toast: ToastFn = Object.assign(\n (message: React.ReactNode, opts?: ToastOptions) => store.add(message, opts),\n {\n positive: (message: React.ReactNode, opts?: ToastOptions) =>\n store.add(message, { ...opts, variant: \"positive\" }),\n error: (message: React.ReactNode, opts?: ToastOptions) =>\n store.add(message, { ...opts, variant: \"error\" }),\n info: (message: React.ReactNode, opts?: ToastOptions) =>\n store.add(message, { ...opts, variant: \"info\" }),\n warn: (message: React.ReactNode, opts?: ToastOptions) =>\n store.add(message, { ...opts, variant: \"warn\" }),\n dismiss: (id: number) => store.dismiss(id),\n },\n);\n\n// Sonner-style stacking: only the front VISIBLE_TOASTS show collapsed; GAP is\n// the px between them once expanded.\nconst VISIBLE_TOASTS = 3;\nconst GAP = 14;\n\ninterface ToastItemProps {\n toast: Toast;\n index: number; // 0 = front / newest\n total: number;\n offset: number; // px from the pinned edge to this toast's expanded slot\n paused: boolean; // the stack is hovered/focused — hold the auto-dismiss\n reportHeight: (id: number, height: number) => void;\n removeHeight: (id: number) => void;\n}\n\nfunction ToastItem({\n toast: t,\n index,\n total,\n offset,\n paused,\n reportHeight,\n removeHeight,\n}: ToastItemProps) {\n const ref = React.useRef<HTMLLIElement>(null);\n const [height, setHeight] = React.useState(0);\n // remaining auto-dismiss budget (banked across hover pauses), the last start\n // timestamp, and the swipe-fling fallback timer.\n const remainingRef = React.useRef(t.duration);\n const startedRef = React.useRef(0);\n const flingRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(\n undefined,\n );\n const dragRef = React.useRef<{ x: number; time: number; dx: number } | null>(\n null,\n );\n\n // measure the *natural* height for the stack maths; re-measure on content\n // change. Back toasts are clamped to --front-height by the CSS, so unset the\n // height for the read (Sonner does the same) then hand it back to the CSS.\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n el.style.height = \"auto\";\n const h = el.getBoundingClientRect().height;\n el.style.height = \"\";\n if (h) {\n setHeight(h);\n reportHeight(t.id, h);\n }\n }, [t.id, t.message, t.description, reportHeight]);\n\n React.useEffect(() => () => removeHeight(t.id), [t.id, removeHeight]);\n\n // Dismissed before its first paint (added and dismissed in one batch), so it\n // mounts already `closing`: its computed style never changes, no transition\n // runs, and `onTransitionEnd` below would never fire — stranding it in the\n // store forever. Nothing was painted, so there is nothing to animate: drop it\n // once mounted. Mirrors the Dioxus port's mount effect.\n const mountedClosing = React.useRef(t.state === \"closing\");\n React.useEffect(() => {\n if (mountedClosing.current) store.remove(t.id);\n }, [t.id]);\n\n // Auto-dismiss, pausable on hover. While `paused` (or closing, or a persistent\n // `duration: Infinity`) no timer runs; the cleanup banks the elapsed time so a\n // resume continues from the remaining budget rather than restarting.\n React.useEffect(() => {\n if (t.duration === Infinity || t.state === \"closing\" || paused) return;\n startedRef.current = Date.now();\n const id = setTimeout(() => store.dismiss(t.id), remainingRef.current);\n return () => {\n clearTimeout(id);\n remainingRef.current = Math.max(\n 0,\n remainingRef.current - (Date.now() - startedRef.current),\n );\n };\n }, [paused, t.id, t.duration, t.state]);\n\n // cancel the swipe-fling fallback if the toast unmounts first\n React.useEffect(\n () => () => {\n if (flingRef.current) clearTimeout(flingRef.current);\n },\n [],\n );\n\n // exit rides the transition: a closing toast slides back off and is dropped on\n // the transform's `transitionend` (guarded so stack repositioning never fires\n // it). No host timer — keeps parity with the Dioxus port.\n const onTransitionEnd = (e: React.TransitionEvent<HTMLLIElement>) => {\n if (t.state === \"closing\" && e.propertyName === \"transform\") {\n store.remove(t.id);\n }\n };\n\n // ── swipe-to-dismiss (horizontal pointer drag) ──────────────────────────\n // Tracked through the `--swipe-x` var so it *composes* with the stack\n // transform (`var(--y) translateX(var(--swipe-x))`) instead of overriding it.\n const onPointerDown = (e: React.PointerEvent<HTMLLIElement>) => {\n const el = ref.current;\n if (!el || e.button > 0 || t.state === \"closing\") return;\n if ((e.target as HTMLElement).closest('[data-slot=\"toast-close\"]')) return;\n // auto-dismiss is already paused (a swipe means the pointer is over the stack)\n dragRef.current = { x: e.clientX, time: e.timeStamp, dx: 0 };\n el.setAttribute(\"data-swiping\", \"true\"); // CSS drops the transition for 1:1 tracking\n try {\n el.setPointerCapture?.(e.pointerId);\n } catch {\n // jsdom / unsupported — capture is a nice-to-have, the drag works without it\n }\n };\n\n const onPointerMove = (e: React.PointerEvent<HTMLLIElement>) => {\n const drag = dragRef.current;\n const el = ref.current;\n if (!drag || !el) return;\n const dx = e.clientX - drag.x;\n drag.dx = dx;\n const dist = el.offsetWidth || 256;\n el.style.setProperty(\"--swipe-x\", `${dx}px`);\n el.style.opacity = `${Math.max(0, 1 - Math.abs(dx) / dist)}`;\n };\n\n const onPointerEnd = (e: React.PointerEvent<HTMLLIElement>) => {\n const drag = dragRef.current;\n const el = ref.current;\n if (!drag || !el) return;\n dragRef.current = null;\n el.removeAttribute(\"data-swiping\"); // CSS restores the transition\n const dt = e.timeStamp - drag.time;\n const velocity = dt > 0 ? Math.abs(drag.dx) / dt : 0;\n if (\n drag.dx !== 0 &&\n (Math.abs(drag.dx) >= SWIPE_THRESHOLD || velocity > SWIPE_VELOCITY)\n ) {\n el.style.setProperty(\"--swipe-x\", `${drag.dx > 0 ? 150 : -150}%`);\n el.style.opacity = \"0\";\n const remove = () => store.remove(t.id);\n el.addEventListener(\"transitionend\", remove, { once: true });\n flingRef.current = setTimeout(remove, SWIPE_FLING_MS + 50); // fallback if transitionend is missed\n } else {\n // snap back: clear the swipe offset (the transition carries it home), then\n // hand styling back to the CSS. Auto-dismiss stays paused while hovered and\n // re-arms from the remaining budget once the pointer leaves.\n el.style.setProperty(\"--swipe-x\", \"0px\");\n el.style.opacity = \"1\";\n el.addEventListener(\n \"transitionend\",\n () => {\n el.style.removeProperty(\"--swipe-x\");\n el.style.opacity = \"\";\n },\n { once: true },\n );\n }\n };\n\n return (\n <li\n ref={ref}\n role=\"status\"\n aria-live=\"polite\"\n data-slot=\"toast\"\n data-variant={t.variant}\n data-state={t.state === \"closing\" ? \"closed\" : \"open\"}\n data-front={index === 0}\n data-visible={index < VISIBLE_TOASTS}\n onTransitionEnd={onTransitionEnd}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerEnd}\n onPointerCancel={onPointerEnd}\n style={\n {\n \"--index\": index,\n \"--z-index\": total - index,\n \"--offset\": `${offset}px`,\n \"--initial-height\": `${height}px`,\n } as React.CSSProperties\n }\n className={cn(\n TOAST_BASE,\n \"touch-pan-y select-none\",\n toastVariantClasses[t.variant],\n )}\n >\n <div className={TOAST_CONTENT}>\n <div className={TOAST_TITLE}>{t.message}</div>\n {t.description ? (\n <div className=\"text-ink-soft text-sm\">{t.description}</div>\n ) : null}\n </div>\n <button\n type=\"button\"\n aria-label=\"Close\"\n data-slot=\"toast-close\"\n onClick={() => store.dismiss(t.id)}\n className={TOAST_CLOSE}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\n </li>\n );\n}\n\nexport interface ToasterProps extends React.ComponentProps<\"ol\"> {\n position?: ToastPosition;\n}\n\n/**\n * Renders the live toast stack from the module store. Fixed-positioned per\n * `position` (default `bottom-right`).\n *\n * Stacking mirrors Sonner: toasts pile up collapsed (the front three peeking,\n * each scaled + lifted by depth and clamped to the front toast's height) and\n * spread into a list on hover / keyboard focus — driven by the measured\n * `--front-height`/`--offset` vars and the `data-stack` CSS in `tokens.css`.\n * Each toast auto-dismisses after its `duration` (default 4000ms) via the same\n * animated `dismiss` as the close button (`data-state=\"closed\"` → slide out →\n * unmount on `transitionend`), and is swipe-to-dismiss (horizontal drag, TS-only\n * pointer physics). Single dark palette — no theme switching.\n */\nexport function Toaster({\n position = \"bottom-right\",\n className,\n ...props\n}: ToasterProps) {\n const [toasts, setToasts] = React.useState<Toast[]>([]);\n const [heights, setHeights] = React.useState<Map<number, number>>(new Map());\n const [paused, setPaused] = React.useState(false);\n\n React.useEffect(() => store.subscribe(setToasts), []);\n\n // Pause auto-dismiss while the stack is hovered or keyboard-focused (Sonner\n // does the same). pointerover/out + focusin/out bubble from the toasts, so they\n // fire even though the <ol> itself is pointer-events:none; the contains() guard\n // keeps it paused while moving *within* the stack.\n const onEnter = () => setPaused(true);\n const onLeave = (e: React.PointerEvent | React.FocusEvent) => {\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {\n setPaused(false);\n }\n };\n\n const reportHeight = React.useCallback((id: number, h: number) => {\n setHeights((prev) => {\n if (prev.get(id) === h) return prev;\n const next = new Map(prev);\n next.set(id, h);\n return next;\n });\n }, []);\n const removeHeight = React.useCallback((id: number) => {\n setHeights((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n\n const yPosition = position.startsWith(\"top\") ? \"top\" : \"bottom\";\n const ordered = [...toasts].reverse(); // newest first → front of the stack\n // front toast's height; until it's measured fall back to the tallest known one\n // so the back toasts don't clamp to 0 for a frame when a new toast arrives\n const known = [...heights.values()];\n const frontHeight = ordered[0]\n ? (heights.get(ordered[0].id) ?? (known.length ? Math.max(...known) : 0))\n : 0;\n\n // cumulative offset: heights of the toasts in front of this one + index * GAP\n let sum = 0;\n const offsets = ordered.map((t, i) => {\n const o = sum + i * GAP;\n sum += heights.get(t.id) ?? 0;\n return o;\n });\n\n return (\n <ol\n data-slot=\"toaster\"\n data-position={position}\n data-y-position={yPosition}\n data-stack=\"\"\n style={\n {\n \"--front-height\": `${frontHeight}px`,\n \"--gap\": `${GAP}px`,\n } as React.CSSProperties\n }\n className={cn(\n TOASTER_BASE,\n positionClasses[position],\n className,\n )}\n {...props}\n onPointerOver={onEnter}\n onPointerOut={onLeave}\n onFocus={onEnter}\n onBlur={onLeave}\n >\n {ordered.map((t, i) => (\n <ToastItem\n key={t.id}\n toast={t}\n index={i}\n total={ordered.length}\n offset={offsets[i]!}\n paused={paused}\n reportHeight={reportHeight}\n removeHeight={removeHeight}\n />\n ))}\n </ol>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,23 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../lib/cn.js";
4
+ function Spinner({ className, ...props }) {
5
+ return /* @__PURE__ */ jsx(
6
+ "svg",
7
+ {
8
+ role: "status",
9
+ "aria-label": "Loading",
10
+ className: cn("size-4 animate-spin", className),
11
+ viewBox: "0 0 24 24",
12
+ fill: "none",
13
+ stroke: "currentColor",
14
+ strokeWidth: 2,
15
+ ...props,
16
+ children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
17
+ }
18
+ );
19
+ }
20
+ export {
21
+ Spinner
22
+ };
23
+ //# sourceMappingURL=spinner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/spinner.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../lib/cn\";\n\nexport function Spinner({ className, ...props }: React.ComponentProps<\"svg\">) {\n return (\n <svg\n role=\"status\"\n aria-label=\"Loading\"\n className={cn(\"size-4 animate-spin\", className)}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n {...props}\n >\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n );\n}\n"],"mappings":"AAeM;AAfN,YAAY,WAAW;AACvB,SAAS,UAAU;AAEZ,SAAS,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAgC;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAW;AAAA,MACX,WAAW,GAAG,uBAAuB,SAAS;AAAA,MAC9C,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAa;AAAA,MACZ,GAAG;AAAA,MAEJ,8BAAC,UAAK,GAAE,+BAA8B;AAAA;AAAA,EACxC;AAEJ;","names":[]}
@@ -0,0 +1,145 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { accentTextClasses } from "../generated/accent.js";
6
+ import { buttonVariants } from "./button.js";
7
+ import { Logo } from "./logo.js";
8
+ function statusCtaClass(accent, variant) {
9
+ return cn(
10
+ buttonVariants({ variant, size: "lg", accent }),
11
+ "font-mono text-xs uppercase tracking-widest"
12
+ );
13
+ }
14
+ function ArrowLeftIcon({ className }) {
15
+ return /* @__PURE__ */ jsxs(
16
+ "svg",
17
+ {
18
+ className,
19
+ viewBox: "0 0 24 24",
20
+ fill: "none",
21
+ stroke: "currentColor",
22
+ strokeWidth: 2,
23
+ strokeLinecap: "round",
24
+ strokeLinejoin: "round",
25
+ "aria-hidden": true,
26
+ children: [
27
+ /* @__PURE__ */ jsx("path", { d: "m12 19-7-7 7-7" }),
28
+ /* @__PURE__ */ jsx("path", { d: "M19 12H5" })
29
+ ]
30
+ }
31
+ );
32
+ }
33
+ function StatusScreen({
34
+ accent,
35
+ eyebrow,
36
+ code,
37
+ headlineLead,
38
+ headlineAccent,
39
+ headlineTail = ".",
40
+ subtext,
41
+ links,
42
+ children,
43
+ linkComponent
44
+ }) {
45
+ const L = linkComponent ?? "a";
46
+ const accentText = accentTextClasses[accent];
47
+ return /* @__PURE__ */ jsxs("section", { className: "relative flex min-h-screen flex-col items-center justify-center overflow-hidden bg-background px-6 py-32 text-center", children: [
48
+ /* @__PURE__ */ jsx(
49
+ "div",
50
+ {
51
+ "aria-hidden": true,
52
+ className: cn(
53
+ "pointer-events-none absolute inset-0 opacity-[0.07] [background:radial-gradient(55%_45%_at_50%_30%,currentColor,transparent_70%)]",
54
+ accentText
55
+ )
56
+ }
57
+ ),
58
+ /* @__PURE__ */ jsxs("div", { className: "relative z-10 flex w-full max-w-2xl flex-col items-center", children: [
59
+ /* @__PURE__ */ jsx(Logo, { className: cn("mb-7 h-10 w-auto", accentText) }),
60
+ /* @__PURE__ */ jsx("p", { className: cn("mb-6 font-mono text-[11px] uppercase tracking-[0.34em]", accentText), children: eyebrow }),
61
+ /* @__PURE__ */ jsx("p", { className: cn("font-serif text-[110px] font-medium leading-[0.9] sm:text-[180px]", accentText), children: code }),
62
+ /* @__PURE__ */ jsxs("h1", { className: "mt-4 font-serif text-3xl font-light leading-tight text-ink sm:text-5xl", children: [
63
+ headlineLead,
64
+ /* @__PURE__ */ jsx("span", { className: cn("font-serif italic", accentText), children: headlineAccent }),
65
+ headlineTail
66
+ ] }),
67
+ /* @__PURE__ */ jsx("p", { className: "mx-auto mt-5 max-w-md text-sm leading-relaxed text-ink/60 sm:text-base", children: subtext }),
68
+ /* @__PURE__ */ jsxs("div", { className: "mt-9 flex flex-col items-center gap-3 sm:flex-row", children: [
69
+ children,
70
+ links?.map((link) => /* @__PURE__ */ jsxs(L, { href: link.href, className: statusCtaClass(accent, link.variant ?? "primary"), children: [
71
+ link.leadingArrow ? /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "mr-2 h-4 w-4" }) : null,
72
+ link.label
73
+ ] }, link.label))
74
+ ] })
75
+ ] })
76
+ ] });
77
+ }
78
+ function NotFound({ linkComponent = "a", homeHref = "/", contactHref = "/contact" }) {
79
+ return /* @__PURE__ */ jsx(
80
+ StatusScreen,
81
+ {
82
+ accent: "debug",
83
+ eyebrow: "Page not found",
84
+ code: "404",
85
+ headlineLead: "You've reached ",
86
+ headlineAccent: "open water",
87
+ subtext: "The page you're looking for has drifted off our coastline \u2014 moved, renamed, or never charted. Let's get you back to shore.",
88
+ linkComponent,
89
+ links: [
90
+ { label: "Back to home", href: homeHref, leadingArrow: true },
91
+ { label: "Contact the team", href: contactHref, variant: "outline" }
92
+ ]
93
+ }
94
+ );
95
+ }
96
+ function Forbidden({ linkComponent = "a", homeHref = "/", contactHref = "/contact" }) {
97
+ return /* @__PURE__ */ jsx(
98
+ StatusScreen,
99
+ {
100
+ accent: "warn",
101
+ eyebrow: "Access forbidden",
102
+ code: "403",
103
+ headlineLead: "This harbour is ",
104
+ headlineAccent: "private",
105
+ subtext: "You don't have the credentials to view this page. If you believe you should, our team can open the right doors.",
106
+ linkComponent,
107
+ links: [
108
+ { label: "Back to home", href: homeHref, leadingArrow: true },
109
+ { label: "Request access", href: contactHref, variant: "outline" }
110
+ ]
111
+ }
112
+ );
113
+ }
114
+ function ServerError({ linkComponent = "a", homeHref = "/", reset }) {
115
+ return /* @__PURE__ */ jsx(
116
+ StatusScreen,
117
+ {
118
+ accent: "error",
119
+ eyebrow: "Server error",
120
+ code: "500",
121
+ headlineLead: "Our systems are ",
122
+ headlineAccent: "recalibrating",
123
+ subtext: "Something broke on our end \u2014 not yours. We've been alerted and are restoring service. Please try again in a moment.",
124
+ linkComponent,
125
+ links: [{ label: "Back to home", href: homeHref, variant: "outline", leadingArrow: true }],
126
+ children: /* @__PURE__ */ jsx(
127
+ "button",
128
+ {
129
+ type: "button",
130
+ className: statusCtaClass("error", "primary"),
131
+ onClick: () => reset ? reset() : window.location.reload(),
132
+ children: "Try again"
133
+ }
134
+ )
135
+ }
136
+ );
137
+ }
138
+ export {
139
+ Forbidden,
140
+ NotFound,
141
+ ServerError,
142
+ StatusScreen,
143
+ statusCtaClass
144
+ };
145
+ //# sourceMappingURL=status-screen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/status-screen.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { accentTextClasses, type Accent } from \"../generated/accent\";\nimport { buttonVariants, type ButtonVariant } from \"./button\";\nimport { Logo } from \"./logo\";\n\n/**\n * The shared 404 / 403 / 500 status surface.\n *\n * `StatusScreen` is the generic shell; `NotFound` / `Forbidden` / `ServerError`\n * are the ready-made pages with their copy baked in — a host renders those with\n * its own hrefs. The mark is `Logo`'s, i.e. the consumer's. Links default to a\n * plain `<a>` (a full document load, which is what you want off an error page);\n * pass `linkComponent` (e.g. `next/link`) for soft navigation from a client\n * boundary.\n */\nexport type { Accent };\n\n/**\n * A status CTA is a Button at the page's accent: the canonical button string for\n * that variant and rung, then the mono/uppercase treatment the error pages wear.\n *\n * Exported because a host that renders its own action into `StatusScreen`'s\n * slot — a retry button wired to Next's `reset`, say — has to be able to match\n * the CTAs beside it.\n */\nexport function statusCtaClass(accent: Accent, variant: ButtonVariant) {\n return cn(\n buttonVariants({ variant, size: \"lg\", accent }),\n \"font-mono text-xs uppercase tracking-widest\",\n );\n}\n\n// lucide `arrow-left`, inlined so the kit keeps its zero-icon-dep footprint.\nfunction ArrowLeftIcon({ className }: { className?: string }) {\n return (\n <svg\n className={className}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"m12 19-7-7 7-7\" />\n <path d=\"M19 12H5\" />\n </svg>\n );\n}\n\nexport interface StatusLinkData {\n label: string;\n href: string;\n variant?: ButtonVariant;\n leadingArrow?: boolean;\n}\n\nexport interface StatusScreenProps {\n accent: Accent;\n eyebrow: string;\n code: string;\n headlineLead: string;\n headlineAccent: string;\n headlineTail?: string;\n subtext: string;\n links?: StatusLinkData[];\n /** Leading action slot, rendered before `links` (e.g. the 500 client retry). */\n children?: React.ReactNode;\n /** CTA element — `next/link` for soft nav; defaults to `<a>` (full load). */\n linkComponent?: React.ElementType;\n}\n\n/**\n * Shared skeleton for the 404 / 403 / 500 status pages: a centred hero with the\n * logo mark, a mono eyebrow, a giant Playfair code, a headline whose final clause\n * is an italic accent, supporting copy, and the CTAs. The accent threads through\n * all four marks. Actions render inline (from `links` data, plus an optional\n * `children` slot) — never passed as a JSX prop through a server boundary.\n */\nexport function StatusScreen({\n accent,\n eyebrow,\n code,\n headlineLead,\n headlineAccent,\n headlineTail = \".\",\n subtext,\n links,\n children,\n linkComponent,\n}: StatusScreenProps) {\n const L = linkComponent ?? \"a\";\n const accentText = accentTextClasses[accent];\n return (\n <section className=\"relative flex min-h-screen flex-col items-center justify-center overflow-hidden bg-background px-6 py-32 text-center\">\n <div\n aria-hidden\n className={cn(\n \"pointer-events-none absolute inset-0 opacity-[0.07] [background:radial-gradient(55%_45%_at_50%_30%,currentColor,transparent_70%)]\",\n accentText,\n )}\n />\n <div className=\"relative z-10 flex w-full max-w-2xl flex-col items-center\">\n <Logo className={cn(\"mb-7 h-10 w-auto\", accentText)} />\n <p className={cn(\"mb-6 font-mono text-[11px] uppercase tracking-[0.34em]\", accentText)}>{eyebrow}</p>\n <p className={cn(\"font-serif text-[110px] font-medium leading-[0.9] sm:text-[180px]\", accentText)}>{code}</p>\n <h1 className=\"mt-4 font-serif text-3xl font-light leading-tight text-ink sm:text-5xl\">\n {headlineLead}\n <span className={cn(\"font-serif italic\", accentText)}>{headlineAccent}</span>\n {headlineTail}\n </h1>\n <p className=\"mx-auto mt-5 max-w-md text-sm leading-relaxed text-ink/60 sm:text-base\">{subtext}</p>\n <div className=\"mt-9 flex flex-col items-center gap-3 sm:flex-row\">\n {children}\n {links?.map((link) => (\n <L key={link.label} href={link.href} className={statusCtaClass(accent, link.variant ?? \"primary\")}>\n {link.leadingArrow ? <ArrowLeftIcon className=\"mr-2 h-4 w-4\" /> : null}\n {link.label}\n </L>\n ))}\n </div>\n </div>\n </section>\n );\n}\n\nexport interface StatusPageProps {\n /** CTA element — `next/link` for soft nav; defaults to `<a>` (full load). */\n linkComponent?: React.ElementType;\n /** \"Back to home\" target — `/` for the landing, `/cabinet` for the cabinet. */\n homeHref?: string;\n /** Secondary CTA target (contact / request access). */\n contactHref?: string;\n}\n\n/** 404 — page not found. */\nexport function NotFound({ linkComponent = \"a\", homeHref = \"/\", contactHref = \"/contact\" }: StatusPageProps) {\n return (\n <StatusScreen\n accent=\"debug\"\n eyebrow=\"Page not found\"\n code=\"404\"\n headlineLead=\"You've reached \"\n headlineAccent=\"open water\"\n subtext=\"The page you're looking for has drifted off our coastline — moved, renamed, or never charted. Let's get you back to shore.\"\n linkComponent={linkComponent}\n links={[\n { label: \"Back to home\", href: homeHref, leadingArrow: true },\n { label: \"Contact the team\", href: contactHref, variant: \"outline\" },\n ]}\n />\n );\n}\n\n/** 403 — access forbidden. */\nexport function Forbidden({ linkComponent = \"a\", homeHref = \"/\", contactHref = \"/contact\" }: StatusPageProps) {\n return (\n <StatusScreen\n accent=\"warn\"\n eyebrow=\"Access forbidden\"\n code=\"403\"\n headlineLead=\"This harbour is \"\n headlineAccent=\"private\"\n subtext=\"You don't have the credentials to view this page. If you believe you should, our team can open the right doors.\"\n linkComponent={linkComponent}\n links={[\n { label: \"Back to home\", href: homeHref, leadingArrow: true },\n { label: \"Request access\", href: contactHref, variant: \"outline\" },\n ]}\n />\n );\n}\n\nexport interface ServerErrorProps {\n /** CTA element — `next/link` for soft nav; defaults to `<a>` (full load). */\n linkComponent?: React.ElementType;\n /** \"Back to home\" target — `/` for the landing, `/cabinet` for the cabinet. */\n homeHref?: string;\n /** Retry handler (e.g. Next's error-boundary `reset`); falls back to a reload. */\n reset?: () => void;\n}\n\n/** 500 — server error. The \"Try again\" button runs `reset` or reloads the page. */\nexport function ServerError({ linkComponent = \"a\", homeHref = \"/\", reset }: ServerErrorProps) {\n return (\n <StatusScreen\n accent=\"error\"\n eyebrow=\"Server error\"\n code=\"500\"\n headlineLead=\"Our systems are \"\n headlineAccent=\"recalibrating\"\n subtext=\"Something broke on our end — not yours. We've been alerted and are restoring service. Please try again in a moment.\"\n linkComponent={linkComponent}\n links={[{ label: \"Back to home\", href: homeHref, variant: \"outline\", leadingArrow: true }]}\n >\n <button\n type=\"button\"\n className={statusCtaClass(\"error\", \"primary\")}\n onClick={() => (reset ? reset() : window.location.reload())}\n >\n Try again\n </button>\n </StatusScreen>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,54 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { SWITCH_BASE, SWITCH_THUMB } from "../generated/switch.js";
6
+ import { useControllableState } from "../primitives/use-controllable-state.js";
7
+ import { useFieldControlId } from "./field-context.js";
8
+ function Switch({
9
+ className,
10
+ checked,
11
+ defaultChecked = false,
12
+ onCheckedChange,
13
+ onClick,
14
+ disabled,
15
+ id,
16
+ ...props
17
+ }) {
18
+ const [on, setOn] = useControllableState({
19
+ ...checked !== void 0 ? { value: checked } : {},
20
+ defaultValue: defaultChecked,
21
+ ...onCheckedChange ? { onChange: onCheckedChange } : {}
22
+ });
23
+ const state = on ? "checked" : "unchecked";
24
+ return /* @__PURE__ */ jsx(
25
+ "button",
26
+ {
27
+ type: "button",
28
+ role: "switch",
29
+ "data-slot": "switch",
30
+ "data-state": state,
31
+ "aria-checked": on,
32
+ disabled,
33
+ id: useFieldControlId(id),
34
+ className: cn(SWITCH_BASE, className),
35
+ onClick: (e) => {
36
+ onClick?.(e);
37
+ setOn(!on);
38
+ },
39
+ ...props,
40
+ children: /* @__PURE__ */ jsx(
41
+ "span",
42
+ {
43
+ "data-slot": "switch-thumb",
44
+ "data-state": state,
45
+ className: SWITCH_THUMB
46
+ }
47
+ )
48
+ }
49
+ );
50
+ }
51
+ export {
52
+ Switch
53
+ };
54
+ //# sourceMappingURL=switch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/switch.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { SWITCH_BASE, SWITCH_THUMB } from \"../generated/switch\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useFieldControlId } from \"./field-context\";\n\nexport interface SwitchProps\n extends Omit<React.ComponentProps<\"button\">, \"onChange\"> {\n checked?: boolean;\n defaultChecked?: boolean;\n onCheckedChange?: (checked: boolean) => void;\n}\n\nexport function Switch({\n className,\n checked,\n defaultChecked = false,\n onCheckedChange,\n onClick,\n disabled,\n id,\n ...props\n}: SwitchProps) {\n const [on, setOn] = useControllableState<boolean>({\n ...(checked !== undefined ? { value: checked } : {}),\n defaultValue: defaultChecked,\n ...(onCheckedChange ? { onChange: onCheckedChange } : {}),\n });\n const state = on ? \"checked\" : \"unchecked\";\n return (\n <button\n type=\"button\"\n role=\"switch\"\n data-slot=\"switch\"\n data-state={state}\n aria-checked={on}\n disabled={disabled}\n id={useFieldControlId(id)}\n className={cn(SWITCH_BASE, className)}\n onClick={(e) => {\n onClick?.(e);\n setOn(!on);\n }}\n {...props}\n >\n <span\n data-slot=\"switch-thumb\"\n data-state={state}\n className={SWITCH_THUMB}\n />\n </button>\n );\n}\n"],"mappings":";AA+CM;AA7CN,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,aAAa,oBAAoB;AAC1C,SAAS,4BAA4B;AACrC,SAAS,yBAAyB;AAS3B,SAAS,OAAO;AAAA,EACrB;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAgB;AACd,QAAM,CAAC,IAAI,KAAK,IAAI,qBAA8B;AAAA,IAChD,GAAI,YAAY,SAAY,EAAE,OAAO,QAAQ,IAAI,CAAC;AAAA,IAClD,cAAc;AAAA,IACd,GAAI,kBAAkB,EAAE,UAAU,gBAAgB,IAAI,CAAC;AAAA,EACzD,CAAC;AACD,QAAM,QAAQ,KAAK,YAAY;AAC/B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAK;AAAA,MACL,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,gBAAc;AAAA,MACd;AAAA,MACA,IAAI,kBAAkB,EAAE;AAAA,MACxB,WAAW,GAAG,aAAa,SAAS;AAAA,MACpC,SAAS,CAAC,MAAM;AACd,kBAAU,CAAC;AACX,cAAM,CAAC,EAAE;AAAA,MACX;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC;AAAA;AAAA,UACC,aAAU;AAAA,UACV,cAAY;AAAA,UACZ,WAAW;AAAA;AAAA,MACb;AAAA;AAAA,EACF;AAEJ;","names":[]}