bits-ui 1.7.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/dist/bits/accordion/accordion.svelte.d.ts +56 -53
  2. package/dist/bits/accordion/accordion.svelte.js +78 -89
  3. package/dist/bits/accordion/components/accordion-content.svelte +5 -3
  4. package/dist/bits/accordion/components/accordion-header.svelte +4 -2
  5. package/dist/bits/accordion/components/accordion-item.svelte +6 -3
  6. package/dist/bits/accordion/components/accordion-trigger.svelte +4 -2
  7. package/dist/bits/accordion/components/accordion.svelte +4 -2
  8. package/dist/bits/alert-dialog/components/alert-dialog-action.svelte +4 -2
  9. package/dist/bits/alert-dialog/components/alert-dialog-cancel.svelte +4 -2
  10. package/dist/bits/alert-dialog/components/alert-dialog-content.svelte +8 -3
  11. package/dist/bits/aspect-ratio/aspect-ratio.svelte.js +2 -2
  12. package/dist/bits/aspect-ratio/components/aspect-ratio.svelte +4 -2
  13. package/dist/bits/avatar/avatar.svelte.js +4 -4
  14. package/dist/bits/avatar/components/avatar-fallback.svelte +4 -2
  15. package/dist/bits/avatar/components/avatar-image.svelte +4 -2
  16. package/dist/bits/avatar/components/avatar.svelte +4 -2
  17. package/dist/bits/button/components/button.svelte +1 -1
  18. package/dist/bits/calendar/calendar.svelte.d.ts +1 -9
  19. package/dist/bits/calendar/calendar.svelte.js +47 -38
  20. package/dist/bits/calendar/components/calendar-cell.svelte +4 -2
  21. package/dist/bits/calendar/components/calendar-day.svelte +4 -2
  22. package/dist/bits/calendar/components/calendar-grid-body.svelte +4 -2
  23. package/dist/bits/calendar/components/calendar-grid-head.svelte +4 -2
  24. package/dist/bits/calendar/components/calendar-grid-row.svelte +4 -2
  25. package/dist/bits/calendar/components/calendar-grid.svelte +4 -2
  26. package/dist/bits/calendar/components/calendar-head-cell.svelte +4 -2
  27. package/dist/bits/calendar/components/calendar-header.svelte +4 -2
  28. package/dist/bits/calendar/components/calendar-heading.svelte +4 -2
  29. package/dist/bits/calendar/components/calendar-next-button.svelte +4 -2
  30. package/dist/bits/calendar/components/calendar-prev-button.svelte +4 -2
  31. package/dist/bits/checkbox/checkbox.svelte.js +4 -14
  32. package/dist/bits/checkbox/components/checkbox-group-label.svelte +4 -2
  33. package/dist/bits/checkbox/components/checkbox-group.svelte +4 -2
  34. package/dist/bits/checkbox/components/checkbox.svelte +4 -2
  35. package/dist/bits/collapsible/collapsible.svelte.js +4 -10
  36. package/dist/bits/collapsible/components/collapsible-content.svelte +5 -3
  37. package/dist/bits/collapsible/components/collapsible-trigger.svelte +4 -2
  38. package/dist/bits/collapsible/components/collapsible.svelte +4 -2
  39. package/dist/bits/combobox/components/combobox-input.svelte +1 -1
  40. package/dist/bits/combobox/components/combobox.svelte +1 -1
  41. package/dist/bits/command/command.svelte.js +13 -45
  42. package/dist/bits/command/components/_command-label.svelte +6 -7
  43. package/dist/bits/command/components/_command-label.svelte.d.ts +1 -1
  44. package/dist/bits/command/components/command-empty.svelte +4 -2
  45. package/dist/bits/command/components/command-group-heading.svelte +4 -2
  46. package/dist/bits/command/components/command-group-items.svelte +4 -2
  47. package/dist/bits/command/components/command-group.svelte +4 -2
  48. package/dist/bits/command/components/command-input.svelte +4 -2
  49. package/dist/bits/command/components/command-item.svelte +4 -2
  50. package/dist/bits/command/components/command-link-item.svelte +4 -2
  51. package/dist/bits/command/components/command-list.svelte +4 -2
  52. package/dist/bits/command/components/command-loading.svelte +4 -2
  53. package/dist/bits/command/components/command-separator.svelte +4 -2
  54. package/dist/bits/command/components/command-viewport.svelte +4 -2
  55. package/dist/bits/command/components/command.svelte +4 -2
  56. package/dist/bits/context-menu/components/context-menu-content-static.svelte +2 -3
  57. package/dist/bits/context-menu/components/context-menu-content.svelte +2 -3
  58. package/dist/bits/context-menu/components/context-menu-trigger.svelte +1 -1
  59. package/dist/bits/date-field/components/date-field-input.svelte +4 -2
  60. package/dist/bits/date-field/components/date-field-label.svelte +4 -2
  61. package/dist/bits/date-field/components/date-field-segment.svelte +4 -2
  62. package/dist/bits/date-field/date-field.svelte.d.ts +4 -4
  63. package/dist/bits/date-field/date-field.svelte.js +15 -33
  64. package/dist/bits/date-picker/components/date-picker-calendar.svelte +4 -2
  65. package/dist/bits/date-range-field/components/date-range-field-input.svelte +4 -2
  66. package/dist/bits/date-range-field/components/date-range-field-label.svelte +4 -2
  67. package/dist/bits/date-range-field/components/date-range-field.svelte +4 -2
  68. package/dist/bits/date-range-field/date-range-field.svelte.js +3 -13
  69. package/dist/bits/date-range-picker/components/date-range-picker-calendar.svelte +4 -2
  70. package/dist/bits/dialog/components/dialog-close.svelte +4 -2
  71. package/dist/bits/dialog/components/dialog-content.svelte +8 -2
  72. package/dist/bits/dialog/components/dialog-description.svelte +4 -2
  73. package/dist/bits/dialog/components/dialog-overlay.svelte +8 -3
  74. package/dist/bits/dialog/components/dialog-title.svelte +4 -2
  75. package/dist/bits/dialog/components/dialog-trigger.svelte +4 -2
  76. package/dist/bits/dialog/dialog.svelte.d.ts +1 -1
  77. package/dist/bits/dialog/dialog.svelte.js +19 -47
  78. package/dist/bits/dropdown-menu/components/dropdown-menu-content-static.svelte +6 -5
  79. package/dist/bits/dropdown-menu/components/dropdown-menu-content.svelte +6 -5
  80. package/dist/bits/index.d.ts +2 -0
  81. package/dist/bits/index.js +2 -0
  82. package/dist/bits/label/components/label.svelte +4 -2
  83. package/dist/bits/label/label.svelte.js +2 -2
  84. package/dist/bits/link-preview/components/link-preview-content-static.svelte +6 -2
  85. package/dist/bits/link-preview/components/link-preview-content.svelte +6 -2
  86. package/dist/bits/link-preview/components/link-preview-trigger.svelte +5 -3
  87. package/dist/bits/link-preview/link-preview.svelte.js +3 -14
  88. package/dist/bits/menu/components/menu-checkbox-group.svelte +4 -2
  89. package/dist/bits/menu/components/menu-checkbox-item.svelte +6 -4
  90. package/dist/bits/menu/components/menu-content-static.svelte +6 -5
  91. package/dist/bits/menu/components/menu-content.svelte +6 -5
  92. package/dist/bits/menu/components/menu-group-heading.svelte +4 -2
  93. package/dist/bits/menu/components/menu-group.svelte +4 -2
  94. package/dist/bits/menu/components/menu-item.svelte +4 -2
  95. package/dist/bits/menu/components/menu-radio-group.svelte +4 -2
  96. package/dist/bits/menu/components/menu-radio-item.svelte +4 -2
  97. package/dist/bits/menu/components/menu-separator.svelte +4 -2
  98. package/dist/bits/menu/components/menu-sub-content-static.svelte +6 -5
  99. package/dist/bits/menu/components/menu-sub-content.svelte +6 -5
  100. package/dist/bits/menu/components/menu-sub-trigger.svelte +6 -3
  101. package/dist/bits/menu/components/menu-trigger.svelte +5 -3
  102. package/dist/bits/menu/menu.svelte.d.ts +7 -20
  103. package/dist/bits/menu/menu.svelte.js +26 -54
  104. package/dist/bits/menubar/components/menubar-content-static.svelte +4 -2
  105. package/dist/bits/menubar/components/menubar-content.svelte +4 -2
  106. package/dist/bits/menubar/components/menubar-menu.svelte +4 -2
  107. package/dist/bits/menubar/components/menubar-trigger.svelte +14 -6
  108. package/dist/bits/menubar/components/menubar.svelte +4 -2
  109. package/dist/bits/menubar/menubar.svelte.d.ts +24 -20
  110. package/dist/bits/menubar/menubar.svelte.js +40 -56
  111. package/dist/bits/meter/components/meter.svelte +4 -2
  112. package/dist/bits/meter/meter.svelte.js +2 -2
  113. package/dist/bits/navigation-menu/components/navigation-menu-content-impl.svelte +5 -2
  114. package/dist/bits/navigation-menu/components/navigation-menu-content.svelte +8 -3
  115. package/dist/bits/navigation-menu/components/navigation-menu-indicator-impl.svelte +4 -2
  116. package/dist/bits/navigation-menu/components/navigation-menu-indicator.svelte +6 -4
  117. package/dist/bits/navigation-menu/components/navigation-menu-item.svelte +8 -3
  118. package/dist/bits/navigation-menu/components/navigation-menu-link.svelte +4 -2
  119. package/dist/bits/navigation-menu/components/navigation-menu-list.svelte +4 -2
  120. package/dist/bits/navigation-menu/components/navigation-menu-sub.svelte +4 -2
  121. package/dist/bits/navigation-menu/components/navigation-menu-trigger.svelte +4 -2
  122. package/dist/bits/navigation-menu/components/navigation-menu-viewport.svelte +7 -3
  123. package/dist/bits/navigation-menu/components/navigation-menu.svelte +4 -2
  124. package/dist/bits/navigation-menu/navigation-menu.svelte.d.ts +29 -17
  125. package/dist/bits/navigation-menu/navigation-menu.svelte.js +107 -74
  126. package/dist/bits/navigation-menu/types.d.ts +14 -7
  127. package/dist/bits/pagination/components/pagination-next-button.svelte +4 -2
  128. package/dist/bits/pagination/components/pagination-page.svelte +4 -2
  129. package/dist/bits/pagination/components/pagination-prev-button.svelte +4 -2
  130. package/dist/bits/pagination/components/pagination.svelte +4 -2
  131. package/dist/bits/pagination/pagination.svelte.js +4 -4
  132. package/dist/bits/pin-input/components/pin-input-cell.svelte +4 -2
  133. package/dist/bits/pin-input/components/pin-input.svelte +5 -3
  134. package/dist/bits/pin-input/pin-input.svelte.js +4 -10
  135. package/dist/bits/popover/components/popover-close.svelte +4 -2
  136. package/dist/bits/popover/components/popover-content-static.svelte +6 -2
  137. package/dist/bits/popover/components/popover-content.svelte +6 -2
  138. package/dist/bits/popover/components/popover-trigger.svelte +5 -3
  139. package/dist/bits/popover/popover.svelte.js +4 -18
  140. package/dist/bits/progress/components/progress.svelte +4 -2
  141. package/dist/bits/progress/progress.svelte.js +2 -2
  142. package/dist/bits/radio-group/components/radio-group-item.svelte +4 -2
  143. package/dist/bits/radio-group/components/radio-group.svelte +4 -2
  144. package/dist/bits/radio-group/radio-group.svelte.js +4 -7
  145. package/dist/bits/range-calendar/components/range-calendar-cell.svelte +4 -2
  146. package/dist/bits/range-calendar/components/range-calendar-day.svelte +4 -2
  147. package/dist/bits/range-calendar/components/range-calendar.svelte +4 -2
  148. package/dist/bits/range-calendar/range-calendar.svelte.js +4 -4
  149. package/dist/bits/scroll-area/components/scroll-area-corner.svelte +8 -2
  150. package/dist/bits/scroll-area/components/scroll-area-scrollbar-auto.svelte +4 -1
  151. package/dist/bits/scroll-area/components/scroll-area-scrollbar-hover.svelte +1 -1
  152. package/dist/bits/scroll-area/components/scroll-area-scrollbar-scroll.svelte +5 -1
  153. package/dist/bits/scroll-area/components/scroll-area-scrollbar.svelte +4 -2
  154. package/dist/bits/scroll-area/components/scroll-area-thumb.svelte +8 -3
  155. package/dist/bits/scroll-area/components/scroll-area-viewport.svelte +4 -2
  156. package/dist/bits/scroll-area/components/scroll-area.svelte +4 -2
  157. package/dist/bits/scroll-area/scroll-area.svelte.js +13 -47
  158. package/dist/bits/select/components/select-content-static.svelte +6 -2
  159. package/dist/bits/select/components/select-content.svelte +6 -2
  160. package/dist/bits/select/components/select-group-heading.svelte +3 -2
  161. package/dist/bits/select/components/select-group.svelte +4 -2
  162. package/dist/bits/select/components/select-item.svelte +4 -2
  163. package/dist/bits/select/components/select-scroll-down-button.svelte +4 -2
  164. package/dist/bits/select/components/select-scroll-up-button.svelte +4 -2
  165. package/dist/bits/select/components/select-trigger.svelte +5 -3
  166. package/dist/bits/select/components/select-viewport.svelte +4 -2
  167. package/dist/bits/select/components/select.svelte +1 -1
  168. package/dist/bits/select/select.svelte.d.ts +0 -18
  169. package/dist/bits/select/select.svelte.js +10 -43
  170. package/dist/bits/separator/components/separator.svelte +4 -2
  171. package/dist/bits/separator/separator.svelte.js +2 -2
  172. package/dist/bits/slider/components/slider-range.svelte +4 -2
  173. package/dist/bits/slider/components/slider-thumb-label.svelte +50 -0
  174. package/dist/bits/slider/components/slider-thumb-label.svelte.d.ts +4 -0
  175. package/dist/bits/slider/components/slider-thumb.svelte +4 -2
  176. package/dist/bits/slider/components/slider-tick-label.svelte +50 -0
  177. package/dist/bits/slider/components/slider-tick-label.svelte.d.ts +4 -0
  178. package/dist/bits/slider/components/slider-tick.svelte +4 -2
  179. package/dist/bits/slider/components/slider.svelte +26 -5
  180. package/dist/bits/slider/exports.d.ts +3 -1
  181. package/dist/bits/slider/exports.js +2 -0
  182. package/dist/bits/slider/helpers.d.ts +14 -0
  183. package/dist/bits/slider/helpers.js +122 -0
  184. package/dist/bits/slider/slider.svelte.d.ts +93 -6
  185. package/dist/bits/slider/slider.svelte.js +198 -101
  186. package/dist/bits/slider/types.d.ts +111 -11
  187. package/dist/bits/switch/components/switch-thumb.svelte +4 -2
  188. package/dist/bits/switch/components/switch.svelte +4 -2
  189. package/dist/bits/switch/switch.svelte.js +3 -3
  190. package/dist/bits/tabs/components/tabs-content.svelte +4 -2
  191. package/dist/bits/tabs/components/tabs-list.svelte +4 -2
  192. package/dist/bits/tabs/components/tabs-trigger.svelte +4 -2
  193. package/dist/bits/tabs/components/tabs.svelte +4 -2
  194. package/dist/bits/tabs/tabs.svelte.js +6 -6
  195. package/dist/bits/time-field/components/time-field-hidden-input.svelte +10 -0
  196. package/dist/bits/{date-field/components/date-field-error.svelte.d.ts → time-field/components/time-field-hidden-input.svelte.d.ts} +6 -14
  197. package/dist/bits/time-field/components/time-field-input.svelte +39 -0
  198. package/dist/bits/time-field/components/time-field-input.svelte.d.ts +4 -0
  199. package/dist/bits/time-field/components/time-field-label.svelte +34 -0
  200. package/dist/bits/time-field/components/time-field-label.svelte.d.ts +4 -0
  201. package/dist/bits/time-field/components/time-field-segment.svelte +37 -0
  202. package/dist/bits/time-field/components/time-field-segment.svelte.d.ts +4 -0
  203. package/dist/bits/time-field/components/time-field.svelte +94 -0
  204. package/dist/bits/time-field/components/time-field.svelte.d.ts +20 -0
  205. package/dist/bits/time-field/exports.d.ts +5 -0
  206. package/dist/bits/time-field/exports.js +4 -0
  207. package/dist/bits/time-field/index.d.ts +1 -0
  208. package/dist/bits/time-field/index.js +1 -0
  209. package/dist/bits/time-field/time-field.svelte.d.ts +415 -0
  210. package/dist/bits/time-field/time-field.svelte.js +971 -0
  211. package/dist/bits/time-field/types.d.ts +137 -0
  212. package/dist/bits/time-field/types.js +1 -0
  213. package/dist/bits/time-range-field/components/time-range-field-input.svelte +43 -0
  214. package/dist/bits/time-range-field/components/time-range-field-input.svelte.d.ts +4 -0
  215. package/dist/bits/time-range-field/components/time-range-field-label.svelte +34 -0
  216. package/dist/bits/time-range-field/components/time-range-field-label.svelte.d.ts +4 -0
  217. package/dist/bits/time-range-field/components/time-range-field.svelte +144 -0
  218. package/dist/bits/time-range-field/components/time-range-field.svelte.d.ts +20 -0
  219. package/dist/bits/time-range-field/exports.d.ts +5 -0
  220. package/dist/bits/time-range-field/exports.js +4 -0
  221. package/dist/bits/time-range-field/index.d.ts +1 -0
  222. package/dist/bits/time-range-field/index.js +1 -0
  223. package/dist/bits/time-range-field/time-range-field.svelte.d.ts +90 -0
  224. package/dist/bits/time-range-field/time-range-field.svelte.js +210 -0
  225. package/dist/bits/time-range-field/types.d.ts +150 -0
  226. package/dist/bits/time-range-field/types.js +1 -0
  227. package/dist/bits/toggle/components/toggle.svelte +4 -2
  228. package/dist/bits/toggle/toggle.svelte.js +2 -2
  229. package/dist/bits/toggle-group/components/toggle-group-item.svelte +4 -2
  230. package/dist/bits/toggle-group/components/toggle-group.svelte +4 -2
  231. package/dist/bits/toggle-group/toggle-group.svelte.js +4 -4
  232. package/dist/bits/toolbar/components/toolbar-button.svelte +4 -2
  233. package/dist/bits/toolbar/components/toolbar-group-item.svelte +4 -2
  234. package/dist/bits/toolbar/components/toolbar-group.svelte +4 -2
  235. package/dist/bits/toolbar/components/toolbar-link.svelte +4 -2
  236. package/dist/bits/toolbar/components/toolbar.svelte +4 -2
  237. package/dist/bits/toolbar/toolbar.svelte.js +7 -7
  238. package/dist/bits/tooltip/components/tooltip-content-static.svelte +6 -2
  239. package/dist/bits/tooltip/components/tooltip-content.svelte +6 -2
  240. package/dist/bits/tooltip/components/tooltip-trigger.svelte +5 -3
  241. package/dist/bits/tooltip/tooltip.svelte.js +3 -14
  242. package/dist/bits/utilities/dismissible-layer/dismissible-layer.svelte +2 -0
  243. package/dist/bits/utilities/dismissible-layer/types.d.ts +2 -0
  244. package/dist/bits/utilities/dismissible-layer/use-dismissable-layer.svelte.d.ts +3 -3
  245. package/dist/bits/utilities/dismissible-layer/use-dismissable-layer.svelte.js +16 -25
  246. package/dist/bits/utilities/floating-layer/components/floating-layer-anchor.svelte +2 -1
  247. package/dist/bits/utilities/floating-layer/types.d.ts +1 -0
  248. package/dist/bits/utilities/floating-layer/use-floating-layer.svelte.d.ts +3 -2
  249. package/dist/bits/utilities/floating-layer/use-floating-layer.svelte.js +5 -26
  250. package/dist/bits/utilities/focus-scope/focus-scope.svelte +2 -0
  251. package/dist/bits/utilities/focus-scope/types.d.ts +2 -0
  252. package/dist/bits/utilities/focus-scope/use-focus-scope.svelte.d.ts +2 -1
  253. package/dist/bits/utilities/focus-scope/use-focus-scope.svelte.js +2 -8
  254. package/dist/bits/utilities/popper-layer/popper-layer-inner.svelte +4 -0
  255. package/dist/bits/utilities/popper-layer/popper-layer.svelte +3 -1
  256. package/dist/bits/utilities/presence-layer/presence-layer.svelte +2 -2
  257. package/dist/bits/utilities/presence-layer/types.d.ts +2 -1
  258. package/dist/bits/utilities/presence-layer/use-presence.svelte.d.ts +1 -1
  259. package/dist/bits/utilities/presence-layer/use-presence.svelte.js +19 -36
  260. package/dist/bits/utilities/text-selection-layer/text-selection-layer.svelte +2 -0
  261. package/dist/bits/utilities/text-selection-layer/types.d.ts +2 -0
  262. package/dist/bits/utilities/text-selection-layer/use-text-selection-layer.svelte.d.ts +3 -1
  263. package/dist/bits/utilities/text-selection-layer/use-text-selection-layer.svelte.js +2 -8
  264. package/dist/index.d.ts +1 -1
  265. package/dist/index.js +1 -1
  266. package/dist/internal/create-id.d.ts +8 -0
  267. package/dist/internal/create-id.js +5 -0
  268. package/dist/internal/date-time/field/helpers.d.ts +1 -0
  269. package/dist/internal/date-time/field/helpers.js +8 -2
  270. package/dist/internal/date-time/field/parts.d.ts +3 -1
  271. package/dist/internal/date-time/field/parts.js +10 -2
  272. package/dist/internal/date-time/field/segments.d.ts +9 -0
  273. package/dist/internal/date-time/field/segments.js +65 -0
  274. package/dist/internal/date-time/field/time-helpers.d.ts +77 -0
  275. package/dist/internal/date-time/field/time-helpers.js +301 -0
  276. package/dist/internal/date-time/field/types.d.ts +2 -2
  277. package/dist/internal/date-time/formatter.d.ts +11 -1
  278. package/dist/internal/date-time/formatter.js +56 -0
  279. package/dist/internal/date-time/utils.d.ts +7 -2
  280. package/dist/internal/date-time/utils.js +15 -1
  281. package/dist/internal/dom-context.svelte.d.ts +9 -0
  282. package/dist/internal/dom-context.svelte.js +26 -0
  283. package/dist/internal/use-roving-focus.svelte.d.ts +3 -3
  284. package/dist/internal/use-roving-focus.svelte.js +10 -11
  285. package/dist/shared/date/types.d.ts +27 -4
  286. package/dist/shared/index.d.ts +9 -2
  287. package/dist/types.d.ts +2 -0
  288. package/package.json +18 -16
  289. package/dist/bits/date-field/components/date-field-error.svelte +0 -0
