ropav 0.1.6 → 0.1.7

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 (208) hide show
  1. package/README.md +6 -0
  2. package/dist/accordion.css +17 -17
  3. package/dist/accordion.js +123 -155
  4. package/dist/alert.css +18 -18
  5. package/dist/alert.js +10 -11
  6. package/dist/aria.js +14 -0
  7. package/dist/aspect-ratio.js +1 -1
  8. package/dist/attributes.js +9 -0
  9. package/dist/attributes2.js +29 -0
  10. package/dist/avatar.css +19 -19
  11. package/dist/avatar.js +9 -17
  12. package/dist/badge.css +16 -16
  13. package/dist/badge.js +5 -5
  14. package/dist/button-group.js +1 -1
  15. package/dist/button-link.js +4 -3
  16. package/dist/button.js +4 -3
  17. package/dist/button.scss_vue_type_style_index_0_src_true_lang.js +1 -1
  18. package/dist/card.js +1 -1
  19. package/dist/checkbox.css +32 -32
  20. package/dist/checkbox.js +22 -28
  21. package/dist/collapse.css +6 -6
  22. package/dist/collapse.js +7 -6
  23. package/dist/collectionNavigation.js +2 -99
  24. package/dist/color-input.css +19 -19
  25. package/dist/color-input.js +14 -13
  26. package/dist/color-picker.css +40 -40
  27. package/dist/color-picker.js +221 -200
  28. package/dist/color-picker2.js +1 -9
  29. package/dist/color-swatch.css +4 -4
  30. package/dist/color-swatch.js +6 -8
  31. package/dist/color.js +5 -10
  32. package/dist/componentColors.js +125 -84
  33. package/dist/componentRef.js +26 -0
  34. package/dist/components/accordion/accordionContext.d.ts +3 -0
  35. package/dist/components/accordion/accordionModel.d.ts +14 -0
  36. package/dist/components/accordion/index.d.ts +2 -1
  37. package/dist/components/accordion/index.js +1 -1
  38. package/dist/components/accordion/types.d.ts +1 -2
  39. package/dist/components/accordion/useAccordion.d.ts +2 -2
  40. package/dist/components/accordion/useAccordionItem.d.ts +2 -0
  41. package/dist/components/checkbox/useCheckbox.d.ts +1 -2
  42. package/dist/components/color-picker/index.js +2 -2
  43. package/dist/components/color-picker/types.d.ts +2 -2
  44. package/dist/components/color-picker/useColorPickerSaturation.d.ts +22 -0
  45. package/dist/components/color-picker/useColorPickerSlider.d.ts +20 -0
  46. package/dist/components/color-picker/useColorPickerValue.d.ts +1 -1
  47. package/dist/components/dialog/{dialog-core.d.ts → dialogContext.d.ts} +1 -5
  48. package/dist/components/dialog/useDialogContentInteractions.d.ts +18 -0
  49. package/dist/components/dialog/useDialogRootState.d.ts +19 -0
  50. package/dist/components/dropdown-menu/dropdown-menu-item-row.d.ts +1 -1
  51. package/dist/components/dropdown-menu/dropdown-menu-items.d.ts +1 -1
  52. package/dist/components/dropdown-menu/dropdown-menu-model.d.ts +8 -0
  53. package/dist/components/dropdown-menu/dropdown-menu-root.d.ts +2 -2
  54. package/dist/components/dropdown-menu/dropdown-menu-sub-trigger.d.ts +1 -1
  55. package/dist/components/dropdown-menu/dropdown-menu-sub.d.ts +1 -1
  56. package/dist/components/dropdown-menu/{dropdown-menu-primitive-core.d.ts → dropdownMenuContext.d.ts} +16 -18
  57. package/dist/components/dropdown-menu/dropdownMenuDataModel.d.ts +20 -0
  58. package/dist/components/dropdown-menu/dropdownMenuInteraction.d.ts +3 -0
  59. package/dist/components/dropdown-menu/dropdownMenuInteractionNavigation.d.ts +24 -0
  60. package/dist/components/dropdown-menu/dropdownMenuInteractionRegistry.d.ts +42 -0
  61. package/dist/components/dropdown-menu/dropdownMenuInteractionTypes.d.ts +85 -0
  62. package/dist/components/dropdown-menu/useDropdownMenu.d.ts +11 -11
  63. package/dist/components/dropdown-menu/useDropdownMenuContextTrigger.d.ts +9 -0
  64. package/dist/components/dropdown-menu/useDropdownMenuDataController.d.ts +34 -0
  65. package/dist/components/dropdown-menu/useDropdownMenuDataRegistration.d.ts +22 -0
  66. package/dist/components/dropdown-menu/useDropdownMenuHoverIntent.d.ts +6 -5
  67. package/dist/components/dropdown-menu/useDropdownMenuInteractionDismissal.d.ts +17 -0
  68. package/dist/components/dropdown-menu/useDropdownMenuInteractionKeyboard.d.ts +19 -0
  69. package/dist/components/dropdown-menu/useDropdownMenuPortalPosition.d.ts +0 -1
  70. package/dist/components/dropdown-menu/useDropdownMenuPresentation.d.ts +29 -0
  71. package/dist/components/dropdown-menu/useDropdownMenuPrimitiveContent.d.ts +2 -2
  72. package/dist/components/dropdown-menu/useDropdownMenuRenderItems.d.ts +1 -1
  73. package/dist/components/dropdown-menu/useDropdownMenuTargetBindings.d.ts +19 -0
  74. package/dist/components/floating/hoverDisclosureInteractionModel.d.ts +42 -0
  75. package/dist/components/floating/index.js +1 -1
  76. package/dist/components/floating/useFloatingPosition.d.ts +0 -1
  77. package/dist/components/floating/useHoverDisclosureInteractions.d.ts +32 -0
  78. package/dist/components/floating/useHoverDisclosureOpenState.d.ts +25 -0
  79. package/dist/components/floating/useHoverDisclosureTargetBinding.d.ts +27 -0
  80. package/dist/components/number-input/numberInputModel.d.ts +15 -0
  81. package/dist/components/number-input/useNumberInput.d.ts +3 -17
  82. package/dist/components/pagination/index.d.ts +1 -1
  83. package/dist/components/pagination/index.js +1 -1
  84. package/dist/components/pagination/paginationModel.d.ts +4 -0
  85. package/dist/components/pagination/types.d.ts +15 -0
  86. package/dist/components/pagination/usePagination.d.ts +3 -19
  87. package/dist/components/popover/usePopoverBindings.d.ts +16 -0
  88. package/dist/components/progress/progressModel.d.ts +8 -0
  89. package/dist/components/progress/useProgress.d.ts +0 -6
  90. package/dist/components/radio/useRadio.d.ts +1 -2
  91. package/dist/components/scroll-area/scroll-area.d.ts +1 -1
  92. package/dist/components/scroll-area/scrollAreaModel.d.ts +64 -0
  93. package/dist/components/scroll-area/useScrollArea.d.ts +70 -53
  94. package/dist/components/scroll-area/{useScrollAreaPresentation.d.ts → useScrollAreaAttributes.d.ts} +37 -17
  95. package/dist/components/scroll-area/useScrollAreaDerivedState.d.ts +8 -0
  96. package/dist/components/scroll-area/useScrollAreaMeasurement.d.ts +35 -0
  97. package/dist/components/scroll-area/useScrollAreaPointer.d.ts +10 -8
  98. package/dist/components/scroll-area/useScrollAreaVisibility.d.ts +15 -20
  99. package/dist/components/slider/rangeSliderModel.d.ts +41 -0
  100. package/dist/components/slider/{sliderCore.d.ts → sliderModel.d.ts} +0 -1
  101. package/dist/components/slider/useRangeSlider.d.ts +8 -16
  102. package/dist/components/slider/useRangeSliderValueState.d.ts +40 -0
  103. package/dist/components/slider/useSlider.d.ts +7 -13
  104. package/dist/components/slider/useSliderPointerPreview.d.ts +22 -0
  105. package/dist/components/slider/useSliderTooltipState.d.ts +40 -0
  106. package/dist/components/slider/useSliderValueState.d.ts +33 -0
  107. package/dist/components/teleport-provider/index.js +1 -1
  108. package/dist/components/teleport-provider/useTeleportTarget.d.ts +1 -2
  109. package/dist/components/toast/useToastLifecycle.d.ts +19 -0
  110. package/dist/components/tooltip/useTooltip.d.ts +1 -2
  111. package/dist/css.js +12 -0
  112. package/dist/dialog.js +226 -218
  113. package/dist/dropdown-menu.js +1458 -1097
  114. package/dist/events.js +59 -0
  115. package/dist/field.css +8 -8
  116. package/dist/field.js +13 -25
  117. package/dist/floating.js +548 -319
  118. package/dist/floatingOffset.js +11 -0
  119. package/dist/focus-trap.js +7 -5
  120. package/dist/focusNavigation.js +37 -0
  121. package/dist/icon-button.css +8 -8
  122. package/dist/icon-button.js +7 -6
  123. package/dist/index.js +6 -6
  124. package/dist/input.css +24 -24
  125. package/dist/input.js +19 -31
  126. package/dist/interactive.js +18 -0
  127. package/dist/modal.js +1 -1
  128. package/dist/number-input.css +18 -18
  129. package/dist/number-input.js +14 -12
  130. package/dist/number.js +22 -0
  131. package/dist/overlay.js +3 -2
  132. package/dist/pagination.css +28 -28
  133. package/dist/pagination.js +65 -63
  134. package/dist/pointer.js +9 -0
  135. package/dist/popover.css +34 -34
  136. package/dist/popover.js +85 -86
  137. package/dist/progress.js +10 -13
  138. package/dist/query.js +40 -0
  139. package/dist/radio.css +18 -18
  140. package/dist/radio.js +30 -36
  141. package/dist/rafScheduler.js +34 -0
  142. package/dist/scroll-area.css +28 -28
  143. package/dist/scroll-area.js +512 -516
  144. package/dist/select.css +45 -45
  145. package/dist/select.js +36 -44
  146. package/dist/slider.css +191 -191
  147. package/dist/slider.js +691 -618
  148. package/dist/styles-api.d.ts +0 -1
  149. package/dist/styles-api.js +1 -4
  150. package/dist/switch.css +17 -17
  151. package/dist/switch.js +14 -15
  152. package/dist/tabs.css +43 -43
  153. package/dist/tabs.js +29 -56
  154. package/dist/text.js +17 -0
  155. package/dist/textarea.css +26 -26
  156. package/dist/textarea.js +21 -32
  157. package/dist/toast.css +26 -26
  158. package/dist/toast.js +141 -118
  159. package/dist/tooltip.css +12 -12
  160. package/dist/tooltip.js +36 -40
  161. package/dist/useCollapse.js +8 -11
  162. package/dist/useControlState.js +3 -6
  163. package/dist/useFloatingPosition.js +140 -10
  164. package/dist/useOverlayZIndex.js +1 -5
  165. package/dist/useTeleportTarget.js +3 -14
  166. package/dist/useTypeahead.js +94 -0
  167. package/dist/utils/aria.d.ts +3 -0
  168. package/dist/utils/attributes.d.ts +2 -0
  169. package/dist/utils/bem.d.ts +3 -0
  170. package/dist/utils/collectionNavigation.d.ts +2 -0
  171. package/dist/utils/color.d.ts +7 -0
  172. package/dist/{components/color-picker/color-picker-utils.d.ts → utils/colorPicker.d.ts} +5 -4
  173. package/dist/utils/componentColors.d.ts +7 -0
  174. package/dist/utils/css.d.ts +3 -0
  175. package/dist/utils/dom/ancestry.d.ts +7 -0
  176. package/dist/utils/dom/attributes.d.ts +16 -0
  177. package/dist/utils/dom/componentRef.d.ts +4 -0
  178. package/dist/utils/dom/events.d.ts +5 -0
  179. package/dist/utils/dom/focusNavigation.d.ts +7 -0
  180. package/dist/utils/dom/interactive.d.ts +2 -0
  181. package/dist/utils/dom/pointer.d.ts +2 -0
  182. package/dist/utils/dom/query.d.ts +4 -0
  183. package/dist/utils/dom/scroll.d.ts +23 -0
  184. package/dist/utils/floatingOffset.d.ts +10 -0
  185. package/dist/utils/geometry.d.ts +14 -0
  186. package/dist/utils/indexPath.d.ts +6 -0
  187. package/dist/utils/number.d.ts +4 -0
  188. package/dist/utils/rafScheduler.d.ts +4 -0
  189. package/dist/utils/text.d.ts +2 -0
  190. package/docs/code-architecture.md +118 -0
  191. package/docs/public-floating-api.md +6 -5
  192. package/package.json +13 -12
  193. package/dist/components/dropdown-menu/dropdown-menu-outside.d.ts +0 -3
  194. package/dist/components/dropdown-menu/dropdown-menu-utils.d.ts +0 -21
  195. package/dist/components/dropdown-menu/useDropdownMenuDisclosure.d.ts +0 -36
  196. package/dist/components/dropdown-menu/useDropdownMenuDom.d.ts +0 -29
  197. package/dist/components/dropdown-menu/useDropdownMenuInteractions.d.ts +0 -52
  198. package/dist/components/dropdown-menu/useDropdownMenuNavigation.d.ts +0 -17
  199. package/dist/components/dropdown-menu/useDropdownSubmenus.d.ts +0 -18
  200. package/dist/components/scroll-area/scrollAreaCore.d.ts +0 -30
  201. package/dist/components/scroll-area/useScrollAreaControls.d.ts +0 -25
  202. package/dist/components/scroll-area/useScrollAreaMetrics.d.ts +0 -38
  203. /package/dist/components/alert/{useAlertColor.d.ts → alertColor.d.ts} +0 -0
  204. /package/dist/components/avatar/{useAvatarColor.d.ts → avatarColor.d.ts} +0 -0
  205. /package/dist/components/badge/{useBadgeColor.d.ts → badgeColor.d.ts} +0 -0
  206. /package/dist/components/button/{useButtonColor.d.ts → buttonColor.d.ts} +0 -0
  207. /package/dist/components/color-swatch/{useColorSwatchColor.d.ts → colorSwatchColor.d.ts} +0 -0
  208. /package/dist/components/toast/{useToastColor.d.ts → toastColor.d.ts} +0 -0
@@ -1,41 +1,49 @@
1
1
  import './dropdown-menu.css';
2
2
  import { t as useControllableValue } from "./useControllableValue.js";
3
- import { n as useStylesApi, t as presence } from "./styles-api.js";
4
- import { t as useRequiredInject } from "./useRequiredInject.js";
3
+ import { t as useStylesApi } from "./styles-api.js";
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
+ import { t as useRequiredInject } from "./useRequiredInject.js";
8
+ import { t as clamp } from "./number.js";
9
+ import { a as snapshotAttributes, i as restoreAttributes } from "./attributes2.js";
10
+ import { t as resolveHTMLElementRef } from "./componentRef.js";
11
+ import { n as isEventWithinElement, r as isEventWithinTargets, t as createCancelableCustomEvent } from "./events.js";
6
12
  import { n as useOverlayZIndex, r as useOverlayLayer } from "./useOverlayZIndex.js";
7
- import { n as useFloatingPosition, r as useFloatingTarget, t as isElementReference } from "./useFloatingPosition.js";
8
- import { n as useTeleportPositioningKey, r as useTeleportTarget, t as provideTeleportTarget } from "./useTeleportTarget.js";
9
- import { n as getNextEnabledIndex, r as useTypeahead, t as getEnabledIndexes } from "./collectionNavigation.js";
13
+ import { n as useFloatingTarget, t as useFloatingPosition } from "./useFloatingPosition.js";
14
+ import { n as useTeleportTarget, t as provideTeleportTarget } from "./useTeleportTarget.js";
15
+ import { t as useTypeahead } from "./useTypeahead.js";
10
16
  import { t as chevron_right_default } from "./chevron-right.js";
