ropav 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/README.md +1 -1
  2. package/dist/ancestry.js +136 -0
  3. package/dist/calendar.css +173 -0
  4. package/dist/calendar.js +714 -0
  5. package/dist/calendar2.js +37 -0
  6. package/dist/chevron-left.js +12 -0
  7. package/dist/color-picker.js +38 -88
  8. package/dist/combobox.css +251 -0
  9. package/dist/combobox.js +410 -0
  10. package/dist/components/calendar/calendar.d.ts +30 -0
  11. package/dist/components/calendar/calendarBehavior.d.ts +49 -0
  12. package/dist/components/calendar/index.d.ts +3 -0
  13. package/dist/components/calendar/index.js +3 -0
  14. package/dist/components/calendar/types.d.ts +53 -0
  15. package/dist/components/calendar/useCalendar.d.ts +24 -0
  16. package/dist/components/color-picker/useColorPickerDrag.d.ts +1 -1
  17. package/dist/components/color-picker/useColorPickerSaturation.d.ts +1 -1
  18. package/dist/components/color-picker/useColorPickerSlider.d.ts +1 -1
  19. package/dist/components/combobox/combobox.d.ts +29 -0
  20. package/dist/components/combobox/comboboxModel.d.ts +4 -0
  21. package/dist/components/combobox/index.d.ts +3 -0
  22. package/dist/components/combobox/index.js +2 -0
  23. package/dist/components/combobox/types.d.ts +43 -0
  24. package/dist/components/combobox/useCombobox.d.ts +45 -0
  25. package/dist/components/date-picker/date-picker.d.ts +33 -0
  26. package/dist/components/date-picker/datePickerModel.d.ts +10 -0
  27. package/dist/components/date-picker/index.d.ts +4 -0
  28. package/dist/components/date-picker/index.js +2 -0
  29. package/dist/components/date-picker/types.d.ts +57 -0
  30. package/dist/components/date-picker/useDatePicker.d.ts +29 -0
  31. package/dist/components/date-picker/useDatePickerValueState.d.ts +21 -0
  32. package/dist/components/dropdown-menu/dropdown-menu-content.d.ts +2 -2
  33. package/dist/components/dropdown-menu/dropdown-menu-sub-content.d.ts +2 -2
  34. package/dist/components/dropdown-menu/dropdown-menu.d.ts +2 -2
  35. package/dist/components/dropdown-menu/dropdownMenuContext.d.ts +7 -18
  36. package/dist/components/dropdown-menu/dropdownMenuInteraction.d.ts +3 -3
  37. package/dist/components/dropdown-menu/dropdownMenuInteractionTypes.d.ts +58 -29
  38. package/dist/components/dropdown-menu/useDropdownMenuDataController.d.ts +4 -3
  39. package/dist/components/dropdown-menu/useDropdownMenuDataRegistration.d.ts +6 -4
  40. package/dist/components/dropdown-menu/useDropdownMenuPresentation.d.ts +3 -4
  41. package/dist/components/dropdown-menu/useDropdownMenuTargetBindings.d.ts +5 -5
  42. package/dist/components/dropzone/dropzone.d.ts +31 -0
  43. package/dist/components/dropzone/dropzoneModel.d.ts +16 -0
  44. package/dist/components/dropzone/index.d.ts +3 -0
  45. package/dist/components/dropzone/index.js +2 -0
  46. package/dist/components/dropzone/types.d.ts +56 -0
  47. package/dist/components/dropzone/useDropzone.d.ts +25 -0
  48. package/dist/components/file-input/file-input.d.ts +29 -0
  49. package/dist/components/file-input/index.d.ts +3 -0
  50. package/dist/components/file-input/index.js +2 -0
  51. package/dist/components/file-input/types.d.ts +37 -0
  52. package/dist/components/file-input/useFileInput.d.ts +15 -0
  53. package/dist/components/floating/index.js +1 -1
  54. package/dist/components/floating/useFloatingPosition.d.ts +1 -13
  55. package/dist/components/floating/useFloatingTargetLifecycle.d.ts +32 -0
  56. package/dist/components/floating/useHoverDisclosure.d.ts +10 -0
  57. package/dist/components/hover-card/hover-card.d.ts +25 -0
  58. package/dist/components/hover-card/index.d.ts +3 -0
  59. package/dist/components/hover-card/index.js +2 -0
  60. package/dist/components/hover-card/types.d.ts +45 -0
  61. package/dist/components/hover-card/useHoverCard.d.ts +22 -0
  62. package/dist/components/hover-card/useHoverCardDisclosure.d.ts +19 -0
  63. package/dist/components/multi-select/index.d.ts +3 -0
  64. package/dist/components/multi-select/index.js +2 -0
  65. package/dist/components/multi-select/multi-select.d.ts +30 -0
  66. package/dist/components/multi-select/multiSelectModel.d.ts +3 -0
  67. package/dist/components/multi-select/types.d.ts +49 -0
  68. package/dist/components/multi-select/useMultiSelect.d.ts +46 -0
  69. package/dist/components/number-input/useNumberInput.d.ts +1 -3
  70. package/dist/components/popover/usePopoverBindings.d.ts +3 -6
  71. package/dist/components/scroll-area/useScrollArea.d.ts +4 -4
  72. package/dist/components/scroll-area/useScrollAreaAttributes.d.ts +3 -3
  73. package/dist/components/scroll-area/useScrollAreaPointer.d.ts +1 -1
  74. package/dist/components/segmented-control/index.d.ts +3 -0
  75. package/dist/components/segmented-control/index.js +2 -0
  76. package/dist/components/segmented-control/segmented-control.d.ts +26 -0
  77. package/dist/components/segmented-control/types.d.ts +47 -0
  78. package/dist/components/segmented-control/useSegmentedControl.d.ts +15 -0
  79. package/dist/components/select/useSelect.d.ts +31 -19
  80. package/dist/components/slider/useRangeSlider.d.ts +1 -1
  81. package/dist/components/slider/useRangeSliderPointer.d.ts +1 -1
  82. package/dist/components/tags-input/index.d.ts +3 -0
  83. package/dist/components/tags-input/index.js +2 -0
  84. package/dist/components/tags-input/tags-input.d.ts +27 -0
  85. package/dist/components/tags-input/tagsInputModel.d.ts +12 -0
  86. package/dist/components/tags-input/types.d.ts +37 -0
  87. package/dist/components/tags-input/useTagsInput.d.ts +23 -0
  88. package/dist/components/tooltip/useTooltipTargetBindings.d.ts +12 -0
  89. package/dist/date-picker.css +49 -0
  90. package/dist/date-picker.js +809 -0
  91. package/dist/dialog.js +9 -20
  92. package/dist/direction.js +6 -0
  93. package/dist/dropdown-menu.css +21 -3
  94. package/dist/dropdown-menu.js +341 -261
  95. package/dist/dropzone.css +135 -0
  96. package/dist/dropzone.js +431 -0
  97. package/dist/file-input.css +135 -0
  98. package/dist/file-input.js +198 -0
  99. package/dist/hover-card.css +80 -0
  100. package/dist/hover-card.js +402 -0
  101. package/dist/index.d.ts +9 -0
  102. package/dist/index.js +14 -3
  103. package/dist/internal/composables/useOverlayLayer.d.ts +12 -0
  104. package/dist/modal.js +1 -1
  105. package/dist/multi-select.css +316 -0
  106. package/dist/multi-select.js +481 -0
  107. package/dist/nativeChoice.js +169 -0
  108. package/dist/number-input.css +18 -18
  109. package/dist/number-input.js +36 -38
  110. package/dist/pagination.js +1 -10
  111. package/dist/pointerSession.js +130 -0
  112. package/dist/popover.css +21 -3
  113. package/dist/popover.js +37 -62
  114. package/dist/scroll-area.js +52 -62
  115. package/dist/segmented-control.css +176 -0
  116. package/dist/segmented-control.js +382 -0
  117. package/dist/select.css +50 -48
  118. package/dist/select.js +121 -271
  119. package/dist/slider.js +70 -129
  120. package/dist/tags-input.css +197 -0
  121. package/dist/tags-input.js +380 -0
  122. package/dist/tooltip.js +35 -30
  123. package/dist/useEditableOptionList.js +216 -0
  124. package/dist/useFlatOptionCollection.js +134 -0
  125. package/dist/useFloatingPosition.js +4 -160
  126. package/dist/useFloatingTargetLifecycle.js +127 -0
  127. package/dist/useFormControl.js +1 -0
  128. package/dist/{floating.js → useHoverDisclosure.js} +338 -421
  129. package/dist/useNativeChoiceTransaction.js +46 -0
  130. package/dist/useNativeFileSelection.js +86 -0
  131. package/dist/useOverlayZIndex.js +246 -15
  132. package/dist/utils/date.d.ts +13 -0
  133. package/dist/utils/dateAvailability.d.ts +7 -0
  134. package/dist/utils/dom/ancestry.d.ts +1 -0
  135. package/dist/utils/dom/direction.d.ts +2 -0
  136. package/dist/utils/dom/files.d.ts +3 -0
  137. package/dist/utils/dom/nativeChoice.d.ts +17 -0
  138. package/dist/utils/dom/pointerSession.d.ts +20 -0
  139. package/dist/utils/dom/scroll.d.ts +2 -1
  140. package/dist/utils/optionFilter.d.ts +5 -0
  141. package/docs/public-floating-api.md +7 -4
  142. package/docs/public-styles-api.md +25 -12
  143. package/package.json +39 -3
  144. package/src/styles/_mixins.scss +26 -0
  145. package/src/styles/styles-manifest.json +1 -2
  146. package/dist/components/floating/hoverDisclosureInteractionModel.d.ts +0 -42
  147. package/dist/components/floating/useHoverDisclosureInteractions.d.ts +0 -32
  148. package/dist/components/floating/useHoverDisclosureOpenState.d.ts +0 -25
  149. package/dist/components/floating/useHoverDisclosureTargetBinding.d.ts +0 -27
  150. package/dist/components/select/useSelectNativeControl.d.ts +0 -9
  151. package/dist/components/select/useSelectNativeValue.d.ts +0 -9
  152. package/dist/pointer.js +0 -9
  153. package/dist/utils/dom/pointer.d.ts +0 -2
