@jobber/components 9.5.3 → 9.6.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 (178) hide show
  1. package/Combobox.d.ts +1 -0
  2. package/Combobox.js +17 -0
  3. package/dist/Autocomplete/index.cjs +0 -2
  4. package/dist/Autocomplete/index.mjs +0 -2
  5. package/dist/Autocomplete-cjs.js +4 -5
  6. package/dist/Autocomplete-es.js +1 -2
  7. package/dist/Card/index.cjs +2 -2
  8. package/dist/Card/index.mjs +2 -2
  9. package/dist/Chip/index.cjs +0 -2
  10. package/dist/Chip/index.mjs +0 -2
  11. package/dist/Chips/InternalChipDismissible/hooks/index.cjs +0 -2
  12. package/dist/Chips/InternalChipDismissible/hooks/index.mjs +0 -2
  13. package/dist/Chips/InternalChipDismissible/index.cjs +0 -2
  14. package/dist/Chips/InternalChipDismissible/index.mjs +0 -2
  15. package/dist/Chips/index.cjs +0 -2
  16. package/dist/Chips/index.mjs +0 -2
  17. package/dist/Combobox/Combobox.contentSlots.d.ts +5 -0
  18. package/dist/Combobox/Combobox.context.d.ts +34 -0
  19. package/dist/Combobox/Combobox.d.ts +69 -0
  20. package/dist/Combobox/Combobox.footer.d.ts +7 -0
  21. package/dist/Combobox/Combobox.items.d.ts +25 -0
  22. package/dist/Combobox/Combobox.responsive.d.ts +54 -0
  23. package/dist/Combobox/Combobox.selectAll.d.ts +4 -0
  24. package/dist/Combobox/Combobox.slotFactory.d.ts +7 -0
  25. package/dist/Combobox/Combobox.testUtils.d.ts +12 -0
  26. package/dist/Combobox/Combobox.types.d.ts +166 -0
  27. package/dist/Combobox/index.cjs +35 -0
  28. package/dist/Combobox/index.d.ts +2 -0
  29. package/dist/Combobox/index.mjs +29 -0
  30. package/dist/Combobox-cjs.js +675 -0
  31. package/dist/Combobox-es.js +673 -0
  32. package/dist/ComboboxPrimitive-cjs.js +88 -47
  33. package/dist/ComboboxPrimitive-es.js +39 -23
  34. package/dist/{ComboboxChipRemove-cjs.js → ComboboxSeparator-cjs.js} +501 -455
  35. package/dist/{ComboboxChipRemove-es.js → ComboboxSeparator-es.js} +500 -456
  36. package/dist/ConfirmationModal/index.cjs +0 -2
  37. package/dist/ConfirmationModal/index.mjs +0 -2
  38. package/dist/ContentBlock/index.cjs +1 -0
  39. package/dist/ContentBlock/index.mjs +1 -0
  40. package/dist/ContentBlock-cjs.js +2 -18
  41. package/dist/ContentBlock-es.js +1 -17
  42. package/dist/DataDump/index.cjs +2 -2
  43. package/dist/DataDump/index.mjs +2 -2
  44. package/dist/DataList/components/DataListActions/index.cjs +0 -2
  45. package/dist/DataList/components/DataListActions/index.mjs +0 -2
  46. package/dist/DataList/components/DataListBulkActions/index.cjs +0 -2
  47. package/dist/DataList/components/DataListBulkActions/index.mjs +0 -2
  48. package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +0 -2
  49. package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +0 -2
  50. package/dist/DataList/components/DataListFilters/index.cjs +0 -2
  51. package/dist/DataList/components/DataListFilters/index.mjs +0 -2
  52. package/dist/DataList/components/DataListHeader/index.cjs +0 -2
  53. package/dist/DataList/components/DataListHeader/index.mjs +0 -2
  54. package/dist/DataList/components/DataListItem/index.cjs +0 -2
  55. package/dist/DataList/components/DataListItem/index.mjs +0 -2
  56. package/dist/DataList/components/DataListItemActions/index.cjs +0 -2
  57. package/dist/DataList/components/DataListItemActions/index.mjs +0 -2
  58. package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +0 -2
  59. package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +0 -2
  60. package/dist/DataList/components/DataListItems/index.cjs +0 -2
  61. package/dist/DataList/components/DataListItems/index.mjs +0 -2
  62. package/dist/DataList/components/DataListLayout/index.cjs +0 -2
  63. package/dist/DataList/components/DataListLayout/index.mjs +0 -2
  64. package/dist/DataList/components/DataListLayoutActions/index.cjs +0 -2
  65. package/dist/DataList/components/DataListLayoutActions/index.mjs +0 -2
  66. package/dist/DataList/index.cjs +0 -2
  67. package/dist/DataList/index.mjs +0 -2
  68. package/dist/DatePicker/index.cjs +0 -2
  69. package/dist/DatePicker/index.mjs +0 -2
  70. package/dist/DatePicker-cjs.js +4 -5
  71. package/dist/DatePicker-es.js +1 -2
  72. package/dist/Dialog/index.cjs +2 -2
  73. package/dist/Dialog/index.mjs +2 -2
  74. package/dist/DrawerDescription-cjs.js +268 -99
  75. package/dist/DrawerDescription-es.js +270 -103
  76. package/dist/DrawerTitle-cjs.js +2 -6
  77. package/dist/DrawerTitle-es.js +3 -7
  78. package/dist/FieldDescription-cjs.js +184 -181
  79. package/dist/FieldDescription-es.js +187 -184
  80. package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +0 -2
  81. package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +0 -2
  82. package/dist/FilterPicker/components/FilterPickerContent/index.cjs +0 -2
  83. package/dist/FilterPicker/components/FilterPickerContent/index.mjs +0 -2
  84. package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +0 -2
  85. package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +0 -2
  86. package/dist/FilterPicker/index.cjs +0 -2
  87. package/dist/FilterPicker/index.mjs +0 -2
  88. package/dist/FilterPickerContent-cjs.js +4 -5
  89. package/dist/FilterPickerContent-es.js +1 -2
  90. package/dist/FormatFile/index.cjs +0 -2
  91. package/dist/FormatFile/index.mjs +0 -2
  92. package/dist/Gallery/index.cjs +0 -2
  93. package/dist/Gallery/index.mjs +0 -2
  94. package/dist/InputDate/index.cjs +0 -2
  95. package/dist/InputDate/index.mjs +0 -2
  96. package/dist/InputNumber/index.cjs +2 -2
  97. package/dist/InputNumber/index.mjs +2 -2
  98. package/dist/InputNumber/types.d.ts +5 -0
  99. package/dist/InputNumber-cjs.js +2 -2
  100. package/dist/InputNumber-es.js +2 -2
  101. package/dist/LightBox/index.cjs +0 -2
  102. package/dist/LightBox/index.mjs +0 -2
  103. package/dist/Menu/index.cjs +2 -2
  104. package/dist/Menu/index.mjs +2 -2
  105. package/dist/Menu-cjs.js +4 -5
  106. package/dist/Menu-es.js +1 -2
  107. package/dist/MenuSubmenuTrigger-cjs.js +206 -164
  108. package/dist/MenuSubmenuTrigger-es.js +194 -152
  109. package/dist/Modal/index.cjs +0 -2
  110. package/dist/Modal/index.mjs +1 -3
  111. package/dist/NumberFieldInput-cjs.js +187 -308
  112. package/dist/NumberFieldInput-es.js +187 -308
  113. package/dist/Page/index.cjs +2 -2
  114. package/dist/Page/index.mjs +2 -2
  115. package/dist/Popover/index.cjs +0 -2
  116. package/dist/Popover/index.mjs +0 -2
  117. package/dist/Popover-cjs.js +7 -8
  118. package/dist/Popover-es.js +1 -2
  119. package/dist/ScrollAreaViewport-cjs.js +747 -1093
  120. package/dist/ScrollAreaViewport-es.js +734 -1081
  121. package/dist/Select/index.cjs +2 -2
  122. package/dist/Select/index.mjs +2 -2
  123. package/dist/SelectGroupLabel-cjs.js +207 -306
  124. package/dist/SelectGroupLabel-es.js +203 -302
  125. package/dist/SelectPrimitive-cjs.js +11 -17
  126. package/dist/SelectPrimitive-es.js +1 -7
  127. package/dist/Tooltip/index.cjs +0 -2
  128. package/dist/Tooltip/index.mjs +0 -2
  129. package/dist/Tooltip-cjs.js +6 -7
  130. package/dist/Tooltip-es.js +1 -2
  131. package/dist/buttonRenderAdapter-cjs.js +2 -1
  132. package/dist/buttonRenderAdapter-es.js +2 -1
  133. package/dist/dialogReturnFocus-cjs.js +1 -1
  134. package/dist/dialogReturnFocus-es.js +2 -2
  135. package/dist/docs/InputNumber/InputNumber.md +2 -2
  136. package/dist/docs/SelectPrimitive/SelectPrimitive.md +1 -1
  137. package/dist/floating-ui.react-cjs.js +2557 -185
  138. package/dist/floating-ui.react-es.js +2512 -149
  139. package/dist/getMappedBreakpointWidth-cjs.js +21 -0
  140. package/dist/getMappedBreakpointWidth-es.js +18 -0
  141. package/dist/index.cjs +15 -10
  142. package/dist/index.d.mts +1 -0
  143. package/dist/index.d.ts +1 -0
  144. package/dist/index.mjs +14 -10
  145. package/dist/mergeClassName-cjs.js +12 -0
  146. package/dist/mergeClassName-es.js +10 -0
  147. package/dist/primitives/BottomSheet/index.cjs +1 -1
  148. package/dist/primitives/BottomSheet/index.mjs +1 -1
  149. package/dist/primitives/ComboboxPrimitive/ComboboxPrimitive.d.ts +2 -2
  150. package/dist/primitives/ComboboxPrimitive/index.cjs +4 -5
  151. package/dist/primitives/ComboboxPrimitive/index.mjs +4 -5
  152. package/dist/primitives/SelectPrimitive/index.cjs +1 -2
  153. package/dist/primitives/SelectPrimitive/index.mjs +1 -2
  154. package/dist/primitives/index.cjs +3 -3
  155. package/dist/primitives/index.mjs +3 -3
  156. package/dist/scrollEdges-cjs.js +1 -11
  157. package/dist/scrollEdges-es.js +1 -11
  158. package/dist/scrollable-cjs.js +58 -0
  159. package/dist/scrollable-es.js +54 -0
  160. package/dist/styles.css +759 -42
  161. package/dist/unstyledPrimitives/index.cjs +720 -478
  162. package/dist/unstyledPrimitives/index.mjs +663 -421
  163. package/dist/useCompositeListItem-cjs.js +2108 -361
  164. package/dist/useCompositeListItem-es.js +2076 -331
  165. package/dist/useLabel-cjs.js +2 -3
  166. package/dist/useLabel-es.js +2 -3
  167. package/dist/useRenderElement-cjs.js +9 -7
  168. package/dist/useRenderElement-es.js +9 -7
  169. package/dist/useScrollLock-cjs.js +819 -344
  170. package/dist/useScrollLock-es.js +732 -296
  171. package/dist/useScrollToActive-cjs.js +4 -5
  172. package/dist/useScrollToActive-es.js +1 -2
  173. package/dist/utils/meta/meta.json +43 -1
  174. package/package.json +8 -3
  175. package/dist/floating-ui.react-dom-cjs.js +0 -2103
  176. package/dist/floating-ui.react-dom-es.js +0 -2072
  177. package/dist/floating-ui.utils-cjs.js +0 -342
  178. package/dist/floating-ui.utils-es.js +0 -300