11
- 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, setStaticTemplateRef, setStyle, setTemplateRefBinding, setText, shallowRef, template, toDisplayString, txt, unref, useAttrs, useId, watch } from "vue";
12
- //#region src/components/dropdown-menu/dropdown-menu-primitive-core.ts
13
- var rootKey = Symbol("dropdown-menu-root");
14
- var menuKey = Symbol("dropdown-menu-content");
15
- var subKey = Symbol("dropdown-menu-sub");
16
- var radioGroupKey = Symbol("dropdown-menu-radio-group");
17
- var checkedKey = Symbol("dropdown-menu-checked");
18
- function optionalAttr(value) {
19
- return value || void 0;
17
+ 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
+ //#region src/utils/geometry.ts
19
+ function getClientPoint(event) {
20
+ return {
21
+ x: event.clientX,
22
+ y: event.clientY
23
+ };
20
24
  }
21
- function getFocusTarget(options) {
22
- if (typeof options === "string") return options;
23
- return options?.focus ?? "first";
25
+ function hasMeasuredRect(rect) {
26
+ return rect.width > 0 || rect.height > 0;
27
+ }
28
+ function isPointInRect(point, rect, padding = 0) {
29
+ return point.x >= rect.left - padding && point.x <= rect.right + padding && point.y >= rect.top - padding && point.y <= rect.bottom + padding;
30
+ }
31
+ function getTriangleArea(a, b, c) {
32
+ return Math.abs((a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2);
24
33
  }
25
- function toHTMLElement(value, fallbackId) {
26
- if (typeof HTMLElement !== "undefined" && value instanceof HTMLElement) return value;
27
- if (typeof HTMLElement === "undefined") return null;
28
- const vdomElement = value?.$el;
29
- if (vdomElement instanceof HTMLElement) return vdomElement;
30
- const vaporBlock = value?.block;
31
- if (vaporBlock instanceof HTMLElement) return vaporBlock;
32
- return fallbackId && typeof document !== "undefined" ? document.getElementById(fallbackId) : null;
34
+ function isPointInTriangle(point, triangle, tolerance = .5) {
35
+ const [a, b, c] = triangle;
36
+ const area = getTriangleArea(a, b, c);
37
+ const area1 = getTriangleArea(point, b, c);
38
+ const area2 = getTriangleArea(a, point, c);
39
+ const area3 = getTriangleArea(a, b, point);
40
+ return Math.abs(area - (area1 + area2 + area3)) < tolerance;
33
41
  }
34
- function resolveHTMLElementRef(value, fallbackId, resolve) {
35
- const element = toHTMLElement(value, fallbackId);
36
- resolve(element);
37
- if (element || !value) return;
38
- nextTick(() => resolve(toHTMLElement(value, fallbackId)));
42
+ function clampPointToRect(point, rect) {
43
+ return {
44
+ x: clamp(point.x, rect.left, rect.right),
45
+ y: clamp(point.y, rect.top, rect.bottom)
46
+ };
39
47
  }
40
48
  function createPointRect(point) {
41
49
  return {
@@ -50,202 +58,72 @@ function createPointRect(point) {
50
58
  toJSON: () => ({})
51
59
  };
52
60
  }
61
+ //#endregion
62
+ //#region src/components/dropdown-menu/dropdownMenuContext.ts
63
+ var rootKey = Symbol("dropdown-menu-root");
64
+ var menuKey = Symbol("dropdown-menu-content");
65
+ var subKey = Symbol("dropdown-menu-sub");
66
+ var radioGroupKey = Symbol("dropdown-menu-radio-group");
67
+ var checkedKey = Symbol("dropdown-menu-checked");
53
68
  function createVirtualAnchor(point) {
54
69
  return { getBoundingClientRect: () => createPointRect(point) };
55
70
  }
56
- function createCustomEvent(type, detail, originalEvent) {
57
- const target = originalEvent.target;
58
- return new (((target instanceof Node ? target.ownerDocument?.defaultView : null)?.CustomEvent) ?? CustomEvent)(type, {
59
- bubbles: false,
60
- cancelable: true,
61
- detail
62
- });
63
- }
64
71
  function createSelectEvent(originalEvent, value) {
65
- return createCustomEvent("dropdown-menu-select", {
72
+ return createCancelableCustomEvent("dropdown-menu-select", {
66
73
  originalEvent,
67
74
  value
68
75
  }, originalEvent);
69
76
  }
70
- function createOutsideEvent(originalEvent) {
71
- return createCustomEvent("dropdown-menu-interact-outside", { originalEvent }, originalEvent);
72
- }
73
- function sortItems(items) {
74
- return [...items].sort((left, right) => {
75
- const leftElement = left.element();
76
- const rightElement = right.element();
77
- if (!leftElement || !rightElement || leftElement === rightElement) return 0;
78
- const position = leftElement.compareDocumentPosition(rightElement);
79
- if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
80
- if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
81
- return 0;
82
- });
83
- }
84
- function getPlacementSide(placement) {
85
- return placement.split("-")[0];
86
- }
87
- function getOpenDirection(sub) {
88
- return getPlacementSide(sub.actualPlacement.value) === "left" ? "ArrowLeft" : "ArrowRight";
89
- }
90
- function getCloseDirection(menu) {
91
- return getPlacementSide(menu.actualPlacement.value) === "left" ? "ArrowRight" : "ArrowLeft";
92
- }
93
- function blockDocumentClick(event) {
94
- if (event.cancelable) event.preventDefault();
95
- event.stopPropagation();
96
- document.removeEventListener("click", blockDocumentClick, true);
97
- }
98
- function blockNextDocumentClick() {
99
- if (typeof document === "undefined") return;
100
- document.addEventListener("click", blockDocumentClick, true);
101
- window.setTimeout(() => document.removeEventListener("click", blockDocumentClick, true), 1e3);
102
- }
103
77
  function createMenuContext(options) {
104
- const items = shallowRef([]);
105
- const activeId = ref(null);
106
- const submenus = /* @__PURE__ */ new Set();
107
- function getEnabledItems() {
108
- return sortItems(items.value).filter((item) => !item.disabled());
109
- }
110
- function registerItem(item) {
111
- items.value = [...items.value.filter((current) => current.id !== item.id), item];
112
- if ((options.parentSub?.isOpen.value ?? options.root.isOpen.value) && activeId.value == null && !item.disabled()) {
113
- activeId.value = item.id;
114
- nextTick(() => {
115
- options.element.value?.focus();
116
- item.element()?.scrollIntoView?.({ block: "nearest" });
117
- });
118
- }
119
- }
120
- function unregisterItem(id) {
121
- items.value = items.value.filter((item) => item.id !== id);
122
- if (activeId.value === id) activeId.value = null;
123
- }
124
- function registerSub(sub) {
125
- submenus.add(sub);
126
- }
127
- function unregisterSub(sub) {
128
- submenus.delete(sub);
129
- }
130
- function closeSubmenus(except) {
131
- for (const submenu of submenus) if (submenu !== except) submenu.close(false);
132
- }
133
- function setActive(id) {
134
- const item = items.value.find((current) => current.id === id);
135
- if (!item || item.disabled()) return;
136
- activeId.value = id;
137
- nextTick(() => item.element()?.scrollIntoView?.({ block: "nearest" }));
138
- }
139
- function focus(target = "first") {
140
- const enabledItems = getEnabledItems();
141
- const item = target === "last" ? enabledItems[enabledItems.length - 1] : enabledItems[0];
142
- if (!item) {
143
- activeId.value = null;
144
- options.element.value?.focus();
145
- return false;
146
- }
147
- activeId.value = item.id;
148
- options.element.value?.focus();
149
- item.element()?.scrollIntoView?.({ block: "nearest" });
150
- return true;
151
- }
152
- function focusElement() {
153
- options.element.value?.focus();
154
- }
155
- function move(direction) {
156
- const enabledItems = getEnabledItems();
157
- if (enabledItems.length === 0) return;
158
- const currentIndex = enabledItems.findIndex((item) => item.id === activeId.value);
159
- const item = enabledItems[((currentIndex < 0 ? direction === 1 ? -1 : 0 : currentIndex) + direction + enabledItems.length) % enabledItems.length];
160
- if (item) setActive(item.id);
161
- }
162
- const typeahead = useTypeahead({
163
- items: () => sortItems(items.value),
164
- activeIndex: () => sortItems(items.value).findIndex((item) => item.id === activeId.value),
165
- getKey: (item) => item.id,
166
- getTextValue: (item) => item.textValue(),
167
- isDisabled: (item) => item.disabled(),
168
- onMatch: (item) => setActive(item.id)
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,
84
+ element: () => options.element.value,
85
+ placement: () => options.actualPlacement.value,
86
+ isOpen: () => options.parentSub?.isOpen.value ?? options.root.isOpen.value,
87
+ setOpen: options.parentSub?.setOpen,
88
+ stopKeyPropagation: true,
89
+ onEscape: options.onEscape
169
90
  });
170
- watch(computed(() => options.parentSub?.isOpen.value ?? options.root.isOpen.value), (open) => {
171
- if (!open) typeahead.reset();
172
- }, { flush: "sync" });
173
- function onHorizontalKeydown(event, active) {
174
- if (options.parentSub && event.key === getCloseDirection(context)) {
175
- event.preventDefault();
176
- event.stopPropagation();
177
- options.parentSub.close(true);
178
- return;
179
- }
180
- if (!active?.submenu || event.key !== getOpenDirection(active.submenu)) return;
181
- event.preventDefault();
182
- event.stopPropagation();
183
- active.submenu.open("first");
184
- }
185
- function onKeydown(event) {
186
- if (typeahead.handleKey(event)) {
187
- event.stopPropagation();
188
- return;
189
- }
190
- const active = items.value.find((item) => item.id === activeId.value);
191
- switch (event.key) {
192
- case "ArrowDown":
193
- event.preventDefault();
194
- event.stopPropagation();
195
- move(1);
196
- break;
197
- case "ArrowUp":
198
- event.preventDefault();
199
- event.stopPropagation();
200
- move(-1);
201
- break;
202
- case "Home":
203
- event.preventDefault();
204
- event.stopPropagation();
205
- focus("first");
206
- break;
207
- case "End":
208
- event.preventDefault();
209
- event.stopPropagation();
210
- focus("last");
211
- break;
212
- case "ArrowRight":
213
- case "ArrowLeft":
214
- onHorizontalKeydown(event, active);
215
- break;
216
- case "Enter":
217
- case " ":
218
- event.preventDefault();
219
- event.stopPropagation();
220
- active?.activate(event);
221
- break;
222
- case "Escape":
223
- event.preventDefault();
224
- event.stopPropagation();
225
- options.onEscape(event);
226
- break;
227
- case "Tab":
228
- options.root.close({ returnFocus: false });
229
- break;
230
- }
91
+ function registerItem(item) {
92
+ interaction.registerItem({
93
+ get id() {
94
+ return item.id;
95
+ },
96
+ menuId: id,
97
+ element: item.element,
98
+ textValue: item.textValue,
99
+ disabled: item.disabled,
100
+ submenuId: () => item.submenu?.menuId,
101
+ submenuDirection: () => item.submenu?.actualPlacement.value.startsWith("left") ? "left" : "right",
102
+ select: item.activate,
103
+ closeOnSelect: item.closeOnSelect
104
+ });
231
105
  }
232
106
  const context = {
233
107
  root: options.root,
108
+ id,
234
109
  element: options.element,
235
110
  activeId,
236
111
  actualPlacement: options.actualPlacement,
237
112
  parentSub: options.parentSub,
238
113
  registerItem,
239
- unregisterItem,
240
- registerSub,
241
- unregisterSub,
242
- closeSubmenus,
243
- setActive,
244
- isActive: (id) => activeId.value === id,
245
- focus,
246
- focusElement,
247
- onKeydown
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)
248
125
  };
126
+ onBeforeUnmount(cleanupMenu);
249
127
  return context;
250
128
  }
251
129
  function usePrimitiveItem(componentName, props, emitSelect, options) {
@@ -255,12 +133,12 @@ function usePrimitiveItem(componentName, props, emitSelect, options) {
255
133
  const element = ref(null);
256
134
  const isDisabled = computed(() => Boolean(menu.root.disabled.value || props.disabled));
257
135
  const focused = computed(() => menu.isActive(id.value));
258
- function activate(originalEvent) {
259
- if (isDisabled.value) return;
136
+ function emitSelection(originalEvent) {
137
+ if (isDisabled.value) return void 0;
260
138
  const selectEvent = createSelectEvent(originalEvent, props.value);
261
139
  emitSelect(selectEvent);
262
140
  options.afterSelect?.();
263
- if (!selectEvent.defaultPrevented && (props.closeOnSelect ?? options.defaultCloseOnSelect)) menu.root.close({ returnFocus: true });
141
+ return selectEvent;
264
142
  }
265
143
  const registration = {
266
144
  get id() {
@@ -269,27 +147,33 @@ function usePrimitiveItem(componentName, props, emitSelect, options) {
269
147
  element: () => element.value,
270
148
  textValue: () => props.textValue ?? element.value?.textContent?.trim() ?? "",
271
149
  disabled: () => isDisabled.value,
272
- activate
150
+ activate: emitSelection,
151
+ closeOnSelect: () => props.closeOnSelect ?? options.defaultCloseOnSelect
273
152
  };
274
153
  menu.registerItem(registration);
275
154
  watch(id, (nextId, previousId) => {
155
+ const wasActive = menu.activeId.value === previousId;
276
156
  menu.unregisterItem(previousId);
277
157
  menu.registerItem(registration);
278
- if (menu.activeId.value === previousId) menu.activeId.value = nextId;
279
- });
158
+ if (wasActive) menu.setActive(nextId);
159
+ }, { flush: "sync" });
160
+ watch(isDisabled, () => menu.root.interaction.reconcile(menu.id));
280
161
  onBeforeUnmount(() => menu.unregisterItem(id.value));
281
162
  function setElement(value) {
282
163
  resolveHTMLElementRef(value, id.value, (resolved) => {
283
164
  element.value = resolved;
165
+ menu.root.interaction.reconcile(menu.id);
284
166
  });
285
167
  }
168
+ function activate(originalEvent) {
169
+ menu.activate(id.value, originalEvent);
170
+ }
286
171
  function onPointerenter() {
287
172
  if (isDisabled.value) return;
288
- menu.closeSubmenus();
289
- menu.setActive(id.value);
173
+ menu.hover(id.value);
290
174
  }
291
175
  function select() {
292
- activate(new Event("select"));
176
+ menu.activate(id.value, new Event("select"));
293
177
  }
294
178
  return {
295
179
  menu,
@@ -369,12 +253,12 @@ var dropdown_menu_checkbox_item_default = /* @__PURE__ */ defineVaporComponent({
369
253
  type: props.as === "button" ? "button" : void 0,
370
254
  role: "menuitemcheckbox",
371
255
  tabindex: -1,
372
- disabled: props.as === "button" ? optionalAttr(isDisabled.value) : void 0,
373
- "aria-disabled": optionalAttr(isDisabled.value),
256
+ disabled: props.as === "button" ? toOptionalAttribute(isDisabled.value) : void 0,
257
+ "aria-disabled": toOptionalAttribute(isDisabled.value),
374
258
  "aria-checked": checked.value,
375
- "data-disabled": optionalAttr(isDisabled.value),
376
- "data-focused": optionalAttr(focused.value),
377
- "data-highlighted": optionalAttr(focused.value),
259
+ "data-disabled": toOptionalAttribute(isDisabled.value),
260
+ "data-focused": toOptionalAttribute(focused.value),
261
+ "data-highlighted": toOptionalAttribute(focused.value),
378
262
  "data-state": state.value,
379
263
  class: bem("rp-dropdown-menu__item", {
380
264
  focused: focused.value,
@@ -396,31 +280,6 @@ var dropdown_menu_checkbox_item_default = /* @__PURE__ */ defineVaporComponent({
396
280
  }
397
281
  });
398
282
  //#endregion
399
- //#region src/components/dropdown-menu/dropdown-menu-outside.ts
400
- function isNode(value) {
401
- return typeof value === "object" && value !== null && "nodeType" in value;
402
- }
403
- function isEventWithinElement(event, element) {
404
- const path = event.composedPath();
405
- if (path.length > 0) return path.some((entry) => entry === element || isNode(entry) && element.contains(entry));
406
- return isNode(event.target) && element.contains(event.target);
407
- }
408
- function isEventWithinTargets(event, targets) {
409
- for (const source of targets) {
410
- const target = isRef(source) ? source.value : source;
411
- if (!target) continue;
412
- if (typeof target === "string") {
413
- if (typeof document === "undefined") continue;
414
- try {
415
- if ([...document.querySelectorAll(target)].some((element) => isEventWithinElement(event, element))) return true;
416
- } catch {}
417
- continue;
418
- }
419
- if (isEventWithinElement(event, target)) return true;
420
- }
421
- return false;
422
- }
423
- //#endregion
424
283
  //#region src/components/dropdown-menu/useDropdownMenuPrimitiveContent.ts
425
284
  function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
426
285
  const componentName = submenu ? "RpDropdownMenuSubContent" : "RpDropdownMenuContent";
@@ -434,20 +293,23 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
434
293
  const actualPlacement = ref(props.placement ?? defaultPlacement);
435
294
  const isOpen = computed(() => sub ? sub.isOpen.value : root.isOpen.value);
436
295
  const reference = computed(() => sub ? sub.trigger.value : root.reference.value);
437
- const teleportPositioningKey = useTeleportPositioningKey();
438
296
  const menu = createMenuContext({
439
297
  root,
440
298
  element,
441
299
  actualPlacement,
442
300
  parentSub: sub,
443
301
  onEscape(event) {
444
- const customEvent = createCustomEvent("dropdown-menu-escape-key-down", { originalEvent: event }, event);
302
+ const customEvent = createCancelableCustomEvent("dropdown-menu-escape-key-down", { originalEvent: event }, event);
445
303
  events.escapeKeyDown(customEvent);
446
- if (customEvent.defaultPrevented) return;
447
- if (sub) sub.close(true);
448
- else root.close({ returnFocus: true });
304
+ return !customEvent.defaultPrevented;
449
305
  }
450
306
  });
307
+ const cleanupDismissal = submenu ? void 0 : root.interaction.registerDismissal({
308
+ ignoredTargets: () => props.ignore ?? [],
309
+ pointerDownOutside: events.pointerDownOutside,
310
+ focusOutside: events.focusOutside,
311
+ interactOutside: events.interactOutside
312
+ });
451
313
  const floating = useFloatingPosition({
452
314
  reference,
453
315
  floating: element,
@@ -460,8 +322,7 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
460
322
  flipOptions: () => props.flipOptions,
461
323
  shift: () => props.shift !== false,
462
324
  collisionPadding: () => props.collisionPadding ?? 8,
463
- autoUpdateOptions: () => props.autoUpdateOptions,
464
- restartKey: teleportPositioningKey
325
+ autoUpdateOptions: () => props.autoUpdateOptions
465
326
  });
466
327
  watch(floating.actualPlacement, (value) => {
467
328
  actualPlacement.value = value;
@@ -482,44 +343,9 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
482
343
  else if (resolved) layerBranchCleanup = root.layer.registerBranch(resolved);
483
344
  });
484
345
  }
485
- function emitOutside(type, originalEvent) {
486
- const outsideEvent = createOutsideEvent(originalEvent);
487
- if (type === "pointer") events.pointerDownOutside(outsideEvent);
488
- else events.focusOutside(outsideEvent);
489
- events.interactOutside(outsideEvent);
490
- return outsideEvent;
491
- }
492
- function onDocumentPointer(event) {
493
- if (!root.layer.isTopLayer() || root.isInside(event) || isEventWithinTargets(event, props.ignore ?? [])) return;
494
- const outsideEvent = emitOutside("pointer", event);
495
- if (root.modal.value) {
496
- if (event.cancelable) event.preventDefault();
497
- event.stopPropagation();
498
- if (event.type === "pointerdown") blockNextDocumentClick();
499
- }
500
- if (!outsideEvent.defaultPrevented) root.close({ returnFocus: root.modal.value });
501
- }
502
- function onDocumentFocus(event) {
503
- if (!root.layer.isTopLayer() || root.isInside(event) || isEventWithinTargets(event, props.ignore ?? [])) return;
504
- const outsideEvent = emitOutside("focus", event);
505
- if (root.modal.value) {
506
- if (event.cancelable) event.preventDefault();
507
- event.stopPropagation();
508
- }
509
- if (!outsideEvent.defaultPrevented) root.close({ returnFocus: root.modal.value });
510
- else if (root.modal.value) nextTick(menu.focusElement);
511
- }
512
- function setDocumentListeners(active) {
513
- if (submenu || typeof document === "undefined") return;
514
- const method = active ? "addEventListener" : "removeEventListener";
515
- document[method]("pointerdown", onDocumentPointer, true);
516
- document[method]("focusin", onDocumentFocus, true);
517
- }
518
346
  watch(isOpen, (open) => {
519
- setDocumentListeners(open);
520
347
  if (!open) {
521
- menu.activeId.value = null;
522
- menu.closeSubmenus();
348
+ if (sub) root.interaction.closeMenu(menu.id, false);
523
349
  return;
524
350
  }
525
351
  nextTick(() => {
@@ -536,7 +362,7 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
536
362
  if (isOpen.value) menu.focus((sub ? sub.pendingFocus.value : root.pendingFocus.value) || "first");
537
363
  });
538
364
  onBeforeUnmount(() => {
539
- setDocumentListeners(false);
365
+ cleanupDismissal?.();
540
366
  layerBranchCleanup?.();
541
367
  if (element.value) root.unregisterInside(element.value);
542
368
  if (!sub && root.layer.element.value === element.value) root.layer.element.value = null;
@@ -549,7 +375,7 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
549
375
  id: id.value,
550
376
  role: "menu",
551
377
  tabindex: -1,
552
- hidden: optionalAttr(!isOpen.value),
378
+ hidden: toOptionalAttribute(!isOpen.value),
553
379
  class: submenu ? bem("rp-dropdown-menu__submenu", {
554
380
  compound: true,
555
381
  open: isOpen.value
@@ -644,15 +470,16 @@ var dropdown_menu_content_default = /* @__PURE__ */ defineVaporComponent({
644
470
  focusOutside: (event) => emit("focusOutside", event),
645
471
  interactOutside: (event) => emit("interactOutside", event)
646
472
  }, false);
473
+ const templateRefs = { arrow: arrowElement };
647
474
  const _setTemplateRef = createTemplateRefSetter();
648
475
  return createIf(() => unref(shouldRender), () => {
649
476
  const n6 = createDynamicComponent(() => __props.as, { $: [() => unref(rootAttrs)] }, extend(() => {
650
477
  return [createIf(() => __props.arrow, () => {
651
478
  const n4 = t0$2();
652
- _setTemplateRef(n4, arrowElement, null, "arrowElement");
653
479
  renderEffect(() => {
654
480
  setAttr(n4, "data-side", unref(placementSide));
655
481
  setStyle(n4, unref(floating).arrowStyle.value);
482
+ _setTemplateRef(n4, templateRefs.arrow);
656
483
  });
657
484
  return n4;
658
485
  }, null, 129), createSlot("default", {
@@ -667,6 +494,117 @@ var dropdown_menu_content_default = /* @__PURE__ */ defineVaporComponent({
667
494
  }
668
495
  });
669
496
  //#endregion
497
+ //#region src/components/dropdown-menu/useDropdownMenuContextTrigger.ts
498
+ function useDropdownMenuContextTrigger(props) {
499
+ const attrs = useAttrs();
500
+ const root = useRequiredInject(rootKey, "RpDropdownMenuContextTrigger");
501
+ const generatedId = useId();
502
+ const id = computed(() => props.id ?? `${generatedId}-context-trigger`);
503
+ const element = ref(null);
504
+ const isDisabled = computed(() => root.disabled.value || props.disabled);
505
+ let timer;
506
+ let pointerId;
507
+ let startPoint;
508
+ let longPressOpened = false;
509
+ let suppressUntil = 0;
510
+ function setElement(value) {
511
+ resolveHTMLElementRef(value, id.value, (resolved) => {
512
+ const previous = element.value;
513
+ if (previous) root.unregisterInside(previous);
514
+ element.value = resolved;
515
+ if (resolved) root.registerInside(resolved);
516
+ if (!root.triggerId.value) root.triggerId.value = id.value;
517
+ });
518
+ }
519
+ function clearLongPress() {
520
+ if (timer) clearTimeout(timer);
521
+ timer = void 0;
522
+ pointerId = void 0;
523
+ startPoint = void 0;
524
+ element.value?.ownerDocument.defaultView?.removeEventListener("scroll", clearLongPress, true);
525
+ }
526
+ function openAt(point) {
527
+ root.setReturnFocus(element.value);
528
+ root.openAt(point);
529
+ }
530
+ function onContextmenu(event) {
531
+ if (isDisabled.value) return;
532
+ event.preventDefault();
533
+ if (Date.now() < suppressUntil) return;
534
+ openAt({
535
+ x: event.clientX,
536
+ y: event.clientY
537
+ });
538
+ }
539
+ function onKeydown(event) {
540
+ if (isDisabled.value) return;
541
+ if (event.key !== "ContextMenu" && !(event.shiftKey && event.key === "F10")) return;
542
+ event.preventDefault();
543
+ const rect = element.value?.getBoundingClientRect();
544
+ openAt({
545
+ x: rect?.left ?? 0,
546
+ y: rect?.bottom ?? 0
547
+ });
548
+ }
549
+ function onPointerdown(event) {
550
+ if (isDisabled.value || event.pointerType !== "touch" && event.pointerType !== "pen") return;
551
+ clearLongPress();
552
+ pointerId = event.pointerId;
553
+ startPoint = {
554
+ x: event.clientX,
555
+ y: event.clientY
556
+ };
557
+ longPressOpened = false;
558
+ (element.value?.ownerDocument.defaultView)?.addEventListener("scroll", clearLongPress, true);
559
+ timer = setTimeout(() => {
560
+ if (!startPoint) return;
561
+ longPressOpened = true;
562
+ suppressUntil = Date.now() + 1e3;
563
+ openAt(startPoint);
564
+ timer = void 0;
565
+ }, props.longPressDelay);
566
+ }
567
+ function onPointermove(event) {
568
+ if (event.pointerId !== pointerId || !startPoint) return;
569
+ if (Math.hypot(event.clientX - startPoint.x, event.clientY - startPoint.y) > (props.longPressTolerance ?? 0)) clearLongPress();
570
+ }
571
+ function finishPointer(event) {
572
+ if (event.pointerId !== pointerId) return;
573
+ if (longPressOpened) event.preventDefault();
574
+ clearLongPress();
575
+ }
576
+ function onClick(event) {
577
+ if (Date.now() >= suppressUntil) return;
578
+ event.preventDefault();
579
+ event.stopPropagation();
580
+ }
581
+ const rootAttrs = computed(() => mergeProps(attrs, {
582
+ id: id.value,
583
+ "aria-controls": root.contentId.value,
584
+ "aria-haspopup": "menu",
585
+ "aria-disabled": toOptionalAttribute(isDisabled.value),
586
+ "data-state": root.isOpen.value ? "open" : "closed",
587
+ "data-disabled": toOptionalAttribute(isDisabled.value),
588
+ onClick,
589
+ onContextmenu,
590
+ onKeydown,
591
+ onPointerdown,
592
+ onPointermove,
593
+ onPointerup: finishPointer,
594
+ onPointercancel: finishPointer
595
+ }));
596
+ onBeforeUnmount(() => {
597
+ clearLongPress();
598
+ if (element.value) root.unregisterInside(element.value);
599
+ if (root.triggerId.value === id.value) root.triggerId.value = void 0;
600
+ });
601
+ return {
602
+ isOpen: root.isOpen,
603
+ rootAttrs,
604
+ setElement
605
+ };
606
+ }
607
+ //#endregion
670
608
  //#region src/components/dropdown-menu/dropdown-menu-context-trigger.vue
671
609
  var dropdown_menu_context_trigger_default = /* @__PURE__ */ defineVaporComponent({
672
610
  name: "RpDropdownMenuContextTrigger",
@@ -683,113 +621,11 @@ var dropdown_menu_context_trigger_default = /* @__PURE__ */ defineVaporComponent
683
621
  longPressTolerance: { default: 10 }
684
622
  },
685
623
  setup(__props) {
686
- const props = __props;
687
- const attrs = useAttrs();
688
- const root = useRequiredInject(rootKey, "RpDropdownMenuContextTrigger");
689
- const generatedId = useId();
690
- const id = computed(() => props.id ?? `${generatedId}-context-trigger`);
691
- const element = ref(null);
692
- const isDisabled = computed(() => root.disabled.value || props.disabled);
693
- let timer;
694
- let pointerId;
695
- let startPoint;
696
- let longPressOpened = false;
697
- let suppressUntil = 0;
698
- function setElement(value) {
699
- resolveHTMLElementRef(value, id.value, (resolved) => {
700
- const previous = element.value;
701
- if (previous) root.unregisterInside(previous);
702
- element.value = resolved;
703
- if (resolved) root.registerInside(resolved);
704
- if (!root.triggerId.value) root.triggerId.value = id.value;
705
- });
706
- }
707
- function clearLongPress() {
708
- if (timer) clearTimeout(timer);
709
- timer = void 0;
710
- pointerId = void 0;
711
- startPoint = void 0;
712
- element.value?.ownerDocument.defaultView?.removeEventListener("scroll", clearLongPress, true);
713
- }
714
- function openAt(point) {
715
- root.setReturnFocus(element.value);
716
- root.openAt(point);
717
- }
718
- function onContextmenu(event) {
719
- if (isDisabled.value) return;
720
- event.preventDefault();
721
- if (Date.now() < suppressUntil) return;
722
- openAt({
723
- x: event.clientX,
724
- y: event.clientY
725
- });
726
- }
727
- function onKeydown(event) {
728
- if (isDisabled.value) return;
729
- if (event.key !== "ContextMenu" && !(event.shiftKey && event.key === "F10")) return;
730
- event.preventDefault();
731
- const rect = element.value?.getBoundingClientRect();
732
- openAt({
733
- x: rect?.left ?? 0,
734
- y: rect?.bottom ?? 0
735
- });
736
- }
737
- function onPointerdown(event) {
738
- if (isDisabled.value || event.pointerType !== "touch" && event.pointerType !== "pen") return;
739
- clearLongPress();
740
- pointerId = event.pointerId;
741
- startPoint = {
742
- x: event.clientX,
743
- y: event.clientY
744
- };
745
- longPressOpened = false;
746
- (element.value?.ownerDocument.defaultView)?.addEventListener("scroll", clearLongPress, true);
747
- timer = setTimeout(() => {
748
- if (!startPoint) return;
749
- longPressOpened = true;
750
- suppressUntil = Date.now() + 1e3;
751
- openAt(startPoint);
752
- timer = void 0;
753
- }, props.longPressDelay);
754
- }
755
- function onPointermove(event) {
756
- if (event.pointerId !== pointerId || !startPoint) return;
757
- if (Math.hypot(event.clientX - startPoint.x, event.clientY - startPoint.y) > props.longPressTolerance) clearLongPress();
758
- }
759
- function finishPointer(event) {
760
- if (event.pointerId !== pointerId) return;
761
- if (longPressOpened) event.preventDefault();
762
- clearLongPress();
763
- }
764
- function onClick(event) {
765
- if (Date.now() >= suppressUntil) return;
766
- event.preventDefault();
767
- event.stopPropagation();
768
- }
769
- const rootAttrs = computed(() => mergeProps(attrs, {
770
- id: id.value,
771
- "aria-controls": root.contentId.value,
772
- "aria-haspopup": "menu",
773
- "aria-disabled": optionalAttr(isDisabled.value),
774
- "data-state": root.isOpen.value ? "open" : "closed",
775
- "data-disabled": optionalAttr(isDisabled.value),
776
- onClick,
777
- onContextmenu,
778
- onKeydown,
779
- onPointerdown,
780
- onPointermove,
781
- onPointerup: finishPointer,
782
- onPointercancel: finishPointer
783
- }));
784
- onBeforeUnmount(() => {
785
- clearLongPress();
786
- if (element.value) root.unregisterInside(element.value);
787
- if (root.triggerId.value === id.value) root.triggerId.value = void 0;
788
- });
789
- const n1 = createDynamicComponent(() => __props.as, { $: [() => rootAttrs.value] }, extend(() => {
790
- return createSlot("default", { isOpen: () => unref(root).isOpen.value });
624
+ const { isOpen, rootAttrs, setElement } = useDropdownMenuContextTrigger(__props);
625
+ const n1 = createDynamicComponent(() => __props.as, { $: [() => unref(rootAttrs)] }, extend(() => {
626
+ return createSlot("default", { isOpen: () => unref(isOpen) });
791
627
  }, { _: 8 }), 1);
792
- setTemplateRefBinding(n1, () => setElement);
628
+ setTemplateRefBinding(n1, () => setElement, void 0, void 0, "setElement");
793
629
  return n1;
794
630
  }
795
631
  });
@@ -831,11 +667,11 @@ var dropdown_menu_item_default = /* @__PURE__ */ defineVaporComponent({
831
667
  type: props.as === "button" ? "button" : void 0,
832
668
  role: "menuitem",
833
669
  tabindex: -1,
834
- disabled: props.as === "button" ? optionalAttr(isDisabled.value) : void 0,
835
- "aria-disabled": optionalAttr(isDisabled.value),
836
- "data-disabled": optionalAttr(isDisabled.value),
837
- "data-focused": optionalAttr(focused.value),
838
- "data-highlighted": optionalAttr(focused.value),
670
+ disabled: props.as === "button" ? toOptionalAttribute(isDisabled.value) : void 0,
671
+ "aria-disabled": toOptionalAttribute(isDisabled.value),
672
+ "data-disabled": toOptionalAttribute(isDisabled.value),
673
+ "data-focused": toOptionalAttribute(focused.value),
674
+ "data-highlighted": toOptionalAttribute(focused.value),
839
675
  class: bem("rp-dropdown-menu__item", {
840
676
  focused: focused.value,
841
677
  disabled: isDisabled.value,
@@ -931,7 +767,7 @@ var dropdown_menu_portal_default = /* @__PURE__ */ defineVaporComponent({
931
767
  const props = __props;
932
768
  const teleportTo = provideTeleportTarget({ get teleportTo() {
933
769
  return props.to ?? props.teleportTo;
934
- } }, () => Boolean(props.teleport && !props.disabled));
770
+ } });
935
771
  return createComponent(VaporTeleport, {
936
772
  to: () => unref(teleportTo),
937
773
  disabled: () => __props.disabled || !__props.teleport
@@ -1038,12 +874,12 @@ var dropdown_menu_radio_item_default = /* @__PURE__ */ defineVaporComponent({
1038
874
  type: props.as === "button" ? "button" : void 0,
1039
875
  role: "menuitemradio",
1040
876
  tabindex: -1,
1041
- disabled: props.as === "button" ? optionalAttr(isDisabled.value) : void 0,
1042
- "aria-disabled": optionalAttr(isDisabled.value),
877
+ disabled: props.as === "button" ? toOptionalAttribute(isDisabled.value) : void 0,
878
+ "aria-disabled": toOptionalAttribute(isDisabled.value),
1043
879
  "aria-checked": checked.value,
1044
- "data-disabled": optionalAttr(isDisabled.value),
1045
- "data-focused": optionalAttr(focused.value),
1046
- "data-highlighted": optionalAttr(focused.value),
880
+ "data-disabled": toOptionalAttribute(isDisabled.value),
881
+ "data-focused": toOptionalAttribute(focused.value),
882
+ "data-highlighted": toOptionalAttribute(focused.value),
1047
883
  "data-state": state.value,
1048
884
  class: bem("rp-dropdown-menu__item", {
1049
885
  focused: focused.value,
@@ -1065,21 +901,657 @@ var dropdown_menu_radio_item_default = /* @__PURE__ */ defineVaporComponent({
1065
901
  }
1066
902
  });
1067
903
  //#endregion
1068
- //#region src/components/dropdown-menu/dropdown-menu-root.vue
1069
- var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1070
- name: "RpDropdownMenuRoot",
1071
- __name: "dropdown-menu-root",
1072
- props: {
1073
- id: {},
1074
- open: {
1075
- type: Boolean,
1076
- default: void 0
1077
- },
1078
- defaultOpen: {
1079
- type: Boolean,
1080
- default: false
1081
- },
1082
- baseZIndex: {},
904
+ //#region src/components/dropdown-menu/dropdownMenuInteractionNavigation.ts
905
+ function createDropdownMenuInteractionNavigation({ host, registry, closeRoot }) {
906
+ const state = registry.state;
907
+ function closeMenu(menuId, focusParent = false) {
908
+ const menu = registry.getMenu(menuId);
909
+ const menuState = state.getMenuState(menuId);
910
+ if (!menu && !menuState || menuId === host.rootMenuId) return false;
911
+ const wasOpen = registry.isMenuOpen(menuId);
912
+ closeSubmenus(menuId);
913
+ const shouldRestoreParent = focusParent || registry.activeMenuId.value === menuId;
914
+ if (wasOpen) state.setMenuOpen(menuId, false);
915
+ state.clearPendingMenuFocus(menuId);
916
+ state.clearMenuState(menuId);
917
+ if (shouldRestoreParent) {
918
+ const parentItem = state.getParentItem(menuId);
919
+ if (!parentItem || !registry.setActive(parentItem.id, focusParent)) {
920
+ const parentMenuId = parentItem?.menuId ?? host.rootMenuId;
921
+ registry.activeMenuId.value = parentMenuId;
922
+ registry.activeItemId.value = state.getActiveIdValue(parentMenuId);
923
+ if (focusParent) registry.focusMenuElement(parentMenuId);
924
+ }
925
+ }
926
+ return wasOpen;
927
+ }
928
+ function closeSubmenus(menuId, exceptMenuId) {
929
+ for (const item of state.getItems(menuId)) {
930
+ const submenuId = item.submenuId?.();
931
+ if (!submenuId || submenuId === exceptMenuId) continue;
932
+ closeMenu(submenuId, false);
933
+ }
934
+ }
935
+ function closeAllSubmenus() {
936
+ closeSubmenus(host.rootMenuId);
937
+ for (const menuId of state.getMenuIds()) if (menuId !== host.rootMenuId && registry.isMenuOpen(menuId)) closeMenu(menuId, false);
938
+ }
939
+ function focusMenu(menuId, target = "first") {
940
+ const enabledItems = state.getEnabledItems(menuId);
941
+ const item = target === "last" ? enabledItems.at(-1) : enabledItems[0];
942
+ registry.focusMenuElement(menuId);
943
+ if (!item) {
944
+ state.clearMenuState(menuId);
945
+ registry.activeMenuId.value = menuId;
946
+ const parentItem = state.getParentItem(menuId);
947
+ if (parentItem) registry.activeItemId.value = parentItem.id;
948
+ return false;
949
+ }
950
+ return registry.setActive(item.id);
951
+ }
952
+ function openMenu(menuId, focus = "first") {
953
+ const menu = registry.getMenu(menuId);
954
+ const menuState = state.getMenuState(menuId);
955
+ if (!menu && !menuState || menuId === host.rootMenuId) return false;
956
+ const parentItem = state.getParentItem(menuId);
957
+ if (!parentItem || parentItem.disabled()) return false;
958
+ closeSubmenus(parentItem.menuId, menuId);
959
+ state.setPendingMenuFocus(menuId, focus);
960
+ state.setMenuOpen(menuId, true);
961
+ if (focus && menu) focusMenu(menuId, focus);
962
+ return true;
963
+ }
964
+ function selectItem(id, originalEvent = new Event("select")) {
965
+ const item = registry.getItem(id);
966
+ if (!item || item.disabled()) return;
967
+ const selectEvent = item.select?.(originalEvent);
968
+ if (selectEvent && !selectEvent.defaultPrevented && (item.closeOnSelect?.() ?? true)) closeRoot({ focusTrigger: true });
969
+ }
970
+ function activateItem(id, originalEvent = new Event("select")) {
971
+ const item = registry.getItem(id);
972
+ if (!item || item.disabled()) return;
973
+ const submenuId = item.submenuId?.();
974
+ if (submenuId) {
975
+ openMenu(submenuId, "first");
976
+ return;
977
+ }
978
+ selectItem(id, originalEvent);
979
+ }
980
+ function hoverItem(id, openSubmenu = false) {
981
+ const item = registry.getItem(id);
982
+ if (!item || item.disabled()) return;
983
+ registry.setActive(id);
984
+ const submenuId = item.submenuId?.();
985
+ if (openSubmenu && submenuId) openMenu(submenuId, false);
986
+ else closeSubmenus(item.menuId, submenuId && registry.isMenuOpen(submenuId) ? submenuId : void 0);
987
+ }
988
+ function reconcileOpenSubmenus() {
989
+ for (const menuId of state.getMenuIds()) {
990
+ if (menuId === host.rootMenuId || !registry.isMenuOpen(menuId)) continue;
991
+ const parentItem = state.getParentItem(menuId);
992
+ if (!parentItem || parentItem.disabled()) closeMenu(menuId, false);
993
+ }
994
+ }
995
+ function reconcile(menuId) {
996
+ const menuIds = menuId ? [menuId] : state.getRegisteredMenuIds();
997
+ for (const id of menuIds) {
998
+ const activeId = state.getActiveIdValue(id);
999
+ if (!activeId) continue;
1000
+ const currentItems = state.getItems(id);
1001
+ const activeIndex = currentItems.findIndex((item) => item.id === activeId);
1002
+ const activeItem = currentItems[activeIndex];
1003
+ if (activeItem && !activeItem.disabled()) continue;
1004
+ state.chooseNeighbor(id, Math.max(0, activeIndex));
1005
+ }
1006
+ reconcileOpenSubmenus();
1007
+ }
1008
+ return {
1009
+ closeMenu,
1010
+ closeSubmenus,
1011
+ closeAllSubmenus,
1012
+ focusMenu,
1013
+ openMenu,
1014
+ selectItem,
1015
+ activateItem,
1016
+ hoverItem,
1017
+ reconcile,
1018
+ reconcileOpenSubmenus
1019
+ };
1020
+ }
1021
+ //#endregion
1022
+ //#region src/components/dropdown-menu/dropdownMenuInteractionRegistry.ts
1023
+ function compareItems(left, right) {
1024
+ const leftElement = left.element();
1025
+ const rightElement = right.element();
1026
+ if (leftElement && rightElement && leftElement !== rightElement && typeof Node !== "undefined") {
1027
+ const position = leftElement.compareDocumentPosition(rightElement);
1028
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
1029
+ if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
1030
+ }
1031
+ return (left.order?.() ?? 0) - (right.order?.() ?? 0);
1032
+ }
1033
+ function scrollItemIntoView(item) {
1034
+ nextTick(() => item.element()?.scrollIntoView?.({ block: "nearest" }));
1035
+ }
1036
+ function createDropdownMenuInteractionRegistry(host, navigation) {
1037
+ const menus = /* @__PURE__ */ new Map();
1038
+ const menuStates = /* @__PURE__ */ new Map();
1039
+ const items = /* @__PURE__ */ new Map();
1040
+ const activeIds = ref(/* @__PURE__ */ new Map());
1041
+ const activeItemId = ref(null);
1042
+ const activeMenuId = ref(host.rootMenuId);
1043
+ const pendingRootFocus = ref("first");
1044
+ const pendingMenuFocus = /* @__PURE__ */ new Map();
1045
+ function getMenu(id) {
1046
+ return menus.get(id);
1047
+ }
1048
+ function getMenuState(id) {
1049
+ return menuStates.get(id);
1050
+ }
1051
+ function getItem(id) {
1052
+ return items.get(id);
1053
+ }
1054
+ function getItems(menuId) {
1055
+ return [...items.values()].filter((item) => item.menuId === menuId).sort(compareItems);
1056
+ }
1057
+ function getEnabledItems(menuId) {
1058
+ return getItems(menuId).filter((item) => !item.disabled());
1059
+ }
1060
+ function getActiveIdValue(menuId) {
1061
+ return activeIds.value.get(menuId) ?? null;
1062
+ }
1063
+ function setActiveId(menuId, id) {
1064
+ const next = new Map(activeIds.value);
1065
+ if (id) next.set(menuId, id);
1066
+ else next.delete(menuId);
1067
+ activeIds.value = next;
1068
+ }
1069
+ function getActiveId(menuId) {
1070
+ return computed(() => getActiveIdValue(menuId));
1071
+ }
1072
+ function isMenuOpen(menuId) {
1073
+ if (menuId === host.rootMenuId) return host.isOpen.value;
1074
+ return menuStates.get(menuId)?.isOpen() ?? menus.get(menuId)?.isOpen() ?? false;
1075
+ }
1076
+ function focusMenuElement(menuId) {
1077
+ nextTick(() => {
1078
+ const menu = menus.get(menuId);
1079
+ (menu?.focusTarget?.() ?? menu?.element())?.focus();
1080
+ });
1081
+ }
1082
+ function clearMenuState(menuId) {
1083
+ const activeId = getActiveIdValue(menuId);
1084
+ if (activeId && activeItemId.value === activeId) activeItemId.value = null;
1085
+ setActiveId(menuId, null);
1086
+ }
1087
+ function setActive(id, focusElement = false) {
1088
+ if (id === null) {
1089
+ clearMenuState(activeMenuId.value);
1090
+ activeItemId.value = null;
1091
+ return true;
1092
+ }
1093
+ const item = items.get(id);
1094
+ if (!item || item.disabled()) return false;
1095
+ setActiveId(item.menuId, item.id);
1096
+ activeItemId.value = item.id;
1097
+ activeMenuId.value = item.menuId;
1098
+ pendingMenuFocus.delete(item.menuId);
1099
+ if (focusElement) focusMenuElement(item.menuId);
1100
+ scrollItemIntoView(item);
1101
+ return true;
1102
+ }
1103
+ function isActive(id) {
1104
+ return getActiveIdValue(items.get(id)?.menuId ?? "") === id;
1105
+ }
1106
+ function getParentItem(menuId) {
1107
+ const parentItemId = menus.get(menuId)?.parentItemId?.();
1108
+ if (parentItemId) {
1109
+ const parentItem = items.get(parentItemId);
1110
+ if (parentItem) return parentItem;
1111
+ }
1112
+ return [...items.values()].find((item) => item.submenuId?.() === menuId);
1113
+ }
1114
+ function setMenuOpen(menuId, open) {
1115
+ const state = menuStates.get(menuId);
1116
+ if (state) state.setOpen(open);
1117
+ else menus.get(menuId)?.setOpen?.(open);
1118
+ }
1119
+ function chooseNeighbor(menuId, previousIndex) {
1120
+ const currentItems = getItems(menuId);
1121
+ const nextCandidates = currentItems.slice(Math.max(0, previousIndex));
1122
+ const previousCandidates = [];
1123
+ for (let index = Math.max(0, previousIndex) - 1; index >= 0; index -= 1) {
1124
+ const item = currentItems[index];
1125
+ if (item) previousCandidates.push(item);
1126
+ }
1127
+ const next = [...nextCandidates, ...previousCandidates].find((item) => !item.disabled());
1128
+ if (next) setActive(next.id);
1129
+ else {
1130
+ clearMenuState(menuId);
1131
+ focusMenuElement(menuId);
1132
+ }
1133
+ }
1134
+ function unregisterItem(id) {
1135
+ const item = items.get(id);
1136
+ if (!item) return;
1137
+ const previousIndex = getItems(item.menuId).findIndex((current) => current.id === id);
1138
+ const submenuId = item.submenuId?.();
1139
+ if (submenuId) navigation.closeMenu(submenuId, false);
1140
+ items.delete(id);
1141
+ if (getActiveIdValue(item.menuId) === id) chooseNeighbor(item.menuId, previousIndex);
1142
+ navigation.reconcileOpenSubmenus();
1143
+ }
1144
+ function registerItem(registration) {
1145
+ const previous = items.get(registration.id);
1146
+ if (previous && previous.menuId !== registration.menuId && getActiveIdValue(previous.menuId) === previous.id) clearMenuState(previous.menuId);
1147
+ items.set(registration.id, registration);
1148
+ if (isMenuOpen(registration.menuId) && !getActiveIdValue(registration.menuId) && !registration.disabled()) {
1149
+ const enabledItems = getEnabledItems(registration.menuId);
1150
+ const candidate = pendingMenuFocus.get(registration.menuId) === "last" ? enabledItems.at(-1) : enabledItems[0];
1151
+ if (candidate) setActive(candidate.id);
1152
+ } else navigation.reconcile(registration.menuId);
1153
+ return () => {
1154
+ if (items.get(registration.id) === registration) unregisterItem(registration.id);
1155
+ };
1156
+ }
1157
+ function unregisterMenu(id) {
1158
+ if (!menus.get(id)) return;
1159
+ navigation.closeSubmenus(id);
1160
+ if (id !== host.rootMenuId) navigation.closeMenu(id, false);
1161
+ clearMenuState(id);
1162
+ menus.delete(id);
1163
+ }
1164
+ function registerMenu(registration) {
1165
+ menus.set(registration.id, registration);
1166
+ const pending = pendingMenuFocus.get(registration.id);
1167
+ if (registration.isOpen() && pending) nextTick(() => navigation.focusMenu(registration.id, pending));
1168
+ return () => {
1169
+ if (menus.get(registration.id) === registration) unregisterMenu(registration.id);
1170
+ };
1171
+ }
1172
+ function registerMenuState(registration) {
1173
+ menuStates.set(registration.id, registration);
1174
+ return () => {
1175
+ if (menuStates.get(registration.id) !== registration) return;
1176
+ menuStates.delete(registration.id);
1177
+ pendingMenuFocus.delete(registration.id);
1178
+ if (!menus.has(registration.id)) clearMenuState(registration.id);
1179
+ };
1180
+ }
1181
+ return {
1182
+ activeItemId,
1183
+ activeMenuId,
1184
+ pendingRootFocus,
1185
+ registerMenu,
1186
+ unregisterMenu,
1187
+ registerMenuState,
1188
+ registerItem,
1189
+ unregisterItem,
1190
+ getActiveId,
1191
+ getItem,
1192
+ getMenu,
1193
+ isActive,
1194
+ isMenuOpen,
1195
+ setActive,
1196
+ focusMenuElement,
1197
+ state: {
1198
+ getMenuState,
1199
+ getItems,
1200
+ getEnabledItems,
1201
+ getActiveIdValue,
1202
+ clearMenuState,
1203
+ getParentItem,
1204
+ setMenuOpen,
1205
+ chooseNeighbor,
1206
+ getMenuIds: () => /* @__PURE__ */ new Set([...menus.keys(), ...menuStates.keys()]),
1207
+ getRegisteredMenuIds: () => [...menus.keys()],
1208
+ getPendingMenuFocus: (menuId) => pendingMenuFocus.get(menuId),
1209
+ setPendingMenuFocus: (menuId, focus) => pendingMenuFocus.set(menuId, focus),
1210
+ clearPendingMenuFocus: (menuId) => pendingMenuFocus.delete(menuId)
1211
+ }
1212
+ };
1213
+ }
1214
+ //#endregion
1215
+ //#region src/components/dropdown-menu/useDropdownMenuInteractionDismissal.ts
1216
+ function createOutsideEvent(originalEvent) {
1217
+ return createCancelableCustomEvent("dropdown-menu-interact-outside", { originalEvent }, originalEvent);
1218
+ }
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;
1226
+ document.addEventListener("click", blockDocumentClick, true);
1227
+ window.setTimeout(() => document.removeEventListener("click", blockDocumentClick, true), 1e3);
1228
+ }
1229
+ function useDropdownMenuInteractionDismissal({ host, registry, closeRoot }) {
1230
+ const inside = /* @__PURE__ */ new Set();
1231
+ let dismissal;
1232
+ let isListening = false;
1233
+ function registerInside(element) {
1234
+ inside.add(element);
1235
+ }
1236
+ function unregisterInside(element) {
1237
+ inside.delete(element);
1238
+ }
1239
+ function isInside(event) {
1240
+ return [...inside].some((element) => isEventWithinElement(event, element));
1241
+ }
1242
+ function emitOutside(type, originalEvent) {
1243
+ const outsideEvent = createOutsideEvent(originalEvent);
1244
+ if (type === "pointer") dismissal?.pointerDownOutside?.(outsideEvent);
1245
+ else dismissal?.focusOutside?.(outsideEvent);
1246
+ dismissal?.interactOutside?.(outsideEvent);
1247
+ return outsideEvent;
1248
+ }
1249
+ function blockModalInteraction(event) {
1250
+ if (!host.modal.value) return;
1251
+ if (event.cancelable) event.preventDefault();
1252
+ event.stopPropagation();
1253
+ if (event.type === "pointerdown") blockNextDocumentClick();
1254
+ }
1255
+ function shouldIgnoreOutside(event) {
1256
+ const ignoredTargets = (dismissal?.ignoredTargets() ?? []).map((target) => isRef(target) ? target.value : target);
1257
+ return isInside(event) || isEventWithinTargets(event, ignoredTargets);
1258
+ }
1259
+ function onDocumentPointer(event) {
1260
+ if (!host.isTopLayer() || shouldIgnoreOutside(event)) return;
1261
+ const outsideEvent = emitOutside("pointer", event);
1262
+ blockModalInteraction(event);
1263
+ if (!outsideEvent.defaultPrevented) closeRoot({ focusTrigger: host.modal.value });
1264
+ }
1265
+ function onDocumentFocus(event) {
1266
+ if (!host.isTopLayer() || shouldIgnoreOutside(event)) return;
1267
+ const outsideEvent = emitOutside("focus", event);
1268
+ blockModalInteraction(event);
1269
+ if (!outsideEvent.defaultPrevented) closeRoot({ focusTrigger: host.modal.value });
1270
+ else if (host.modal.value) registry.focusMenuElement(registry.activeMenuId.value);
1271
+ }
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
+ function registerDismissal(registration) {
1280
+ dismissal = registration;
1281
+ setDocumentListeners(host.isOpen.value);
1282
+ return () => {
1283
+ if (dismissal !== registration) return;
1284
+ dismissal = void 0;
1285
+ setDocumentListeners(false);
1286
+ };
1287
+ }
1288
+ watch(host.isOpen, (open) => setDocumentListeners(open && Boolean(dismissal)));
1289
+ onBeforeUnmount(() => setDocumentListeners(false));
1290
+ return {
1291
+ registerInside,
1292
+ unregisterInside,
1293
+ registerDismissal
1294
+ };
1295
+ }
1296
+ //#endregion
1297
+ //#region src/components/dropdown-menu/useDropdownMenuInteractionKeyboard.ts
1298
+ function getPlacementSide(placement) {
1299
+ return placement.split("-")[0];
1300
+ }
1301
+ function useDropdownMenuInteractionKeyboard({ host, registry, navigation, openRoot, closeRoot }) {
1302
+ const state = registry.state;
1303
+ const typeaheadMenuId = ref(host.rootMenuId);
1304
+ const typeahead = useTypeahead({
1305
+ items: () => state.getItems(typeaheadMenuId.value),
1306
+ activeIndex: () => state.getItems(typeaheadMenuId.value).findIndex((item) => item.id === state.getActiveIdValue(typeaheadMenuId.value)),
1307
+ getKey: (item) => item.id,
1308
+ getTextValue: (item) => item.textValue(),
1309
+ isDisabled: (item) => item.disabled(),
1310
+ scopeKey: () => typeaheadMenuId.value,
1311
+ onMatch(item) {
1312
+ registry.setActive(item.id);
1313
+ navigation.closeSubmenus(item.menuId);
1314
+ }
1315
+ });
1316
+ function move(menuId, direction) {
1317
+ const enabledItems = state.getEnabledItems(menuId);
1318
+ if (enabledItems.length === 0) {
1319
+ state.clearMenuState(menuId);
1320
+ return;
1321
+ }
1322
+ const currentIndex = enabledItems.findIndex((item) => item.id === state.getActiveIdValue(menuId));
1323
+ const item = enabledItems[((currentIndex < 0 ? direction === 1 ? -1 : 0 : currentIndex) + direction + enabledItems.length) % enabledItems.length];
1324
+ if (item) {
1325
+ registry.setActive(item.id);
1326
+ navigation.closeSubmenus(menuId);
1327
+ }
1328
+ }
1329
+ function handleHorizontalKey(menuId, event, activeItem) {
1330
+ const menu = registry.getMenu(menuId);
1331
+ const isRight = event.key === "ArrowRight";
1332
+ if (menuId !== host.rootMenuId && menu) {
1333
+ if (isRight === (getPlacementSide(menu.placement()) === "left")) {
1334
+ event.preventDefault();
1335
+ if (menu.stopKeyPropagation) event.stopPropagation();
1336
+ navigation.closeMenu(menuId, true);
1337
+ return;
1338
+ }
1339
+ }
1340
+ const submenuId = activeItem?.submenuId?.();
1341
+ if (!activeItem || activeItem.disabled() || !submenuId) return;
1342
+ const submenu = registry.getMenu(submenuId);
1343
+ if (isRight !== (activeItem.submenuDirection?.() !== "left" && (!submenu || getPlacementSide(submenu.placement()) !== "left"))) return;
1344
+ event.preventDefault();
1345
+ if (menu?.stopKeyPropagation) event.stopPropagation();
1346
+ navigation.openMenu(submenuId, "first");
1347
+ }
1348
+ function onMenuKeydown(menuId, event) {
1349
+ const menu = registry.getMenu(menuId);
1350
+ typeaheadMenuId.value = menuId;
1351
+ if (typeahead.handleKey(event)) {
1352
+ if (menu?.stopKeyPropagation) event.stopPropagation();
1353
+ return;
1354
+ }
1355
+ const activeId = state.getActiveIdValue(menuId);
1356
+ const activeItem = activeId ? registry.getItem(activeId) : void 0;
1357
+ const stop = () => {
1358
+ if (menu?.stopKeyPropagation) event.stopPropagation();
1359
+ };
1360
+ switch (event.key) {
1361
+ case "ArrowDown":
1362
+ event.preventDefault();
1363
+ stop();
1364
+ move(menuId, 1);
1365
+ break;
1366
+ case "ArrowUp":
1367
+ event.preventDefault();
1368
+ stop();
1369
+ move(menuId, -1);
1370
+ break;
1371
+ case "Home":
1372
+ event.preventDefault();
1373
+ stop();
1374
+ navigation.focusMenu(menuId, "first");
1375
+ navigation.closeSubmenus(menuId);
1376
+ break;
1377
+ case "End":
1378
+ event.preventDefault();
1379
+ stop();
1380
+ navigation.focusMenu(menuId, "last");
1381
+ navigation.closeSubmenus(menuId);
1382
+ break;
1383
+ case "ArrowRight":
1384
+ case "ArrowLeft":
1385
+ handleHorizontalKey(menuId, event, activeItem);
1386
+ break;
1387
+ case "Enter":
1388
+ case " ":
1389
+ event.preventDefault();
1390
+ stop();
1391
+ if (activeItem) navigation.activateItem(activeItem.id, event);
1392
+ break;
1393
+ case "Escape":
1394
+ event.preventDefault();
1395
+ stop();
1396
+ if (menu?.onEscape && !menu.onEscape(event)) return;
1397
+ if (!navigation.closeMenu(menuId, true)) closeRoot({ focusTrigger: true });
1398
+ break;
1399
+ case "Tab":
1400
+ closeRoot();
1401
+ break;
1402
+ }
1403
+ }
1404
+ function onTriggerKeydown(event, beforeOpen) {
1405
+ if (host.disabled.value) return;
1406
+ switch (event.key) {
1407
+ case "Enter":
1408
+ case " ":
1409
+ case "ArrowDown":
1410
+ event.preventDefault();
1411
+ beforeOpen?.();
1412
+ openRoot("first");
1413
+ break;
1414
+ case "ArrowUp":
1415
+ event.preventDefault();
1416
+ beforeOpen?.();
1417
+ openRoot("last");
1418
+ break;
1419
+ case "Escape":
1420
+ closeRoot({ focusTrigger: true });
1421
+ break;
1422
+ }
1423
+ }
1424
+ return {
1425
+ onMenuKeydown,
1426
+ onTriggerKeydown,
1427
+ resetTypeahead: typeahead.reset
1428
+ };
1429
+ }
1430
+ //#endregion
1431
+ //#region src/components/dropdown-menu/dropdownMenuInteraction.ts
1432
+ function resolveOpenFocusTarget(options) {
1433
+ if (typeof options === "string") return options;
1434
+ return options?.focus ?? "first";
1435
+ }
1436
+ function useDropdownMenuInteraction(host) {
1437
+ let navigation;
1438
+ const registry = createDropdownMenuInteractionRegistry(host, {
1439
+ closeMenu: (menuId, focusParent) => navigation.closeMenu(menuId, focusParent),
1440
+ closeSubmenus: (menuId, exceptMenuId) => navigation.closeSubmenus(menuId, exceptMenuId),
1441
+ focusMenu: (menuId, target) => navigation.focusMenu(menuId, target),
1442
+ reconcile: (menuId) => navigation.reconcile(menuId),
1443
+ reconcileOpenSubmenus: () => navigation.reconcileOpenSubmenus()
1444
+ });
1445
+ function open(options) {
1446
+ if (host.disabled.value) return;
1447
+ host.beforeOpen?.();
1448
+ registry.pendingRootFocus.value = resolveOpenFocusTarget(options);
1449
+ host.setOpen(true);
1450
+ navigation.focusMenu(host.rootMenuId, registry.pendingRootFocus.value);
1451
+ }
1452
+ function close(options = {}) {
1453
+ host.beforeClose?.();
1454
+ host.setOpen(false);
1455
+ registry.pendingRootFocus.value = false;
1456
+ navigation.closeAllSubmenus();
1457
+ registry.state.clearMenuState(host.rootMenuId);
1458
+ registry.activeItemId.value = null;
1459
+ registry.activeMenuId.value = host.rootMenuId;
1460
+ keyboard.resetTypeahead();
1461
+ if (options.focusTrigger || options.returnFocus) host.focusTrigger();
1462
+ }
1463
+ function toggle() {
1464
+ if (host.isOpen.value) close({ returnFocus: true });
1465
+ else open();
1466
+ }
1467
+ function onTriggerClick(beforeToggle) {
1468
+ beforeToggle?.();
1469
+ toggle();
1470
+ }
1471
+ navigation = createDropdownMenuInteractionNavigation({
1472
+ host,
1473
+ registry,
1474
+ closeRoot: close
1475
+ });
1476
+ const keyboard = useDropdownMenuInteractionKeyboard({
1477
+ host,
1478
+ registry,
1479
+ navigation,
1480
+ openRoot: open,
1481
+ closeRoot: close
1482
+ });
1483
+ const dismissal = useDropdownMenuInteractionDismissal({
1484
+ host,
1485
+ registry,
1486
+ closeRoot: close
1487
+ });
1488
+ watch(host.disabled, (disabled) => {
1489
+ if (disabled) close();
1490
+ });
1491
+ watch(host.isOpen, (isOpen) => {
1492
+ if (!isOpen) {
1493
+ navigation.closeAllSubmenus();
1494
+ registry.state.clearMenuState(host.rootMenuId);
1495
+ registry.activeItemId.value = null;
1496
+ registry.activeMenuId.value = host.rootMenuId;
1497
+ keyboard.resetTypeahead();
1498
+ return;
1499
+ }
1500
+ const focus = registry.pendingRootFocus.value || "first";
1501
+ navigation.focusMenu(host.rootMenuId, focus);
1502
+ }, { immediate: true });
1503
+ return {
1504
+ rootMenuId: host.rootMenuId,
1505
+ 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,
1524
+ open,
1525
+ close,
1526
+ 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
+ onTriggerClick,
1535
+ onTriggerKeydown: keyboard.onTriggerKeydown,
1536
+ onMenuKeydown: keyboard.onMenuKeydown
1537
+ };
1538
+ }
1539
+ //#endregion
1540
+ //#region src/components/dropdown-menu/dropdown-menu-root.vue
1541
+ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1542
+ name: "RpDropdownMenuRoot",
1543
+ __name: "dropdown-menu-root",
1544
+ props: {
1545
+ id: {},
1546
+ open: {
1547
+ type: Boolean,
1548
+ default: void 0
1549
+ },
1550
+ defaultOpen: {
1551
+ type: Boolean,
1552
+ default: false
1553
+ },
1554
+ baseZIndex: {},
1083
1555
  disabled: {
1084
1556
  type: Boolean,
1085
1557
  default: false
@@ -1111,9 +1583,7 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1111
1583
  const activeReference = shallowRef(null);
1112
1584
  const { reference: configuredReference } = useFloatingTarget(() => props.target ?? props.virtualAnchor, trigger);
1113
1585
  const reference = computed(() => activeReference.value ?? configuredReference.value);
1114
- const pendingFocus = ref("first");
1115
1586
  const contentElement = ref(null);
1116
- const inside = /* @__PURE__ */ new Set();
1117
1587
  let returnFocusElement = null;
1118
1588
  const baseZIndex = useOverlayZIndex({
1119
1589
  baseZIndex: () => props.baseZIndex,
@@ -1134,34 +1604,30 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1134
1604
  const activeElement = document.activeElement;
1135
1605
  if (activeElement instanceof HTMLElement) returnFocusElement = activeElement;
1136
1606
  }
1137
- function open(options) {
1138
- if (disabled.value) return;
1139
- rememberFocus();
1140
- pendingFocus.value = getFocusTarget(options);
1141
- setOpen(true);
1142
- }
1143
1607
  function focusReturnTarget() {
1144
1608
  const target = returnFocusElement ?? trigger.value;
1145
1609
  nextTick(() => target?.focus());
1146
1610
  }
1147
- function close(options = {}) {
1148
- setOpen(false);
1149
- pendingFocus.value = false;
1150
- if (options.focusTrigger || options.returnFocus) focusReturnTarget();
1151
- }
1152
- function toggle() {
1153
- if (isOpen.value) close({ returnFocus: true });
1154
- else open();
1155
- }
1611
+ const interaction = useDropdownMenuInteraction({
1612
+ rootMenuId: `${generatedId}-interaction-root`,
1613
+ isOpen,
1614
+ disabled,
1615
+ modal,
1616
+ setOpen,
1617
+ isTopLayer: layer.isTopLayer,
1618
+ focusTrigger: focusReturnTarget,
1619
+ beforeOpen: rememberFocus
1620
+ });
1621
+ const { pendingRootFocus: pendingFocus, open, close, toggle } = interaction;
1156
1622
  function openAt(point, options) {
1157
1623
  activeReference.value = createVirtualAnchor(point);
1158
1624
  open(options);
1159
1625
  }
1160
1626
  function setTrigger(element, nextId) {
1161
- if (trigger.value && trigger.value !== element) inside.delete(trigger.value);
1627
+ if (trigger.value && trigger.value !== element) interaction.unregisterInside(trigger.value);
1162
1628
  trigger.value = element;
1163
1629
  triggerId.value = nextId;
1164
- if (element) inside.add(element);
1630
+ if (element) interaction.registerInside(element);
1165
1631
  }
1166
1632
  const context = {
1167
1633
  id,
@@ -1174,6 +1640,7 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1174
1640
  reference,
1175
1641
  pendingFocus,
1176
1642
  layer,
1643
+ interaction,
1177
1644
  open,
1178
1645
  close,
1179
1646
  toggle,
@@ -1186,13 +1653,10 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1186
1653
  returnFocusElement = element;
1187
1654
  },
1188
1655
  registerInside(element) {
1189
- inside.add(element);
1656
+ interaction.registerInside(element);
1190
1657
  },
1191
1658
  unregisterInside(element) {
1192
- inside.delete(element);
1193
- },
1194
- isInside(event) {
1195
- return [...inside].some((element) => isEventWithinElement(event, element));
1659
+ interaction.unregisterInside(element);
1196
1660
  }
1197
1661
  };
1198
1662
  const slotProps = computed(() => ({
@@ -1202,9 +1666,6 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1202
1666
  toggle,
1203
1667
  openAt
1204
1668
  }));
1205
- watch(disabled, (value) => {
1206
- if (value) close();
1207
- });
1208
1669
  watch(isOpen, (value) => {
1209
1670
  if (!value) activeReference.value = null;
1210
1671
  }, { flush: "sync" });
@@ -1263,6 +1724,7 @@ var dropdown_menu_sub_default = /* @__PURE__ */ defineVaporComponent({
1263
1724
  const props = __props;
1264
1725
  const emit = __emit;
1265
1726
  const parentMenu = useRequiredInject(menuKey, "RpDropdownMenuSub");
1727
+ const menuId = `${useId()}-interaction-menu`;
1266
1728
  const controllableOpen = useControllableValue({
1267
1729
  modelValue: () => props.open,
1268
1730
  defaultValue: () => props.defaultOpen,
@@ -1276,35 +1738,31 @@ var dropdown_menu_sub_default = /* @__PURE__ */ defineVaporComponent({
1276
1738
  function setOpen(value) {
1277
1739
  if (isOpen.value !== value) controllableOpen.setValue(value);
1278
1740
  }
1741
+ const cleanupMenuState = parentMenu.root.interaction.registerMenuState({
1742
+ id: menuId,
1743
+ isOpen: () => isOpen.value,
1744
+ setOpen
1745
+ });
1279
1746
  const context = {
1747
+ menuId,
1280
1748
  isOpen,
1281
1749
  trigger,
1282
1750
  contentId,
1283
1751
  actualPlacement,
1284
1752
  pendingFocus,
1285
1753
  menu: null,
1754
+ setOpen,
1286
1755
  open(focus = "first") {
1287
- parentMenu.closeSubmenus(context);
1288
1756
  pendingFocus.value = focus;
1289
- setOpen(true);
1290
- if (focus) nextTick(() => context.menu?.focus(focus));
1757
+ if (!parentMenu.root.interaction.openMenu(menuId, focus)) setOpen(true);
1291
1758
  },
1292
1759
  close(focusParent = false) {
1293
- setOpen(false);
1294
1760
  pendingFocus.value = false;
1295
- context.menu?.closeSubmenus();
1296
- if (focusParent) {
1297
- parentMenu.setActive(trigger.value?.id ?? "");
1298
- parentMenu.focusElement();
1299
- }
1761
+ if (!parentMenu.root.interaction.closeMenu(menuId, focusParent)) setOpen(false);
1300
1762
  }
1301
1763
  };
1302
- parentMenu.registerSub(context);
1303
1764
  provide(subKey, context);
1304
- watch(parentMenu.root.isOpen, (open) => {
1305
- if (!open) context.close(false);
1306
- });
1307
- onBeforeUnmount(() => parentMenu.unregisterSub(context));
1765
+ onBeforeUnmount(cleanupMenuState);
1308
1766
  return createSlot("default", {
1309
1767
  isOpen: () => unref(isOpen),
1310
1768
  open: () => context.open,
@@ -1402,10 +1860,17 @@ var dropdown_menu_sub_trigger_default = /* @__PURE__ */ defineVaporComponent({
1402
1860
  element: () => sub.trigger.value,
1403
1861
  textValue: () => props.textValue ?? sub.trigger.value?.textContent?.trim() ?? "",
1404
1862
  disabled: () => isDisabled.value,
1405
- activate: () => sub.open("first"),
1863
+ activate: () => void 0,
1406
1864
  submenu: sub
1407
1865
  };
1408
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);
1872
+ }, { flush: "sync" });
1873
+ watch(isDisabled, () => menu.root.interaction.reconcile(menu.id));
1409
1874
  onBeforeUnmount(() => menu.unregisterItem(id.value));
1410
1875
  function setElement(value) {
1411
1876
  resolveHTMLElementRef(value, id.value, (resolved) => {
@@ -1414,25 +1879,24 @@ var dropdown_menu_sub_trigger_default = /* @__PURE__ */ defineVaporComponent({
1414
1879
  }
1415
1880
  function onMouseenter() {
1416
1881
  if (isDisabled.value) return;
1417
- menu.setActive(id.value);
1418
- sub.open(false);
1882
+ menu.hover(id.value, true);
1419
1883
  }
1420
1884
  function onClick(event) {
1421
- if (!event.defaultPrevented && !isDisabled.value) sub.open("first");
1885
+ if (!event.defaultPrevented && !isDisabled.value) menu.activate(id.value, event);
1422
1886
  }
1423
1887
  const rootAttrs = computed(() => mergeProps(attrs, {
1424
1888
  id: id.value,
1425
1889
  type: props.as === "button" ? "button" : void 0,
1426
1890
  role: "menuitem",
1427
1891
  tabindex: -1,
1428
- disabled: props.as === "button" ? optionalAttr(isDisabled.value) : void 0,
1429
- "aria-disabled": optionalAttr(isDisabled.value),
1892
+ disabled: props.as === "button" ? toOptionalAttribute(isDisabled.value) : void 0,
1893
+ "aria-disabled": toOptionalAttribute(isDisabled.value),
1430
1894
  "aria-haspopup": "menu",
1431
1895
  "aria-expanded": sub.isOpen.value,
1432
1896
  "aria-controls": sub.contentId.value,
1433
- "data-disabled": optionalAttr(isDisabled.value),
1434
- "data-focused": optionalAttr(focused.value),
1435
- "data-highlighted": optionalAttr(focused.value),
1897
+ "data-disabled": toOptionalAttribute(isDisabled.value),
1898
+ "data-focused": toOptionalAttribute(focused.value),
1899
+ "data-highlighted": toOptionalAttribute(focused.value),
1436
1900
  "data-state": sub.isOpen.value ? "open" : "closed",
1437
1901
  class: bem("rp-dropdown-menu__item", {
1438
1902
  focused: focused.value,
@@ -1484,41 +1948,28 @@ var dropdown_menu_trigger_default = /* @__PURE__ */ defineVaporComponent({
1484
1948
  root.setTrigger(resolved, id.value);
1485
1949
  });
1486
1950
  }
1487
- function openWithReference(focus = "first") {
1951
+ function prepareReference() {
1488
1952
  root.setReference(element.value);
1489
1953
  root.setReturnFocus(element.value);
1490
- root.open({ focus });
1491
1954
  }
1492
1955
  function onClick(event) {
1493
1956
  if (event.defaultPrevented || isDisabled.value) return;
1494
- root.setReference(element.value);
1495
- root.setReturnFocus(element.value);
1496
- root.toggle();
1957
+ root.interaction.onTriggerClick(prepareReference);
1497
1958
  }
1498
1959
  function onKeydown(event) {
1499
1960
  if (isDisabled.value) return;
1500
- if (event.key === "ArrowUp") {
1501
- event.preventDefault();
1502
- openWithReference("last");
1503
- } else if ([
1504
- "Enter",
1505
- " ",
1506
- "ArrowDown"
1507
- ].includes(event.key)) {
1508
- event.preventDefault();
1509
- openWithReference("first");
1510
- } else if (event.key === "Escape") root.close({ focusTrigger: true });
1961
+ root.interaction.onTriggerKeydown(event, prepareReference);
1511
1962
  }
1512
1963
  const rootAttrs = computed(() => mergeProps(attrs, {
1513
1964
  id: id.value,
1514
1965
  type: props.as === "button" ? "button" : void 0,
1515
- disabled: props.as === "button" ? optionalAttr(isDisabled.value) : void 0,
1966
+ disabled: props.as === "button" ? toOptionalAttribute(isDisabled.value) : void 0,
1516
1967
  "aria-controls": root.contentId.value,
1517
1968
  "aria-expanded": root.isOpen.value,
1518
1969
  "aria-haspopup": "menu",
1519
- "aria-disabled": props.as === "button" ? void 0 : optionalAttr(isDisabled.value),
1970
+ "aria-disabled": props.as === "button" ? void 0 : toOptionalAttribute(isDisabled.value),
1520
1971
  "data-state": root.isOpen.value ? "open" : "closed",
1521
- "data-disabled": optionalAttr(isDisabled.value),
1972
+ "data-disabled": toOptionalAttribute(isDisabled.value),
1522
1973
  onClick,
1523
1974
  onKeydown
1524
1975
  }));
@@ -1530,31 +1981,10 @@ var dropdown_menu_trigger_default = /* @__PURE__ */ defineVaporComponent({
1530
1981
  return n1;
1531
1982
  }
1532
1983
  });
1533
- var DEFAULT_FOCUS_TARGET = "first";
1534
- function getOpenFocusTarget(options) {
1535
- if (typeof options === "string") return options;
1536
- return options?.focus ?? "first";
1537
- }
1538
- function getItemClass(item, focused, disabled, hasSubmenu, submenuOpen) {
1539
- return bem("rp-dropdown-menu__item", {
1540
- focused,
1541
- disabled,
1542
- destructive: item.destructive,
1543
- submenu: hasSubmenu,
1544
- open: submenuOpen
1545
- });
1546
- }
1547
- function hasItemSubmenu(item) {
1548
- return (item.children?.length ?? 0) > 0;
1549
- }
1550
- function hasNestedItems(items) {
1551
- return items.some(hasItemSubmenu);
1552
- }
1553
- function getPathKey(path) {
1554
- return path.length === 0 ? "root" : path.join("-");
1555
- }
1556
- function getParentPath(path) {
1557
- return path.slice(0, -1);
1984
+ //#endregion
1985
+ //#region src/utils/indexPath.ts
1986
+ function getPathKey(path) {
1987
+ return path.length === 0 ? "root" : path.join("-");
1558
1988
  }
1559
1989
  function arePathsEqual(a, b) {
1560
1990
  return a.length === b.length && a.every((part, index) => part === b[index]);
@@ -1565,17 +1995,21 @@ function isPathPrefix(prefix, path) {
1565
1995
  function normalizePath(indexOrPath) {
1566
1996
  return Array.isArray(indexOrPath) ? indexOrPath : [indexOrPath];
1567
1997
  }
1568
- function getEventPoint(event) {
1569
- return {
1570
- x: event.clientX,
1571
- y: event.clientY
1572
- };
1998
+ var DEFAULT_FOCUS_TARGET = "first";
1999
+ function getItemClass(item, focused, disabled, hasSubmenu, submenuOpen) {
2000
+ return bem("rp-dropdown-menu__item", {
2001
+ focused,
2002
+ disabled,
2003
+ destructive: item.destructive,
2004
+ submenu: hasSubmenu,
2005
+ open: submenuOpen
2006
+ });
1573
2007
  }
1574
- function hasMeasuredRect(rect) {
1575
- return rect.width > 0 || rect.height > 0;
2008
+ function hasItemSubmenu(item) {
2009
+ return (item.children?.length ?? 0) > 0;
1576
2010
  }
1577
- function isPointInRect(point, rect, padding = 0) {
1578
- return point.x >= rect.left - padding && point.x <= rect.right + padding && point.y >= rect.top - padding && point.y <= rect.bottom + padding;
2011
+ function hasNestedItems(items) {
2012
+ return items.some(hasItemSubmenu);
1579
2013
  }
1580
2014
  //#endregion
1581
2015
  //#region src/components/dropdown-menu/dropdown-menu-item-row.vue?vue&type=script&setup=true&vapor=true&lang.ts
@@ -1607,6 +2041,10 @@ var dropdown_menu_item_row_default = /* @__PURE__ */ defineVaporComponent({
1607
2041
  const slotProps = computed(() => props.context.getItemSlotProps(props.item, path.value, focused.value, disabled.value, submenuOpen.value));
1608
2042
  const itemElement = ref(null);
1609
2043
  const submenuElement = ref(null);
2044
+ const templateRefs = {
2045
+ item: itemElement,
2046
+ submenu: submenuElement
2047
+ };
1610
2048
  const { actualPlacement, floatingStyle } = useFloatingPosition({
1611
2049
  reference: itemElement,
1612
2050
  floating: submenuElement,
@@ -1632,14 +2070,13 @@ var dropdown_menu_item_row_default = /* @__PURE__ */ defineVaporComponent({
1632
2070
  function getChildKey(index, child) {
1633
2071
  return `${getPathKey([...path.value, index])}:${String(child.value)}`;
1634
2072
  }
1635
- const _setTemplateRef = createTemplateRefSetter();
1636
2073
  const _component_DropdownMenuItemRow__self = resolveComponent("DropdownMenuItemRow", true);
1637
2074
  const n10 = t1$1();
1638
2075
  const n1 = child(n10);
1639
2076
  renderEffect(() => setDynamicProps(n1, [publicItemProps.value]));
1640
2077
  setInsertionState(n1, null, 0);
1641
2078
  createSlot("item", { $: [() => slotProps.value] });
1642
- setStaticTemplateRef(n1, itemElement, null, "itemElement");
2079
+ setTemplateRefBinding(n1, () => templateRefs.item);
1643
2080
  setInsertionState(n10, null, 1);
1644
2081
  createIf(() => hasSubmenu.value && submenuOpen.value, () => {
1645
2082
  const n9 = t0$1();
@@ -1659,7 +2096,7 @@ var dropdown_menu_item_row_default = /* @__PURE__ */ defineVaporComponent({
1659
2096
  return createSlot("item", { $: [() => childSlotProps] });
1660
2097
  }, { _: 8 }) });
1661
2098
  }, (child, childIndex) => getChildKey(childIndex, child), 3);
1662
- _setTemplateRef(n9, submenuElement, null, "submenuElement");
2099
+ setTemplateRefBinding(n9, () => templateRefs.submenu);
1663
2100
  return n9;
1664
2101
  });
1665
2102
  return n10;
@@ -1693,151 +2130,167 @@ var dropdown_menu_items_default = /* @__PURE__ */ defineVaporComponent({
1693
2130
  }
1694
2131
  });
1695
2132
  //#endregion
1696
- //#region src/components/dropdown-menu/useDropdownMenuDisclosure.ts
1697
- function useDropdownMenuDisclosure({ props, emit, rootRef, menuRef, targetRef, setControllableOpen, isDisabled, isOpen, isVisible, focusedPath, resetFocus, resetSubmenus, resetHoverIntent, focusItem, focusMenu, focusTrigger, layer }) {
1698
- function setOpen(nextOpen) {
1699
- if (nextOpen && isDisabled.value) return;
1700
- if (isOpen.value !== nextOpen) setControllableOpen(nextOpen);
1701
- }
1702
- function resetMenuFocus() {
1703
- resetFocus();
1704
- resetSubmenus();
1705
- resetHoverIntent();
1706
- }
1707
- function open(options) {
1708
- if (isDisabled.value) return;
1709
- setOpen(true);
1710
- focusItem(getOpenFocusTarget(options));
1711
- resetSubmenus();
1712
- resetHoverIntent();
1713
- focusMenu();
1714
- }
1715
- function close(options = {}) {
1716
- setOpen(false);
1717
- resetMenuFocus();
1718
- if (options.focusTrigger) focusTrigger();
1719
- }
1720
- function toggle() {
1721
- if (isVisible.value) close({ focusTrigger: true });
1722
- else open();
1723
- }
1724
- watch(isDisabled, (disabled) => {
1725
- if (disabled) close();
1726
- });
1727
- watch(isVisible, (visible) => {
1728
- if (visible) {
1729
- if (focusedPath.value.length === 0) focusItem(DEFAULT_FOCUS_TARGET);
1730
- focusMenu();
1731
- } else resetMenuFocus();
1732
- });
1733
- function emitOutside(type, originalEvent) {
1734
- const outsideEvent = createOutsideEvent(originalEvent);
1735
- if (type === "pointer") emit.pointerDownOutside?.(outsideEvent);
1736
- else emit.focusOutside?.(outsideEvent);
1737
- emit.interactOutside?.(outsideEvent);
1738
- return outsideEvent;
1739
- }
1740
- function isInside(event) {
1741
- return isEventWithinTargets(event, [
1742
- rootRef,
1743
- menuRef,
1744
- targetRef,
1745
- ...props.ignore ?? []
1746
- ]);
1747
- }
1748
- function blockModalInteraction(event) {
1749
- if (!props.modal) return;
1750
- if (event.cancelable) event.preventDefault();
1751
- event.stopPropagation();
1752
- if (event.type === "pointerdown") blockNextDocumentClick();
1753
- }
1754
- function onDocumentPointerdown(event) {
1755
- if (!layer.isTopLayer()) return;
1756
- if (isInside(event)) return;
1757
- const outsideEvent = emitOutside("pointer", event);
1758
- blockModalInteraction(event);
1759
- if (!outsideEvent.defaultPrevented) close({ focusTrigger: Boolean(props.modal) });
1760
- }
1761
- function onDocumentFocus(event) {
1762
- if (!layer.isTopLayer()) return;
1763
- if (isInside(event)) return;
1764
- const outsideEvent = emitOutside("focus", event);
1765
- blockModalInteraction(event);
1766
- if (!outsideEvent.defaultPrevented) close({ focusTrigger: Boolean(props.modal) });
1767
- else if (props.modal) focusMenu();
1768
- }
1769
- let isListening = false;
1770
- function setDocumentListeners(active) {
1771
- if (typeof document === "undefined" || active === isListening) return;
1772
- isListening = active;
1773
- const method = active ? "addEventListener" : "removeEventListener";
1774
- document[method]("pointerdown", onDocumentPointerdown, true);
1775
- document[method]("focusin", onDocumentFocus, true);
2133
+ //#region src/components/dropdown-menu/dropdownMenuDataModel.ts
2134
+ function createDataIndexState(rootMenuId) {
2135
+ return {
2136
+ entries: [],
2137
+ itemIdByPath: /* @__PURE__ */ new Map(),
2138
+ pathByItemId: /* @__PURE__ */ new Map(),
2139
+ menuIdByPath: /* @__PURE__ */ new Map([["root", rootMenuId]]),
2140
+ pathByMenuId: /* @__PURE__ */ new Map([[rootMenuId, []]]),
2141
+ primaryItemId: /* @__PURE__ */ new WeakMap(),
2142
+ occurrences: /* @__PURE__ */ new Map()
2143
+ };
2144
+ }
2145
+ function getOccurrenceItemId(item, state, getStableItemId) {
2146
+ const occurrence = state.occurrences.get(item) ?? 0;
2147
+ state.occurrences.set(item, occurrence + 1);
2148
+ const stableItemId = getStableItemId(item);
2149
+ return {
2150
+ occurrence,
2151
+ itemId: occurrence === 0 ? stableItemId : `${stableItemId}-copy-${occurrence}`
2152
+ };
2153
+ }
2154
+ function indexItems(items, parentPath, ownerMenuId, state, getStableItemId) {
2155
+ for (const [order, item] of items.entries()) {
2156
+ const path = [...parentPath, order];
2157
+ const pathKey = getPathKey(path);
2158
+ const { itemId, occurrence } = getOccurrenceItemId(item, state, getStableItemId);
2159
+ const submenuMenuId = hasItemSubmenu(item) ? `${itemId}-menu` : void 0;
2160
+ state.entries.push({
2161
+ item,
2162
+ path,
2163
+ itemId,
2164
+ ownerMenuId,
2165
+ order,
2166
+ submenuMenuId
2167
+ });
2168
+ state.itemIdByPath.set(pathKey, itemId);
2169
+ state.pathByItemId.set(itemId, path);
2170
+ if (occurrence === 0) state.primaryItemId.set(item, itemId);
2171
+ if (!submenuMenuId) continue;
2172
+ state.menuIdByPath.set(pathKey, submenuMenuId);
2173
+ state.pathByMenuId.set(submenuMenuId, path);
2174
+ indexItems(item.children ?? [], path, submenuMenuId, state, getStableItemId);
1776
2175
  }
1777
- watch(isVisible, setDocumentListeners, { immediate: true });
1778
- onBeforeUnmount(() => setDocumentListeners(false));
2176
+ }
2177
+ function buildDropdownMenuDataIndex(items, rootMenuId, getStableItemId) {
2178
+ const state = createDataIndexState(rootMenuId);
2179
+ indexItems(items, [], rootMenuId, state, getStableItemId);
1779
2180
  return {
1780
- open,
1781
- close,
1782
- toggle,
1783
- resetMenuFocus
2181
+ entries: state.entries,
2182
+ itemIdByPath: state.itemIdByPath,
2183
+ pathByItemId: state.pathByItemId,
2184
+ menuIdByPath: state.menuIdByPath,
2185
+ pathByMenuId: state.pathByMenuId,
2186
+ primaryItemId: state.primaryItemId
1784
2187
  };
1785
2188
  }
2189
+ function getDropdownMenuDataCollectionState(items) {
2190
+ const state = [];
2191
+ function collect(currentItems) {
2192
+ state.push(currentItems.length);
2193
+ for (const item of currentItems) {
2194
+ const children = item.children;
2195
+ state.push(item, Boolean(item.disabled), children);
2196
+ if (children) collect(children);
2197
+ }
2198
+ }
2199
+ collect(items);
2200
+ return state;
2201
+ }
1786
2202
  //#endregion
1787
- //#region src/components/dropdown-menu/useDropdownMenuDom.ts
1788
- function useDropdownMenuDom({ menuId, rootRef, menuRef, targetRef, placement, focusedPath }) {
1789
- const activeDescendantId = computed(() => focusedPath.value.length === 0 ? void 0 : getItemId(focusedPath.value));
1790
- function focusMenu() {
1791
- nextTick(() => menuRef.value?.focus());
2203
+ //#region src/components/dropdown-menu/useDropdownMenuDataRegistration.ts
2204
+ function getCurrentPath(index, entry) {
2205
+ return index.pathByItemId.get(entry.itemId) ?? entry.path;
2206
+ }
2207
+ function useDropdownMenuDataRegistration({ props, items, rootMenuId, interaction, menuRef, getItemElement, getSubmenuElement, isSubmenuOpeningLeft, onSelect }) {
2208
+ const openMenuIds = ref(/* @__PURE__ */ new Set());
2209
+ const itemIdentity = /* @__PURE__ */ new WeakMap();
2210
+ let nextItemIdentity = 0;
2211
+ let registeredItemIds = /* @__PURE__ */ new Set();
2212
+ let registeredMenuIds = /* @__PURE__ */ new Set();
2213
+ function getStableItemId(item) {
2214
+ const existing = itemIdentity.get(item);
2215
+ if (existing) return existing;
2216
+ nextItemIdentity += 1;
2217
+ const id = `${rootMenuId}-item-${nextItemIdentity}`;
2218
+ itemIdentity.set(item, id);
2219
+ return id;
2220
+ }
2221
+ const registrationIndex = shallowRef(buildDropdownMenuDataIndex([], rootMenuId, getStableItemId));
2222
+ const collectionState = computed(() => getDropdownMenuDataCollectionState(items.value));
2223
+ function setMenuOpen(id, open) {
2224
+ const next = new Set(openMenuIds.value);
2225
+ if (open) next.add(id);
2226
+ else next.delete(id);
2227
+ openMenuIds.value = next;
2228
+ }
2229
+ function createMenuRegistration(index, entry) {
2230
+ const submenuMenuId = entry.submenuMenuId;
2231
+ if (!submenuMenuId) return void 0;
2232
+ return {
2233
+ id: submenuMenuId,
2234
+ 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
+ isOpen: () => openMenuIds.value.has(submenuMenuId),
2241
+ setOpen: (open) => setMenuOpen(submenuMenuId, open)
2242
+ };
1792
2243
  }
1793
- function focusTrigger() {
1794
- nextTick(() => {
1795
- if (targetRef.value instanceof HTMLElement) {
1796
- targetRef.value.focus();
1797
- return;
2244
+ function createSelectEvent(entry, originalEvent) {
2245
+ const event = new CustomEvent("dropdown-menu-select", {
2246
+ cancelable: true,
2247
+ detail: {
2248
+ originalEvent,
2249
+ value: entry.item.value
1798
2250
  }
1799
- (rootRef.value?.querySelector("[aria-haspopup=\"menu\"]"))?.focus();
2251
+ });
2252
+ onSelect?.(entry.item, event);
2253
+ return event;
2254
+ }
2255
+ function registerEntry(index, entry) {
2256
+ const menuRegistration = createMenuRegistration(index, entry);
2257
+ if (menuRegistration) interaction.registerMenu(menuRegistration);
2258
+ interaction.registerItem({
2259
+ id: entry.itemId,
2260
+ menuId: entry.ownerMenuId,
2261
+ element: () => getItemElement(getCurrentPath(index, entry)),
2262
+ textValue: () => entry.item.label,
2263
+ disabled: () => Boolean(entry.item.disabled || props.disabled),
2264
+ order: () => entry.order,
2265
+ submenuId: () => entry.submenuMenuId,
2266
+ submenuDirection: () => isSubmenuOpeningLeft(getCurrentPath(index, entry)) ? "left" : "right",
2267
+ select: (originalEvent) => createSelectEvent(entry, originalEvent),
2268
+ closeOnSelect: () => props.closeOnSelect !== false
1800
2269
  });
1801
2270
  }
1802
- function getItemId(indexOrPath) {
1803
- return `${menuId.value}-item-${getPathKey(normalizePath(indexOrPath))}`;
1804
- }
1805
- function getSubmenuId(path) {
1806
- return `${menuId.value}-submenu-${getPathKey(path)}`;
1807
- }
1808
- function getItemElement(path) {
1809
- return document.getElementById(getItemId(path));
1810
- }
1811
- function getSubmenuElement(path) {
1812
- return document.getElementById(getSubmenuId(path));
1813
- }
1814
- function isSubmenuOpeningLeft(path) {
1815
- const submenuRect = getSubmenuElement(path)?.getBoundingClientRect();
1816
- const itemRect = getItemElement(path)?.getBoundingClientRect();
1817
- if (submenuRect && itemRect && hasMeasuredRect(submenuRect) && hasMeasuredRect(itemRect)) return submenuRect.right <= itemRect.left;
1818
- return placement.value.endsWith("end");
1819
- }
1820
- function getMenuActiveDescendant(menuPath) {
1821
- return arePathsEqual(getParentPath(focusedPath.value), menuPath) ? getItemId(focusedPath.value) : void 0;
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
+ }
1822
2278
  }
1823
- watch(focusedPath, (path) => {
1824
- if (path.length === 0) return;
1825
- const targetPath = [...path];
1826
- nextTick(() => {
1827
- if (!arePathsEqual(targetPath, focusedPath.value)) return;
1828
- getItemElement(targetPath)?.scrollIntoView?.({ block: "nearest" });
1829
- });
1830
- });
2279
+ function registerItems() {
2280
+ 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;
2287
+ registrationIndex.value = nextIndex;
2288
+ interaction.reconcile();
2289
+ }
2290
+ watch(collectionState, registerItems, { immediate: true });
1831
2291
  return {
1832
- activeDescendantId,
1833
- focusMenu,
1834
- focusTrigger,
1835
- getItemId,
1836
- getSubmenuId,
1837
- getItemElement,
1838
- getSubmenuElement,
1839
- isSubmenuOpeningLeft,
1840
- getMenuActiveDescendant
2292
+ openMenuIds,
2293
+ registrationIndex
1841
2294
  };
1842
2295
  }
1843
2296
  //#endregion
@@ -1845,26 +2298,6 @@ function useDropdownMenuDom({ menuId, rootRef, menuRef, targetRef, placement, fo
1845
2298
  var SAFE_TRIANGLE_PADDING = 2;
1846
2299
  var SAFE_TRIANGLE_ORIGIN_OFFSET = 2;
1847
2300
  var SAFE_TRIANGLE_TIMEOUT = 400;
1848
- function getTriangleArea(a, b, c) {
1849
- return Math.abs((a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2);
1850
- }
1851
- function isPointInTriangle(point, triangle) {
1852
- const [a, b, c] = triangle;
1853
- const area = getTriangleArea(a, b, c);
1854
- const area1 = getTriangleArea(point, b, c);
1855
- const area2 = getTriangleArea(a, point, c);
1856
- const area3 = getTriangleArea(a, b, point);
1857
- return Math.abs(area - (area1 + area2 + area3)) < .5;
1858
- }
1859
- function clamp(value, min, max) {
1860
- return Math.min(Math.max(value, min), max);
1861
- }
1862
- function clampPointToRect(point, rect) {
1863
- return {
1864
- x: clamp(point.x, rect.left, rect.right),
1865
- y: clamp(point.y, rect.top, rect.bottom)
1866
- };
1867
- }
1868
2301
  function getDropdownMenuSafeTriangle({ itemRect, submenuRect, origin }) {
1869
2302
  const measuredItemRect = itemRect && hasMeasuredRect(itemRect) ? itemRect : null;
1870
2303
  const opensRight = !measuredItemRect || submenuRect.left >= measuredItemRect.right;
@@ -1961,7 +2394,7 @@ function useDropdownMenuHoverIntent({ openSubmenuPath, getItemElement, getSubmen
1961
2394
  }, SAFE_TRIANGLE_TIMEOUT);
1962
2395
  }
1963
2396
  function handleItemHover(item, path, event, commit) {
1964
- const point = event ? getEventPoint(event) : void 0;
2397
+ const point = event ? getClientPoint(event) : void 0;
1965
2398
  latestPointerPoint = point ?? latestPointerPoint;
1966
2399
  if (point && shouldDelayHover(path, point)) {
1967
2400
  delayHoveredItem(item, path, commit);
@@ -1970,7 +2403,7 @@ function useDropdownMenuHoverIntent({ openSubmenuPath, getItemElement, getSubmen
1970
2403
  commit(item, path, point);
1971
2404
  }
1972
2405
  function handleMenuMousemove(event, commit) {
1973
- const point = getEventPoint(event);
2406
+ const point = getClientPoint(event);
1974
2407
  latestPointerPoint = point;
1975
2408
  updateSafeTriangleOrigin(point);
1976
2409
  if (!pendingHover) return;
@@ -1988,221 +2421,131 @@ function useDropdownMenuHoverIntent({ openSubmenuPath, getItemElement, getSubmen
1988
2421
  };
1989
2422
  }
1990
2423
  //#endregion
1991
- //#region src/components/dropdown-menu/useDropdownMenuInteractions.ts
1992
- function useDropdownMenuInteractions({ props, emit, isDisabled, focusedPath, openSubmenuPath, getItemAtPath, focusItem, moveFocus, handleTypeahead, submenus, hoverIntent, disclosure, isSubmenuOpeningLeft }) {
1993
- function activateItem(item, path, event) {
1994
- if (item.disabled) return;
1995
- if (hasItemSubmenu(item)) {
1996
- openSubmenu(path, DEFAULT_FOCUS_TARGET, event instanceof MouseEvent ? getEventPoint(event) : void 0);
1997
- return;
1998
- }
1999
- selectItem(item, event);
2424
+ //#region src/components/dropdown-menu/useDropdownMenuDataController.ts
2425
+ function useDropdownMenuDataController({ props, items, rootMenuId, menuId, menuRef, actualPlacement, interaction, onSelect }) {
2426
+ function getItemId(path) {
2427
+ return `${menuId.value}-item-${getPathKey(path)}`;
2000
2428
  }
2001
- function selectItem(item, originalEvent = new Event("select")) {
2002
- if (item.disabled) return;
2003
- const event = new CustomEvent("dropdown-menu-select", {
2004
- cancelable: true,
2005
- detail: {
2006
- originalEvent,
2007
- value: item.value
2008
- }
2009
- });
2010
- emit.select?.(item, event);
2011
- if (!event.defaultPrevented && props.closeOnSelect !== false) disclosure.close({ focusTrigger: true });
2429
+ function getSubmenuId(path) {
2430
+ return `${menuId.value}-submenu-${getPathKey(path)}`;
2431
+ }
2432
+ function getItemElement(path) {
2433
+ return typeof document === "undefined" ? null : document.getElementById(getItemId(path));
2434
+ }
2435
+ function getSubmenuElement(path) {
2436
+ return typeof document === "undefined" ? null : document.getElementById(getSubmenuId(path));
2437
+ }
2438
+ function isSubmenuOpeningLeft(path) {
2439
+ const submenuRect = getSubmenuElement(path)?.getBoundingClientRect();
2440
+ const itemRect = getItemElement(path)?.getBoundingClientRect();
2441
+ if (submenuRect && itemRect && hasMeasuredRect(submenuRect) && hasMeasuredRect(itemRect)) return submenuRect.right <= itemRect.left;
2442
+ return actualPlacement.value.endsWith("end");
2443
+ }
2444
+ const { openMenuIds, registrationIndex } = useDropdownMenuDataRegistration({
2445
+ props,
2446
+ items,
2447
+ rootMenuId,
2448
+ interaction,
2449
+ menuRef,
2450
+ getItemElement,
2451
+ getSubmenuElement,
2452
+ isSubmenuOpeningLeft,
2453
+ onSelect
2454
+ });
2455
+ function getDataItemId(path) {
2456
+ return registrationIndex.value.itemIdByPath.get(getPathKey(path));
2012
2457
  }
2013
- function closeSubmenusAfter(menuPath) {
2014
- submenus.closeSubmenusAfter(menuPath);
2015
- hoverIntent.resetHoverIntent();
2458
+ const focusedPath = computed({
2459
+ get() {
2460
+ const id = interaction.activeItemId.value;
2461
+ return id ? [...registrationIndex.value.pathByItemId.get(id) ?? []] : [];
2462
+ },
2463
+ set(path) {
2464
+ interaction.setActive(path.length > 0 ? getDataItemId(path) ?? null : null);
2465
+ }
2466
+ });
2467
+ const focusedIndex = computed(() => focusedPath.value[0] ?? -1);
2468
+ const activeDescendantId = computed(() => focusedPath.value.length === 0 ? void 0 : getItemId(focusedPath.value));
2469
+ const openSubmenuPath = computed(() => {
2470
+ let deepest = [];
2471
+ for (const id of openMenuIds.value) {
2472
+ const path = registrationIndex.value.pathByMenuId.get(id);
2473
+ if (path && path.length > deepest.length) deepest = path;
2474
+ }
2475
+ return [...deepest];
2476
+ });
2477
+ function isItemFocused(indexOrPath) {
2478
+ const id = getDataItemId(normalizePath(indexOrPath));
2479
+ return Boolean(id && interaction.activeItemId.value === id);
2480
+ }
2481
+ function isSubmenuOpen(path) {
2482
+ const itemId = getDataItemId(path);
2483
+ const submenuId = itemId ? interaction.getItem(itemId)?.submenuId?.() : void 0;
2484
+ return Boolean(submenuId && interaction.isMenuOpen(submenuId));
2016
2485
  }
2017
2486
  function openSubmenu(path, focus = DEFAULT_FOCUS_TARGET, point) {
2018
- const opened = submenus.openSubmenu(path, focus);
2019
- if (opened) hoverIntent.trackSubmenuOpen(path, point);
2020
- return opened;
2487
+ const itemId = getDataItemId(path);
2488
+ const submenuId = itemId ? interaction.getItem(itemId)?.submenuId?.() : void 0;
2489
+ if (!submenuId || !interaction.openMenu(submenuId, focus)) return false;
2490
+ hoverIntent.trackSubmenuOpen(path, point);
2491
+ return true;
2021
2492
  }
2022
2493
  function closeSubmenu(path) {
2023
- const closed = submenus.closeSubmenu(path);
2024
- if (closed) hoverIntent.resetHoverIntent();
2025
- return closed;
2026
- }
2027
- function closeCurrentSubmenu() {
2028
- const closed = submenus.closeCurrentSubmenu();
2494
+ const itemId = getDataItemId(path);
2495
+ const submenuId = itemId ? interaction.getItem(itemId)?.submenuId?.() : void 0;
2496
+ const closed = submenuId ? interaction.closeMenu(submenuId, false) : false;
2029
2497
  if (closed) hoverIntent.resetHoverIntent();
2030
2498
  return closed;
2031
2499
  }
2032
2500
  function commitHoveredItem(item, path, point) {
2501
+ const itemId = getDataItemId(path);
2502
+ const itemRegistration = itemId ? interaction.getItem(itemId) : void 0;
2503
+ if (!itemRegistration) return;
2033
2504
  hoverIntent.clearPendingHover();
2034
- focusedPath.value = [...path];
2505
+ interaction.setActive(itemRegistration.id);
2035
2506
  if (hasItemSubmenu(item)) openSubmenu(path, false, point);
2036
- else closeSubmenusAfter(getParentPath(path));
2507
+ else interaction.closeSubmenus(itemRegistration.menuId);
2037
2508
  }
2509
+ const hoverIntent = useDropdownMenuHoverIntent({
2510
+ openSubmenuPath,
2511
+ getItemElement,
2512
+ getSubmenuElement
2513
+ });
2038
2514
  function focusHoveredItem(item, indexOrPath, event) {
2039
2515
  if (item.disabled) return;
2040
- const path = normalizePath(indexOrPath);
2041
- hoverIntent.handleItemHover(item, path, event, commitHoveredItem);
2042
- }
2043
- function selectFocusedItem(event) {
2044
- const item = getItemAtPath(focusedPath.value);
2045
- if (!item) return;
2046
- activateItem(item, focusedPath.value, event);
2047
- }
2048
- function moveFocusedItem(direction) {
2049
- const menuPath = moveFocus(direction);
2050
- if (menuPath) closeSubmenusAfter(menuPath);
2051
- }
2052
- function onTriggerClick() {
2053
- disclosure.toggle();
2054
- }
2055
- function onTriggerKeydown(event) {
2056
- if (isDisabled.value) return;
2057
- switch (event.key) {
2058
- case "Enter":
2059
- case " ":
2060
- case "ArrowDown":
2061
- event.preventDefault();
2062
- disclosure.open();
2063
- break;
2064
- case "ArrowUp":
2065
- event.preventDefault();
2066
- disclosure.open({ focus: "last" });
2067
- break;
2068
- case "Escape":
2069
- disclosure.close({ focusTrigger: true });
2070
- break;
2071
- }
2516
+ hoverIntent.handleItemHover(item, normalizePath(indexOrPath), event, commitHoveredItem);
2072
2517
  }
2073
- function onMenuKeydown(event) {
2074
- hoverIntent.resetHoverIntent();
2075
- if (handleTypeahead(event)) return;
2076
- switch (event.key) {
2077
- case "ArrowDown":
2078
- event.preventDefault();
2079
- moveFocusedItem(1);
2080
- break;
2081
- case "ArrowUp":
2082
- event.preventDefault();
2083
- moveFocusedItem(-1);
2084
- break;
2085
- case "Home":
2086
- event.preventDefault();
2087
- focusItem("first", getParentPath(focusedPath.value));
2088
- break;
2089
- case "End":
2090
- event.preventDefault();
2091
- focusItem("last", getParentPath(focusedPath.value));
2092
- break;
2093
- case "ArrowRight":
2094
- case "ArrowLeft":
2095
- onMenuHorizontalKeydown(event);
2096
- break;
2097
- case "Enter":
2098
- case " ":
2099
- event.preventDefault();
2100
- selectFocusedItem(event);
2101
- break;
2102
- case "Escape":
2103
- event.preventDefault();
2104
- if (!closeCurrentSubmenu()) disclosure.close({ focusTrigger: true });
2105
- break;
2106
- case "Tab":
2107
- disclosure.close();
2108
- break;
2109
- }
2110
- }
2111
- function onMenuHorizontalKeydown(event) {
2112
- const isRight = event.key === "ArrowRight";
2113
- const currentMenuPath = getParentPath(focusedPath.value);
2114
- const closePath = focusedPath.value.length > 1 ? currentMenuPath : arePathsEqual(focusedPath.value, openSubmenuPath.value) ? focusedPath.value : void 0;
2115
- if (closePath && isRight === isSubmenuOpeningLeft(closePath)) {
2116
- event.preventDefault();
2117
- closeSubmenu(closePath);
2118
- return;
2119
- }
2120
- const item = getItemAtPath(focusedPath.value);
2121
- if (!item || item.disabled || !hasItemSubmenu(item)) return;
2122
- if (isRight === isSubmenuOpeningLeft(focusedPath.value)) return;
2123
- event.preventDefault();
2124
- openSubmenu(focusedPath.value);
2125
- }
2126
- function onMenuMousemove(event) {
2127
- hoverIntent.handleMenuMousemove(event, commitHoveredItem);
2128
- }
2129
- function onMenuMouseleave() {
2130
- hoverIntent.resetHoverIntent();
2131
- }
2132
- return {
2133
- activateItem,
2134
- selectItem,
2135
- openSubmenu,
2136
- closeSubmenu,
2137
- focusHoveredItem,
2138
- onTriggerClick,
2139
- onTriggerKeydown,
2140
- onMenuKeydown,
2141
- onMenuMousemove,
2142
- onMenuMouseleave
2143
- };
2144
- }
2145
- //#endregion
2146
- //#region src/components/dropdown-menu/useDropdownMenuNavigation.ts
2147
- function useDropdownMenuNavigation(items) {
2148
- const focusedPath = ref([]);
2149
- const focusedIndex = computed(() => focusedPath.value[0] ?? -1);
2150
- function getItemsAtPath(path) {
2151
- let currentItems = items.value;
2152
- for (const index of path) {
2153
- const item = currentItems[index];
2154
- if (!item) return [];
2155
- currentItems = item.children ?? [];
2156
- }
2157
- return currentItems;
2158
- }
2159
- function getItemAtPath(path) {
2160
- if (path.length === 0) return void 0;
2161
- return getItemsAtPath(getParentPath(path))[path[path.length - 1]];
2162
- }
2163
- function focusItem(target, menuPath = []) {
2164
- const enabledIndexes = getEnabledIndexes(getItemsAtPath(menuPath), (item) => Boolean(item.disabled));
2165
- const index = target === "last" ? enabledIndexes[enabledIndexes.length - 1] : enabledIndexes[0];
2166
- if (index === void 0) {
2167
- if (menuPath.length === 0) focusedPath.value = [];
2168
- return false;
2169
- }
2170
- focusedPath.value = [...menuPath, index];
2171
- return true;
2518
+ function selectItem(item, originalEvent = new Event("select")) {
2519
+ const id = registrationIndex.value.primaryItemId.get(item);
2520
+ if (id) interaction.selectItem(id, originalEvent);
2172
2521
  }
2173
- function resetFocus() {
2174
- focusedPath.value = [];
2522
+ function activateItem(_item, path, event) {
2523
+ const id = getDataItemId(path);
2524
+ if (id) interaction.activateItem(id, event);
2175
2525
  }
2176
- function setFocusedIndex(index, menuPath = []) {
2177
- const item = getItemsAtPath(menuPath)[index];
2178
- if (!item || item.disabled) return false;
2179
- focusedPath.value = [...menuPath, index];
2180
- return true;
2181
- }
2182
- function isItemFocused(indexOrPath) {
2183
- return arePathsEqual(normalizePath(indexOrPath), focusedPath.value);
2184
- }
2185
- function moveFocus(direction) {
2186
- const menuPath = getParentPath(focusedPath.value);
2187
- const itemsAtPath = getItemsAtPath(menuPath);
2188
- const nextIndex = getNextEnabledIndex(itemsAtPath, focusedPath.value.length === 0 ? direction === 1 ? -1 : itemsAtPath.length : focusedPath.value[focusedPath.value.length - 1], direction, (item) => Boolean(item.disabled));
2189
- if (nextIndex === void 0) {
2190
- focusedPath.value = [];
2191
- return;
2192
- }
2193
- focusedPath.value = [...menuPath, nextIndex];
2194
- return menuPath;
2526
+ function getMenuActiveDescendant(path) {
2527
+ const pathKey = getPathKey(path);
2528
+ const activeId = interaction.getActiveId(registrationIndex.value.menuIdByPath.get(pathKey) ?? rootMenuId).value;
2529
+ const activePath = activeId ? registrationIndex.value.pathByItemId.get(activeId) : void 0;
2530
+ return activePath ? getItemId(activePath) : void 0;
2195
2531
  }
2196
2532
  return {
2197
2533
  focusedPath,
2198
2534
  focusedIndex,
2199
- getItemsAtPath,
2200
- getItemAtPath,
2201
- focusItem,
2202
- resetFocus,
2203
- setFocusedIndex,
2535
+ activeDescendantId,
2536
+ getItemId,
2537
+ getSubmenuId,
2538
+ getItemElement,
2539
+ getMenuActiveDescendant,
2204
2540
  isItemFocused,
2205
- moveFocus
2541
+ isSubmenuOpen,
2542
+ openSubmenu,
2543
+ closeSubmenu,
2544
+ focusHoveredItem,
2545
+ onMenuMousemove: (event) => hoverIntent.handleMenuMousemove(event, commitHoveredItem),
2546
+ resetHoverIntent: hoverIntent.resetHoverIntent,
2547
+ selectItem,
2548
+ activateItem
2206
2549
  };
2207
2550
  }
2208
2551
  //#endregion
@@ -2220,8 +2563,7 @@ function useDropdownMenuPortalPosition(options) {
2220
2563
  flipOptions: () => options.props.flipOptions,
2221
2564
  shift: () => options.props.shift !== false,
2222
2565
  collisionPadding: () => options.props.collisionPadding ?? 8,
2223
- autoUpdateOptions: () => options.props.autoUpdateOptions,
2224
- restartKey: options.restartKey
2566
+ autoUpdateOptions: () => options.props.autoUpdateOptions
2225
2567
  });
2226
2568
  return {
2227
2569
  actualPlacement: floating.actualPlacement,
@@ -2230,6 +2572,67 @@ function useDropdownMenuPortalPosition(options) {
2230
2572
  };
2231
2573
  }
2232
2574
  //#endregion
2575
+ //#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 }) {
2577
+ const rootClass = computed(() => bem("rp-dropdown-menu", {
2578
+ [`placement-${actualPlacement.value}`]: true,
2579
+ target: isExplicitTarget.value,
2580
+ open: isVisible.value,
2581
+ disabled: isDisabled.value
2582
+ }));
2583
+ const contentClass = computed(() => bem("rp-dropdown-menu__content", { "has-submenu": contentHasSubmenu.value }));
2584
+ const triggerProps = computed(() => ({
2585
+ "aria-controls": isDisabled.value ? void 0 : menuId.value,
2586
+ "aria-expanded": isDisabled.value ? void 0 : isVisible.value,
2587
+ "aria-haspopup": "menu",
2588
+ disabled: isDisabled.value || void 0,
2589
+ onClick: () => interaction.onTriggerClick(),
2590
+ onKeydown: (event) => interaction.onTriggerKeydown(event)
2591
+ }));
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
+ function onMenuKeydown(event) {
2608
+ resetHoverIntent();
2609
+ interaction.onMenuKeydown(getKeyboardMenuId(event), event);
2610
+ }
2611
+ return {
2612
+ rootClass,
2613
+ contentClass,
2614
+ triggerProps,
2615
+ contentProps: computed(() => ({
2616
+ id: menuId.value,
2617
+ role: "menu",
2618
+ tabindex: -1,
2619
+ "aria-label": props.ariaLabel || void 0,
2620
+ "aria-activedescendant": activeDescendantId.value,
2621
+ onKeydown: onMenuKeydown,
2622
+ onMousemove: onMenuMousemove,
2623
+ onMouseleave: resetHoverIntent
2624
+ })),
2625
+ slotProps: computed(() => ({
2626
+ triggerProps: triggerProps.value,
2627
+ isOpen: isVisible.value,
2628
+ open,
2629
+ close,
2630
+ toggle
2631
+ })),
2632
+ onMenuKeydown
2633
+ };
2634
+ }
2635
+ //#endregion
2233
2636
  //#region src/components/dropdown-menu/useDropdownMenuRenderItems.ts
2234
2637
  function useDropdownMenuRenderItems({ props, actualPlacement, items, getItemId, getSubmenuId, getMenuActiveDescendant, getItemElement, isItemFocused, isSubmenuOpen, activateItem, focusHoveredItem, selectItem, openSubmenu, closeSubmenu, close }) {
2235
2638
  const renderedItems = computed(() => getRenderedItems(items.value));
@@ -2272,9 +2675,9 @@ function useDropdownMenuRenderItems({ props, actualPlacement, items, getItemId,
2272
2675
  "aria-expanded": hasSubmenu ? resolvedSubmenuOpen : void 0,
2273
2676
  "aria-haspopup": hasSubmenu ? "menu" : void 0,
2274
2677
  "aria-disabled": disabled || void 0,
2275
- "data-disabled": disabled ? "" : void 0,
2276
- "data-highlighted": focused ? "" : void 0,
2277
- "data-submenu": hasSubmenu ? "" : void 0,
2678
+ "data-disabled": toPresenceAttribute(disabled),
2679
+ "data-highlighted": toPresenceAttribute(focused),
2680
+ "data-submenu": toPresenceAttribute(hasSubmenu),
2278
2681
  onClick: (event) => activateItem(item, path, event),
2279
2682
  onMouseenter: (event) => focusHoveredItem(item, path, event)
2280
2683
  };
@@ -2332,45 +2735,66 @@ function useDropdownMenuRenderItems({ props, actualPlacement, items, getItemId,
2332
2735
  };
2333
2736
  }
2334
2737
  //#endregion
2335
- //#region src/components/dropdown-menu/useDropdownSubmenus.ts
2336
- function useDropdownSubmenus({ focusedPath, getItemAtPath, focusItem }) {
2337
- const openSubmenuPath = ref([]);
2338
- function resetSubmenus() {
2339
- openSubmenuPath.value = [];
2340
- }
2341
- function isSubmenuOpen(path) {
2342
- return path.length > 0 && isPathPrefix(path, openSubmenuPath.value);
2343
- }
2344
- function closeSubmenusAfter(menuPath) {
2345
- openSubmenuPath.value = [...menuPath];
2346
- }
2347
- function openSubmenu(path, focus = DEFAULT_FOCUS_TARGET) {
2348
- const item = getItemAtPath(path);
2349
- if (!item || item.disabled || !hasItemSubmenu(item)) return false;
2350
- openSubmenuPath.value = [...path];
2351
- if (focus && !focusItem(focus, path)) focusedPath.value = [...path];
2352
- return true;
2353
- }
2354
- function closeSubmenu(path) {
2355
- if (!isSubmenuOpen(path)) return false;
2356
- openSubmenuPath.value = getParentPath(path);
2357
- if (isPathPrefix(path, focusedPath.value) && focusedPath.value.length > path.length) focusedPath.value = [...path];
2358
- return true;
2359
- }
2360
- function closeCurrentSubmenu() {
2361
- const path = focusedPath.value.length > 1 ? getParentPath(focusedPath.value) : focusedPath.value;
2362
- if (path.length === 0 || !isSubmenuOpen(path)) return false;
2363
- return closeSubmenu(path);
2364
- }
2365
- return {
2366
- openSubmenuPath,
2367
- resetSubmenus,
2368
- isSubmenuOpen,
2369
- closeSubmenusAfter,
2370
- openSubmenu,
2371
- closeSubmenu,
2372
- closeCurrentSubmenu
2373
- };
2738
+ //#region src/components/dropdown-menu/useDropdownMenuTargetBindings.ts
2739
+ var TARGET_ATTRIBUTES = [
2740
+ "aria-controls",
2741
+ "aria-expanded",
2742
+ "aria-haspopup",
2743
+ "aria-disabled"
2744
+ ];
2745
+ function syncTargetAttributes(target, id, visible, disabled) {
2746
+ if (disabled) {
2747
+ target.removeAttribute("aria-controls");
2748
+ target.removeAttribute("aria-expanded");
2749
+ target.removeAttribute("aria-haspopup");
2750
+ target.setAttribute("aria-disabled", "true");
2751
+ return;
2752
+ }
2753
+ target.setAttribute("aria-controls", id);
2754
+ target.setAttribute("aria-expanded", String(visible));
2755
+ target.setAttribute("aria-haspopup", "menu");
2756
+ target.removeAttribute("aria-disabled");
2757
+ }
2758
+ function useDropdownMenuTargetBindings({ interaction, rootRef, menuRef, targetElement, isExplicitTarget, menuId, isVisible, isDisabled, ignoredTargets, pointerDownOutside, focusOutside, interactOutside }) {
2759
+ function watchInside(source) {
2760
+ watch(source, (element, previous) => {
2761
+ if (previous) interaction.unregisterInside(previous);
2762
+ if (element) interaction.registerInside(element);
2763
+ }, { immediate: true });
2764
+ }
2765
+ watchInside(rootRef);
2766
+ watchInside(menuRef);
2767
+ watchInside(targetElement);
2768
+ const cleanupDismissal = interaction.registerDismissal({
2769
+ ignoredTargets,
2770
+ pointerDownOutside,
2771
+ focusOutside,
2772
+ interactOutside
2773
+ });
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" });
2797
+ onBeforeUnmount(cleanupDismissal);
2374
2798
  }
2375
2799
  //#endregion
2376
2800
  //#region src/components/dropdown-menu/useDropdownMenu.ts
@@ -2378,21 +2802,22 @@ function useDropdownMenu(props, emit = {}) {
2378
2802
  const rootRef = ref(null);
2379
2803
  const menuRef = ref(null);
2380
2804
  const arrowRef = ref(null);
2805
+ const generatedId = useId();
2806
+ const interactionRootMenuId = `${generatedId}-interaction-root`;
2381
2807
  const controllableOpen = useControllableValue({
2382
2808
  modelValue: () => props.open,
2383
2809
  defaultValue: () => false,
2384
2810
  onChange: (open) => emit.openChange?.(open)
2385
2811
  });
2386
- const generatedId = useId();
2387
2812
  const menuId = computed(() => props.id ?? `${generatedId}-menu`);
2388
2813
  const placement = computed(() => props.placement ?? "bottom-start");
2389
2814
  const teleportTo = useTeleportTarget(() => props.teleportTo);
2390
- const teleportPositioningKey = useTeleportPositioningKey();
2391
2815
  const shouldTeleport = computed(() => props.teleport !== false);
2392
2816
  const { isExplicitTarget, reference, resolvedTarget } = useFloatingTarget(() => props.target, rootRef);
2393
- const targetElement = computed(() => isElementReference(resolvedTarget.value) ? resolvedTarget.value : null);
2817
+ const targetElement = computed(() => isElement(resolvedTarget.value) ? resolvedTarget.value : null);
2394
2818
  const visibleItems = computed(() => props.items ?? []);
2395
2819
  const isDisabled = computed(() => Boolean(props.disabled));
2820
+ const modal = computed(() => Boolean(props.modal));
2396
2821
  const isOpen = controllableOpen.value;
2397
2822
  const isVisible = computed(() => isOpen.value && !isDisabled.value);
2398
2823
  const layer = useOverlayLayer({
@@ -2412,173 +2837,107 @@ function useDropdownMenu(props, emit = {}) {
2412
2837
  menuRef,
2413
2838
  arrowRef,
2414
2839
  isVisible,
2415
- placement,
2416
- restartKey: () => [
2417
- shouldTeleport.value,
2418
- teleportTo.value,
2419
- teleportPositioningKey.value
2420
- ]
2840
+ placement
2421
2841
  });
2422
2842
  const placementSide = computed(() => actualPlacement.value.split("-")[0]);
2423
- const { focusedPath, focusedIndex, getItemsAtPath, getItemAtPath, focusItem, resetFocus, setFocusedIndex, isItemFocused, moveFocus } = useDropdownMenuNavigation(visibleItems);
2424
- const submenus = useDropdownSubmenus({
2425
- focusedPath,
2426
- getItemAtPath,
2427
- focusItem
2843
+ function focusTrigger() {
2844
+ nextTick(() => {
2845
+ if (targetElement.value instanceof HTMLElement) {
2846
+ targetElement.value.focus();
2847
+ return;
2848
+ }
2849
+ rootRef.value?.querySelector("[aria-haspopup=\"menu\"]")?.focus();
2850
+ });
2851
+ }
2852
+ function setOpen(nextOpen) {
2853
+ if (nextOpen && isDisabled.value) return;
2854
+ if (isOpen.value !== nextOpen) controllableOpen.setValue(nextOpen);
2855
+ }
2856
+ let resetHoverIntent;
2857
+ const interaction = useDropdownMenuInteraction({
2858
+ rootMenuId: interactionRootMenuId,
2859
+ isOpen: isVisible,
2860
+ disabled: isDisabled,
2861
+ modal,
2862
+ setOpen,
2863
+ isTopLayer: layer.isTopLayer,
2864
+ focusTrigger,
2865
+ beforeOpen: () => resetHoverIntent(),
2866
+ beforeClose: () => resetHoverIntent()
2428
2867
  });
2429
- const { openSubmenuPath, isSubmenuOpen } = submenus;
2430
- const activeMenuPath = computed(() => getParentPath(focusedPath.value));
2431
- const typeahead = useTypeahead({
2432
- items: () => getItemsAtPath(activeMenuPath.value),
2433
- activeIndex: () => focusedPath.value.at(-1) ?? -1,
2434
- getKey: (item) => item.value,
2435
- getTextValue: (item) => item.label,
2436
- isDisabled: (item) => Boolean(item.disabled),
2437
- scopeKey: () => activeMenuPath.value.join("/"),
2438
- onMatch(_item, index) {
2439
- const menuPath = activeMenuPath.value;
2440
- if (setFocusedIndex(index, menuPath)) submenus.closeSubmenusAfter(menuPath);
2441
- }
2868
+ const open = interaction.open;
2869
+ const close = (options = {}) => interaction.close(options);
2870
+ const toggle = interaction.toggle;
2871
+ const cleanupRootMenu = interaction.registerMenu({
2872
+ id: interactionRootMenuId,
2873
+ element: () => menuRef.value,
2874
+ placement: () => actualPlacement.value,
2875
+ isOpen: () => isVisible.value
2442
2876
  });
2443
- const { activeDescendantId, focusMenu, focusTrigger, getItemId, getSubmenuId, getItemElement, getSubmenuElement, isSubmenuOpeningLeft, getMenuActiveDescendant } = useDropdownMenuDom({
2877
+ const dataController = useDropdownMenuDataController({
2878
+ props,
2879
+ items: visibleItems,
2880
+ rootMenuId: interactionRootMenuId,
2444
2881
  menuId,
2445
- rootRef,
2446
2882
  menuRef,
2447
- targetRef: targetElement,
2448
- placement: actualPlacement,
2449
- focusedPath
2450
- });
2451
- const hoverIntent = useDropdownMenuHoverIntent({
2452
- openSubmenuPath,
2453
- getItemElement,
2454
- getSubmenuElement
2883
+ actualPlacement,
2884
+ interaction,
2885
+ onSelect: (item, event) => emit.select?.(item, event)
2455
2886
  });
2456
- const disclosure = useDropdownMenuDisclosure({
2887
+ resetHoverIntent = dataController.resetHoverIntent;
2888
+ const { renderedItems, renderContext, getItemProps, getItemSlotProps } = useDropdownMenuRenderItems({
2457
2889
  props,
2458
- emit,
2459
- rootRef,
2460
- menuRef,
2461
- targetRef: targetElement,
2462
- setControllableOpen: controllableOpen.setValue,
2463
- isDisabled,
2464
- isOpen,
2465
- isVisible,
2466
- focusedPath,
2467
- resetFocus,
2468
- resetSubmenus: submenus.resetSubmenus,
2469
- resetHoverIntent: hoverIntent.resetHoverIntent,
2470
- focusItem,
2471
- focusMenu,
2472
- focusTrigger,
2473
- layer
2890
+ actualPlacement,
2891
+ items: visibleItems,
2892
+ getItemId: dataController.getItemId,
2893
+ getSubmenuId: dataController.getSubmenuId,
2894
+ getMenuActiveDescendant: dataController.getMenuActiveDescendant,
2895
+ getItemElement: dataController.getItemElement,
2896
+ isItemFocused: dataController.isItemFocused,
2897
+ isSubmenuOpen: dataController.isSubmenuOpen,
2898
+ activateItem: dataController.activateItem,
2899
+ focusHoveredItem: dataController.focusHoveredItem,
2900
+ selectItem: dataController.selectItem,
2901
+ openSubmenu: dataController.openSubmenu,
2902
+ closeSubmenu: dataController.closeSubmenu,
2903
+ close
2474
2904
  });
2475
- const { open, close, toggle } = disclosure;
2476
- const { activateItem, selectItem, openSubmenu, closeSubmenu, focusHoveredItem, onTriggerClick, onTriggerKeydown, onMenuKeydown, onMenuMousemove, onMenuMouseleave } = useDropdownMenuInteractions({
2905
+ const presentation = useDropdownMenuPresentation({
2477
2906
  props,
2478
- emit,
2907
+ rootMenuId: interactionRootMenuId,
2908
+ menuId,
2479
2909
  isDisabled,
2480
- focusedPath,
2481
- openSubmenuPath,
2482
- getItemAtPath,
2483
- focusItem,
2484
- moveFocus,
2485
- handleTypeahead: typeahead.handleKey,
2486
- submenus,
2487
- hoverIntent,
2488
- disclosure,
2489
- isSubmenuOpeningLeft
2490
- });
2491
- const rootClass = computed(() => bem("rp-dropdown-menu", {
2492
- [`placement-${actualPlacement.value}`]: true,
2493
- target: isExplicitTarget.value,
2494
- open: isVisible.value,
2495
- disabled: isDisabled.value
2496
- }));
2497
- const contentClass = computed(() => bem("rp-dropdown-menu__content", { "has-submenu": contentHasSubmenu.value }));
2498
- const triggerProps = computed(() => ({
2499
- "aria-controls": isDisabled.value ? void 0 : menuId.value,
2500
- "aria-expanded": isDisabled.value ? void 0 : isVisible.value,
2501
- "aria-haspopup": "menu",
2502
- disabled: isDisabled.value || void 0,
2503
- onClick: onTriggerClick,
2504
- onKeydown: onTriggerKeydown
2505
- }));
2506
- const contentProps = computed(() => ({
2507
- id: menuId.value,
2508
- role: "menu",
2509
- tabindex: -1,
2510
- "aria-label": props.ariaLabel || void 0,
2511
- "aria-activedescendant": activeDescendantId.value,
2512
- onKeydown: onMenuKeydown,
2513
- onMousemove: onMenuMousemove,
2514
- onMouseleave: onMenuMouseleave
2515
- }));
2516
- const slotProps = computed(() => ({
2517
- triggerProps: triggerProps.value,
2518
- isOpen: isVisible.value,
2910
+ isVisible,
2911
+ isExplicitTarget,
2912
+ actualPlacement,
2913
+ contentHasSubmenu,
2914
+ activeDescendantId: dataController.activeDescendantId,
2915
+ interaction,
2916
+ resetHoverIntent,
2917
+ onMenuMousemove: dataController.onMenuMousemove,
2519
2918
  open,
2520
2919
  close,
2521
2920
  toggle
2522
- }));
2523
- const { renderedItems, renderContext, getItemProps, getItemSlotProps } = useDropdownMenuRenderItems({
2524
- props,
2525
- actualPlacement,
2526
- items: visibleItems,
2527
- getItemId,
2528
- getSubmenuId,
2529
- getMenuActiveDescendant,
2530
- getItemElement,
2531
- isItemFocused,
2532
- isSubmenuOpen,
2533
- activateItem,
2534
- focusHoveredItem,
2535
- selectItem,
2536
- openSubmenu,
2537
- closeSubmenu,
2538
- close
2539
2921
  });
2540
- watch([isExplicitTarget, targetElement], ([explicit, target], _previous, onCleanup) => {
2541
- if (!explicit || !target) return;
2542
- target.addEventListener("click", onTriggerClick);
2543
- target.addEventListener("keydown", onTriggerKeydown);
2544
- onCleanup(() => {
2545
- target.removeEventListener("click", onTriggerClick);
2546
- target.removeEventListener("keydown", onTriggerKeydown);
2547
- });
2548
- }, { flush: "sync" });
2549
- watch(isVisible, (visible) => {
2550
- if (!visible) typeahead.reset();
2551
- }, { flush: "sync" });
2552
- watch([
2553
- isExplicitTarget,
2922
+ useDropdownMenuTargetBindings({
2923
+ interaction,
2924
+ rootRef,
2925
+ menuRef,
2554
2926
  targetElement,
2927
+ isExplicitTarget,
2555
2928
  menuId,
2556
2929
  isVisible,
2557
- isDisabled
2558
- ], ([explicit, target, id, visible, disabled], _previous, onCleanup) => {
2559
- if (!explicit || !target) return;
2560
- const snapshot = new Map([
2561
- "aria-controls",
2562
- "aria-expanded",
2563
- "aria-haspopup",
2564
- "aria-disabled"
2565
- ].map((attribute) => [attribute, target.getAttribute(attribute)]));
2566
- if (disabled) {
2567
- target.removeAttribute("aria-controls");
2568
- target.removeAttribute("aria-expanded");
2569
- target.removeAttribute("aria-haspopup");
2570
- target.setAttribute("aria-disabled", "true");
2571
- } else {
2572
- target.setAttribute("aria-controls", id);
2573
- target.setAttribute("aria-expanded", String(visible));
2574
- target.setAttribute("aria-haspopup", "menu");
2575
- target.removeAttribute("aria-disabled");
2576
- }
2577
- onCleanup(() => {
2578
- for (const [attribute, value] of snapshot) if (value == null) target.removeAttribute(attribute);
2579
- else target.setAttribute(attribute, value);
2580
- });
2581
- }, { flush: "sync" });
2930
+ isDisabled,
2931
+ ignoredTargets: () => props.ignore ?? [],
2932
+ pointerDownOutside: emit.pointerDownOutside,
2933
+ focusOutside: emit.focusOutside,
2934
+ interactOutside: emit.interactOutside
2935
+ });
2936
+ const layeredContentStyle = computed(() => ({
2937
+ ...contentStyle.value,
2938
+ zIndex: layer.zIndex.value
2939
+ }));
2940
+ onBeforeUnmount(cleanupRootMenu);
2582
2941
  return {
2583
2942
  rootRef,
2584
2943
  menuRef,
@@ -2589,33 +2948,30 @@ function useDropdownMenu(props, emit = {}) {
2589
2948
  visibleItems,
2590
2949
  renderedItems,
2591
2950
  renderContext,
2592
- focusedIndex,
2593
- focusedPath,
2594
- activeDescendantId,
2595
- rootClass,
2596
- contentClass,
2597
- contentStyle: computed(() => ({
2598
- ...contentStyle.value,
2599
- zIndex: layer.zIndex.value
2600
- })),
2951
+ focusedIndex: dataController.focusedIndex,
2952
+ focusedPath: dataController.focusedPath,
2953
+ activeDescendantId: dataController.activeDescendantId,
2954
+ rootClass: presentation.rootClass,
2955
+ contentClass: presentation.contentClass,
2956
+ contentStyle: layeredContentStyle,
2601
2957
  arrowStyle,
2602
2958
  actualPlacement,
2603
2959
  placementSide,
2604
2960
  isTargetMode: isExplicitTarget,
2605
2961
  teleportTo,
2606
2962
  shouldTeleport,
2607
- triggerProps,
2608
- contentProps,
2609
- slotProps,
2963
+ triggerProps: presentation.triggerProps,
2964
+ contentProps: presentation.contentProps,
2965
+ slotProps: presentation.slotProps,
2610
2966
  open,
2611
2967
  close,
2612
2968
  toggle,
2613
- selectItem,
2614
- openSubmenu,
2615
- closeSubmenu,
2616
- onItemMouseenter: focusHoveredItem,
2617
- onTriggerKeydown,
2618
- onMenuKeydown,
2969
+ selectItem: dataController.selectItem,
2970
+ openSubmenu: dataController.openSubmenu,
2971
+ closeSubmenu: dataController.closeSubmenu,
2972
+ onItemMouseenter: dataController.focusHoveredItem,
2973
+ onTriggerKeydown: (event) => interaction.onTriggerKeydown(event),
2974
+ onMenuKeydown: presentation.onMenuKeydown,
2619
2975
  getItemProps,
2620
2976
  getItemSlotProps
2621
2977
  };
@@ -2701,12 +3057,17 @@ var dropdown_menu_default = /* @__PURE__ */ defineVaporComponent({
2701
3057
  focusOutside: (event) => emit("focusOutside", event),
2702
3058
  interactOutside: (event) => emit("interactOutside", event)
2703
3059
  });
3060
+ const templateRefs = {
3061
+ root: rootRef,
3062
+ menu: menuRef,
3063
+ arrow: arrowRef
3064
+ };
2704
3065
  const { getPartAttrs, getRootAttrs } = useStylesApi(props, "root");
2705
3066
  const state = computed(() => isVisible.value ? "open" : "closed");
2706
3067
  const rootAttrs = computed(() => getRootAttrs({
2707
3068
  class: rootClass.value,
2708
3069
  "data-state": state.value,
2709
- "data-disabled": presence(props.disabled)
3070
+ "data-disabled": toPresenceAttribute(props.disabled)
2710
3071
  }));
2711
3072
  const publicSlotProps = computed(() => {
2712
3073
  const partAttrs = getPartAttrs("trigger");
@@ -2717,7 +3078,7 @@ var dropdown_menu_default = /* @__PURE__ */ defineVaporComponent({
2717
3078
  ...props.classNames?.trigger !== void 0 ? { class: partAttrs.class } : {},
2718
3079
  ...props.styles?.trigger !== void 0 ? { style: partAttrs.style } : {},
2719
3080
  "data-state": state.value,
2720
- "data-disabled": presence(props.disabled)
3081
+ "data-disabled": toPresenceAttribute(props.disabled)
2721
3082
  }
2722
3083
  };
2723
3084
  });
@@ -2753,10 +3114,10 @@ var dropdown_menu_default = /* @__PURE__ */ defineVaporComponent({
2753
3114
  setInsertionState(n26, null, 0);
2754
3115
  createIf(() => __props.arrow, () => {
2755
3116
  const n7 = t0();
2756
- _setTemplateRef(n7, arrowRef, null, "arrowRef");
2757
3117
  renderEffect(() => {
2758
3118
  setAttr(n7, "data-side", unref(placementSide));
2759
3119
  setStyle(n7, unref(arrowStyle));
3120
+ _setTemplateRef(n7, templateRefs.arrow);
2760
3121
  });
2761
3122
  return n7;
2762
3123
  });
@@ -2799,12 +3160,12 @@ var dropdown_menu_default = /* @__PURE__ */ defineVaporComponent({
2799
3160
  return n23;
2800
3161
  }, null, 129)];
2801
3162
  }, { _: 8 }) });
2802
- _setTemplateRef(n26, menuRef, null, "menuRef");
3163
+ setTemplateRefBinding(n26, () => templateRefs.menu, _setTemplateRef);
2803
3164
  return n26;
2804
3165
  }, null, 129);
2805
3166
  }, { _: 8 }));
2806
3167
  });
2807
- setStaticTemplateRef(n29, rootRef, null, "rootRef");
3168
+ setTemplateRefBinding(n29, () => templateRefs.root);
2808
3169
  return n29;
2809
3170
  }
2810
3171
  });