@financedistrict/apps-ui 0.1.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 (136) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +119 -0
  3. package/dist/Radio-BYWz1n7p.d.cts +32 -0
  4. package/dist/Radio-BYWz1n7p.d.ts +32 -0
  5. package/dist/SelectTrigger-By0iPBI3.d.cts +78 -0
  6. package/dist/SelectTrigger-By0iPBI3.d.ts +78 -0
  7. package/dist/accordion.cjs +322 -0
  8. package/dist/accordion.d.cts +156 -0
  9. package/dist/accordion.d.ts +156 -0
  10. package/dist/accordion.js +2 -0
  11. package/dist/apps-ui.umd.js +1 -0
  12. package/dist/button.cjs +366 -0
  13. package/dist/button.d.cts +52 -0
  14. package/dist/button.d.ts +52 -0
  15. package/dist/button.js +3 -0
  16. package/dist/checkbox.cjs +74 -0
  17. package/dist/checkbox.d.cts +63 -0
  18. package/dist/checkbox.d.ts +63 -0
  19. package/dist/checkbox.js +2 -0
  20. package/dist/chunk-3RU7JMFE.js +27 -0
  21. package/dist/chunk-5TWRK4NW.js +783 -0
  22. package/dist/chunk-7EOSDFS3.js +9 -0
  23. package/dist/chunk-AIQ4UOGK.js +142 -0
  24. package/dist/chunk-B3MMGTPV.js +200 -0
  25. package/dist/chunk-DAHQYLUI.js +146 -0
  26. package/dist/chunk-DGBXADSS.js +1 -0
  27. package/dist/chunk-E6KPNTWW.js +295 -0
  28. package/dist/chunk-EOAB6G2D.js +149 -0
  29. package/dist/chunk-FAOVKDVM.js +32 -0
  30. package/dist/chunk-FV76Z4Z2.js +78 -0
  31. package/dist/chunk-GLTLBUZ7.js +285 -0
  32. package/dist/chunk-GTZFV43T.js +154 -0
  33. package/dist/chunk-HRGZNL4A.js +88 -0
  34. package/dist/chunk-IEVN3J2F.js +98 -0
  35. package/dist/chunk-IRS3T6LF.js +352 -0
  36. package/dist/chunk-IYYHCQXG.js +146 -0
  37. package/dist/chunk-JIXW3455.js +89 -0
  38. package/dist/chunk-N4VAP2GB.js +77 -0
  39. package/dist/chunk-SJ2ZRYVV.js +167 -0
  40. package/dist/chunk-SNU4YXOR.js +66 -0
  41. package/dist/chunk-TT6JQATV.js +99 -0
  42. package/dist/chunk-UPQ3ZPCO.js +66 -0
  43. package/dist/chunk-UVUSVFOH.js +264 -0
  44. package/dist/chunk-WJFPGGJB.js +255 -0
  45. package/dist/chunk-WXEBQA7A.js +371 -0
  46. package/dist/chunk-Y2O4YYZQ.js +184 -0
  47. package/dist/cut-corner.cjs +111 -0
  48. package/dist/cut-corner.d.cts +49 -0
  49. package/dist/cut-corner.d.ts +49 -0
  50. package/dist/cut-corner.js +1 -0
  51. package/dist/dialog.cjs +441 -0
  52. package/dist/dialog.d.cts +240 -0
  53. package/dist/dialog.d.ts +240 -0
  54. package/dist/dialog.js +3 -0
  55. package/dist/feature-card.cjs +182 -0
  56. package/dist/feature-card.d.cts +54 -0
  57. package/dist/feature-card.d.ts +54 -0
  58. package/dist/feature-card.js +2 -0
  59. package/dist/filter.cjs +873 -0
  60. package/dist/filter.d.cts +129 -0
  61. package/dist/filter.d.ts +129 -0
  62. package/dist/filter.js +6 -0
  63. package/dist/index.cjs +4583 -0
  64. package/dist/index.d.cts +29 -0
  65. package/dist/index.d.ts +29 -0
  66. package/dist/index.js +27 -0
  67. package/dist/inline-select.cjs +856 -0
  68. package/dist/inline-select.d.cts +92 -0
  69. package/dist/inline-select.d.ts +92 -0
  70. package/dist/inline-select.js +6 -0
  71. package/dist/input.cjs +263 -0
  72. package/dist/input.d.cts +157 -0
  73. package/dist/input.d.ts +157 -0
  74. package/dist/input.js +4 -0
  75. package/dist/panel.cjs +185 -0
  76. package/dist/panel.d.cts +103 -0
  77. package/dist/panel.d.ts +103 -0
  78. package/dist/panel.js +2 -0
  79. package/dist/property-grid.cjs +110 -0
  80. package/dist/property-grid.d.cts +56 -0
  81. package/dist/property-grid.d.ts +56 -0
  82. package/dist/property-grid.js +2 -0
  83. package/dist/radio.cjs +74 -0
  84. package/dist/radio.d.cts +39 -0
  85. package/dist/radio.d.ts +39 -0
  86. package/dist/radio.js +2 -0
  87. package/dist/scrollbar.cjs +34 -0
  88. package/dist/scrollbar.d.cts +35 -0
  89. package/dist/scrollbar.d.ts +35 -0
  90. package/dist/scrollbar.js +1 -0
  91. package/dist/search-bar.cjs +408 -0
  92. package/dist/search-bar.d.cts +42 -0
  93. package/dist/search-bar.d.ts +42 -0
  94. package/dist/search-bar.js +4 -0
  95. package/dist/select.cjs +998 -0
  96. package/dist/select.d.cts +357 -0
  97. package/dist/select.d.ts +357 -0
  98. package/dist/select.js +5 -0
  99. package/dist/selectable-feature-card.cjs +159 -0
  100. package/dist/selectable-feature-card.d.cts +74 -0
  101. package/dist/selectable-feature-card.d.ts +74 -0
  102. package/dist/selectable-feature-card.js +3 -0
  103. package/dist/sidebar-item.cjs +175 -0
  104. package/dist/sidebar-item.d.cts +75 -0
  105. package/dist/sidebar-item.d.ts +75 -0
  106. package/dist/sidebar-item.js +2 -0
  107. package/dist/switch.cjs +86 -0
  108. package/dist/switch.d.cts +72 -0
  109. package/dist/switch.d.ts +72 -0
  110. package/dist/switch.js +2 -0
  111. package/dist/table-cell.cjs +196 -0
  112. package/dist/table-cell.d.cts +76 -0
  113. package/dist/table-cell.d.ts +76 -0
  114. package/dist/table-cell.js +2 -0
  115. package/dist/table-title.cjs +34 -0
  116. package/dist/table-title.d.cts +16 -0
  117. package/dist/table-title.d.ts +16 -0
  118. package/dist/table-title.js +2 -0
  119. package/dist/tabs.cjs +299 -0
  120. package/dist/tabs.d.cts +116 -0
  121. package/dist/tabs.d.ts +116 -0
  122. package/dist/tabs.js +2 -0
  123. package/dist/toast.cjs +87 -0
  124. package/dist/toast.d.cts +48 -0
  125. package/dist/toast.d.ts +48 -0
  126. package/dist/toast.js +2 -0
  127. package/dist/token-input.cjs +415 -0
  128. package/dist/token-input.d.cts +90 -0
  129. package/dist/token-input.d.ts +90 -0
  130. package/dist/token-input.js +3 -0
  131. package/dist/tooltip.cjs +384 -0
  132. package/dist/tooltip.d.cts +142 -0
  133. package/dist/tooltip.d.ts +142 -0
  134. package/dist/tooltip.js +2 -0
  135. package/package.json +200 -0
  136. package/src/fonts.css +33 -0
