@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.
- package/LICENSE +19 -0
- package/README.md +119 -0
- package/dist/Radio-BYWz1n7p.d.cts +32 -0
- package/dist/Radio-BYWz1n7p.d.ts +32 -0
- package/dist/SelectTrigger-By0iPBI3.d.cts +78 -0
- package/dist/SelectTrigger-By0iPBI3.d.ts +78 -0
- package/dist/accordion.cjs +322 -0
- package/dist/accordion.d.cts +156 -0
- package/dist/accordion.d.ts +156 -0
- package/dist/accordion.js +2 -0
- package/dist/apps-ui.umd.js +1 -0
- package/dist/button.cjs +366 -0
- package/dist/button.d.cts +52 -0
- package/dist/button.d.ts +52 -0
- package/dist/button.js +3 -0
- package/dist/checkbox.cjs +74 -0
- package/dist/checkbox.d.cts +63 -0
- package/dist/checkbox.d.ts +63 -0
- package/dist/checkbox.js +2 -0
- package/dist/chunk-3RU7JMFE.js +27 -0
- package/dist/chunk-5TWRK4NW.js +783 -0
- package/dist/chunk-7EOSDFS3.js +9 -0
- package/dist/chunk-AIQ4UOGK.js +142 -0
- package/dist/chunk-B3MMGTPV.js +200 -0
- package/dist/chunk-DAHQYLUI.js +146 -0
- package/dist/chunk-DGBXADSS.js +1 -0
- package/dist/chunk-E6KPNTWW.js +295 -0
- package/dist/chunk-EOAB6G2D.js +149 -0
- package/dist/chunk-FAOVKDVM.js +32 -0
- package/dist/chunk-FV76Z4Z2.js +78 -0
- package/dist/chunk-GLTLBUZ7.js +285 -0
- package/dist/chunk-GTZFV43T.js +154 -0
- package/dist/chunk-HRGZNL4A.js +88 -0
- package/dist/chunk-IEVN3J2F.js +98 -0
- package/dist/chunk-IRS3T6LF.js +352 -0
- package/dist/chunk-IYYHCQXG.js +146 -0
- package/dist/chunk-JIXW3455.js +89 -0
- package/dist/chunk-N4VAP2GB.js +77 -0
- package/dist/chunk-SJ2ZRYVV.js +167 -0
- package/dist/chunk-SNU4YXOR.js +66 -0
- package/dist/chunk-TT6JQATV.js +99 -0
- package/dist/chunk-UPQ3ZPCO.js +66 -0
- package/dist/chunk-UVUSVFOH.js +264 -0
- package/dist/chunk-WJFPGGJB.js +255 -0
- package/dist/chunk-WXEBQA7A.js +371 -0
- package/dist/chunk-Y2O4YYZQ.js +184 -0
- package/dist/cut-corner.cjs +111 -0
- package/dist/cut-corner.d.cts +49 -0
- package/dist/cut-corner.d.ts +49 -0
- package/dist/cut-corner.js +1 -0
- package/dist/dialog.cjs +441 -0
- package/dist/dialog.d.cts +240 -0
- package/dist/dialog.d.ts +240 -0
- package/dist/dialog.js +3 -0
- package/dist/feature-card.cjs +182 -0
- package/dist/feature-card.d.cts +54 -0
- package/dist/feature-card.d.ts +54 -0
- package/dist/feature-card.js +2 -0
- package/dist/filter.cjs +873 -0
- package/dist/filter.d.cts +129 -0
- package/dist/filter.d.ts +129 -0
- package/dist/filter.js +6 -0
- package/dist/index.cjs +4583 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +27 -0
- package/dist/inline-select.cjs +856 -0
- package/dist/inline-select.d.cts +92 -0
- package/dist/inline-select.d.ts +92 -0
- package/dist/inline-select.js +6 -0
- package/dist/input.cjs +263 -0
- package/dist/input.d.cts +157 -0
- package/dist/input.d.ts +157 -0
- package/dist/input.js +4 -0
- package/dist/panel.cjs +185 -0
- package/dist/panel.d.cts +103 -0
- package/dist/panel.d.ts +103 -0
- package/dist/panel.js +2 -0
- package/dist/property-grid.cjs +110 -0
- package/dist/property-grid.d.cts +56 -0
- package/dist/property-grid.d.ts +56 -0
- package/dist/property-grid.js +2 -0
- package/dist/radio.cjs +74 -0
- package/dist/radio.d.cts +39 -0
- package/dist/radio.d.ts +39 -0
- package/dist/radio.js +2 -0
- package/dist/scrollbar.cjs +34 -0
- package/dist/scrollbar.d.cts +35 -0
- package/dist/scrollbar.d.ts +35 -0
- package/dist/scrollbar.js +1 -0
- package/dist/search-bar.cjs +408 -0
- package/dist/search-bar.d.cts +42 -0
- package/dist/search-bar.d.ts +42 -0
- package/dist/search-bar.js +4 -0
- package/dist/select.cjs +998 -0
- package/dist/select.d.cts +357 -0
- package/dist/select.d.ts +357 -0
- package/dist/select.js +5 -0
- package/dist/selectable-feature-card.cjs +159 -0
- package/dist/selectable-feature-card.d.cts +74 -0
- package/dist/selectable-feature-card.d.ts +74 -0
- package/dist/selectable-feature-card.js +3 -0
- package/dist/sidebar-item.cjs +175 -0
- package/dist/sidebar-item.d.cts +75 -0
- package/dist/sidebar-item.d.ts +75 -0
- package/dist/sidebar-item.js +2 -0
- package/dist/switch.cjs +86 -0
- package/dist/switch.d.cts +72 -0
- package/dist/switch.d.ts +72 -0
- package/dist/switch.js +2 -0
- package/dist/table-cell.cjs +196 -0
- package/dist/table-cell.d.cts +76 -0
- package/dist/table-cell.d.ts +76 -0
- package/dist/table-cell.js +2 -0
- package/dist/table-title.cjs +34 -0
- package/dist/table-title.d.cts +16 -0
- package/dist/table-title.d.ts +16 -0
- package/dist/table-title.js +2 -0
- package/dist/tabs.cjs +299 -0
- package/dist/tabs.d.cts +116 -0
- package/dist/tabs.d.ts +116 -0
- package/dist/tabs.js +2 -0
- package/dist/toast.cjs +87 -0
- package/dist/toast.d.cts +48 -0
- package/dist/toast.d.ts +48 -0
- package/dist/toast.js +2 -0
- package/dist/token-input.cjs +415 -0
- package/dist/token-input.d.cts +90 -0
- package/dist/token-input.d.ts +90 -0
- package/dist/token-input.js +3 -0
- package/dist/tooltip.cjs +384 -0
- package/dist/tooltip.d.cts +142 -0
- package/dist/tooltip.d.ts +142 -0
- package/dist/tooltip.js +2 -0
- package/package.json +200 -0
- package/src/fonts.css +33 -0
package/dist/dialog.cjs
ADDED
|
@@ -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 };
|