@jobber/components 8.21.0 → 8.22.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 (59) hide show
  1. package/Dialog.d.ts +1 -0
  2. package/Dialog.js +17 -0
  3. package/dist/BottomSheet-cjs.js +34 -7
  4. package/dist/BottomSheet-es.js +34 -8
  5. package/dist/Card/index.cjs +1 -0
  6. package/dist/Card/index.mjs +1 -0
  7. package/dist/ComboboxChipRemove-es.js +3 -3
  8. package/dist/DataDump/index.cjs +1 -0
  9. package/dist/DataDump/index.mjs +1 -0
  10. package/dist/Dialog/Dialog.d.ts +3 -0
  11. package/dist/Dialog/Dialog.types.d.ts +205 -0
  12. package/dist/Dialog/DialogBottomSheet.d.ts +28 -0
  13. package/dist/Dialog/DialogModal.d.ts +20 -0
  14. package/dist/Dialog/constants.d.ts +1 -0
  15. package/dist/Dialog/dialogComposableShared.d.ts +53 -0
  16. package/dist/Dialog/dialogReturnFocus.d.ts +43 -0
  17. package/dist/Dialog/index.cjs +32 -0
  18. package/dist/Dialog/index.d.ts +4 -0
  19. package/dist/Dialog/index.mjs +24 -0
  20. package/dist/DrawerDescription-cjs.js +409 -0
  21. package/dist/DrawerDescription-es.js +382 -0
  22. package/dist/DrawerTitle-cjs.js +116 -0
  23. package/dist/DrawerTitle-es.js +92 -0
  24. package/dist/InputNumberExperimental-es.js +1 -1
  25. package/dist/InternalBackdrop-es.js +2 -2
  26. package/dist/Menu/index.cjs +1 -0
  27. package/dist/Menu/index.mjs +1 -0
  28. package/dist/Menu-cjs.js +5 -3
  29. package/dist/Menu-es.js +5 -3
  30. package/dist/MenuSubmenuTrigger-cjs.js +49 -91
  31. package/dist/MenuSubmenuTrigger-es.js +51 -91
  32. package/dist/NumberFieldInput-es.js +1 -1
  33. package/dist/Page/index.cjs +1 -0
  34. package/dist/Page/index.mjs +1 -0
  35. package/dist/ScrollAreaViewport-cjs.js +938 -935
  36. package/dist/ScrollAreaViewport-es.js +938 -938
  37. package/dist/dialogReturnFocus-cjs.js +427 -0
  38. package/dist/dialogReturnFocus-es.js +423 -0
  39. package/dist/docs/Icon/Icon.md +1 -0
  40. package/dist/index.cjs +13 -7
  41. package/dist/index.d.mts +1 -0
  42. package/dist/index.d.ts +1 -0
  43. package/dist/index.mjs +10 -7
  44. package/dist/primitives/BottomSheet/BottomSheet.d.ts +19 -1
  45. package/dist/primitives/BottomSheet/BottomSheet.types.d.ts +8 -0
  46. package/dist/primitives/BottomSheet/index.cjs +1 -0
  47. package/dist/primitives/BottomSheet/index.d.ts +1 -1
  48. package/dist/primitives/BottomSheet/index.mjs +1 -1
  49. package/dist/primitives/index.cjs +1 -0
  50. package/dist/primitives/index.mjs +1 -1
  51. package/dist/styles.css +360 -0
  52. package/dist/unstyledPrimitives/index.cjs +32 -249
  53. package/dist/unstyledPrimitives/index.d.ts +1 -0
  54. package/dist/unstyledPrimitives/index.mjs +30 -248
  55. package/dist/useButton-es.js +1 -1
  56. package/dist/useCompositeListItem-es.js +2 -2
  57. package/dist/useLabel-es.js +1 -1
  58. package/dist/utils/meta/meta.json +11 -1
  59. package/package.json +3 -3
