@reopt-ai/opt-ui-primitives 1.5.1 → 1.5.2

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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import React, { cloneElement, createContext, forwardRef, isValidElement, useCallback, useContext, useEffect, useId as useId$1, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
2
+ import React, { cloneElement, createContext, createElement, forwardRef, isValidElement, useCallback, useContext, useEffect, useId as useId$1, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
3
  import { FloatingFocusManager, FloatingPortal, autoUpdate, flip, hide, offset, shift, size, useDismiss, useFloating as useFloating$1 } from "@floating-ui/react";
4
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/hooks/use-controllable-state.ts
@@ -152,6 +152,32 @@ function removeGlobalListeners() {
152
152
  document.removeEventListener("touchstart", handlePointerDown, true);
153
153
  }
154
154
  }
155
+ /**
156
+ * Subscribe to the shared modality tracking and read it on demand.
157
+ *
158
+ * Returns a stable predicate answering "should this focus show as visible?".
159
+ * Widgets that must react ONLY to keyboard focus (a tooltip must not pop open
160
+ * because the user clicked its trigger) gate their focus handler on it — the
161
+ * `visibleOnly` behavior.
162
+ *
163
+ * The browser's own `:focus-visible` is the authority whenever the element and
164
+ * the engine support it: it knows about text inputs (which show a ring even
165
+ * after a click), programmatic focus, and platform conventions the modality
166
+ * heuristic cannot infer. The document-level heuristic is the fallback for
167
+ * engines and test environments where matching `:focus-visible` throws.
168
+ */
169
+ function useKeyboardModality() {
170
+ useEffect(() => {
171
+ addGlobalListeners();
172
+ return removeGlobalListeners;
173
+ }, []);
174
+ return useCallback((element) => {
175
+ if (element) try {
176
+ if (element.matches(":focus-visible")) return true;
177
+ } catch (_unused) {}
178
+ return hadKeyboardEvent;
179
+ }, []);
180
+ }
155
181
  /** Returns `focusVisibleProps` to spread on a focusable element. */
156
182
  function useFocusVisible() {
157
183
  useEffect(() => {
@@ -168,6 +194,89 @@ function useFocusVisible() {
168
194
  } };
169
195
  }
170
196
  //#endregion
197
+ //#region src/hooks/use-item-registry.ts
198
+ const DOCUMENT_POSITION_FOLLOWING = 4;
199
+ function resolveElement(entry) {
200
+ if (entry.element) return entry.element;
201
+ if (typeof document === "undefined") return null;
202
+ return document.getElementById(entry.id);
203
+ }
204
+ /** Insert `entry` at its document position instead of appending it. */
205
+ function insertByDomOrder(list, entry) {
206
+ const element = resolveElement(entry);
207
+ const lastElement = list.length > 0 ? resolveElement(list[list.length - 1]) : null;
208
+ if (!element || !lastElement || lastElement.compareDocumentPosition(element) & DOCUMENT_POSITION_FOLLOWING) {
209
+ list.push(entry);
210
+ return;
211
+ }
212
+ for (let i = 0; i < list.length; i += 1) {
213
+ const other = resolveElement(list[i]);
214
+ if (other && element.compareDocumentPosition(other) & DOCUMENT_POSITION_FOLLOWING) {
215
+ list.splice(i, 0, entry);
216
+ return;
217
+ }
218
+ }
219
+ list.push(entry);
220
+ }
221
+ /** Id of the first item that is not disabled. */
222
+ function firstEnabledId(items) {
223
+ var _items$find$id, _items$find;
224
+ return (_items$find$id = (_items$find = items.find((item) => !item.disabled)) === null || _items$find === void 0 ? void 0 : _items$find.id) !== null && _items$find$id !== void 0 ? _items$find$id : null;
225
+ }
226
+ /** Id of the last item that is not disabled. */
227
+ function lastEnabledId(items) {
228
+ for (let i = items.length - 1; i >= 0; i -= 1) if (!items[i].disabled) return items[i].id;
229
+ return null;
230
+ }
231
+ /**
232
+ * Id `delta` enabled items away from `activeId` (disabled skipped).
233
+ *
234
+ * Clamps at the ends by default — the listbox/menu behavior. Pass
235
+ * `{ wrap: true }` for the WAI-ARIA tabs behavior, where the arrow keys cycle.
236
+ * With no active item, a forward move lands on the first enabled item and a
237
+ * backward move on the last.
238
+ */
239
+ function moveActiveId(items, activeId, delta, options) {
240
+ const enabled = items.filter((item) => !item.disabled);
241
+ if (enabled.length === 0) return null;
242
+ const index = enabled.findIndex((item) => item.id === activeId);
243
+ if (index < 0) {
244
+ if (options === null || options === void 0 ? void 0 : options.wrap) return enabled[0].id;
245
+ return delta > 0 ? enabled[0].id : enabled[enabled.length - 1].id;
246
+ }
247
+ const count = enabled.length;
248
+ return enabled[(options === null || options === void 0 ? void 0 : options.wrap) ? ((index + delta) % count + count) % count : Math.min(count - 1, Math.max(0, index + delta))].id;
249
+ }
250
+ /**
251
+ * Id of the enabled item nearest to `index`: forward first (the item that took
252
+ * the removed slot), then backward. `null` when the list has none left.
253
+ *
254
+ * This is what keeps the navigation POSITION when the active item is removed.
255
+ */
256
+ function nearestEnabledId(items, index) {
257
+ for (let i = Math.max(0, index); i < items.length; i += 1) if (!items[i].disabled) return items[i].id;
258
+ for (let i = Math.min(index, items.length) - 1; i >= 0; i -= 1) if (!items[i].disabled) return items[i].id;
259
+ return null;
260
+ }
261
+ /** Creates a document-ordered item registry. Stable across renders. */
262
+ function useItemRegistry() {
263
+ const items = useRef([]);
264
+ return {
265
+ items,
266
+ registerItem: useCallback((entry) => {
267
+ const list = items.current;
268
+ const existing = list.findIndex((item) => item.id === entry.id);
269
+ if (existing >= 0) list.splice(existing, 1);
270
+ insertByDomOrder(list, entry);
271
+ }, []),
272
+ unregisterItem: useCallback((id) => {
273
+ const index = items.current.findIndex((item) => item.id === id);
274
+ if (index >= 0) items.current.splice(index, 1);
275
+ return index;
276
+ }, [])
277
+ };
278
+ }
279
+ //#endregion
171
280
  //#region src/hooks/use-roving-tabindex.ts
172
281
  /**
173
282
  * Implements roving tabindex pattern for keyboard navigation.
@@ -179,7 +288,7 @@ function useFocusVisible() {
179
288
  */
180
289
  function useRovingTabindex(options = {}) {
181
290
  const { orientation = "horizontal", loop = false, rtl = false, columns } = options;
182
- const itemsRef = useRef([]);
291
+ const { items: itemsRef, registerItem: registerEntry, unregisterItem: removeItem } = useItemRegistry();
183
292
  const [activeId, setActiveId] = useState(null);
184
293
  const containerRef = useRef(null);
185
294
  const seededRef = useRef(false);
@@ -187,14 +296,8 @@ function useRovingTabindex(options = {}) {
187
296
  return itemsRef.current.filter((item) => !item.disabled);
188
297
  }, []);
189
298
  const register = useCallback((id, element, disabled) => {
190
- const existing = itemsRef.current.findIndex((item) => item.id === id);
191
- const activeBecameDisabled = existing >= 0 && id === activeId && !!disabled;
192
- if (existing >= 0) itemsRef.current[existing] = {
193
- id,
194
- element,
195
- disabled
196
- };
197
- else itemsRef.current.push({
299
+ const activeBecameDisabled = itemsRef.current.findIndex((item) => item.id === id) >= 0 && id === activeId && !!disabled;
300
+ registerEntry({
198
301
  id,
199
302
  element,
200
303
  disabled
@@ -203,21 +306,28 @@ function useRovingTabindex(options = {}) {
203
306
  seededRef.current = true;
204
307
  setActiveId(id);
205
308
  } else if (activeBecameDisabled) {
206
- var _itemsRef$current$fin, _itemsRef$current$fin2;
207
- const next = (_itemsRef$current$fin = (_itemsRef$current$fin2 = itemsRef.current.find((item) => !item.disabled)) === null || _itemsRef$current$fin2 === void 0 ? void 0 : _itemsRef$current$fin2.id) !== null && _itemsRef$current$fin !== void 0 ? _itemsRef$current$fin : null;
309
+ const next = firstEnabledId(itemsRef.current);
208
310
  if (next === null) seededRef.current = false;
209
311
  setActiveId(next);
210
312
  }
211
- }, [activeId]);
313
+ }, [
314
+ activeId,
315
+ itemsRef,
316
+ registerEntry
317
+ ]);
212
318
  const unregister = useCallback((id) => {
213
- itemsRef.current = itemsRef.current.filter((item) => item.id !== id);
319
+ const removedIndex = removeItem(id);
320
+ if (removedIndex < 0) return;
214
321
  if (activeId === id) {
215
- var _getEnabledItems$0$id, _getEnabledItems$;
216
- const next = (_getEnabledItems$0$id = (_getEnabledItems$ = getEnabledItems()[0]) === null || _getEnabledItems$ === void 0 ? void 0 : _getEnabledItems$.id) !== null && _getEnabledItems$0$id !== void 0 ? _getEnabledItems$0$id : null;
322
+ const next = nearestEnabledId(itemsRef.current, removedIndex);
217
323
  if (next === null) seededRef.current = false;
218
324
  setActiveId(next);
219
325
  }
220
- }, [activeId, getEnabledItems]);
326
+ }, [
327
+ activeId,
328
+ itemsRef,
329
+ removeItem
330
+ ]);
221
331
  const moveTo = useCallback((id) => {
222
332
  setActiveId(id);
223
333
  const item = itemsRef.current.find((i) => i.id === id);
@@ -255,12 +365,12 @@ function useRovingTabindex(options = {}) {
255
365
  moveByOffset(rtl ? 1 : -1);
256
366
  } else if (e.key === "Home") {
257
367
  e.preventDefault();
258
- const enabled = getEnabledItems();
259
- if (enabled.length > 0) moveTo(enabled[0].id);
368
+ const first = firstEnabledId(itemsRef.current);
369
+ if (first) moveTo(first);
260
370
  } else if (e.key === "End") {
261
371
  e.preventDefault();
262
- const enabled = getEnabledItems();
263
- if (enabled.length > 0) moveTo(enabled[enabled.length - 1].id);
372
+ const last = lastEnabledId(itemsRef.current);
373
+ if (last) moveTo(last);
264
374
  }
265
375
  }, [
266
376
  orientation,
@@ -417,6 +527,28 @@ function useFloating(options = {}) {
417
527
  Object.assign(elements.floating.style, styles);
418
528
  }
419
529
  }));
530
+ m.push({
531
+ name: "optTransformOrigin",
532
+ fn(state) {
533
+ var _ref2, _middlewareData$shift, _middlewareData$shift2;
534
+ const { elements, middlewareData, placement: resolved, rects } = state;
535
+ const [resolvedSide, resolvedAlign] = resolved.split("-");
536
+ const isVertical = resolvedSide === "top" || resolvedSide === "bottom";
537
+ const so = sideOffsetRef.current;
538
+ const sideOffsetValue = (_ref2 = typeof so === "function" ? so({
539
+ rects,
540
+ placement: resolved
541
+ }) : so) !== null && _ref2 !== void 0 ? _ref2 : 0;
542
+ const shiftAmount = isVertical ? ((_middlewareData$shift = middlewareData.shift) === null || _middlewareData$shift === void 0 ? void 0 : _middlewareData$shift.x) || 0 : ((_middlewareData$shift2 = middlewareData.shift) === null || _middlewareData$shift2 === void 0 ? void 0 : _middlewareData$shift2.y) || 0;
543
+ let crossOrigin;
544
+ if (!resolvedAlign) crossOrigin = "50%";
545
+ else if (Math.abs(shiftAmount) <= 1) crossOrigin = resolvedAlign === "start" ? "0%" : "100%";
546
+ else crossOrigin = isVertical ? `${rects.reference.x + rects.reference.width / 2 - state.x}px` : `${rects.reference.y + rects.reference.height / 2 - state.y}px`;
547
+ const sideOrigin = resolvedSide === "top" || resolvedSide === "left" ? `calc(100% + ${sideOffsetValue}px)` : `${-sideOffsetValue}px`;
548
+ elements.floating.style.setProperty("--opt-transform-origin", isVertical ? `${crossOrigin} ${sideOrigin}` : `${sideOrigin} ${crossOrigin}`);
549
+ return {};
550
+ }
551
+ });
420
552
  m.push(hide({ padding: overflowPadding }));
421
553
  return m;
422
554
  }, [
@@ -471,7 +603,8 @@ function useFloating(options = {}) {
471
603
  ref: floating.refs.setFloating,
472
604
  style: _objectSpread2(_objectSpread2({
473
605
  "--opt-available-width": "100vw",
474
- "--opt-available-height": "100vh"
606
+ "--opt-available-height": "100vh",
607
+ "--opt-transform-origin": "center"
475
608
  }, floating.floatingStyles), isPositioned ? null : { opacity: 0 })
476
609
  }),
