@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,255 @@
1
+ import { useCutCornerClipPath, BUTTON_CUT } from './chunk-JIXW3455.js';
2
+ import { cn } from './chunk-7EOSDFS3.js';
3
+ import * as React from 'react';
4
+ import { CircleNotchIcon } from '@phosphor-icons/react';
5
+ import { cva } from 'class-variance-authority';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+
8
+ var buttonVariants = cva(
9
+ [
10
+ // Layout
11
+ "inline-flex shrink-0 items-center justify-center gap-1",
12
+ // Type — the Figma `button-01` text style (Archivo Medium 14/20)
13
+ "button-01",
14
+ // Shape — cut corners are applied as a clip-path in <Button>; keep the box square
15
+ "rounded-none",
16
+ // Transition
17
+ "transition-colors cursor-pointer",
18
+ // Suppress the native ring. The Figma focus ring is drawn OUTSIDE the
19
+ // clip-path (which would swallow an outline here), so cut-corner variants
20
+ // render it on the wrapper in <Button>; ghost renders it directly (below).
21
+ "outline-none",
22
+ // Disabled
23
+ "disabled:pointer-events-none disabled:cursor-not-allowed",
24
+ // Icons
25
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0"
26
+ ],
27
+ {
28
+ variants: {
29
+ variation: {
30
+ primary: [
31
+ "bg-button-primary-background text-button-primary-foreground",
32
+ "hover:bg-button-primary-accent active:bg-button-primary-accent",
33
+ "disabled:bg-button-primary-muted disabled:text-button-primary-foreground-muted"
34
+ ],
35
+ brand: [
36
+ "bg-button-brand-primary-background text-button-brand-primary-foreground",
37
+ "hover:bg-button-brand-primary-accent active:bg-button-brand-primary-accent",
38
+ "disabled:bg-button-primary-muted disabled:text-button-primary-foreground-muted"
39
+ ],
40
+ destructive: [
41
+ "bg-button-destructive-background text-button-destructive-primary-foreground",
42
+ "hover:bg-button-destructive-accent active:bg-button-destructive-accent",
43
+ "disabled:bg-button-primary-muted disabled:text-button-primary-foreground-muted"
44
+ ],
45
+ // Outline — background fill + an SVG stroke border drawn in <Button>.
46
+ secondary: [
47
+ "bg-button-secondary-background text-button-secondary-foreground",
48
+ "hover:text-button-secondary-foreground-accent active:text-button-secondary-foreground-accent",
49
+ "disabled:text-button-secondary-foreground-muted"
50
+ ],
51
+ // No background, no cut corner — so no wrapper; ring on the element.
52
+ // `outline-solid` restores the style that base `outline-none` zeroed.
53
+ ghost: [
54
+ "bg-transparent text-button-secondary-foreground",
55
+ "hover:text-button-secondary-foreground-accent active:text-button-secondary-foreground-accent",
56
+ "disabled:text-button-secondary-foreground-muted",
57
+ // `rounded-xs` (2px) keeps the ghost focus ring as sharp as the other
58
+ // variants' corners (which now use sharp chamfers + 2px square corners).
59
+ "rounded-xs focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus"
60
+ ]
61
+ },
62
+ size: {
63
+ sm: "h-8 px-2.5 [&_svg:not([class*='size-'])]:size-4",
64
+ md: "h-10 px-2.5 [&_svg:not([class*='size-'])]:size-4",
65
+ lg: "h-12 px-3 [&_svg:not([class*='size-'])]:size-4"
66
+ },
67
+ iconOnly: {
68
+ true: "",
69
+ false: ""
70
+ }
71
+ },
72
+ compoundVariants: [
73
+ // Icon-only: square footprint, no horizontal padding.
74
+ { iconOnly: true, size: "sm", className: "w-8 px-0" },
75
+ { iconOnly: true, size: "md", className: "w-10 px-0" },
76
+ { iconOnly: true, size: "lg", className: "w-12 px-0" },
77
+ // Ghost has no chamfer/background, so it sits flush with no inset padding.
78
+ { variation: "ghost", iconOnly: false, className: "px-0" }
79
+ ],
80
+ defaultVariants: {
81
+ variation: "primary",
82
+ size: "md",
83
+ iconOnly: false
84
+ }
85
+ }
86
+ );
87
+ var FOCUS_RING_GAP = 3;
88
+ var FOCUS_RING_WIDTH = 2;
89
+ function setRef(ref, node) {
90
+ if (typeof ref === "function") ref(node);
91
+ else if (ref) ref.current = node;
92
+ }
93
+ function Button({
94
+ className,
95
+ variation = "primary",
96
+ size = "md",
97
+ iconOnly = false,
98
+ asChild = false,
99
+ loading = false,
100
+ leftSlot,
101
+ rightSlot,
102
+ wrapperClassName,
103
+ disabled,
104
+ style,
105
+ children,
106
+ onClick,
107
+ ref,
108
+ ...props
109
+ }) {
110
+ const {
111
+ ref: cutRef,
112
+ clipPath,
113
+ pathD,
114
+ outsetPathD
115
+ } = useCutCornerClipPath(BUTTON_CUT.cut, {
116
+ radius: BUTTON_CUT.radius,
117
+ radiusCuts: BUTTON_CUT.radiusCuts,
118
+ outset: FOCUS_RING_GAP
119
+ });
120
+ const setRootRef = React.useCallback(
121
+ (node) => {
122
+ cutRef.current = node;
123
+ setRef(ref, node);
124
+ },
125
+ [cutRef, ref]
126
+ );
127
+ const isDisabled = disabled;
128
+ const hasCutCorner = variation !== "ghost";
129
+ const hasSvgBorder = variation === "secondary";
130
+ const handleClick = loading ? (event) => {
131
+ event.preventDefault();
132
+ event.stopPropagation();
133
+ } : onClick;
134
+ const rootClassName = cn(
135
+ buttonVariants({ variation, size, iconOnly }),
136
+ hasSvgBorder && "border-0",
137
+ loading && "pointer-events-none",
138
+ className
139
+ );
140
+ const rootStyle = hasCutCorner ? { ...style, clipPath } : style;
141
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
142
+ loading ? /* @__PURE__ */ jsx(CircleNotchIcon, { className: "animate-spin", "aria-hidden": "true" }) : leftSlot,
143
+ !iconOnly && children,
144
+ iconOnly && !loading && children,
145
+ !iconOnly && rightSlot
146
+ ] });
147
+ let rootEl;
148
+ if (asChild) {
149
+ if (!React.isValidElement(children)) {
150
+ throw new Error(
151
+ "Button: `asChild` requires a single React element child"
152
+ );
153
+ }
154
+ const child = children;
155
+ const childProps = child.props;
156
+ const childOnClick = childProps.onClick;
157
+ rootEl = React.cloneElement(
158
+ child,
159
+ {
160
+ ...props,
161
+ ...childProps,
162
+ "data-slot": "button",
163
+ "data-loading": loading || void 0,
164
+ "aria-busy": loading || void 0,
165
+ "aria-disabled": loading || void 0,
166
+ // Block activation while loading; otherwise run both handlers.
167
+ onClick: loading ? (event) => {
168
+ event.preventDefault();
169
+ event.stopPropagation();
170
+ } : (event) => {
171
+ onClick?.(event);
172
+ childOnClick?.(event);
173
+ },
174
+ ref: (node) => {
175
+ setRootRef(node);
176
+ setRef(childProps.ref, node);
177
+ },
178
+ className: cn(
179
+ rootClassName,
180
+ childProps.className
181
+ ),
182
+ style: {
183
+ ...rootStyle,
184
+ ...childProps.style
185
+ }
186
+ },
187
+ loading ? /* @__PURE__ */ jsx(CircleNotchIcon, { className: "animate-spin", "aria-hidden": "true" }) : null,
188
+ childProps.children
189
+ );
190
+ } else {
191
+ rootEl = /* @__PURE__ */ jsx(
192
+ "button",
193
+ {
194
+ "data-slot": "button",
195
+ "data-loading": loading || void 0,
196
+ "aria-busy": loading || void 0,
197
+ "aria-disabled": loading || void 0,
198
+ className: rootClassName,
199
+ style: rootStyle,
200
+ disabled: isDisabled,
201
+ ...props,
202
+ onClick: handleClick,
203
+ ref: setRootRef,
204
+ children: content
205
+ }
206
+ );
207
+ }
208
+ if (!hasCutCorner) return rootEl;
209
+ return /* @__PURE__ */ jsxs("span", { className: cn("group relative inline-block", wrapperClassName), children: [
210
+ rootEl,
211
+ outsetPathD && /* @__PURE__ */ jsx(
212
+ "svg",
213
+ {
214
+ className: "pointer-events-none absolute inset-0 h-full w-full overflow-visible opacity-0 group-has-[:focus-visible]:opacity-100",
215
+ "aria-hidden": "true",
216
+ children: /* @__PURE__ */ jsx(
217
+ "path",
218
+ {
219
+ d: outsetPathD,
220
+ fill: "none",
221
+ strokeWidth: FOCUS_RING_WIDTH,
222
+ className: "stroke-focus"
223
+ }
224
+ )
225
+ }
226
+ ),
227
+ hasSvgBorder && pathD && /* @__PURE__ */ jsx(
228
+ "svg",
229
+ {
230
+ className: "pointer-events-none absolute inset-0 h-full w-full overflow-visible",
231
+ "aria-hidden": "true",
232
+ children: /* @__PURE__ */ jsx(
233
+ "path",
234
+ {
235
+ d: pathD,
236
+ fill: "none",
237
+ strokeWidth: 1,
238
+ className: cn(
239
+ // Disabled locks the muted stroke; the hover/active accent
240
+ // classes must not be present at all (a `group-hover:` variant
241
+ // out-specifies the plain muted class, so it would still
242
+ // recolor the border on hover of a disabled button).
243
+ isDisabled ? "stroke-button-secondary-border-muted" : [
244
+ "stroke-button-secondary-border",
245
+ "group-hover:stroke-button-secondary-border-accent group-active:stroke-button-secondary-border-accent"
246
+ ]
247
+ )
248
+ }
249
+ )
250
+ }
251
+ )
252
+ ] });
253
+ }
254
+
255
+ export { Button, buttonVariants };
@@ -0,0 +1,371 @@
1
+ import { scrollbarVariants } from './chunk-FAOVKDVM.js';
2
+ import { cn } from './chunk-7EOSDFS3.js';
3
+ import * as React2 from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { IconContext, XIcon } from '@phosphor-icons/react';
6
+ import { cva } from 'class-variance-authority';
7
+ import { jsx, jsxs } from 'react/jsx-runtime';
8
+
9
+ var FOCUSABLE_SELECTOR = [
10
+ "a[href]",
11
+ "button:not([disabled])",
12
+ "textarea:not([disabled])",
13
+ "input:not([disabled])",
14
+ "select:not([disabled])",
15
+ '[tabindex]:not([tabindex="-1"])'
16
+ ].join(",");
17
+ function getFocusableElements(container) {
18
+ return Array.from(
19
+ container.querySelectorAll(FOCUSABLE_SELECTOR)
20
+ );
21
+ }
22
+ function useFocusTrap({
23
+ active,
24
+ containerRef
25
+ }) {
26
+ React2.useEffect(() => {
27
+ if (!active) return;
28
+ const container = containerRef.current;
29
+ if (!container) return;
30
+ const previouslyFocused = document.activeElement;
31
+ const initial = getFocusableElements(container)[0] ?? container;
32
+ initial.focus();
33
+ const body = document.body;
34
+ const previousOverflow = body.style.overflow;
35
+ body.style.overflow = "hidden";
36
+ const handleKeyDown = (event) => {
37
+ if (event.key !== "Tab") return;
38
+ const focusables = getFocusableElements(container);
39
+ if (focusables.length === 0) {
40
+ event.preventDefault();
41
+ container.focus();
42
+ return;
43
+ }
44
+ const first = focusables[0];
45
+ const last = focusables[focusables.length - 1];
46
+ const activeEl = document.activeElement;
47
+ if (event.shiftKey) {
48
+ if (activeEl === first || !container.contains(activeEl)) {
49
+ event.preventDefault();
50
+ last.focus();
51
+ }
52
+ } else if (activeEl === last || !container.contains(activeEl)) {
53
+ event.preventDefault();
54
+ first.focus();
55
+ }
56
+ };
57
+ document.addEventListener("keydown", handleKeyDown, true);
58
+ return () => {
59
+ document.removeEventListener("keydown", handleKeyDown, true);
60
+ body.style.overflow = previousOverflow;
61
+ previouslyFocused?.focus?.();
62
+ };
63
+ }, [active, containerRef]);
64
+ }
65
+ var dialogOverlayVariants = cva([
66
+ "fixed inset-0 z-50 flex items-center justify-center p-4",
67
+ "bg-overlay"
68
+ ]);
69
+ var dialogContentVariants = cva([
70
+ "relative flex w-[438px] max-w-[calc(100vw-2rem)] max-h-[calc(100vh-2rem)] flex-col gap-6",
71
+ "rounded-md border border-border bg-card-background p-6 shadow-s",
72
+ // Programmatic-focus fallback target (no visible ring when focused as a last
73
+ // resort by the focus trap — the card is not an interactive control).
74
+ "outline-none"
75
+ ]);
76
+ var dialogHeaderVariants = cva([
77
+ "flex shrink-0 flex-col items-start gap-4"
78
+ ]);
79
+ var dialogHeaderIconVariants = cva([
80
+ "flex items-center text-foreground [&_svg]:size-8 [&_svg]:shrink-0"
81
+ ]);
82
+ var dialogHeaderTextVariants = cva(["flex w-full flex-col gap-2"]);
83
+ var dialogCloseButtonVariants = cva([
84
+ "absolute right-6 top-6 flex cursor-pointer items-center justify-center rounded-xs",
85
+ "text-foreground-muted transition-colors hover:text-foreground",
86
+ "[&_svg]:size-4 [&_svg]:shrink-0",
87
+ // TW4 needs `outline-solid` to render a solid ring (a bare `outline` won't).
88
+ "outline-none focus-visible:text-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus"
89
+ ]);
90
+ var dialogBodyVariants = cva([
91
+ "min-h-0 flex-1 overflow-y-auto text-foreground"
92
+ ]);
93
+ var dialogFooterVariants = cva([
94
+ "flex w-full shrink-0 gap-2 [&>*]:flex-1 [&_button]:w-full"
95
+ ]);
96
+ var DialogContext = React2.createContext(null);
97
+ function useDialogContext(part) {
98
+ const context = React2.useContext(DialogContext);
99
+ if (context === null) {
100
+ throw new Error(`${part} must be used within <Dialog.Root>`);
101
+ }
102
+ return context;
103
+ }
104
+ function DialogRoot({
105
+ open: openProp,
106
+ defaultOpen = false,
107
+ onOpenChange,
108
+ dismissable = true,
109
+ children
110
+ }) {
111
+ const [uncontrolledOpen, setUncontrolledOpen] = React2.useState(defaultOpen);
112
+ const [hasDescription, setHasDescription] = React2.useState(false);
113
+ const isControlled = openProp !== void 0;
114
+ const open = isControlled ? openProp : uncontrolledOpen;
115
+ const reactId = React2.useId();
116
+ const titleId = `${reactId}-title`;
117
+ const descriptionId = `${reactId}-description`;
118
+ const setOpen = React2.useCallback(
119
+ (next) => {
120
+ if (!isControlled) setUncontrolledOpen(next);
121
+ onOpenChange?.(next);
122
+ },
123
+ [isControlled, onOpenChange]
124
+ );
125
+ const value = {
126
+ open,
127
+ setOpen,
128
+ dismissable,
129
+ titleId,
130
+ descriptionId,
131
+ hasDescription,
132
+ setHasDescription
133
+ };
134
+ return /* @__PURE__ */ jsx(DialogContext.Provider, { value, children });
135
+ }
136
+ function setRef(ref, node) {
137
+ if (typeof ref === "function") ref(node);
138
+ else if (ref) ref.current = node;
139
+ }
140
+ function TriggerLike({
141
+ asChild = false,
142
+ onActivate,
143
+ dataSlot,
144
+ children,
145
+ className,
146
+ type,
147
+ ...props
148
+ }) {
149
+ if (asChild) {
150
+ if (!React2.isValidElement(children)) {
151
+ throw new Error(
152
+ `${dataSlot}: \`asChild\` requires a single React element child`
153
+ );
154
+ }
155
+ const child = children;
156
+ const childProps = child.props;
157
+ const childOnClick = childProps.onClick;
158
+ return React2.cloneElement(child, {
159
+ ...props,
160
+ ...childProps,
161
+ "data-slot": dataSlot,
162
+ onClick: (event) => {
163
+ childOnClick?.(event);
164
+ if (!event.defaultPrevented) onActivate();
165
+ },
166
+ className: cn(className, childProps.className),
167
+ ref: (node) => {
168
+ setRef(childProps.ref, node);
169
+ }
170
+ });
171
+ }
172
+ return /* @__PURE__ */ jsx(
173
+ "button",
174
+ {
175
+ type: type ?? "button",
176
+ "data-slot": dataSlot,
177
+ className,
178
+ onClick: () => onActivate(),
179
+ ...props,
180
+ children
181
+ }
182
+ );
183
+ }
184
+ function DialogTrigger({ asChild = false, ...props }) {
185
+ const { open, setOpen } = useDialogContext("Dialog.Trigger");
186
+ return /* @__PURE__ */ jsx(
187
+ TriggerLike,
188
+ {
189
+ asChild,
190
+ dataSlot: "dialog-trigger",
191
+ onActivate: () => setOpen(true),
192
+ "aria-haspopup": "dialog",
193
+ "aria-expanded": open,
194
+ ...props
195
+ }
196
+ );
197
+ }
198
+ function DialogClose({ asChild = false, ...props }) {
199
+ const { setOpen } = useDialogContext("Dialog.Close");
200
+ return /* @__PURE__ */ jsx(
201
+ TriggerLike,
202
+ {
203
+ asChild,
204
+ dataSlot: "dialog-close",
205
+ onActivate: () => setOpen(false),
206
+ ...props
207
+ }
208
+ );
209
+ }
210
+ function DialogContent({
211
+ className,
212
+ children,
213
+ ref,
214
+ onKeyDown,
215
+ ...props
216
+ }) {
217
+ const { open, setOpen, dismissable, titleId, descriptionId, hasDescription } = useDialogContext("Dialog.Content");
218
+ const cardRef = React2.useRef(null);
219
+ useFocusTrap({ active: open, containerRef: cardRef });
220
+ React2.useEffect(() => {
221
+ if (!open || !dismissable) return;
222
+ const handleEscape = (event) => {
223
+ if (event.key === "Escape") {
224
+ event.stopPropagation();
225
+ setOpen(false);
226
+ }
227
+ };
228
+ document.addEventListener("keydown", handleEscape);
229
+ return () => document.removeEventListener("keydown", handleEscape);
230
+ }, [open, dismissable, setOpen]);
231
+ if (!open) return null;
232
+ const setCardRef = (node) => {
233
+ cardRef.current = node;
234
+ setRef(ref, node);
235
+ };
236
+ return createPortal(
237
+ /* @__PURE__ */ jsx(
238
+ "div",
239
+ {
240
+ "data-slot": "dialog-overlay",
241
+ className: dialogOverlayVariants(),
242
+ onClick: (event) => {
243
+ if (dismissable && event.target === event.currentTarget) {
244
+ setOpen(false);
245
+ }
246
+ },
247
+ children: /* @__PURE__ */ jsx(
248
+ "div",
249
+ {
250
+ ref: setCardRef,
251
+ role: "dialog",
252
+ "aria-modal": "true",
253
+ "aria-labelledby": titleId,
254
+ "aria-describedby": hasDescription ? descriptionId : void 0,
255
+ tabIndex: -1,
256
+ "data-slot": "dialog-content",
257
+ className: cn(dialogContentVariants(), className),
258
+ onKeyDown,
259
+ ...props,
260
+ children
261
+ }
262
+ )
263
+ }
264
+ ),
265
+ document.body
266
+ );
267
+ }
268
+ function DialogHeader({
269
+ icon,
270
+ title,
271
+ description,
272
+ showClose = true,
273
+ className,
274
+ ref,
275
+ ...props
276
+ }) {
277
+ const { titleId, descriptionId, setHasDescription, setOpen } = useDialogContext("Dialog.Header");
278
+ const hasDescription = description != null;
279
+ React2.useEffect(() => {
280
+ if (!hasDescription) return;
281
+ setHasDescription(true);
282
+ return () => setHasDescription(false);
283
+ }, [hasDescription, setHasDescription]);
284
+ return /* @__PURE__ */ jsxs(
285
+ "div",
286
+ {
287
+ ref,
288
+ "data-slot": "dialog-header",
289
+ className: cn(dialogHeaderVariants(), className),
290
+ ...props,
291
+ children: [
292
+ icon != null && /* @__PURE__ */ jsx(
293
+ "span",
294
+ {
295
+ "data-slot": "dialog-header-icon",
296
+ "aria-hidden": "true",
297
+ className: dialogHeaderIconVariants(),
298
+ children: /* @__PURE__ */ jsx(IconContext.Provider, { value: { weight: "fill" }, children: icon })
299
+ }
300
+ ),
301
+ /* @__PURE__ */ jsxs(
302
+ "div",
303
+ {
304
+ className: cn(
305
+ dialogHeaderTextVariants(),
306
+ // Reserve right space for the corner X only when it would collide: an
307
+ // icon-less header puts the title on the X's row. With an icon the
308
+ // title sits below the X, so no reservation is needed.
309
+ showClose && icon == null && "pr-8"
310
+ ),
311
+ children: [
312
+ /* @__PURE__ */ jsx("h2", { id: titleId, className: "display-04 text-foreground", children: title }),
313
+ hasDescription && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "body-03 text-foreground-muted", children: description })
314
+ ]
315
+ }
316
+ ),
317
+ showClose && /* @__PURE__ */ jsx(
318
+ "button",
319
+ {
320
+ type: "button",
321
+ "data-slot": "dialog-header-close",
322
+ "aria-label": "Close",
323
+ className: dialogCloseButtonVariants(),
324
+ onClick: () => setOpen(false),
325
+ children: /* @__PURE__ */ jsx(XIcon, { weight: "regular" })
326
+ }
327
+ )
328
+ ]
329
+ }
330
+ );
331
+ }
332
+ function DialogBody({ className, children, ref, ...props }) {
333
+ return /* @__PURE__ */ jsx(
334
+ "div",
335
+ {
336
+ ref,
337
+ "data-slot": "dialog-body",
338
+ className: cn(dialogBodyVariants(), scrollbarVariants(), className),
339
+ ...props,
340
+ children
341
+ }
342
+ );
343
+ }
344
+ function DialogFooter({
345
+ className,
346
+ children,
347
+ ref,
348
+ ...props
349
+ }) {
350
+ return /* @__PURE__ */ jsx(
351
+ "div",
352
+ {
353
+ ref,
354
+ "data-slot": "dialog-footer",
355
+ className: cn(dialogFooterVariants(), className),
356
+ ...props,
357
+ children
358
+ }
359
+ );
360
+ }
361
+ var Dialog = {
362
+ Root: DialogRoot,
363
+ Trigger: DialogTrigger,
364
+ Content: DialogContent,
365
+ Header: DialogHeader,
366
+ Body: DialogBody,
367
+ Footer: DialogFooter,
368
+ Close: DialogClose
369
+ };
370
+
371
+ export { Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogRoot, DialogTrigger, dialogBodyVariants, dialogCloseButtonVariants, dialogContentVariants, dialogFooterVariants, dialogHeaderIconVariants, dialogHeaderTextVariants, dialogHeaderVariants, dialogOverlayVariants, getFocusableElements, useFocusTrap };