@@ -3,17 +3,16 @@ import { t as useControllableValue } from "./useControllableValue.js";
3
3
  import { t as useStylesApi } from "./styles-api.js";
4
4
  import { n as toPresenceAttribute, t as toOptionalAttribute } from "./attributes.js";
5
5
  import { t as bem } from "./bem.js";
6
- import { t as isElement } from "./query.js";
7
6
  import { t as useRequiredInject } from "./useRequiredInject.js";
8
7
  import { t as clamp } from "./number.js";
9
- import { a as snapshotAttributes, i as restoreAttributes } from "./attributes2.js";
8
+ import { t as chevron_right_default } from "./chevron-right.js";
10
9
  import { t as resolveHTMLElementRef } from "./componentRef.js";
11
- import { n as isEventWithinElement, r as isEventWithinTargets, t as createCancelableCustomEvent } from "./events.js";
10
+ import { r as isEventWithinTargets, t as createCancelableCustomEvent } from "./events.js";
12
11
  import { n as useOverlayZIndex, r as useOverlayLayer } from "./useOverlayZIndex.js";
13
- import { n as useFloatingTarget, t as useFloatingPosition } from "./useFloatingPosition.js";
12
+ import { t as useFloatingPosition } from "./useFloatingPosition.js";
13
+ import { t as useFloatingTargetLifecycle } from "./useFloatingTargetLifecycle.js";
14
14
  import { n as useTeleportTarget, t as provideTeleportTarget } from "./useTeleportTarget.js";
15
15
  import { t as useTypeahead } from "./useTypeahead.js";
16
- import { t as chevron_right_default } from "./chevron-right.js";
17
16
  import { VaporTeleport, VaporTransition, child, computed, createComponent, createComponentWithFallback, createDynamicComponent, createFor, createIf, createSlot, createTemplateRefSetter, defineVaporComponent, extend, isRef, mergeProps, nextTick, onBeforeUnmount, onMounted, onUnmounted, provide, ref, renderEffect, resolveComponent, setAttr, setDynamicProps, setInsertionState, setStyle, setTemplateRefBinding, setText, shallowRef, template, toDisplayString, txt, unref, useAttrs, useId, watch } from "vue";
18
17
  //#region src/utils/geometry.ts
19
18
  function getClientPoint(event) {
@@ -75,29 +74,25 @@ function createSelectEvent(originalEvent, value) {
75
74
  }, originalEvent);
76
75
  }
