ropav 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/README.md +7 -1
  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/ancestry.js +136 -0
  7. package/dist/aria.js +14 -0
  8. package/dist/aspect-ratio.js +1 -1
  9. package/dist/attributes.js +9 -0
  10. package/dist/attributes2.js +29 -0
  11. package/dist/avatar.css +19 -19
  12. package/dist/avatar.js +9 -17
  13. package/dist/badge.css +16 -16
  14. package/dist/badge.js +5 -5
  15. package/dist/button-group.js +1 -1
  16. package/dist/button-link.js +4 -3
  17. package/dist/button.js +4 -3
  18. package/dist/button.scss_vue_type_style_index_0_src_true_lang.js +1 -1
  19. package/dist/calendar.css +173 -0
  20. package/dist/calendar.js +714 -0
  21. package/dist/calendar2.js +37 -0
  22. package/dist/card.js +1 -1
  23. package/dist/checkbox.css +32 -32
  24. package/dist/checkbox.js +22 -28
  25. package/dist/chevron-left.js +12 -0
  26. package/dist/collapse.css +6 -6
  27. package/dist/collapse.js +7 -6
  28. package/dist/collectionNavigation.js +2 -99
  29. package/dist/color-input.css +19 -19
  30. package/dist/color-input.js +14 -13
  31. package/dist/color-picker.css +40 -40
  32. package/dist/color-picker.js +247 -276
  33. package/dist/color-picker2.js +1 -9
  34. package/dist/color-swatch.css +4 -4
  35. package/dist/color-swatch.js +6 -8
  36. package/dist/color.js +5 -10
  37. package/dist/combobox.css +251 -0
  38. package/dist/combobox.js +410 -0
  39. package/dist/componentColors.js +125 -84
  40. package/dist/componentRef.js +26 -0
  41. package/dist/components/accordion/accordionContext.d.ts +3 -0
  42. package/dist/components/accordion/accordionModel.d.ts +14 -0
  43. package/dist/components/accordion/index.d.ts +2 -1
  44. package/dist/components/accordion/index.js +1 -1
  45. package/dist/components/accordion/types.d.ts +1 -2
  46. package/dist/components/accordion/useAccordion.d.ts +2 -2
  47. package/dist/components/accordion/useAccordionItem.d.ts +2 -0
  48. package/dist/components/calendar/calendar.d.ts +30 -0
  49. package/dist/components/calendar/calendarBehavior.d.ts +49 -0
  50. package/dist/components/calendar/index.d.ts +3 -0
  51. package/dist/components/calendar/index.js +3 -0
  52. package/dist/components/calendar/types.d.ts +53 -0
  53. package/dist/components/calendar/useCalendar.d.ts +24 -0
  54. package/dist/components/checkbox/useCheckbox.d.ts +1 -2
  55. package/dist/components/color-picker/index.js +2 -2
  56. package/dist/components/color-picker/types.d.ts +2 -2
  57. package/dist/components/color-picker/useColorPickerDrag.d.ts +1 -1
  58. package/dist/components/color-picker/useColorPickerSaturation.d.ts +22 -0
  59. package/dist/components/color-picker/useColorPickerSlider.d.ts +20 -0
  60. package/dist/components/color-picker/useColorPickerValue.d.ts +1 -1
  61. package/dist/components/combobox/combobox.d.ts +29 -0
  62. package/dist/components/combobox/comboboxModel.d.ts +4 -0
  63. package/dist/components/combobox/index.d.ts +3 -0
  64. package/dist/components/combobox/index.js +2 -0
  65. package/dist/components/combobox/types.d.ts +43 -0
  66. package/dist/components/combobox/useCombobox.d.ts +45 -0
  67. package/dist/components/date-picker/date-picker.d.ts +33 -0
  68. package/dist/components/date-picker/datePickerModel.d.ts +10 -0
  69. package/dist/components/date-picker/index.d.ts +4 -0
  70. package/dist/components/date-picker/index.js +2 -0
  71. package/dist/components/date-picker/types.d.ts +57 -0
  72. package/dist/components/date-picker/useDatePicker.d.ts +29 -0
  73. package/dist/components/date-picker/useDatePickerValueState.d.ts +21 -0
  74. package/dist/components/dialog/{dialog-core.d.ts → dialogContext.d.ts} +1 -5
  75. package/dist/components/dialog/useDialogContentInteractions.d.ts +18 -0
  76. package/dist/components/dialog/useDialogRootState.d.ts +19 -0
  77. package/dist/components/dropdown-menu/dropdown-menu-content.d.ts +2 -2
  78. package/dist/components/dropdown-menu/dropdown-menu-item-row.d.ts +1 -1
  79. package/dist/components/dropdown-menu/dropdown-menu-items.d.ts +1 -1
  80. package/dist/components/dropdown-menu/dropdown-menu-model.d.ts +8 -0
  81. package/dist/components/dropdown-menu/dropdown-menu-root.d.ts +2 -2
  82. package/dist/components/dropdown-menu/dropdown-menu-sub-content.d.ts +2 -2
  83. package/dist/components/dropdown-menu/dropdown-menu-sub-trigger.d.ts +1 -1
  84. package/dist/components/dropdown-menu/dropdown-menu-sub.d.ts +1 -1
  85. package/dist/components/dropdown-menu/dropdown-menu.d.ts +2 -2
  86. package/dist/components/dropdown-menu/{dropdown-menu-primitive-core.d.ts → dropdownMenuContext.d.ts} +17 -30
  87. package/dist/components/dropdown-menu/dropdownMenuDataModel.d.ts +20 -0
  88. package/dist/components/dropdown-menu/dropdownMenuInteraction.d.ts +3 -0
  89. package/dist/components/dropdown-menu/dropdownMenuInteractionNavigation.d.ts +24 -0
  90. package/dist/components/dropdown-menu/dropdownMenuInteractionRegistry.d.ts +42 -0
  91. package/dist/components/dropdown-menu/dropdownMenuInteractionTypes.d.ts +114 -0
  92. package/dist/components/dropdown-menu/useDropdownMenu.d.ts +11 -11
  93. package/dist/components/dropdown-menu/useDropdownMenuContextTrigger.d.ts +9 -0
  94. package/dist/components/dropdown-menu/useDropdownMenuDataController.d.ts +35 -0
  95. package/dist/components/dropdown-menu/useDropdownMenuDataRegistration.d.ts +24 -0
  96. package/dist/components/dropdown-menu/useDropdownMenuHoverIntent.d.ts +6 -5
  97. package/dist/components/dropdown-menu/useDropdownMenuInteractionDismissal.d.ts +17 -0
  98. package/dist/components/dropdown-menu/useDropdownMenuInteractionKeyboard.d.ts +19 -0
  99. package/dist/components/dropdown-menu/useDropdownMenuPortalPosition.d.ts +0 -1
  100. package/dist/components/dropdown-menu/useDropdownMenuPresentation.d.ts +28 -0
  101. package/dist/components/dropdown-menu/useDropdownMenuPrimitiveContent.d.ts +2 -2
  102. package/dist/components/dropdown-menu/useDropdownMenuRenderItems.d.ts +1 -1
  103. package/dist/components/dropdown-menu/useDropdownMenuTargetBindings.d.ts +19 -0
  104. package/dist/components/dropzone/dropzone.d.ts +31 -0
  105. package/dist/components/dropzone/dropzoneModel.d.ts +16 -0
  106. package/dist/components/dropzone/index.d.ts +3 -0
  107. package/dist/components/dropzone/index.js +2 -0
  108. package/dist/components/dropzone/types.d.ts +56 -0
  109. package/dist/components/dropzone/useDropzone.d.ts +25 -0
  110. package/dist/components/file-input/file-input.d.ts +29 -0
  111. package/dist/components/file-input/index.d.ts +3 -0
  112. package/dist/components/file-input/index.js +2 -0
  113. package/dist/components/file-input/types.d.ts +37 -0
  114. package/dist/components/file-input/useFileInput.d.ts +15 -0
  115. package/dist/components/floating/index.js +2 -2
  116. package/dist/components/floating/useFloatingPosition.d.ts +1 -14
  117. package/dist/components/floating/useFloatingTargetLifecycle.d.ts +32 -0
  118. package/dist/components/floating/useHoverDisclosure.d.ts +10 -0
  119. package/dist/components/hover-card/hover-card.d.ts +25 -0
  120. package/dist/components/hover-card/index.d.ts +3 -0
  121. package/dist/components/hover-card/index.js +2 -0
  122. package/dist/components/hover-card/types.d.ts +45 -0
  123. package/dist/components/hover-card/useHoverCard.d.ts +22 -0
  124. package/dist/components/hover-card/useHoverCardDisclosure.d.ts +19 -0
  125. package/dist/components/multi-select/index.d.ts +3 -0
  126. package/dist/components/multi-select/index.js +2 -0
  127. package/dist/components/multi-select/multi-select.d.ts +30 -0
  128. package/dist/components/multi-select/multiSelectModel.d.ts +3 -0
  129. package/dist/components/multi-select/types.d.ts +49 -0
  130. package/dist/components/multi-select/useMultiSelect.d.ts +46 -0
  131. package/dist/components/number-input/numberInputModel.d.ts +15 -0
  132. package/dist/components/number-input/useNumberInput.d.ts +3 -19
  133. package/dist/components/pagination/index.d.ts +1 -1
  134. package/dist/components/pagination/index.js +1 -1
  135. package/dist/components/pagination/paginationModel.d.ts +4 -0
  136. package/dist/components/pagination/types.d.ts +15 -0
  137. package/dist/components/pagination/usePagination.d.ts +3 -19
  138. package/dist/components/popover/usePopoverBindings.d.ts +13 -0
  139. package/dist/components/progress/progressModel.d.ts +8 -0
  140. package/dist/components/progress/useProgress.d.ts +0 -6
  141. package/dist/components/radio/useRadio.d.ts +1 -2
  142. package/dist/components/scroll-area/scroll-area.d.ts +1 -1
  143. package/dist/components/scroll-area/scrollAreaModel.d.ts +64 -0
  144. package/dist/components/scroll-area/useScrollArea.d.ts +70 -53
  145. package/dist/components/scroll-area/{useScrollAreaPresentation.d.ts → useScrollAreaAttributes.d.ts} +37 -17
  146. package/dist/components/scroll-area/useScrollAreaDerivedState.d.ts +8 -0
  147. package/dist/components/scroll-area/useScrollAreaMeasurement.d.ts +35 -0
  148. package/dist/components/scroll-area/useScrollAreaPointer.d.ts +11 -9
  149. package/dist/components/scroll-area/useScrollAreaVisibility.d.ts +15 -20
  150. package/dist/components/segmented-control/index.d.ts +3 -0
  151. package/dist/components/segmented-control/index.js +2 -0
  152. package/dist/components/segmented-control/segmented-control.d.ts +26 -0
  153. package/dist/components/segmented-control/types.d.ts +47 -0
  154. package/dist/components/segmented-control/useSegmentedControl.d.ts +15 -0
  155. package/dist/components/select/useSelect.d.ts +31 -19
  156. package/dist/components/slider/rangeSliderModel.d.ts +41 -0
  157. package/dist/components/slider/{sliderCore.d.ts → sliderModel.d.ts} +0 -1
  158. package/dist/components/slider/useRangeSlider.d.ts +9 -17
  159. package/dist/components/slider/useRangeSliderPointer.d.ts +1 -1
  160. package/dist/components/slider/useRangeSliderValueState.d.ts +40 -0
  161. package/dist/components/slider/useSlider.d.ts +7 -13
  162. package/dist/components/slider/useSliderPointerPreview.d.ts +22 -0
  163. package/dist/components/slider/useSliderTooltipState.d.ts +40 -0
  164. package/dist/components/slider/useSliderValueState.d.ts +33 -0
  165. package/dist/components/tags-input/index.d.ts +3 -0
  166. package/dist/components/tags-input/index.js +2 -0
  167. package/dist/components/tags-input/tags-input.d.ts +27 -0
  168. package/dist/components/tags-input/tagsInputModel.d.ts +12 -0
  169. package/dist/components/tags-input/types.d.ts +37 -0
  170. package/dist/components/tags-input/useTagsInput.d.ts +23 -0
  171. package/dist/components/teleport-provider/index.js +1 -1
  172. package/dist/components/teleport-provider/useTeleportTarget.d.ts +1 -2
  173. package/dist/components/toast/useToastLifecycle.d.ts +19 -0
  174. package/dist/components/tooltip/useTooltip.d.ts +1 -2
  175. package/dist/components/tooltip/useTooltipTargetBindings.d.ts +12 -0
  176. package/dist/css.js +12 -0
  177. package/dist/date-picker.css +49 -0
  178. package/dist/date-picker.js +809 -0
  179. package/dist/dialog.js +215 -218
  180. package/dist/direction.js +6 -0
  181. package/dist/dropdown-menu.css +21 -3
  182. package/dist/dropdown-menu.js +1626 -1185
  183. package/dist/dropzone.css +135 -0
  184. package/dist/dropzone.js +431 -0
  185. package/dist/events.js +59 -0
  186. package/dist/field.css +8 -8
  187. package/dist/field.js +13 -25
  188. package/dist/file-input.css +135 -0
  189. package/dist/file-input.js +198 -0
  190. package/dist/floatingOffset.js +11 -0
  191. package/dist/focus-trap.js +7 -5
  192. package/dist/focusNavigation.js +37 -0
  193. package/dist/hover-card.css +80 -0
  194. package/dist/hover-card.js +402 -0
  195. package/dist/icon-button.css +8 -8
  196. package/dist/icon-button.js +7 -6
  197. package/dist/index.d.ts +9 -0
  198. package/dist/index.js +20 -9
  199. package/dist/input.css +24 -24
  200. package/dist/input.js +19 -31
  201. package/dist/interactive.js +18 -0
  202. package/dist/internal/composables/useOverlayLayer.d.ts +12 -0
  203. package/dist/modal.js +2 -2
  204. package/dist/multi-select.css +316 -0
  205. package/dist/multi-select.js +481 -0
  206. package/dist/nativeChoice.js +169 -0
  207. package/dist/number-input.css +18 -18
  208. package/dist/number-input.js +44 -44
  209. package/dist/number.js +22 -0
  210. package/dist/overlay.js +3 -2
  211. package/dist/pagination.css +28 -28
  212. package/dist/pagination.js +66 -73
  213. package/dist/pointerSession.js +130 -0
  214. package/dist/popover.css +55 -37
  215. package/dist/popover.js +75 -101
  216. package/dist/progress.js +10 -13
  217. package/dist/query.js +40 -0
  218. package/dist/radio.css +18 -18
  219. package/dist/radio.js +30 -36
  220. package/dist/rafScheduler.js +34 -0
  221. package/dist/scroll-area.css +28 -28
  222. package/dist/scroll-area.js +540 -554
  223. package/dist/segmented-control.css +176 -0
  224. package/dist/segmented-control.js +382 -0
  225. package/dist/select.css +50 -48
  226. package/dist/select.js +131 -289
  227. package/dist/slider.css +191 -191
  228. package/dist/slider.js +720 -706
  229. package/dist/styles-api.d.ts +0 -1
  230. package/dist/styles-api.js +1 -4
  231. package/dist/switch.css +17 -17
  232. package/dist/switch.js +14 -15
  233. package/dist/tabs.css +43 -43
  234. package/dist/tabs.js +29 -56
  235. package/dist/tags-input.css +197 -0
  236. package/dist/tags-input.js +380 -0
  237. package/dist/text.js +17 -0
  238. package/dist/textarea.css +26 -26
  239. package/dist/textarea.js +21 -32
  240. package/dist/toast.css +26 -26
  241. package/dist/toast.js +141 -118
  242. package/dist/tooltip.css +12 -12
  243. package/dist/tooltip.js +64 -63
  244. package/dist/useCollapse.js +8 -11
  245. package/dist/useControlState.js +3 -6
  246. package/dist/useEditableOptionList.js +216 -0
  247. package/dist/useFlatOptionCollection.js +134 -0
  248. package/dist/useFloatingPosition.js +14 -40
  249. package/dist/useFloatingTargetLifecycle.js +127 -0
  250. package/dist/useFormControl.js +1 -0
  251. package/dist/useHoverDisclosure.js +561 -0
  252. package/dist/useNativeChoiceTransaction.js +46 -0
  253. package/dist/useNativeFileSelection.js +86 -0
  254. package/dist/useOverlayZIndex.js +246 -19
  255. package/dist/useTeleportTarget.js +3 -14
  256. package/dist/useTypeahead.js +94 -0
  257. package/dist/utils/aria.d.ts +3 -0
  258. package/dist/utils/attributes.d.ts +2 -0
  259. package/dist/utils/bem.d.ts +3 -0
  260. package/dist/utils/collectionNavigation.d.ts +2 -0
  261. package/dist/utils/color.d.ts +7 -0
  262. package/dist/{components/color-picker/color-picker-utils.d.ts → utils/colorPicker.d.ts} +5 -4
  263. package/dist/utils/componentColors.d.ts +7 -0
  264. package/dist/utils/css.d.ts +3 -0
  265. package/dist/utils/date.d.ts +13 -0
  266. package/dist/utils/dateAvailability.d.ts +7 -0
  267. package/dist/utils/dom/ancestry.d.ts +8 -0
  268. package/dist/utils/dom/attributes.d.ts +16 -0
  269. package/dist/utils/dom/componentRef.d.ts +4 -0
  270. package/dist/utils/dom/direction.d.ts +2 -0
  271. package/dist/utils/dom/events.d.ts +5 -0
  272. package/dist/utils/dom/files.d.ts +3 -0
  273. package/dist/utils/dom/focusNavigation.d.ts +7 -0
  274. package/dist/utils/dom/interactive.d.ts +2 -0
  275. package/dist/utils/dom/nativeChoice.d.ts +17 -0
  276. package/dist/utils/dom/pointerSession.d.ts +20 -0
  277. package/dist/utils/dom/query.d.ts +4 -0
  278. package/dist/utils/dom/scroll.d.ts +24 -0
  279. package/dist/utils/floatingOffset.d.ts +10 -0
  280. package/dist/utils/geometry.d.ts +14 -0
  281. package/dist/utils/indexPath.d.ts +6 -0
  282. package/dist/utils/number.d.ts +4 -0
  283. package/dist/utils/optionFilter.d.ts +5 -0
  284. package/dist/utils/rafScheduler.d.ts +4 -0
  285. package/dist/utils/text.d.ts +2 -0
  286. package/docs/code-architecture.md +118 -0
  287. package/docs/public-floating-api.md +13 -9
  288. package/docs/public-styles-api.md +25 -12
  289. package/package.json +51 -14
  290. package/src/styles/_mixins.scss +26 -0
  291. package/src/styles/styles-manifest.json +1 -2
  292. package/dist/components/dropdown-menu/dropdown-menu-outside.d.ts +0 -3
  293. package/dist/components/dropdown-menu/dropdown-menu-utils.d.ts +0 -21
  294. package/dist/components/dropdown-menu/useDropdownMenuDisclosure.d.ts +0 -36
  295. package/dist/components/dropdown-menu/useDropdownMenuDom.d.ts +0 -29
  296. package/dist/components/dropdown-menu/useDropdownMenuInteractions.d.ts +0 -52
  297. package/dist/components/dropdown-menu/useDropdownMenuNavigation.d.ts +0 -17
  298. package/dist/components/dropdown-menu/useDropdownSubmenus.d.ts +0 -18
  299. package/dist/components/scroll-area/scrollAreaCore.d.ts +0 -30
  300. package/dist/components/scroll-area/useScrollAreaControls.d.ts +0 -25
  301. package/dist/components/scroll-area/useScrollAreaMetrics.d.ts +0 -38
  302. package/dist/components/select/useSelectNativeControl.d.ts +0 -9
  303. package/dist/components/select/useSelectNativeValue.d.ts +0 -9
  304. package/dist/floating.js +0 -415
  305. /package/dist/components/alert/{useAlertColor.d.ts → alertColor.d.ts} +0 -0
  306. /package/dist/components/avatar/{useAvatarColor.d.ts → avatarColor.d.ts} +0 -0
  307. /package/dist/components/badge/{useBadgeColor.d.ts → badgeColor.d.ts} +0 -0
  308. /package/dist/components/button/{useButtonColor.d.ts → buttonColor.d.ts} +0 -0
  309. /package/dist/components/color-swatch/{useColorSwatchColor.d.ts → colorSwatchColor.d.ts} +0 -0
  310. /package/dist/components/toast/{useToastColor.d.ts → toastColor.d.ts} +0 -0