477
610
  /**
@@ -541,6 +674,137 @@ function useScrollActiveDescendantIntoView(activeId) {
541
674
  }, [activeId]);
542
675
  }
543
676
  //#endregion
677
+ //#region src/hooks/use-hidden-until-found.ts
678
+ /**
679
+ * Keeps collapsed content reachable by browser find-in-page.
680
+ *
681
+ * The content stays MOUNTED and toggles `hidden="until-found"` instead of
682
+ * unmounting, so Chromium can match text inside it; when it does, the
683
+ * `beforematch` event fires and `onReveal` opens the owning disclosure so the
684
+ * component state stays consistent with what the browser just revealed.
685
+ *
686
+ * React coerces the `hidden` prop to a boolean attribute, so the "until-found"
687
+ * string has to be written imperatively — hence the ref rather than a prop.
688
+ *
689
+ * Progressive enhancement: browsers without `hidden="until-found"` treat it as
690
+ * plain `hidden`, which is the correct fallback.
691
+ */
692
+ function useHiddenUntilFound(ref, { enabled, open, onReveal }) {
693
+ useEffect(() => {
694
+ const el = ref.current;
695
+ if (!el || !enabled) return;
696
+ const handleBeforeMatch = () => onReveal();
697
+ el.addEventListener("beforematch", handleBeforeMatch);
698
+ return () => el.removeEventListener("beforematch", handleBeforeMatch);
699
+ }, [
700
+ ref,
701
+ enabled,
702
+ onReveal
703
+ ]);
704
+ useEffect(() => {
705
+ const el = ref.current;
706
+ if (!el || !enabled) return;
707
+ if (open) el.removeAttribute("hidden");
708
+ else el.setAttribute("hidden", "until-found");
709
+ }, [
710
+ ref,
711
+ enabled,
712
+ open
713
+ ]);
714
+ }
715
+ //#endregion
716
+ //#region src/internal/merge-props.ts
717
+ const EVENT_HANDLER = /^on[A-Z]/;
718
+ function isSyntheticEvent(value) {
719
+ return typeof value === "object" && value !== null && "nativeEvent" in value;
720
+ }
721
+ function makePreventable(event) {
722
+ if (typeof event.preventOptHandler === "function") return;
723
+ event.optHandlerPrevented = false;
724
+ event.preventOptHandler = () => {
725
+ event.optHandlerPrevented = true;
726
+ };
727
+ }
728
+ function chainHandlers(a, b) {
729
+ return (...args) => {
730
+ const event = args[0];
731
+ if (isSyntheticEvent(event)) {
732
+ makePreventable(event);
733
+ a(...args);
734
+ if (event.optHandlerPrevented) return void 0;
735
+ return b(...args);
736
+ }
737
+ a(...args);
738
+ return b(...args);
739
+ };
740
+ }
741
+ /** Merge prop objects left→right with handler/className/style composition. */
742
+ function mergeProps(...parts) {
743
+ const result = {};
744
+ for (const part of parts) {
745
+ if (!part) continue;
746
+ for (const key in part) {
747
+ if (!Object.prototype.hasOwnProperty.call(part, key)) continue;
748
+ const value = part[key];
749
+ const existing = result[key];
750
+ if (EVENT_HANDLER.test(key)) {
751
+ if (typeof value === "function" && typeof existing === "function") result[key] = chainHandlers(existing, value);
752
+ else if (value === void 0 && typeof existing === "function") continue;
753
+ else result[key] = value;
754
+ } else if (key === "className") result[key] = [existing, value].filter(Boolean).join(" ") || void 0;
755
+ else if (key === "style" && existing && typeof existing === "object" && value && typeof value === "object") result[key] = _objectSpread2(_objectSpread2({}, existing), value);
756
+ else result[key] = value;
757
+ }
758
+ }
759
+ return result;
760
+ }
761
+ /**
762
+ * Merge multiple refs (callback or object) into one callback ref.
763
+ *
764
+ * Returns a React 19 ref CLEANUP function, so every merged ref is detached at
765
+ * exactly the right time: callback refs that themselves return a cleanup have
766
+ * it invoked (previously the returned cleanup was dropped on the floor, leaking
767
+ * whatever it was meant to release), and the rest are detached by being called
768
+ * with `null` / having their `.current` reset. Cleanups run in reverse
769
+ * attachment order, mirroring React's own teardown order.
770
+ */
771
+ function mergeRefs(...refs) {
772
+ return (value) => {
773
+ const cleanups = [];
774
+ for (const ref of refs) if (typeof ref === "function") {
775
+ const cleanup = ref(value);
776
+ cleanups.push(typeof cleanup === "function" ? cleanup : () => ref(null));
777
+ } else if (ref != null) {
778
+ const objectRef = ref;
779
+ objectRef.current = value;
780
+ cleanups.push(() => {
781
+ objectRef.current = null;
782
+ });
783
+ }
784
+ return () => {
785
+ for (let i = cleanups.length - 1; i >= 0; i -= 1) cleanups[i]();
786
+ };
787
+ };
788
+ }
789
+ //#endregion
790
+ //#region src/hooks/use-merged-ref.ts
791
+ /**
792
+ * A STABLE merged ref callback.
793
+ *
794
+ * `mergeRefs` returns a fresh function on every call, and React re-attaches a
795
+ * callback ref whenever its identity changes: it detaches first (nulling every
796
+ * ref in the chain), then attaches again. Building the merged ref inline during
797
+ * render therefore made every render null out the element the animation and
798
+ * measurement code reads — which is how the collapsible content on the explore
799
+ * detail pages stopped opening.
800
+ *
801
+ * Memoizing on the ref identities keeps the callback stable across renders, so
802
+ * the refs are attached exactly once.
803
+ */
804
+ function useMergedRef(...refs) {
805
+ return useMemo(() => mergeRefs(...refs), refs);
806
+ }
807
+ //#endregion
544
808
  //#region \0@oxc-project+runtime@0.146.0/helpers/esm/objectWithoutPropertiesLoose.js