@@ -1,12 +1,15 @@
1
1
  <script lang="ts">
2
2
  import type { NavigationMenuViewportProps } from "../types.js";
3
3
  import { useNavigationMenuViewport } from "../navigation-menu.svelte.js";
4
- import { useId } from "../../../internal/use-id.js";
4
+ import { createId } from "../../../internal/create-id.js";
5
5
  import PresenceLayer from "../../utilities/presence-layer/presence-layer.svelte";
6
6
  import { box, mergeProps } from "svelte-toolbelt";
7
+ import { Mounted } from "../../utilities/index.js";
8
+
9
+ const uid = $props.id();
7
10
 
8
11
  let {
9
- id = useId(),
12
+ id = createId(uid),
10
13
  ref = $bindable(null),
11
14
  forceMount = false,
12
15
  child,
@@ -25,7 +28,7 @@
25
28
  const mergedProps = $derived(mergeProps(restProps, viewportState.props));
26
29
  </script>
27
30
 
28
- <PresenceLayer {id} present={forceMount || viewportState.open}>
31
+ <PresenceLayer present={forceMount || viewportState.open} ref={viewportState.opts.ref}>
29
32
  {#snippet presence()}
30
33
  {#if child}
31
34
  {@render child({ props: mergedProps })}
@@ -34,5 +37,6 @@
34
37
  {@render children?.()}
35
38
  </div>
36
39
  {/if}
40
+ <Mounted bind:mounted={viewportState.mounted} />
37
41
  {/snippet}
38
42
  </PresenceLayer>
@@ -2,13 +2,15 @@
2
2
  import { box, mergeProps } from "svelte-toolbelt";
3
3
  import type { NavigationMenuRootProps } from "../types.js";
4
4
  import { useNavigationMenuRoot } from "../navigation-menu.svelte.js";
5
- import { useId } from "../../../internal/use-id.js";
5
+ import { createId } from "../../../internal/create-id.js";
6
6
  import { noop } from "../../../internal/noop.js";
7
7
 
8
+ const uid = $props.id();
9
+
8
10
  let {
9
11
  child,
10
12
  children,
11
- id = useId(),
13
+ id = createId(uid),
12
14
  ref = $bindable(null),
13
15
  value = $bindable(""),
14
16
  onValueChange = noop,
@@ -9,6 +9,7 @@ import { SvelteMap } from "svelte/reactivity";
9
9
  import { type Direction, type Orientation } from "../../shared/index.js";
10
10
  import type { BitsFocusEvent, BitsKeyboardEvent, BitsMouseEvent, BitsPointerEvent } from "../../internal/types.js";
11
11
  import { useRovingFocus } from "../../internal/use-roving-focus.svelte.js";
12
+ import type { FocusEventHandler, KeyboardEventHandler, MouseEventHandler, PointerEventHandler } from "svelte/elements";
12
13
  type NavigationMenuProviderStateProps = ReadableBoxedValues<{
13
14
  dir: Direction;
14
15
  orientation: Orientation;
@@ -18,11 +19,11 @@ type NavigationMenuProviderStateProps = ReadableBoxedValues<{
18
19
  previousValue: string;
19
20
  }> & {
20
21
  isRootMenu: boolean;
21
- onTriggerEnter: (itemValue: string) => void;
22
+ onTriggerEnter: (itemValue: string, itemState: NavigationMenuItemState | null) => void;
22
23
  onTriggerLeave?: () => void;
23
24
  onContentEnter?: () => void;
24
25
  onContentLeave?: () => void;
25
- onItemSelect: (itemValue: string) => void;
26
+ onItemSelect: (itemValue: string, itemState: NavigationMenuItemState | null) => void;
26
27
  onItemDismiss: () => void;
27
28
  };
28
29
  declare class NavigationMenuProviderState {
@@ -36,7 +37,10 @@ declare class NavigationMenuProviderState {
36
37
  onContentLeave: () => void;
37
38
  onItemSelect: NavigationMenuProviderStateProps["onItemSelect"];
38
39
  onItemDismiss: NavigationMenuProviderStateProps["onItemDismiss"];
40
+ activeItem: NavigationMenuItemState | null;
41
+ prevActiveItem: NavigationMenuItemState | null;
39
42
  constructor(opts: NavigationMenuProviderStateProps);
43
+ setActiveItem: (item: NavigationMenuItemState | null) => void;
40
44
  }
41
45
  type NavigationMenuRootStateProps = WithRefProps<WritableBoxedValues<{
42
46
  value: string;
@@ -53,7 +57,7 @@ declare class NavigationMenuRootState {
53
57
  previousValue: WritableBox<string>;
54
58
  isDelaySkipped: WritableBox<boolean>;
55
59
  constructor(opts: NavigationMenuRootStateProps);
56
- setValue: (newValue: string) => void;
60
+ setValue: (newValue: string, itemState: NavigationMenuItemState | null) => void;
57
61
  props: {
58
62
  readonly id: string;
59
63
  readonly "data-orientation": "horizontal" | "vertical";
@@ -71,8 +75,9 @@ declare class NavigationMenuSubState {
71
75
  readonly opts: NavigationMenuSubStateProps;
72
76
  readonly context: NavigationMenuProviderState;
73
77
  previousValue: WritableBox<string>;
78
+ subProvider: NavigationMenuProviderState;
74
79
  constructor(opts: NavigationMenuSubStateProps, context: NavigationMenuProviderState);
75
- setValue: (newValue: string) => void;
80
+ setValue: (newValue: string, itemState: NavigationMenuItemState | null) => void;
76
81
  props: {
77
82
  readonly id: string;
78
83
  readonly "data-orientation": "horizontal" | "vertical";
@@ -102,6 +107,7 @@ declare class NavigationMenuListState {
102
107
  }
103
108
  type NavigationMenuItemStateProps = WithRefProps<ReadableBoxedValues<{
104
109
  value: string;
110
+ openOnHover: boolean;
105
111
  }>>;
106
112
  export declare class NavigationMenuItemState {
107
113
  #private;
@@ -147,13 +153,14 @@ declare class NavigationMenuTriggerState {
147
153
  provider: NavigationMenuProviderState;
148
154
  item: NavigationMenuItemState;
149
155
  list: NavigationMenuListState;
156
+ sub: NavigationMenuSubState | null;
150
157
  });
151
158
  onpointerenter: (_: BitsPointerEvent<HTMLButtonElement>) => void;
152
- onpointermove: BitsPointerEventHandler<HTMLElement>;
153
- onpointerleave: BitsPointerEventHandler<HTMLElement>;
154
- onclick: (_: BitsMouseEvent<HTMLButtonElement>) => void;
155
- onkeydown: (e: BitsKeyboardEvent<HTMLButtonElement>) => void;
156
- focusProxyOnFocus: (e: BitsFocusEvent) => void;
159
+ onpointermove: PointerEventHandler<HTMLElement>;
160
+ onpointerleave: PointerEventHandler<HTMLElement>;
161
+ onclick: MouseEventHandler<HTMLButtonElement>;
162
+ onkeydown: KeyboardEventHandler<HTMLButtonElement>;
163
+ focusProxyOnFocus: FocusEventHandler<HTMLElement>;
157
164
  props: {
158
165
  readonly id: string;
159
166
  readonly disabled: boolean | null | undefined;
@@ -163,16 +170,16 @@ declare class NavigationMenuTriggerState {
163
170
  readonly "aria-expanded": "true" | "false";
164
171
  readonly "aria-controls": string | undefined;
165
172
  readonly "data-navigation-menu-trigger": "";
166
- readonly onpointermove: BitsPointerEventHandler<HTMLElement>;
167
- readonly onpointerleave: BitsPointerEventHandler<HTMLElement>;
173
+ readonly onpointermove: PointerEventHandler<HTMLElement>;
174
+ readonly onpointerleave: PointerEventHandler<HTMLElement>;
168
175
  readonly onpointerenter: (_: BitsPointerEvent<HTMLButtonElement>) => void;
169
- readonly onclick: (_: BitsMouseEvent<HTMLButtonElement>) => void;
170
- readonly onkeydown: (e: BitsKeyboardEvent<HTMLButtonElement>) => void;
176
+ readonly onclick: MouseEventHandler<HTMLButtonElement>;
177
+ readonly onkeydown: KeyboardEventHandler<HTMLButtonElement>;
171
178
  };
172
179
  focusProxyProps: {
173
180
  readonly id: string;
174
181
  readonly tabindex: 0;
175
- readonly onfocus: (e: BitsFocusEvent) => void;
182
+ readonly onfocus: FocusEventHandler<HTMLElement>;
176
183
  };
177
184
  restructureSpanProps: {
178
185
  readonly "aria-owns": string | undefined;
@@ -183,6 +190,7 @@ type NavigationMenuLinkStateProps = WithRefProps & ReadableBoxedValues<{
183
190
  onSelect: (e: Event) => void;
184
191
  }>;
185
192
  declare class NavigationMenuLinkState {
193
+ #private;
186
194
  readonly opts: NavigationMenuLinkStateProps;
187
195
  readonly context: {
188
196
  provider: NavigationMenuProviderState;
@@ -197,6 +205,8 @@ declare class NavigationMenuLinkState {
197
205
  onkeydown: (e: BitsKeyboardEvent) => void;
198
206
  onfocus: (_: BitsFocusEvent) => void;
199
207
  onblur: (_: BitsFocusEvent) => void;
208
+ onpointerenter: PointerEventHandler<HTMLAnchorElement>;
209
+ onpointermove: PointerEventHandler<HTMLElement>;
200
210
  props: {
201
211
  readonly id: string;
202
212
  readonly "data-active": "" | undefined;
@@ -206,6 +216,8 @@ declare class NavigationMenuLinkState {
206
216
  readonly onkeydown: (e: BitsKeyboardEvent) => void;
207
217
  readonly onfocus: (_: BitsFocusEvent) => void;
208
218
  readonly onblur: (_: BitsFocusEvent) => void;
219
+ readonly onpointerenter: PointerEventHandler<HTMLAnchorElement>;
220
+ readonly onpointermove: PointerEventHandler<HTMLElement>;
209
221
  readonly "data-navigation-menu-link": "";
210
222
  };
211
223
  }
@@ -266,11 +278,11 @@ declare class NavigationMenuContentState {
266
278
  list: NavigationMenuListState;
267
279
  });
268
280
  onpointerenter: (_: BitsPointerEvent) => void;
269
- onpointerleave: BitsPointerEventHandler<HTMLElement>;
281
+ onpointerleave: PointerEventHandler<HTMLElement>;
270
282
  props: {
271
283
  readonly id: string;
272
284
  readonly onpointerenter: (_: BitsPointerEvent) => void;
273
- readonly onpointerleave: BitsPointerEventHandler<HTMLElement>;
285
+ readonly onpointerleave: PointerEventHandler<HTMLElement>;
274
286
  };
275
287
  }
276
288
  type MotionAttribute = "to-start" | "to-end" | "from-start" | "from-end";
@@ -309,6 +321,7 @@ declare class NavigationMenuViewportState {
309
321
  viewportWidth: string | undefined;
310
322
  viewportHeight: string | undefined;
311
323
  activeContentValue: string;
324
+ mounted: boolean;
312
325
  constructor(opts: NavigationMenuViewportImplStateProps, context: NavigationMenuProviderState);
313
326
  props: {
314
327
  readonly id: string;
@@ -338,5 +351,4 @@ export declare function useNavigationMenuLink(props: NavigationMenuLinkStateProp
338
351
  export declare function useNavigationMenuContentImpl(props: NavigationMenuContentImplStateProps, itemState?: NavigationMenuItemState): NavigationMenuContentImplState;
339
352
  export declare function useNavigationMenuViewport(props: NavigationMenuViewportImplStateProps): NavigationMenuViewportState;
340
353
  export declare function useNavigationMenuIndicator(): NavigationMenuIndicatorState;
341
- type BitsPointerEventHandler<T extends HTMLElement = HTMLElement> = (e: BitsPointerEvent<T>) => void;
342
354
  export {};
@@ -2,7 +2,7 @@
2
2
  * Based on Radix UI's Navigation Menu
3
3
  * https://www.radix-ui.com/docs/primitives/components/navigation-menu
4
4
  */
5
- import { afterSleep, afterTick, box, useRefById, } from "svelte-toolbelt";
5
+ import { afterSleep, afterTick, box, attachRef, } from "svelte-toolbelt";
6
6
  import { Context, useDebounce, watch } from "runed";
7
7
  import { untrack } from "svelte";
8
8
  import { SvelteMap } from "svelte/reactivity";
@@ -38,6 +38,8 @@ class NavigationMenuProviderState {
38
38
  onContentLeave = noop;
39
39
  onItemSelect;
40
40
  onItemDismiss;
41
+ activeItem = null;
42
+ prevActiveItem = null;
41
43
  constructor(opts) {
42
44
  this.opts = opts;
43
45
  this.onTriggerEnter = opts.onTriggerEnter;
@@ -47,6 +49,10 @@ class NavigationMenuProviderState {
47
49
  this.onItemDismiss = opts.onItemDismiss;
48
50
  this.onItemSelect = opts.onItemSelect;
49
51
  }
52
+ setActiveItem = (item) => {
53
+ this.prevActiveItem = this.activeItem;
54
+ this.activeItem = item;
55
+ };
50
56
  }
51
57
  class NavigationMenuRootState {
52
58
  opts;
@@ -66,7 +72,6 @@ class NavigationMenuRootState {
66
72
  constructor(opts) {
67
73
  this.opts = opts;
68
74
  this.isDelaySkipped = boxAutoReset(false, this.opts.skipDelayDuration.current);
69
- useRefById(opts);
70
75
  this.provider = useNavigationMenuProvider({
71
76
  value: this.opts.value,
72
77
  previousValue: this.previousValue,
@@ -74,8 +79,8 @@ class NavigationMenuRootState {
74
79
  orientation: this.opts.orientation,
75
80
  rootNavigationMenuRef: this.opts.ref,
76
81
  isRootMenu: true,
77
- onTriggerEnter: (itemValue) => {
78
- this.#onTriggerEnter(itemValue);
82
+ onTriggerEnter: (itemValue, itemState) => {
83
+ this.#onTriggerEnter(itemValue, itemState);
79
84
  },
80
85
  onTriggerLeave: this.#onTriggerLeave,
81
86
  onContentEnter: this.#onContentEnter,
@@ -84,34 +89,44 @@ class NavigationMenuRootState {
84
89
  onItemDismiss: this.#onItemDismiss,
85
90
  });
86
91
  }
87
- #debouncedFn = useDebounce((val) => {
92
+ #debouncedFn = useDebounce((val, itemState) => {
88
93
  // passing `undefined` meant to reset the debounce timer
89
94
  if (typeof val === "string") {
90
- this.setValue(val);
95
+ this.setValue(val, itemState);
91
96
  }
92
97
  }, () => this.#derivedDelay);
93
- #onTriggerEnter = (itemValue) => {
94
- this.#debouncedFn(itemValue);
98
+ #onTriggerEnter = (itemValue, itemState) => {
99
+ this.#debouncedFn(itemValue, itemState);
95
100
  };
96
101
  #onTriggerLeave = () => {
97
102
  this.isDelaySkipped.current = false;
98
- this.#debouncedFn("");
103
+ this.#debouncedFn("", null);
99
104
  };
100
105
  #onContentEnter = () => {
101
- this.#debouncedFn();
106
+ this.#debouncedFn(undefined, null);
102
107
  };
103
108
  #onContentLeave = () => {
104
- this.#debouncedFn("");
109
+ if (this.provider.activeItem &&
110
+ this.provider.activeItem.opts.openOnHover.current === false) {
111
+ return;
112
+ }
113
+ this.#debouncedFn("", null);
105
114
  };
106
- #onItemSelect = (itemValue) => {
107
- this.setValue(itemValue);
115
+ #onItemSelect = (itemValue, itemState) => {
116
+ this.setValue(itemValue, itemState);
108
117
  };
109
118
  #onItemDismiss = () => {
110
- this.setValue("");
119
+ this.setValue("", null);
111
120
  };
112
- setValue = (newValue) => {
121
+ setValue = (newValue, itemState) => {
113
122
  this.previousValue.current = this.opts.value.current;
114
123
  this.opts.value.current = newValue;
124
+ this.provider.setActiveItem(itemState);
125
+ // When all menus are closed, we want to reset previousValue to prevent
126
+ // weird transitions from old positions when opening fresh
127
+ if (newValue === "") {
128
+ this.previousValue.current = "";
129
+ }
115
130
  };
116
131
  props = $derived.by(() => ({
117
132
  id: this.opts.id.current,
@@ -119,17 +134,18 @@ class NavigationMenuRootState {
119
134
  dir: this.opts.dir.current,
120
135
  [NAVIGATION_MENU_ROOT_ATTR]: "",
121
136
  [NAVIGATION_MENU_ATTR]: "",
137
+ ...attachRef(this.opts.ref),
122
138
  }));
123
139
  }
124
140
  class NavigationMenuSubState {
125
141
  opts;
126
142
  context;
127
143
  previousValue = box("");
144
+ subProvider;
128
145
  constructor(opts, context) {
129
146
  this.opts = opts;
130
147
  this.context = context;
131
- useRefById(opts);
132
- useNavigationMenuProvider({
148
+ this.subProvider = useNavigationMenuProvider({
133
149
  isRootMenu: false,
134
150
  value: this.opts.value,
135
151
  dir: this.context.opts.dir,
@@ -137,18 +153,26 @@ class NavigationMenuSubState {
137
153
  rootNavigationMenuRef: this.opts.ref,
138
154
  onTriggerEnter: this.setValue,
139
155
  onItemSelect: this.setValue,
140
- onItemDismiss: () => this.setValue(""),
156
+ onItemDismiss: () => this.setValue("", null),
141
157
  previousValue: this.previousValue,
142
158
  });
143
159
  }
144
- setValue = (newValue) => {
160
+ setValue = (newValue, itemState) => {
161
+ this.previousValue.current = this.opts.value.current;
145
162
  this.opts.value.current = newValue;
163
+ this.subProvider.setActiveItem(itemState);
164
+ // When all menus are closed, we want to reset previousValue to prevent
165
+ // weird transitions from old positions when opening fresh
166
+ if (newValue === "") {
167
+ this.previousValue.current = "";
168
+ }
146
169
  };
147
170
  props = $derived.by(() => ({
148
171
  id: this.opts.id.current,
149
172
  "data-orientation": getDataOrientation(this.opts.orientation.current),
150
173
  [NAVIGATION_MENU_SUB_ATTR]: "",
151
174
  [NAVIGATION_MENU_ATTR]: "",
175
+ ...attachRef(this.opts.ref),
152
176
  }));
153
177
  }
154
178
  class NavigationMenuListState {
@@ -162,17 +186,8 @@ class NavigationMenuListState {
162
186
  constructor(opts, context) {
163
187
  this.opts = opts;
164
188
  this.context = context;
165
- useRefById(opts);
166
- useRefById({
167
- id: this.wrapperId,
168
- ref: this.wrapperRef,
169
- onRefChange: (node) => {
170
- this.context.indicatorTrackRef.current = node;
171
- },
172
- deps: () => this.wrapperMounted,
173
- });
174
189
  this.rovingFocusGroup = useRovingFocus({
175
- rootNodeId: opts.id,
190
+ rootNode: opts.ref,
176
191
  candidateSelector: `[${NAVIGATION_MENU_TRIGGER_ATTR}]:not([data-disabled]), [${NAVIGATION_MENU_LINK_ATTR}]:not([data-disabled])`,
177
192
  loop: box.with(() => false),
178
193
  orientation: this.context.opts.orientation,
@@ -187,11 +202,13 @@ class NavigationMenuListState {
187
202
  }
188
203
  wrapperProps = $derived.by(() => ({
189
204
  id: this.wrapperId.current,
205
+ ...attachRef(this.wrapperRef, (v) => (this.context.indicatorTrackRef.current = v)),
190
206
  }));
191
207
  props = $derived.by(() => ({
192
208
  id: this.opts.id.current,
193
209
  "data-orientation": getDataOrientation(this.context.opts.orientation.current),
194
210
  [NAVIGATION_MENU_LIST_ATTR]: "",
211
+ ...attachRef(this.opts.ref),
195
212
  }));
196
213
  }
197
214
  export class NavigationMenuItemState {
@@ -252,20 +269,6 @@ class NavigationMenuTriggerState {
252
269
  this.context = context.provider;
253
270
  this.itemContext = context.item;
254
271
  this.listContext = context.list;
255
- useRefById({
256
- ...opts,
257
- onRefChange: (node) => {
258
- this.itemContext.triggerNode = node;
259
- },
260
- });
261
- useRefById({
262
- id: this.focusProxyId,
263
- ref: this.focusProxyRef,
264
- onRefChange: (node) => {
265
- this.itemContext.focusProxyNode = node;
266
- },
267
- deps: () => this.focusProxyMounted,
268
- });
269
272
  watch(() => this.opts.ref.current, () => {
270
273
  const node = this.opts.ref.current;
271
274
  if (!node)
@@ -281,28 +284,30 @@ class NavigationMenuTriggerState {
281
284
  if (this.opts.disabled.current ||
282
285
  this.wasClickClose ||
283
286
  this.itemContext.wasEscapeClose ||
284
- this.hasPointerMoveOpened.current) {
287
+ this.hasPointerMoveOpened.current ||
288
+ !this.itemContext.opts.openOnHover.current) {
285
289
  return;
286
290
  }
287
- this.context.onTriggerEnter(this.itemContext.opts.value.current);
291
+ this.context.onTriggerEnter(this.itemContext.opts.value.current, this.itemContext);
288
292
  this.hasPointerMoveOpened.current = true;
289
293
  });
290
294
  onpointerleave = whenMouse(() => {
291
- if (this.opts.disabled.current)
295
+ if (this.opts.disabled.current || !this.itemContext.opts.openOnHover.current)
292
296
  return;
293
297
  this.context.onTriggerLeave();
294
298
  this.hasPointerMoveOpened.current = false;
295
299
  });
296
- onclick = (_) => {
300
+ onclick = () => {
297
301
  // if opened via pointer move, we prevent the click event
298
302
  if (this.hasPointerMoveOpened.current)
299
303
  return;
300
- const shouldClose = this.open && this.context.opts.isRootMenu;
304
+ const shouldClose = this.open &&
305
+ (!this.itemContext.opts.openOnHover.current || this.context.opts.isRootMenu);
301
306
  if (shouldClose) {
302
- this.context.onItemSelect("");
307
+ this.context.onItemSelect("", null);
303
308
  }
304
309
  else if (!this.open) {
305
- this.context.onItemSelect(this.itemContext.opts.value.current);
310
+ this.context.onItemSelect(this.itemContext.opts.value.current, this.itemContext);
306
311
  }
307
312
  this.wasClickClose = shouldClose;
308
313
  };
@@ -340,11 +345,13 @@ class NavigationMenuTriggerState {
340
345
  onpointerenter: this.onpointerenter,
341
346
  onclick: this.onclick,
342
347
  onkeydown: this.onkeydown,
348
+ ...attachRef(this.opts.ref, (v) => (this.itemContext.triggerNode = v)),
343
349
  }));
344
350
  focusProxyProps = $derived.by(() => ({
345
351
  id: this.focusProxyId.current,
346
352
  tabindex: 0,
347
353
  onfocus: this.focusProxyOnFocus,
354
+ ...attachRef(this.focusProxyRef, (v) => (this.itemContext.focusProxyNode = v)),
348
355
  }));
349
356
  restructureSpanProps = $derived.by(() => ({
350
357
  "aria-owns": this.itemContext.contentId,
@@ -365,7 +372,6 @@ class NavigationMenuLinkState {
365
372
  constructor(opts, context) {
366
373
  this.opts = opts;
367
374
  this.context = context;
368
- useRefById(opts);
369
375
  }
370
376
  onclick = (e) => {
371
377
  const currTarget = e.currentTarget;
@@ -386,6 +392,23 @@ class NavigationMenuLinkState {
386
392
  onblur = (_) => {
387
393
  this.isFocused = false;
388
394
  };
395
+ #handlePointerDismiss = () => {
396
+ // only close submenu if this link is not inside the currently open submenu content
397
+ const currentlyOpenValue = this.context.provider.opts.value.current;
398
+ const isInsideOpenSubmenu = this.context.item.opts.value.current === currentlyOpenValue;
399
+ const activeItem = this.context.item.listContext.context.activeItem;
400
+ if (activeItem && !activeItem.opts.openOnHover.current)
401
+ return;
402
+ if (currentlyOpenValue && !isInsideOpenSubmenu) {
403
+ this.context.provider.onItemDismiss();
404
+ }
405
+ };
406
+ onpointerenter = () => {
407
+ this.#handlePointerDismiss();
408
+ };
409
+ onpointermove = whenMouse(() => {
410
+ this.#handlePointerDismiss();
411
+ });
389
412
  props = $derived.by(() => ({
390
413
  id: this.opts.id.current,
391
414
  "data-active": this.opts.active.current ? "" : undefined,
@@ -395,7 +418,10 @@ class NavigationMenuLinkState {
395
418
  onkeydown: this.onkeydown,
396
419
  onfocus: this.onfocus,
397
420
  onblur: this.onblur,
421
+ onpointerenter: this.onpointerenter,
422
+ onpointermove: this.onpointermove,
398
423
  [NAVIGATION_MENU_LINK_ATTR]: "",
424
+ ...attachRef(this.opts.ref),
399
425
  }));
400
426
  }
401
427
  class NavigationMenuIndicatorState {
@@ -424,10 +450,6 @@ class NavigationMenuIndicatorImplState {
424
450
  this.listContext = context.list;
425
451
  useResizeObserver(() => this.activeTrigger, this.handlePositionChange);
426
452
  useResizeObserver(() => this.context.indicatorTrackRef.current, this.handlePositionChange);
427
- useRefById({
428
- ...opts,
429
- deps: () => this.context.opts.value.current,
430
- });
431
453
  }
432
454
  handlePositionChange = () => {
433
455
  if (!this.activeTrigger)
@@ -460,6 +482,7 @@ class NavigationMenuIndicatorImplState {
460
482
  }),
461
483
  },
462
484
  [NAVIGATION_MENU_INDICATOR_ATTR]: "",
485
+ ...attachRef(this.opts.ref),
463
486
  }));
464
487
  }
465
488
  class NavigationMenuContentState {
@@ -485,24 +508,20 @@ class NavigationMenuContentState {
485
508
  this.context = context.provider;
486
509
  this.itemContext = context.item;
487
510
  this.listContext = context.list;
488
- useRefById({
489
- ...opts,
490
- onRefChange: (node) => {
491
- this.itemContext.contentNode = node;
492
- },
493
- deps: () => this.mounted,
494
- });
495
511
  }
496
512
  onpointerenter = (_) => {
497
513
  this.context.onContentEnter();
498
514
  };
499
515
  onpointerleave = whenMouse(() => {
516
+ if (!this.itemContext.opts.openOnHover.current)
517
+ return;
500
518
  this.context.onContentLeave();
501
519
  });
502
520
  props = $derived.by(() => ({
503
521
  id: this.opts.id.current,
504
522
  onpointerenter: this.onpointerenter,
505
523
  onpointerleave: this.onpointerleave,
524
+ ...attachRef(this.opts.ref, (v) => (this.itemContext.contentNode = v)),
506
525
  }));
507
526
  }
508
527
  class NavigationMenuContentImplState {
@@ -520,6 +539,11 @@ class NavigationMenuContentImplState {
520
539
  const prevIndex = values.indexOf(this.context.opts.previousValue.current);
521
540
  const isSelected = this.itemContext.opts.value.current === this.context.opts.value.current;
522
541
  const wasSelected = prevIndex === values.indexOf(this.itemContext.opts.value.current);
542
+ // When all menus are closed, we want to reset motion state to prevent residual animations
543
+ if (!this.context.opts.value.current && !this.context.opts.previousValue.current) {
544
+ untrack(() => (this.prevMotionAttribute = null));
545
+ return null;
546
+ }
523
547
  // We only want to update selected and the last selected content
524
548
  // this avoids animations being interrupted outside of that range
525
549
  if (!isSelected && !wasSelected)
@@ -546,10 +570,6 @@ class NavigationMenuContentImplState {
546
570
  this.itemContext = itemContext;
547
571
  this.listContext = itemContext.listContext;
548
572
  this.context = itemContext.listContext.context;
549
- useRefById({
550
- ...opts,
551
- deps: () => this.context.opts.value.current,
552
- });
553
573
  watch([
554
574
  () => this.itemContext.opts.value.current,
555
575
  () => this.itemContext.triggerNode,
@@ -585,8 +605,17 @@ class NavigationMenuContentImplState {
585
605
  const target = e.target;
586
606
  const isTrigger = this.listContext.listTriggers.some((trigger) => trigger.contains(target));
587
607
  const isRootViewport = this.context.opts.isRootMenu && this.context.viewportRef.current?.contains(target);
588
- if (isTrigger || isRootViewport || !this.context.opts.isRootMenu)
608
+ if (!this.context.opts.isRootMenu && !isTrigger) {
609
+ this.context.onItemDismiss();
610
+ return;
611
+ }
612
+ if (isTrigger || isRootViewport) {
589
613
  e.preventDefault();
614
+ return;
615
+ }
616
+ if (!this.itemContext.opts.openOnHover.current) {
617
+ this.context.onItemSelect("", null);
618
+ }
590
619
  };
591
620
  onkeydown = (e) => {
592
621
  // prevent parent menus handling sub-menu keydown events
@@ -650,6 +679,7 @@ class NavigationMenuContentImplState {
650
679
  "data-state": getDataOpenClosed(this.context.opts.value.current === this.itemContext.opts.value.current),
651
680
  onkeydown: this.onkeydown,
652
681
  [NAVIGATION_MENU_CONTENT_ATTR]: "",
682
+ ...attachRef(this.opts.ref),
653
683
  }));
654
684
  }
655
685
  class NavigationMenuViewportState {
@@ -661,16 +691,10 @@ class NavigationMenuViewportState {
661
691
  viewportWidth = $derived.by(() => (this.size ? `${this.size.width}px` : undefined));
662
692
  viewportHeight = $derived.by(() => (this.size ? `${this.size.height}px` : undefined));
663
693
  activeContentValue = $derived.by(() => this.context.opts.value.current);
694
+ mounted = $state(false);
664
695
  constructor(opts, context) {
665
696
  this.opts = opts;
666
697
  this.context = context;
667
- useRefById({
668
- ...opts,
669
- onRefChange: (node) => {
670
- this.context.viewportRef.current = node;
671
- },
672
- deps: () => this.open,
673
- });
674
698
  watch([() => this.activeContentValue, () => this.open], () => {
675
699
  afterTick(() => {
676
700
  const currNode = this.context.viewportRef.current;
@@ -695,6 +719,12 @@ class NavigationMenuViewportState {
695
719
  };
696
720
  }
697
721
  });
722
+ // reset size when viewport closes to prevent residual size animations
723
+ watch(() => this.mounted, () => {
724
+ if (!this.mounted && this.size) {
725
+ this.size = null;
726
+ }
727
+ });
698
728
  }
699
729
  props = $derived.by(() => ({
700
730
  id: this.opts.id.current,
@@ -708,12 +738,14 @@ class NavigationMenuViewportState {
708
738
  [NAVIGATION_MENU_VIEWPORT_ATTR]: "",
709
739
  onpointerenter: this.context.onContentEnter,
710
740
  onpointerleave: this.context.onContentLeave,
741
+ ...attachRef(this.opts.ref, (v) => (this.context.viewportRef.current = v)),
711
742
  }));
712
743
  }
713
744
  const NavigationMenuProviderContext = new Context("NavigationMenu.Root");
714
745
  export const NavigationMenuItemContext = new Context("NavigationMenu.Item");
715
746
  const NavigationMenuListContext = new Context("NavigationMenu.List");
716
747
  const NavigationMenuContentContext = new Context("NavigationMenu.Content");
748
+ const NavigationMenuSubContext = new Context("NavigationMenu.Sub");
717
749
  export function useNavigationMenuRoot(props) {
718
750
  return new NavigationMenuRootState(props);
719
751
  }
@@ -740,6 +772,7 @@ export function useNavigationMenuTrigger(props) {
740
772
  provider: NavigationMenuProviderContext.get(),
741
773
  item: NavigationMenuItemContext.get(),
742
774
  list: NavigationMenuListContext.get(),
775
+ sub: NavigationMenuSubContext.getOr(null),
743
776
  });
744
777
  }
745
778
  export function useNavigationMenuContent(props) {