@@ -0,0 +1,423 @@
1
+ import { _ as __rest } from './tslib.es6-es.js';
2
+ import React__default, { isValidElement } from 'react';
3
+ import { useWindowDimensions } from '@jobber/hooks';
4
+ import classnames from 'classnames';
5
+ import { n as normalizeBaseUiButtonProps, i as isAtlantisButtonRenderTarget, s as stripNativeTypeFromButtonRenderTarget, r as renderAtlantisButtonTarget } from './buttonRenderAdapter-es.js';
6
+ import { B as Button } from './Button-es.js';
7
+ import { S as ScrollAreaRoot, e as ScrollAreaViewport, g as DialogRoot$1, h as DialogTrigger$1, i as DialogPortal } from './ScrollAreaViewport-es.js';
8
+ import { H as Heading } from './Heading-es.js';
9
+ import { a as BottomSheet, B as BottomSheetScrollAreaContext } from './BottomSheet-es.js';
10
+ import { D as DrawerTitle, a as DrawerClose, b as DialogTitle$1, c as DialogClose$1 } from './DrawerTitle-es.js';
11
+ import { m as mergeProps } from './useRenderElement-es.js';
12
+ import { D as DrawerDescription, a as DialogBackdrop, b as DialogViewport, c as DialogPopup, d as DialogDescription, e as createDialogHandle } from './DrawerDescription-es.js';
13
+
14
+ // Copied from Menu's constant so the two surfaces agree on what "mobile" means.
15
+ // (The proposal calls for copying shared Menu values into the component first
16
+ // and deciding later whether to promote them to a shared location.)
17
+ const SMALL_SCREEN_BREAKPOINT = 490;
18
+
19
+ var styles = {"small":"uYYyCR3l6E8-","base":"X1jGz-sLWf4-","large":"_4zFf2PkFhaA-","fullScreen":"nJoCr8oWJ7w-","header":"XFKiCQ8QbQU-","close":"FDOhnO0TT08-","scrollRegion":"_8DjYA23vHXA-","scrollViewport":"Bwr9IUYpLfM-","body":"zp9kx9uue2I-","fullBleed":"EhtwBTy1ai4-","footer":"DRIJhI1knKw-","sheetSurface":"vmcZRwOcNMo-","popup":"mFX7qDCd17I-","backdrop":"NE-BIsVE-hE-","viewport":"hp70o-3Cf-w-","spinning":"Zf-Z8DJcC-Y-"};
20
+
21
+ var actionsStyles = {"actions":"hKVNddbxkmo-","leftActions":"nslrozOH-v4-","buttonGroup":"BrXAnQBttWE-","align-left":"_9Vgd-3YJxlI-","align-right":"_8HC9NqvmJdo-","align-fullWidth":"ZZ3XuxGv6ow-","spinning":"PWf7wHPhd2A-"};
22
+
23
+ /**
24
+ * Resolves the trigger element for `Dialog.Trigger`. Copied from Menu's
25
+ * `resolveMenuTriggerRender` so an Atlantis `<Button>` works as a trigger via
26
+ * `children` or `render` identically in both shells. Raw text children are
27
+ * wrapped in a default `Button`.
28
+ */
29
+ function resolveComposableTriggerRender({ children, render, }) {
30
+ if (render) {
31
+ if (typeof render === "function") {
32
+ return (props) => {
33
+ const renderedElement = render(normalizeBaseUiButtonProps(props));
34
+ return isAtlantisButtonRenderTarget(renderedElement)
35
+ ? stripNativeTypeFromButtonRenderTarget(renderedElement)
36
+ : renderedElement;
37
+ };
38
+ }
39
+ return isAtlantisButtonRenderTarget(render)
40
+ ? renderAtlantisButtonTarget(render)
41
+ : render;
42
+ }
43
+ if (isAtlantisButtonRenderTarget(children)) {
44
+ return renderAtlantisButtonTarget(children);
45
+ }
46
+ if (React__default.isValidElement(children)) {
47
+ return children;
48
+ }
49
+ return renderAtlantisButtonTarget(React__default.createElement(Button, { type: "primary" },
50
+ React__default.createElement(Button.Label, null, children)));
51
+ }
52
+ /**
53
+ * Resolves the dismiss element for `Dialog.Close`, mirroring
54
+ * `resolveComposableTriggerRender` so a custom render works in both shells.
55
+ * Falls back to the subtle "remove" `ButtonDismiss` when neither `render` nor
56
+ * `children` are supplied. An Atlantis `<Button>`/`<ButtonDismiss>` passed via
57
+ * `render` or `children` is adapted; any other valid element is used as-is.
58
+ */
59
+ function resolveComposableCloseRender({ children, render, }) {
60
+ if (render) {
61
+ if (typeof render === "function") {
62
+ return (props) => {
63
+ const renderedElement = render(normalizeBaseUiButtonProps(props));
64
+ return isAtlantisButtonRenderTarget(renderedElement)
65
+ ? stripNativeTypeFromButtonRenderTarget(renderedElement)
66
+ : renderedElement;
67
+ };
68
+ }
69
+ return isAtlantisButtonRenderTarget(render)
70
+ ? renderAtlantisButtonTarget(render)
71
+ : render;
72
+ }
73
+ if (isAtlantisButtonRenderTarget(children)) {
74
+ return renderAtlantisButtonTarget(children);
75
+ }
76
+ if (React__default.isValidElement(children)) {
77
+ return children;
78
+ }
79
+ return renderAtlantisButtonTarget(React__default.createElement(Button, { type: "tertiary", variation: "subtle", ariaLabel: "Close dialog" },
80
+ React__default.createElement(Button.Icon, { name: "remove" })));
81
+ }
82
+ const DialogContext = React__default.createContext({
83
+ type: "dialog",
84
+ size: "base",
85
+ isMobile: false,
86
+ finalFocus: undefined,
87
+ });
88
+ /** Supplies the static config (type/size/isMobile/finalFocus) to the parts. */
89
+ function DialogProvider({ children, type, size, isMobile, finalFocus, }) {
90
+ return (React__default.createElement(DialogContext.Provider, { value: { type, size, isMobile, finalFocus } }, children));
91
+ }
92
+ /** Reads the Dialog config; throws if a part is used outside `<Dialog>`. */
93
+ function useDialogContext() {
94
+ const context = React__default.useContext(DialogContext);
95
+ return context;
96
+ }
97
+ /**
98
+ * True when the current parts render inside a `Dialog.ScrollArea` (or the scroll
99
+ * region a `Dialog.Body` wraps itself in). Header/Footer read it to know whether
100
+ * they are sticky (not nested inside a `Dialog.ScrollArea`) or scroll (nested inside a `Dialog.ScrollArea`); `Dialog.Body`
101
+ * reads it to avoid creating a nested scroller.
102
+ */
103
+ const DialogScrollAreaContext = React__default.createContext(false);
104
+ /** Whether the current part is nested inside a `Dialog.ScrollArea`. */
105
+ function useInsideDialogScrollArea() {
106
+ return React__default.useContext(DialogScrollAreaContext);
107
+ }
108
+ /**
109
+ * The modal's scroll region — a BaseUI `ScrollArea` that scrolls its content
110
+ * with the browser's native scrollbar (re-enabled in Dialog.module.css). Content
111
+ * placed inside it scrolls and owns the scrollbar; `Dialog.Body` used on its own
112
+ * wraps itself in one. (The bottom sheet uses `DialogBottomSheetScrollArea`,
113
+ * backed by the BottomSheet primitive's scroll area, for its fade.)
114
+ */
115
+ function DialogScrollArea$1({ children, className, style, }) {
116
+ return (React__default.createElement(ScrollAreaRoot, { className: classnames(styles.scrollRegion, className), style: style },
117
+ React__default.createElement(ScrollAreaViewport, { className: styles.scrollViewport,
118
+ // BaseUI defaults the viewport to `overflow: scroll`; `auto` so the
119
+ // native scrollbar only shows when the content overflows.
120
+ style: { overflow: "auto" } },
121
+ React__default.createElement(DialogScrollAreaContext.Provider, { value: true }, children))));
122
+ }
123
+ /** Layout-only header row; sticky unless nested inside a `Dialog.ScrollArea`. */
124
+ function SharedDialogHeader(_a) {
125
+ var { children, className, style } = _a, rest = __rest(_a, ["children", "className", "style"]);
126
+ const sticky = !useInsideDialogScrollArea();
127
+ return (React__default.createElement("div", Object.assign({}, rest, { className: classnames(styles.header, className), style: style, "data-sticky": sticky || undefined }), children));
128
+ }
129
+ /** Generic footer region; sticky unless nested inside a `Dialog.ScrollArea`. */
130
+ function SharedDialogFooter(_a) {
131
+ var { children, className, style } = _a, rest = __rest(_a, ["children", "className", "style"]);
132
+ const sticky = !useInsideDialogScrollArea();
133
+ return (React__default.createElement("div", Object.assign({}, rest, { className: classnames(styles.footer, className), style: style, "data-sticky": sticky || undefined, "data-testid": "ATL-Dialog-Footer" }), children));
134
+ }
135
+ /**
136
+ * The action-button layout: tertiary on the left, primary/secondary grouped on
137
+ * the right. Drop it inside a `Dialog.Footer`.
138
+ */
139
+ function DialogActions(_a) {
140
+ var { primary, secondary, tertiary, align = "right", className, style } = _a, rest = __rest(_a, ["primary", "secondary", "tertiary", "align", "className", "style"]);
141
+ const secondaryAction = secondary && isValidElement(secondary) ? (secondary) : (React__default.createElement(Button, Object.assign({ type: "primary", variation: "subtle" }, secondary)));
142
+ const primaryAction = primary && isValidElement(primary) ? (primary) : (React__default.createElement(Button, Object.assign({ type: "primary" }, primary)));
143
+ const tertiaryAction = tertiary && isValidElement(tertiary) ? (tertiary) : (React__default.createElement(Button, Object.assign({ type: "secondary", variation: "destructive" }, tertiary)));
144
+ return (React__default.createElement("div", Object.assign({}, rest, { className: classnames(actionsStyles.actions, [actionsStyles[`align-${align}`]], className), style: style, "data-testid": "ATL-Dialog-Actions" }),
145
+ tertiary && (React__default.createElement("div", { className: actionsStyles.leftActions }, tertiaryAction)),
146
+ React__default.createElement("div", { className: classnames(actionsStyles.buttonGroup, actionsStyles[`align-${align}`]) },
147
+ secondary && secondaryAction,
148
+ primary && primaryAction)));
149
+ }
150
+
151
+ function DialogBottomSheetRoot(props) {
152
+ return (React__default.createElement(BottomSheet.Root, Object.assign({}, props)));
153
+ }
154
+ function DialogBottomSheetTrigger(props) {
155
+ return (React__default.createElement(BottomSheet.Trigger, Object.assign({}, props)));
156
+ }
157
+ function DialogBottomSheetContent(_a) {
158
+ var { children, className, style } = _a, rest = __rest(_a, ["children", "className", "style"]);
159
+ const { type, size, finalFocus } = useDialogContext();
160
+ return (
161
+ // Opt-out of the BottomSheet's built-in scroll area.
162
+ // Dialog will handle the scroll area itself.
163
+ React__default.createElement(BottomSheetScrollAreaContext.Provider, { value: false },
164
+ React__default.createElement(BottomSheet.Popup, Object.assign({}, rest, { className: classnames(styles.sheetSurface, className), style: style, role: type, finalFocus: finalFocus, "data-dialog-variant": "sheet", "data-size": size }), children)));
165
+ }
166
+ function DialogBottomSheetTitle(_a) {
167
+ var { children } = _a, rest = __rest(_a, ["children"]);
168
+ return (React__default.createElement(DrawerTitle, Object.assign({}, rest, { render: titleProps => (React__default.createElement(Heading, Object.assign({ level: 2 }, titleProps), children)) })));
169
+ }
170
+ function DialogBottomSheetClose(_a) {
171
+ var { children, render } = _a, rest = __rest(_a, ["children", "render"]);
172
+ const closeProps = mergeProps({ className: styles.close }, Object.assign({}, rest));
173
+ return (React__default.createElement(DrawerClose, Object.assign({}, closeProps, { render: resolveComposableCloseRender({ children, render }) })));
174
+ }
175
+ /**
176
+ * The sheet's scroll region — backed by the BottomSheet primitive's styled
177
+ * scroll area (native scrollbar + bottom fade). Marks its content as inside a
178
+ * scroll area so a nested `Dialog.Body` renders plain and sticky regions know
179
+ * they scroll.
180
+ */
181
+ function DialogBottomSheetScrollArea({ children, className, style, }) {
182
+ return (React__default.createElement(BottomSheet.ScrollArea, { className: className, style: style },
183
+ React__default.createElement(DialogScrollAreaContext.Provider, { value: true }, children)));
184
+ }
185
+ function DialogBottomSheetBody(_a) {
186
+ var { children, fullBleed = false, className, style } = _a, rest = __rest(_a, ["children", "fullBleed", "className", "style"]);
187
+ const insideScrollArea = useInsideDialogScrollArea();
188
+ // Owns padding and the accessible-description wiring. By default it is the
189
+ // scroll region (wraps itself in the sheet's scroll area); inside an explicit
190
+ // `Dialog.ScrollArea` it renders as plain content (no nested scroller).
191
+ const content = (React__default.createElement(DrawerDescription, { render: descriptionProps => (React__default.createElement("div", Object.assign({}, rest, descriptionProps, { className: classnames(styles.body, { [styles.fullBleed]: fullBleed }, className), style: style }), children)) }));
192
+ return insideScrollArea ? (content) : (React__default.createElement(DialogBottomSheetScrollArea, null, content));
193
+ }
194
+ /** Mobile shell: a bottom sheet built on the BottomSheet/Drawer primitives. */
195
+ const DialogBottomSheet = Object.assign(DialogBottomSheetRoot, {
196
+ Trigger: DialogBottomSheetTrigger,
197
+ Content: DialogBottomSheetContent,
198
+ Header: SharedDialogHeader,
199
+ Title: DialogBottomSheetTitle,
200
+ Close: DialogBottomSheetClose,
201
+ Body: DialogBottomSheetBody,
202
+ Footer: SharedDialogFooter,
203
+ ScrollArea: DialogBottomSheetScrollArea,
204
+ });
205
+
206
+ function DialogModalRoot(props) {
207
+ return React__default.createElement(DialogRoot$1, Object.assign({}, props));
208
+ }
209
+ function DialogModalTrigger(_a) {
210
+ var { children, render, handle, payload, className, style } = _a, rest = __rest(_a, ["children", "render", "handle", "payload", "className", "style"]);
211
+ return (React__default.createElement(DialogTrigger$1, Object.assign({}, rest, { handle: handle, payload: payload, className: className, style: style, render: resolveComposableTriggerRender({ children, render }) })));
212
+ }
213
+ function DialogModalContent(_a) {
214
+ var { children, className, style } = _a, rest = __rest(_a, ["children", "className", "style"]);
215
+ const { type, size, finalFocus } = useDialogContext();
216
+ return (React__default.createElement(DialogPortal, null,
217
+ React__default.createElement(DialogBackdrop, { forceRender: true, className: styles.backdrop, "data-testid": "ATL-Dialog-Backdrop" }),
218
+ React__default.createElement(DialogViewport, { className: styles.viewport },
219
+ React__default.createElement(DialogPopup, Object.assign({}, rest, { className: classnames(styles.popup, styles[size], className), style: style, role: type, finalFocus: finalFocus, "data-dialog-variant": "modal" }), children))));
220
+ }
221
+ function DialogModalTitle(_a) {
222
+ var { children } = _a, rest = __rest(_a, ["children"]);
223
+ return (React__default.createElement(DialogTitle$1, Object.assign({}, rest, { render: titleProps => (React__default.createElement(Heading, Object.assign({ level: 2 }, titleProps), children)) })));
224
+ }
225
+ function DialogModalClose(_a) {
226
+ var { children, render } = _a, rest = __rest(_a, ["children", "render"]);
227
+ const closeProps = mergeProps({ className: styles.close }, Object.assign({}, rest));
228
+ return (React__default.createElement(DialogClose$1, Object.assign({}, closeProps, { render: resolveComposableCloseRender({ children, render }) })));
229
+ }
230
+ function DialogModalBody(_a) {
231
+ var { children, fullBleed = false, className, style } = _a, rest = __rest(_a, ["children", "fullBleed", "className", "style"]);
232
+ const insideScrollArea = useInsideDialogScrollArea();
233
+ // Owns padding and the accessible-description wiring. On its own the body is
234
+ // the scroll region, so it wraps itself in a `DialogScrollArea`; inside an
235
+ // explicit `Dialog.ScrollArea` it renders as plain content (no nested scroller).
236
+ const content = (React__default.createElement(DialogDescription, { render: descriptionProps => (React__default.createElement("div", Object.assign({}, rest, descriptionProps, { className: classnames(styles.body, { [styles.fullBleed]: fullBleed }, className), style: style }), children)) }));
237
+ return insideScrollArea ? (content) : (React__default.createElement(DialogScrollArea$1, null, content));
238
+ }
239
+ const DialogModal = Object.assign(DialogModalRoot, {
240
+ Trigger: DialogModalTrigger,
241
+ Content: DialogModalContent,
242
+ Header: SharedDialogHeader,
243
+ Title: DialogModalTitle,
244
+ Close: DialogModalClose,
245
+ Body: DialogModalBody,
246
+ Footer: SharedDialogFooter,
247
+ // The modal keeps the plain native-scrollbar scroll region.
248
+ ScrollArea: DialogScrollArea$1,
249
+ });
250
+
251
+ /**
252
+ * Creates a handle that opens/closes a `Dialog` from detached triggers (e.g. an
253
+ * action inside a `Combobox`/`Menu`) without prop-drilling open state. Mirrors
254
+ * BaseUI's `Dialog.createHandle()`.
255
+ */
256
+ function createHandle() {
257
+ return createDialogHandle();
258
+ }
259
+ /**
260
+ * Returns the active shell — the bottom sheet on mobile, otherwise the centered
261
+ * modal. Each shell is a root component with
262
+ * its parts attached, so callers do `const Shell = useDialogShell()` then render
263
+ * `<Shell.Whatever />`.
264
+ */
265
+ function useDialogShell() {
266
+ const { isMobile } = useDialogContext();
267
+ return isMobile ? DialogBottomSheet : DialogModal;
268
+ }
269
+ /**
270
+ * The Dialog root is a centered modal on
271
+ * desktop, a bottom sheet on mobile. Compose it with `Dialog.Trigger`,
272
+ * `Dialog.Content`, and the layout parts.
273
+ */
274
+ function DialogRoot(_a) {
275
+ var { onRequestClose, size = "base", type = "dialog", finalFocus, children, onOpenChange } = _a, rest = __rest(_a, ["onRequestClose", "size", "type", "finalFocus", "children", "onOpenChange"]);
276
+ function handleOpenChange(nextOpen, eventDetails) {
277
+ if (!nextOpen) {
278
+ onRequestClose === null || onRequestClose === void 0 ? void 0 : onRequestClose(eventDetails);
279
+ }
280
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(nextOpen, eventDetails);
281
+ }
282
+ const { width } = useWindowDimensions();
283
+ const isMobile = width !== undefined && width <= SMALL_SCREEN_BREAKPOINT;
284
+ // The shell is the root component; its parts are reached via `useDialogShell`
285
+ // in each part below (the Menu pattern).
286
+ const RootComponent = isMobile ? DialogBottomSheet : DialogModal;
287
+ const rootProps = Object.assign(Object.assign({}, rest), { onOpenChange: handleOpenChange, modal: true, disablePointerDismissal: type === "alertdialog", children });
288
+ return (React__default.createElement(DialogProvider, { type: type, size: size, isMobile: isMobile, finalFocus: finalFocus },
289
+ React__default.createElement(RootComponent, Object.assign({}, rootProps))));
290
+ }
291
+ /**
292
+ * Opens the dialog. Accepts an Atlantis `<Button>` (via `children` or `render`)
293
+ * or links a detached `Dialog` through a matching `handle`.
294
+ */
295
+ function DialogTrigger(props) {
296
+ const Shell = useDialogShell();
297
+ return React__default.createElement(Shell.Trigger, Object.assign({}, props));
298
+ }
299
+ /**
300
+ * The dialog popup surface. Holds the `Header`, `Body`, and `Footer`; renders as a
301
+ * centered modal on desktop and a bottom sheet on mobile.
302
+ */
303
+ function DialogContent(props) {
304
+ const Shell = useDialogShell();
305
+ return React__default.createElement(Shell.Content, Object.assign({}, props));
306
+ }
307
+ /** Layout-only header row — typically a `Dialog.Title` and a `Dialog.Close`. */
308
+ function DialogHeader(props) {
309
+ const Shell = useDialogShell();
310
+ return React__default.createElement(Shell.Header, Object.assign({}, props));
311
+ }
312
+ /** The dialog's accessible title; wired to `aria-labelledby`. */
313
+ function DialogTitle(props) {
314
+ const Shell = useDialogShell();
315
+ return React__default.createElement(Shell.Title, Object.assign({}, props));
316
+ }
317
+ /** A dismiss button that closes the dialog. */
318
+ function DialogClose(props) {
319
+ const Shell = useDialogShell();
320
+ return React__default.createElement(Shell.Close, Object.assign({}, props));
321
+ }
322
+ /** The dialog body; wired to `aria-describedby`. */
323
+ function DialogBody(props) {
324
+ const Shell = useDialogShell();
325
+ return React__default.createElement(Shell.Body, Object.assign({}, props));
326
+ }
327
+ /** Footer region for actions or supporting content. */
328
+ function DialogFooter(props) {
329
+ const Shell = useDialogShell();
330
+ return React__default.createElement(Shell.Footer, Object.assign({}, props));
331
+ }
332
+ /**
333
+ * Advanced scroll region. Delegates to the shell: the modal uses a native
334
+ * scrollbar; the sheet uses the BottomSheet primitive's scroll area (with fade).
335
+ */
336
+ function DialogScrollArea(props) {
337
+ const Shell = useDialogShell();
338
+ return React__default.createElement(Shell.ScrollArea, Object.assign({}, props));
339
+ }
340
+ const Dialog = DialogRoot;
341
+ Dialog.Trigger = DialogTrigger;
342
+ Dialog.Content = DialogContent;
343
+ Dialog.Header = DialogHeader;
344
+ Dialog.Title = DialogTitle;
345
+ Dialog.Close = DialogClose;
346
+ Dialog.Body = DialogBody;
347
+ Dialog.Footer = DialogFooter;
348
+ Dialog.Actions = DialogActions;
349
+ Dialog.ScrollArea = DialogScrollArea;
350
+ Dialog.createHandle = createHandle;
351
+
352
+ /**
353
+ * Selector for elements that can receive focus. Mirrors the common
354
+ * "focus-trap"/"tabbable" baseline without pulling in a dependency: anything
355
+ * natively focusable plus explicit positive/zero tabindex, minus disabled
356
+ * controls and `tabindex="-1"`.
357
+ */
358
+ const FOCUSABLE_SELECTOR = [
359
+ "a[href]",
360
+ "area[href]",
361
+ "button:not([disabled])",
362
+ "input:not([disabled]):not([type='hidden'])",
363
+ "select:not([disabled])",
364
+ "textarea:not([disabled])",
365
+ "iframe",
366
+ "audio[controls]",
367
+ "video[controls]",
368
+ "[contenteditable]:not([contenteditable='false'])",
369
+ '[tabindex]:not([tabindex="-1"])',
370
+ ].join(",");
371
+ function isVisible(element) {
372
+ if (typeof element.checkVisibility === "function") {
373
+ return element.checkVisibility();
374
+ }
375
+ return Boolean(element.offsetWidth ||
376
+ element.offsetHeight ||
377
+ element.getClientRects().length);
378
+ }
379
+ /**
380
+ * The first focusable element within `container` (the container itself counts if
381
+ * it is focusable). Skips hidden elements. Returns `null` when nothing inside is
382
+ * focusable.
383
+ */
384
+ function getFirstFocusableWithin(container) {
385
+ var _a;
386
+ if (!container)
387
+ return null;
388
+ if (container.matches(FOCUSABLE_SELECTOR) && isVisible(container)) {
389
+ return container;
390
+ }
391
+ const candidates = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));
392
+ return (_a = candidates.find(isVisible)) !== null && _a !== void 0 ? _a : null;
393
+ }
394
+ /**
395
+ * Builds a `finalFocus` callback for `Dialog` that keeps keyboard focus near the
396
+ * trigger even when the trigger unmounts while the dialog is open (e.g. an
397
+ * autocomplete "Create" action whose row is replaced by a card on save).
398
+ *
399
+ * Resolution order on close:
400
+ * 1. the `trigger`, if it is still connected;
401
+ * 2. otherwise the first focusable element inside `boundary` (the "closest"
402
+ * landing spot);
403
+ * 3. otherwise `null`, which lets BaseUI fall back to its default behavior.
404
+ *
405
+ * @example
406
+ * const triggerRef = useRef<HTMLInputElement>(null);
407
+ * const boundaryRef = useRef<HTMLDivElement>(null);
408
+ * <div ref={boundaryRef}>…</div>
409
+ * <Dialog
410
+ * handle={handle}
411
+ * finalFocus={getDialogReturnFocus({ trigger: triggerRef, boundary: boundaryRef })}
412
+ * >
413
+ */
414
+ function getDialogReturnFocus({ trigger, boundary, }) {
415
+ return () => {
416
+ const target = trigger === null || trigger === void 0 ? void 0 : trigger.current;
417
+ if (target === null || target === void 0 ? void 0 : target.isConnected)
418
+ return target;
419
+ return getFirstFocusableWithin(boundary.current);
420
+ };
421
+ }
422
+
423
+ export { Dialog as D, getFirstFocusableWithin as a, getDialogReturnFocus as g };
@@ -417,6 +417,7 @@ export function IconSizesExample() {
417
417
  | | `future` |
418
418
  | | `history` |
419
419
  | | `import` |
420
+ | | `merge` |
420
421
  | | `redo` |
421
422
  | | `remove` |
422
423
  | | `search` |
package/dist/index.cjs CHANGED
@@ -34,6 +34,7 @@ require('./DataTableTable-cjs.js');
34
34
  var reactTable = require('@tanstack/react-table');
35
35
  var DatePicker = require('./DatePicker-cjs.js');
36
36
  var DescriptionList = require('./DescriptionList-cjs.js');
37
+ var dialogReturnFocus = require('./dialogReturnFocus-cjs.js');
37
38
  var Disclosure = require('./Disclosure-cjs.js');
38
39
  var Divider = require('./Divider-cjs.js');
39
40
  var DrawerGrid = require('./DrawerGrid-cjs.js');
@@ -169,18 +170,20 @@ require('./DataListAction-cjs.js');
169
170
  require('./DataListLayoutActions-cjs.js');
170
171
  require('./DataListStatusBar-cjs.js');
171
172
  require('./clsx-cjs.js');
172
- require('./useFormFieldFocus-cjs.js');
173
- require('filesize');
174
- require('./GridCell-cjs.js');
175
- require('./FormFieldPostFix-cjs.js');
176
- require('axios');
177
- require('./useRenderElement-cjs.js');
178
173
  require('./buttonRenderAdapter-cjs.js');
179
- require('./BottomSheet-cjs.js');
174
+ require('./useRenderElement-cjs.js');
180
175
  require('./ScrollAreaViewport-cjs.js');
181
176
  require('./InternalBackdrop-cjs.js');
182
177
  require('./useButton-cjs.js');
183
178
  require('./clamp-cjs.js');
179
+ require('./BottomSheet-cjs.js');
180
+ require('./DrawerTitle-cjs.js');
181
+ require('./DrawerDescription-cjs.js');
182
+ require('./useFormFieldFocus-cjs.js');
183
+ require('filesize');
184
+ require('./GridCell-cjs.js');
185
+ require('./FormFieldPostFix-cjs.js');
186
+ require('axios');
184
187
  require('./OverlaySeparator-cjs.js');
185
188
  require('./Separator-cjs.js');
186
189
  require('./MenuSubmenuTrigger-cjs.js');
@@ -244,6 +247,9 @@ Object.defineProperty(exports, "createColumnHelper", {
244
247
  });
245
248
  exports.DatePicker = DatePicker.DatePicker;
246
249
  exports.DescriptionList = DescriptionList.DescriptionList;
250
+ exports.Dialog = dialogReturnFocus.Dialog;
251
+ exports.getDialogReturnFocus = dialogReturnFocus.getDialogReturnFocus;
252
+ exports.getFirstFocusableWithin = dialogReturnFocus.getFirstFocusableWithin;
247
253
  exports.Disclosure = Disclosure.Disclosure;
248
254
  exports.Divider = Divider.Divider;
249
255
  exports.Drawer = DrawerGrid.Drawer;
package/dist/index.d.mts CHANGED
@@ -24,6 +24,7 @@ export * from "./DataList";
24
24
  export { DataTable, createColumnHelper } from "./DataTable";
25
25
  export * from "./DatePicker";
26
26
  export * from "./DescriptionList";
27
+ export * from "./Dialog";
27
28
  export * from "./Disclosure";
28
29
  export * from "./Divider";
29
30
  export * from "./Drawer";
package/dist/index.d.ts CHANGED
@@ -24,6 +24,7 @@ export * from "./DataList";
24
24
  export { DataTable, createColumnHelper } from "./DataTable";
25
25
  export * from "./DatePicker";
26
26
  export * from "./DescriptionList";
27
+ export * from "./Dialog";
27
28
  export * from "./Disclosure";
28
29
  export * from "./Divider";
29
30
  export * from "./Drawer";
package/dist/index.mjs CHANGED
@@ -32,6 +32,7 @@ import './DataTableTable-es.js';
32
32
  export { createColumnHelper } from '@tanstack/react-table';
33
33
  export { D as DatePicker } from './DatePicker-es.js';
34
34
  export { D as DescriptionList } from './DescriptionList-es.js';
35
+ export { D as Dialog, g as getDialogReturnFocus, a as getFirstFocusableWithin } from './dialogReturnFocus-es.js';
35
36
  export { D as Disclosure } from './Disclosure-es.js';
36
37
  export { D as Divider } from './Divider-es.js';
37
38
  export { D as Drawer, a as DrawerGrid } from './DrawerGrid-es.js';
@@ -167,18 +168,20 @@ import './DataListAction-es.js';
167
168
  import './DataListLayoutActions-es.js';
168
169
  import './DataListStatusBar-es.js';
169
170
  import './clsx-es.js';
170
- import './useFormFieldFocus-es.js';
171
- import 'filesize';
172
- import './GridCell-es.js';
173
- import './FormFieldPostFix-es.js';
174
- import 'axios';
175
- import './useRenderElement-es.js';
176
171
  import './buttonRenderAdapter-es.js';
177
- import './BottomSheet-es.js';
172
+ import './useRenderElement-es.js';
178
173
  import './ScrollAreaViewport-es.js';
179
174
  import './InternalBackdrop-es.js';
180
175
  import './useButton-es.js';
181
176
  import './clamp-es.js';
177
+ import './BottomSheet-es.js';
178
+ import './DrawerTitle-es.js';
179
+ import './DrawerDescription-es.js';
180
+ import './useFormFieldFocus-es.js';
181
+ import 'filesize';
182
+ import './GridCell-es.js';
183
+ import './FormFieldPostFix-es.js';
184
+ import 'axios';
182
185
  import './OverlaySeparator-es.js';
183
186
  import './Separator-es.js';
184
187
  import './MenuSubmenuTrigger-es.js';
@@ -1,5 +1,14 @@
1
1
  import React from "react";
2
- import type { BottomSheetContentProps, BottomSheetPopupProps, BottomSheetProps, BottomSheetRootProps, BottomSheetTriggerProps } from "./BottomSheet.types";
2
+ import type { BottomSheetContentProps, BottomSheetPopupProps, BottomSheetProps, BottomSheetRootProps, BottomSheetScrollAreaProps, BottomSheetTriggerProps } from "./BottomSheet.types";
3
+ /**
4
+ * Whether `BottomSheet.Popup` renders its own built-in scroll area (with the
5
+ * bottom fade). Defaults to `true` — the popup wraps its `children` in a
6
+ * scrollable viewport. A consumer that manages its own scroll region (e.g.
7
+ * `Dialog`, which pins a header/footer outside a single scroll area) provides
8
+ * `false` to opt out. Mirrors how `Dialog.Body` decides whether to wrap itself
9
+ * via context rather than a prop.
10
+ */
11
+ export declare const BottomSheetScrollAreaContext: React.Context<boolean>;
3
12
  declare function BottomSheetTrigger({ children, render, className, ...triggerProps }: BottomSheetTriggerProps): React.JSX.Element;
4
13
  /**
5
14
  * Thin pass-through to the Drawer root: a single owner manages open state, and
@@ -8,6 +17,14 @@ declare function BottomSheetTrigger({ children, render, className, ...triggerPro
8
17
  */
9
18
  declare function BottomSheetRoot({ children, ...rootProps }: BottomSheetRootProps): React.JSX.Element;
10
19
  declare function BottomSheetPopup({ children, className, initialFocus, finalFocus, ref, ...popupProps }: BottomSheetPopupProps): React.JSX.Element;
20
+ /**
21
+ * The sheet's scroll region: a styled BaseUI `ScrollArea` (native scrollbar) with
22
+ * a bottom fade that hints at more content while the viewport can scroll further.
23
+ * `BottomSheet.Popup` renders this automatically unless a consumer opts out via
24
+ * `BottomSheetScrollAreaContext` and composes it (or its own scroll region)
25
+ * directly.
26
+ */
27
+ declare function BottomSheetScrollArea({ children, className, style, }: BottomSheetScrollAreaProps): React.JSX.Element;
11
28
  declare function BottomSheetContent({ children, className, ref, ...contentProps }: BottomSheetContentProps): React.JSX.Element;
12
29
  declare function BottomSheetComponent({ children, className, initialFocus, finalFocus, open, defaultOpen, onOpenChange, ref, ...contentProps }: BottomSheetProps): React.JSX.Element;
13
30
  export declare const BottomSheet: typeof BottomSheetComponent & {
@@ -15,5 +32,6 @@ export declare const BottomSheet: typeof BottomSheetComponent & {
15
32
  Trigger: typeof BottomSheetTrigger;
16
33
  Popup: typeof BottomSheetPopup;
17
34
  Content: typeof BottomSheetContent;
35
+ ScrollArea: typeof BottomSheetScrollArea;
18
36
  };
19
37
  export {};
@@ -4,6 +4,14 @@ type BottomSheetPopupBaseProps = Omit<React.ComponentPropsWithRef<typeof BaseDra
4
4
  export interface BottomSheetPopupProps extends BottomSheetPopupBaseProps {
5
5
  readonly className?: string;
6
6
  }
7
+ export interface BottomSheetScrollAreaProps {
8
+ /** Scrollable content. */
9
+ readonly children: React.ReactNode;
10
+ /** Class name applied to the scroll area root. */
11
+ readonly className?: string;
12
+ /** Inline styles applied to the scroll area root. */
13
+ readonly style?: React.CSSProperties;
14
+ }
7
15
  type BottomSheetContentBaseProps = Omit<React.ComponentPropsWithRef<typeof BaseDrawer.Content>, "className">;
8
16
  export interface BottomSheetContentProps extends BottomSheetContentBaseProps {
9
17
  readonly className?: string;
@@ -23,3 +23,4 @@ require('../../clamp-cjs.js');
23
23
 
24
24
 
25
25
  exports.BottomSheet = BottomSheet.BottomSheet;
26
+ exports.BottomSheetScrollAreaContext = BottomSheet.BottomSheetScrollAreaContext;
@@ -1,2 +1,2 @@
1
1
  export * from "./BottomSheet";
2
- export type { BottomSheetRootProps, BottomSheetPopupProps, BottomSheetContentProps, BottomSheetTriggerProps, BottomSheetProps, } from "./BottomSheet.types";
2
+ export type { BottomSheetRootProps, BottomSheetPopupProps, BottomSheetContentProps, BottomSheetScrollAreaProps, BottomSheetTriggerProps, BottomSheetProps, } from "./BottomSheet.types";
@@ -1,4 +1,4 @@
1
- export { B as BottomSheet } from '../../BottomSheet-es.js';
1
+ export { a as BottomSheet, B as BottomSheetScrollAreaContext } from '../../BottomSheet-es.js';
2
2
  import '../../tslib.es6-es.js';
3
3
  import 'react';
4
4
  import 'classnames';
@@ -36,6 +36,7 @@ require('../NumberFieldInput-cjs.js');
36
36
 
37
37
  exports.OverlaySeparator = OverlaySeparator.OverlaySeparator;
38
38
  exports.BottomSheet = BottomSheet.BottomSheet;
39
+ exports.BottomSheetScrollAreaContext = BottomSheet.BottomSheetScrollAreaContext;
39
40
  exports.HelperText = HelperText.HelperText;
40
41
  exports.ComboboxPrimitive = ComboboxPrimitive.ComboboxPrimitive;
41
42
  exports.InputNumberExperimental = InputNumberExperimental.InputNumberExperimental;
@@ -1,5 +1,5 @@
1
1
  export { O as OverlaySeparator } from '../OverlaySeparator-es.js';
2
- export { B as BottomSheet } from '../BottomSheet-es.js';
2
+ export { a as BottomSheet, B as BottomSheetScrollAreaContext } from '../BottomSheet-es.js';
3
3
  export { H as HelperText } from '../HelperText-es.js';
4
4
  export { C as ComboboxPrimitive } from '../ComboboxPrimitive-es.js';
5
5
  export { I as InputNumberExperimental } from '../InputNumberExperimental-es.js';