77
76
  function createMenuContext(options) {
78
- const id = options.parentSub?.menuId ?? options.root.interaction.rootMenuId;
79
- const interaction = options.root.interaction;
80
- const activeId = interaction.getActiveId(id);
81
- const cleanupMenu = interaction.registerMenu({
82
- id,
83
- parentItemId: () => options.parentSub?.trigger.value?.id,
77
+ const id = options.parentSub?.menuId ?? options.root.interactionRootMenuId;
78
+ const interaction = options.parentSub ? options.parentSub.interaction.connectContent({
79
+ element: () => options.element.value,
80
+ placement: () => options.actualPlacement.value,
81
+ stopKeyPropagation: true,
82
+ onEscape: options.onEscape
83
+ }) : options.root.interaction.connectRootMenu({
84
84
  element: () => options.element.value,
85
85
  placement: () => options.actualPlacement.value,
86
- isOpen: () => options.parentSub?.isOpen.value ?? options.root.isOpen.value,
87
- setOpen: options.parentSub?.setOpen,
88
86
  stopKeyPropagation: true,
89
87
  onEscape: options.onEscape
90
88
  });
91
- function registerItem(item) {
92
- interaction.registerItem({
93
- get id() {
94
- return item.id;
95
- },
96
- menuId: id,
89
+ function connectItem(item) {
90
+ return interaction.connectItem({
91
+ id: item.id,
97
92
  element: item.element,
98
93
  textValue: item.textValue,
99
94
  disabled: item.disabled,
100
- submenuId: () => item.submenu?.menuId,
95
+ submenu: item.submenu?.interaction,
101
96
  submenuDirection: () => item.submenu?.actualPlacement.value.startsWith("left") ? "left" : "right",
102
97
  select: item.activate,
103
98
  closeOnSelect: item.closeOnSelect
@@ -107,23 +102,12 @@ function createMenuContext(options) {
107
102
  root: options.root,
108
103
  id,
109
104
  element: options.element,
110
- activeId,
111
105
  actualPlacement: options.actualPlacement,
112
106
  parentSub: options.parentSub,
113
- registerItem,
114
- unregisterItem: interaction.unregisterItem,
115
- closeSubmenus: (except) => interaction.closeSubmenus(id, except?.menuId),
116
- setActive: (itemId) => {
117
- interaction.setActive(itemId);
118
- },
119
- isActive: interaction.isActive,
120
- focus: (target = "first") => interaction.focusMenu(id, target),
121
- focusElement: () => interaction.focusMenuElement(id),
122
- activate: interaction.activateItem,
123
- hover: interaction.hoverItem,
124
- onKeydown: (event) => interaction.onMenuKeydown(id, event)
107
+ interaction,
108
+ connectItem
125
109
  };
126
- onBeforeUnmount(cleanupMenu);
110
+ onBeforeUnmount(interaction.dispose);
127
111
  return context;
128
112
  }
129
113
  function usePrimitiveItem(componentName, props, emitSelect, options) {
@@ -132,7 +116,6 @@ function usePrimitiveItem(componentName, props, emitSelect, options) {
132
116
  const id = computed(() => props.id ?? `${generatedId}-item`);
133
117
  const element = ref(null);
134
118
  const isDisabled = computed(() => Boolean(menu.root.disabled.value || props.disabled));
135
- const focused = computed(() => menu.isActive(id.value));
136
119
  function emitSelection(originalEvent) {
137
120
  if (isDisabled.value) return void 0;
138
121
  const selectEvent = createSelectEvent(originalEvent, props.value);
@@ -150,30 +133,32 @@ function usePrimitiveItem(componentName, props, emitSelect, options) {
150
133
  activate: emitSelection,
151
134
  closeOnSelect: () => props.closeOnSelect ?? options.defaultCloseOnSelect
152
135
  };
153
- menu.registerItem(registration);
154
- watch(id, (nextId, previousId) => {
155
- const wasActive = menu.activeId.value === previousId;
156
- menu.unregisterItem(previousId);
157
- menu.registerItem(registration);
158
- if (wasActive) menu.setActive(nextId);
136
+ let itemInteraction = menu.connectItem(registration);
137
+ const focused = computed(() => {
138
+ return id.value === registration.id && itemInteraction.active.value;
139
+ });
140
+ watch(id, () => {
141
+ const previous = itemInteraction;
142
+ const wasActive = previous.active.value;
143
+ itemInteraction = menu.connectItem(registration);
144
+ previous.dispose();
145
+ if (wasActive) itemInteraction.setActive();
159
146
  }, { flush: "sync" });
160
- watch(isDisabled, () => menu.root.interaction.reconcile(menu.id));
161
- onBeforeUnmount(() => menu.unregisterItem(id.value));
147
+ onBeforeUnmount(() => itemInteraction.dispose());
162
148
  function setElement(value) {
163
149
  resolveHTMLElementRef(value, id.value, (resolved) => {
164
150
  element.value = resolved;
165
- menu.root.interaction.reconcile(menu.id);
166
151
  });
167
152
  }
168
153
  function activate(originalEvent) {
169
- menu.activate(id.value, originalEvent);
154
+ itemInteraction.activate(originalEvent);
170
155
  }
171
156
  function onPointerenter() {
172
157
  if (isDisabled.value) return;
173
- menu.hover(id.value);
158
+ itemInteraction.hover();
174
159
  }
175
160
  function select() {
176
- menu.activate(id.value, new Event("select"));
161
+ itemInteraction.activate(new Event("select"));
177
162
  }
178
163
  return {
179
164
  menu,
@@ -304,7 +289,7 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
304
289
  return !customEvent.defaultPrevented;
305
290
  }
306
291
  });
307
- const cleanupDismissal = submenu ? void 0 : root.interaction.registerDismissal({
292
+ const cleanupDismissal = submenu ? void 0 : root.interaction.connectDismissal({
308
293
  ignoredTargets: () => props.ignore ?? [],
309
294
  pointerDownOutside: events.pointerDownOutside,
310
295
  focusOutside: events.focusOutside,
@@ -329,29 +314,24 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
329
314
  }, { immediate: true });
330
315
  const shouldRender = computed(() => props.forceMount === true || isOpen.value);
331
316
  let layerBranchCleanup;
317
+ let disconnectInside;
332
318
  provide(menuKey, menu);
333
319
  if (sub) sub.menu = menu;
334
320
  function setElement(value) {
335
321
  resolveHTMLElementRef(value, id.value, (resolved) => {
336
- const previous = element.value;
337
- if (previous) root.unregisterInside(previous);
322
+ disconnectInside?.();
323
+ disconnectInside = void 0;
338
324
  layerBranchCleanup?.();
339
325
  layerBranchCleanup = void 0;
340
326
  element.value = resolved;
341
- if (resolved) root.registerInside(resolved);
327
+ if (resolved) disconnectInside = root.connectInside(resolved);
342
328
  if (!sub) root.layer.element.value = resolved;
343
329
  else if (resolved) layerBranchCleanup = root.layer.registerBranch(resolved);
344
330
  });
345
331
  }
346
332
  watch(isOpen, (open) => {
347
- if (!open) {
348
- if (sub) root.interaction.closeMenu(menu.id, false);
349
- return;
350
- }
351
- nextTick(() => {
352
- const focus = sub ? sub.pendingFocus.value : root.pendingFocus.value;
353
- menu.focus(focus || "first");
354
- });
333
+ if (!open) return;
334
+ nextTick(menu.interaction.focusPending);
355
335
  }, {
356
336
  flush: "post",
357
337
  immediate: true
@@ -359,12 +339,12 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
359
339
  onMounted(() => {
360
340
  if (sub) sub.contentId.value = id.value;
361
341
  else root.contentId.value = id.value;
362
- if (isOpen.value) menu.focus((sub ? sub.pendingFocus.value : root.pendingFocus.value) || "first");
342
+ if (isOpen.value) menu.interaction.focusPending();
363
343
  });
364
344
  onBeforeUnmount(() => {
365
345
  cleanupDismissal?.();
346
+ disconnectInside?.();
366
347
  layerBranchCleanup?.();
367
- if (element.value) root.unregisterInside(element.value);
368
348
  if (!sub && root.layer.element.value === element.value) root.layer.element.value = null;
369
349
  if (sub) sub.menu = null;
370
350
  else if (root.contentId.value === id.value) root.contentId.value = void 0;
@@ -391,12 +371,12 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
391
371
  "aria-label": props.ariaLabel,
392
372
  "aria-labelledby": props.ariaLabelledby ?? (submenu ? void 0 : root.triggerId.value),
393
373
  "aria-describedby": props.ariaDescribedby,
394
- "aria-activedescendant": menu.activeId.value ?? void 0,
374
+ "aria-activedescendant": menu.interaction.activeId.value ?? void 0,
395
375
  "data-state": isOpen.value ? "open" : "closed",
396
376
  "data-placement": floating.actualPlacement.value,
397
377
  "data-side": placementSide.value,
398
378
  "data-align": placementAlign.value,
399
- onKeydown: menu.onKeydown
379
+ onKeydown: menu.interaction.onKeydown
400
380
  }));
401
381
  function close() {
402
382
  if (sub) sub.close(true);
@@ -507,12 +487,13 @@ function useDropdownMenuContextTrigger(props) {
507
487
  let startPoint;
508
488
  let longPressOpened = false;
509
489
  let suppressUntil = 0;
490
+ let disconnectInside;
510
491
  function setElement(value) {
511
492
  resolveHTMLElementRef(value, id.value, (resolved) => {
512
- const previous = element.value;
513
- if (previous) root.unregisterInside(previous);
493
+ disconnectInside?.();
494
+ disconnectInside = void 0;
514
495
  element.value = resolved;
515
- if (resolved) root.registerInside(resolved);
496
+ if (resolved) disconnectInside = root.connectInside(resolved);
516
497
  if (!root.triggerId.value) root.triggerId.value = id.value;
517
498
  });
518
499
  }
@@ -595,7 +576,7 @@ function useDropdownMenuContextTrigger(props) {
595
576
  }));
596
577
  onBeforeUnmount(() => {
597
578
  clearLongPress();
598
- if (element.value) root.unregisterInside(element.value);
579
+ disconnectInside?.();
599
580
  if (root.triggerId.value === id.value) root.triggerId.value = void 0;
600
581
  });
601
582
  return {
@@ -1216,28 +1197,26 @@ function createDropdownMenuInteractionRegistry(host, navigation) {
1216
1197
  function createOutsideEvent(originalEvent) {
1217
1198
  return createCancelableCustomEvent("dropdown-menu-interact-outside", { originalEvent }, originalEvent);
1218
1199
  }
1219
- function blockDocumentClick(event) {
1220
- if (event.cancelable) event.preventDefault();
1221
- event.stopPropagation();
1222
- document.removeEventListener("click", blockDocumentClick, true);
1223
- }
1224
- function blockNextDocumentClick() {
1225
- if (typeof document === "undefined") return;
1200
+ function blockNextDocumentClick(document) {
1201
+ function blockDocumentClick(event) {
1202
+ if (event.cancelable) event.preventDefault();
1203
+ event.stopPropagation();
1204
+ document.removeEventListener("click", blockDocumentClick, true);
1205
+ }
1226
1206
  document.addEventListener("click", blockDocumentClick, true);
1227
- window.setTimeout(() => document.removeEventListener("click", blockDocumentClick, true), 1e3);
1207
+ document.defaultView?.setTimeout(() => document.removeEventListener("click", blockDocumentClick, true), 1e3);
1228
1208
  }
1229
1209
  function useDropdownMenuInteractionDismissal({ host, registry, closeRoot }) {
1230
1210
  const inside = /* @__PURE__ */ new Set();
1231
1211
  let dismissal;
1232
- let isListening = false;
1212
+ let layerInteraction;
1233
1213
  function registerInside(element) {
1234
1214
  inside.add(element);
1215
+ layerInteraction?.refresh();
1235
1216
  }
1236
1217
  function unregisterInside(element) {
1237
1218
  inside.delete(element);
1238
- }
1239
- function isInside(event) {
1240
- return [...inside].some((element) => isEventWithinElement(event, element));
1219
+ layerInteraction?.refresh();
1241
1220
  }
1242
1221
  function emitOutside(type, originalEvent) {
1243
1222
  const outsideEvent = createOutsideEvent(originalEvent);
@@ -1250,43 +1229,40 @@ function useDropdownMenuInteractionDismissal({ host, registry, closeRoot }) {
1250
1229
  if (!host.modal.value) return;
1251
1230
  if (event.cancelable) event.preventDefault();
1252
1231
  event.stopPropagation();
1253
- if (event.type === "pointerdown") blockNextDocumentClick();
1232
+ const listenerDocument = event.currentTarget;
1233
+ if (event.type === "pointerdown" && listenerDocument?.nodeType === 9) blockNextDocumentClick(listenerDocument);
1254
1234
  }
1255
1235
  function shouldIgnoreOutside(event) {
1256
- const ignoredTargets = (dismissal?.ignoredTargets() ?? []).map((target) => isRef(target) ? target.value : target);
1257
- return isInside(event) || isEventWithinTargets(event, ignoredTargets);
1236
+ return isEventWithinTargets(event, (dismissal?.ignoredTargets() ?? []).map((target) => isRef(target) ? target.value : target));
1258
1237
  }
1259
1238
  function onDocumentPointer(event) {
1260
- if (!host.isTopLayer() || shouldIgnoreOutside(event)) return;
1239
+ if (shouldIgnoreOutside(event)) return;
1261
1240
  const outsideEvent = emitOutside("pointer", event);
1262
1241
  blockModalInteraction(event);
1263
1242
  if (!outsideEvent.defaultPrevented) closeRoot({ focusTrigger: host.modal.value });
1264
1243
  }
1265
1244
  function onDocumentFocus(event) {
1266
- if (!host.isTopLayer() || shouldIgnoreOutside(event)) return;
1245
+ if (shouldIgnoreOutside(event)) return;
1267
1246
  const outsideEvent = emitOutside("focus", event);
1268
1247
  blockModalInteraction(event);
1269
1248
  if (!outsideEvent.defaultPrevented) closeRoot({ focusTrigger: host.modal.value });
1270
1249
  else if (host.modal.value) registry.focusMenuElement(registry.activeMenuId.value);
1271
1250
  }
1272
- function setDocumentListeners(active) {
1273
- if (typeof document === "undefined" || active === isListening) return;
1274
- isListening = active;
1275
- const method = active ? "addEventListener" : "removeEventListener";
1276
- document[method]("pointerdown", onDocumentPointer, true);
1277
- document[method]("focusin", onDocumentFocus, true);
1278
- }
1279
1251
  function registerDismissal(registration) {
1252
+ layerInteraction?.();
1280
1253
  dismissal = registration;
1281
- setDocumentListeners(host.isOpen.value);
1254
+ layerInteraction = host.connectLayerInteraction({
1255
+ inside: () => [...inside],
1256
+ pointerDownOutside: onDocumentPointer,
1257
+ focusOutside: onDocumentFocus
1258
+ });
1282
1259
  return () => {
1283
1260
  if (dismissal !== registration) return;
1284
1261
  dismissal = void 0;
1285
- setDocumentListeners(false);
1262
+ layerInteraction?.();
1263
+ layerInteraction = void 0;
1286
1264
  };
1287
1265
  }
1288
- watch(host.isOpen, (open) => setDocumentListeners(open && Boolean(dismissal)));
1289
- onBeforeUnmount(() => setDocumentListeners(false));
1290
1266
  return {
1291
1267
  registerInside,
1292
1268
  unregisterInside,
@@ -1433,6 +1409,22 @@ function resolveOpenFocusTarget(options) {
1433
1409
  if (typeof options === "string") return options;
1434
1410
  return options?.focus ?? "first";
1435
1411
  }
1412
+ function resolveKeyboardMenuId(rootMenuId, registry, event) {
1413
+ const activeItemId = registry.activeItemId.value;
1414
+ const activeItem = activeItemId ? registry.getItem(activeItemId) : void 0;
1415
+ const openSubmenuId = activeItem?.submenuId?.();
1416
+ if (openSubmenuId && registry.isMenuOpen(openSubmenuId)) {
1417
+ if (event.key === "Escape") return openSubmenuId;
1418
+ const opensLeft = activeItem?.submenuDirection?.() === "left" || registry.getMenu(openSubmenuId)?.placement().startsWith("left");
1419
+ if (event.key === (opensLeft ? "ArrowRight" : "ArrowLeft")) return openSubmenuId;
1420
+ }
1421
+ if ([
1422
+ "ArrowLeft",
1423
+ "ArrowRight",
1424
+ "Escape"
1425
+ ].includes(event.key) && registry.activeMenuId.value !== rootMenuId) return registry.activeMenuId.value;
1426
+ return activeItem?.menuId ?? registry.activeMenuId.value;
1427
+ }
1436
1428
  function useDropdownMenuInteraction(host) {
1437
1429
  let navigation;
1438
1430
  const registry = createDropdownMenuInteractionRegistry(host, {
@@ -1485,6 +1477,129 @@ function useDropdownMenuInteraction(host) {
1485
1477
  registry,
1486
1478
  closeRoot: close
1487
1479
  });
1480
+ function connectItem(menuId, options) {
1481
+ const registration = {
1482
+ id: options.id,
1483
+ menuId,
1484
+ element: options.element,
1485
+ textValue: options.textValue,
1486
+ disabled: options.disabled,
1487
+ order: options.order,
1488
+ submenuId: options.submenu ? () => options.submenu?.id : void 0,
1489
+ submenuDirection: options.submenuDirection,
1490
+ select: options.select,
1491
+ closeOnSelect: options.closeOnSelect
1492
+ };
1493
+ const disconnect = registry.registerItem(registration);
1494
+ const stopReconciliation = watch([
1495
+ options.disabled,
1496
+ options.element,
1497
+ () => options.submenu?.id
1498
+ ], () => navigation.reconcile(menuId), { flush: "sync" });
1499
+ let disposed = false;
1500
+ return {
1501
+ active: computed(() => registry.isActive(options.id)),
1502
+ submenuOpen: computed(() => options.submenu ? registry.isMenuOpen(options.submenu.id) : false),
1503
+ setActive: (focusElement = false) => registry.setActive(options.id, focusElement),
1504
+ openSubmenu: (focus = "first") => options.submenu?.open(focus) ?? false,
1505
+ closeSubmenu: (focusParent = false) => options.submenu?.close(focusParent) ?? false,
1506
+ select: (originalEvent) => navigation.selectItem(options.id, originalEvent),
1507
+ activate: (originalEvent) => navigation.activateItem(options.id, originalEvent),
1508
+ hover(openSubmenu = false) {
1509
+ navigation.hoverItem(options.id, openSubmenu);
1510
+ return Boolean(options.submenu && registry.isMenuOpen(options.submenu.id));
1511
+ },
1512
+ dispose() {
1513
+ if (disposed) return;
1514
+ disposed = true;
1515
+ stopReconciliation();
1516
+ disconnect();
1517
+ }
1518
+ };
1519
+ }
1520
+ function connectMenu(id, options, state, getRequestedFocus) {
1521
+ const disconnect = registry.registerMenu({
1522
+ id,
1523
+ ...options,
1524
+ ...state
1525
+ });
1526
+ const items = /* @__PURE__ */ new Set();
1527
+ let disposed = false;
1528
+ return {
1529
+ activeId: registry.getActiveId(id),
1530
+ connectItem(itemOptions) {
1531
+ const item = connectItem(id, itemOptions);
1532
+ const dispose = item.dispose;
1533
+ item.dispose = () => {
1534
+ dispose();
1535
+ items.delete(item);
1536
+ };
1537
+ items.add(item);
1538
+ return item;
1539
+ },
1540
+ clearActive: () => {
1541
+ registry.setActive(null);
1542
+ },
1543
+ focusPending() {
1544
+ const pending = id === host.rootMenuId ? registry.pendingRootFocus.value : getRequestedFocus?.() ?? registry.state.getPendingMenuFocus(id);
1545
+ return navigation.focusMenu(id, pending || "first");
1546
+ },
1547
+ onKeydown: (event) => keyboard.onMenuKeydown(id, event),
1548
+ dispose() {
1549
+ if (disposed) return;
1550
+ disposed = true;
1551
+ for (const item of items) item.dispose();
1552
+ disconnect();
1553
+ }
1554
+ };
1555
+ }
1556
+ function connectRootMenu(options) {
1557
+ return connectMenu(host.rootMenuId, options, { isOpen: () => host.isOpen.value });
1558
+ }
1559
+ function connectSubmenu(options) {
1560
+ const disconnectState = registry.registerMenuState(options);
1561
+ let requestedFocus;
1562
+ const stopStateSync = watch(options.isOpen, (isOpen) => {
1563
+ if (isOpen) return;
1564
+ requestedFocus = void 0;
1565
+ navigation.closeMenu(options.id, false);
1566
+ }, { flush: "sync" });
1567
+ let content;
1568
+ let disposed = false;
1569
+ return {
1570
+ id: options.id,
1571
+ open(focus = "first") {
1572
+ requestedFocus = focus;
1573
+ return navigation.openMenu(options.id, focus);
1574
+ },
1575
+ close(focusParent = false) {
1576
+ requestedFocus = void 0;
1577
+ return navigation.closeMenu(options.id, focusParent);
1578
+ },
1579
+ connectContent(menuOptions) {
1580
+ const previous = content;
1581
+ content = connectMenu(options.id, menuOptions, options, () => requestedFocus);
1582
+ previous?.dispose();
1583
+ return content;
1584
+ },
1585
+ dispose() {
1586
+ if (disposed) return;
1587
+ disposed = true;
1588
+ content?.dispose();
1589
+ stopStateSync();
1590
+ disconnectState();
1591
+ }
1592
+ };
1593
+ }
1594
+ function connectInside(element) {
1595
+ dismissal.registerInside(element);
1596
+ let connected = true;
1597
+ return () => {
1598
+ if (!connected) return;
1599
+ connected = false;
1600
+ dismissal.unregisterInside(element);
1601
+ };
1602
+ }
1488
1603
  watch(host.disabled, (disabled) => {
1489
1604
  if (disabled) close();
1490
1605
  });
@@ -1501,39 +1616,17 @@ function useDropdownMenuInteraction(host) {
1501
1616
  navigation.focusMenu(host.rootMenuId, focus);
1502
1617
  }, { immediate: true });
1503
1618
  return {
1504
- rootMenuId: host.rootMenuId,
1505
1619
  activeItemId: registry.activeItemId,
1506
- activeMenuId: registry.activeMenuId,
1507
- pendingRootFocus: registry.pendingRootFocus,
1508
- registerMenu: registry.registerMenu,
1509
- unregisterMenu: registry.unregisterMenu,
1510
- registerMenuState: registry.registerMenuState,
1511
- registerItem: registry.registerItem,
1512
- unregisterItem: registry.unregisterItem,
1513
- registerInside: dismissal.registerInside,
1514
- unregisterInside: dismissal.unregisterInside,
1515
- registerDismissal: dismissal.registerDismissal,
1516
- getActiveId: registry.getActiveId,
1517
- getItem: registry.getItem,
1518
- getMenu: registry.getMenu,
1519
- isActive: registry.isActive,
1520
- isMenuOpen: registry.isMenuOpen,
1521
- setActive: registry.setActive,
1522
- focusMenu: navigation.focusMenu,
1523
- focusMenuElement: registry.focusMenuElement,
1620
+ connectRootMenu,
1621
+ connectSubmenu,
1622
+ connectInside,
1623
+ connectDismissal: dismissal.registerDismissal,
1524
1624
  open,
1525
1625
  close,
1526
1626
  toggle,
1527
- openMenu: navigation.openMenu,
1528
- closeMenu: navigation.closeMenu,
1529
- closeSubmenus: navigation.closeSubmenus,
1530
- selectItem: navigation.selectItem,
1531
- activateItem: navigation.activateItem,
1532
- hoverItem: navigation.hoverItem,
1533
- reconcile: navigation.reconcile,
1534
1627
  onTriggerClick,
1535
1628
  onTriggerKeydown: keyboard.onTriggerKeydown,
1536
- onMenuKeydown: keyboard.onMenuKeydown
1629
+ onMenuKeydown: (event) => keyboard.onMenuKeydown(resolveKeyboardMenuId(host.rootMenuId, registry, event), event)
1537
1630
  };
1538
1631
  }
1539
1632
  //#endregion
@@ -1581,7 +1674,10 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1581
1674
  const triggerId = ref();
1582
1675
  const contentId = ref();
1583
1676
  const activeReference = shallowRef(null);
1584
- const { reference: configuredReference } = useFloatingTarget(() => props.target ?? props.virtualAnchor, trigger);
1677
+ const { reference: configuredReference } = useFloatingTargetLifecycle({
1678
+ target: () => props.target ?? props.virtualAnchor,
1679
+ fallback: trigger
1680
+ });
1585
1681
  const reference = computed(() => activeReference.value ?? configuredReference.value);
1586
1682
  const contentElement = ref(null);
1587
1683
  let returnFocusElement = null;
@@ -1608,26 +1704,29 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1608
1704
  const target = returnFocusElement ?? trigger.value;
1609
1705
  nextTick(() => target?.focus());
1610
1706
  }
1707
+ const interactionRootMenuId = `${generatedId}-interaction-root`;
1611
1708
  const interaction = useDropdownMenuInteraction({
1612
- rootMenuId: `${generatedId}-interaction-root`,
1709
+ rootMenuId: interactionRootMenuId,
1613
1710
  isOpen,
1614
1711
  disabled,
1615
1712
  modal,
1616
1713
  setOpen,
1617
- isTopLayer: layer.isTopLayer,
1714
+ connectLayerInteraction: layer.connectInteraction,
1618
1715
  focusTrigger: focusReturnTarget,
1619
1716
  beforeOpen: rememberFocus
1620
1717
  });
1621
- const { pendingRootFocus: pendingFocus, open, close, toggle } = interaction;
1718
+ const { open, close, toggle } = interaction;
1622
1719
  function openAt(point, options) {
1623
1720
  activeReference.value = createVirtualAnchor(point);
1624
1721
  open(options);
1625
1722
  }
1723
+ let disconnectTrigger;
1626
1724
  function setTrigger(element, nextId) {
1627
- if (trigger.value && trigger.value !== element) interaction.unregisterInside(trigger.value);
1725
+ disconnectTrigger?.();
1726
+ disconnectTrigger = void 0;
1628
1727
  trigger.value = element;
1629
1728
  triggerId.value = nextId;
1630
- if (element) interaction.registerInside(element);
1729
+ if (element) disconnectTrigger = interaction.connectInside(element);
1631
1730
  }
1632
1731
  const context = {
1633
1732
  id,
@@ -1638,7 +1737,7 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1638
1737
  triggerId,
1639
1738
  contentId,
1640
1739
  reference,
1641
- pendingFocus,
1740
+ interactionRootMenuId,
1642
1741
  layer,
1643
1742
  interaction,
1644
1743
  open,
@@ -1652,12 +1751,7 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1652
1751
  setReturnFocus(element) {
1653
1752
  returnFocusElement = element;
1654
1753
  },
1655
- registerInside(element) {
1656
- interaction.registerInside(element);
1657
- },
1658
- unregisterInside(element) {
1659
- interaction.unregisterInside(element);
1660
- }
1754
+ connectInside: interaction.connectInside
1661
1755
  };
1662
1756
  const slotProps = computed(() => ({
1663
1757
  isOpen: isOpen.value,
@@ -1670,6 +1764,7 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1670
1764
  if (!value) activeReference.value = null;
1671
1765
  }, { flush: "sync" });
1672
1766
  provide(rootKey, context);
1767
+ onBeforeUnmount(() => disconnectTrigger?.());
1673
1768
  __expose({
1674
1769
  open,
1675
1770
  close,
@@ -1734,12 +1829,12 @@ var dropdown_menu_sub_default = /* @__PURE__ */ defineVaporComponent({
1734
1829
  const trigger = ref(null);
1735
1830
  const contentId = ref();
1736
1831
  const actualPlacement = ref("right-start");
1737
- const pendingFocus = ref(false);
1738
1832
  function setOpen(value) {
1739
1833
  if (isOpen.value !== value) controllableOpen.setValue(value);
1740
1834
  }
1741
- const cleanupMenuState = parentMenu.root.interaction.registerMenuState({
1835
+ const interaction = parentMenu.root.interaction.connectSubmenu({
1742
1836
  id: menuId,
1837
+ parentItemId: () => trigger.value?.id,
1743
1838
  isOpen: () => isOpen.value,
1744
1839
  setOpen
1745
1840
  });
@@ -1749,20 +1844,17 @@ var dropdown_menu_sub_default = /* @__PURE__ */ defineVaporComponent({
1749
1844
  trigger,
1750
1845
  contentId,
1751
1846
  actualPlacement,
1752
- pendingFocus,
1753
1847
  menu: null,
1754
- setOpen,
1848
+ interaction,
1755
1849
  open(focus = "first") {
1756
- pendingFocus.value = focus;
1757
- if (!parentMenu.root.interaction.openMenu(menuId, focus)) setOpen(true);
1850
+ if (!interaction.open(focus)) setOpen(true);
1758
1851
  },
1759
1852
  close(focusParent = false) {
1760
- pendingFocus.value = false;
1761
- if (!parentMenu.root.interaction.closeMenu(menuId, focusParent)) setOpen(false);
1853
+ if (!interaction.close(focusParent)) setOpen(false);
1762
1854
  }
1763
1855
  };
1764
1856
  provide(subKey, context);
1765
- onBeforeUnmount(cleanupMenuState);
1857
+ onBeforeUnmount(interaction.dispose);
1766
1858
  return createSlot("default", {
1767
1859
  isOpen: () => unref(isOpen),
1768
1860
  open: () => context.open,
@@ -1852,7 +1944,6 @@ var dropdown_menu_sub_trigger_default = /* @__PURE__ */ defineVaporComponent({
1852
1944
  const generatedId = useId();
1853
1945
  const id = computed(() => props.id ?? `${generatedId}-sub-trigger`);
1854
1946
  const isDisabled = computed(() => menu.root.disabled.value || props.disabled);
1855
- const focused = computed(() => menu.isActive(id.value));
1856
1947
  const registration = {
1857
1948
  get id() {
1858
1949
  return id.value;
@@ -1863,15 +1954,18 @@ var dropdown_menu_sub_trigger_default = /* @__PURE__ */ defineVaporComponent({
1863
1954
  activate: () => void 0,
1864
1955
  submenu: sub
1865
1956
  };
1866
- menu.registerItem(registration);
1867
- watch(id, (nextId, previousId) => {
1868
- const wasActive = menu.activeId.value === previousId;
1869
- menu.unregisterItem(previousId);
1870
- menu.registerItem(registration);
1871
- if (wasActive) menu.setActive(nextId);
1957
+ let itemInteraction = menu.connectItem(registration);
1958
+ const focused = computed(() => {
1959
+ return id.value === registration.id && itemInteraction.active.value;
1960
+ });
1961
+ watch(id, () => {
1962
+ const previous = itemInteraction;
1963
+ const wasActive = previous.active.value;
1964
+ itemInteraction = menu.connectItem(registration);
1965
+ previous.dispose();
1966
+ if (wasActive) itemInteraction.setActive();
1872
1967
  }, { flush: "sync" });
1873
- watch(isDisabled, () => menu.root.interaction.reconcile(menu.id));
1874
- onBeforeUnmount(() => menu.unregisterItem(id.value));
1968
+ onBeforeUnmount(() => itemInteraction.dispose());
1875
1969
  function setElement(value) {
1876
1970
  resolveHTMLElementRef(value, id.value, (resolved) => {
1877
1971
  sub.trigger.value = resolved;
@@ -1879,10 +1973,10 @@ var dropdown_menu_sub_trigger_default = /* @__PURE__ */ defineVaporComponent({
1879
1973
  }
1880
1974
  function onMouseenter() {
1881
1975
  if (isDisabled.value) return;
1882
- menu.hover(id.value, true);
1976
+ itemInteraction.hover(true);
1883
1977
  }
1884
1978
  function onClick(event) {
1885
- if (!event.defaultPrevented && !isDisabled.value) menu.activate(id.value, event);
1979
+ if (!event.defaultPrevented && !isDisabled.value) itemInteraction.activate(event);
1886
1980
  }
1887
1981
  const rootAttrs = computed(() => mergeProps(attrs, {
1888
1982
  id: id.value,
@@ -2204,12 +2298,13 @@ function getDropdownMenuDataCollectionState(items) {
2204
2298
  function getCurrentPath(index, entry) {
2205
2299
  return index.pathByItemId.get(entry.itemId) ?? entry.path;
2206
2300
  }
2207
- function useDropdownMenuDataRegistration({ props, items, rootMenuId, interaction, menuRef, getItemElement, getSubmenuElement, isSubmenuOpeningLeft, onSelect }) {
2301
+ function useDropdownMenuDataRegistration({ props, items, rootMenuId, interaction, rootMenu, menuRef, getItemElement, getSubmenuElement, isSubmenuOpeningLeft, onSelect }) {
2208
2302
  const openMenuIds = ref(/* @__PURE__ */ new Set());
2209
2303
  const itemIdentity = /* @__PURE__ */ new WeakMap();
2210
2304
  let nextItemIdentity = 0;
2211
- let registeredItemIds = /* @__PURE__ */ new Set();
2212
- let registeredMenuIds = /* @__PURE__ */ new Set();
2305
+ let itemInteractions = /* @__PURE__ */ new Map();
2306
+ let menuInteractions = /* @__PURE__ */ new Map([[rootMenuId, rootMenu]]);
2307
+ let submenuInteractions = /* @__PURE__ */ new Map();
2213
2308
  function getStableItemId(item) {
2214
2309
  const existing = itemIdentity.get(item);
2215
2310
  if (existing) return existing;
@@ -2226,19 +2321,24 @@ function useDropdownMenuDataRegistration({ props, items, rootMenuId, interaction
2226
2321
  else next.delete(id);
2227
2322
  openMenuIds.value = next;
2228
2323
  }
2229
- function createMenuRegistration(index, entry) {
2324
+ function connectSubmenu(index, entry) {
2230
2325
  const submenuMenuId = entry.submenuMenuId;
2231
2326
  if (!submenuMenuId) return void 0;
2232
- return {
2327
+ const submenu = interaction.connectSubmenu({
2233
2328
  id: submenuMenuId,
2234
2329
  parentItemId: () => entry.itemId,
2235
- element: () => getSubmenuElement(getCurrentPath(index, entry)),
2236
- focusTarget: () => menuRef.value,
2237
- placement: () => {
2238
- return getSubmenuElement(getCurrentPath(index, entry))?.dataset.placement ?? "right-start";
2239
- },
2240
2330
  isOpen: () => openMenuIds.value.has(submenuMenuId),
2241
2331
  setOpen: (open) => setMenuOpen(submenuMenuId, open)
2332
+ });
2333
+ return {
2334
+ menu: submenu.connectContent({
2335
+ element: () => getSubmenuElement(getCurrentPath(index, entry)),
2336
+ focusTarget: () => menuRef.value,
2337
+ placement: () => {
2338
+ return getSubmenuElement(getCurrentPath(index, entry))?.dataset.placement ?? "right-start";
2339
+ }
2340
+ }),
2341
+ submenu
2242
2342
  };
2243
2343
  }
2244
2344
  function createSelectEvent(entry, originalEvent) {
@@ -2252,45 +2352,53 @@ function useDropdownMenuDataRegistration({ props, items, rootMenuId, interaction
2252
2352
  onSelect?.(entry.item, event);
2253
2353
  return event;
2254
2354
  }
2255
- function registerEntry(index, entry) {
2256
- const menuRegistration = createMenuRegistration(index, entry);
2257
- if (menuRegistration) interaction.registerMenu(menuRegistration);
2258
- interaction.registerItem({
2355
+ function connectItem(index, entry, nextMenus, nextSubmenus) {
2356
+ const menu = nextMenus.get(entry.ownerMenuId);
2357
+ if (!menu) return void 0;
2358
+ return menu.connectItem({
2259
2359
  id: entry.itemId,
2260
- menuId: entry.ownerMenuId,
2261
2360
  element: () => getItemElement(getCurrentPath(index, entry)),
2262
2361
  textValue: () => entry.item.label,
2263
2362
  disabled: () => Boolean(entry.item.disabled || props.disabled),
2264
2363
  order: () => entry.order,
2265
- submenuId: () => entry.submenuMenuId,
2364
+ submenu: entry.submenuMenuId ? nextSubmenus.get(entry.submenuMenuId) : void 0,
2266
2365
  submenuDirection: () => isSubmenuOpeningLeft(getCurrentPath(index, entry)) ? "left" : "right",
2267
2366
  select: (originalEvent) => createSelectEvent(entry, originalEvent),
2268
2367
  closeOnSelect: () => props.closeOnSelect !== false
2269
2368
  });
2270
2369
  }
2271
- function unregisterStaleEntries(nextRegisteredItemIds, nextRegisteredMenuIds) {
2272
- for (const id of registeredItemIds) if (!nextRegisteredItemIds.has(id)) interaction.unregisterItem(id);
2273
- for (const id of registeredMenuIds) {
2274
- if (nextRegisteredMenuIds.has(id)) continue;
2275
- interaction.unregisterMenu(id);
2276
- setMenuOpen(id, false);
2277
- }
2278
- }
2279
2370
  function registerItems() {
2280
2371
  const nextIndex = buildDropdownMenuDataIndex(items.value, rootMenuId, getStableItemId);
2281
- const nextRegisteredItemIds = new Set(nextIndex.entries.map((entry) => entry.itemId));
2282
- const nextRegisteredMenuIds = new Set(nextIndex.entries.flatMap((entry) => entry.submenuMenuId ? [entry.submenuMenuId] : []));
2283
- for (const entry of nextIndex.entries) registerEntry(nextIndex, entry);
2284
- unregisterStaleEntries(nextRegisteredItemIds, nextRegisteredMenuIds);
2285
- registeredItemIds = nextRegisteredItemIds;
2286
- registeredMenuIds = nextRegisteredMenuIds;
2372
+ const nextItems = /* @__PURE__ */ new Map();
2373
+ const nextMenus = /* @__PURE__ */ new Map([[rootMenuId, rootMenu]]);
2374
+ const nextSubmenus = /* @__PURE__ */ new Map();
2375
+ for (const entry of nextIndex.entries) {
2376
+ const connection = connectSubmenu(nextIndex, entry);
2377
+ if (!entry.submenuMenuId || !connection) continue;
2378
+ nextMenus.set(entry.submenuMenuId, connection.menu);
2379
+ nextSubmenus.set(entry.submenuMenuId, connection.submenu);
2380
+ }
2381
+ for (const entry of nextIndex.entries) {
2382
+ const item = connectItem(nextIndex, entry, nextMenus, nextSubmenus);
2383
+ if (item) nextItems.set(entry.itemId, item);
2384
+ }
2385
+ for (const item of itemInteractions.values()) item.dispose();
2386
+ for (const submenu of submenuInteractions.values()) submenu.dispose();
2387
+ itemInteractions = nextItems;
2388
+ menuInteractions = nextMenus;
2389
+ submenuInteractions = nextSubmenus;
2287
2390
  registrationIndex.value = nextIndex;
2288
- interaction.reconcile();
2289
2391
  }
2290
2392
  watch(collectionState, registerItems, { immediate: true });
2393
+ onBeforeUnmount(() => {
2394
+ for (const item of itemInteractions.values()) item.dispose();
2395
+ for (const submenu of submenuInteractions.values()) submenu.dispose();
2396
+ });
2291
2397
  return {
2292
2398
  openMenuIds,
2293
- registrationIndex
2399
+ registrationIndex,
2400
+ getItemInteraction: (id) => itemInteractions.get(id),
2401
+ getMenuInteraction: (id) => menuInteractions.get(id)
2294
2402
  };
2295
2403
  }
2296
2404
  //#endregion
@@ -2422,7 +2530,7 @@ function useDropdownMenuHoverIntent({ openSubmenuPath, getItemElement, getSubmen
2422
2530
  }
2423
2531
  //#endregion
2424
2532
  //#region src/components/dropdown-menu/useDropdownMenuDataController.ts
2425
- function useDropdownMenuDataController({ props, items, rootMenuId, menuId, menuRef, actualPlacement, interaction, onSelect }) {
2533
+ function useDropdownMenuDataController({ props, items, rootMenuId, menuId, menuRef, actualPlacement, interaction, rootMenu, onSelect }) {
2426
2534
  function getItemId(path) {
2427
2535
  return `${menuId.value}-item-${getPathKey(path)}`;
2428
2536
  }
@@ -2441,11 +2549,12 @@ function useDropdownMenuDataController({ props, items, rootMenuId, menuId, menuR
2441
2549
  if (submenuRect && itemRect && hasMeasuredRect(submenuRect) && hasMeasuredRect(itemRect)) return submenuRect.right <= itemRect.left;
2442
2550
  return actualPlacement.value.endsWith("end");
2443
2551
  }
2444
- const { openMenuIds, registrationIndex } = useDropdownMenuDataRegistration({
2552
+ const { openMenuIds, registrationIndex, getItemInteraction, getMenuInteraction } = useDropdownMenuDataRegistration({
2445
2553
  props,
2446
2554
  items,
2447
2555
  rootMenuId,
2448
2556
  interaction,
2557
+ rootMenu,
2449
2558
  menuRef,
2450
2559
  getItemElement,
2451
2560
  getSubmenuElement,
@@ -2461,7 +2570,9 @@ function useDropdownMenuDataController({ props, items, rootMenuId, menuId, menuR
2461
2570
  return id ? [...registrationIndex.value.pathByItemId.get(id) ?? []] : [];
2462
2571
  },
2463
2572
  set(path) {
2464
- interaction.setActive(path.length > 0 ? getDataItemId(path) ?? null : null);
2573
+ const itemId = path.length > 0 ? getDataItemId(path) : void 0;
2574
+ if (itemId) getItemInteraction(itemId)?.setActive();
2575
+ else rootMenu.clearActive();
2465
2576
  }
2466
2577
  });
2467
2578
  const focusedIndex = computed(() => focusedPath.value[0] ?? -1);
@@ -2480,31 +2591,28 @@ function useDropdownMenuDataController({ props, items, rootMenuId, menuId, menuR
2480
2591
  }
2481
2592
  function isSubmenuOpen(path) {
2482
2593
  const itemId = getDataItemId(path);
2483
- const submenuId = itemId ? interaction.getItem(itemId)?.submenuId?.() : void 0;
2484
- return Boolean(submenuId && interaction.isMenuOpen(submenuId));
2594
+ return itemId ? getItemInteraction(itemId)?.submenuOpen.value ?? false : false;
2485
2595
  }
2486
2596
  function openSubmenu(path, focus = DEFAULT_FOCUS_TARGET, point) {
2487
2597
  const itemId = getDataItemId(path);
2488
- const submenuId = itemId ? interaction.getItem(itemId)?.submenuId?.() : void 0;
2489
- if (!submenuId || !interaction.openMenu(submenuId, focus)) return false;
2598
+ if (!(itemId ? getItemInteraction(itemId) : void 0)?.openSubmenu(focus)) return false;
2490
2599
  hoverIntent.trackSubmenuOpen(path, point);
2491
2600
  return true;
2492
2601
  }
2493
2602
  function closeSubmenu(path) {
2494
2603
  const itemId = getDataItemId(path);
2495
- const submenuId = itemId ? interaction.getItem(itemId)?.submenuId?.() : void 0;
2496
- const closed = submenuId ? interaction.closeMenu(submenuId, false) : false;
2604
+ const closed = itemId ? getItemInteraction(itemId)?.closeSubmenu(false) ?? false : false;
2497
2605
  if (closed) hoverIntent.resetHoverIntent();
2498
2606
  return closed;
2499
2607
  }
2500
2608
  function commitHoveredItem(item, path, point) {
2501
2609
  const itemId = getDataItemId(path);
2502
- const itemRegistration = itemId ? interaction.getItem(itemId) : void 0;
2503
- if (!itemRegistration) return;
2610
+ const itemInteraction = itemId ? getItemInteraction(itemId) : void 0;
2611
+ if (!itemInteraction) return;
2504
2612
  hoverIntent.clearPendingHover();
2505
- interaction.setActive(itemRegistration.id);
2506
- if (hasItemSubmenu(item)) openSubmenu(path, false, point);
2507
- else interaction.closeSubmenus(itemRegistration.menuId);
2613
+ const hasSubmenu = hasItemSubmenu(item);
2614
+ const opened = itemInteraction.hover(hasSubmenu);
2615
+ if (hasSubmenu && opened) hoverIntent.trackSubmenuOpen(path, point);
2508
2616
  }
2509
2617
  const hoverIntent = useDropdownMenuHoverIntent({
2510
2618
  openSubmenuPath,
@@ -2517,15 +2625,15 @@ function useDropdownMenuDataController({ props, items, rootMenuId, menuId, menuR
2517
2625
  }
2518
2626
  function selectItem(item, originalEvent = new Event("select")) {
2519
2627
  const id = registrationIndex.value.primaryItemId.get(item);
2520
- if (id) interaction.selectItem(id, originalEvent);
2628
+ if (id) getItemInteraction(id)?.select(originalEvent);
2521
2629
  }
2522
2630
  function activateItem(_item, path, event) {
2523
2631
  const id = getDataItemId(path);
2524
- if (id) interaction.activateItem(id, event);
2632
+ if (id) getItemInteraction(id)?.activate(event);
2525
2633
  }
2526
2634
  function getMenuActiveDescendant(path) {
2527
2635
  const pathKey = getPathKey(path);
2528
- const activeId = interaction.getActiveId(registrationIndex.value.menuIdByPath.get(pathKey) ?? rootMenuId).value;
2636
+ const activeId = getMenuInteraction(registrationIndex.value.menuIdByPath.get(pathKey) ?? rootMenuId)?.activeId.value;
2529
2637
  const activePath = activeId ? registrationIndex.value.pathByItemId.get(activeId) : void 0;
2530
2638
  return activePath ? getItemId(activePath) : void 0;
2531
2639
  }
@@ -2573,7 +2681,7 @@ function useDropdownMenuPortalPosition(options) {
2573
2681
  }
2574
2682
  //#endregion
2575
2683
  //#region src/components/dropdown-menu/useDropdownMenuPresentation.ts
2576
- function useDropdownMenuPresentation({ props, rootMenuId, menuId, isDisabled, isVisible, isExplicitTarget, actualPlacement, contentHasSubmenu, activeDescendantId, interaction, resetHoverIntent, onMenuMousemove, open, close, toggle }) {
2684
+ function useDropdownMenuPresentation({ props, menuId, isDisabled, isVisible, isExplicitTarget, actualPlacement, contentHasSubmenu, activeDescendantId, interaction, resetHoverIntent, onMenuMousemove, open, close, toggle }) {
2577
2685
  const rootClass = computed(() => bem("rp-dropdown-menu", {
2578
2686
  [`placement-${actualPlacement.value}`]: true,
2579
2687
  target: isExplicitTarget.value,
@@ -2589,24 +2697,9 @@ function useDropdownMenuPresentation({ props, rootMenuId, menuId, isDisabled, is
2589
2697
  onClick: () => interaction.onTriggerClick(),
2590
2698
  onKeydown: (event) => interaction.onTriggerKeydown(event)
2591
2699
  }));
2592
- function getKeyboardMenuId(event) {
2593
- const activeItem = interaction.activeItemId.value ? interaction.getItem(interaction.activeItemId.value) : void 0;
2594
- const openSubmenuId = activeItem?.submenuId?.();
2595
- if (openSubmenuId && interaction.isMenuOpen(openSubmenuId)) {
2596
- if (event.key === "Escape") return openSubmenuId;
2597
- const opensLeft = activeItem?.submenuDirection?.() === "left" || interaction.getMenu(openSubmenuId)?.placement().startsWith("left");
2598
- if (event.key === (opensLeft ? "ArrowRight" : "ArrowLeft")) return openSubmenuId;
2599
- }
2600
- if ([
2601
- "ArrowLeft",
2602
- "ArrowRight",
2603
- "Escape"
2604
- ].includes(event.key) && interaction.activeMenuId.value !== rootMenuId) return interaction.activeMenuId.value;
2605
- return activeItem?.menuId ?? interaction.activeMenuId.value;
2606
- }
2607
2700
  function onMenuKeydown(event) {
2608
2701
  resetHoverIntent();
2609
- interaction.onMenuKeydown(getKeyboardMenuId(event), event);
2702
+ interaction.onMenuKeydown(event);
2610
2703
  }
2611
2704
  return {
2612
2705
  rootClass,
@@ -2755,45 +2848,32 @@ function syncTargetAttributes(target, id, visible, disabled) {
2755
2848
  target.setAttribute("aria-haspopup", "menu");
2756
2849
  target.removeAttribute("aria-disabled");
2757
2850
  }
2758
- function useDropdownMenuTargetBindings({ interaction, rootRef, menuRef, targetElement, isExplicitTarget, menuId, isVisible, isDisabled, ignoredTargets, pointerDownOutside, focusOutside, interactOutside }) {
2851
+ function useDropdownMenuTargetBindings({ interaction, rootRef, menuRef, targetLifecycle, menuId, isVisible, isDisabled, ignoredTargets, pointerDownOutside, focusOutside, interactOutside }) {
2759
2852
  function watchInside(source) {
2760
- watch(source, (element, previous) => {
2761
- if (previous) interaction.unregisterInside(previous);
2762
- if (element) interaction.registerInside(element);
2853
+ watch(source, (element, _previous, onCleanup) => {
2854
+ if (element) onCleanup(interaction.connectInside(element));
2763
2855
  }, { immediate: true });
2764
2856
  }
2765
2857
  watchInside(rootRef);
2766
2858
  watchInside(menuRef);
2767
- watchInside(targetElement);
2768
- const cleanupDismissal = interaction.registerDismissal({
2859
+ const cleanupDismissal = interaction.connectDismissal({
2769
2860
  ignoredTargets,
2770
2861
  pointerDownOutside,
2771
2862
  focusOutside,
2772
2863
  interactOutside
2773
2864
  });
2774
- watch([isExplicitTarget, targetElement], ([explicit, target], _previous, onCleanup) => {
2775
- if (!explicit || !target) return;
2776
- const onClick = () => interaction.onTriggerClick();
2777
- const onKeydown = (event) => interaction.onTriggerKeydown(event);
2778
- target.addEventListener("click", onClick);
2779
- target.addEventListener("keydown", onKeydown);
2780
- onCleanup(() => {
2781
- target.removeEventListener("click", onClick);
2782
- target.removeEventListener("keydown", onKeydown);
2783
- });
2784
- }, { flush: "sync" });
2785
- watch([
2786
- isExplicitTarget,
2787
- targetElement,
2788
- menuId,
2789
- isVisible,
2790
- isDisabled
2791
- ], ([explicit, target, id, visible, disabled], _previous, onCleanup) => {
2792
- if (!explicit || !target) return;
2793
- const snapshot = snapshotAttributes(target, TARGET_ATTRIBUTES);
2794
- syncTargetAttributes(target, id, visible, disabled);
2795
- onCleanup(() => restoreAttributes(target, snapshot));
2796
- }, { flush: "sync" });
2865
+ const onClick = () => interaction.onTriggerClick();
2866
+ const onKeydown = (event) => interaction.onTriggerKeydown(event);
2867
+ targetLifecycle.bindTarget({
2868
+ connect: (target) => interaction.connectInside(target),
2869
+ listeners: [["click", onClick], ["keydown", onKeydown]],
2870
+ attributes: {
2871
+ names: TARGET_ATTRIBUTES,
2872
+ apply: (target) => {
2873
+ syncTargetAttributes(target, menuId.value, isVisible.value, isDisabled.value);
2874
+ }
2875
+ }
2876
+ });
2797
2877
  onBeforeUnmount(cleanupDismissal);
2798
2878
  }
2799
2879
  //#endregion
@@ -2813,8 +2893,11 @@ function useDropdownMenu(props, emit = {}) {
2813
2893
  const placement = computed(() => props.placement ?? "bottom-start");
2814
2894
  const teleportTo = useTeleportTarget(() => props.teleportTo);
2815
2895
  const shouldTeleport = computed(() => props.teleport !== false);
2816
- const { isExplicitTarget, reference, resolvedTarget } = useFloatingTarget(() => props.target, rootRef);
2817
- const targetElement = computed(() => isElement(resolvedTarget.value) ? resolvedTarget.value : null);
2896
+ const targetLifecycle = useFloatingTargetLifecycle({
2897
+ target: () => props.target,
2898
+ fallback: rootRef
2899
+ });
2900
+ const { isExplicitTarget, reference, targetElement } = targetLifecycle;
2818
2901
  const visibleItems = computed(() => props.items ?? []);
2819
2902
  const isDisabled = computed(() => Boolean(props.disabled));
2820
2903
  const modal = computed(() => Boolean(props.modal));
@@ -2860,7 +2943,7 @@ function useDropdownMenu(props, emit = {}) {
2860
2943
  disabled: isDisabled,
2861
2944
  modal,
2862
2945
  setOpen,
2863
- isTopLayer: layer.isTopLayer,
2946
+ connectLayerInteraction: layer.connectInteraction,
2864
2947
  focusTrigger,
2865
2948
  beforeOpen: () => resetHoverIntent(),
2866
2949
  beforeClose: () => resetHoverIntent()
@@ -2868,11 +2951,9 @@ function useDropdownMenu(props, emit = {}) {
2868
2951
  const open = interaction.open;
2869
2952
  const close = (options = {}) => interaction.close(options);
2870
2953
  const toggle = interaction.toggle;
2871
- const cleanupRootMenu = interaction.registerMenu({
2872
- id: interactionRootMenuId,
2954
+ const rootMenu = interaction.connectRootMenu({
2873
2955
  element: () => menuRef.value,
2874
- placement: () => actualPlacement.value,
2875
- isOpen: () => isVisible.value
2956
+ placement: () => actualPlacement.value
2876
2957
  });
2877
2958
  const dataController = useDropdownMenuDataController({
2878
2959
  props,
@@ -2882,6 +2963,7 @@ function useDropdownMenu(props, emit = {}) {
2882
2963
  menuRef,
2883
2964
  actualPlacement,
2884
2965
  interaction,
2966
+ rootMenu,
2885
2967
  onSelect: (item, event) => emit.select?.(item, event)
2886
2968
  });
2887
2969
  resetHoverIntent = dataController.resetHoverIntent;
@@ -2904,7 +2986,6 @@ function useDropdownMenu(props, emit = {}) {
2904
2986
  });
2905
2987
  const presentation = useDropdownMenuPresentation({
2906
2988
  props,
2907
- rootMenuId: interactionRootMenuId,
2908
2989
  menuId,
2909
2990
  isDisabled,
2910
2991
  isVisible,
@@ -2923,8 +3004,7 @@ function useDropdownMenu(props, emit = {}) {
2923
3004
  interaction,
2924
3005
  rootRef,
2925
3006
  menuRef,
2926
- targetElement,
2927
- isExplicitTarget,
3007
+ targetLifecycle,
2928
3008
  menuId,
2929
3009
  isVisible,
2930
3010
  isDisabled,
@@ -2937,7 +3017,7 @@ function useDropdownMenu(props, emit = {}) {
2937
3017
  ...contentStyle.value,
2938
3018
  zIndex: layer.zIndex.value
2939
3019
  }));
2940
- onBeforeUnmount(cleanupRootMenu);
3020
+ onBeforeUnmount(rootMenu.dispose);
2941
3021
  return {
2942
3022
  rootRef,
2943
3023
  menuRef,