@@ -1,41 +1,48 @@
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 { 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";
6
+ import { t as useRequiredInject } from "./useRequiredInject.js";
7
+ import { t as clamp } from "./number.js";
10
8
  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;
9
+ import { t as resolveHTMLElementRef } from "./componentRef.js";
10
+ import { r as isEventWithinTargets, t as createCancelableCustomEvent } from "./events.js";
11
+ import { n as useOverlayZIndex, r as useOverlayLayer } from "./useOverlayZIndex.js";
12
+ import { t as useFloatingPosition } from "./useFloatingPosition.js";
13
+ import { t as useFloatingTargetLifecycle } from "./useFloatingTargetLifecycle.js";
14
+ import { n as useTeleportTarget, t as provideTeleportTarget } from "./useTeleportTarget.js";
15
+ import { t as useTypeahead } from "./useTypeahead.js";
16
+ import { VaporTeleport, VaporTransition, child, computed, createComponent, createComponentWithFallback, createDynamicComponent, createFor, createIf, createSlot, createTemplateRefSetter, defineVaporComponent, extend, isRef, mergeProps, nextTick, onBeforeUnmount, onMounted, onUnmounted, provide, ref, renderEffect, resolveComponent, setAttr, setDynamicProps, setInsertionState, setStyle, setTemplateRefBinding, setText, shallowRef, template, toDisplayString, txt, unref, useAttrs, useId, watch } from "vue";
17
+ //#region src/utils/geometry.ts
18
+ function getClientPoint(event) {
19
+ return {
20
+ x: event.clientX,
21
+ y: event.clientY
22
+ };
20
23
  }
21
- function getFocusTarget(options) {
22
- if (typeof options === "string") return options;
23
- return options?.focus ?? "first";
24
+ function hasMeasuredRect(rect) {
25
+ return rect.width > 0 || rect.height > 0;
26
+ }
27
+ function isPointInRect(point, rect, padding = 0) {
28
+ return point.x >= rect.left - padding && point.x <= rect.right + padding && point.y >= rect.top - padding && point.y <= rect.bottom + padding;
24
29
  }
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;
30
+ function getTriangleArea(a, b, c) {
31
+ return Math.abs((a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2);
32
+ }
33
+ function isPointInTriangle(point, triangle, tolerance = .5) {
34
+ const [a, b, c] = triangle;
35
+ const area = getTriangleArea(a, b, c);
36
+ const area1 = getTriangleArea(point, b, c);
37
+ const area2 = getTriangleArea(a, point, c);
38
+ const area3 = getTriangleArea(a, b, point);
39
+ return Math.abs(area - (area1 + area2 + area3)) < tolerance;
33
40
  }
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)));
41
+ function clampPointToRect(point, rect) {
42
+ return {
43
+ x: clamp(point.x, rect.left, rect.right),
44
+ y: clamp(point.y, rect.top, rect.bottom)
45
+ };
39
46
  }
40
47
  function createPointRect(point) {
41
48
  return {
@@ -50,202 +57,57 @@ function createPointRect(point) {
50
57
  toJSON: () => ({})
51
58
  };
52
59
  }
60
+ //#endregion
61
+ //#region src/components/dropdown-menu/dropdownMenuContext.ts
62
+ var rootKey = Symbol("dropdown-menu-root");
63
+ var menuKey = Symbol("dropdown-menu-content");
64
+ var subKey = Symbol("dropdown-menu-sub");
65
+ var radioGroupKey = Symbol("dropdown-menu-radio-group");
66
+ var checkedKey = Symbol("dropdown-menu-checked");
53
67
  function createVirtualAnchor(point) {
54
68
  return { getBoundingClientRect: () => createPointRect(point) };
55
69
  }
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
70
  function createSelectEvent(originalEvent, value) {
65
- return createCustomEvent("dropdown-menu-select", {
71
+ return createCancelableCustomEvent("dropdown-menu-select", {
66
72
  originalEvent,
67
73
  value
68
74
  }, originalEvent);
69
75
  }
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
76
  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)
77
+ const id = options.parentSub?.menuId ?? options.root.interactionRootMenuId;
78
+ const interaction = options.parentSub ? options.parentSub.interaction.connectContent({
79
+ element: () => options.element.value,
80
+ placement: () => options.actualPlacement.value,
81
+ stopKeyPropagation: true,
82
+ onEscape: options.onEscape
83
+ }) : options.root.interaction.connectRootMenu({
84
+ element: () => options.element.value,
85
+ placement: () => options.actualPlacement.value,
86
+ stopKeyPropagation: true,
87
+ onEscape: options.onEscape
169
88
  });
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
- }
89
+ function connectItem(item) {
90
+ return interaction.connectItem({
91
+ id: item.id,
92
+ element: item.element,
93
+ textValue: item.textValue,
94
+ disabled: item.disabled,
95
+ submenu: item.submenu?.interaction,
96
+ submenuDirection: () => item.submenu?.actualPlacement.value.startsWith("left") ? "left" : "right",
97
+ select: item.activate,
98
+ closeOnSelect: item.closeOnSelect
99
+ });
231
100
  }
232
101
  const context = {
233
102
  root: options.root,
103
+ id,
234
104
  element: options.element,
235
- activeId,
236
105
  actualPlacement: options.actualPlacement,
237
106
  parentSub: options.parentSub,
238
- registerItem,
239
- unregisterItem,
240
- registerSub,
241
- unregisterSub,
242
- closeSubmenus,
243
- setActive,
244
- isActive: (id) => activeId.value === id,
245
- focus,
246
- focusElement,
247
- onKeydown
107
+ interaction,
108
+ connectItem
248
109
  };
110
+ onBeforeUnmount(interaction.dispose);
249
111
  return context;
250
112
  }