@@ -26,10 +26,223 @@ function _interopNamespaceDefault(e) {
26
26
 
27
27
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
28
28
 
29
- const stateAttributesMapping$1 = {
30
- ...useScrollLock.popupStateMapping,
31
- ...useScrollLock.transitionStatusMapping
32
- };
29
+ /**
30
+ * Minimal store contract exposed by popup handles to detached triggers.
31
+ *
32
+ * Detached triggers read `store` during render and subscribe to be notified when the handle switches
33
+ * between its fallback store and a root's live store.
34
+ *
35
+ * @template HandleStore Store shape exposed to detached triggers.
36
+ */
37
+
38
+ /**
39
+ * Store shape holding a trigger registry, required by `BasePopupHandle.openByTrigger` to resolve a
40
+ * trigger element by id on both the attached root's store and the fallback store.
41
+ */
42
+
43
+ /**
44
+ * Store shape required by `BasePopupHandle.openByTrigger`/`closePopup` to drive open/close state.
45
+ * Only the root-owned `Store` needs this — the `HandleStore` view exposed to detached triggers may
46
+ * omit `setOpen` entirely (as Dialog and PreviewCard's do) since it is never called while detached.
47
+ */
48
+
49
+ /**
50
+ * Shared implementation for popup handles that coordinate detached triggers with a mounted root.
51
+ *
52
+ * Subclasses provide the component-specific imperative methods, while this base class owns the
53
+ * fallback store, root store attachment stack, subscriber notifications, and development warning for
54
+ * overlapping roots.
55
+ *
56
+ * @template HandleStore Store shape exposed to detached triggers.
57
+ * @template Store Root-owned store attached by the component root.
58
+ */
59
+ class BasePopupHandle {
60
+ /**
61
+ * Stores of every root currently using this handle, in attach order. A handle is meant to be used
62
+ * by a single mounted root, but roots can transiently overlap (e.g. during an animated route
63
+ * transition), so this stack lets `attachStore`'s cleanup restore the previous root instead of
64
+ * leaving a still-mounted root uncontrollable when a newer overlapping root detaches first.
65
+ */
66
+ attachedStores = [];
67
+
68
+ /**
69
+ * Store of the root that currently controls the handle: the most recently attached one still
70
+ * mounted, or `null` when no root is attached. Imperative methods are no-ops while this is `null`.
71
+ */
72
+ attachedStoreValue = null;
73
+
74
+ /**
75
+ * Listeners notified when `attachedStore` changes, so detached triggers can follow the store pointer.
76
+ */
77
+ storeListeners = new Set();
78
+
79
+ /**
80
+ * Creates a handle backed by the store used while no root is attached.
81
+ *
82
+ * @param fallbackStore Inert, closed store handed to detached triggers while no root is attached,
83
+ * so they can render and register without a mounted root. Triggers register into whichever store
84
+ * `store` currently resolves to, so while detached they live in this store's trigger map and
85
+ * migrate themselves to the root's store (and back) as it attaches/detaches.
86
+ * @param componentName Component name used to prefix dev warnings, e.g. `'Menu'` produces
87
+ * `MenuHandle.open()` in warning text.
88
+ * @param throwOnMissingTrigger Whether `open(triggerId)` throws when no trigger with that id is
89
+ * registered. Anchored popups (Menu, Popover, Tooltip, PreviewCard) need a trigger to anchor to,
90
+ * so they throw; Dialog is not anchored and instead opens unassociated with a dev warning.
91
+ */
92
+ constructor(fallbackStore, componentName, throwOnMissingTrigger = true) {
93
+ this.fallbackStore = fallbackStore;
94
+ this.componentName = componentName;
95
+ this.throwOnMissingTrigger = throwOnMissingTrigger;
96
+ }
97
+ get attachedStore() {
98
+ return this.attachedStoreValue;
99
+ }
100
+
101
+ /**
102
+ * Store that detached triggers read from: the attached root's store, or an inert fallback store
103
+ * used while no root is attached.
104
+ * @internal
105
+ */
106
+ get store() {
107
+ return this.attachedStoreValue ?? this.fallbackStore;
108
+ }
109
+
110
+ /**
111
+ * Stable fallback store used for server rendering and hydration. Root stores cannot be recorded on
112
+ * the handle during render because a handle can be shared by concurrent server-rendered requests.
113
+ * @internal
114
+ */
115
+ get serverStore() {
116
+ return this.fallbackStore;
117
+ }
118
+
119
+ /**
120
+ * Subscribes to changes of the attached store pointer so detached triggers re-render and re-bind
121
+ * when a root attaches or detaches. Returns a function that removes the listener.
122
+ * @internal
123
+ */
124
+ subscribeStore(listener) {
125
+ this.storeListeners.add(listener);
126
+ return () => {
127
+ this.storeListeners.delete(listener);
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Points the handle at a root's store and notifies subscribers so detached triggers re-render and
133
+ * re-register into it (their registration ref re-fires on the store-pointer change). Returns a
134
+ * cleanup function that detaches the store again.
135
+ * @internal
136
+ */
137
+ attachStore(newStore) {
138
+ this.attachedStores.push(newStore);
139
+ this.setActiveStore(newStore);
140
+ if (process.env.NODE_ENV !== 'production') {
141
+ if (this.attachedStores.length > 1) {
142
+ // More than one root is attached at once. This is usually a transient overlap during an
143
+ // animated route transition, where the outgoing root unmounts shortly after the incoming
144
+ // one mounts. Defer the check by a frame and only warn if the overlap is still present once
145
+ // the transition has settled (more than one root stayed mounted), so a clean handoff doesn't
146
+ // warn regardless of the exact unmount timing.
147
+ // The warning frame only exists in development; it is created lazily so it never appears on
148
+ // instances in production (like `controlledValues` in `ReactStore`).
149
+ const dev = this;
150
+ (dev.overlapWarningFrame ??= useScrollLock.AnimationFrame.create()).request(() => {
151
+ if (this.attachedStores.length > 1) {
152
+ console.warn('Base UI: A handle is attached to more than one mounted root at the same time. ' + 'The most recently mounted root takes over and the previous one stops being controlled by the handle. ' + 'A handle should be used by a single root that stays mounted for the lifetime of the handle.');
153
+ }
154
+ });
155
+ }
156
+ }
157
+ return () => {
158
+ const index = this.attachedStores.lastIndexOf(newStore);
159
+ if (index !== -1) {
160
+ this.attachedStores.splice(index, 1);
161
+ }
162
+ // Restore control to the most recently attached root that is still mounted (or detach fully if
163
+ // none remain). Clearing unconditionally would leave a still-mounted older root uncontrollable
164
+ // when a newer overlapping root detaches first (e.g. a canceled route transition).
165
+ this.setActiveStore(this.attachedStores[this.attachedStores.length - 1] ?? null);
166
+ };
167
+ }
168
+
169
+ /**
170
+ * Sets the store that currently controls the handle and notifies subscribers when it changes, so
171
+ * detached triggers re-render and migrate their registration to the new store.
172
+ */
173
+ setActiveStore(store) {
174
+ if (this.attachedStoreValue !== store) {
175
+ this.attachedStoreValue = store;
176
+ this.storeListeners.forEach(listener => {
177
+ listener();
178
+ });
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Opens the attached root's store and associates it with the trigger with the given id, or a
184
+ * no-op (with a dev warning) while no root is attached. Shared by every concrete handle's public
185
+ * `open()` method, which only narrows the parameter type.
186
+ *
187
+ * When a trigger id is given but no matching trigger is registered, anchored popups throw (see
188
+ * `throwOnMissingTrigger`); Dialog opens unassociated with a dev warning instead.
189
+ *
190
+ * This method should only be called in an event handler or an effect (not during rendering).
191
+ *
192
+ * @param triggerId ID of the trigger to associate with the popup, or `null`/`undefined` to open
193
+ * without associating any trigger.
194
+ */
195
+ openByTrigger(triggerId) {
196
+ const attachedStore = this.attachedStore;
197
+ if (attachedStore === null) {
198
+ if (process.env.NODE_ENV !== 'production') {
199
+ console.warn(`Base UI: ${this.componentName}Handle.open() was called while no root using this handle is mounted. ` + 'The call was ignored; mount a root with this handle before opening it imperatively.');
200
+ }
201
+ return;
202
+ }
203
+
204
+ // Registered triggers normally live in the active root's store. During the commit in which a
205
+ // root attaches, a still-mounted detached trigger has not re-registered into that store yet (it
206
+ // migrates on its next render): it is still registered wherever it lived before — the fallback
207
+ // store when this is the first root to attach, or a previously attached root's store during a
208
+ // transient overlap (e.g. an animated route transition). Search the whole attachment stack
209
+ // (newest first) and the fallback map so an imperative open-by-id called in that commit (e.g.
210
+ // from a layout effect) still resolves the trigger instead of treating it as missing.
211
+ let triggerElement;
212
+ if (triggerId) {
213
+ for (let i = this.attachedStores.length - 1; i >= 0 && !triggerElement; i -= 1) {
214
+ triggerElement = this.attachedStores[i].context.triggerElements.getById(triggerId);
215
+ }
216
+ triggerElement ??= this.fallbackStore.context.triggerElements.getById(triggerId);
217
+ }
218
+ if (triggerId && !triggerElement) {
219
+ if (this.throwOnMissingTrigger) {
220
+ throw new Error(process.env.NODE_ENV !== "production" ? `Base UI: ${this.componentName}Handle.open() was called with the trigger id "${triggerId}", ` + 'but no matching trigger is registered with this handle. ' + 'An anchored popup cannot open without a trigger to anchor to. ' + `Pass the id of a mounted ${this.componentName}.Trigger that has this handle set on its "handle" prop.` : useRenderElement.formatErrorMessage(99, this.componentName, triggerId, this.componentName));
221
+ }
222
+ if (process.env.NODE_ENV !== 'production') {
223
+ console.warn(`Base UI: ${this.componentName}Handle.open: No trigger found with id "${triggerId}". ` + 'The popup will open, but the trigger will not be associated with it.');
224
+ }
225
+ }
226
+ attachedStore.setOpen(true, useScrollLock.createChangeEventDetails(useScrollLock.imperativeAction, undefined, triggerElement));
227
+ }
228
+
229
+ /**
230
+ * Closes the popup by setting the attached root's store to closed, or a no-op (with a dev warning)
231
+ * while no root is attached. Shared by every concrete handle's public `close()` method.
232
+ *
233
+ * This method should only be called in an event handler or an effect (not during rendering).
234
+ */
235
+ closePopup() {
236
+ const attachedStore = this.attachedStore;
237
+ if (attachedStore === null) {
238
+ if (process.env.NODE_ENV !== 'production') {
239
+ console.warn(`Base UI: ${this.componentName}Handle.close() was called while no root using this handle is mounted. ` + 'The call was ignored.');
240
+ }
241
+ return;
242
+ }
243
+ attachedStore.setOpen(false, useScrollLock.createChangeEventDetails(useScrollLock.imperativeAction));
244
+ }
245
+ }
33
246
 
34
247
  /**
35
248
  * An overlay displayed beneath the popup.
@@ -45,9 +258,7 @@ const DialogBackdrop = /*#__PURE__*/React__namespace.forwardRef(function DialogB
45
258
  forceRender = false,
46
259
  ...elementProps
47
260
  } = componentProps;
48
- const {
49
- store
50
- } = ScrollAreaViewport.useDialogRootContext();
261
+ const store = ScrollAreaViewport.useDialogRootContext();
51
262
  const open = store.useState('open');
52
263
  const nested = store.useState('nested');
53
264
  const mounted = store.useState('mounted');
@@ -59,7 +270,7 @@ const DialogBackdrop = /*#__PURE__*/React__namespace.forwardRef(function DialogB
59
270
  return useRenderElement.useRenderElement('div', componentProps, {
60
271
  state,
61
272
  ref: [store.context.backdropRef, forwardedRef],
62
- stateAttributesMapping: stateAttributesMapping$1,
273
+ stateAttributesMapping: useScrollLock.popupTransitionStateMapping,
63
274
  props: [{
64
275
  role: 'presentation',
65
276
  hidden: !mounted,
@@ -87,9 +298,7 @@ const DialogDescription = /*#__PURE__*/React__namespace.forwardRef(function Dial
87
298
  id: idProp,
88
299
  ...elementProps
89
300
  } = componentProps;
90
- const {
91
- store
92
- } = ScrollAreaViewport.useDialogRootContext();
301
+ const store = ScrollAreaViewport.useDialogRootContext();
93
302
  const id = useScrollLock.useBaseUiId(idProp);
94
303
  store.useSyncedValueWithCleanup('descriptionElementId', id);
95
304
  return useRenderElement.useRenderElement('p', componentProps, {
@@ -101,59 +310,6 @@ const DialogDescription = /*#__PURE__*/React__namespace.forwardRef(function Dial
101
310
  });
102
311
  if (process.env.NODE_ENV !== "production") DialogDescription.displayName = "DialogDescription";
103
312
 
104
- let DialogPopupCssVars = /*#__PURE__*/function (DialogPopupCssVars) {
105
- /**
106
- * Indicates how many dialogs are nested within.
107
- * @type {number}
108
- */
109
- DialogPopupCssVars["nestedDialogs"] = "--nested-dialogs";
110
- return DialogPopupCssVars;
111
- }({});
112
-
113
- let DialogPopupDataAttributes = function (DialogPopupDataAttributes) {
114
- /**
115
- * Present when the dialog is open.
116
- */
117
- DialogPopupDataAttributes[DialogPopupDataAttributes["open"] = useScrollLock.CommonPopupDataAttributes.open] = "open";
118
- /**
119
- * Present when the dialog is closed.
120
- */
121
- DialogPopupDataAttributes[DialogPopupDataAttributes["closed"] = useScrollLock.CommonPopupDataAttributes.closed] = "closed";
122
- /**
123
- * Present when the dialog is animating in.
124
- */
125
- DialogPopupDataAttributes[DialogPopupDataAttributes["startingStyle"] = useScrollLock.CommonPopupDataAttributes.startingStyle] = "startingStyle";
126
- /**
127
- * Present when the dialog is animating out.
128
- */
129
- DialogPopupDataAttributes[DialogPopupDataAttributes["endingStyle"] = useScrollLock.CommonPopupDataAttributes.endingStyle] = "endingStyle";
130
- /**
131
- * Present when the dialog is nested within another dialog.
132
- */
133
- DialogPopupDataAttributes["nested"] = "data-nested";
134
- /**
135
- * Present when the dialog has other open dialogs nested within it.
136
- */
137
- DialogPopupDataAttributes["nestedDialogOpen"] = "data-nested-dialog-open";
138
- return DialogPopupDataAttributes;
139
- }({});
140
-
141
- const stateAttributesMapping = {
142
- ...useScrollLock.popupStateMapping,
143
- ...useScrollLock.transitionStatusMapping,
144
- nestedDialogOpen(value) {
145
- return value ? {
146
- [DialogPopupDataAttributes.nestedDialogOpen]: ''
147
- } : null;
148
- }
149
- };
150
-
151
- /**
152
- * A container for the dialog contents.
153
- * Renders a `<div>` element.
154
- *
155
- * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
156
- */
157
313
  const DialogPopup = /*#__PURE__*/React__namespace.forwardRef(function DialogPopup(componentProps, forwardedRef) {
158
314
  const {
159
315
  render,
@@ -163,9 +319,7 @@ const DialogPopup = /*#__PURE__*/React__namespace.forwardRef(function DialogPopu
163
319
  initialFocus,
164
320
  ...elementProps
165
321
  } = componentProps;
166
- const {
167
- store
168
- } = ScrollAreaViewport.useDialogRootContext();
322
+ const store = ScrollAreaViewport.useDialogRootContext();
169
323
  const descriptionElementId = store.useState('descriptionElementId');
170
324
  const disablePointerDismissal = store.useState('disablePointerDismissal');
171
325
  const floatingRootContext = store.useState('floatingRootContext');
@@ -180,7 +334,6 @@ const DialogPopup = /*#__PURE__*/React__namespace.forwardRef(function DialogPopu
180
334
  const transitionStatus = store.useState('transitionStatus');
181
335
  const role = store.useState('role');
182
336
  const floatingId = floatingRootContext.useState('floatingId');
183
- const popupId = elementProps.id ?? floatingId;
184
337
  ScrollAreaViewport.useDialogPortalContext();
185
338
  useScrollLock.useOpenChangeComplete({
186
339
  open,
@@ -203,9 +356,9 @@ const DialogPopup = /*#__PURE__*/React__namespace.forwardRef(function DialogPopu
203
356
  const element = useRenderElement.useRenderElement('div', componentProps, {
204
357
  state,
205
358
  props: [rootPopupProps, {
206
- id: popupId,
207
- 'aria-labelledby': titleElementId ?? undefined,
208
- 'aria-describedby': descriptionElementId ?? undefined,
359
+ id: floatingId,
360
+ 'aria-labelledby': titleElementId,
361
+ 'aria-describedby': descriptionElementId,
209
362
  role,
210
363
  ...useScrollLock.FOCUSABLE_POPUP_PROPS,
211
364
  hidden: !mounted,
@@ -215,11 +368,11 @@ const DialogPopup = /*#__PURE__*/React__namespace.forwardRef(function DialogPopu
215
368
  }
216
369
  },
217
370
  style: {
218
- [DialogPopupCssVars.nestedDialogs]: nestedOpenDialogCount
371
+ '--nested-dialogs': nestedOpenDialogCount
219
372
  }
220
373
  }, elementProps],
221
374
  ref: [forwardedRef, store.context.popupRef, setPopupElement],
222
- stateAttributesMapping
375
+ stateAttributesMapping: ScrollAreaViewport.dialogStateAttributesMapping
223
376
  });
224
377
  return /*#__PURE__*/jsxRuntime.jsx(useScrollLock.FloatingFocusManager, {
225
378
  context: floatingRootContext,
@@ -236,59 +389,73 @@ const DialogPopup = /*#__PURE__*/React__namespace.forwardRef(function DialogPopu
236
389
  if (process.env.NODE_ENV !== "production") DialogPopup.displayName = "DialogPopup";
237
390
 
238
391
  /**
239
- * A handle to control a Dialog imperatively and to associate detached triggers with it.
392
+ * Groups all parts of the dialog.
393
+ * Doesn't render its own HTML element.
394
+ *
395
+ * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)
240
396
  */
241
- class DialogHandle {
242
- /**
243
- * Internal store holding the dialog state.
244
- * @internal
245
- */
397
+ function DialogRoot(props) {
398
+ return ScrollAreaViewport.useRenderDialogRoot('dialog', props);
399
+ }
246
400
 
247
- constructor(store) {
248
- this.store = store ?? new ScrollAreaViewport.DialogStore();
401
+ /**
402
+ * Controls a Dialog imperatively and associates detached `Dialog.Trigger` components with a
403
+ * `Dialog.Root`. Create one with `Dialog.createHandle()` and pass it to the `handle` prop of the
404
+ * root and of any triggers rendered outside of it.
405
+ *
406
+ * The imperative methods take effect only while a root using this handle is mounted; calls made
407
+ * before a root attaches (or after it unmounts) are ignored.
408
+ */
409
+ class DialogHandle extends BasePopupHandle {
410
+ constructor() {
411
+ super(ScrollAreaViewport.createNullDialogStore(), 'Dialog', false);
249
412
  }
250
413
 
251
414
  /**
252
- * Opens the dialog and associates it with the trigger with the given id.
253
- * The trigger, if provided, must be a matching Trigger component with this handle passed as a prop.
415
+ * Opens the dialog, optionally associating it with a trigger.
254
416
  *
255
417
  * This method should only be called in an event handler or an effect (not during rendering).
256
418
  *
257
- * @param triggerId ID of the trigger to associate with the dialog. If null, the dialog will open without a trigger association.
419
+ * @param triggerId ID of the trigger to associate with the dialog. The trigger must be a matching
420
+ * `Dialog.Trigger` with this handle passed as a prop. Pass `null` to open without associating any trigger.
258
421
  */
259
422
  open(triggerId) {
260
- const triggerElement = triggerId ? this.store.context.triggerElements.getById(triggerId) : undefined;
261
- if (process.env.NODE_ENV !== 'production') {
262
- if (triggerId && !triggerElement) {
263
- console.warn(`Base UI: DialogHandle.open: No trigger found with id "${triggerId}". The dialog will open, but the trigger will not be associated with the dialog.`);
264
- }
265
- }
266
- this.store.setOpen(true, useScrollLock.createChangeEventDetails(useScrollLock.imperativeAction, undefined, triggerElement));
423
+ this.openByTrigger(triggerId);
267
424
  }
268
425
 
269
426
  /**
270
- * Opens the dialog and sets the payload.
271
- * Does not associate the dialog with any trigger.
427
+ * Opens the dialog with the given payload, without associating it with any trigger.
428
+ *
429
+ * This method should only be called in an event handler or an effect (not during rendering).
272
430
  *
273
- * @param payload Payload to set when opening the dialog.
431
+ * @param payload Payload to set when opening the dialog. It is exposed to the root's render-prop children.
274
432
  */
275
433
  openWithPayload(payload) {
276
- this.store.set('payload', payload);
277
- this.store.setOpen(true, useScrollLock.createChangeEventDetails(useScrollLock.imperativeAction, undefined, undefined));
434
+ const attachedStore = this.attachedStore;
435
+ if (attachedStore === null) {
436
+ if (process.env.NODE_ENV !== 'production') {
437
+ console.warn('Base UI: DialogHandle.openWithPayload() was called while no root using this handle is mounted. ' + 'The call and its payload were ignored; mount a root with this handle before opening it imperatively.');
438
+ }
439
+ return;
440
+ }
441
+ attachedStore.set('payload', payload);
442
+ attachedStore.setOpen(true, useScrollLock.createChangeEventDetails(useScrollLock.imperativeAction));
278
443
  }
279
444
 
280
445
  /**
281
446
  * Closes the dialog.
447
+ *
448
+ * This method should only be called in an event handler or an effect (not during rendering).
282
449
  */
283
450
  close() {
284
- this.store.setOpen(false, useScrollLock.createChangeEventDetails(useScrollLock.imperativeAction, undefined, undefined));
451
+ this.closePopup();
285
452
  }
286
453
 
287
454
  /**
288
- * Indicates whether the dialog is currently open.
455
+ * Whether the dialog is currently open. Returns `false` while no root is attached to the handle.
289
456
  */
290
457
  get isOpen() {
291
- return this.store.select('open');
458
+ return this.attachedStore?.select('open') ?? false;
292
459
  }
293
460
  }
294
461
 
@@ -307,9 +474,11 @@ function createDialogHandle() {
307
474
  */
308
475
  const DrawerDescription = DialogDescription;
309
476
 
477
+ exports.BasePopupHandle = BasePopupHandle;
310
478
  exports.DialogBackdrop = DialogBackdrop;
311
479
  exports.DialogDescription = DialogDescription;
312
480
  exports.DialogHandle = DialogHandle;
313
481
  exports.DialogPopup = DialogPopup;
482
+ exports.DialogRoot = DialogRoot;
314
483
  exports.DrawerDescription = DrawerDescription;
315
484
  exports.createDialogHandle = createDialogHandle;