545
809
  function _objectWithoutPropertiesLoose(r, e) {
546
810
  if (null == r) return {};
@@ -615,24 +879,14 @@ function DisclosureContent(_ref2) {
615
879
  const { open, setOpen, contentId, triggerId, animated } = useDisclosureContext();
616
880
  const { ref, mounted, dataAttributes } = useEnterLeave(open, { animated: animated && !hiddenUntilFound });
617
881
  const innerRef = useRef(null);
618
- useEffect(() => {
619
- if (innerRef.current) ref.current = innerRef.current;
620
- }, [ref]);
621
- useEffect(() => {
622
- const el = innerRef.current;
623
- if (!el || !hiddenUntilFound) return;
624
- const onBeforeMatch = () => setOpen(true);
625
- el.addEventListener("beforematch", onBeforeMatch);
626
- return () => el.removeEventListener("beforematch", onBeforeMatch);
627
- }, [hiddenUntilFound, setOpen]);
628
- useEffect(() => {
629
- const el = innerRef.current;
630
- if (!el || !hiddenUntilFound) return;
631
- if (open) el.removeAttribute("hidden");
632
- else el.setAttribute("hidden", "until-found");
633
- }, [open, hiddenUntilFound]);
882
+ const contentRef = useMergedRef(innerRef, ref);
883
+ useHiddenUntilFound(innerRef, {
884
+ enabled: hiddenUntilFound,
885
+ open,
886
+ onReveal: useCallback(() => setOpen(true), [setOpen])
887
+ });
634
888
  if (hiddenUntilFound) return /* @__PURE__ */ jsx("div", _objectSpread2({
635
- ref: innerRef,
889
+ ref: contentRef,
636
890
  id: contentId,
637
891
  role: "region",
638
892
  "aria-labelledby": triggerId,
@@ -641,7 +895,7 @@ function DisclosureContent(_ref2) {
641
895
  }, props));
642
896
  if (!mounted) return null;
643
897
  return /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2({
644
- ref: innerRef,
898
+ ref: contentRef,
645
899
  id: contentId,
646
900
  role: "region",
647
901
  "aria-labelledby": triggerId,
@@ -787,12 +1041,7 @@ const DialogPanel = forwardRef((_ref2, forwardedRef) => {
787
1041
  lastFocusedRef.current = null;
788
1042
  }
789
1043
  }, [mounted]);
790
- const mergedRef = useCallback((node) => {
791
- dialogRef.current = node;
792
- enterLeaveRef.current = node;
793
- if (typeof forwardedRef === "function") forwardedRef(node);
794
- else if (forwardedRef) forwardedRef.current = node;
795
- }, [
1044
+ const mergedRef = useMemo(() => mergeRefs(dialogRef, enterLeaveRef, forwardedRef), [
796
1045
  dialogRef,
797
1046
  enterLeaveRef,
798
1047
  forwardedRef
@@ -809,7 +1058,8 @@ const DialogPanel = forwardRef((_ref2, forwardedRef) => {
809
1058
  }, [
810
1059
  dialogRef,
811
1060
  setOpen,
812
- dismissOnEscape
1061
+ dismissOnEscape,
1062
+ mounted
813
1063
  ]);
814
1064
  const handleClick = useCallback((e) => {
815
1065
  if (dismissOnBackdrop && e.target === e.currentTarget) setOpen(false);
@@ -874,13 +1124,9 @@ const _excluded2$12 = [
874
1124
  "onClick"
875
1125
  ];
876
1126
  const _excluded3$9 = ["tabId"];
1127
+ /** WAI-ARIA tabs wrap at both ends, unlike a listbox/menu (which clamps). */
877
1128
  function nextEnabledTabId(tabs, currentId, delta) {
878
- const enabled = tabs.filter((t) => !t.disabled);
879
- if (enabled.length === 0) return null;
880
- const idx = enabled.findIndex((t) => t.id === currentId);
881
- if (idx < 0) return enabled[0].id;
882
- const n = enabled.length;
883
- return enabled[((idx + delta) % n + n) % n].id;
1129
+ return moveActiveId(tabs, currentId, delta, { wrap: true });
884
1130
  }
885
1131
  const TabsContext = createContext(null);
886
1132
  function useTabsContext() {
@@ -892,37 +1138,54 @@ function useTabsContext() {
892
1138
  function TabsRoot({ children, selectedId: controlledId, defaultSelectedId = "", onSelectedIdChange, setSelectedId: setSelectedIdDeprecated, orientation = "horizontal" }) {
893
1139
  const [selectedId, setSelectedId] = useControllableState(defaultSelectedId, controlledId, onSelectedIdChange !== null && onSelectedIdChange !== void 0 ? onSelectedIdChange : setSelectedIdDeprecated);
894
1140
  const baseId = useId();
895
- const tabs = useRef([]);
896
- const registerTab = useCallback((id, disabled) => {
897
- const existing = tabs.current.findIndex((t) => t.id === id);
898
- if (existing >= 0) tabs.current[existing] = {
899
- id,
900
- disabled
901
- };
902
- else tabs.current.push({
903
- id,
904
- disabled
905
- });
906
- }, []);
1141
+ const { items: tabs, registerItem, unregisterItem } = useItemRegistry();
1142
+ const selectedIdRef = useRef(selectedId);
1143
+ selectedIdRef.current = selectedId;
1144
+ const isControlled = controlledId !== void 0;
1145
+ const isControlledRef = useRef(isControlled);
1146
+ isControlledRef.current = isControlled;
1147
+ const setSelectedIdRef = useRef(setSelectedId);
1148
+ setSelectedIdRef.current = setSelectedId;
1149
+ const registerTab = useCallback((id, disabled) => registerItem({
1150
+ id,
1151
+ disabled
1152
+ }), [registerItem]);
907
1153
  const unregisterTab = useCallback((id) => {
908
- tabs.current = tabs.current.filter((t) => t.id !== id);
909
- }, []);
1154
+ const removedIndex = unregisterItem(id);
1155
+ if (selectedIdRef.current !== id) return;
1156
+ if (isControlledRef.current) return;
1157
+ const next = nearestEnabledId(tabs.current, removedIndex);
1158
+ if (next) setSelectedIdRef.current(next);
1159
+ }, [tabs, unregisterItem]);
910
1160
  useLayoutEffect(() => {
911
1161
  if (!selectedId) {
912
- const first = tabs.current.find((t) => !t.disabled);
913
- if (first) setSelectedId(first.id);
1162
+ const first = firstEnabledId(tabs.current);
1163
+ if (first) setSelectedId(first);
914
1164
  }
915
- }, [selectedId, setSelectedId]);
1165
+ }, [
1166
+ selectedId,
1167
+ setSelectedId,
1168
+ tabs
1169
+ ]);
1170
+ const contextValue = useMemo(() => ({
1171
+ selectedId,
1172
+ setSelectedId,
1173
+ baseId,
1174
+ orientation,
1175
+ registerTab,
1176
+ unregisterTab,
1177
+ tabs
1178
+ }), [
1179
+ selectedId,
1180
+ setSelectedId,
1181
+ baseId,
1182
+ orientation,
1183
+ registerTab,
1184
+ unregisterTab,
1185
+ tabs
1186
+ ]);
916
1187
  return /* @__PURE__ */ jsx(TabsContext.Provider, {
917
- value: {
918
- selectedId,
919
- setSelectedId,
920
- baseId,
921
- orientation,
922
- registerTab,
923
- unregisterTab,
924
- tabs
925
- },
1188
+ value: contextValue,
926
1189
  children
927
1190
  });
928
1191
  }
@@ -1084,39 +1347,6 @@ const Radio = forwardRef((_ref2, ref) => {
1084
1347
  });
1085
1348
  Radio.displayName = "Radio";
1086
1349
  //#endregion
1087
- //#region src/internal/merge-props.ts
1088
- const EVENT_HANDLER = /^on[A-Z]/;
1089
- /** Merge prop objects left→right with handler/className/style composition. */
1090
- function mergeProps(...parts) {
1091
- const result = {};
1092
- for (const part of parts) {
1093
- if (!part) continue;
1094
- for (const key in part) {
1095
- if (!Object.prototype.hasOwnProperty.call(part, key)) continue;
1096
- const value = part[key];
1097
- const existing = result[key];
1098
- if (EVENT_HANDLER.test(key) && typeof value === "function" && typeof existing === "function") {
1099
- const a = existing;
1100
- const b = value;
1101
- result[key] = (...args) => {
1102
- a(...args);
1103
- return b(...args);
1104
- };
1105
- } else if (key === "className") result[key] = [existing, value].filter(Boolean).join(" ") || void 0;
1106
- else if (key === "style" && existing && typeof existing === "object" && value && typeof value === "object") result[key] = _objectSpread2(_objectSpread2({}, existing), value);
1107
- else result[key] = value;
1108
- }
1109
- }
1110
- return result;
1111
- }
1112
- /** Merge multiple refs (callback or object) into one callback ref. */
1113
- function mergeRefs(...refs) {
1114
- return (value) => {
1115
- for (const ref of refs) if (typeof ref === "function") ref(value);
1116
- else if (ref != null) ref.current = value;
1117
- };
1118
- }
1119
- //#endregion
1120
1350
  //#region src/internal/overlay-portal.tsx
1121
1351
  /**
1122
1352
  * Conditionally portals overlay content. When `portal` is false, children render
@@ -1142,6 +1372,32 @@ function OverlayPortal({ portal, portalRoot, children }) {
1142
1372
  });
1143
1373
  }
1144
1374
  //#endregion
1375
+ //#region src/internal/render-element.tsx
1376
+ /**
1377
+ * Renders one part of a primitive: merges prop objects, merges refs, and
1378
+ * honors a `render` prop.
1379
+ *
1380
+ * A plain function, not a hook, so parts can call it after an early return
1381
+ * (`if (!mounted) return null`) — which most overlay panels do.
1382
+ *
1383
+ * Every part used to hand-roll this — an inline `ref={(node) => {...}}` that
1384
+ * poked one ref object and then type-checked a floating-ui callback ref, plus
1385
+ * an effect copying an element into the enter/leave ref. Eight copies of the
1386
+ * same wiring meant a fix (React 19 ref cleanup, say) had to be applied eight
1387
+ * times, and only `Composite` ever supported `render`. Routing parts through
1388
+ * here fixes all of them at once and makes `render` universal.
1389
+ */
1390
+ function renderElement(tag, { render, refs, props }) {
1391
+ const mergedProps = mergeProps(...props !== null && props !== void 0 ? props : []);
1392
+ const mergedRef = mergeRefs(...refs !== null && refs !== void 0 ? refs : []);
1393
+ if (typeof render === "function") return render(_objectSpread2(_objectSpread2({}, mergedProps), {}, { ref: mergedRef }));
1394
+ if (render && isValidElement(render)) {
1395
+ const renderProps = render.props;
1396
+ return cloneElement(render, _objectSpread2(_objectSpread2({}, mergeProps(renderProps, mergedProps)), {}, { ref: mergeRefs(renderProps.ref, mergedRef) }));
1397
+ }
1398
+ return createElement(tag, _objectSpread2(_objectSpread2({}, mergedProps), {}, { ref: mergedRef }));
1399
+ }
1400
+ //#endregion
1145
1401
  //#region src/primitives/tooltip.tsx
1146
1402
  const _excluded$15 = [
1147
1403
  "children",
@@ -1152,7 +1408,8 @@ const _excluded2$10 = ["ref", "aria-describedby"];
1152
1408
  const _excluded3$8 = [
1153
1409
  "children",
1154
1410
  "onMouseEnter",
1155
- "onMouseLeave"
1411
+ "onMouseLeave",
1412
+ "render"
1156
1413
  ];
1157
1414
  function mergeAriaDescribedBy(...values) {
1158
1415
  const ids = values.flatMap((value) => typeof value === "string" ? value.trim().split(/\s+/) : []);
@@ -1162,53 +1419,98 @@ function mergeAriaDescribedBy(...values) {
1162
1419
  const TooltipContext = createContext(null);
1163
1420
  function useTooltipContext() {
1164
1421
  const ctx = useContext(TooltipContext);
1165
- if (!ctx) throw new Error("Tooltip components must be used within TooltipProvider");
1422
+ if (!ctx) throw new Error("Tooltip components must be used within TooltipRoot");
1166
1423
  return ctx;
1167
1424
  }
1168
- /** Renders the `TooltipProvider` component. */
1169
- function TooltipProvider({ children, timeout, showTimeout = 700, hideTimeout = 300, placement = "top", animated = true, portal = false, portalRoot = null }) {
1425
+ const TooltipGroupContext = createContext(null);
1426
+ /**
1427
+ * Groups sibling tooltips so moving between them does not re-pay the show
1428
+ * delay — the APG/desktop convention for toolbars and icon rows, where waiting
1429
+ * 700ms per button makes the row feel broken.
1430
+ *
1431
+ * Optional: a `TooltipProvider` outside any group keeps its own delay.
1432
+ */
1433
+ function TooltipGroup({ children, skipDelay = 300 }) {
1434
+ const warmUntilRef = useRef(0);
1435
+ const cooldownRef = useRef(void 0);
1436
+ const value = useMemo(() => ({
1437
+ isWarm: () => skipDelay > 0 && Date.now() < warmUntilRef.current,
1438
+ onOpen: () => {
1439
+ clearTimeout(cooldownRef.current);
1440
+ warmUntilRef.current = 0;
1441
+ },
1442
+ onClose: () => {
1443
+ if (skipDelay <= 0) return;
1444
+ warmUntilRef.current = Date.now() + skipDelay;
1445
+ clearTimeout(cooldownRef.current);
1446
+ cooldownRef.current = setTimeout(() => {
1447
+ warmUntilRef.current = 0;
1448
+ }, skipDelay);
1449
+ }
1450
+ }), [skipDelay]);
1451
+ useEffect(() => () => clearTimeout(cooldownRef.current), []);
1452
+ return /* @__PURE__ */ jsx(TooltipGroupContext.Provider, {
1453
+ value,
1454
+ children
1455
+ });
1456
+ }
1457
+ /**
1458
+ * Owns ONE tooltip: its open state, delays, placement and portal.
1459
+ *
1460
+ * Wrap several of these in a {@link TooltipGroup} to share a skip-delay window.
1461
+ */
1462
+ function TooltipRoot({ children, timeout, showTimeout = 700, hideTimeout = 300, placement = "top", animated = true, portal = false, portalRoot = null }) {
1170
1463
  const [open, setOpen] = useState(false);
1171
1464
  const tooltipId = useId();
1172
1465
  const showDelay = timeout !== null && timeout !== void 0 ? timeout : showTimeout;
1173
1466
  const showTimerRef = useRef(void 0);
1174
1467
  const hideTimerRef = useRef(void 0);
1468
+ const group = useContext(TooltipGroupContext);
1469
+ const isKeyboardModality = useKeyboardModality();
1470
+ const clearTimers = useCallback(() => {
1471
+ clearTimeout(showTimerRef.current);
1472
+ clearTimeout(hideTimerRef.current);
1473
+ }, []);
1175
1474
  const { getReferenceProps: getFloatRefProps, getFloatingProps } = useFloating({
1176
1475
  placement,
1177
1476
  gutter: 8,
1178
- open
1477
+ open,
1478
+ onOpenChange: useCallback((next) => {
1479
+ if (next) return;
1480
+ clearTimers();
1481
+ setOpen(false);
1482
+ }, [clearTimers]),
1483
+ dismiss: {
1484
+ escapeKey: true,
1485
+ outsidePress: false
1486
+ }
1179
1487
  });
1180
1488
  const show = useCallback(() => {
1181
1489
  clearTimeout(hideTimerRef.current);
1182
- showTimerRef.current = setTimeout(() => setOpen(true), showDelay);
1183
- }, [showDelay]);
1490
+ const delay = (group === null || group === void 0 ? void 0 : group.isWarm()) ? 0 : showDelay;
1491
+ showTimerRef.current = setTimeout(() => {
1492
+ group === null || group === void 0 || group.onOpen();
1493
+ setOpen(true);
1494
+ }, delay);
1495
+ }, [showDelay, group]);
1184
1496
  const hide = useCallback(() => {
1185
1497
  clearTimeout(showTimerRef.current);
1186
- hideTimerRef.current = setTimeout(() => setOpen(false), hideTimeout);
1187
- }, [hideTimeout]);
1188
- useEffect(() => {
1189
- return () => {
1190
- clearTimeout(showTimerRef.current);
1191
- clearTimeout(hideTimerRef.current);
1192
- };
1193
- }, []);
1194
- useEffect(() => {
1195
- if (!open) return;
1196
- const handler = (e) => {
1197
- if (e.key === "Escape") {
1198
- clearTimeout(showTimerRef.current);
1199
- clearTimeout(hideTimerRef.current);
1200
- setOpen(false);
1201
- }
1202
- };
1203
- document.addEventListener("keydown", handler);
1204
- return () => document.removeEventListener("keydown", handler);
1205
- }, [open]);
1498
+ hideTimerRef.current = setTimeout(() => {
1499
+ setOpen((wasOpen) => {
1500
+ if (wasOpen) group === null || group === void 0 || group.onClose();
1501
+ return false;
1502
+ });
1503
+ }, hideTimeout);
1504
+ }, [hideTimeout, group]);
1505
+ useEffect(() => clearTimers, [clearTimers]);
1206
1506
  const getReferenceProps = useCallback(() => {
1207
1507
  return _objectSpread2(_objectSpread2({}, getFloatRefProps()), {}, {
1208
1508
  "aria-describedby": open ? tooltipId : void 0,
1209
1509
  onMouseEnter: show,
1210
1510
  onMouseLeave: hide,
1211
- onFocus: show,
1511
+ onFocus: (event) => {
1512
+ if (isKeyboardModality(event.currentTarget)) show();
1513
+ },
1212
1514
  onBlur: hide
1213
1515
  });
1214
1516
  }, [
@@ -1216,24 +1518,46 @@ function TooltipProvider({ children, timeout, showTimeout = 700, hideTimeout = 3
1216
1518
  open,
1217
1519
  tooltipId,
1218
1520
  show,
1219
- hide
1521
+ hide,
1522
+ isKeyboardModality
1523
+ ]);
1524
+ const contextValue = useMemo(() => ({
1525
+ open,
1526
+ setOpen,
1527
+ tooltipId,
1528
+ show,
1529
+ hide,
1530
+ animated,
1531
+ portal,
1532
+ portalRoot,
1533
+ getReferenceProps,
1534
+ getFloatingProps
1535
+ }), [
1536
+ open,
1537
+ setOpen,
1538
+ tooltipId,
1539
+ show,
1540
+ hide,
1541
+ animated,
1542
+ portal,
1543
+ portalRoot,
1544
+ getReferenceProps,
1545
+ getFloatingProps
1220
1546
  ]);
1221
1547
  return /* @__PURE__ */ jsx(TooltipContext.Provider, {
1222
- value: {
1223
- open,
1224
- setOpen,
1225
- tooltipId,
1226
- show,
1227
- hide,
1228
- animated,
1229
- portal,
1230
- portalRoot,
1231
- getReferenceProps,
1232
- getFloatingProps
1233
- },
1548
+ value: contextValue,
1234
1549
  children
1235
1550
  });
1236
1551
  }
1552
+ /**
1553
+ * A single tooltip.
1554
+ *
1555
+ * @deprecated Renamed to {@link TooltipRoot}. Despite the name this component
1556
+ * never provided anything to a subtree of tooltips — it IS one tooltip. The
1557
+ * delay-sharing provider is {@link TooltipGroup}. Kept as an alias for
1558
+ * back-compat; scheduled for removal in the next major.
1559
+ */
1560
+ const TooltipProvider = TooltipRoot;
1237
1561
  const TooltipAnchor = forwardRef((_ref, ref) => {
1238
1562
  let { children, render, "aria-describedby": ariaDescribedBy } = _ref, props = _objectWithoutProperties(_ref, _excluded$15);
1239
1563
  const { getReferenceProps } = useTooltipContext();
@@ -1256,28 +1580,32 @@ const TooltipAnchor = forwardRef((_ref, ref) => {
1256
1580
  TooltipAnchor.displayName = "TooltipAnchor";
1257
1581
  /** Renders the `Tooltip` component. */
1258
1582
  function Tooltip(_ref3) {
1259
- let { children, onMouseEnter, onMouseLeave } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$8);
1583
+ let { children, onMouseEnter, onMouseLeave, render } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$8);
1260
1584
  const { open, tooltipId, show, hide, animated, portal, portalRoot, getFloatingProps } = useTooltipContext();
1261
1585
  const { mounted, dataAttributes, ref } = useEnterLeave(open, { animated });
1262
1586
  const floatingProps = getFloatingProps();
1587
+ const panelRef = useMergedRef(ref, floatingProps.ref);
1263
1588
  if (!mounted) return null;
1264
- const node = /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2(_objectSpread2({
1265
- ref: (n) => {
1266
- ref.current = n;
1267
- if (floatingProps.ref && typeof floatingProps.ref === "function") floatingProps.ref(n);
1268
- },
1269
- id: tooltipId,
1270
- role: "tooltip",
1271
- style: floatingProps.style,
1272
- onMouseEnter: (e) => {
1273
- show();
1274
- onMouseEnter === null || onMouseEnter === void 0 || onMouseEnter(e);
1275
- },
1276
- onMouseLeave: (e) => {
1277
- hide();
1278
- onMouseLeave === null || onMouseLeave === void 0 || onMouseLeave(e);
1279
- }
1280
- }, dataAttributes), props), {}, { children }));
1589
+ const node = renderElement("div", {
1590
+ render,
1591
+ refs: [panelRef],
1592
+ props: [
1593
+ {
1594
+ id: tooltipId,
1595
+ role: "tooltip",
1596
+ style: floatingProps.style,
1597
+ onMouseEnter: show,
1598
+ onMouseLeave: hide
1599
+ },
1600
+ dataAttributes,
1601
+ {
1602
+ onMouseEnter,
1603
+ onMouseLeave
1604
+ },
1605
+ props,
1606
+ { children }
1607
+ ]
1608
+ });
1281
1609
  return /* @__PURE__ */ jsx(OverlayPortal, {
1282
1610
  portal,
1283
1611
  portalRoot,
@@ -1287,7 +1615,7 @@ function Tooltip(_ref3) {
1287
1615
  //#endregion
1288
1616
  //#region src/primitives/popover.tsx
1289
1617
  const _excluded$14 = ["onClick"];
1290
- const _excluded2$9 = ["children"];
1618
+ const _excluded2$9 = ["children", "render"];
1291
1619
  const _excluded3$7 = ["onClick"];
1292
1620
  const PopoverContext = createContext(null);
1293
1621
  function usePopoverContext() {
@@ -1306,20 +1634,33 @@ function PopoverRoot({ children, open: controlledOpen, defaultOpen = false, onOp
1306
1634
  onOpenChange: setOpen,
1307
1635
  dismiss: true
1308
1636
  });
1637
+ const contextValue = useMemo(() => ({
1638
+ open,
1639
+ setOpen,
1640
+ popoverId,
1641
+ manageFocus,
1642
+ animated,
1643
+ portal,
1644
+ portalRoot,
1645
+ getReferenceProps,
1646
+ getFloatingProps,
1647
+ getPositionerStateProps,
1648
+ context
1649
+ }), [
1650
+ open,
1651
+ setOpen,
1652
+ popoverId,
1653
+ manageFocus,
1654
+ animated,
1655
+ portal,
1656
+ portalRoot,
1657
+ getReferenceProps,
1658
+ getFloatingProps,
1659
+ getPositionerStateProps,
1660
+ context
1661
+ ]);
1309
1662
  return /* @__PURE__ */ jsx(PopoverContext.Provider, {
1310
- value: {
1311
- open,
1312
- setOpen,
1313
- popoverId,
1314
- manageFocus,
1315
- animated,
1316
- portal,
1317
- portalRoot,
1318
- getReferenceProps,
1319
- getFloatingProps,
1320
- getPositionerStateProps,
1321
- context
1322
- },
1663
+ value: contextValue,
1323
1664
  children
1324
1665
  });
1325
1666
  }
@@ -1351,21 +1692,28 @@ const PopoverTrigger = forwardRef((_ref, ref) => {
1351
1692
  PopoverTrigger.displayName = "PopoverTrigger";
1352
1693
  /** Renders the `PopoverContent` component. */
1353
1694
  function PopoverContent(_ref2) {
1354
- let { children } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$9);
1695
+ let { children, render } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$9);
1355
1696
  const { open, popoverId, manageFocus, animated, portal, portalRoot, getFloatingProps, getPositionerStateProps, context } = usePopoverContext();
1356
1697
  const { ref, mounted, dataAttributes } = useEnterLeave(open, { animated });
1357
1698
  const floatingProps = getFloatingProps();
1699
+ const panelRef = useMergedRef(ref, floatingProps.ref);
1358
1700
  if (!mounted) return null;
1359
- const node = /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
1360
- ref: (n) => {
1361
- ref.current = n;
1362
- if (floatingProps.ref && typeof floatingProps.ref === "function") floatingProps.ref(n);
1363
- },
1364
- id: popoverId,
1365
- "data-popover-id": popoverId,
1366
- tabIndex: -1,
1367
- style: floatingProps.style
1368
- }, getPositionerStateProps(open)), dataAttributes), props), {}, { children }));
1701
+ const node = renderElement("div", {
1702
+ render,
1703
+ refs: [panelRef],
1704
+ props: [
1705
+ {
1706
+ id: popoverId,
1707
+ "data-popover-id": popoverId,
1708
+ tabIndex: -1,
1709
+ style: floatingProps.style
1710
+ },
1711
+ getPositionerStateProps(open),
1712
+ dataAttributes,
1713
+ props,
1714
+ { children }
1715
+ ]
1716
+ });
1369
1717
  return /* @__PURE__ */ jsx(OverlayPortal, {
1370
1718
  portal,
1371
1719
  portalRoot,
@@ -1392,144 +1740,245 @@ const PopoverClose = forwardRef((_ref3, ref) => {
1392
1740
  });
1393
1741
  PopoverClose.displayName = "PopoverClose";
1394
1742
  //#endregion
1743
+ //#region src/internal/create-store.ts
1744
+ /** Creates a {@link Store} seeded with `initial`. */
1745
+ function createStore(initial) {
1746
+ let state = initial;
1747
+ const listeners = /* @__PURE__ */ new Set();
1748
+ return {
1749
+ getState: () => state,
1750
+ setState: (partial) => {
1751
+ let changed = false;
1752
+ for (const key in partial) if (!Object.is(state[key], partial[key])) {
1753
+ changed = true;
1754
+ break;
1755
+ }
1756
+ if (!changed) return;
1757
+ state = _objectSpread2(_objectSpread2({}, state), partial);
1758
+ for (const listener of listeners) listener();
1759
+ },
1760
+ subscribe: (listener) => {
1761
+ listeners.add(listener);
1762
+ return () => {
1763
+ listeners.delete(listener);
1764
+ };
1765
+ }
1766
+ };
1767
+ }
1768
+ /**
1769
+ * Subscribe to a slice of a {@link Store}.
1770
+ *
1771
+ * The selector MUST return a stable value (primitive, or a referentially stable
1772
+ * object) — `useSyncExternalStore` re-renders in a loop otherwise.
1773
+ */
1774
+ function useStoreSelector(store, selector) {
1775
+ return useSyncExternalStore(store.subscribe, () => selector(store.getState()), () => selector(store.getState()));
1776
+ }
1777
+ //#endregion
1395
1778
  //#region src/primitives/select.tsx
1396
1779
  const _excluded$13 = [
1397
1780
  "onClick",
1398
1781
  "onKeyDown",
1399
1782
  "children"
1400
1783
  ];
1401
- const _excluded2$8 = ["children"];
1784
+ const _excluded2$8 = ["children", "render"];
1402
1785
  const _excluded3$6 = [
1403
1786
  "value",
1404
1787
  "disabled",
1405
1788
  "onClick",
1406
1789
  "children"
1407
1790
  ];
1408
- function firstEnabledId$4(items) {
1409
- var _items$find$id, _items$find;
1410
- return (_items$find$id = (_items$find = items.find((i) => !i.disabled)) === null || _items$find === void 0 ? void 0 : _items$find.id) !== null && _items$find$id !== void 0 ? _items$find$id : null;
1791
+ /** Normalize the raw value (single `string` or multiple `string[]`). */
1792
+ function toValues(value) {
1793
+ if (Array.isArray(value)) return value;
1794
+ return value === "" ? [] : [value];
1795
+ }
1796
+ const SelectStoreContext = createContext(null);
1797
+ function useSelectStoreContext() {
1798
+ const ctx = useContext(SelectStoreContext);
1799
+ if (!ctx) throw new Error("Select components must be used within SelectRoot");
1800
+ return ctx;
1411
1801
  }
1412
- function moveActiveId$4(items, activeId, delta) {
1413
- const enabled = items.filter((i) => !i.disabled);
1414
- if (enabled.length === 0) return null;
1415
- const idx = enabled.findIndex((i) => i.id === activeId);
1416
- if (idx < 0) return delta > 0 ? enabled[0].id : enabled[enabled.length - 1].id;
1417
- return enabled[Math.min(enabled.length - 1, Math.max(0, idx + delta))].id;
1802
+ /** Subscribe to a slice of select state; re-renders only when it changes. */
1803
+ function useSelectSelector(selector) {
1804
+ const { store } = useSelectStoreContext();
1805
+ return useStoreSelector(store, selector);
1418
1806
  }
1419
- const SelectContext = createContext(null);
1420
- function useSelectContext() {
1421
- const ctx = useContext(SelectContext);
1807
+ const SelectFloatingContext = createContext(null);
1808
+ function useSelectFloating() {
1809
+ const ctx = useContext(SelectFloatingContext);
1422
1810
  if (!ctx) throw new Error("Select components must be used within SelectRoot");
1423
1811
  return ctx;
1424
1812
  }
1425
1813
  /** Renders the `SelectRoot` component. */
1426
- function SelectRoot({ children, value: controlledValue, defaultValue, onValueChange, setValue: setValueDeprecated, multiple = false, name, open: controlledOpen, onOpenChange, setOpen: setOpenDeprecated, animated = true, portal = false, portalRoot = null }) {
1814
+ function SelectRoot({ children, value: controlledValue, defaultValue, onValueChange, setValue: setValueDeprecated, multiple = false, readOnly = false, name, open: controlledOpen, onOpenChange, setOpen: setOpenDeprecated, animated = true, portal = false, portalRoot = null }) {
1427
1815
  var _selectedValues$;
1428
- const [value, setValue] = useControllableState(defaultValue !== null && defaultValue !== void 0 ? defaultValue : multiple ? [] : "", controlledValue, onValueChange !== null && onValueChange !== void 0 ? onValueChange : setValueDeprecated);
1429
- const [open, setOpenState] = useState(controlledOpen !== null && controlledOpen !== void 0 ? controlledOpen : false);
1430
- const onOpenChangeCb = onOpenChange !== null && onOpenChange !== void 0 ? onOpenChange : setOpenDeprecated;
1431
- const [activeId, setActiveId] = useState(null);
1432
1816
  const selectId = useId();
1433
1817
  const listboxId = `${selectId}-listbox`;
1434
- const items = useRef([]);
1435
- useEffect(() => {
1436
- if (controlledOpen !== void 0) setOpenState(controlledOpen);
1437
- }, [controlledOpen]);
1438
- const setOpen = useCallback((v) => {
1439
- if (controlledOpen === void 0) setOpenState(v);
1440
- onOpenChangeCb === null || onOpenChangeCb === void 0 || onOpenChangeCb(v);
1441
- if (!v) setActiveId(null);
1442
- }, [controlledOpen, onOpenChangeCb]);
1443
- const selectedValues = useMemo(() => Array.isArray(value) ? value : value === "" ? [] : [value], [value]);
1444
- const isSelected = useCallback((v) => selectedValues.includes(v), [selectedValues]);
1445
- const selectValue = useCallback((v) => {
1446
- if (multiple) {
1447
- const arr = Array.isArray(value) ? value : value ? [value] : [];
1448
- setValue(arr.includes(v) ? arr.filter((x) => x !== v) : [...arr, v]);
1449
- } else {
1450
- setValue(v);
1451
- setOpen(false);
1452
- }
1453
- }, [
1818
+ const { items, registerItem, unregisterItem: removeItem } = useItemRegistry();
1819
+ const [store] = useState(() => {
1820
+ var _ref;
1821
+ return createStore({
1822
+ value: (_ref = controlledValue !== null && controlledValue !== void 0 ? controlledValue : defaultValue) !== null && _ref !== void 0 ? _ref : multiple ? [] : "",
1823
+ activeId: null,
1824
+ open: controlledOpen !== null && controlledOpen !== void 0 ? controlledOpen : false
1825
+ });
1826
+ });
1827
+ const propsRef = useRef({
1828
+ controlledValue,
1829
+ controlledOpen,
1454
1830
  multiple,
1455
- value,
1456
- setValue,
1457
- setOpen
1458
- ]);
1459
- const registerItem = useCallback((entry) => {
1460
- const list = items.current;
1461
- const existing = list.findIndex((i) => i.id === entry.id);
1462
- if (existing >= 0) list[existing] = entry;
1463
- else list.push(entry);
1464
- }, []);
1831
+ readOnly,
1832
+ onValueChange: onValueChange !== null && onValueChange !== void 0 ? onValueChange : setValueDeprecated,
1833
+ onOpenChange: onOpenChange !== null && onOpenChange !== void 0 ? onOpenChange : setOpenDeprecated
1834
+ });
1835
+ propsRef.current = {
1836
+ controlledValue,
1837
+ controlledOpen,
1838
+ multiple,
1839
+ readOnly,
1840
+ onValueChange: onValueChange !== null && onValueChange !== void 0 ? onValueChange : setValueDeprecated,
1841
+ onOpenChange: onOpenChange !== null && onOpenChange !== void 0 ? onOpenChange : setOpenDeprecated
1842
+ };
1843
+ useLayoutEffect(() => {
1844
+ if (controlledValue !== void 0) store.setState({ value: controlledValue });
1845
+ }, [controlledValue, store]);
1846
+ useLayoutEffect(() => {
1847
+ if (controlledOpen !== void 0) store.setState({ open: controlledOpen });
1848
+ }, [controlledOpen, store]);
1849
+ const actions = useMemo(() => {
1850
+ const setValue = (v) => {
1851
+ var _p$onValueChange;
1852
+ const p = propsRef.current;
1853
+ if (p.controlledValue === void 0) store.setState({ value: v });
1854
+ (_p$onValueChange = p.onValueChange) === null || _p$onValueChange === void 0 || _p$onValueChange.call(p, v);
1855
+ };
1856
+ const setOpen = (v) => {
1857
+ var _p$onOpenChange;
1858
+ const p = propsRef.current;
1859
+ if (p.controlledOpen === void 0) store.setState({ open: v });
1860
+ (_p$onOpenChange = p.onOpenChange) === null || _p$onOpenChange === void 0 || _p$onOpenChange.call(p, v);
1861
+ if (!v) store.setState({ activeId: null });
1862
+ };
1863
+ return {
1864
+ setValue,
1865
+ setOpen,
1866
+ setActiveId: (updater) => {
1867
+ const cur = store.getState().activeId;
1868
+ const next = typeof updater === "function" ? updater(cur) : updater;
1869
+ store.setState({ activeId: next });
1870
+ },
1871
+ selectValue: (v) => {
1872
+ const p = propsRef.current;
1873
+ if (p.readOnly) return;
1874
+ if (p.multiple) {
1875
+ const arr = toValues(store.getState().value);
1876
+ setValue(arr.includes(v) ? arr.filter((x) => x !== v) : [...arr, v]);
1877
+ } else {
1878
+ setValue(v);
1879
+ setOpen(false);
1880
+ }
1881
+ }
1882
+ };
1883
+ }, [store]);
1465
1884
  const unregisterItem = useCallback((id) => {
1466
- items.current = items.current.filter((i) => i.id !== id);
1467
- }, []);
1885
+ const removedIndex = removeItem(id);
1886
+ if (store.getState().activeId !== id) return;
1887
+ store.setState({ activeId: nearestEnabledId(items.current, removedIndex) });
1888
+ }, [
1889
+ items,
1890
+ removeItem,
1891
+ store
1892
+ ]);
1893
+ const open = useStoreSelector(store, (state) => state.open);
1894
+ const value = useStoreSelector(store, (state) => state.value);
1895
+ const selectedValues = useMemo(() => toValues(value), [value]);
1468
1896
  const { getReferenceProps, getFloatingProps } = useFloating({
1469
1897
  placement: "bottom-start",
1470
1898
  gutter: 4,
1471
1899
  sameWidth: true,
1472
1900
  open,
1473
- onOpenChange: setOpen,
1901
+ onOpenChange: actions.setOpen,
1474
1902
  dismiss: {
1475
1903
  outsidePress: true,
1476
1904
  escapeKey: false
1477
1905
  }
1478
1906
  });
1479
- return /* @__PURE__ */ jsxs(SelectContext.Provider, {
1480
- value: {
1481
- open,
1482
- setOpen,
1483
- value,
1484
- selectedValues,
1485
- multiple,
1486
- isSelected,
1487
- selectValue,
1488
- activeId,
1489
- setActiveId,
1490
- selectId,
1491
- listboxId,
1492
- getReferenceProps,
1493
- getFloatingProps,
1494
- items,
1495
- registerItem,
1496
- unregisterItem,
1497
- animated,
1498
- portal,
1499
- portalRoot
1500
- },
1501
- children: [children, name && (multiple ? selectedValues.map((v) => /* @__PURE__ */ jsx("input", {
1502
- type: "hidden",
1503
- name,
1504
- value: v
1505
- }, v)) : /* @__PURE__ */ jsx("input", {
1506
- type: "hidden",
1507
- name,
1508
- value: (_selectedValues$ = selectedValues[0]) !== null && _selectedValues$ !== void 0 ? _selectedValues$ : ""
1509
- }))]
1907
+ const storeContext = useMemo(() => ({
1908
+ store,
1909
+ actions,
1910
+ multiple,
1911
+ readOnly,
1912
+ selectId,
1913
+ listboxId,
1914
+ items,
1915
+ registerItem,
1916
+ unregisterItem
1917
+ }), [
1918
+ store,
1919
+ actions,
1920
+ multiple,
1921
+ readOnly,
1922
+ selectId,
1923
+ listboxId,
1924
+ registerItem,
1925
+ unregisterItem
1926
+ ]);
1927
+ const floatingContext = useMemo(() => ({
1928
+ getReferenceProps,
1929
+ getFloatingProps,
1930
+ animated,
1931
+ portal,
1932
+ portalRoot
1933
+ }), [
1934
+ getReferenceProps,
1935
+ getFloatingProps,
1936
+ animated,
1937
+ portal,
1938
+ portalRoot
1939
+ ]);
1940
+ return /* @__PURE__ */ jsx(SelectStoreContext.Provider, {
1941
+ value: storeContext,
1942
+ children: /* @__PURE__ */ jsxs(SelectFloatingContext.Provider, {
1943
+ value: floatingContext,
1944
+ children: [children, name && (multiple ? selectedValues.map((v) => /* @__PURE__ */ jsx("input", {
1945
+ type: "hidden",
1946
+ name,
1947
+ value: v
1948
+ }, v)) : /* @__PURE__ */ jsx("input", {
1949
+ type: "hidden",
1950
+ name,
1951
+ value: (_selectedValues$ = selectedValues[0]) !== null && _selectedValues$ !== void 0 ? _selectedValues$ : ""
1952
+ }))]
1953
+ })
1510
1954
  });
1511
1955
  }
1512
1956
  /** Renders the `SelectLabel` component. */
1513
1957
  function SelectLabel(props) {
1514
- const { selectId } = useSelectContext();
1958
+ const { selectId } = useSelectStoreContext();
1515
1959
  return /* @__PURE__ */ jsx("label", _objectSpread2({ htmlFor: selectId }, props));
1516
1960
  }
1517
- const SelectTrigger = forwardRef((_ref, ref) => {
1961
+ const SelectTrigger = forwardRef((_ref2, ref) => {
1518
1962
  var _selectedValues$2;
1519
- let { onClick, onKeyDown, children } = _ref, props = _objectWithoutProperties(_ref, _excluded$13);
1520
- const { open, setOpen, selectedValues, multiple, selectValue, selectId, listboxId, getReferenceProps, items, activeId, setActiveId } = useSelectContext();
1963
+ let { onClick, onKeyDown, children } = _ref2, props = _objectWithoutProperties(_ref2, _excluded$13);
1964
+ const { actions, multiple, readOnly, selectId, listboxId, items, store } = useSelectStoreContext();
1965
+ const { getReferenceProps } = useSelectFloating();
1966
+ const open = useSelectSelector((state) => state.open);
1967
+ const activeId = useSelectSelector((state) => state.activeId);
1968
+ const value = useSelectSelector((state) => state.value);
1969
+ const selectedValues = useMemo(() => toValues(value), [value]);
1521
1970
  const refProps = getReferenceProps();
1522
1971
  const typeahead = useRef({
1523
1972
  buffer: "",
1524
1973
  timer: 0
1525
1974
  });
1526
1975
  const selectActive = useCallback(() => {
1527
- const item = items.current.find((i) => i.id === activeId);
1528
- if (item && !item.disabled) selectValue(item.value);
1976
+ const item = items.current.find((i) => i.id === store.getState().activeId);
1977
+ if (item && !item.disabled) actions.selectValue(item.value);
1529
1978
  }, [
1530
1979
  items,
1531
- activeId,
1532
- selectValue
1980
+ store,
1981
+ actions
1533
1982
  ]);
1534
1983
  const runTypeahead = useCallback((char) => {
1535
1984
  const t = typeahead.current;
@@ -1542,52 +1991,51 @@ const SelectTrigger = forwardRef((_ref, ref) => {
1542
1991
  var _document$getElementB;
1543
1992
  return (_document$getElementB = document.getElementById(i.id)) === null || _document$getElementB === void 0 || (_document$getElementB = _document$getElementB.textContent) === null || _document$getElementB === void 0 ? void 0 : _document$getElementB.trim().toLowerCase().startsWith(t.buffer);
1544
1993
  });
1545
- if (match) setActiveId(match.id);
1546
- }, [items, setActiveId]);
1994
+ if (match) actions.setActiveId(match.id);
1995
+ }, [items, actions]);
1547
1996
  const handleClick = useCallback((e) => {
1548
- setOpen(!open);
1997
+ actions.setOpen(!open);
1549
1998
  onClick === null || onClick === void 0 || onClick(e);
1550
1999
  }, [
1551
2000
  open,
1552
- setOpen,
2001
+ actions,
1553
2002
  onClick
1554
2003
  ]);
1555
2004
  const handleKeyDown = useCallback((e) => {
2005
+ const currentActiveId = store.getState().activeId;
1556
2006
  switch (e.key) {
1557
2007
  case "ArrowDown":
1558
2008
  e.preventDefault();
1559
- if (!open) setOpen(true);
1560
- else setActiveId(moveActiveId$4(items.current, activeId, 1));
2009
+ if (!open) actions.setOpen(true);
2010
+ else actions.setActiveId(moveActiveId(items.current, currentActiveId, 1));
1561
2011
  break;
1562
2012
  case "ArrowUp":
1563
2013
  e.preventDefault();
1564
- if (!open) setOpen(true);
1565
- else setActiveId(moveActiveId$4(items.current, activeId, -1));
2014
+ if (!open) actions.setOpen(true);
2015
+ else actions.setActiveId(moveActiveId(items.current, currentActiveId, -1));
1566
2016
  break;
1567
2017
  case "Home":
1568
2018
  if (open) {
1569
2019
  e.preventDefault();
1570
- setActiveId(firstEnabledId$4(items.current));
2020
+ actions.setActiveId(firstEnabledId(items.current));
1571
2021
  }
1572
2022
  break;
1573
2023
  case "End":
1574
2024
  if (open) {
1575
- var _enabled$id, _enabled;
1576
2025
  e.preventDefault();
1577
- const enabled = items.current.filter((i) => !i.disabled);
1578
- setActiveId((_enabled$id = (_enabled = enabled[enabled.length - 1]) === null || _enabled === void 0 ? void 0 : _enabled.id) !== null && _enabled$id !== void 0 ? _enabled$id : null);
2026
+ actions.setActiveId(lastEnabledId(items.current));
1579
2027
  }
1580
2028
  break;
1581
2029
  case "Enter":
1582
2030
  case " ":
1583
2031
  e.preventDefault();
1584
2032
  if (open) selectActive();
1585
- else setOpen(true);
2033
+ else actions.setOpen(true);
1586
2034
  break;
1587
2035
  case "Escape":
1588
2036
  if (open) {
1589
2037
  e.preventDefault();
1590
- setOpen(false);
2038
+ actions.setOpen(false);
1591
2039
  }
1592
2040
  break;
1593
2041
  default: if (open && e.key.length === 1 && e.key !== " " && !e.metaKey && !e.ctrlKey && !e.altKey && !e.nativeEvent.isComposing) runTypeahead(e.key);
@@ -1596,9 +2044,8 @@ const SelectTrigger = forwardRef((_ref, ref) => {
1596
2044
  }, [
1597
2045
  open,
1598
2046
  items,
1599
- activeId,
1600
- setActiveId,
1601
- setOpen,
2047
+ store,
2048
+ actions,
1602
2049
  selectActive,
1603
2050
  runTypeahead,
1604
2051
  onKeyDown
@@ -1616,6 +2063,8 @@ const SelectTrigger = forwardRef((_ref, ref) => {
1616
2063
  "aria-haspopup": "listbox",
1617
2064
  "aria-controls": open ? listboxId : void 0,
1618
2065
  "aria-activedescendant": open ? activeId !== null && activeId !== void 0 ? activeId : void 0 : void 0,
2066
+ "aria-readonly": readOnly || void 0,
2067
+ "data-readonly": readOnly ? "" : void 0,
1619
2068
  "data-select-trigger": selectId,
1620
2069
  onClick: handleClick,
1621
2070
  onKeyDown: handleKeyDown
@@ -1623,38 +2072,50 @@ const SelectTrigger = forwardRef((_ref, ref) => {
1623
2072
  });
1624
2073
  SelectTrigger.displayName = "SelectTrigger";
1625
2074
  /** Renders the `SelectPopover` component. */
1626
- function SelectPopover(_ref2) {
1627
- let { children } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$8);
1628
- const { open, multiple, selectedValues, listboxId, selectId, getFloatingProps, items, activeId, setActiveId, animated, portal, portalRoot } = useSelectContext();
2075
+ function SelectPopover(_ref3) {
2076
+ let { children, render } = _ref3, props = _objectWithoutProperties(_ref3, _excluded2$8);
2077
+ const { actions, multiple, readOnly, selectId, listboxId, items, store } = useSelectStoreContext();
2078
+ const { getFloatingProps, animated, portal, portalRoot } = useSelectFloating();
2079
+ const open = useSelectSelector((state) => state.open);
2080
+ const activeId = useSelectSelector((state) => state.activeId);
1629
2081
  const { ref, mounted, dataAttributes } = useEnterLeave(open, { animated });
1630
2082
  const floatingProps = getFloatingProps();
2083
+ const panelRef = useMergedRef(ref, floatingProps.ref);
1631
2084
  useLayoutEffect(() => {
1632
- if (mounted) setActiveId((prev) => {
2085
+ if (!mounted) return;
2086
+ actions.setActiveId((prev) => {
1633
2087
  var _selected$id;
1634
2088
  if (prev) return prev;
2089
+ const selectedValues = toValues(store.getState().value);
1635
2090
  const selected = items.current.find((i) => selectedValues.includes(i.value));
1636
- return (_selected$id = selected === null || selected === void 0 ? void 0 : selected.id) !== null && _selected$id !== void 0 ? _selected$id : firstEnabledId$4(items.current);
2091
+ return (_selected$id = selected === null || selected === void 0 ? void 0 : selected.id) !== null && _selected$id !== void 0 ? _selected$id : firstEnabledId(items.current);
1637
2092
  });
1638
2093
  }, [
1639
2094
  mounted,
1640
2095
  items,
1641
- selectedValues,
1642
- setActiveId
2096
+ store,
2097
+ actions
1643
2098
  ]);
1644
2099
  useScrollActiveDescendantIntoView(activeId);
1645
2100
  if (!mounted) return null;
1646
- const node = /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2(_objectSpread2({
1647
- ref: (n) => {
1648
- ref.current = n;
1649
- if (floatingProps.ref && typeof floatingProps.ref === "function") floatingProps.ref(n);
1650
- },
1651
- id: listboxId,
1652
- role: "listbox",
1653
- "aria-multiselectable": multiple || void 0,
1654
- "aria-activedescendant": activeId !== null && activeId !== void 0 ? activeId : void 0,
1655
- "data-select-id": selectId,
1656
- style: floatingProps.style
1657
- }, dataAttributes), props), {}, { children }));
2101
+ const node = renderElement("div", {
2102
+ render,
2103
+ refs: [panelRef],
2104
+ props: [
2105
+ {
2106
+ id: listboxId,
2107
+ role: "listbox",
2108
+ "aria-multiselectable": multiple || void 0,
2109
+ "aria-readonly": readOnly || void 0,
2110
+ "aria-activedescendant": activeId !== null && activeId !== void 0 ? activeId : void 0,
2111
+ "data-select-id": selectId,
2112
+ style: floatingProps.style
2113
+ },
2114
+ dataAttributes,
2115
+ props,
2116
+ { children }
2117
+ ]
2118
+ });
1658
2119
  return /* @__PURE__ */ jsx(OverlayPortal, {
1659
2120
  portal,
1660
2121
  portalRoot,
@@ -1662,12 +2123,12 @@ function SelectPopover(_ref2) {
1662
2123
  });
1663
2124
  }
1664
2125
  /** Renders the `SelectItem` component. */
1665
- function SelectItem(_ref3) {
1666
- let { value: itemValue, disabled, onClick, children } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$6);
1667
- const { isSelected: isValueSelected, selectValue, activeId, setActiveId, registerItem, unregisterItem } = useSelectContext();
2126
+ function SelectItem(_ref4) {
2127
+ let { value: itemValue, disabled, onClick, children } = _ref4, props = _objectWithoutProperties(_ref4, _excluded3$6);
2128
+ const { actions, registerItem, unregisterItem } = useSelectStoreContext();
1668
2129
  const itemId = useId();
1669
- const isSelected = isValueSelected(itemValue);
1670
- const isActive = activeId === itemId;
2130
+ const isSelected = useSelectSelector((state) => toValues(state.value).includes(itemValue));
2131
+ const isActive = useSelectSelector((state) => state.activeId === itemId);
1671
2132
  useLayoutEffect(() => {
1672
2133
  registerItem({
1673
2134
  id: itemId,
@@ -1683,12 +2144,12 @@ function SelectItem(_ref3) {
1683
2144
  unregisterItem
1684
2145
  ]);
1685
2146
  const handleClick = useCallback((e) => {
1686
- if (!disabled) selectValue(itemValue);
2147
+ if (!disabled) actions.selectValue(itemValue);
1687
2148
  onClick === null || onClick === void 0 || onClick(e);
1688
2149
  }, [
1689
2150
  disabled,
1690
2151
  itemValue,
1691
- selectValue,
2152
+ actions,
1692
2153
  onClick
1693
2154
  ]);
1694
2155
  return /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2({
@@ -1700,14 +2161,14 @@ function SelectItem(_ref3) {
1700
2161
  "data-disabled": disabled ? "" : void 0,
1701
2162
  onClick: handleClick,
1702
2163
  onMouseEnter: () => {
1703
- if (!disabled) setActiveId(itemId);
2164
+ if (!disabled) actions.setActiveId(itemId);
1704
2165
  }
1705
2166
  }, props), {}, { children: children !== null && children !== void 0 ? children : itemValue }));
1706
2167
  }
1707
2168
  //#endregion
1708
2169
  //#region src/primitives/combobox.tsx
1709
2170
  const _excluded$12 = ["onKeyDown"];
1710
- const _excluded2$7 = ["children"];
2171
+ const _excluded2$7 = ["children", "render"];
1711
2172
  const _excluded3$5 = [
1712
2173
  "value",
1713
2174
  "disabled",
@@ -1715,36 +2176,6 @@ const _excluded3$5 = [
1715
2176
  "children"
1716
2177
  ];
1717
2178
  const _excluded4$3 = ["children"];
1718
- function firstEnabledId$3(items) {
1719
- var _items$find$id, _items$find;
1720
- return (_items$find$id = (_items$find = items.find((i) => !i.disabled)) === null || _items$find === void 0 ? void 0 : _items$find.id) !== null && _items$find$id !== void 0 ? _items$find$id : null;
1721
- }
1722
- function moveActiveId$3(items, activeId, delta) {
1723
- const enabled = items.filter((i) => !i.disabled);
1724
- if (enabled.length === 0) return null;
1725
- const idx = enabled.findIndex((i) => i.id === activeId);
1726
- if (idx < 0) return delta > 0 ? enabled[0].id : enabled[enabled.length - 1].id;
1727
- return enabled[Math.min(enabled.length - 1, Math.max(0, idx + delta))].id;
1728
- }
1729
- function createComboboxStore(initial) {
1730
- let state = initial;
1731
- const listeners = /* @__PURE__ */ new Set();
1732
- return {
1733
- getState: () => state,
1734
- setState: (partial) => {
1735
- const next = _objectSpread2(_objectSpread2({}, state), partial);
1736
- if (next.value === state.value && next.searchValue === state.searchValue && next.activeId === state.activeId && next.open === state.open) return;
1737
- state = next;
1738
- listeners.forEach((l) => l());
1739
- },
1740
- subscribe: (listener) => {
1741
- listeners.add(listener);
1742
- return () => {
1743
- listeners.delete(listener);
1744
- };
1745
- }
1746
- };
1747
- }
1748
2179
  const ComboboxStoreContext = createContext(null);
1749
2180
  function useComboboxStoreContext() {
1750
2181
  const ctx = useContext(ComboboxStoreContext);
@@ -1754,7 +2185,7 @@ function useComboboxStoreContext() {
1754
2185
  /** Subscribe to a slice of combobox state; re-renders only when it changes. */
1755
2186
  function useComboboxSelector(selector) {
1756
2187
  const { store } = useComboboxStoreContext();
1757
- return useSyncExternalStore(store.subscribe, () => selector(store.getState()), () => selector(store.getState()));
2188
+ return useStoreSelector(store, selector);
1758
2189
  }
1759
2190
  const ComboboxFloatingContext = createContext(null);
1760
2191
  function useComboboxFloating() {
@@ -1763,11 +2194,11 @@ function useComboboxFloating() {
1763
2194
  return ctx;
1764
2195
  }
1765
2196
  /** Renders the `ComboboxRoot` component. */
1766
- function ComboboxRoot({ children, value: controlledValue, defaultValue = "", onValueChange, setValue: setValueDeprecated, open: controlledOpen, onOpenChange, setOpen: setOpenDeprecated, animated = true, portal = false, portalRoot = null }) {
2197
+ function ComboboxRoot({ children, value: controlledValue, defaultValue = "", onValueChange, setValue: setValueDeprecated, readOnly = false, open: controlledOpen, onOpenChange, setOpen: setOpenDeprecated, animated = true, portal = false, portalRoot = null }) {
1767
2198
  const comboboxId = useId();
1768
2199
  const listboxId = `${comboboxId}-listbox`;
1769
- const items = useRef([]);
1770
- const [store] = useState(() => createComboboxStore({
2200
+ const { items, registerItem, unregisterItem: removeItem } = useItemRegistry();
2201
+ const [store] = useState(() => createStore({
1771
2202
  value: controlledValue !== null && controlledValue !== void 0 ? controlledValue : defaultValue,
1772
2203
  searchValue: "",
1773
2204
  activeId: null,
@@ -1785,10 +2216,10 @@ function ComboboxRoot({ children, value: controlledValue, defaultValue = "", onV
1785
2216
  onValueChange: onValueChange !== null && onValueChange !== void 0 ? onValueChange : setValueDeprecated,
1786
2217
  onOpenChange: onOpenChange !== null && onOpenChange !== void 0 ? onOpenChange : setOpenDeprecated
1787
2218
  };
1788
- useEffect(() => {
2219
+ useLayoutEffect(() => {
1789
2220
  if (controlledValue !== void 0) store.setState({ value: controlledValue });
1790
2221
  }, [controlledValue, store]);
1791
- useEffect(() => {
2222
+ useLayoutEffect(() => {
1792
2223
  if (controlledOpen !== void 0) store.setState({ open: controlledOpen });
1793
2224
  }, [controlledOpen, store]);
1794
2225
  const actions = useMemo(() => ({
@@ -1812,21 +2243,21 @@ function ComboboxRoot({ children, value: controlledValue, defaultValue = "", onV
1812
2243
  store.setState({ activeId: next });
1813
2244
  }
1814
2245
  }), [store]);
1815
- const registerItem = useCallback((entry) => {
1816
- const list = items.current;
1817
- const existing = list.findIndex((i) => i.id === entry.id);
1818
- if (existing >= 0) list[existing] = entry;
1819
- else list.push(entry);
1820
- }, []);
1821
2246
  const unregisterItem = useCallback((id) => {
1822
- items.current = items.current.filter((i) => i.id !== id);
1823
- }, []);
2247
+ const removedIndex = removeItem(id);
2248
+ if (store.getState().activeId !== id) return;
2249
+ store.setState({ activeId: nearestEnabledId(items.current, removedIndex) });
2250
+ }, [
2251
+ items,
2252
+ removeItem,
2253
+ store
2254
+ ]);
1824
2255
  const { getReferenceProps, getFloatingProps } = useFloating({
1825
2256
  placement: "bottom-start",
1826
2257
  gutter: 4,
1827
2258
  sameWidth: true,
1828
2259
  lazyFlip: true,
1829
- open: useSyncExternalStore(store.subscribe, () => store.getState().open, () => store.getState().open),
2260
+ open: useStoreSelector(store, (state) => state.open),
1830
2261
  onOpenChange: actions.setOpen,
1831
2262
  dismiss: {
1832
2263
  outsidePress: true,
@@ -1836,6 +2267,7 @@ function ComboboxRoot({ children, value: controlledValue, defaultValue = "", onV
1836
2267
  const storeContext = useMemo(() => ({
1837
2268
  store,
1838
2269
  actions,
2270
+ readOnly,
1839
2271
  comboboxId,
1840
2272
  listboxId,
1841
2273
  items,
@@ -1844,6 +2276,7 @@ function ComboboxRoot({ children, value: controlledValue, defaultValue = "", onV
1844
2276
  }), [
1845
2277
  store,
1846
2278
  actions,
2279
+ readOnly,
1847
2280
  comboboxId,
1848
2281
  listboxId,
1849
2282
  registerItem,
@@ -1872,7 +2305,7 @@ function ComboboxRoot({ children, value: controlledValue, defaultValue = "", onV
1872
2305
  }
1873
2306
  const ComboboxInput = forwardRef((_ref, ref) => {
1874
2307
  let { onKeyDown } = _ref, props = _objectWithoutProperties(_ref, _excluded$12);
1875
- const { actions, comboboxId, listboxId, items } = useComboboxStoreContext();
2308
+ const { actions, readOnly, comboboxId, listboxId, items } = useComboboxStoreContext();
1876
2309
  const { getReferenceProps } = useComboboxFloating();
1877
2310
  const open = useComboboxSelector((s) => s.open);
1878
2311
  const searchValue = useComboboxSelector((s) => s.searchValue);
@@ -1885,6 +2318,7 @@ const ComboboxInput = forwardRef((_ref, ref) => {
1885
2318
  actions.setActiveId(null);
1886
2319
  }, [actions, open]);
1887
2320
  const selectActive = useCallback(() => {
2321
+ if (readOnly) return;
1888
2322
  const item = items.current.find((i) => i.id === activeId);
1889
2323
  if (item && !item.disabled) {
1890
2324
  actions.setValue(item.value);
@@ -1894,7 +2328,8 @@ const ComboboxInput = forwardRef((_ref, ref) => {
1894
2328
  }, [
1895
2329
  items,
1896
2330
  activeId,
1897
- actions
2331
+ actions,
2332
+ readOnly
1898
2333
  ]);
1899
2334
  const handleKeyDown = useCallback((e) => {
1900
2335
  if (e.nativeEvent.isComposing) {
@@ -1905,12 +2340,12 @@ const ComboboxInput = forwardRef((_ref, ref) => {
1905
2340
  case "ArrowDown":
1906
2341
  e.preventDefault();
1907
2342
  if (!open) actions.setOpen(true);
1908
- actions.setActiveId(moveActiveId$3(items.current, activeId, 1));
2343
+ actions.setActiveId(moveActiveId(items.current, activeId, 1));
1909
2344
  break;
1910
2345
  case "ArrowUp":
1911
2346
  e.preventDefault();
1912
2347
  if (!open) actions.setOpen(true);
1913
- actions.setActiveId(moveActiveId$3(items.current, activeId, -1));
2348
+ actions.setActiveId(moveActiveId(items.current, activeId, -1));
1914
2349
  break;
1915
2350
  case "Enter":
1916
2351
  if (open && activeId) {
@@ -1945,6 +2380,9 @@ const ComboboxInput = forwardRef((_ref, ref) => {
1945
2380
  "aria-autocomplete": "list",
1946
2381
  "aria-controls": open ? listboxId : void 0,
1947
2382
  "aria-activedescendant": open ? activeId !== null && activeId !== void 0 ? activeId : void 0 : void 0,
2383
+ "aria-readonly": readOnly || void 0,
2384
+ readOnly,
2385
+ "data-readonly": readOnly ? "" : void 0,
1948
2386
  "data-combobox-input": comboboxId,
1949
2387
  value: searchValue,
1950
2388
  onChange: handleChange,
@@ -1954,29 +2392,35 @@ const ComboboxInput = forwardRef((_ref, ref) => {
1954
2392
  ComboboxInput.displayName = "ComboboxInput";
1955
2393
  /** Renders the `ComboboxPopover` component. */
1956
2394
  function ComboboxPopover(_ref2) {
1957
- let { children } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$7);
2395
+ let { children, render } = _ref2, props = _objectWithoutProperties(_ref2, _excluded2$7);
1958
2396
  const { actions, comboboxId, listboxId, items } = useComboboxStoreContext();
1959
2397
  const { getFloatingProps, animated, portal, portalRoot } = useComboboxFloating();
1960
2398
  const { ref, mounted, dataAttributes } = useEnterLeave(useComboboxSelector((s) => s.open), { animated });
1961
2399
  const floatingProps = getFloatingProps();
2400
+ const panelRef = useMergedRef(ref, floatingProps.ref);
1962
2401
  useLayoutEffect(() => {
1963
- if (mounted) actions.setActiveId((prev) => prev !== null && prev !== void 0 ? prev : firstEnabledId$3(items.current));
2402
+ if (mounted) actions.setActiveId((prev) => prev !== null && prev !== void 0 ? prev : firstEnabledId(items.current));
1964
2403
  }, [
1965
2404
  mounted,
1966
2405
  items,
1967
2406
  actions
1968
2407
  ]);
1969
2408
  if (!mounted) return null;
1970
- const node = /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2(_objectSpread2({
1971
- ref: (n) => {
1972
- ref.current = n;
1973
- if (floatingProps.ref && typeof floatingProps.ref === "function") floatingProps.ref(n);
1974
- },
1975
- id: listboxId,
1976
- role: "listbox",
1977
- "data-combobox-id": comboboxId,
1978
- style: floatingProps.style
1979
- }, dataAttributes), props), {}, { children }));
2409
+ const node = renderElement("div", {
2410
+ render,
2411
+ refs: [panelRef],
2412
+ props: [
2413
+ {
2414
+ id: listboxId,
2415
+ role: "listbox",
2416
+ "data-combobox-id": comboboxId,
2417
+ style: floatingProps.style
2418
+ },
2419
+ dataAttributes,
2420
+ props,
2421
+ { children }
2422
+ ]
2423
+ });
1980
2424
  return /* @__PURE__ */ jsx(OverlayPortal, {
1981
2425
  portal,
1982
2426
  portalRoot,
@@ -1986,7 +2430,7 @@ function ComboboxPopover(_ref2) {
1986
2430
  /** Renders the `ComboboxItem` component. */
1987
2431
  function ComboboxItem(_ref3) {
1988
2432
  let { value: itemValue, disabled, onClick, children } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$5);
1989
- const { actions, registerItem, unregisterItem } = useComboboxStoreContext();
2433
+ const { actions, readOnly, registerItem, unregisterItem } = useComboboxStoreContext();
1990
2434
  const itemId = useId();
1991
2435
  const isSelected = useComboboxSelector((s) => s.value === itemValue);
1992
2436
  const isActive = useComboboxSelector((s) => s.activeId === itemId);
@@ -2005,7 +2449,7 @@ function ComboboxItem(_ref3) {
2005
2449
  unregisterItem
2006
2450
  ]);
2007
2451
  const handleClick = useCallback((e) => {
2008
- if (!disabled) {
2452
+ if (!disabled && !readOnly) {
2009
2453
  actions.setValue(itemValue);
2010
2454
  actions.setSearchValue(itemValue);
2011
2455
  actions.setOpen(false);
@@ -2013,6 +2457,7 @@ function ComboboxItem(_ref3) {
2013
2457
  onClick === null || onClick === void 0 || onClick(e);
2014
2458
  }, [
2015
2459
  disabled,
2460
+ readOnly,
2016
2461
  itemValue,
2017
2462
  actions,
2018
2463
  onClick
@@ -2284,10 +2729,18 @@ function CommandGroup(_ref5) {
2284
2729
  children
2285
2730
  })] }));
2286
2731
  }
2287
- /** Renders the `CommandSeparator` component. */
2732
+ /**
2733
+ * Renders the `CommandSeparator` component.
2734
+ *
2735
+ * Rendered with `role="presentation"`: the separator is a child of
2736
+ * `CommandList` (`role="listbox"`), whose only permitted children are `option`
2737
+ * and `group`. A real `role="separator"` there fails
2738
+ * `aria-required-children` and corrupts the option count announced by screen
2739
+ * readers. (Menu separators keep `role="separator"` — `menu` permits it.)
2740
+ */
2288
2741
  function CommandSeparator(props) {
2289
2742
  return /* @__PURE__ */ jsx("div", _objectSpread2({
2290
- role: "separator",
2743
+ role: "presentation",
2291
2744
  "data-slot": "command-separator"
2292
2745
  }, props));
2293
2746
  }
@@ -2370,7 +2823,11 @@ const _excluded2$5 = [
2370
2823
  "onKeyDown",
2371
2824
  "role"
2372
2825
  ];
2373
- const _excluded3$3 = ["children", "onKeyDown"];
2826
+ const _excluded3$3 = [
2827
+ "children",
2828
+ "onKeyDown",
2829
+ "render"
2830
+ ];
2374
2831
  const _excluded4$1 = [
2375
2832
  "disabled",
2376
2833
  "hideOnClick",
@@ -2391,19 +2848,6 @@ const _excluded6$1 = [
2391
2848
  "onClick",
2392
2849
  "children"
2393
2850
  ];
2394
- /** First enabled item id, or null. */
2395
- function firstEnabledId$2(items) {
2396
- var _items$find$id, _items$find;
2397
- return (_items$find$id = (_items$find = items.find((i) => !i.disabled)) === null || _items$find === void 0 ? void 0 : _items$find.id) !== null && _items$find$id !== void 0 ? _items$find$id : null;
2398
- }
2399
- /** Move the active id by `delta` over the enabled items (clamped, no wrap). */
2400
- function moveActiveId$2(items, activeId, delta) {
2401
- const enabled = items.filter((i) => !i.disabled);
2402
- if (enabled.length === 0) return null;
2403
- const idx = enabled.findIndex((i) => i.id === activeId);
2404
- if (idx < 0) return delta > 0 ? enabled[0].id : enabled[enabled.length - 1].id;
2405
- return enabled[Math.min(enabled.length - 1, Math.max(0, idx + delta))].id;
2406
- }
2407
2851
  const MenuContext = createContext(null);
2408
2852
  function useMenuContext() {
2409
2853
  const ctx = useContext(MenuContext);
@@ -2454,20 +2898,19 @@ function MenubarContainer(_ref) {
2454
2898
  }
2455
2899
  /** Renders the `MenuRoot` component. */
2456
2900
  function MenuRoot({ children, open: controlledOpen, onOpenChange, setOpen: setOpenDeprecated, animated = true, portal = false, portalRoot = null }) {
2457
- const [open, setOpenState] = useState(controlledOpen !== null && controlledOpen !== void 0 ? controlledOpen : false);
2901
+ const [open, setOpenState] = useControllableState(false, controlledOpen, onOpenChange !== null && onOpenChange !== void 0 ? onOpenChange : setOpenDeprecated);
2458
2902
  const [activeId, setActiveId] = useState(null);
2459
2903
  const menuId = useId();
2460
2904
  const triggerId = `${menuId}-trigger`;
2461
- const items = useRef([]);
2462
- const onOpenChangeCb = onOpenChange !== null && onOpenChange !== void 0 ? onOpenChange : setOpenDeprecated;
2463
- useEffect(() => {
2464
- if (controlledOpen !== void 0) setOpenState(controlledOpen);
2465
- }, [controlledOpen]);
2905
+ const { items, registerItem: registerItemEntry, unregisterItem: removeItem } = useItemRegistry();
2906
+ const registerItem = useCallback((id, disabled) => registerItemEntry({
2907
+ id,
2908
+ disabled
2909
+ }), [registerItemEntry]);
2466
2910
  const setOpen = useCallback((v) => {
2467
- if (controlledOpen === void 0) setOpenState(v);
2468
- onOpenChangeCb === null || onOpenChangeCb === void 0 || onOpenChangeCb(v);
2911
+ setOpenState(v);
2469
2912
  if (!v) setActiveId(null);
2470
- }, [controlledOpen, onOpenChangeCb]);
2913
+ }, [setOpenState]);
2471
2914
  const focusTrigger = useCallback(() => {
2472
2915
  var _document$getElementB;
2473
2916
  (_document$getElementB = document.getElementById(triggerId)) === null || _document$getElementB === void 0 || _document$getElementB.focus();
@@ -2482,39 +2925,45 @@ function MenuRoot({ children, open: controlledOpen, onOpenChange, setOpen: setOp
2482
2925
  escapeKey: false
2483
2926
  }
2484
2927
  });
2485
- const registerItem = useCallback((id, disabled) => {
2486
- const list = items.current;
2487
- const existing = list.findIndex((i) => i.id === id);
2488
- if (existing >= 0) list[existing] = {
2489
- id,
2490
- disabled
2491
- };
2492
- else list.push({
2493
- id,
2494
- disabled
2495
- });
2496
- }, []);
2497
2928
  const unregisterItem = useCallback((id) => {
2498
- items.current = items.current.filter((i) => i.id !== id);
2499
- }, []);
2929
+ const removedIndex = removeItem(id);
2930
+ setActiveId((prev) => prev === id ? nearestEnabledId(items.current, removedIndex) : prev);
2931
+ }, [items, removeItem]);
2932
+ const contextValue = useMemo(() => ({
2933
+ open,
2934
+ setOpen,
2935
+ menuId,
2936
+ triggerId,
2937
+ getReferenceProps,
2938
+ getFloatingProps,
2939
+ activeId,
2940
+ setActiveId,
2941
+ items,
2942
+ registerItem,
2943
+ unregisterItem,
2944
+ focusTrigger,
2945
+ animated,
2946
+ portal,
2947
+ portalRoot
2948
+ }), [
2949
+ open,
2950
+ setOpen,
2951
+ menuId,
2952
+ triggerId,
2953
+ getReferenceProps,
2954
+ getFloatingProps,
2955
+ activeId,
2956
+ setActiveId,
2957
+ items,
2958
+ registerItem,
2959
+ unregisterItem,
2960
+ focusTrigger,
2961
+ animated,
2962
+ portal,
2963
+ portalRoot
2964
+ ]);
2500
2965
  return /* @__PURE__ */ jsx(MenuContext.Provider, {
2501
- value: {
2502
- open,
2503
- setOpen,
2504
- menuId,
2505
- triggerId,
2506
- getReferenceProps,
2507
- getFloatingProps,
2508
- activeId,
2509
- setActiveId,
2510
- items,
2511
- registerItem,
2512
- unregisterItem,
2513
- focusTrigger,
2514
- animated,
2515
- portal,
2516
- portalRoot
2517
- },
2966
+ value: contextValue,
2518
2967
  children
2519
2968
  });
2520
2969
  }
@@ -2558,11 +3007,12 @@ const MenuTrigger = forwardRef((_ref2, ref) => {
2558
3007
  MenuTrigger.displayName = "MenuTrigger";
2559
3008
  /** Renders the `MenuPopover` component. */
2560
3009
  function MenuPopover(_ref3) {
2561
- let { children, onKeyDown } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$3);
3010
+ let { children, onKeyDown, render } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$3);
2562
3011
  const { open, setOpen, menuId, triggerId, getFloatingProps, items, activeId, setActiveId, focusTrigger, animated, portal, portalRoot } = useMenuContext();
2563
3012
  const { ref, mounted, dataAttributes } = useEnterLeave(open, { animated });
2564
3013
  const floatingProps = getFloatingProps();
2565
3014
  const menuRef = useRef(null);
3015
+ const panelRef = useMergedRef(menuRef, ref, floatingProps.ref);
2566
3016
  const typeahead = useRef({
2567
3017
  buffer: "",
2568
3018
  timer: 0
@@ -2588,23 +3038,20 @@ function MenuPopover(_ref3) {
2588
3038
  switch (e.key) {
2589
3039
  case "ArrowDown":
2590
3040
  e.preventDefault();
2591
- setActiveId(moveActiveId$2(items.current, activeId, 1));
3041
+ setActiveId(moveActiveId(items.current, activeId, 1));
2592
3042
  break;
2593
3043
  case "ArrowUp":
2594
3044
  e.preventDefault();
2595
- setActiveId(moveActiveId$2(items.current, activeId, -1));
3045
+ setActiveId(moveActiveId(items.current, activeId, -1));
2596
3046
  break;
2597
3047
  case "Home":
2598
3048
  e.preventDefault();
2599
- setActiveId(firstEnabledId$2(items.current));
3049
+ setActiveId(firstEnabledId(items.current));
2600
3050
  break;
2601
- case "End": {
2602
- var _enabled$id, _enabled;
3051
+ case "End":
2603
3052
  e.preventDefault();
2604
- const enabled = items.current.filter((i) => !i.disabled);
2605
- setActiveId((_enabled$id = (_enabled = enabled[enabled.length - 1]) === null || _enabled === void 0 ? void 0 : _enabled.id) !== null && _enabled$id !== void 0 ? _enabled$id : null);
3053
+ setActiveId(lastEnabledId(items.current));
2606
3054
  break;
2607
- }
2608
3055
  case "Enter":
2609
3056
  case " ":
2610
3057
  e.preventDefault();
@@ -2635,7 +3082,7 @@ function MenuPopover(_ref3) {
2635
3082
  if (mounted) {
2636
3083
  var _menuRef$current;
2637
3084
  (_menuRef$current = menuRef.current) === null || _menuRef$current === void 0 || _menuRef$current.focus();
2638
- setActiveId((prev) => prev !== null && prev !== void 0 ? prev : firstEnabledId$2(items.current));
3085
+ setActiveId((prev) => prev !== null && prev !== void 0 ? prev : firstEnabledId(items.current));
2639
3086
  }
2640
3087
  }, [
2641
3088
  mounted,
@@ -2644,21 +3091,25 @@ function MenuPopover(_ref3) {
2644
3091
  ]);
2645
3092
  useScrollActiveDescendantIntoView(activeId);
2646
3093
  if (!mounted) return null;
2647
- const node = /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2(_objectSpread2({
2648
- ref: (n) => {
2649
- menuRef.current = n;
2650
- ref.current = n;
2651
- if (floatingProps.ref && typeof floatingProps.ref === "function") floatingProps.ref(n);
2652
- },
2653
- id: menuId,
2654
- role: "menu",
2655
- "aria-labelledby": triggerId,
2656
- "aria-activedescendant": activeId !== null && activeId !== void 0 ? activeId : void 0,
2657
- "data-menu-id": menuId,
2658
- style: floatingProps.style,
2659
- tabIndex: -1,
2660
- onKeyDown: handleKeyDown
2661
- }, dataAttributes), props), {}, { children }));
3094
+ const node = renderElement("div", {
3095
+ render,
3096
+ refs: [panelRef],
3097
+ props: [
3098
+ {
3099
+ id: menuId,
3100
+ role: "menu",
3101
+ "aria-labelledby": triggerId,
3102
+ "aria-activedescendant": activeId !== null && activeId !== void 0 ? activeId : void 0,
3103
+ "data-menu-id": menuId,
3104
+ style: floatingProps.style,
3105
+ tabIndex: -1,
3106
+ onKeyDown: handleKeyDown
3107
+ },
3108
+ dataAttributes,
3109
+ props,
3110
+ { children }
3111
+ ]
3112
+ });
2662
3113
  return /* @__PURE__ */ jsx(OverlayPortal, {
2663
3114
  portal,
2664
3115
  portalRoot,
@@ -2777,17 +3228,6 @@ function MenuButtonArrow(props) {
2777
3228
  //#region src/primitives/toolbar.tsx
2778
3229
  const _excluded$9 = ["onKeyDown"];
2779
3230
  const _excluded2$4 = ["disabled", "onFocus"];
2780
- function firstEnabledId$1(items) {
2781
- var _items$find$id, _items$find;
2782
- return (_items$find$id = (_items$find = items.find((i) => !i.disabled)) === null || _items$find === void 0 ? void 0 : _items$find.id) !== null && _items$find$id !== void 0 ? _items$find$id : null;
2783
- }
2784
- function moveActiveId$1(items, activeId, delta) {
2785
- const enabled = items.filter((i) => !i.disabled);
2786
- if (enabled.length === 0) return null;
2787
- const idx = enabled.findIndex((i) => i.id === activeId);
2788
- if (idx < 0) return delta > 0 ? enabled[0].id : enabled[enabled.length - 1].id;
2789
- return enabled[Math.min(enabled.length - 1, Math.max(0, idx + delta))].id;
2790
- }
2791
3231
  const ToolbarContext = createContext(null);
2792
3232
  function useToolbarContext() {
2793
3233
  const ctx = useContext(ToolbarContext);
@@ -2797,29 +3237,31 @@ function useToolbarContext() {
2797
3237
  /** Renders the `ToolbarRoot` component. */
2798
3238
  function ToolbarRoot({ children, orientation = "horizontal" }) {
2799
3239
  const [activeId, setActiveId] = useState(null);
2800
- const items = useRef([]);
2801
- const registerItem = useCallback((entry) => {
2802
- const list = items.current;
2803
- const existing = list.findIndex((i) => i.id === entry.id);
2804
- if (existing >= 0) list[existing] = entry;
2805
- else list.push(entry);
2806
- }, []);
3240
+ const { items, registerItem, unregisterItem: removeItem } = useItemRegistry();
2807
3241
  const unregisterItem = useCallback((id) => {
2808
- items.current = items.current.filter((i) => i.id !== id);
2809
- setActiveId((prev) => prev === id ? firstEnabledId$1(items.current) : prev);
2810
- }, []);
3242
+ const removedIndex = removeItem(id);
3243
+ setActiveId((prev) => prev === id ? nearestEnabledId(items.current, removedIndex) : prev);
3244
+ }, [items, removeItem]);
2811
3245
  useLayoutEffect(() => {
2812
- setActiveId((prev) => prev !== null && prev !== void 0 ? prev : firstEnabledId$1(items.current));
3246
+ setActiveId((prev) => prev !== null && prev !== void 0 ? prev : firstEnabledId(items.current));
2813
3247
  }, [activeId]);
3248
+ const contextValue = useMemo(() => ({
3249
+ orientation,
3250
+ activeId,
3251
+ setActiveId,
3252
+ registerItem,
3253
+ unregisterItem,
3254
+ items
3255
+ }), [
3256
+ orientation,
3257
+ activeId,
3258
+ setActiveId,
3259
+ registerItem,
3260
+ unregisterItem,
3261
+ items
3262
+ ]);
2814
3263
  return /* @__PURE__ */ jsx(ToolbarContext.Provider, {
2815
- value: {
2816
- orientation,
2817
- activeId,
2818
- setActiveId,
2819
- registerItem,
2820
- unregisterItem,
2821
- items
2822
- },
3264
+ value: contextValue,
2823
3265
  children
2824
3266
  });
2825
3267
  }
@@ -2838,18 +3280,16 @@ function ToolbarContainer(_ref) {
2838
3280
  let nextId;
2839
3281
  if (e.key === nextKey) {
2840
3282
  e.preventDefault();
2841
- nextId = moveActiveId$1(items.current, activeId, 1);
3283
+ nextId = moveActiveId(items.current, activeId, 1);
2842
3284
  } else if (e.key === prevKey) {
2843
3285
  e.preventDefault();
2844
- nextId = moveActiveId$1(items.current, activeId, -1);
3286
+ nextId = moveActiveId(items.current, activeId, -1);
2845
3287
  } else if (e.key === "Home") {
2846
3288
  e.preventDefault();
2847
- nextId = firstEnabledId$1(items.current);
3289
+ nextId = firstEnabledId(items.current);
2848
3290
  } else if (e.key === "End") {
2849
- var _enabled$id, _enabled;
2850
3291
  e.preventDefault();
2851
- const enabled = items.current.filter((i) => !i.disabled);
2852
- nextId = (_enabled$id = (_enabled = enabled[enabled.length - 1]) === null || _enabled === void 0 ? void 0 : _enabled.id) !== null && _enabled$id !== void 0 ? _enabled$id : null;
3292
+ nextId = lastEnabledId(items.current);
2853
3293
  }
2854
3294
  if (nextId) {
2855
3295
  setActiveId(nextId);
@@ -2923,6 +3363,10 @@ const _excluded3$2 = [
2923
3363
  /**
2924
3364
  * From `start`, return the index of the first enabled item walking in `dir`
2925
3365
  * (+1/-1). Honors `loop`. Returns -1 if none.
3366
+ *
3367
+ * Composite navigates by INDEX (grid rows/columns, wrap and loop modes), which
3368
+ * is why it keeps this directional scan instead of the registry's id-based
3369
+ * `moveActiveId`.
2926
3370
  */
2927
3371
  function firstEnabledFrom(list, start, dir, loop) {
2928
3372
  const n = list.length;
@@ -2945,7 +3389,7 @@ function useCompositeContext() {
2945
3389
  /** Renders the `CompositeProvider` component. */
2946
3390
  function CompositeProvider({ children, focusLoop = false, focusWrap = false, orientation = "both", activeId: controlledActiveId, onActiveIdChange, setActiveId: setActiveIdDeprecated }) {
2947
3391
  const [internalActiveId, setInternalActiveId] = useState(null);
2948
- const items = useRef([]);
3392
+ const { items, registerItem: registerEntry, unregisterItem: removeItem } = useItemRegistry();
2949
3393
  const onActiveIdChangeCb = onActiveIdChange !== null && onActiveIdChange !== void 0 ? onActiveIdChange : setActiveIdDeprecated;
2950
3394
  const activeId = controlledActiveId !== void 0 ? controlledActiveId : internalActiveId;
2951
3395
  const activeIdRef = useRef(activeId);
@@ -2957,16 +3401,8 @@ function CompositeProvider({ children, focusLoop = false, focusWrap = false, ori
2957
3401
  }, [controlledActiveId, onActiveIdChangeCb]);
2958
3402
  const registerItem = useCallback((id, element, row = 0, col = 0, disabled = false) => {
2959
3403
  const list = items.current;
2960
- const existing = list.findIndex((i) => i.id === id);
2961
- const activeBecameDisabled = existing >= 0 && id === activeIdRef.current && disabled;
2962
- if (existing >= 0) list[existing] = {
2963
- id,
2964
- element,
2965
- row,
2966
- col,
2967
- disabled
2968
- };
2969
- else list.push({
3404
+ const activeBecameDisabled = list.findIndex((i) => i.id === id) >= 0 && id === activeIdRef.current && disabled;
3405
+ registerEntry({
2970
3406
  id,
2971
3407
  element,
2972
3408
  row,
@@ -2987,29 +3423,48 @@ function CompositeProvider({ children, focusLoop = false, focusWrap = false, ori
2987
3423
  setInternalActiveId(null);
2988
3424
  } else setActiveId(nextId);
2989
3425
  }
2990
- }, [controlledActiveId, setActiveId]);
3426
+ }, [
3427
+ controlledActiveId,
3428
+ setActiveId,
3429
+ items,
3430
+ registerEntry
3431
+ ]);
2991
3432
  const unregisterItem = useCallback((id) => {
2992
- items.current = items.current.filter((i) => i.id !== id);
3433
+ const removedIndex = removeItem(id);
3434
+ if (removedIndex < 0) return;
2993
3435
  if (controlledActiveId === void 0) setInternalActiveId((prev) => {
2994
3436
  if (prev !== id) return prev;
2995
- const idx = firstEnabledFrom(items.current, 0, 1, false);
2996
- const nextId = idx >= 0 ? items.current[idx].id : null;
3437
+ const nextId = nearestEnabledId(items.current, removedIndex);
2997
3438
  activeIdRef.current = nextId;
2998
3439
  if (nextId === null) seededRef.current = false;
2999
3440
  return nextId;
3000
3441
  });
3001
- }, [controlledActiveId]);
3442
+ }, [
3443
+ controlledActiveId,
3444
+ items,
3445
+ removeItem
3446
+ ]);
3447
+ const contextValue = useMemo(() => ({
3448
+ activeId,
3449
+ setActiveId,
3450
+ registerItem,
3451
+ unregisterItem,
3452
+ items,
3453
+ focusLoop,
3454
+ focusWrap,
3455
+ orientation
3456
+ }), [
3457
+ activeId,
3458
+ setActiveId,
3459
+ registerItem,
3460
+ unregisterItem,
3461
+ items,
3462
+ focusLoop,
3463
+ focusWrap,
3464
+ orientation
3465
+ ]);
3002
3466
  return /* @__PURE__ */ jsx(CompositeContext.Provider, {
3003
- value: {
3004
- activeId,
3005
- setActiveId,
3006
- registerItem,
3007
- unregisterItem,
3008
- items,
3009
- focusLoop,
3010
- focusWrap,
3011
- orientation
3012
- },
3467
+ value: contextValue,
3013
3468
  children
3014
3469
  });
3015
3470
  }
@@ -3176,17 +3631,6 @@ const _excluded2$2 = [
3176
3631
  "onClick",
3177
3632
  "onFocus"
3178
3633
  ];
3179
- function firstEnabledId(items) {
3180
- var _items$find$id, _items$find;
3181
- return (_items$find$id = (_items$find = items.find((i) => !i.disabled)) === null || _items$find === void 0 ? void 0 : _items$find.id) !== null && _items$find$id !== void 0 ? _items$find$id : null;
3182
- }
3183
- function moveActiveId(items, activeId, delta) {
3184
- const enabled = items.filter((i) => !i.disabled);
3185
- if (enabled.length === 0) return null;
3186
- const idx = enabled.findIndex((i) => i.id === activeId);
3187
- if (idx < 0) return delta > 0 ? enabled[0].id : enabled[enabled.length - 1].id;
3188
- return enabled[Math.min(enabled.length - 1, Math.max(0, idx + delta))].id;
3189
- }
3190
3634
  const ToggleGroupContext = createContext(null);
3191
3635
  /**
3192
3636
  * Groups related `Toggle`s with roving-tabindex keyboard navigation and shared
@@ -3214,17 +3658,11 @@ function ToggleGroup(_ref) {
3214
3658
  setValue
3215
3659
  ]);
3216
3660
  const [activeId, setActiveId] = useState(null);
3217
- const items = useRef([]);
3218
- const registerItem = useCallback((entry) => {
3219
- const list = items.current;
3220
- const existing = list.findIndex((i) => i.id === entry.id);
3221
- if (existing >= 0) list[existing] = entry;
3222
- else list.push(entry);
3223
- }, []);
3661
+ const { items, registerItem, unregisterItem: removeItem } = useItemRegistry();
3224
3662
  const unregisterItem = useCallback((id) => {
3225
- items.current = items.current.filter((i) => i.id !== id);
3226
- setActiveId((prev) => prev === id ? firstEnabledId(items.current) : prev);
3227
- }, []);
3663
+ const removedIndex = removeItem(id);
3664
+ setActiveId((prev) => prev === id ? nearestEnabledId(items.current, removedIndex) : prev);
3665
+ }, [items, removeItem]);
3228
3666
  useLayoutEffect(() => {
3229
3667
  setActiveId((prev) => prev !== null && prev !== void 0 ? prev : firstEnabledId(items.current));
3230
3668
  }, [activeId]);
@@ -3247,10 +3685,8 @@ function ToggleGroup(_ref) {
3247
3685
  e.preventDefault();
3248
3686
  nextId = firstEnabledId(items.current);
3249
3687
  } else if (e.key === "End") {
3250
- var _enabled$id, _enabled;
3251
3688
  e.preventDefault();
3252
- const enabled = items.current.filter((i) => !i.disabled);
3253
- nextId = (_enabled$id = (_enabled = enabled[enabled.length - 1]) === null || _enabled === void 0 ? void 0 : _enabled.id) !== null && _enabled$id !== void 0 ? _enabled$id : null;
3689
+ nextId = lastEnabledId(items.current);
3254
3690
  }
3255
3691
  if (nextId) {
3256
3692
  setActiveId(nextId);
@@ -3263,18 +3699,28 @@ function ToggleGroup(_ref) {
3263
3699
  focusActive,
3264
3700
  onKeyDown
3265
3701
  ]);
3702
+ const contextValue = useMemo(() => ({
3703
+ isPressed,
3704
+ toggle,
3705
+ disabled,
3706
+ orientation,
3707
+ activeId,
3708
+ setActiveId,
3709
+ registerItem,
3710
+ unregisterItem,
3711
+ items
3712
+ }), [
3713
+ isPressed,
3714
+ toggle,
3715
+ disabled,
3716
+ orientation,
3717
+ activeId,
3718
+ registerItem,
3719
+ unregisterItem,
3720
+ items
3721
+ ]);
3266
3722
  return /* @__PURE__ */ jsx(ToggleGroupContext.Provider, {
3267
- value: {
3268
- isPressed,
3269
- toggle,
3270
- disabled,
3271
- orientation,
3272
- activeId,
3273
- setActiveId,
3274
- registerItem,
3275
- unregisterItem,
3276
- items
3277
- },
3723
+ value: contextValue,
3278
3724
  children: /* @__PURE__ */ jsx("div", _objectSpread2({
3279
3725
  role: "group",
3280
3726
  onKeyDown: handleKeyDown
@@ -3294,17 +3740,19 @@ function Toggle(_ref2) {
3294
3740
  const [standalonePressed, setStandalonePressed] = useControllableState(defaultPressed, pressedProp, onPressedChange);
3295
3741
  const inGroup = group !== null;
3296
3742
  const isDisabled = disabled || inGroup && group.disabled || false;
3743
+ const registerItem = group === null || group === void 0 ? void 0 : group.registerItem;
3744
+ const unregisterItem = group === null || group === void 0 ? void 0 : group.unregisterItem;
3297
3745
  useLayoutEffect(() => {
3298
- if (!inGroup) return void 0;
3299
- group.registerItem({
3746
+ if (!registerItem || !unregisterItem) return void 0;
3747
+ registerItem({
3300
3748
  id: itemId,
3301
3749
  element: buttonRef.current,
3302
3750
  disabled: isDisabled
3303
3751
  });
3304
- return () => group.unregisterItem(itemId);
3752
+ return () => unregisterItem(itemId);
3305
3753
  }, [
3306
- inGroup,
3307
- group,
3754
+ registerItem,
3755
+ unregisterItem,
3308
3756
  itemId,
3309
3757
  isDisabled
3310
3758
  ]);
@@ -3532,7 +3980,7 @@ const Meter = forwardRef(function Meter(_ref, ref) {
3532
3980
  //#region src/primitives/accordion.tsx
3533
3981
  const _excluded$1 = ["value", "disabled"];
3534
3982
  const _excluded2$1 = ["onClick", "disabled"];
3535
- const _excluded3$1 = ["style"];
3983
+ const _excluded3$1 = ["hiddenUntilFound", "style"];
3536
3984
  const AccordionRootContext = createContext(null);
3537
3985
  function useAccordionRoot() {
3538
3986
  const ctx = useContext(AccordionRootContext);
@@ -3619,17 +4067,31 @@ function AccordionTrigger(_ref2) {
3619
4067
  }
3620
4068
  /** Renders the `AccordionContent` component. */
3621
4069
  function AccordionContent(_ref3) {
3622
- let { style } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$1);
4070
+ let { hiddenUntilFound = false, style } = _ref3, props = _objectWithoutProperties(_ref3, _excluded3$1);
3623
4071
  const root = useAccordionRoot();
3624
4072
  const item = useAccordionItem();
3625
- const { ref, mounted, dataAttributes } = useEnterLeave(root.isExpanded(item.value), { animated: root.animated });
4073
+ const expanded = root.isExpanded(item.value);
4074
+ const { ref, mounted, dataAttributes } = useEnterLeave(expanded, { animated: root.animated && !hiddenUntilFound });
3626
4075
  const innerRef = useRef(null);
3627
- useEffect(() => {
3628
- if (innerRef.current) ref.current = innerRef.current;
3629
- }, [ref]);
4076
+ const contentRef = useMergedRef(innerRef, ref);
4077
+ useHiddenUntilFound(innerRef, {
4078
+ enabled: hiddenUntilFound,
4079
+ open: expanded,
4080
+ onReveal: useCallback(() => {
4081
+ if (!root.isExpanded(item.value)) root.toggle(item.value);
4082
+ }, [root, item.value])
4083
+ });
4084
+ if (hiddenUntilFound) return /* @__PURE__ */ jsx("div", _objectSpread2({
4085
+ ref: contentRef,
4086
+ id: item.contentId,
4087
+ role: "region",
4088
+ "aria-labelledby": item.triggerId,
4089
+ "data-state": expanded ? "open" : "closed",
4090
+ style
4091
+ }, props));
3630
4092
  if (!mounted) return null;
3631
4093
  return /* @__PURE__ */ jsx("div", _objectSpread2(_objectSpread2({
3632
- ref: innerRef,
4094
+ ref: contentRef,
3633
4095
  id: item.contentId,
3634
4096
  role: "region",
3635
4097
  "aria-labelledby": item.triggerId,
@@ -3981,18 +4443,21 @@ const _excluded4 = [
3981
4443
  "name",
3982
4444
  "id",
3983
4445
  "onChange",
4446
+ "onBlur",
3984
4447
  "aria-describedby"
3985
4448
  ];
3986
4449
  const _excluded5 = [
3987
4450
  "name",
3988
4451
  "id",
3989
4452
  "onChange",
4453
+ "onBlur",
3990
4454
  "aria-describedby"
3991
4455
  ];
3992
4456
  const _excluded6 = [
3993
4457
  "name",
3994
4458
  "id",
3995
4459
  "onChange",
4460
+ "onBlur",
3996
4461
  "aria-describedby",
3997
4462
  "children"
3998
4463
  ];
@@ -4103,7 +4568,7 @@ function FormLabel(_ref3) {
4103
4568
  }
4104
4569
  const FormInput = forwardRef((_ref4, ref) => {
4105
4570
  var _useFieldValueMaybe;
4106
- let { name, id, onChange, "aria-describedby": ariaDescribedBy } = _ref4, props = _objectWithoutProperties(_ref4, _excluded4);
4571
+ let { name, id, onChange, onBlur, "aria-describedby": ariaDescribedBy } = _ref4, props = _objectWithoutProperties(_ref4, _excluded4);
4107
4572
  const form = useFormContext();
4108
4573
  const field = useFormField();
4109
4574
  const value = (_useFieldValueMaybe = useFieldValueMaybe(form, name)) !== null && _useFieldValueMaybe !== void 0 ? _useFieldValueMaybe : "";
@@ -4122,13 +4587,21 @@ const FormInput = forwardRef((_ref4, ref) => {
4122
4587
  name,
4123
4588
  onChange
4124
4589
  ]);
4590
+ const handleBlur = useCallback((e) => {
4591
+ form === null || form === void 0 || form.setFieldTouched(name, true);
4592
+ onBlur === null || onBlur === void 0 || onBlur(e);
4593
+ }, [
4594
+ form,
4595
+ name,
4596
+ onBlur
4597
+ ]);
4125
4598
  return /* @__PURE__ */ jsx("input", _objectSpread2({
4126
4599
  ref: setRef,
4127
4600
  id: id !== null && id !== void 0 ? id : field === null || field === void 0 ? void 0 : field.inputId,
4128
4601
  name,
4129
4602
  value,
4130
4603
  onChange: handleChange,
4131
- onBlur: () => form === null || form === void 0 ? void 0 : form.setFieldTouched(name, true),
4604
+ onBlur: handleBlur,
4132
4605
  "aria-invalid": hasError || void 0,
4133
4606
  "aria-describedby": composeDescribedBy(field, hasError, ariaDescribedBy)
4134
4607
  }, props));
@@ -4136,7 +4609,7 @@ const FormInput = forwardRef((_ref4, ref) => {
4136
4609
  FormInput.displayName = "FormInput";
4137
4610
  const FormTextarea = forwardRef((_ref5, ref) => {
4138
4611
  var _useFieldValueMaybe2;
4139
- let { name, id, onChange, "aria-describedby": ariaDescribedBy } = _ref5, props = _objectWithoutProperties(_ref5, _excluded5);
4612
+ let { name, id, onChange, onBlur, "aria-describedby": ariaDescribedBy } = _ref5, props = _objectWithoutProperties(_ref5, _excluded5);
4140
4613
  const form = useFormContext();
4141
4614
  const field = useFormField();
4142
4615
  const value = (_useFieldValueMaybe2 = useFieldValueMaybe(form, name)) !== null && _useFieldValueMaybe2 !== void 0 ? _useFieldValueMaybe2 : "";
@@ -4152,13 +4625,21 @@ const FormTextarea = forwardRef((_ref5, ref) => {
4152
4625
  name,
4153
4626
  onChange
4154
4627
  ]);
4628
+ const handleBlur = useCallback((e) => {
4629
+ form === null || form === void 0 || form.setFieldTouched(name, true);
4630
+ onBlur === null || onBlur === void 0 || onBlur(e);
4631
+ }, [
4632
+ form,
4633
+ name,
4634
+ onBlur
4635
+ ]);
4155
4636
  return /* @__PURE__ */ jsx("textarea", _objectSpread2({
4156
4637
  ref: setRef,
4157
4638
  id: id !== null && id !== void 0 ? id : field === null || field === void 0 ? void 0 : field.inputId,
4158
4639
  name,
4159
4640
  value,
4160
4641
  onChange: handleChange,
4161
- onBlur: () => form === null || form === void 0 ? void 0 : form.setFieldTouched(name, true),
4642
+ onBlur: handleBlur,
4162
4643
  "aria-invalid": hasError || void 0,
4163
4644
  "aria-describedby": composeDescribedBy(field, hasError, ariaDescribedBy)
4164
4645
  }, props));
@@ -4166,7 +4647,7 @@ const FormTextarea = forwardRef((_ref5, ref) => {
4166
4647
  FormTextarea.displayName = "FormTextarea";
4167
4648
  const FormSelect = forwardRef((_ref6, ref) => {
4168
4649
  var _useFieldValueMaybe3;
4169
- let { name, id, onChange, "aria-describedby": ariaDescribedBy, children } = _ref6, props = _objectWithoutProperties(_ref6, _excluded6);
4650
+ let { name, id, onChange, onBlur, "aria-describedby": ariaDescribedBy, children } = _ref6, props = _objectWithoutProperties(_ref6, _excluded6);
4170
4651
  const form = useFormContext();
4171
4652
  const field = useFormField();
4172
4653
  const value = (_useFieldValueMaybe3 = useFieldValueMaybe(form, name)) !== null && _useFieldValueMaybe3 !== void 0 ? _useFieldValueMaybe3 : "";
@@ -4182,13 +4663,21 @@ const FormSelect = forwardRef((_ref6, ref) => {
4182
4663
  name,
4183
4664
  onChange
4184
4665
  ]);
4666
+ const handleBlur = useCallback((e) => {
4667
+ form === null || form === void 0 || form.setFieldTouched(name, true);
4668
+ onBlur === null || onBlur === void 0 || onBlur(e);
4669
+ }, [
4670
+ form,
4671
+ name,
4672
+ onBlur
4673
+ ]);
4185
4674
  return /* @__PURE__ */ jsx("select", _objectSpread2(_objectSpread2({
4186
4675
  ref: setRef,
4187
4676
  id: id !== null && id !== void 0 ? id : field === null || field === void 0 ? void 0 : field.inputId,
4188
4677
  name,
4189
4678
  value,
4190
4679
  onChange: handleChange,
4191
- onBlur: () => form === null || form === void 0 ? void 0 : form.setFieldTouched(name, true),
4680
+ onBlur: handleBlur,
4192
4681
  "aria-invalid": hasError || void 0,
4193
4682
  "aria-describedby": composeDescribedBy(field, hasError, ariaDescribedBy)
4194
4683
  }, props), {}, { children }));
@@ -4396,4 +4885,4 @@ function FormGroupLabel(props) {
4396
4885
  return /* @__PURE__ */ jsx("legend", _objectSpread2({}, props));
4397
4886
  }
4398
4887
  //#endregion
4399
- export { AccordionContent, AccordionItem, AccordionRoot, AccordionTrigger, AlertDialogPanel, Button, Checkbox, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxItem, ComboboxPopover, ComboboxRoot, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandRoot, CommandSeparator, Composite, CompositeItem, CompositeProvider, CompositeRow, DialogDescription, DialogDisclosure, DialogDismiss, DialogHeading, DialogPanel, DialogRoot, DisclosureContent, DisclosureRoot, DisclosureTrigger, FormCheckbox, FormControl, FormDescription, FormError, FormField, FormGroup, FormGroupLabel, FormInput, FormLabel, FormProvider, FormPush, FormRadio, FormRadioGroup, FormRemove, FormReset, FormRoot, FormSelect, FormSubmit, FormSwitch, FormTextarea, MenuButtonArrow, MenuItem, MenuItemCheckbox, MenuItemRadio, MenuPopover, MenuRoot, MenuSeparator, MenuTrigger, MenubarContainer, MenubarRoot, Meter, PopoverClose, PopoverContent, PopoverRoot, PopoverTrigger, Progress, Radio, RadioGroupRoot, SelectItem, SelectLabel, SelectPopover, SelectRoot, SelectTrigger, Separator, Switch, Tab, TabList, TabPanel, TabsRoot, Toggle, ToggleGroup, ToolbarButton, ToolbarContainer, ToolbarRoot, ToolbarSeparator, Tooltip, TooltipAnchor, TooltipProvider, useCommandState, useControllableState, useDialogClose, useEnterLeave, useFieldValue, useFloating, useFocusVisible, useFocusableWhenDisabled, useFormContext, useFormStore, useId, useMenubarContext, useRadioGroupContext, useRovingTabindex, useScrollActiveDescendantIntoView };
4888
+ export { AccordionContent, AccordionItem, AccordionRoot, AccordionTrigger, AlertDialogPanel, Button, Checkbox, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxItem, ComboboxPopover, ComboboxRoot, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandRoot, CommandSeparator, Composite, CompositeItem, CompositeProvider, CompositeRow, DialogDescription, DialogDisclosure, DialogDismiss, DialogHeading, DialogPanel, DialogRoot, DisclosureContent, DisclosureRoot, DisclosureTrigger, FormCheckbox, FormControl, FormDescription, FormError, FormField, FormGroup, FormGroupLabel, FormInput, FormLabel, FormProvider, FormPush, FormRadio, FormRadioGroup, FormRemove, FormReset, FormRoot, FormSelect, FormSubmit, FormSwitch, FormTextarea, MenuButtonArrow, MenuItem, MenuItemCheckbox, MenuItemRadio, MenuPopover, MenuRoot, MenuSeparator, MenuTrigger, MenubarContainer, MenubarRoot, Meter, PopoverClose, PopoverContent, PopoverRoot, PopoverTrigger, Progress, Radio, RadioGroupRoot, SelectItem, SelectLabel, SelectPopover, SelectRoot, SelectTrigger, Separator, Switch, Tab, TabList, TabPanel, TabsRoot, Toggle, ToggleGroup, ToolbarButton, ToolbarContainer, ToolbarRoot, ToolbarSeparator, Tooltip, TooltipAnchor, TooltipGroup, TooltipProvider, TooltipRoot, useCommandState, useControllableState, useDialogClose, useEnterLeave, useFieldValue, useFloating, useFocusVisible, useFocusableWhenDisabled, useFormContext, useFormStore, useId, useMenubarContext, useRadioGroupContext, useRovingTabindex, useScrollActiveDescendantIntoView };