251
113
  function usePrimitiveItem(componentName, props, emitSelect, options) {
@@ -254,13 +116,12 @@ function usePrimitiveItem(componentName, props, emitSelect, options) {
254
116
  const id = computed(() => props.id ?? `${generatedId}-item`);
255
117
  const element = ref(null);
256
118
  const isDisabled = computed(() => Boolean(menu.root.disabled.value || props.disabled));
257
- const focused = computed(() => menu.isActive(id.value));
258
- function activate(originalEvent) {
259
- if (isDisabled.value) return;
119
+ function emitSelection(originalEvent) {
120
+ if (isDisabled.value) return void 0;
260
121
  const selectEvent = createSelectEvent(originalEvent, props.value);
261
122
  emitSelect(selectEvent);
262
123
  options.afterSelect?.();
263
- if (!selectEvent.defaultPrevented && (props.closeOnSelect ?? options.defaultCloseOnSelect)) menu.root.close({ returnFocus: true });
124
+ return selectEvent;
264
125
  }
265
126
  const registration = {
266
127
  get id() {
@@ -269,27 +130,35 @@ function usePrimitiveItem(componentName, props, emitSelect, options) {
269
130
  element: () => element.value,
270
131
  textValue: () => props.textValue ?? element.value?.textContent?.trim() ?? "",
271
132
  disabled: () => isDisabled.value,
272
- activate
133
+ activate: emitSelection,
134
+ closeOnSelect: () => props.closeOnSelect ?? options.defaultCloseOnSelect
273
135
  };
274
- menu.registerItem(registration);
275
- watch(id, (nextId, previousId) => {
276
- menu.unregisterItem(previousId);
277
- menu.registerItem(registration);
278
- if (menu.activeId.value === previousId) menu.activeId.value = nextId;
136
+ let itemInteraction = menu.connectItem(registration);
137
+ const focused = computed(() => {
138
+ return id.value === registration.id && itemInteraction.active.value;
279
139
  });
280
- onBeforeUnmount(() => menu.unregisterItem(id.value));
140
+ watch(id, () => {
141
+ const previous = itemInteraction;
142
+ const wasActive = previous.active.value;
143
+ itemInteraction = menu.connectItem(registration);
144
+ previous.dispose();
145
+ if (wasActive) itemInteraction.setActive();
146
+ }, { flush: "sync" });
147
+ onBeforeUnmount(() => itemInteraction.dispose());
281
148
  function setElement(value) {
282
149
  resolveHTMLElementRef(value, id.value, (resolved) => {
283
150
  element.value = resolved;
284
151
  });
285
152
  }
153
+ function activate(originalEvent) {
154
+ itemInteraction.activate(originalEvent);
155
+ }
286
156
  function onPointerenter() {
287
157
  if (isDisabled.value) return;
288
- menu.closeSubmenus();
289
- menu.setActive(id.value);
158
+ itemInteraction.hover();
290
159
  }
291
160
  function select() {
292
- activate(new Event("select"));
161
+ itemInteraction.activate(new Event("select"));
293
162
  }
294
163
  return {
295
164
  menu,
@@ -369,12 +238,12 @@ var dropdown_menu_checkbox_item_default = /* @__PURE__ */ defineVaporComponent({
369
238
  type: props.as === "button" ? "button" : void 0,
370
239
  role: "menuitemcheckbox",
371
240
  tabindex: -1,
372
- disabled: props.as === "button" ? optionalAttr(isDisabled.value) : void 0,
373
- "aria-disabled": optionalAttr(isDisabled.value),
241
+ disabled: props.as === "button" ? toOptionalAttribute(isDisabled.value) : void 0,
242
+ "aria-disabled": toOptionalAttribute(isDisabled.value),
374
243
  "aria-checked": checked.value,
375
- "data-disabled": optionalAttr(isDisabled.value),
376
- "data-focused": optionalAttr(focused.value),
377
- "data-highlighted": optionalAttr(focused.value),
244
+ "data-disabled": toOptionalAttribute(isDisabled.value),
245
+ "data-focused": toOptionalAttribute(focused.value),
246
+ "data-highlighted": toOptionalAttribute(focused.value),
378
247
  "data-state": state.value,
379
248
  class: bem("rp-dropdown-menu__item", {
380
249
  focused: focused.value,
@@ -396,31 +265,6 @@ var dropdown_menu_checkbox_item_default = /* @__PURE__ */ defineVaporComponent({
396
265
  }
397
266
  });
398
267
  //#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
268
  //#region src/components/dropdown-menu/useDropdownMenuPrimitiveContent.ts
425
269
  function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
426
270
  const componentName = submenu ? "RpDropdownMenuSubContent" : "RpDropdownMenuContent";
@@ -434,20 +278,23 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
434
278
  const actualPlacement = ref(props.placement ?? defaultPlacement);
435
279
  const isOpen = computed(() => sub ? sub.isOpen.value : root.isOpen.value);
436
280
  const reference = computed(() => sub ? sub.trigger.value : root.reference.value);
437
- const teleportPositioningKey = useTeleportPositioningKey();
438
281
  const menu = createMenuContext({
439
282
  root,
440
283
  element,
441
284
  actualPlacement,
442
285
  parentSub: sub,
443
286
  onEscape(event) {
444
- const customEvent = createCustomEvent("dropdown-menu-escape-key-down", { originalEvent: event }, event);
287
+ const customEvent = createCancelableCustomEvent("dropdown-menu-escape-key-down", { originalEvent: event }, event);
445
288
  events.escapeKeyDown(customEvent);
446
- if (customEvent.defaultPrevented) return;
447
- if (sub) sub.close(true);
448
- else root.close({ returnFocus: true });
289
+ return !customEvent.defaultPrevented;
449
290
  }
450
291
  });
292
+ const cleanupDismissal = submenu ? void 0 : root.interaction.connectDismissal({
293
+ ignoredTargets: () => props.ignore ?? [],
294
+ pointerDownOutside: events.pointerDownOutside,
295
+ focusOutside: events.focusOutside,
296
+ interactOutside: events.interactOutside
297
+ });
451
298
  const floating = useFloatingPosition({
452
299
  reference,
453
300
  floating: element,
@@ -460,72 +307,31 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
460
307
  flipOptions: () => props.flipOptions,
461
308
  shift: () => props.shift !== false,
462
309
  collisionPadding: () => props.collisionPadding ?? 8,
463
- autoUpdateOptions: () => props.autoUpdateOptions,
464
- restartKey: teleportPositioningKey
310
+ autoUpdateOptions: () => props.autoUpdateOptions
465
311
  });
466
312
  watch(floating.actualPlacement, (value) => {
467
313
  actualPlacement.value = value;
468
314
  }, { immediate: true });
469
315
  const shouldRender = computed(() => props.forceMount === true || isOpen.value);
470
316
  let layerBranchCleanup;
317
+ let disconnectInside;
471
318
  provide(menuKey, menu);
472
319
  if (sub) sub.menu = menu;
473
320
  function setElement(value) {
474
321
  resolveHTMLElementRef(value, id.value, (resolved) => {
475
- const previous = element.value;
476
- if (previous) root.unregisterInside(previous);
322
+ disconnectInside?.();
323
+ disconnectInside = void 0;
477
324
  layerBranchCleanup?.();
478
325
  layerBranchCleanup = void 0;
479
326
  element.value = resolved;
480
- if (resolved) root.registerInside(resolved);
327
+ if (resolved) disconnectInside = root.connectInside(resolved);
481
328
  if (!sub) root.layer.element.value = resolved;
482
329
  else if (resolved) layerBranchCleanup = root.layer.registerBranch(resolved);
483
330
  });
484
331
  }
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
332
  watch(isOpen, (open) => {
519
- setDocumentListeners(open);
520
- if (!open) {
521
- menu.activeId.value = null;
522
- menu.closeSubmenus();
523
- return;
524
- }
525
- nextTick(() => {
526
- const focus = sub ? sub.pendingFocus.value : root.pendingFocus.value;
527
- menu.focus(focus || "first");
528
- });
333
+ if (!open) return;
334
+ nextTick(menu.interaction.focusPending);
529
335
  }, {
530
336
  flush: "post",
531
337
  immediate: true
@@ -533,12 +339,12 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
533
339
  onMounted(() => {
534
340
  if (sub) sub.contentId.value = id.value;
535
341
  else root.contentId.value = id.value;
536
- if (isOpen.value) menu.focus((sub ? sub.pendingFocus.value : root.pendingFocus.value) || "first");
342
+ if (isOpen.value) menu.interaction.focusPending();
537
343
  });
538
344
  onBeforeUnmount(() => {
539
- setDocumentListeners(false);
345
+ cleanupDismissal?.();
346
+ disconnectInside?.();
540
347
  layerBranchCleanup?.();
541
- if (element.value) root.unregisterInside(element.value);
542
348
  if (!sub && root.layer.element.value === element.value) root.layer.element.value = null;
543
349
  if (sub) sub.menu = null;
544
350
  else if (root.contentId.value === id.value) root.contentId.value = void 0;
@@ -549,7 +355,7 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
549
355
  id: id.value,
550
356
  role: "menu",
551
357
  tabindex: -1,
552
- hidden: optionalAttr(!isOpen.value),
358
+ hidden: toOptionalAttribute(!isOpen.value),
553
359
  class: submenu ? bem("rp-dropdown-menu__submenu", {
554
360
  compound: true,
555
361
  open: isOpen.value
@@ -565,12 +371,12 @@ function useDropdownMenuPrimitiveContent(props, attrs, events, submenu) {
565
371
  "aria-label": props.ariaLabel,
566
372
  "aria-labelledby": props.ariaLabelledby ?? (submenu ? void 0 : root.triggerId.value),
567
373
  "aria-describedby": props.ariaDescribedby,
568
- "aria-activedescendant": menu.activeId.value ?? void 0,
374
+ "aria-activedescendant": menu.interaction.activeId.value ?? void 0,
569
375
  "data-state": isOpen.value ? "open" : "closed",
570
376
  "data-placement": floating.actualPlacement.value,
571
377
  "data-side": placementSide.value,
572
378
  "data-align": placementAlign.value,
573
- onKeydown: menu.onKeydown
379
+ onKeydown: menu.interaction.onKeydown
574
380
  }));
575
381
  function close() {
576
382
  if (sub) sub.close(true);
@@ -644,15 +450,16 @@ var dropdown_menu_content_default = /* @__PURE__ */ defineVaporComponent({
644
450
  focusOutside: (event) => emit("focusOutside", event),
645
451
  interactOutside: (event) => emit("interactOutside", event)
646
452
  }, false);
453
+ const templateRefs = { arrow: arrowElement };
647
454
  const _setTemplateRef = createTemplateRefSetter();
648
455
  return createIf(() => unref(shouldRender), () => {
649
456
  const n6 = createDynamicComponent(() => __props.as, { $: [() => unref(rootAttrs)] }, extend(() => {
650
457
  return [createIf(() => __props.arrow, () => {
651
458
  const n4 = t0$2();
652
- _setTemplateRef(n4, arrowElement, null, "arrowElement");
653
459
  renderEffect(() => {
654
460
  setAttr(n4, "data-side", unref(placementSide));
655
461
  setStyle(n4, unref(floating).arrowStyle.value);
462
+ _setTemplateRef(n4, templateRefs.arrow);
656
463
  });
657
464
  return n4;
658
465
  }, null, 129), createSlot("default", {
@@ -667,6 +474,118 @@ var dropdown_menu_content_default = /* @__PURE__ */ defineVaporComponent({
667
474
  }
668
475
  });
669
476
  //#endregion
477
+ //#region src/components/dropdown-menu/useDropdownMenuContextTrigger.ts
478
+ function useDropdownMenuContextTrigger(props) {
479
+ const attrs = useAttrs();
480
+ const root = useRequiredInject(rootKey, "RpDropdownMenuContextTrigger");
481
+ const generatedId = useId();
482
+ const id = computed(() => props.id ?? `${generatedId}-context-trigger`);
483
+ const element = ref(null);
484
+ const isDisabled = computed(() => root.disabled.value || props.disabled);
485
+ let timer;
486
+ let pointerId;
487
+ let startPoint;
488
+ let longPressOpened = false;
489
+ let suppressUntil = 0;
490
+ let disconnectInside;
491
+ function setElement(value) {
492
+ resolveHTMLElementRef(value, id.value, (resolved) => {
493
+ disconnectInside?.();
494
+ disconnectInside = void 0;
495
+ element.value = resolved;
496
+ if (resolved) disconnectInside = root.connectInside(resolved);
497
+ if (!root.triggerId.value) root.triggerId.value = id.value;
498
+ });
499
+ }
500
+ function clearLongPress() {
501
+ if (timer) clearTimeout(timer);
502
+ timer = void 0;
503
+ pointerId = void 0;
504
+ startPoint = void 0;
505
+ element.value?.ownerDocument.defaultView?.removeEventListener("scroll", clearLongPress, true);
506
+ }
507
+ function openAt(point) {
508
+ root.setReturnFocus(element.value);
509
+ root.openAt(point);
510
+ }
511
+ function onContextmenu(event) {
512
+ if (isDisabled.value) return;
513
+ event.preventDefault();
514
+ if (Date.now() < suppressUntil) return;
515
+ openAt({
516
+ x: event.clientX,
517
+ y: event.clientY
518
+ });
519
+ }
520
+ function onKeydown(event) {
521
+ if (isDisabled.value) return;
522
+ if (event.key !== "ContextMenu" && !(event.shiftKey && event.key === "F10")) return;
523
+ event.preventDefault();
524
+ const rect = element.value?.getBoundingClientRect();
525
+ openAt({
526
+ x: rect?.left ?? 0,
527
+ y: rect?.bottom ?? 0
528
+ });
529
+ }
530
+ function onPointerdown(event) {
531
+ if (isDisabled.value || event.pointerType !== "touch" && event.pointerType !== "pen") return;
532
+ clearLongPress();
533
+ pointerId = event.pointerId;
534
+ startPoint = {
535
+ x: event.clientX,
536
+ y: event.clientY
537
+ };
538
+ longPressOpened = false;
539
+ (element.value?.ownerDocument.defaultView)?.addEventListener("scroll", clearLongPress, true);
540
+ timer = setTimeout(() => {
541
+ if (!startPoint) return;
542
+ longPressOpened = true;
543
+ suppressUntil = Date.now() + 1e3;
544
+ openAt(startPoint);
545
+ timer = void 0;
546
+ }, props.longPressDelay);
547
+ }
548
+ function onPointermove(event) {
549
+ if (event.pointerId !== pointerId || !startPoint) return;
550
+ if (Math.hypot(event.clientX - startPoint.x, event.clientY - startPoint.y) > (props.longPressTolerance ?? 0)) clearLongPress();
551
+ }
552
+ function finishPointer(event) {
553
+ if (event.pointerId !== pointerId) return;
554
+ if (longPressOpened) event.preventDefault();
555
+ clearLongPress();
556
+ }
557
+ function onClick(event) {
558
+ if (Date.now() >= suppressUntil) return;
559
+ event.preventDefault();
560
+ event.stopPropagation();
561
+ }
562
+ const rootAttrs = computed(() => mergeProps(attrs, {
563
+ id: id.value,
564
+ "aria-controls": root.contentId.value,
565
+ "aria-haspopup": "menu",
566
+ "aria-disabled": toOptionalAttribute(isDisabled.value),
567
+ "data-state": root.isOpen.value ? "open" : "closed",
568
+ "data-disabled": toOptionalAttribute(isDisabled.value),
569
+ onClick,
570
+ onContextmenu,
571
+ onKeydown,
572
+ onPointerdown,
573
+ onPointermove,
574
+ onPointerup: finishPointer,
575
+ onPointercancel: finishPointer
576
+ }));
577
+ onBeforeUnmount(() => {
578
+ clearLongPress();
579
+ disconnectInside?.();
580
+ if (root.triggerId.value === id.value) root.triggerId.value = void 0;
581
+ });
582
+ return {
583
+ isOpen: root.isOpen,
584
+ rootAttrs,
585
+ setElement
586
+ };
587
+ }
588
+ //#endregion
670
589
  //#region src/components/dropdown-menu/dropdown-menu-context-trigger.vue
671
590
  var dropdown_menu_context_trigger_default = /* @__PURE__ */ defineVaporComponent({
672
591
  name: "RpDropdownMenuContextTrigger",
@@ -683,113 +602,11 @@ var dropdown_menu_context_trigger_default = /* @__PURE__ */ defineVaporComponent
683
602
  longPressTolerance: { default: 10 }
684
603
  },
685
604
  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 });
605
+ const { isOpen, rootAttrs, setElement } = useDropdownMenuContextTrigger(__props);
606
+ const n1 = createDynamicComponent(() => __props.as, { $: [() => unref(rootAttrs)] }, extend(() => {
607
+ return createSlot("default", { isOpen: () => unref(isOpen) });
791
608
  }, { _: 8 }), 1);
792
- setTemplateRefBinding(n1, () => setElement);
609
+ setTemplateRefBinding(n1, () => setElement, void 0, void 0, "setElement");
793
610
  return n1;
794
611
  }
795
612
  });
@@ -831,11 +648,11 @@ var dropdown_menu_item_default = /* @__PURE__ */ defineVaporComponent({
831
648
  type: props.as === "button" ? "button" : void 0,
832
649
  role: "menuitem",
833
650
  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),
651
+ disabled: props.as === "button" ? toOptionalAttribute(isDisabled.value) : void 0,
652
+ "aria-disabled": toOptionalAttribute(isDisabled.value),
653
+ "data-disabled": toOptionalAttribute(isDisabled.value),
654
+ "data-focused": toOptionalAttribute(focused.value),
655
+ "data-highlighted": toOptionalAttribute(focused.value),
839
656
  class: bem("rp-dropdown-menu__item", {
840
657
  focused: focused.value,
841
658
  disabled: isDisabled.value,
@@ -931,7 +748,7 @@ var dropdown_menu_portal_default = /* @__PURE__ */ defineVaporComponent({
931
748
  const props = __props;
932
749
  const teleportTo = provideTeleportTarget({ get teleportTo() {
933
750
  return props.to ?? props.teleportTo;
934
- } }, () => Boolean(props.teleport && !props.disabled));
751
+ } });
935
752
  return createComponent(VaporTeleport, {
936
753
  to: () => unref(teleportTo),
937
754
  disabled: () => __props.disabled || !__props.teleport
@@ -1038,12 +855,12 @@ var dropdown_menu_radio_item_default = /* @__PURE__ */ defineVaporComponent({
1038
855
  type: props.as === "button" ? "button" : void 0,
1039
856
  role: "menuitemradio",
1040
857
  tabindex: -1,
1041
- disabled: props.as === "button" ? optionalAttr(isDisabled.value) : void 0,
1042
- "aria-disabled": optionalAttr(isDisabled.value),
858
+ disabled: props.as === "button" ? toOptionalAttribute(isDisabled.value) : void 0,
859
+ "aria-disabled": toOptionalAttribute(isDisabled.value),
1043
860
  "aria-checked": checked.value,
1044
- "data-disabled": optionalAttr(isDisabled.value),
1045
- "data-focused": optionalAttr(focused.value),
1046
- "data-highlighted": optionalAttr(focused.value),
861
+ "data-disabled": toOptionalAttribute(isDisabled.value),
862
+ "data-focused": toOptionalAttribute(focused.value),
863
+ "data-highlighted": toOptionalAttribute(focused.value),
1047
864
  "data-state": state.value,
1048
865
  class: bem("rp-dropdown-menu__item", {
1049
866
  focused: focused.value,
@@ -1065,37 +882,785 @@ var dropdown_menu_radio_item_default = /* @__PURE__ */ defineVaporComponent({
1065
882
  }
1066
883
  });
1067
884
  //#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: {},
1083
- disabled: {
1084
- type: Boolean,
1085
- default: false
1086
- },
1087
- modal: {
1088
- type: Boolean,
1089
- default: true
1090
- },
1091
- target: { default: null },
1092
- virtualAnchor: { default: null }
1093
- },
1094
- emits: ["update:open"],
1095
- setup(__props, { expose: __expose, emit: __emit }) {
1096
- const props = __props;
1097
- const emit = __emit;
1098
- const generatedId = useId();
885
+ //#region src/components/dropdown-menu/dropdownMenuInteractionNavigation.ts
886
+ function createDropdownMenuInteractionNavigation({ host, registry, closeRoot }) {
887
+ const state = registry.state;
888
+ function closeMenu(menuId, focusParent = false) {
889
+ const menu = registry.getMenu(menuId);
890
+ const menuState = state.getMenuState(menuId);
891
+ if (!menu && !menuState || menuId === host.rootMenuId) return false;
892
+ const wasOpen = registry.isMenuOpen(menuId);
893
+ closeSubmenus(menuId);
894
+ const shouldRestoreParent = focusParent || registry.activeMenuId.value === menuId;
895
+ if (wasOpen) state.setMenuOpen(menuId, false);
896
+ state.clearPendingMenuFocus(menuId);
897
+ state.clearMenuState(menuId);
898
+ if (shouldRestoreParent) {
899
+ const parentItem = state.getParentItem(menuId);
900
+ if (!parentItem || !registry.setActive(parentItem.id, focusParent)) {
901
+ const parentMenuId = parentItem?.menuId ?? host.rootMenuId;
902
+ registry.activeMenuId.value = parentMenuId;
903
+ registry.activeItemId.value = state.getActiveIdValue(parentMenuId);
904
+ if (focusParent) registry.focusMenuElement(parentMenuId);
905
+ }
906
+ }
907
+ return wasOpen;
908
+ }
909
+ function closeSubmenus(menuId, exceptMenuId) {
910
+ for (const item of state.getItems(menuId)) {
911
+ const submenuId = item.submenuId?.();
912
+ if (!submenuId || submenuId === exceptMenuId) continue;
913
+ closeMenu(submenuId, false);
914
+ }
915
+ }
916
+ function closeAllSubmenus() {
917
+ closeSubmenus(host.rootMenuId);
918
+ for (const menuId of state.getMenuIds()) if (menuId !== host.rootMenuId && registry.isMenuOpen(menuId)) closeMenu(menuId, false);
919
+ }
920
+ function focusMenu(menuId, target = "first") {
921
+ const enabledItems = state.getEnabledItems(menuId);
922
+ const item = target === "last" ? enabledItems.at(-1) : enabledItems[0];
923
+ registry.focusMenuElement(menuId);
924
+ if (!item) {
925
+ state.clearMenuState(menuId);
926
+ registry.activeMenuId.value = menuId;
927
+ const parentItem = state.getParentItem(menuId);
928
+ if (parentItem) registry.activeItemId.value = parentItem.id;
929
+ return false;
930
+ }
931
+ return registry.setActive(item.id);
932
+ }
933
+ function openMenu(menuId, focus = "first") {
934
+ const menu = registry.getMenu(menuId);
935
+ const menuState = state.getMenuState(menuId);
936
+ if (!menu && !menuState || menuId === host.rootMenuId) return false;
937
+ const parentItem = state.getParentItem(menuId);
938
+ if (!parentItem || parentItem.disabled()) return false;
939
+ closeSubmenus(parentItem.menuId, menuId);
940
+ state.setPendingMenuFocus(menuId, focus);
941
+ state.setMenuOpen(menuId, true);
942
+ if (focus && menu) focusMenu(menuId, focus);
943
+ return true;
944
+ }
945
+ function selectItem(id, originalEvent = new Event("select")) {
946
+ const item = registry.getItem(id);
947
+ if (!item || item.disabled()) return;
948
+ const selectEvent = item.select?.(originalEvent);
949
+ if (selectEvent && !selectEvent.defaultPrevented && (item.closeOnSelect?.() ?? true)) closeRoot({ focusTrigger: true });
950
+ }
951
+ function activateItem(id, originalEvent = new Event("select")) {
952
+ const item = registry.getItem(id);
953
+ if (!item || item.disabled()) return;
954
+ const submenuId = item.submenuId?.();
955
+ if (submenuId) {
956
+ openMenu(submenuId, "first");
957
+ return;
958
+ }
959
+ selectItem(id, originalEvent);
960
+ }
961
+ function hoverItem(id, openSubmenu = false) {
962
+ const item = registry.getItem(id);
963
+ if (!item || item.disabled()) return;
964
+ registry.setActive(id);
965
+ const submenuId = item.submenuId?.();
966
+ if (openSubmenu && submenuId) openMenu(submenuId, false);
967
+ else closeSubmenus(item.menuId, submenuId && registry.isMenuOpen(submenuId) ? submenuId : void 0);
968
+ }
969
+ function reconcileOpenSubmenus() {
970
+ for (const menuId of state.getMenuIds()) {
971
+ if (menuId === host.rootMenuId || !registry.isMenuOpen(menuId)) continue;
972
+ const parentItem = state.getParentItem(menuId);
973
+ if (!parentItem || parentItem.disabled()) closeMenu(menuId, false);
974
+ }
975
+ }
976
+ function reconcile(menuId) {
977
+ const menuIds = menuId ? [menuId] : state.getRegisteredMenuIds();
978
+ for (const id of menuIds) {
979
+ const activeId = state.getActiveIdValue(id);
980
+ if (!activeId) continue;
981
+ const currentItems = state.getItems(id);
982
+ const activeIndex = currentItems.findIndex((item) => item.id === activeId);
983
+ const activeItem = currentItems[activeIndex];
984
+ if (activeItem && !activeItem.disabled()) continue;
985
+ state.chooseNeighbor(id, Math.max(0, activeIndex));
986
+ }
987
+ reconcileOpenSubmenus();
988
+ }
989
+ return {
990
+ closeMenu,
991
+ closeSubmenus,
992
+ closeAllSubmenus,
993
+ focusMenu,
994
+ openMenu,
995
+ selectItem,
996
+ activateItem,
997
+ hoverItem,
998
+ reconcile,
999
+ reconcileOpenSubmenus
1000
+ };
1001
+ }
1002
+ //#endregion
1003
+ //#region src/components/dropdown-menu/dropdownMenuInteractionRegistry.ts
1004
+ function compareItems(left, right) {
1005
+ const leftElement = left.element();
1006
+ const rightElement = right.element();
1007
+ if (leftElement && rightElement && leftElement !== rightElement && typeof Node !== "undefined") {
1008
+ const position = leftElement.compareDocumentPosition(rightElement);
1009
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
1010
+ if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
1011
+ }
1012
+ return (left.order?.() ?? 0) - (right.order?.() ?? 0);
1013
+ }
1014
+ function scrollItemIntoView(item) {
1015
+ nextTick(() => item.element()?.scrollIntoView?.({ block: "nearest" }));
1016
+ }
1017
+ function createDropdownMenuInteractionRegistry(host, navigation) {
1018
+ const menus = /* @__PURE__ */ new Map();
1019
+ const menuStates = /* @__PURE__ */ new Map();
1020
+ const items = /* @__PURE__ */ new Map();
1021
+ const activeIds = ref(/* @__PURE__ */ new Map());
1022
+ const activeItemId = ref(null);
1023
+ const activeMenuId = ref(host.rootMenuId);
1024
+ const pendingRootFocus = ref("first");
1025
+ const pendingMenuFocus = /* @__PURE__ */ new Map();
1026
+ function getMenu(id) {
1027
+ return menus.get(id);
1028
+ }
1029
+ function getMenuState(id) {
1030
+ return menuStates.get(id);
1031
+ }
1032
+ function getItem(id) {
1033
+ return items.get(id);
1034
+ }
1035
+ function getItems(menuId) {
1036
+ return [...items.values()].filter((item) => item.menuId === menuId).sort(compareItems);
1037
+ }
1038
+ function getEnabledItems(menuId) {
1039
+ return getItems(menuId).filter((item) => !item.disabled());
1040
+ }
1041
+ function getActiveIdValue(menuId) {
1042
+ return activeIds.value.get(menuId) ?? null;
1043
+ }
1044
+ function setActiveId(menuId, id) {
1045
+ const next = new Map(activeIds.value);
1046
+ if (id) next.set(menuId, id);
1047
+ else next.delete(menuId);
1048
+ activeIds.value = next;
1049
+ }
1050
+ function getActiveId(menuId) {
1051
+ return computed(() => getActiveIdValue(menuId));
1052
+ }
1053
+ function isMenuOpen(menuId) {
1054
+ if (menuId === host.rootMenuId) return host.isOpen.value;
1055
+ return menuStates.get(menuId)?.isOpen() ?? menus.get(menuId)?.isOpen() ?? false;
1056
+ }
1057
+ function focusMenuElement(menuId) {
1058
+ nextTick(() => {
1059
+ const menu = menus.get(menuId);
1060
+ (menu?.focusTarget?.() ?? menu?.element())?.focus();
1061
+ });
1062
+ }
1063
+ function clearMenuState(menuId) {
1064
+ const activeId = getActiveIdValue(menuId);
1065
+ if (activeId && activeItemId.value === activeId) activeItemId.value = null;
1066
+ setActiveId(menuId, null);
1067
+ }
1068
+ function setActive(id, focusElement = false) {
1069
+ if (id === null) {
1070
+ clearMenuState(activeMenuId.value);
1071
+ activeItemId.value = null;
1072
+ return true;
1073
+ }
1074
+ const item = items.get(id);
1075
+ if (!item || item.disabled()) return false;
1076
+ setActiveId(item.menuId, item.id);
1077
+ activeItemId.value = item.id;
1078
+ activeMenuId.value = item.menuId;
1079
+ pendingMenuFocus.delete(item.menuId);
1080
+ if (focusElement) focusMenuElement(item.menuId);
1081
+ scrollItemIntoView(item);
1082
+ return true;
1083
+ }
1084
+ function isActive(id) {
1085
+ return getActiveIdValue(items.get(id)?.menuId ?? "") === id;
1086
+ }
1087
+ function getParentItem(menuId) {
1088
+ const parentItemId = menus.get(menuId)?.parentItemId?.();
1089
+ if (parentItemId) {
1090
+ const parentItem = items.get(parentItemId);
1091
+ if (parentItem) return parentItem;
1092
+ }
1093
+ return [...items.values()].find((item) => item.submenuId?.() === menuId);
1094
+ }
1095
+ function setMenuOpen(menuId, open) {
1096
+ const state = menuStates.get(menuId);
1097
+ if (state) state.setOpen(open);
1098
+ else menus.get(menuId)?.setOpen?.(open);
1099
+ }
1100
+ function chooseNeighbor(menuId, previousIndex) {
1101
+ const currentItems = getItems(menuId);
1102
+ const nextCandidates = currentItems.slice(Math.max(0, previousIndex));
1103
+ const previousCandidates = [];
1104
+ for (let index = Math.max(0, previousIndex) - 1; index >= 0; index -= 1) {
1105
+ const item = currentItems[index];
1106
+ if (item) previousCandidates.push(item);
1107
+ }
1108
+ const next = [...nextCandidates, ...previousCandidates].find((item) => !item.disabled());
1109
+ if (next) setActive(next.id);
1110
+ else {
1111
+ clearMenuState(menuId);
1112
+ focusMenuElement(menuId);
1113
+ }
1114
+ }
1115
+ function unregisterItem(id) {
1116
+ const item = items.get(id);
1117
+ if (!item) return;
1118
+ const previousIndex = getItems(item.menuId).findIndex((current) => current.id === id);
1119
+ const submenuId = item.submenuId?.();
1120
+ if (submenuId) navigation.closeMenu(submenuId, false);
1121
+ items.delete(id);
1122
+ if (getActiveIdValue(item.menuId) === id) chooseNeighbor(item.menuId, previousIndex);
1123
+ navigation.reconcileOpenSubmenus();
1124
+ }
1125
+ function registerItem(registration) {
1126
+ const previous = items.get(registration.id);
1127
+ if (previous && previous.menuId !== registration.menuId && getActiveIdValue(previous.menuId) === previous.id) clearMenuState(previous.menuId);
1128
+ items.set(registration.id, registration);
1129
+ if (isMenuOpen(registration.menuId) && !getActiveIdValue(registration.menuId) && !registration.disabled()) {
1130
+ const enabledItems = getEnabledItems(registration.menuId);
1131
+ const candidate = pendingMenuFocus.get(registration.menuId) === "last" ? enabledItems.at(-1) : enabledItems[0];
1132
+ if (candidate) setActive(candidate.id);
1133
+ } else navigation.reconcile(registration.menuId);
1134
+ return () => {
1135
+ if (items.get(registration.id) === registration) unregisterItem(registration.id);
1136
+ };
1137
+ }
1138
+ function unregisterMenu(id) {
1139
+ if (!menus.get(id)) return;
1140
+ navigation.closeSubmenus(id);
1141
+ if (id !== host.rootMenuId) navigation.closeMenu(id, false);
1142
+ clearMenuState(id);
1143
+ menus.delete(id);
1144
+ }
1145
+ function registerMenu(registration) {
1146
+ menus.set(registration.id, registration);
1147
+ const pending = pendingMenuFocus.get(registration.id);
1148
+ if (registration.isOpen() && pending) nextTick(() => navigation.focusMenu(registration.id, pending));
1149
+ return () => {
1150
+ if (menus.get(registration.id) === registration) unregisterMenu(registration.id);
1151
+ };
1152
+ }
1153
+ function registerMenuState(registration) {
1154
+ menuStates.set(registration.id, registration);
1155
+ return () => {
1156
+ if (menuStates.get(registration.id) !== registration) return;
1157
+ menuStates.delete(registration.id);
1158
+ pendingMenuFocus.delete(registration.id);
1159
+ if (!menus.has(registration.id)) clearMenuState(registration.id);
1160
+ };
1161
+ }
1162
+ return {
1163
+ activeItemId,
1164
+ activeMenuId,
1165
+ pendingRootFocus,
1166
+ registerMenu,
1167
+ unregisterMenu,
1168
+ registerMenuState,
1169
+ registerItem,
1170
+ unregisterItem,
1171
+ getActiveId,
1172
+ getItem,
1173
+ getMenu,
1174
+ isActive,
1175
+ isMenuOpen,
1176
+ setActive,
1177
+ focusMenuElement,
1178
+ state: {
1179
+ getMenuState,
1180
+ getItems,
1181
+ getEnabledItems,
1182
+ getActiveIdValue,
1183
+ clearMenuState,
1184
+ getParentItem,
1185
+ setMenuOpen,
1186
+ chooseNeighbor,
1187
+ getMenuIds: () => /* @__PURE__ */ new Set([...menus.keys(), ...menuStates.keys()]),
1188
+ getRegisteredMenuIds: () => [...menus.keys()],
1189
+ getPendingMenuFocus: (menuId) => pendingMenuFocus.get(menuId),
1190
+ setPendingMenuFocus: (menuId, focus) => pendingMenuFocus.set(menuId, focus),
1191
+ clearPendingMenuFocus: (menuId) => pendingMenuFocus.delete(menuId)
1192
+ }
1193
+ };
1194
+ }
1195
+ //#endregion
1196
+ //#region src/components/dropdown-menu/useDropdownMenuInteractionDismissal.ts
1197
+ function createOutsideEvent(originalEvent) {
1198
+ return createCancelableCustomEvent("dropdown-menu-interact-outside", { originalEvent }, originalEvent);
1199
+ }
1200
+ function blockNextDocumentClick(document) {
1201
+ function blockDocumentClick(event) {
1202
+ if (event.cancelable) event.preventDefault();
1203
+ event.stopPropagation();
1204
+ document.removeEventListener("click", blockDocumentClick, true);
1205
+ }
1206
+ document.addEventListener("click", blockDocumentClick, true);
1207
+ document.defaultView?.setTimeout(() => document.removeEventListener("click", blockDocumentClick, true), 1e3);
1208
+ }
1209
+ function useDropdownMenuInteractionDismissal({ host, registry, closeRoot }) {
1210
+ const inside = /* @__PURE__ */ new Set();
1211
+ let dismissal;
1212
+ let layerInteraction;
1213
+ function registerInside(element) {
1214
+ inside.add(element);
1215
+ layerInteraction?.refresh();
1216
+ }
1217
+ function unregisterInside(element) {
1218
+ inside.delete(element);
1219
+ layerInteraction?.refresh();
1220
+ }
1221
+ function emitOutside(type, originalEvent) {
1222
+ const outsideEvent = createOutsideEvent(originalEvent);
1223
+ if (type === "pointer") dismissal?.pointerDownOutside?.(outsideEvent);
1224
+ else dismissal?.focusOutside?.(outsideEvent);
1225
+ dismissal?.interactOutside?.(outsideEvent);
1226
+ return outsideEvent;
1227
+ }
1228
+ function blockModalInteraction(event) {
1229
+ if (!host.modal.value) return;
1230
+ if (event.cancelable) event.preventDefault();
1231
+ event.stopPropagation();
1232
+ const listenerDocument = event.currentTarget;
1233
+ if (event.type === "pointerdown" && listenerDocument?.nodeType === 9) blockNextDocumentClick(listenerDocument);
1234
+ }
1235
+ function shouldIgnoreOutside(event) {
1236
+ return isEventWithinTargets(event, (dismissal?.ignoredTargets() ?? []).map((target) => isRef(target) ? target.value : target));
1237
+ }
1238
+ function onDocumentPointer(event) {
1239
+ if (shouldIgnoreOutside(event)) return;
1240
+ const outsideEvent = emitOutside("pointer", event);
1241
+ blockModalInteraction(event);
1242
+ if (!outsideEvent.defaultPrevented) closeRoot({ focusTrigger: host.modal.value });
1243
+ }
1244
+ function onDocumentFocus(event) {
1245
+ if (shouldIgnoreOutside(event)) return;
1246
+ const outsideEvent = emitOutside("focus", event);
1247
+ blockModalInteraction(event);
1248
+ if (!outsideEvent.defaultPrevented) closeRoot({ focusTrigger: host.modal.value });
1249
+ else if (host.modal.value) registry.focusMenuElement(registry.activeMenuId.value);
1250
+ }
1251
+ function registerDismissal(registration) {
1252
+ layerInteraction?.();
1253
+ dismissal = registration;
1254
+ layerInteraction = host.connectLayerInteraction({
1255
+ inside: () => [...inside],
1256
+ pointerDownOutside: onDocumentPointer,
1257
+ focusOutside: onDocumentFocus
1258
+ });
1259
+ return () => {
1260
+ if (dismissal !== registration) return;
1261
+ dismissal = void 0;
1262
+ layerInteraction?.();
1263
+ layerInteraction = void 0;
1264
+ };
1265
+ }
1266
+ return {
1267
+ registerInside,
1268
+ unregisterInside,
1269
+ registerDismissal
1270
+ };
1271
+ }
1272
+ //#endregion
1273
+ //#region src/components/dropdown-menu/useDropdownMenuInteractionKeyboard.ts
1274
+ function getPlacementSide(placement) {
1275
+ return placement.split("-")[0];
1276
+ }
1277
+ function useDropdownMenuInteractionKeyboard({ host, registry, navigation, openRoot, closeRoot }) {
1278
+ const state = registry.state;
1279
+ const typeaheadMenuId = ref(host.rootMenuId);
1280
+ const typeahead = useTypeahead({
1281
+ items: () => state.getItems(typeaheadMenuId.value),
1282
+ activeIndex: () => state.getItems(typeaheadMenuId.value).findIndex((item) => item.id === state.getActiveIdValue(typeaheadMenuId.value)),
1283
+ getKey: (item) => item.id,
1284
+ getTextValue: (item) => item.textValue(),
1285
+ isDisabled: (item) => item.disabled(),
1286
+ scopeKey: () => typeaheadMenuId.value,
1287
+ onMatch(item) {
1288
+ registry.setActive(item.id);
1289
+ navigation.closeSubmenus(item.menuId);
1290
+ }
1291
+ });
1292
+ function move(menuId, direction) {
1293
+ const enabledItems = state.getEnabledItems(menuId);
1294
+ if (enabledItems.length === 0) {
1295
+ state.clearMenuState(menuId);
1296
+ return;
1297
+ }
1298
+ const currentIndex = enabledItems.findIndex((item) => item.id === state.getActiveIdValue(menuId));
1299
+ const item = enabledItems[((currentIndex < 0 ? direction === 1 ? -1 : 0 : currentIndex) + direction + enabledItems.length) % enabledItems.length];
1300
+ if (item) {
1301
+ registry.setActive(item.id);
1302
+ navigation.closeSubmenus(menuId);
1303
+ }
1304
+ }
1305
+ function handleHorizontalKey(menuId, event, activeItem) {
1306
+ const menu = registry.getMenu(menuId);
1307
+ const isRight = event.key === "ArrowRight";
1308
+ if (menuId !== host.rootMenuId && menu) {
1309
+ if (isRight === (getPlacementSide(menu.placement()) === "left")) {
1310
+ event.preventDefault();
1311
+ if (menu.stopKeyPropagation) event.stopPropagation();
1312
+ navigation.closeMenu(menuId, true);
1313
+ return;
1314
+ }
1315
+ }
1316
+ const submenuId = activeItem?.submenuId?.();
1317
+ if (!activeItem || activeItem.disabled() || !submenuId) return;
1318
+ const submenu = registry.getMenu(submenuId);
1319
+ if (isRight !== (activeItem.submenuDirection?.() !== "left" && (!submenu || getPlacementSide(submenu.placement()) !== "left"))) return;
1320
+ event.preventDefault();
1321
+ if (menu?.stopKeyPropagation) event.stopPropagation();
1322
+ navigation.openMenu(submenuId, "first");
1323
+ }
1324
+ function onMenuKeydown(menuId, event) {
1325
+ const menu = registry.getMenu(menuId);
1326
+ typeaheadMenuId.value = menuId;
1327
+ if (typeahead.handleKey(event)) {
1328
+ if (menu?.stopKeyPropagation) event.stopPropagation();
1329
+ return;
1330
+ }
1331
+ const activeId = state.getActiveIdValue(menuId);
1332
+ const activeItem = activeId ? registry.getItem(activeId) : void 0;
1333
+ const stop = () => {
1334
+ if (menu?.stopKeyPropagation) event.stopPropagation();
1335
+ };
1336
+ switch (event.key) {
1337
+ case "ArrowDown":
1338
+ event.preventDefault();
1339
+ stop();
1340
+ move(menuId, 1);
1341
+ break;
1342
+ case "ArrowUp":
1343
+ event.preventDefault();
1344
+ stop();
1345
+ move(menuId, -1);
1346
+ break;
1347
+ case "Home":
1348
+ event.preventDefault();
1349
+ stop();
1350
+ navigation.focusMenu(menuId, "first");
1351
+ navigation.closeSubmenus(menuId);
1352
+ break;
1353
+ case "End":
1354
+ event.preventDefault();
1355
+ stop();
1356
+ navigation.focusMenu(menuId, "last");
1357
+ navigation.closeSubmenus(menuId);
1358
+ break;
1359
+ case "ArrowRight":
1360
+ case "ArrowLeft":
1361
+ handleHorizontalKey(menuId, event, activeItem);
1362
+ break;
1363
+ case "Enter":
1364
+ case " ":
1365
+ event.preventDefault();
1366
+ stop();
1367
+ if (activeItem) navigation.activateItem(activeItem.id, event);
1368
+ break;
1369
+ case "Escape":
1370
+ event.preventDefault();
1371
+ stop();
1372
+ if (menu?.onEscape && !menu.onEscape(event)) return;
1373
+ if (!navigation.closeMenu(menuId, true)) closeRoot({ focusTrigger: true });
1374
+ break;
1375
+ case "Tab":
1376
+ closeRoot();
1377
+ break;
1378
+ }
1379
+ }
1380
+ function onTriggerKeydown(event, beforeOpen) {
1381
+ if (host.disabled.value) return;
1382
+ switch (event.key) {
1383
+ case "Enter":
1384
+ case " ":
1385
+ case "ArrowDown":
1386
+ event.preventDefault();
1387
+ beforeOpen?.();
1388
+ openRoot("first");
1389
+ break;
1390
+ case "ArrowUp":
1391
+ event.preventDefault();
1392
+ beforeOpen?.();
1393
+ openRoot("last");
1394
+ break;
1395
+ case "Escape":
1396
+ closeRoot({ focusTrigger: true });
1397
+ break;
1398
+ }
1399
+ }
1400
+ return {
1401
+ onMenuKeydown,
1402
+ onTriggerKeydown,
1403
+ resetTypeahead: typeahead.reset
1404
+ };
1405
+ }
1406
+ //#endregion
1407
+ //#region src/components/dropdown-menu/dropdownMenuInteraction.ts
1408
+ function resolveOpenFocusTarget(options) {
1409
+ if (typeof options === "string") return options;
1410
+ return options?.focus ?? "first";
1411
+ }
1412
+ function resolveKeyboardMenuId(rootMenuId, registry, event) {
1413
+ const activeItemId = registry.activeItemId.value;
1414
+ const activeItem = activeItemId ? registry.getItem(activeItemId) : void 0;
1415
+ const openSubmenuId = activeItem?.submenuId?.();
1416
+ if (openSubmenuId && registry.isMenuOpen(openSubmenuId)) {
1417
+ if (event.key === "Escape") return openSubmenuId;
1418
+ const opensLeft = activeItem?.submenuDirection?.() === "left" || registry.getMenu(openSubmenuId)?.placement().startsWith("left");
1419
+ if (event.key === (opensLeft ? "ArrowRight" : "ArrowLeft")) return openSubmenuId;
1420
+ }
1421
+ if ([
1422
+ "ArrowLeft",
1423
+ "ArrowRight",
1424
+ "Escape"
1425
+ ].includes(event.key) && registry.activeMenuId.value !== rootMenuId) return registry.activeMenuId.value;
1426
+ return activeItem?.menuId ?? registry.activeMenuId.value;
1427
+ }
1428
+ function useDropdownMenuInteraction(host) {
1429
+ let navigation;
1430
+ const registry = createDropdownMenuInteractionRegistry(host, {
1431
+ closeMenu: (menuId, focusParent) => navigation.closeMenu(menuId, focusParent),
1432
+ closeSubmenus: (menuId, exceptMenuId) => navigation.closeSubmenus(menuId, exceptMenuId),
1433
+ focusMenu: (menuId, target) => navigation.focusMenu(menuId, target),
1434
+ reconcile: (menuId) => navigation.reconcile(menuId),
1435
+ reconcileOpenSubmenus: () => navigation.reconcileOpenSubmenus()
1436
+ });
1437
+ function open(options) {
1438
+ if (host.disabled.value) return;
1439
+ host.beforeOpen?.();
1440
+ registry.pendingRootFocus.value = resolveOpenFocusTarget(options);
1441
+ host.setOpen(true);
1442
+ navigation.focusMenu(host.rootMenuId, registry.pendingRootFocus.value);
1443
+ }
1444
+ function close(options = {}) {
1445
+ host.beforeClose?.();
1446
+ host.setOpen(false);
1447
+ registry.pendingRootFocus.value = false;
1448
+ navigation.closeAllSubmenus();
1449
+ registry.state.clearMenuState(host.rootMenuId);
1450
+ registry.activeItemId.value = null;
1451
+ registry.activeMenuId.value = host.rootMenuId;
1452
+ keyboard.resetTypeahead();
1453
+ if (options.focusTrigger || options.returnFocus) host.focusTrigger();
1454
+ }
1455
+ function toggle() {
1456
+ if (host.isOpen.value) close({ returnFocus: true });
1457
+ else open();
1458
+ }
1459
+ function onTriggerClick(beforeToggle) {
1460
+ beforeToggle?.();
1461
+ toggle();
1462
+ }
1463
+ navigation = createDropdownMenuInteractionNavigation({
1464
+ host,
1465
+ registry,
1466
+ closeRoot: close
1467
+ });
1468
+ const keyboard = useDropdownMenuInteractionKeyboard({
1469
+ host,
1470
+ registry,
1471
+ navigation,
1472
+ openRoot: open,
1473
+ closeRoot: close
1474
+ });
1475
+ const dismissal = useDropdownMenuInteractionDismissal({
1476
+ host,
1477
+ registry,
1478
+ closeRoot: close
1479
+ });
1480
+ function connectItem(menuId, options) {
1481
+ const registration = {
1482
+ id: options.id,
1483
+ menuId,
1484
+ element: options.element,
1485
+ textValue: options.textValue,
1486
+ disabled: options.disabled,
1487
+ order: options.order,
1488
+ submenuId: options.submenu ? () => options.submenu?.id : void 0,
1489
+ submenuDirection: options.submenuDirection,
1490
+ select: options.select,
1491
+ closeOnSelect: options.closeOnSelect
1492
+ };
1493
+ const disconnect = registry.registerItem(registration);
1494
+ const stopReconciliation = watch([
1495
+ options.disabled,
1496
+ options.element,
1497
+ () => options.submenu?.id
1498
+ ], () => navigation.reconcile(menuId), { flush: "sync" });
1499
+ let disposed = false;
1500
+ return {
1501
+ active: computed(() => registry.isActive(options.id)),
1502
+ submenuOpen: computed(() => options.submenu ? registry.isMenuOpen(options.submenu.id) : false),
1503
+ setActive: (focusElement = false) => registry.setActive(options.id, focusElement),
1504
+ openSubmenu: (focus = "first") => options.submenu?.open(focus) ?? false,
1505
+ closeSubmenu: (focusParent = false) => options.submenu?.close(focusParent) ?? false,
1506
+ select: (originalEvent) => navigation.selectItem(options.id, originalEvent),
1507
+ activate: (originalEvent) => navigation.activateItem(options.id, originalEvent),
1508
+ hover(openSubmenu = false) {
1509
+ navigation.hoverItem(options.id, openSubmenu);
1510
+ return Boolean(options.submenu && registry.isMenuOpen(options.submenu.id));
1511
+ },
1512
+ dispose() {
1513
+ if (disposed) return;
1514
+ disposed = true;
1515
+ stopReconciliation();
1516
+ disconnect();
1517
+ }
1518
+ };
1519
+ }
1520
+ function connectMenu(id, options, state, getRequestedFocus) {
1521
+ const disconnect = registry.registerMenu({
1522
+ id,
1523
+ ...options,
1524
+ ...state
1525
+ });
1526
+ const items = /* @__PURE__ */ new Set();
1527
+ let disposed = false;
1528
+ return {
1529
+ activeId: registry.getActiveId(id),
1530
+ connectItem(itemOptions) {
1531
+ const item = connectItem(id, itemOptions);
1532
+ const dispose = item.dispose;
1533
+ item.dispose = () => {
1534
+ dispose();
1535
+ items.delete(item);
1536
+ };
1537
+ items.add(item);
1538
+ return item;
1539
+ },
1540
+ clearActive: () => {
1541
+ registry.setActive(null);
1542
+ },
1543
+ focusPending() {
1544
+ const pending = id === host.rootMenuId ? registry.pendingRootFocus.value : getRequestedFocus?.() ?? registry.state.getPendingMenuFocus(id);
1545
+ return navigation.focusMenu(id, pending || "first");
1546
+ },
1547
+ onKeydown: (event) => keyboard.onMenuKeydown(id, event),
1548
+ dispose() {
1549
+ if (disposed) return;
1550
+ disposed = true;
1551
+ for (const item of items) item.dispose();
1552
+ disconnect();
1553
+ }
1554
+ };
1555
+ }
1556
+ function connectRootMenu(options) {
1557
+ return connectMenu(host.rootMenuId, options, { isOpen: () => host.isOpen.value });
1558
+ }
1559
+ function connectSubmenu(options) {
1560
+ const disconnectState = registry.registerMenuState(options);
1561
+ let requestedFocus;
1562
+ const stopStateSync = watch(options.isOpen, (isOpen) => {
1563
+ if (isOpen) return;
1564
+ requestedFocus = void 0;
1565
+ navigation.closeMenu(options.id, false);
1566
+ }, { flush: "sync" });
1567
+ let content;
1568
+ let disposed = false;
1569
+ return {
1570
+ id: options.id,
1571
+ open(focus = "first") {
1572
+ requestedFocus = focus;
1573
+ return navigation.openMenu(options.id, focus);
1574
+ },
1575
+ close(focusParent = false) {
1576
+ requestedFocus = void 0;
1577
+ return navigation.closeMenu(options.id, focusParent);
1578
+ },
1579
+ connectContent(menuOptions) {
1580
+ const previous = content;
1581
+ content = connectMenu(options.id, menuOptions, options, () => requestedFocus);
1582
+ previous?.dispose();
1583
+ return content;
1584
+ },
1585
+ dispose() {
1586
+ if (disposed) return;
1587
+ disposed = true;
1588
+ content?.dispose();
1589
+ stopStateSync();
1590
+ disconnectState();
1591
+ }
1592
+ };
1593
+ }
1594
+ function connectInside(element) {
1595
+ dismissal.registerInside(element);
1596
+ let connected = true;
1597
+ return () => {
1598
+ if (!connected) return;
1599
+ connected = false;
1600
+ dismissal.unregisterInside(element);
1601
+ };
1602
+ }
1603
+ watch(host.disabled, (disabled) => {
1604
+ if (disabled) close();
1605
+ });
1606
+ watch(host.isOpen, (isOpen) => {
1607
+ if (!isOpen) {
1608
+ navigation.closeAllSubmenus();
1609
+ registry.state.clearMenuState(host.rootMenuId);
1610
+ registry.activeItemId.value = null;
1611
+ registry.activeMenuId.value = host.rootMenuId;
1612
+ keyboard.resetTypeahead();
1613
+ return;
1614
+ }
1615
+ const focus = registry.pendingRootFocus.value || "first";
1616
+ navigation.focusMenu(host.rootMenuId, focus);
1617
+ }, { immediate: true });
1618
+ return {
1619
+ activeItemId: registry.activeItemId,
1620
+ connectRootMenu,
1621
+ connectSubmenu,
1622
+ connectInside,
1623
+ connectDismissal: dismissal.registerDismissal,
1624
+ open,
1625
+ close,
1626
+ toggle,
1627
+ onTriggerClick,
1628
+ onTriggerKeydown: keyboard.onTriggerKeydown,
1629
+ onMenuKeydown: (event) => keyboard.onMenuKeydown(resolveKeyboardMenuId(host.rootMenuId, registry, event), event)
1630
+ };
1631
+ }
1632
+ //#endregion
1633
+ //#region src/components/dropdown-menu/dropdown-menu-root.vue
1634
+ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1635
+ name: "RpDropdownMenuRoot",
1636
+ __name: "dropdown-menu-root",
1637
+ props: {
1638
+ id: {},
1639
+ open: {
1640
+ type: Boolean,
1641
+ default: void 0
1642
+ },
1643
+ defaultOpen: {
1644
+ type: Boolean,
1645
+ default: false
1646
+ },
1647
+ baseZIndex: {},
1648
+ disabled: {
1649
+ type: Boolean,
1650
+ default: false
1651
+ },
1652
+ modal: {
1653
+ type: Boolean,
1654
+ default: true
1655
+ },
1656
+ target: { default: null },
1657
+ virtualAnchor: { default: null }
1658
+ },
1659
+ emits: ["update:open"],
1660
+ setup(__props, { expose: __expose, emit: __emit }) {
1661
+ const props = __props;
1662
+ const emit = __emit;
1663
+ const generatedId = useId();
1099
1664
  const id = computed(() => props.id ?? `${generatedId}-dropdown-menu`);
1100
1665
  const controllableOpen = useControllableValue({
1101
1666
  modelValue: () => props.open,
@@ -1109,11 +1674,12 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1109
1674
  const triggerId = ref();
1110
1675
  const contentId = ref();
1111
1676
  const activeReference = shallowRef(null);
1112
- const { reference: configuredReference } = useFloatingTarget(() => props.target ?? props.virtualAnchor, trigger);
1677
+ const { reference: configuredReference } = useFloatingTargetLifecycle({
1678
+ target: () => props.target ?? props.virtualAnchor,
1679
+ fallback: trigger
1680
+ });
1113
1681
  const reference = computed(() => activeReference.value ?? configuredReference.value);
1114
- const pendingFocus = ref("first");
1115
1682
  const contentElement = ref(null);
1116
- const inside = /* @__PURE__ */ new Set();
1117
1683
  let returnFocusElement = null;
1118
1684
  const baseZIndex = useOverlayZIndex({
1119
1685
  baseZIndex: () => props.baseZIndex,
@@ -1134,34 +1700,33 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1134
1700
  const activeElement = document.activeElement;
1135
1701
  if (activeElement instanceof HTMLElement) returnFocusElement = activeElement;
1136
1702
  }
1137
- function open(options) {
1138
- if (disabled.value) return;
1139
- rememberFocus();
1140
- pendingFocus.value = getFocusTarget(options);
1141
- setOpen(true);
1142
- }
1143
1703
  function focusReturnTarget() {
1144
1704
  const target = returnFocusElement ?? trigger.value;
1145
1705
  nextTick(() => target?.focus());
1146
1706
  }
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
- }
1707
+ const interactionRootMenuId = `${generatedId}-interaction-root`;
1708
+ const interaction = useDropdownMenuInteraction({
1709
+ rootMenuId: interactionRootMenuId,
1710
+ isOpen,
1711
+ disabled,
1712
+ modal,
1713
+ setOpen,
1714
+ connectLayerInteraction: layer.connectInteraction,
1715
+ focusTrigger: focusReturnTarget,
1716
+ beforeOpen: rememberFocus
1717
+ });
1718
+ const { open, close, toggle } = interaction;
1156
1719
  function openAt(point, options) {
1157
1720
  activeReference.value = createVirtualAnchor(point);
1158
1721
  open(options);
1159
1722
  }
1723
+ let disconnectTrigger;
1160
1724
  function setTrigger(element, nextId) {
1161
- if (trigger.value && trigger.value !== element) inside.delete(trigger.value);
1725
+ disconnectTrigger?.();
1726
+ disconnectTrigger = void 0;
1162
1727
  trigger.value = element;
1163
1728
  triggerId.value = nextId;
1164
- if (element) inside.add(element);
1729
+ if (element) disconnectTrigger = interaction.connectInside(element);
1165
1730
  }
1166
1731
  const context = {
1167
1732
  id,
@@ -1172,8 +1737,9 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1172
1737
  triggerId,
1173
1738
  contentId,
1174
1739
  reference,
1175
- pendingFocus,
1740
+ interactionRootMenuId,
1176
1741
  layer,
1742
+ interaction,
1177
1743
  open,
1178
1744
  close,
1179
1745
  toggle,
@@ -1185,15 +1751,7 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1185
1751
  setReturnFocus(element) {
1186
1752
  returnFocusElement = element;
1187
1753
  },
1188
- registerInside(element) {
1189
- inside.add(element);
1190
- },
1191
- unregisterInside(element) {
1192
- inside.delete(element);
1193
- },
1194
- isInside(event) {
1195
- return [...inside].some((element) => isEventWithinElement(event, element));
1196
- }
1754
+ connectInside: interaction.connectInside
1197
1755
  };
1198
1756
  const slotProps = computed(() => ({
1199
1757
  isOpen: isOpen.value,
@@ -1202,13 +1760,11 @@ var dropdown_menu_root_default = /* @__PURE__ */ defineVaporComponent({
1202
1760
  toggle,
1203
1761
  openAt
1204
1762
  }));
1205
- watch(disabled, (value) => {
1206
- if (value) close();
1207
- });
1208
1763
  watch(isOpen, (value) => {
1209
1764
  if (!value) activeReference.value = null;
1210
1765
  }, { flush: "sync" });
1211
1766
  provide(rootKey, context);
1767
+ onBeforeUnmount(() => disconnectTrigger?.());
1212
1768
  __expose({
1213
1769
  open,
1214
1770
  close,
@@ -1263,6 +1819,7 @@ var dropdown_menu_sub_default = /* @__PURE__ */ defineVaporComponent({
1263
1819
  const props = __props;
1264
1820
  const emit = __emit;
1265
1821
  const parentMenu = useRequiredInject(menuKey, "RpDropdownMenuSub");
1822
+ const menuId = `${useId()}-interaction-menu`;
1266
1823
  const controllableOpen = useControllableValue({
1267
1824
  modelValue: () => props.open,
1268
1825
  defaultValue: () => props.defaultOpen,
@@ -1272,39 +1829,32 @@ var dropdown_menu_sub_default = /* @__PURE__ */ defineVaporComponent({
1272
1829
  const trigger = ref(null);
1273
1830
  const contentId = ref();
1274
1831
  const actualPlacement = ref("right-start");
1275
- const pendingFocus = ref(false);
1276
1832
  function setOpen(value) {
1277
1833
  if (isOpen.value !== value) controllableOpen.setValue(value);
1278
1834
  }
1835
+ const interaction = parentMenu.root.interaction.connectSubmenu({
1836
+ id: menuId,
1837
+ parentItemId: () => trigger.value?.id,
1838
+ isOpen: () => isOpen.value,
1839
+ setOpen
1840
+ });
1279
1841
  const context = {
1842
+ menuId,
1280
1843
  isOpen,
1281
1844
  trigger,
1282
1845
  contentId,
1283
1846
  actualPlacement,
1284
- pendingFocus,
1285
1847
  menu: null,
1848
+ interaction,
1286
1849
  open(focus = "first") {
1287
- parentMenu.closeSubmenus(context);
1288
- pendingFocus.value = focus;
1289
- setOpen(true);
1290
- if (focus) nextTick(() => context.menu?.focus(focus));
1850
+ if (!interaction.open(focus)) setOpen(true);
1291
1851
  },
1292
1852
  close(focusParent = false) {
1293
- setOpen(false);
1294
- pendingFocus.value = false;
1295
- context.menu?.closeSubmenus();
1296
- if (focusParent) {
1297
- parentMenu.setActive(trigger.value?.id ?? "");
1298
- parentMenu.focusElement();
1299
- }
1853
+ if (!interaction.close(focusParent)) setOpen(false);
1300
1854
  }
1301
1855
  };
1302
- parentMenu.registerSub(context);
1303
1856
  provide(subKey, context);
1304
- watch(parentMenu.root.isOpen, (open) => {
1305
- if (!open) context.close(false);
1306
- });
1307
- onBeforeUnmount(() => parentMenu.unregisterSub(context));
1857
+ onBeforeUnmount(interaction.dispose);
1308
1858
  return createSlot("default", {
1309
1859
  isOpen: () => unref(isOpen),
1310
1860
  open: () => context.open,
@@ -1394,7 +1944,6 @@ var dropdown_menu_sub_trigger_default = /* @__PURE__ */ defineVaporComponent({
1394
1944
  const generatedId = useId();
1395
1945
  const id = computed(() => props.id ?? `${generatedId}-sub-trigger`);
1396
1946
  const isDisabled = computed(() => menu.root.disabled.value || props.disabled);
1397
- const focused = computed(() => menu.isActive(id.value));
1398
1947
  const registration = {
1399
1948
  get id() {
1400
1949
  return id.value;
@@ -1402,11 +1951,21 @@ var dropdown_menu_sub_trigger_default = /* @__PURE__ */ defineVaporComponent({
1402
1951
  element: () => sub.trigger.value,
1403
1952
  textValue: () => props.textValue ?? sub.trigger.value?.textContent?.trim() ?? "",
1404
1953
  disabled: () => isDisabled.value,
1405
- activate: () => sub.open("first"),
1954
+ activate: () => void 0,
1406
1955
  submenu: sub
1407
1956
  };
1408
- menu.registerItem(registration);
1409
- onBeforeUnmount(() => menu.unregisterItem(id.value));
1957
+ let itemInteraction = menu.connectItem(registration);
1958
+ const focused = computed(() => {
1959
+ return id.value === registration.id && itemInteraction.active.value;
1960
+ });
1961
+ watch(id, () => {
1962
+ const previous = itemInteraction;
1963
+ const wasActive = previous.active.value;
1964
+ itemInteraction = menu.connectItem(registration);
1965
+ previous.dispose();
1966
+ if (wasActive) itemInteraction.setActive();
1967
+ }, { flush: "sync" });
1968
+ onBeforeUnmount(() => itemInteraction.dispose());
1410
1969
  function setElement(value) {
1411
1970
  resolveHTMLElementRef(value, id.value, (resolved) => {
1412
1971
  sub.trigger.value = resolved;
@@ -1414,25 +1973,24 @@ var dropdown_menu_sub_trigger_default = /* @__PURE__ */ defineVaporComponent({
1414
1973
  }
1415
1974
  function onMouseenter() {
1416
1975
  if (isDisabled.value) return;
1417
- menu.setActive(id.value);
1418
- sub.open(false);
1976
+ itemInteraction.hover(true);
1419
1977
  }
1420
1978
  function onClick(event) {
1421
- if (!event.defaultPrevented && !isDisabled.value) sub.open("first");
1979
+ if (!event.defaultPrevented && !isDisabled.value) itemInteraction.activate(event);
1422
1980
  }
1423
1981
  const rootAttrs = computed(() => mergeProps(attrs, {
1424
1982
  id: id.value,
1425
1983
  type: props.as === "button" ? "button" : void 0,
1426
1984
  role: "menuitem",
1427
1985
  tabindex: -1,
1428
- disabled: props.as === "button" ? optionalAttr(isDisabled.value) : void 0,
1429
- "aria-disabled": optionalAttr(isDisabled.value),
1986
+ disabled: props.as === "button" ? toOptionalAttribute(isDisabled.value) : void 0,
1987
+ "aria-disabled": toOptionalAttribute(isDisabled.value),
1430
1988
  "aria-haspopup": "menu",
1431
1989
  "aria-expanded": sub.isOpen.value,
1432
1990
  "aria-controls": sub.contentId.value,
1433
- "data-disabled": optionalAttr(isDisabled.value),
1434
- "data-focused": optionalAttr(focused.value),
1435
- "data-highlighted": optionalAttr(focused.value),
1991
+ "data-disabled": toOptionalAttribute(isDisabled.value),
1992
+ "data-focused": toOptionalAttribute(focused.value),
1993
+ "data-highlighted": toOptionalAttribute(focused.value),
1436
1994
  "data-state": sub.isOpen.value ? "open" : "closed",
1437
1995
  class: bem("rp-dropdown-menu__item", {
1438
1996
  focused: focused.value,
@@ -1484,41 +2042,28 @@ var dropdown_menu_trigger_default = /* @__PURE__ */ defineVaporComponent({
1484
2042
  root.setTrigger(resolved, id.value);
1485
2043
  });
1486
2044
  }
1487
- function openWithReference(focus = "first") {
2045
+ function prepareReference() {
1488
2046
  root.setReference(element.value);
1489
2047
  root.setReturnFocus(element.value);
1490
- root.open({ focus });
1491
2048
  }
1492
2049
  function onClick(event) {
1493
2050
  if (event.defaultPrevented || isDisabled.value) return;
1494
- root.setReference(element.value);
1495
- root.setReturnFocus(element.value);
1496
- root.toggle();
2051
+ root.interaction.onTriggerClick(prepareReference);
1497
2052
  }
1498
2053
  function onKeydown(event) {
1499
2054
  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 });
2055
+ root.interaction.onTriggerKeydown(event, prepareReference);
1511
2056
  }
1512
2057
  const rootAttrs = computed(() => mergeProps(attrs, {
1513
2058
  id: id.value,
1514
2059
  type: props.as === "button" ? "button" : void 0,
1515
- disabled: props.as === "button" ? optionalAttr(isDisabled.value) : void 0,
2060
+ disabled: props.as === "button" ? toOptionalAttribute(isDisabled.value) : void 0,
1516
2061
  "aria-controls": root.contentId.value,
1517
2062
  "aria-expanded": root.isOpen.value,
1518
2063
  "aria-haspopup": "menu",
1519
- "aria-disabled": props.as === "button" ? void 0 : optionalAttr(isDisabled.value),
2064
+ "aria-disabled": props.as === "button" ? void 0 : toOptionalAttribute(isDisabled.value),
1520
2065
  "data-state": root.isOpen.value ? "open" : "closed",
1521
- "data-disabled": optionalAttr(isDisabled.value),
2066
+ "data-disabled": toOptionalAttribute(isDisabled.value),
1522
2067
  onClick,
1523
2068
  onKeydown
1524
2069
  }));
@@ -1530,11 +2075,21 @@ var dropdown_menu_trigger_default = /* @__PURE__ */ defineVaporComponent({
1530
2075
  return n1;
1531
2076
  }
1532
2077
  });
1533
- var DEFAULT_FOCUS_TARGET = "first";
1534
- function getOpenFocusTarget(options) {
1535
- if (typeof options === "string") return options;
1536
- return options?.focus ?? "first";
2078
+ //#endregion
2079
+ //#region src/utils/indexPath.ts
2080
+ function getPathKey(path) {
2081
+ return path.length === 0 ? "root" : path.join("-");
2082
+ }
2083
+ function arePathsEqual(a, b) {
2084
+ return a.length === b.length && a.every((part, index) => part === b[index]);
2085
+ }
2086
+ function isPathPrefix(prefix, path) {
2087
+ return prefix.length <= path.length && prefix.every((part, index) => part === path[index]);
2088
+ }
2089
+ function normalizePath(indexOrPath) {
2090
+ return Array.isArray(indexOrPath) ? indexOrPath : [indexOrPath];
1537
2091
  }
2092
+ var DEFAULT_FOCUS_TARGET = "first";
1538
2093
  function getItemClass(item, focused, disabled, hasSubmenu, submenuOpen) {
1539
2094
  return bem("rp-dropdown-menu__item", {
1540
2095
  focused,
@@ -1550,33 +2105,6 @@ function hasItemSubmenu(item) {
1550
2105
  function hasNestedItems(items) {
1551
2106
  return items.some(hasItemSubmenu);
1552
2107
  }
1553
- function getPathKey(path) {
1554
- return path.length === 0 ? "root" : path.join("-");
1555
- }
1556
- function getParentPath(path) {
1557
- return path.slice(0, -1);
1558
- }
1559
- function arePathsEqual(a, b) {
1560
- return a.length === b.length && a.every((part, index) => part === b[index]);
1561
- }
1562
- function isPathPrefix(prefix, path) {
1563
- return prefix.length <= path.length && prefix.every((part, index) => part === path[index]);
1564
- }
1565
- function normalizePath(indexOrPath) {
1566
- return Array.isArray(indexOrPath) ? indexOrPath : [indexOrPath];
1567
- }
1568
- function getEventPoint(event) {
1569
- return {
1570
- x: event.clientX,
1571
- y: event.clientY
1572
- };
1573
- }
1574
- function hasMeasuredRect(rect) {
1575
- return rect.width > 0 || rect.height > 0;
1576
- }
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;
1579
- }
1580
2108
  //#endregion
1581
2109
  //#region src/components/dropdown-menu/dropdown-menu-item-row.vue?vue&type=script&setup=true&vapor=true&lang.ts
1582
2110
  var t0$1 = template("<div>");
@@ -1607,6 +2135,10 @@ var dropdown_menu_item_row_default = /* @__PURE__ */ defineVaporComponent({
1607
2135
  const slotProps = computed(() => props.context.getItemSlotProps(props.item, path.value, focused.value, disabled.value, submenuOpen.value));
1608
2136
  const itemElement = ref(null);
1609
2137
  const submenuElement = ref(null);
2138
+ const templateRefs = {
2139
+ item: itemElement,
2140
+ submenu: submenuElement
2141
+ };
1610
2142
  const { actualPlacement, floatingStyle } = useFloatingPosition({
1611
2143
  reference: itemElement,
1612
2144
  floating: submenuElement,
@@ -1632,14 +2164,13 @@ var dropdown_menu_item_row_default = /* @__PURE__ */ defineVaporComponent({
1632
2164
  function getChildKey(index, child) {
1633
2165
  return `${getPathKey([...path.value, index])}:${String(child.value)}`;
1634
2166
  }
1635
- const _setTemplateRef = createTemplateRefSetter();
1636
2167
  const _component_DropdownMenuItemRow__self = resolveComponent("DropdownMenuItemRow", true);
1637
2168
  const n10 = t1$1();
1638
2169
  const n1 = child(n10);
1639
2170
  renderEffect(() => setDynamicProps(n1, [publicItemProps.value]));
1640
2171
  setInsertionState(n1, null, 0);
1641
2172
  createSlot("item", { $: [() => slotProps.value] });
1642
- setStaticTemplateRef(n1, itemElement, null, "itemElement");
2173
+ setTemplateRefBinding(n1, () => templateRefs.item);
1643
2174
  setInsertionState(n10, null, 1);
1644
2175
  createIf(() => hasSubmenu.value && submenuOpen.value, () => {
1645
2176
  const n9 = t0$1();
@@ -1659,7 +2190,7 @@ var dropdown_menu_item_row_default = /* @__PURE__ */ defineVaporComponent({
1659
2190
  return createSlot("item", { $: [() => childSlotProps] });
1660
2191
  }, { _: 8 }) });
1661
2192
  }, (child, childIndex) => getChildKey(childIndex, child), 3);
1662
- _setTemplateRef(n9, submenuElement, null, "submenuElement");
2193
+ setTemplateRefBinding(n9, () => templateRefs.submenu);
1663
2194
  return n9;
1664
2195
  });
1665
2196
  return n10;
@@ -1693,151 +2224,181 @@ var dropdown_menu_items_default = /* @__PURE__ */ defineVaporComponent({
1693
2224
  }
1694
2225
  });
1695
2226
  //#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) });
2227
+ //#region src/components/dropdown-menu/dropdownMenuDataModel.ts
2228
+ function createDataIndexState(rootMenuId) {
2229
+ return {
2230
+ entries: [],
2231
+ itemIdByPath: /* @__PURE__ */ new Map(),
2232
+ pathByItemId: /* @__PURE__ */ new Map(),
2233
+ menuIdByPath: /* @__PURE__ */ new Map([["root", rootMenuId]]),
2234
+ pathByMenuId: /* @__PURE__ */ new Map([[rootMenuId, []]]),
2235
+ primaryItemId: /* @__PURE__ */ new WeakMap(),
2236
+ occurrences: /* @__PURE__ */ new Map()
2237
+ };
2238
+ }
2239
+ function getOccurrenceItemId(item, state, getStableItemId) {
2240
+ const occurrence = state.occurrences.get(item) ?? 0;
2241
+ state.occurrences.set(item, occurrence + 1);
2242
+ const stableItemId = getStableItemId(item);
2243
+ return {
2244
+ occurrence,
2245
+ itemId: occurrence === 0 ? stableItemId : `${stableItemId}-copy-${occurrence}`
2246
+ };
2247
+ }
2248
+ function indexItems(items, parentPath, ownerMenuId, state, getStableItemId) {
2249
+ for (const [order, item] of items.entries()) {
2250
+ const path = [...parentPath, order];
2251
+ const pathKey = getPathKey(path);
2252
+ const { itemId, occurrence } = getOccurrenceItemId(item, state, getStableItemId);
2253
+ const submenuMenuId = hasItemSubmenu(item) ? `${itemId}-menu` : void 0;
2254
+ state.entries.push({
2255
+ item,
2256
+ path,
2257
+ itemId,
2258
+ ownerMenuId,
2259
+ order,
2260
+ submenuMenuId
2261
+ });
2262
+ state.itemIdByPath.set(pathKey, itemId);
2263
+ state.pathByItemId.set(itemId, path);
2264
+ if (occurrence === 0) state.primaryItemId.set(item, itemId);
2265
+ if (!submenuMenuId) continue;
2266
+ state.menuIdByPath.set(pathKey, submenuMenuId);
2267
+ state.pathByMenuId.set(submenuMenuId, path);
2268
+ indexItems(item.children ?? [], path, submenuMenuId, state, getStableItemId);
1760
2269
  }
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);
1776
- }
1777
- watch(isVisible, setDocumentListeners, { immediate: true });
1778
- onBeforeUnmount(() => setDocumentListeners(false));
2270
+ }
2271
+ function buildDropdownMenuDataIndex(items, rootMenuId, getStableItemId) {
2272
+ const state = createDataIndexState(rootMenuId);
2273
+ indexItems(items, [], rootMenuId, state, getStableItemId);
1779
2274
  return {
1780
- open,
1781
- close,
1782
- toggle,
1783
- resetMenuFocus
2275
+ entries: state.entries,
2276
+ itemIdByPath: state.itemIdByPath,
2277
+ pathByItemId: state.pathByItemId,
2278
+ menuIdByPath: state.menuIdByPath,
2279
+ pathByMenuId: state.pathByMenuId,
2280
+ primaryItemId: state.primaryItemId
1784
2281
  };
1785
2282
  }
2283
+ function getDropdownMenuDataCollectionState(items) {
2284
+ const state = [];
2285
+ function collect(currentItems) {
2286
+ state.push(currentItems.length);
2287
+ for (const item of currentItems) {
2288
+ const children = item.children;
2289
+ state.push(item, Boolean(item.disabled), children);
2290
+ if (children) collect(children);
2291
+ }
2292
+ }
2293
+ collect(items);
2294
+ return state;
2295
+ }
1786
2296
  //#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());
2297
+ //#region src/components/dropdown-menu/useDropdownMenuDataRegistration.ts
2298
+ function getCurrentPath(index, entry) {
2299
+ return index.pathByItemId.get(entry.itemId) ?? entry.path;
2300
+ }
2301
+ function useDropdownMenuDataRegistration({ props, items, rootMenuId, interaction, rootMenu, menuRef, getItemElement, getSubmenuElement, isSubmenuOpeningLeft, onSelect }) {
2302
+ const openMenuIds = ref(/* @__PURE__ */ new Set());
2303
+ const itemIdentity = /* @__PURE__ */ new WeakMap();
2304
+ let nextItemIdentity = 0;
2305
+ let itemInteractions = /* @__PURE__ */ new Map();
2306
+ let menuInteractions = /* @__PURE__ */ new Map([[rootMenuId, rootMenu]]);
2307
+ let submenuInteractions = /* @__PURE__ */ new Map();
2308
+ function getStableItemId(item) {
2309
+ const existing = itemIdentity.get(item);
2310
+ if (existing) return existing;
2311
+ nextItemIdentity += 1;
2312
+ const id = `${rootMenuId}-item-${nextItemIdentity}`;
2313
+ itemIdentity.set(item, id);
2314
+ return id;
2315
+ }
2316
+ const registrationIndex = shallowRef(buildDropdownMenuDataIndex([], rootMenuId, getStableItemId));
2317
+ const collectionState = computed(() => getDropdownMenuDataCollectionState(items.value));
2318
+ function setMenuOpen(id, open) {
2319
+ const next = new Set(openMenuIds.value);
2320
+ if (open) next.add(id);
2321
+ else next.delete(id);
2322
+ openMenuIds.value = next;
2323
+ }
2324
+ function connectSubmenu(index, entry) {
2325
+ const submenuMenuId = entry.submenuMenuId;
2326
+ if (!submenuMenuId) return void 0;
2327
+ const submenu = interaction.connectSubmenu({
2328
+ id: submenuMenuId,
2329
+ parentItemId: () => entry.itemId,
2330
+ isOpen: () => openMenuIds.value.has(submenuMenuId),
2331
+ setOpen: (open) => setMenuOpen(submenuMenuId, open)
2332
+ });
2333
+ return {
2334
+ menu: submenu.connectContent({
2335
+ element: () => getSubmenuElement(getCurrentPath(index, entry)),
2336
+ focusTarget: () => menuRef.value,
2337
+ placement: () => {
2338
+ return getSubmenuElement(getCurrentPath(index, entry))?.dataset.placement ?? "right-start";
2339
+ }
2340
+ }),
2341
+ submenu
2342
+ };
1792
2343
  }
1793
- function focusTrigger() {
1794
- nextTick(() => {
1795
- if (targetRef.value instanceof HTMLElement) {
1796
- targetRef.value.focus();
1797
- return;
2344
+ function createSelectEvent(entry, originalEvent) {
2345
+ const event = new CustomEvent("dropdown-menu-select", {
2346
+ cancelable: true,
2347
+ detail: {
2348
+ originalEvent,
2349
+ value: entry.item.value
1798
2350
  }
1799
- (rootRef.value?.querySelector("[aria-haspopup=\"menu\"]"))?.focus();
1800
2351
  });
1801
- }
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;
1822
- }
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" });
2352
+ onSelect?.(entry.item, event);
2353
+ return event;
2354
+ }
2355
+ function connectItem(index, entry, nextMenus, nextSubmenus) {
2356
+ const menu = nextMenus.get(entry.ownerMenuId);
2357
+ if (!menu) return void 0;
2358
+ return menu.connectItem({
2359
+ id: entry.itemId,
2360
+ element: () => getItemElement(getCurrentPath(index, entry)),
2361
+ textValue: () => entry.item.label,
2362
+ disabled: () => Boolean(entry.item.disabled || props.disabled),
2363
+ order: () => entry.order,
2364
+ submenu: entry.submenuMenuId ? nextSubmenus.get(entry.submenuMenuId) : void 0,
2365
+ submenuDirection: () => isSubmenuOpeningLeft(getCurrentPath(index, entry)) ? "left" : "right",
2366
+ select: (originalEvent) => createSelectEvent(entry, originalEvent),
2367
+ closeOnSelect: () => props.closeOnSelect !== false
1829
2368
  });
2369
+ }
2370
+ function registerItems() {
2371
+ const nextIndex = buildDropdownMenuDataIndex(items.value, rootMenuId, getStableItemId);
2372
+ const nextItems = /* @__PURE__ */ new Map();
2373
+ const nextMenus = /* @__PURE__ */ new Map([[rootMenuId, rootMenu]]);
2374
+ const nextSubmenus = /* @__PURE__ */ new Map();
2375
+ for (const entry of nextIndex.entries) {
2376
+ const connection = connectSubmenu(nextIndex, entry);
2377
+ if (!entry.submenuMenuId || !connection) continue;
2378
+ nextMenus.set(entry.submenuMenuId, connection.menu);
2379
+ nextSubmenus.set(entry.submenuMenuId, connection.submenu);
2380
+ }
2381
+ for (const entry of nextIndex.entries) {
2382
+ const item = connectItem(nextIndex, entry, nextMenus, nextSubmenus);
2383
+ if (item) nextItems.set(entry.itemId, item);
2384
+ }
2385
+ for (const item of itemInteractions.values()) item.dispose();
2386
+ for (const submenu of submenuInteractions.values()) submenu.dispose();
2387
+ itemInteractions = nextItems;
2388
+ menuInteractions = nextMenus;
2389
+ submenuInteractions = nextSubmenus;
2390
+ registrationIndex.value = nextIndex;
2391
+ }
2392
+ watch(collectionState, registerItems, { immediate: true });
2393
+ onBeforeUnmount(() => {
2394
+ for (const item of itemInteractions.values()) item.dispose();
2395
+ for (const submenu of submenuInteractions.values()) submenu.dispose();
1830
2396
  });
1831
2397
  return {
1832
- activeDescendantId,
1833
- focusMenu,
1834
- focusTrigger,
1835
- getItemId,
1836
- getSubmenuId,
1837
- getItemElement,
1838
- getSubmenuElement,
1839
- isSubmenuOpeningLeft,
1840
- getMenuActiveDescendant
2398
+ openMenuIds,
2399
+ registrationIndex,
2400
+ getItemInteraction: (id) => itemInteractions.get(id),
2401
+ getMenuInteraction: (id) => menuInteractions.get(id)
1841
2402
  };
1842
2403
  }
1843
2404
  //#endregion
@@ -1845,26 +2406,6 @@ function useDropdownMenuDom({ menuId, rootRef, menuRef, targetRef, placement, fo
1845
2406
  var SAFE_TRIANGLE_PADDING = 2;
1846
2407
  var SAFE_TRIANGLE_ORIGIN_OFFSET = 2;
1847
2408
  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
2409
  function getDropdownMenuSafeTriangle({ itemRect, submenuRect, origin }) {
1869
2410
  const measuredItemRect = itemRect && hasMeasuredRect(itemRect) ? itemRect : null;
1870
2411
  const opensRight = !measuredItemRect || submenuRect.left >= measuredItemRect.right;
@@ -1949,260 +2490,170 @@ function useDropdownMenuHoverIntent({ openSubmenuPath, getItemElement, getSubmen
1949
2490
  item,
1950
2491
  path
1951
2492
  };
1952
- if (pendingHoverTimer !== void 0) window.clearTimeout(pendingHoverTimer);
1953
- pendingHoverTimer = window.setTimeout(() => {
1954
- if (!pendingHover) return;
1955
- const nextHover = pendingHover;
1956
- if (latestPointerPoint && isPointInCurrentSubmenu(latestPointerPoint)) {
1957
- clearPendingHover();
1958
- return;
1959
- }
1960
- commit(nextHover.item, nextHover.path);
1961
- }, SAFE_TRIANGLE_TIMEOUT);
1962
- }
1963
- function handleItemHover(item, path, event, commit) {
1964
- const point = event ? getEventPoint(event) : void 0;
1965
- latestPointerPoint = point ?? latestPointerPoint;
1966
- if (point && shouldDelayHover(path, point)) {
1967
- delayHoveredItem(item, path, commit);
1968
- return;
1969
- }
1970
- commit(item, path, point);
1971
- }
1972
- function handleMenuMousemove(event, commit) {
1973
- const point = getEventPoint(event);
1974
- latestPointerPoint = point;
1975
- updateSafeTriangleOrigin(point);
1976
- if (!pendingHover) return;
1977
- if (shouldDelayHover(pendingHover.path, point)) return;
1978
- const nextHover = pendingHover;
1979
- commit(nextHover.item, nextHover.path, point);
1980
- }
1981
- onUnmounted(clearPendingHover);
1982
- return {
1983
- clearPendingHover,
1984
- resetHoverIntent,
1985
- trackSubmenuOpen,
1986
- handleItemHover,
1987
- handleMenuMousemove
1988
- };
1989
- }
1990
- //#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);
2000
- }
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 });
2012
- }
2013
- function closeSubmenusAfter(menuPath) {
2014
- submenus.closeSubmenusAfter(menuPath);
2015
- hoverIntent.resetHoverIntent();
2016
- }
2017
- 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;
2021
- }
2022
- 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();
2029
- if (closed) hoverIntent.resetHoverIntent();
2030
- return closed;
2031
- }
2032
- function commitHoveredItem(item, path, point) {
2033
- hoverIntent.clearPendingHover();
2034
- focusedPath.value = [...path];
2035
- if (hasItemSubmenu(item)) openSubmenu(path, false, point);
2036
- else closeSubmenusAfter(getParentPath(path));
2037
- }
2038
- function focusHoveredItem(item, indexOrPath, event) {
2039
- 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
- }
2072
- }
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
- }
2493
+ if (pendingHoverTimer !== void 0) window.clearTimeout(pendingHoverTimer);
2494
+ pendingHoverTimer = window.setTimeout(() => {
2495
+ if (!pendingHover) return;
2496
+ const nextHover = pendingHover;
2497
+ if (latestPointerPoint && isPointInCurrentSubmenu(latestPointerPoint)) {
2498
+ clearPendingHover();
2499
+ return;
2500
+ }
2501
+ commit(nextHover.item, nextHover.path);
2502
+ }, SAFE_TRIANGLE_TIMEOUT);
2110
2503
  }
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);
2504
+ function handleItemHover(item, path, event, commit) {
2505
+ const point = event ? getClientPoint(event) : void 0;
2506
+ latestPointerPoint = point ?? latestPointerPoint;
2507
+ if (point && shouldDelayHover(path, point)) {
2508
+ delayHoveredItem(item, path, commit);
2118
2509
  return;
2119
2510
  }
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);
2511
+ commit(item, path, point);
2128
2512
  }
2129
- function onMenuMouseleave() {
2130
- hoverIntent.resetHoverIntent();
2513
+ function handleMenuMousemove(event, commit) {
2514
+ const point = getClientPoint(event);
2515
+ latestPointerPoint = point;
2516
+ updateSafeTriangleOrigin(point);
2517
+ if (!pendingHover) return;
2518
+ if (shouldDelayHover(pendingHover.path, point)) return;
2519
+ const nextHover = pendingHover;
2520
+ commit(nextHover.item, nextHover.path, point);
2131
2521
  }
2522
+ onUnmounted(clearPendingHover);
2132
2523
  return {
2133
- activateItem,
2134
- selectItem,
2135
- openSubmenu,
2136
- closeSubmenu,
2137
- focusHoveredItem,
2138
- onTriggerClick,
2139
- onTriggerKeydown,
2140
- onMenuKeydown,
2141
- onMenuMousemove,
2142
- onMenuMouseleave
2524
+ clearPendingHover,
2525
+ resetHoverIntent,
2526
+ trackSubmenuOpen,
2527
+ handleItemHover,
2528
+ handleMenuMousemove
2143
2529
  };
2144
2530
  }
2145
2531
  //#endregion
2146
- //#region src/components/dropdown-menu/useDropdownMenuNavigation.ts
2147
- function useDropdownMenuNavigation(items) {
2148
- const focusedPath = ref([]);
2532
+ //#region src/components/dropdown-menu/useDropdownMenuDataController.ts
2533
+ function useDropdownMenuDataController({ props, items, rootMenuId, menuId, menuRef, actualPlacement, interaction, rootMenu, onSelect }) {
2534
+ function getItemId(path) {
2535
+ return `${menuId.value}-item-${getPathKey(path)}`;
2536
+ }
2537
+ function getSubmenuId(path) {
2538
+ return `${menuId.value}-submenu-${getPathKey(path)}`;
2539
+ }
2540
+ function getItemElement(path) {
2541
+ return typeof document === "undefined" ? null : document.getElementById(getItemId(path));
2542
+ }
2543
+ function getSubmenuElement(path) {
2544
+ return typeof document === "undefined" ? null : document.getElementById(getSubmenuId(path));
2545
+ }
2546
+ function isSubmenuOpeningLeft(path) {
2547
+ const submenuRect = getSubmenuElement(path)?.getBoundingClientRect();
2548
+ const itemRect = getItemElement(path)?.getBoundingClientRect();
2549
+ if (submenuRect && itemRect && hasMeasuredRect(submenuRect) && hasMeasuredRect(itemRect)) return submenuRect.right <= itemRect.left;
2550
+ return actualPlacement.value.endsWith("end");
2551
+ }
2552
+ const { openMenuIds, registrationIndex, getItemInteraction, getMenuInteraction } = useDropdownMenuDataRegistration({
2553
+ props,
2554
+ items,
2555
+ rootMenuId,
2556
+ interaction,
2557
+ rootMenu,
2558
+ menuRef,
2559
+ getItemElement,
2560
+ getSubmenuElement,
2561
+ isSubmenuOpeningLeft,
2562
+ onSelect
2563
+ });
2564
+ function getDataItemId(path) {
2565
+ return registrationIndex.value.itemIdByPath.get(getPathKey(path));
2566
+ }
2567
+ const focusedPath = computed({
2568
+ get() {
2569
+ const id = interaction.activeItemId.value;
2570
+ return id ? [...registrationIndex.value.pathByItemId.get(id) ?? []] : [];
2571
+ },
2572
+ set(path) {
2573
+ const itemId = path.length > 0 ? getDataItemId(path) : void 0;
2574
+ if (itemId) getItemInteraction(itemId)?.setActive();
2575
+ else rootMenu.clearActive();
2576
+ }
2577
+ });
2149
2578
  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 ?? [];
2579
+ const activeDescendantId = computed(() => focusedPath.value.length === 0 ? void 0 : getItemId(focusedPath.value));
2580
+ const openSubmenuPath = computed(() => {
2581
+ let deepest = [];
2582
+ for (const id of openMenuIds.value) {
2583
+ const path = registrationIndex.value.pathByMenuId.get(id);
2584
+ if (path && path.length > deepest.length) deepest = path;
2156
2585
  }
2157
- return currentItems;
2586
+ return [...deepest];
2587
+ });
2588
+ function isItemFocused(indexOrPath) {
2589
+ const id = getDataItemId(normalizePath(indexOrPath));
2590
+ return Boolean(id && interaction.activeItemId.value === id);
2158
2591
  }
2159
- function getItemAtPath(path) {
2160
- if (path.length === 0) return void 0;
2161
- return getItemsAtPath(getParentPath(path))[path[path.length - 1]];
2592
+ function isSubmenuOpen(path) {
2593
+ const itemId = getDataItemId(path);
2594
+ return itemId ? getItemInteraction(itemId)?.submenuOpen.value ?? false : false;
2162
2595
  }
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];
2596
+ function openSubmenu(path, focus = DEFAULT_FOCUS_TARGET, point) {
2597
+ const itemId = getDataItemId(path);
2598
+ if (!(itemId ? getItemInteraction(itemId) : void 0)?.openSubmenu(focus)) return false;
2599
+ hoverIntent.trackSubmenuOpen(path, point);
2171
2600
  return true;
2172
2601
  }
2173
- function resetFocus() {
2174
- focusedPath.value = [];
2602
+ function closeSubmenu(path) {
2603
+ const itemId = getDataItemId(path);
2604
+ const closed = itemId ? getItemInteraction(itemId)?.closeSubmenu(false) ?? false : false;
2605
+ if (closed) hoverIntent.resetHoverIntent();
2606
+ return closed;
2175
2607
  }
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;
2608
+ function commitHoveredItem(item, path, point) {
2609
+ const itemId = getDataItemId(path);
2610
+ const itemInteraction = itemId ? getItemInteraction(itemId) : void 0;
2611
+ if (!itemInteraction) return;
2612
+ hoverIntent.clearPendingHover();
2613
+ const hasSubmenu = hasItemSubmenu(item);
2614
+ const opened = itemInteraction.hover(hasSubmenu);
2615
+ if (hasSubmenu && opened) hoverIntent.trackSubmenuOpen(path, point);
2181
2616
  }
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;
2617
+ const hoverIntent = useDropdownMenuHoverIntent({
2618
+ openSubmenuPath,
2619
+ getItemElement,
2620
+ getSubmenuElement
2621
+ });
2622
+ function focusHoveredItem(item, indexOrPath, event) {
2623
+ if (item.disabled) return;
2624
+ hoverIntent.handleItemHover(item, normalizePath(indexOrPath), event, commitHoveredItem);
2625
+ }
2626
+ function selectItem(item, originalEvent = new Event("select")) {
2627
+ const id = registrationIndex.value.primaryItemId.get(item);
2628
+ if (id) getItemInteraction(id)?.select(originalEvent);
2629
+ }
2630
+ function activateItem(_item, path, event) {
2631
+ const id = getDataItemId(path);
2632
+ if (id) getItemInteraction(id)?.activate(event);
2633
+ }
2634
+ function getMenuActiveDescendant(path) {
2635
+ const pathKey = getPathKey(path);
2636
+ const activeId = getMenuInteraction(registrationIndex.value.menuIdByPath.get(pathKey) ?? rootMenuId)?.activeId.value;
2637
+ const activePath = activeId ? registrationIndex.value.pathByItemId.get(activeId) : void 0;
2638
+ return activePath ? getItemId(activePath) : void 0;
2195
2639
  }
2196
2640
  return {
2197
2641
  focusedPath,
2198
2642
  focusedIndex,
2199
- getItemsAtPath,
2200
- getItemAtPath,
2201
- focusItem,
2202
- resetFocus,
2203
- setFocusedIndex,
2643
+ activeDescendantId,
2644
+ getItemId,
2645
+ getSubmenuId,
2646
+ getItemElement,
2647
+ getMenuActiveDescendant,
2204
2648
  isItemFocused,
2205
- moveFocus
2649
+ isSubmenuOpen,
2650
+ openSubmenu,
2651
+ closeSubmenu,
2652
+ focusHoveredItem,
2653
+ onMenuMousemove: (event) => hoverIntent.handleMenuMousemove(event, commitHoveredItem),
2654
+ resetHoverIntent: hoverIntent.resetHoverIntent,
2655
+ selectItem,
2656
+ activateItem
2206
2657
  };
2207
2658
  }
2208
2659
  //#endregion
@@ -2220,8 +2671,7 @@ function useDropdownMenuPortalPosition(options) {
2220
2671
  flipOptions: () => options.props.flipOptions,
2221
2672
  shift: () => options.props.shift !== false,
2222
2673
  collisionPadding: () => options.props.collisionPadding ?? 8,
2223
- autoUpdateOptions: () => options.props.autoUpdateOptions,
2224
- restartKey: options.restartKey
2674
+ autoUpdateOptions: () => options.props.autoUpdateOptions
2225
2675
  });
2226
2676
  return {
2227
2677
  actualPlacement: floating.actualPlacement,
@@ -2230,6 +2680,52 @@ function useDropdownMenuPortalPosition(options) {
2230
2680
  };
2231
2681
  }
2232
2682
  //#endregion
2683
+ //#region src/components/dropdown-menu/useDropdownMenuPresentation.ts
2684
+ function useDropdownMenuPresentation({ props, menuId, isDisabled, isVisible, isExplicitTarget, actualPlacement, contentHasSubmenu, activeDescendantId, interaction, resetHoverIntent, onMenuMousemove, open, close, toggle }) {
2685
+ const rootClass = computed(() => bem("rp-dropdown-menu", {
2686
+ [`placement-${actualPlacement.value}`]: true,
2687
+ target: isExplicitTarget.value,
2688
+ open: isVisible.value,
2689
+ disabled: isDisabled.value
2690
+ }));
2691
+ const contentClass = computed(() => bem("rp-dropdown-menu__content", { "has-submenu": contentHasSubmenu.value }));
2692
+ const triggerProps = computed(() => ({
2693
+ "aria-controls": isDisabled.value ? void 0 : menuId.value,
2694
+ "aria-expanded": isDisabled.value ? void 0 : isVisible.value,
2695
+ "aria-haspopup": "menu",
2696
+ disabled: isDisabled.value || void 0,
2697
+ onClick: () => interaction.onTriggerClick(),
2698
+ onKeydown: (event) => interaction.onTriggerKeydown(event)
2699
+ }));
2700
+ function onMenuKeydown(event) {
2701
+ resetHoverIntent();
2702
+ interaction.onMenuKeydown(event);
2703
+ }
2704
+ return {
2705
+ rootClass,
2706
+ contentClass,
2707
+ triggerProps,
2708
+ contentProps: computed(() => ({
2709
+ id: menuId.value,
2710
+ role: "menu",
2711
+ tabindex: -1,
2712
+ "aria-label": props.ariaLabel || void 0,
2713
+ "aria-activedescendant": activeDescendantId.value,
2714
+ onKeydown: onMenuKeydown,
2715
+ onMousemove: onMenuMousemove,
2716
+ onMouseleave: resetHoverIntent
2717
+ })),
2718
+ slotProps: computed(() => ({
2719
+ triggerProps: triggerProps.value,
2720
+ isOpen: isVisible.value,
2721
+ open,
2722
+ close,
2723
+ toggle
2724
+ })),
2725
+ onMenuKeydown
2726
+ };
2727
+ }
2728
+ //#endregion
2233
2729
  //#region src/components/dropdown-menu/useDropdownMenuRenderItems.ts
2234
2730
  function useDropdownMenuRenderItems({ props, actualPlacement, items, getItemId, getSubmenuId, getMenuActiveDescendant, getItemElement, isItemFocused, isSubmenuOpen, activateItem, focusHoveredItem, selectItem, openSubmenu, closeSubmenu, close }) {
2235
2731
  const renderedItems = computed(() => getRenderedItems(items.value));
@@ -2272,9 +2768,9 @@ function useDropdownMenuRenderItems({ props, actualPlacement, items, getItemId,
2272
2768
  "aria-expanded": hasSubmenu ? resolvedSubmenuOpen : void 0,
2273
2769
  "aria-haspopup": hasSubmenu ? "menu" : void 0,
2274
2770
  "aria-disabled": disabled || void 0,
2275
- "data-disabled": disabled ? "" : void 0,
2276
- "data-highlighted": focused ? "" : void 0,
2277
- "data-submenu": hasSubmenu ? "" : void 0,
2771
+ "data-disabled": toPresenceAttribute(disabled),
2772
+ "data-highlighted": toPresenceAttribute(focused),
2773
+ "data-submenu": toPresenceAttribute(hasSubmenu),
2278
2774
  onClick: (event) => activateItem(item, path, event),
2279
2775
  onMouseenter: (event) => focusHoveredItem(item, path, event)
2280
2776
  };
@@ -2332,45 +2828,53 @@ function useDropdownMenuRenderItems({ props, actualPlacement, items, getItemId,
2332
2828
  };
2333
2829
  }
2334
2830
  //#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
- };
2831
+ //#region src/components/dropdown-menu/useDropdownMenuTargetBindings.ts
2832
+ var TARGET_ATTRIBUTES = [
2833
+ "aria-controls",
2834
+ "aria-expanded",
2835
+ "aria-haspopup",
2836
+ "aria-disabled"
2837
+ ];
2838
+ function syncTargetAttributes(target, id, visible, disabled) {
2839
+ if (disabled) {
2840
+ target.removeAttribute("aria-controls");
2841
+ target.removeAttribute("aria-expanded");
2842
+ target.removeAttribute("aria-haspopup");
2843
+ target.setAttribute("aria-disabled", "true");
2844
+ return;
2845
+ }
2846
+ target.setAttribute("aria-controls", id);
2847
+ target.setAttribute("aria-expanded", String(visible));
2848
+ target.setAttribute("aria-haspopup", "menu");
2849
+ target.removeAttribute("aria-disabled");
2850
+ }
2851
+ function useDropdownMenuTargetBindings({ interaction, rootRef, menuRef, targetLifecycle, menuId, isVisible, isDisabled, ignoredTargets, pointerDownOutside, focusOutside, interactOutside }) {
2852
+ function watchInside(source) {
2853
+ watch(source, (element, _previous, onCleanup) => {
2854
+ if (element) onCleanup(interaction.connectInside(element));
2855
+ }, { immediate: true });
2856
+ }
2857
+ watchInside(rootRef);
2858
+ watchInside(menuRef);
2859
+ const cleanupDismissal = interaction.connectDismissal({
2860
+ ignoredTargets,
2861
+ pointerDownOutside,
2862
+ focusOutside,
2863
+ interactOutside
2864
+ });
2865
+ const onClick = () => interaction.onTriggerClick();
2866
+ const onKeydown = (event) => interaction.onTriggerKeydown(event);
2867
+ targetLifecycle.bindTarget({
2868
+ connect: (target) => interaction.connectInside(target),
2869
+ listeners: [["click", onClick], ["keydown", onKeydown]],
2870
+ attributes: {
2871
+ names: TARGET_ATTRIBUTES,
2872
+ apply: (target) => {
2873
+ syncTargetAttributes(target, menuId.value, isVisible.value, isDisabled.value);
2874
+ }
2875
+ }
2876
+ });
2877
+ onBeforeUnmount(cleanupDismissal);
2374
2878
  }
2375
2879
  //#endregion
2376
2880
  //#region src/components/dropdown-menu/useDropdownMenu.ts
@@ -2378,21 +2882,25 @@ function useDropdownMenu(props, emit = {}) {
2378
2882
  const rootRef = ref(null);
2379
2883
  const menuRef = ref(null);
2380
2884
  const arrowRef = ref(null);
2885
+ const generatedId = useId();
2886
+ const interactionRootMenuId = `${generatedId}-interaction-root`;
2381
2887
  const controllableOpen = useControllableValue({
2382
2888
  modelValue: () => props.open,
2383
2889
  defaultValue: () => false,
2384
2890
  onChange: (open) => emit.openChange?.(open)
2385
2891
  });
2386
- const generatedId = useId();
2387
2892
  const menuId = computed(() => props.id ?? `${generatedId}-menu`);
2388
2893
  const placement = computed(() => props.placement ?? "bottom-start");
2389
2894
  const teleportTo = useTeleportTarget(() => props.teleportTo);
2390
- const teleportPositioningKey = useTeleportPositioningKey();
2391
2895
  const shouldTeleport = computed(() => props.teleport !== false);
2392
- const { isExplicitTarget, reference, resolvedTarget } = useFloatingTarget(() => props.target, rootRef);
2393
- const targetElement = computed(() => isElementReference(resolvedTarget.value) ? resolvedTarget.value : null);
2896
+ const targetLifecycle = useFloatingTargetLifecycle({
2897
+ target: () => props.target,
2898
+ fallback: rootRef
2899
+ });
2900
+ const { isExplicitTarget, reference, targetElement } = targetLifecycle;
2394
2901
  const visibleItems = computed(() => props.items ?? []);
2395
2902
  const isDisabled = computed(() => Boolean(props.disabled));
2903
+ const modal = computed(() => Boolean(props.modal));
2396
2904
  const isOpen = controllableOpen.value;
2397
2905
  const isVisible = computed(() => isOpen.value && !isDisabled.value);
2398
2906
  const layer = useOverlayLayer({
@@ -2412,173 +2920,104 @@ function useDropdownMenu(props, emit = {}) {
2412
2920
  menuRef,
2413
2921
  arrowRef,
2414
2922
  isVisible,
2415
- placement,
2416
- restartKey: () => [
2417
- shouldTeleport.value,
2418
- teleportTo.value,
2419
- teleportPositioningKey.value
2420
- ]
2923
+ placement
2421
2924
  });
2422
2925
  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
2926
+ function focusTrigger() {
2927
+ nextTick(() => {
2928
+ if (targetElement.value instanceof HTMLElement) {
2929
+ targetElement.value.focus();
2930
+ return;
2931
+ }
2932
+ rootRef.value?.querySelector("[aria-haspopup=\"menu\"]")?.focus();
2933
+ });
2934
+ }
2935
+ function setOpen(nextOpen) {
2936
+ if (nextOpen && isDisabled.value) return;
2937
+ if (isOpen.value !== nextOpen) controllableOpen.setValue(nextOpen);
2938
+ }
2939
+ let resetHoverIntent;
2940
+ const interaction = useDropdownMenuInteraction({
2941
+ rootMenuId: interactionRootMenuId,
2942
+ isOpen: isVisible,
2943
+ disabled: isDisabled,
2944
+ modal,
2945
+ setOpen,
2946
+ connectLayerInteraction: layer.connectInteraction,
2947
+ focusTrigger,
2948
+ beforeOpen: () => resetHoverIntent(),
2949
+ beforeClose: () => resetHoverIntent()
2428
2950
  });
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
- }
2951
+ const open = interaction.open;
2952
+ const close = (options = {}) => interaction.close(options);
2953
+ const toggle = interaction.toggle;
2954
+ const rootMenu = interaction.connectRootMenu({
2955
+ element: () => menuRef.value,
2956
+ placement: () => actualPlacement.value
2442
2957
  });
2443
- const { activeDescendantId, focusMenu, focusTrigger, getItemId, getSubmenuId, getItemElement, getSubmenuElement, isSubmenuOpeningLeft, getMenuActiveDescendant } = useDropdownMenuDom({
2958
+ const dataController = useDropdownMenuDataController({
2959
+ props,
2960
+ items: visibleItems,
2961
+ rootMenuId: interactionRootMenuId,
2444
2962
  menuId,
2445
- rootRef,
2446
2963
  menuRef,
2447
- targetRef: targetElement,
2448
- placement: actualPlacement,
2449
- focusedPath
2450
- });
2451
- const hoverIntent = useDropdownMenuHoverIntent({
2452
- openSubmenuPath,
2453
- getItemElement,
2454
- getSubmenuElement
2964
+ actualPlacement,
2965
+ interaction,
2966
+ rootMenu,
2967
+ onSelect: (item, event) => emit.select?.(item, event)
2455
2968
  });
2456
- const disclosure = useDropdownMenuDisclosure({
2969
+ resetHoverIntent = dataController.resetHoverIntent;
2970
+ const { renderedItems, renderContext, getItemProps, getItemSlotProps } = useDropdownMenuRenderItems({
2457
2971
  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
2972
+ actualPlacement,
2973
+ items: visibleItems,
2974
+ getItemId: dataController.getItemId,
2975
+ getSubmenuId: dataController.getSubmenuId,
2976
+ getMenuActiveDescendant: dataController.getMenuActiveDescendant,
2977
+ getItemElement: dataController.getItemElement,
2978
+ isItemFocused: dataController.isItemFocused,
2979
+ isSubmenuOpen: dataController.isSubmenuOpen,
2980
+ activateItem: dataController.activateItem,
2981
+ focusHoveredItem: dataController.focusHoveredItem,
2982
+ selectItem: dataController.selectItem,
2983
+ openSubmenu: dataController.openSubmenu,
2984
+ closeSubmenu: dataController.closeSubmenu,
2985
+ close
2474
2986
  });
2475
- const { open, close, toggle } = disclosure;
2476
- const { activateItem, selectItem, openSubmenu, closeSubmenu, focusHoveredItem, onTriggerClick, onTriggerKeydown, onMenuKeydown, onMenuMousemove, onMenuMouseleave } = useDropdownMenuInteractions({
2987
+ const presentation = useDropdownMenuPresentation({
2477
2988
  props,
2478
- emit,
2989
+ menuId,
2479
2990
  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,
2991
+ isVisible,
2992
+ isExplicitTarget,
2993
+ actualPlacement,
2994
+ contentHasSubmenu,
2995
+ activeDescendantId: dataController.activeDescendantId,
2996
+ interaction,
2997
+ resetHoverIntent,
2998
+ onMenuMousemove: dataController.onMenuMousemove,
2519
2999
  open,
2520
3000
  close,
2521
3001
  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
3002
  });
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,
2554
- targetElement,
3003
+ useDropdownMenuTargetBindings({
3004
+ interaction,
3005
+ rootRef,
3006
+ menuRef,
3007
+ targetLifecycle,
2555
3008
  menuId,
2556
3009
  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" });
3010
+ isDisabled,
3011
+ ignoredTargets: () => props.ignore ?? [],
3012
+ pointerDownOutside: emit.pointerDownOutside,
3013
+ focusOutside: emit.focusOutside,
3014
+ interactOutside: emit.interactOutside
3015
+ });
3016
+ const layeredContentStyle = computed(() => ({
3017
+ ...contentStyle.value,
3018
+ zIndex: layer.zIndex.value
3019
+ }));
3020
+ onBeforeUnmount(rootMenu.dispose);
2582
3021
  return {
2583
3022
  rootRef,
2584
3023
  menuRef,
@@ -2589,33 +3028,30 @@ function useDropdownMenu(props, emit = {}) {
2589
3028
  visibleItems,
2590
3029
  renderedItems,
2591
3030
  renderContext,
2592
- focusedIndex,
2593
- focusedPath,
2594
- activeDescendantId,
2595
- rootClass,
2596
- contentClass,
2597
- contentStyle: computed(() => ({
2598
- ...contentStyle.value,
2599
- zIndex: layer.zIndex.value
2600
- })),
3031
+ focusedIndex: dataController.focusedIndex,
3032
+ focusedPath: dataController.focusedPath,
3033
+ activeDescendantId: dataController.activeDescendantId,
3034
+ rootClass: presentation.rootClass,
3035
+ contentClass: presentation.contentClass,
3036
+ contentStyle: layeredContentStyle,
2601
3037
  arrowStyle,
2602
3038
  actualPlacement,
2603
3039
  placementSide,
2604
3040
  isTargetMode: isExplicitTarget,
2605
3041
  teleportTo,
2606
3042
  shouldTeleport,
2607
- triggerProps,
2608
- contentProps,
2609
- slotProps,
3043
+ triggerProps: presentation.triggerProps,
3044
+ contentProps: presentation.contentProps,
3045
+ slotProps: presentation.slotProps,
2610
3046
  open,
2611
3047
  close,
2612
3048
  toggle,
2613
- selectItem,
2614
- openSubmenu,
2615
- closeSubmenu,
2616
- onItemMouseenter: focusHoveredItem,
2617
- onTriggerKeydown,
2618
- onMenuKeydown,
3049
+ selectItem: dataController.selectItem,
3050
+ openSubmenu: dataController.openSubmenu,
3051
+ closeSubmenu: dataController.closeSubmenu,
3052
+ onItemMouseenter: dataController.focusHoveredItem,
3053
+ onTriggerKeydown: (event) => interaction.onTriggerKeydown(event),
3054
+ onMenuKeydown: presentation.onMenuKeydown,
2619
3055
  getItemProps,
2620
3056
  getItemSlotProps
2621
3057
  };
@@ -2701,12 +3137,17 @@ var dropdown_menu_default = /* @__PURE__ */ defineVaporComponent({
2701
3137
  focusOutside: (event) => emit("focusOutside", event),
2702
3138
  interactOutside: (event) => emit("interactOutside", event)
2703
3139
  });
3140
+ const templateRefs = {
3141
+ root: rootRef,
3142
+ menu: menuRef,
3143
+ arrow: arrowRef
3144
+ };
2704
3145
  const { getPartAttrs, getRootAttrs } = useStylesApi(props, "root");
2705
3146
  const state = computed(() => isVisible.value ? "open" : "closed");
2706
3147
  const rootAttrs = computed(() => getRootAttrs({
2707
3148
  class: rootClass.value,
2708
3149
  "data-state": state.value,
2709
- "data-disabled": presence(props.disabled)
3150
+ "data-disabled": toPresenceAttribute(props.disabled)
2710
3151
  }));
2711
3152
  const publicSlotProps = computed(() => {
2712
3153
  const partAttrs = getPartAttrs("trigger");
@@ -2717,7 +3158,7 @@ var dropdown_menu_default = /* @__PURE__ */ defineVaporComponent({
2717
3158
  ...props.classNames?.trigger !== void 0 ? { class: partAttrs.class } : {},
2718
3159
  ...props.styles?.trigger !== void 0 ? { style: partAttrs.style } : {},
2719
3160
  "data-state": state.value,
2720
- "data-disabled": presence(props.disabled)
3161
+ "data-disabled": toPresenceAttribute(props.disabled)
2721
3162
  }
2722
3163
  };
2723
3164
  });
@@ -2753,10 +3194,10 @@ var dropdown_menu_default = /* @__PURE__ */ defineVaporComponent({
2753
3194
  setInsertionState(n26, null, 0);
2754
3195
  createIf(() => __props.arrow, () => {
2755
3196
  const n7 = t0();
2756
- _setTemplateRef(n7, arrowRef, null, "arrowRef");
2757
3197
  renderEffect(() => {
2758
3198
  setAttr(n7, "data-side", unref(placementSide));
2759
3199
  setStyle(n7, unref(arrowStyle));
3200
+ _setTemplateRef(n7, templateRefs.arrow);
2760
3201
  });
2761
3202
  return n7;
2762
3203
  });
@@ -2799,12 +3240,12 @@ var dropdown_menu_default = /* @__PURE__ */ defineVaporComponent({
2799
3240
  return n23;
2800
3241
  }, null, 129)];
2801
3242
  }, { _: 8 }) });
2802
- _setTemplateRef(n26, menuRef, null, "menuRef");
3243
+ setTemplateRefBinding(n26, () => templateRefs.menu, _setTemplateRef);
2803
3244
  return n26;
2804
3245
  }, null, 129);
2805
3246
  }, { _: 8 }));
2806
3247
  });
2807
- setStaticTemplateRef(n29, rootRef, null, "rootRef");
3248
+ setTemplateRefBinding(n29, () => templateRefs.root);
2808
3249
  return n29;
2809
3250
  }
2810
3251
  });