@@ -0,0 +1,441 @@
1
+ 'use strict';
2
+
3
+ var React2 = require('react');
4
+ var reactDom = require('react-dom');
5
+ var react = require('@phosphor-icons/react');
6
+ var clsx = require('clsx');
7
+ var tailwindMerge = require('tailwind-merge');
8
+ var classVarianceAuthority = require('class-variance-authority');
9
+ var jsxRuntime = require('react/jsx-runtime');
10
+
11
+ function _interopNamespace(e) {
12
+ if (e && e.__esModule) return e;
13
+ var n = Object.create(null);
14
+ if (e) {
15
+ Object.keys(e).forEach(function (k) {
16
+ if (k !== 'default') {
17
+ var d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: function () { return e[k]; }
21
+ });
22
+ }
23
+ });
24
+ }
25
+ n.default = e;
26
+ return Object.freeze(n);
27
+ }
28
+
29
+ var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
30
+
31
+ // src/dialog/Dialog.tsx
32
+ function cn(...inputs) {
33
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
34
+ }
35
+ var scrollbarVariants = classVarianceAuthority.cva(
36
+ [
37
+ // Firefox (approximate): thin themed scrollbar over a transparent track.
38
+ "[scrollbar-width:thin]",
39
+ "[scrollbar-color:var(--color-card-foreground-muted)_transparent]",
40
+ // WebKit gutter — 12px, both axes so the utility is axis-agnostic.
41
+ "[&::-webkit-scrollbar]:w-3 [&::-webkit-scrollbar]:h-3",
42
+ // WebKit thumb — 4px pill: a 4px transparent border + content-box clip
43
+ // leaves a 4px-wide fill centred in the 12px gutter.
44
+ "[&::-webkit-scrollbar-thumb]:rounded-full",
45
+ "[&::-webkit-scrollbar-thumb]:border-4 [&::-webkit-scrollbar-thumb]:border-solid [&::-webkit-scrollbar-thumb]:border-transparent",
46
+ "[&::-webkit-scrollbar-thumb]:bg-clip-content",
47
+ "[&::-webkit-scrollbar-thumb]:bg-card-foreground-muted",
48
+ // WebKit track — transparent by default (stroke is the `stroke` variant).
49
+ "[&::-webkit-scrollbar-track]:bg-transparent"
50
+ ],
51
+ {
52
+ variants: {
53
+ stroke: {
54
+ // 1px leading-edge track stroke (Figma `stroke`, on by default).
55
+ true: "[&::-webkit-scrollbar-track]:border-l [&::-webkit-scrollbar-track]:border-card-border",
56
+ false: ""
57
+ }
58
+ },
59
+ defaultVariants: { stroke: true }
60
+ }
61
+ );
62
+ var FOCUSABLE_SELECTOR = [
63
+ "a[href]",
64
+ "button:not([disabled])",
65
+ "textarea:not([disabled])",
66
+ "input:not([disabled])",
67
+ "select:not([disabled])",
68
+ '[tabindex]:not([tabindex="-1"])'
69
+ ].join(",");
70
+ function getFocusableElements(container) {
71
+ return Array.from(
72
+ container.querySelectorAll(FOCUSABLE_SELECTOR)
73
+ );
74
+ }
75
+ function useFocusTrap({
76
+ active,
77
+ containerRef
78
+ }) {
79
+ React2__namespace.useEffect(() => {
80
+ if (!active) return;
81
+ const container = containerRef.current;
82
+ if (!container) return;
83
+ const previouslyFocused = document.activeElement;
84
+ const initial = getFocusableElements(container)[0] ?? container;
85
+ initial.focus();
86
+ const body = document.body;
87
+ const previousOverflow = body.style.overflow;
88
+ body.style.overflow = "hidden";
89
+ const handleKeyDown = (event) => {
90
+ if (event.key !== "Tab") return;
91
+ const focusables = getFocusableElements(container);
92
+ if (focusables.length === 0) {
93
+ event.preventDefault();
94
+ container.focus();
95
+ return;
96
+ }
97
+ const first = focusables[0];
98
+ const last = focusables[focusables.length - 1];
99
+ const activeEl = document.activeElement;
100
+ if (event.shiftKey) {
101
+ if (activeEl === first || !container.contains(activeEl)) {
102
+ event.preventDefault();
103
+ last.focus();
104
+ }
105
+ } else if (activeEl === last || !container.contains(activeEl)) {
106
+ event.preventDefault();
107
+ first.focus();
108
+ }
109
+ };
110
+ document.addEventListener("keydown", handleKeyDown, true);
111
+ return () => {
112
+ document.removeEventListener("keydown", handleKeyDown, true);
113
+ body.style.overflow = previousOverflow;
114
+ previouslyFocused?.focus?.();
115
+ };
116
+ }, [active, containerRef]);
117
+ }
118
+ var dialogOverlayVariants = classVarianceAuthority.cva([
119
+ "fixed inset-0 z-50 flex items-center justify-center p-4",
120
+ "bg-overlay"
121
+ ]);
122
+ var dialogContentVariants = classVarianceAuthority.cva([
123
+ "relative flex w-[438px] max-w-[calc(100vw-2rem)] max-h-[calc(100vh-2rem)] flex-col gap-6",
124
+ "rounded-md border border-border bg-card-background p-6 shadow-s",
125
+ // Programmatic-focus fallback target (no visible ring when focused as a last
126
+ // resort by the focus trap — the card is not an interactive control).
127
+ "outline-none"
128
+ ]);
129
+ var dialogHeaderVariants = classVarianceAuthority.cva([
130
+ "flex shrink-0 flex-col items-start gap-4"
131
+ ]);
132
+ var dialogHeaderIconVariants = classVarianceAuthority.cva([
133
+ "flex items-center text-foreground [&_svg]:size-8 [&_svg]:shrink-0"
134
+ ]);
135
+ var dialogHeaderTextVariants = classVarianceAuthority.cva(["flex w-full flex-col gap-2"]);
136
+ var dialogCloseButtonVariants = classVarianceAuthority.cva([
137
+ "absolute right-6 top-6 flex cursor-pointer items-center justify-center rounded-xs",
138
+ "text-foreground-muted transition-colors hover:text-foreground",
139
+ "[&_svg]:size-4 [&_svg]:shrink-0",
140
+ // TW4 needs `outline-solid` to render a solid ring (a bare `outline` won't).
141
+ "outline-none focus-visible:text-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus"
142
+ ]);
143
+ var dialogBodyVariants = classVarianceAuthority.cva([
144
+ "min-h-0 flex-1 overflow-y-auto text-foreground"
145
+ ]);
146
+ var dialogFooterVariants = classVarianceAuthority.cva([
147
+ "flex w-full shrink-0 gap-2 [&>*]:flex-1 [&_button]:w-full"
148
+ ]);
149
+ var DialogContext = React2__namespace.createContext(null);
150
+ function useDialogContext(part) {
151
+ const context = React2__namespace.useContext(DialogContext);
152
+ if (context === null) {
153
+ throw new Error(`${part} must be used within <Dialog.Root>`);
154
+ }
155
+ return context;
156
+ }
157
+ function DialogRoot({
158
+ open: openProp,
159
+ defaultOpen = false,
160
+ onOpenChange,
161
+ dismissable = true,
162
+ children
163
+ }) {
164
+ const [uncontrolledOpen, setUncontrolledOpen] = React2__namespace.useState(defaultOpen);
165
+ const [hasDescription, setHasDescription] = React2__namespace.useState(false);
166
+ const isControlled = openProp !== void 0;
167
+ const open = isControlled ? openProp : uncontrolledOpen;
168
+ const reactId = React2__namespace.useId();
169
+ const titleId = `${reactId}-title`;
170
+ const descriptionId = `${reactId}-description`;
171
+ const setOpen = React2__namespace.useCallback(
172
+ (next) => {
173
+ if (!isControlled) setUncontrolledOpen(next);
174
+ onOpenChange?.(next);
175
+ },
176
+ [isControlled, onOpenChange]
177
+ );
178
+ const value = {
179
+ open,
180
+ setOpen,
181
+ dismissable,
182
+ titleId,
183
+ descriptionId,
184
+ hasDescription,
185
+ setHasDescription
186
+ };
187
+ return /* @__PURE__ */ jsxRuntime.jsx(DialogContext.Provider, { value, children });
188
+ }
189
+ function setRef(ref, node) {
190
+ if (typeof ref === "function") ref(node);
191
+ else if (ref) ref.current = node;
192
+ }
193
+ function TriggerLike({
194
+ asChild = false,
195
+ onActivate,
196
+ dataSlot,
197
+ children,
198
+ className,
199
+ type,
200
+ ...props
201
+ }) {
202
+ if (asChild) {
203
+ if (!React2__namespace.isValidElement(children)) {
204
+ throw new Error(
205
+ `${dataSlot}: \`asChild\` requires a single React element child`
206
+ );
207
+ }
208
+ const child = children;
209
+ const childProps = child.props;
210
+ const childOnClick = childProps.onClick;
211
+ return React2__namespace.cloneElement(child, {
212
+ ...props,
213
+ ...childProps,
214
+ "data-slot": dataSlot,
215
+ onClick: (event) => {
216
+ childOnClick?.(event);
217
+ if (!event.defaultPrevented) onActivate();
218
+ },
219
+ className: cn(className, childProps.className),
220
+ ref: (node) => {
221
+ setRef(childProps.ref, node);
222
+ }
223
+ });
224
+ }
225
+ return /* @__PURE__ */ jsxRuntime.jsx(
226
+ "button",
227
+ {
228
+ type: type ?? "button",
229
+ "data-slot": dataSlot,
230
+ className,
231
+ onClick: () => onActivate(),
232
+ ...props,
233
+ children
234
+ }
235
+ );
236
+ }
237
+ function DialogTrigger({ asChild = false, ...props }) {
238
+ const { open, setOpen } = useDialogContext("Dialog.Trigger");
239
+ return /* @__PURE__ */ jsxRuntime.jsx(
240
+ TriggerLike,
241
+ {
242
+ asChild,
243
+ dataSlot: "dialog-trigger",
244
+ onActivate: () => setOpen(true),
245
+ "aria-haspopup": "dialog",
246
+ "aria-expanded": open,
247
+ ...props
248
+ }
249
+ );
250
+ }
251
+ function DialogClose({ asChild = false, ...props }) {
252
+ const { setOpen } = useDialogContext("Dialog.Close");
253
+ return /* @__PURE__ */ jsxRuntime.jsx(
254
+ TriggerLike,
255
+ {
256
+ asChild,
257
+ dataSlot: "dialog-close",
258
+ onActivate: () => setOpen(false),
259
+ ...props
260
+ }
261
+ );
262
+ }
263
+ function DialogContent({
264
+ className,
265
+ children,
266
+ ref,
267
+ onKeyDown,
268
+ ...props
269
+ }) {
270
+ const { open, setOpen, dismissable, titleId, descriptionId, hasDescription } = useDialogContext("Dialog.Content");
271
+ const cardRef = React2__namespace.useRef(null);
272
+ useFocusTrap({ active: open, containerRef: cardRef });
273
+ React2__namespace.useEffect(() => {
274
+ if (!open || !dismissable) return;
275
+ const handleEscape = (event) => {
276
+ if (event.key === "Escape") {
277
+ event.stopPropagation();
278
+ setOpen(false);
279
+ }
280
+ };
281
+ document.addEventListener("keydown", handleEscape);
282
+ return () => document.removeEventListener("keydown", handleEscape);
283
+ }, [open, dismissable, setOpen]);
284
+ if (!open) return null;
285
+ const setCardRef = (node) => {
286
+ cardRef.current = node;
287
+ setRef(ref, node);
288
+ };
289
+ return reactDom.createPortal(
290
+ /* @__PURE__ */ jsxRuntime.jsx(
291
+ "div",
292
+ {
293
+ "data-slot": "dialog-overlay",
294
+ className: dialogOverlayVariants(),
295
+ onClick: (event) => {
296
+ if (dismissable && event.target === event.currentTarget) {
297
+ setOpen(false);
298
+ }
299
+ },
300
+ children: /* @__PURE__ */ jsxRuntime.jsx(
301
+ "div",
302
+ {
303
+ ref: setCardRef,
304
+ role: "dialog",
305
+ "aria-modal": "true",
306
+ "aria-labelledby": titleId,
307
+ "aria-describedby": hasDescription ? descriptionId : void 0,
308
+ tabIndex: -1,
309
+ "data-slot": "dialog-content",
310
+ className: cn(dialogContentVariants(), className),
311
+ onKeyDown,
312
+ ...props,
313
+ children
314
+ }
315
+ )
316
+ }
317
+ ),
318
+ document.body
319
+ );
320
+ }
321
+ function DialogHeader({
322
+ icon,
323
+ title,
324
+ description,
325
+ showClose = true,
326
+ className,
327
+ ref,
328
+ ...props
329
+ }) {
330
+ const { titleId, descriptionId, setHasDescription, setOpen } = useDialogContext("Dialog.Header");
331
+ const hasDescription = description != null;
332
+ React2__namespace.useEffect(() => {
333
+ if (!hasDescription) return;
334
+ setHasDescription(true);
335
+ return () => setHasDescription(false);
336
+ }, [hasDescription, setHasDescription]);
337
+ return /* @__PURE__ */ jsxRuntime.jsxs(
338
+ "div",
339
+ {
340
+ ref,
341
+ "data-slot": "dialog-header",
342
+ className: cn(dialogHeaderVariants(), className),
343
+ ...props,
344
+ children: [
345
+ icon != null && /* @__PURE__ */ jsxRuntime.jsx(
346
+ "span",
347
+ {
348
+ "data-slot": "dialog-header-icon",
349
+ "aria-hidden": "true",
350
+ className: dialogHeaderIconVariants(),
351
+ children: /* @__PURE__ */ jsxRuntime.jsx(react.IconContext.Provider, { value: { weight: "fill" }, children: icon })
352
+ }
353
+ ),
354
+ /* @__PURE__ */ jsxRuntime.jsxs(
355
+ "div",
356
+ {
357
+ className: cn(
358
+ dialogHeaderTextVariants(),
359
+ // Reserve right space for the corner X only when it would collide: an
360
+ // icon-less header puts the title on the X's row. With an icon the
361
+ // title sits below the X, so no reservation is needed.
362
+ showClose && icon == null && "pr-8"
363
+ ),
364
+ children: [
365
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: titleId, className: "display-04 text-foreground", children: title }),
366
+ hasDescription && /* @__PURE__ */ jsxRuntime.jsx("p", { id: descriptionId, className: "body-03 text-foreground-muted", children: description })
367
+ ]
368
+ }
369
+ ),
370
+ showClose && /* @__PURE__ */ jsxRuntime.jsx(
371
+ "button",
372
+ {
373
+ type: "button",
374
+ "data-slot": "dialog-header-close",
375
+ "aria-label": "Close",
376
+ className: dialogCloseButtonVariants(),
377
+ onClick: () => setOpen(false),
378
+ children: /* @__PURE__ */ jsxRuntime.jsx(react.XIcon, { weight: "regular" })
379
+ }
380
+ )
381
+ ]
382
+ }
383
+ );
384
+ }
385
+ function DialogBody({ className, children, ref, ...props }) {
386
+ return /* @__PURE__ */ jsxRuntime.jsx(
387
+ "div",
388
+ {
389
+ ref,
390
+ "data-slot": "dialog-body",
391
+ className: cn(dialogBodyVariants(), scrollbarVariants(), className),
392
+ ...props,
393
+ children
394
+ }
395
+ );
396
+ }
397
+ function DialogFooter({
398
+ className,
399
+ children,
400
+ ref,
401
+ ...props
402
+ }) {
403
+ return /* @__PURE__ */ jsxRuntime.jsx(
404
+ "div",
405
+ {
406
+ ref,
407
+ "data-slot": "dialog-footer",
408
+ className: cn(dialogFooterVariants(), className),
409
+ ...props,
410
+ children
411
+ }
412
+ );
413
+ }
414
+ var Dialog = {
415
+ Root: DialogRoot,
416
+ Trigger: DialogTrigger,
417
+ Content: DialogContent,
418
+ Header: DialogHeader,
419
+ Body: DialogBody,
420
+ Footer: DialogFooter,
421
+ Close: DialogClose
422
+ };
423
+
424
+ exports.Dialog = Dialog;
425
+ exports.DialogBody = DialogBody;
426
+ exports.DialogClose = DialogClose;
427
+ exports.DialogContent = DialogContent;
428
+ exports.DialogFooter = DialogFooter;
429
+ exports.DialogHeader = DialogHeader;
430
+ exports.DialogRoot = DialogRoot;
431
+ exports.DialogTrigger = DialogTrigger;
432
+ exports.dialogBodyVariants = dialogBodyVariants;
433
+ exports.dialogCloseButtonVariants = dialogCloseButtonVariants;
434
+ exports.dialogContentVariants = dialogContentVariants;
435
+ exports.dialogFooterVariants = dialogFooterVariants;
436
+ exports.dialogHeaderIconVariants = dialogHeaderIconVariants;
437
+ exports.dialogHeaderTextVariants = dialogHeaderTextVariants;
438
+ exports.dialogHeaderVariants = dialogHeaderVariants;
439
+ exports.dialogOverlayVariants = dialogOverlayVariants;
440
+ exports.getFocusableElements = getFocusableElements;
441
+ exports.useFocusTrap = useFocusTrap;
@@ -0,0 +1,240 @@
1
+ import * as React from 'react';
2
+ import * as class_variance_authority_types from 'class-variance-authority/types';
3
+
4
+ interface DialogRootProps {
5
+ /** Controlled open state. Pair with `onOpenChange`. Omit for uncontrolled. */
6
+ open?: boolean;
7
+ /** Initial open state when uncontrolled. Ignored if `open` is provided. */
8
+ defaultOpen?: boolean;
9
+ /** Called with the next open state from trigger/close/Escape/backdrop. */
10
+ onOpenChange?: (open: boolean) => void;
11
+ /**
12
+ * Whether Escape and a backdrop click dismiss the dialog. `false` forces the
13
+ * user through an explicit control (a footer button / the header X, which
14
+ * always close). Default `true`.
15
+ */
16
+ dismissable?: boolean;
17
+ children?: React.ReactNode;
18
+ }
19
+ /**
20
+ * State owner + context provider for the compound `Dialog`. Renders no DOM of
21
+ * its own. Controlled via `open`/`onOpenChange`, or uncontrolled via
22
+ * `defaultOpen`. Generates the `aria-labelledby`/`aria-describedby` ids that
23
+ * `Dialog.Header` and `Dialog.Content` share, so the wiring cannot drift.
24
+ */
25
+ declare function DialogRoot({ open: openProp, defaultOpen, onOpenChange, dismissable, children, }: DialogRootProps): React.JSX.Element;
26
+ interface TriggerLikeProps extends Omit<React.ComponentProps<"button">, "onClick"> {
27
+ /** Render the single child element as the root instead of a `<button>`. */
28
+ asChild?: boolean;
29
+ }
30
+ interface DialogTriggerProps extends TriggerLikeProps {
31
+ }
32
+ /**
33
+ * Optional button that opens the dialog. `asChild` adopts a passed element
34
+ * (e.g. `<Button>`). Wires `aria-haspopup="dialog"` + `aria-expanded`. Skip it
35
+ * for pure controlled usage.
36
+ */
37
+ declare function DialogTrigger({ asChild, ...props }: DialogTriggerProps): React.JSX.Element;
38
+ interface DialogCloseProps extends TriggerLikeProps {
39
+ }
40
+ /**
41
+ * Optional button that closes the dialog (e.g. wrapping a footer Cancel
42
+ * button). `asChild` adopts a passed element. Always closes, independent of
43
+ * `dismissable` (that only gates Escape + backdrop).
44
+ */
45
+ declare function DialogClose({ asChild, ...props }: DialogCloseProps): React.JSX.Element;
46
+ interface DialogContentProps extends React.ComponentProps<"div"> {
47
+ }
48
+ /**
49
+ * The portaled overlay layer — mounts via `createPortal` → `document.body`
50
+ * (escaping `overflow`/stacking ancestors, like `SelectListbox`) only while
51
+ * open. Renders the backdrop + the centred card, and owns the dialog semantics
52
+ * (`role="dialog"`, `aria-modal`, `aria-labelledby`/`aria-describedby`), the
53
+ * focus trap + return + scroll lock (`useFocusTrap`), Escape, and backdrop
54
+ * dismiss. Forwards `ref` to the card element.
55
+ */
56
+ declare function DialogContent({ className, children, ref, onKeyDown, ...props }: DialogContentProps): React.ReactPortal | null;
57
+ interface DialogHeaderProps extends Omit<React.ComponentProps<"div">, "title"> {
58
+ /**
59
+ * Decorative 32px icon (rendered `aria-hidden`). Hidden when omitted.
60
+ *
61
+ * **Rule: use a filled icon here.** The slot defaults Phosphor icons to
62
+ * `weight="fill"` via `IconContext`, so a plain `<SomeIcon />` renders filled
63
+ * with no per-call weight needed — matching the dialog's visual language. An
64
+ * explicit `weight` on the passed icon still wins, but line/regular weights are
65
+ * off-pattern for this slot. The top-right close (X) button is the sole
66
+ * exception and stays a regular-weight line icon.
67
+ */
68
+ icon?: React.ReactNode;
69
+ /** Title — becomes the dialog's `aria-labelledby` target (`display-04`). */
70
+ title: React.ReactNode;
71
+ /** Optional description — becomes `aria-describedby` (`body-03`, muted). */
72
+ description?: React.ReactNode;
73
+ /**
74
+ * Show the X close button, positioned against the card at 24px from its top and
75
+ * right (absolute — independent of the icon). Shown by **default**; pass
76
+ * `false` to hide it. Always closes, independent of `dismissable`.
77
+ */
78
+ showClose?: boolean;
79
+ }
80
+ /**
81
+ * Header block: an optional decorative icon, the title (registers the
82
+ * `aria-labelledby` id), an optional description (registers `aria-describedby`),
83
+ * and an optional X close button. Ids come from `Dialog.Root` context so the
84
+ * ARIA wiring stays in lock-step with `Dialog.Content`.
85
+ */
86
+ declare function DialogHeader({ icon, title, description, showClose, className, ref, ...props }: DialogHeaderProps): React.JSX.Element;
87
+ interface DialogBodyProps extends React.ComponentProps<"div"> {
88
+ }
89
+ /**
90
+ * Flexible content slot — inputs or any composition. Sizes to content and, in
91
+ * the card's bounded flex column, becomes the internal scroll region so the
92
+ * dialog never exceeds the viewport (custom scrollbar via `scrollbarVariants`).
93
+ */
94
+ declare function DialogBody({ className, children, ref, ...props }: DialogBodyProps): React.JSX.Element;
95
+ interface DialogFooterProps extends React.ComponentProps<"div"> {
96
+ }
97
+ /**
98
+ * Action-row slot — a full-width flex row (`gap-2`) whose children stretch to
99
+ * equal width (Figma's two-button layout). Apps pass `Button`s; a destructive
100
+ * dialog uses `Button variation="destructive"` here.
101
+ */
102
+ declare function DialogFooter({ className, children, ref, ...props }: DialogFooterProps): React.JSX.Element;
103
+ /**
104
+ * Compound modal dialog. Assemble from its namespaced parts:
105
+ *
106
+ * ```tsx
107
+ * <Dialog.Root open={open} onOpenChange={setOpen}>
108
+ * <Dialog.Trigger asChild><Button>Open</Button></Dialog.Trigger>
109
+ * <Dialog.Content>
110
+ * <Dialog.Header icon={<Icon />} title="Title" description="…" showClose />
111
+ * <Dialog.Body>{/* content *\/}</Dialog.Body>
112
+ * <Dialog.Footer>
113
+ * <Dialog.Close asChild><Button variation="secondary">Cancel</Button></Dialog.Close>
114
+ * <Button>Confirm</Button>
115
+ * </Dialog.Footer>
116
+ * </Dialog.Content>
117
+ * </Dialog.Root>
118
+ * ```
119
+ */
120
+ declare const Dialog: {
121
+ Root: typeof DialogRoot;
122
+ Trigger: typeof DialogTrigger;
123
+ Content: typeof DialogContent;
124
+ Header: typeof DialogHeader;
125
+ Body: typeof DialogBody;
126
+ Footer: typeof DialogFooter;
127
+ Close: typeof DialogClose;
128
+ };
129
+
130
+ /**
131
+ * All tabbable descendants of `container`, in DOM order.
132
+ *
133
+ * Deliberately does NOT filter by visibility (`offsetParent`/rects): jsdom
134
+ * performs no layout, so a visibility filter would drop every element under
135
+ * test. A modal's card rarely contains hidden-but-matching focusables, and the
136
+ * trap only ever cycles among what it returns, so the DOM-order set is
137
+ * sufficient. Kept pure (no React) so it is unit-testable in isolation.
138
+ */
139
+ declare function getFocusableElements(container: HTMLElement): HTMLElement[];
140
+ interface UseFocusTrapOptions {
141
+ /** Whether the trap is engaged (the dialog is open). */
142
+ active: boolean;
143
+ /** Ref to the element that focus is confined within (the dialog card). */
144
+ containerRef: React.RefObject<HTMLElement | null>;
145
+ }
146
+ /**
147
+ * Modal focus machinery for `Dialog.Content` — the piece ADR-0013 #2 explicitly
148
+ * deferred for `Select` (which kept focus on its trigger via
149
+ * `aria-activedescendant`). Dialog is a true modal, so on open it:
150
+ *
151
+ * 1. remembers the element that had focus (typically the trigger),
152
+ * 2. moves focus into the container (first focusable, else the container
153
+ * itself — give it `tabIndex={-1}` for that fallback),
154
+ * 3. locks body scroll, and
155
+ * 4. traps `Tab` / `Shift+Tab` so focus cycles within the container and cannot
156
+ * escape to the page behind the backdrop.
157
+ *
158
+ * On close/unmount it releases the scroll lock and restores focus to the
159
+ * remembered element. Dependency-free per ADR-0013 (no focus-trap library).
160
+ */
161
+ declare function useFocusTrap({ active, containerRef, }: UseFocusTrapOptions): void;
162
+
163
+ /**
164
+ * Dialog styling — the Figma `Dialog` card (nodes `4985:12074` / `4988:1882`)
165
+ * plus the overlay plumbing the library owns. Binds ONLY to the semantic token
166
+ * layer (never primitives, never the mode plumbing); theme switching is
167
+ * automatic via the `.dark` class, so no `dark:` variants are needed.
168
+ *
169
+ * Dialog is presentation-neutral: there is no `variation` prop (see the design
170
+ * spec / ADR-0022). The "basic" vs "destructive" Figma looks come entirely from
171
+ * the app-supplied header icon and footer `Button variation`, so these tables
172
+ * carry no variant axes — they are static class sets composed with `cn`.
173
+ */
174
+ /**
175
+ * The full-viewport backdrop. Fixed over everything, flex-centres the card, and
176
+ * paints the `--overlay` semantic token (a derived-alpha value, ADR-0005 — never
177
+ * an ad-hoc `bg-black/50`). `p-4` (1rem each side = 2rem total) reserves the
178
+ * viewport gutter the card's `max-w`/`max-h` calc subtract, so the card never
179
+ * touches an edge.
180
+ */
181
+ declare const dialogOverlayVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
182
+ /**
183
+ * The centred card. `w-[438px]` is the Figma-exact default width; there is no
184
+ * container token at 438px (closest is `--container-md` = 448px) and no width is
185
+ * expressible on the 4px spacing grid, so an arbitrary value is used
186
+ * deliberately (same posture as `scrollbarVariants`) and is `className`-
187
+ * overridable. `max-w`/`max-h` are viewport calcs (no token can express
188
+ * `100vw − gutter`) that keep the card inside the `p-4` overlay gutter; combined
189
+ * with the flex column below they let `Dialog.Body` scroll internally rather
190
+ * than let the card exceed the viewport.
191
+ */
192
+ declare const dialogContentVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
193
+ /**
194
+ * Header block: a stacked 32px decorative icon above the title/description
195
+ * column. `gap-4` (16px) between them, `items-start` left-aligns children.
196
+ * `shrink-0` keeps it fixed while the body scrolls. NOT `relative` on purpose —
197
+ * the close button positions against the card (`Dialog.Content`), so the header
198
+ * must not become the positioning anchor.
199
+ */
200
+ declare const dialogHeaderVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
201
+ /** Decorative 32px icon slot (`aria-hidden` on the element in `Dialog.Header`). */
202
+ declare const dialogHeaderIconVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
203
+ /**
204
+ * Title + description column: `gap-2` (8px) between the two, `w-full` so both
205
+ * span the header width. When the close X is shown on an icon-less header the
206
+ * component adds `pr-8` here so a long title clears the corner button (with an
207
+ * icon the title sits below the X, so no reservation is needed).
208
+ */
209
+ declare const dialogHeaderTextVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
210
+ /**
211
+ * The 16px muted X close button. Absolutely positioned against the card
212
+ * (`Dialog.Content` is the `relative` anchor) at `top-6 right-6` — always 24px
213
+ * from the card's top and right regardless of whether the header has an icon, so
214
+ * it never shifts with header content. Focus ring per ADR-0010.
215
+ */
216
+ declare const dialogCloseButtonVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
217
+ /**
218
+ * Body slot: sizes to content and, inside the card's bounded flex column,
219
+ * becomes the scroll region (`min-h-0 flex-1 overflow-y-auto`) so overflowing
220
+ * content scrolls internally and the dialog never exceeds the viewport. Custom
221
+ * scrollbar is composed in via `scrollbarVariants` in the component.
222
+ */
223
+ declare const dialogBodyVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
224
+ /**
225
+ * Footer action row: full width, `gap-2`, children stretched to equal width
226
+ * (Figma's two-button layout — each button is `flex: 1 0 0`). `shrink-0` keeps
227
+ * it pinned while the body scrolls.
228
+ *
229
+ * Two-level stretch: `[&>*]:flex-1` grows each direct child equally, but a cut-
230
+ * corner `Button` renders an `inline-block` wrapper span around its `<button>`,
231
+ * so the span fills the flex track while the inner button would stay content-
232
+ * width. `[&_button]:w-full` stretches that inner button to fill the track (a
233
+ * ghost button, which is the direct flex child, keeps its `flex:1 1 0%` basis).
234
+ * Targets the element, not `[data-slot=button]`, because a `Dialog.Close asChild`
235
+ * wrapper overrides the inner button's `data-slot` to `dialog-close`. The
236
+ * button's own `justify-center` then centers the label, matching Figma.
237
+ */
238
+ declare const dialogFooterVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
239
+
240
+ export { Dialog, DialogBody, type DialogBodyProps, DialogClose, type DialogCloseProps, DialogContent, type DialogContentProps, DialogFooter, type DialogFooterProps, DialogHeader, type DialogHeaderProps, DialogRoot, type DialogRootProps, DialogTrigger, type DialogTriggerProps, type UseFocusTrapOptions, dialogBodyVariants, dialogCloseButtonVariants, dialogContentVariants, dialogFooterVariants, dialogHeaderIconVariants, dialogHeaderTextVariants, dialogHeaderVariants, dialogOverlayVariants, getFocusableElements, useFocusTrap };