@ng-cn/core 1.0.16 → 1.0.18

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 (262) hide show
  1. package/package.json +6 -5
  2. package/src/app/lib/components/ui/accordion/accordion-content.component.ts +11 -14
  3. package/src/app/lib/components/ui/accordion/accordion-context.ts +1 -0
  4. package/src/app/lib/components/ui/accordion/accordion-item.component.ts +8 -0
  5. package/src/app/lib/components/ui/accordion/accordion-trigger.component.ts +5 -1
  6. package/src/app/lib/components/ui/accordion/accordion.component.ts +24 -6
  7. package/src/app/lib/components/ui/alert/alert-variants.ts +18 -4
  8. package/src/app/lib/components/ui/alert-dialog/alert-dialog-action.component.ts +1 -0
  9. package/src/app/lib/components/ui/alert-dialog/alert-dialog-cancel.component.ts +1 -1
  10. package/src/app/lib/components/ui/alert-dialog/alert-dialog-content.component.ts +34 -17
  11. package/src/app/lib/components/ui/alert-dialog/alert-dialog-description.component.ts +1 -0
  12. package/src/app/lib/components/ui/alert-dialog/alert-dialog-footer.component.ts +1 -0
  13. package/src/app/lib/components/ui/alert-dialog/alert-dialog-header.component.ts +1 -0
  14. package/src/app/lib/components/ui/alert-dialog/alert-dialog-title.component.ts +1 -0
  15. package/src/app/lib/components/ui/alert-dialog/alert-dialog-trigger.component.ts +1 -0
  16. package/src/app/lib/components/ui/alert-dialog/alert-dialog.component.ts +4 -0
  17. package/src/app/lib/components/ui/aspect-ratio/aspect-ratio.component.ts +1 -0
  18. package/src/app/lib/components/ui/avatar/avatar-context.ts +9 -0
  19. package/src/app/lib/components/ui/avatar/avatar-fallback.component.ts +6 -9
  20. package/src/app/lib/components/ui/avatar/avatar-image.component.ts +40 -11
  21. package/src/app/lib/components/ui/avatar/avatar.component.ts +18 -13
  22. package/src/app/lib/components/ui/avatar/index.ts +1 -0
  23. package/src/app/lib/components/ui/avatar/ui-avatar.component.ts +13 -20
  24. package/src/app/lib/components/ui/badge/badge-variants.ts +1 -1
  25. package/src/app/lib/components/ui/badge/badge.component.ts +1 -0
  26. package/src/app/lib/components/ui/breadcrumb/breadcrumb-ellipsis.component.ts +1 -0
  27. package/src/app/lib/components/ui/breadcrumb/breadcrumb-item.component.ts +3 -7
  28. package/src/app/lib/components/ui/breadcrumb/breadcrumb-link.component.ts +4 -11
  29. package/src/app/lib/components/ui/breadcrumb/breadcrumb-list.component.ts +3 -7
  30. package/src/app/lib/components/ui/breadcrumb/breadcrumb-page.component.ts +1 -0
  31. package/src/app/lib/components/ui/breadcrumb/breadcrumb-separator.component.ts +20 -24
  32. package/src/app/lib/components/ui/breadcrumb/breadcrumb.component.ts +1 -0
  33. package/src/app/lib/components/ui/button/button-variants.ts +3 -3
  34. package/src/app/lib/components/ui/button/button.component.ts +1 -0
  35. package/src/app/lib/components/ui/button-group/button-group.component.ts +1 -0
  36. package/src/app/lib/components/ui/calendar/calendar.component.ts +5 -1
  37. package/src/app/lib/components/ui/carousel/carousel-content.component.ts +1 -0
  38. package/src/app/lib/components/ui/carousel/carousel-item.component.ts +1 -0
  39. package/src/app/lib/components/ui/carousel/carousel-next.component.ts +1 -0
  40. package/src/app/lib/components/ui/carousel/carousel-previous.component.ts +1 -0
  41. package/src/app/lib/components/ui/carousel/carousel.component.ts +1 -0
  42. package/src/app/lib/components/ui/chart/chart-container.component.ts +1 -0
  43. package/src/app/lib/components/ui/chart/chart-legend-content.component.ts +1 -0
  44. package/src/app/lib/components/ui/chart/chart-legend.component.ts +5 -5
  45. package/src/app/lib/components/ui/chart/chart-tooltip-content.component.ts +5 -5
  46. package/src/app/lib/components/ui/chart/chart-tooltip.component.ts +5 -5
  47. package/src/app/lib/components/ui/chart/chart.component.ts +1 -0
  48. package/src/app/lib/components/ui/checkbox/checkbox.component.ts +1 -1
  49. package/src/app/lib/components/ui/collapsible/collapsible-content.component.ts +3 -1
  50. package/src/app/lib/components/ui/collapsible/collapsible-context.ts +1 -0
  51. package/src/app/lib/components/ui/collapsible/collapsible-trigger.component.ts +2 -0
  52. package/src/app/lib/components/ui/collapsible/collapsible.component.ts +4 -0
  53. package/src/app/lib/components/ui/combobox/combobox-content.component.ts +1 -0
  54. package/src/app/lib/components/ui/combobox/combobox-empty.component.ts +1 -0
  55. package/src/app/lib/components/ui/combobox/combobox-group.component.ts +1 -0
  56. package/src/app/lib/components/ui/combobox/combobox-input.component.ts +1 -0
  57. package/src/app/lib/components/ui/combobox/combobox-item.component.ts +1 -4
  58. package/src/app/lib/components/ui/combobox/combobox-list.component.ts +1 -1
  59. package/src/app/lib/components/ui/combobox/combobox-trigger.component.ts +1 -0
  60. package/src/app/lib/components/ui/combobox/combobox-value.component.ts +1 -0
  61. package/src/app/lib/components/ui/combobox/combobox.component.ts +1 -1
  62. package/src/app/lib/components/ui/command/command-dialog.component.ts +1 -0
  63. package/src/app/lib/components/ui/command/command-empty.component.ts +1 -0
  64. package/src/app/lib/components/ui/command/command-group.component.ts +1 -0
  65. package/src/app/lib/components/ui/command/command-input.component.ts +1 -0
  66. package/src/app/lib/components/ui/command/command-item.component.ts +2 -1
  67. package/src/app/lib/components/ui/command/command-list.component.ts +1 -0
  68. package/src/app/lib/components/ui/command/command-separator.component.ts +1 -0
  69. package/src/app/lib/components/ui/command/command-shortcut.component.ts +1 -0
  70. package/src/app/lib/components/ui/command/command.component.ts +1 -0
  71. package/src/app/lib/components/ui/context-menu/context-menu-checkbox-item.component.ts +1 -0
  72. package/src/app/lib/components/ui/context-menu/context-menu-content.component.ts +49 -17
  73. package/src/app/lib/components/ui/context-menu/context-menu-item.component.ts +2 -1
  74. package/src/app/lib/components/ui/context-menu/context-menu-label.component.ts +1 -0
  75. package/src/app/lib/components/ui/context-menu/context-menu-radio-group.component.ts +1 -0
  76. package/src/app/lib/components/ui/context-menu/context-menu-radio-item.component.ts +1 -0
  77. package/src/app/lib/components/ui/context-menu/context-menu-separator.component.ts +1 -0
  78. package/src/app/lib/components/ui/context-menu/context-menu-shortcut.component.ts +1 -0
  79. package/src/app/lib/components/ui/context-menu/context-menu-sub-content.component.ts +3 -0
  80. package/src/app/lib/components/ui/context-menu/context-menu-sub-trigger.component.ts +32 -2
  81. package/src/app/lib/components/ui/context-menu/context-menu-sub.component.ts +4 -0
  82. package/src/app/lib/components/ui/context-menu/context-menu-trigger.component.ts +1 -0
  83. package/src/app/lib/components/ui/context-menu/context-menu.component.ts +1 -0
  84. package/src/app/lib/components/ui/data-table/data-table-content.component.ts +1 -0
  85. package/src/app/lib/components/ui/data-table/data-table-pagination.component.ts +1 -0
  86. package/src/app/lib/components/ui/data-table/data-table-search.component.ts +1 -0
  87. package/src/app/lib/components/ui/data-table/data-table-toolbar.component.ts +1 -0
  88. package/src/app/lib/components/ui/data-table/data-table-view-options.component.ts +1 -0
  89. package/src/app/lib/components/ui/data-table/data-table.component.ts +1 -1
  90. package/src/app/lib/components/ui/date-picker/date-picker.component.ts +1 -0
  91. package/src/app/lib/components/ui/dialog/dialog-close.component.ts +1 -0
  92. package/src/app/lib/components/ui/dialog/dialog-content.component.ts +32 -21
  93. package/src/app/lib/components/ui/dialog/dialog-description.component.ts +1 -0
  94. package/src/app/lib/components/ui/dialog/dialog-footer.component.ts +1 -0
  95. package/src/app/lib/components/ui/dialog/dialog-header.component.ts +1 -0
  96. package/src/app/lib/components/ui/dialog/dialog-title.component.ts +1 -0
  97. package/src/app/lib/components/ui/dialog/dialog-trigger.component.ts +1 -0
  98. package/src/app/lib/components/ui/dialog/dialog.component.ts +1 -0
  99. package/src/app/lib/components/ui/direction/direction-context.ts +9 -0
  100. package/src/app/lib/components/ui/direction/direction.component.ts +48 -0
  101. package/src/app/lib/components/ui/direction/index.ts +2 -0
  102. package/src/app/lib/components/ui/drawer/drawer-close.component.ts +1 -0
  103. package/src/app/lib/components/ui/drawer/drawer-content.component.ts +45 -0
  104. package/src/app/lib/components/ui/drawer/drawer-description.component.ts +1 -0
  105. package/src/app/lib/components/ui/drawer/drawer-footer.component.ts +1 -0
  106. package/src/app/lib/components/ui/drawer/drawer-header.component.ts +1 -0
  107. package/src/app/lib/components/ui/drawer/drawer-title.component.ts +1 -0
  108. package/src/app/lib/components/ui/drawer/drawer-trigger.component.ts +1 -0
  109. package/src/app/lib/components/ui/drawer/drawer.component.ts +4 -0
  110. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.component.ts +1 -0
  111. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-content.component.ts +3 -2
  112. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-group.component.ts +1 -0
  113. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-item.component.ts +1 -0
  114. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-label.component.ts +1 -0
  115. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.component.ts +1 -0
  116. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.component.ts +2 -0
  117. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-separator.component.ts +1 -0
  118. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.component.ts +1 -0
  119. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.component.ts +3 -0
  120. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.component.ts +30 -3
  121. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-sub.component.ts +4 -0
  122. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-trigger.component.ts +26 -0
  123. package/src/app/lib/components/ui/dropdown-menu/dropdown-menu.component.ts +1 -0
  124. package/src/app/lib/components/ui/empty/empty-action.component.ts +1 -0
  125. package/src/app/lib/components/ui/empty/empty-description.component.ts +1 -0
  126. package/src/app/lib/components/ui/empty/empty-icon.component.ts +2 -1
  127. package/src/app/lib/components/ui/empty/empty-title.component.ts +1 -0
  128. package/src/app/lib/components/ui/empty/empty.component.ts +1 -0
  129. package/src/app/lib/components/ui/form/form-description.component.ts +2 -2
  130. package/src/app/lib/components/ui/hover-card/hover-card-content.component.ts +109 -60
  131. package/src/app/lib/components/ui/hover-card/hover-card-context.ts +4 -2
  132. package/src/app/lib/components/ui/hover-card/hover-card-trigger.component.ts +6 -3
  133. package/src/app/lib/components/ui/hover-card/hover-card.component.ts +9 -3
  134. package/src/app/lib/components/ui/input-group/input-group-addon.component.ts +1 -0
  135. package/src/app/lib/components/ui/input-group/input-group-input.component.ts +1 -0
  136. package/src/app/lib/components/ui/input-group/input-group.component.ts +1 -0
  137. package/src/app/lib/components/ui/input-otp/input-otp-group.component.ts +1 -0
  138. package/src/app/lib/components/ui/input-otp/input-otp-separator.component.ts +1 -0
  139. package/src/app/lib/components/ui/input-otp/input-otp-slot.component.ts +1 -0
  140. package/src/app/lib/components/ui/input-otp/input-otp.component.ts +1 -0
  141. package/src/app/lib/components/ui/kbd/kbd.component.ts +1 -0
  142. package/src/app/lib/components/ui/menubar/menubar-checkbox-item.component.ts +1 -0
  143. package/src/app/lib/components/ui/menubar/menubar-content.component.ts +2 -1
  144. package/src/app/lib/components/ui/menubar/menubar-item.component.ts +1 -0
  145. package/src/app/lib/components/ui/menubar/menubar-label.component.ts +1 -0
  146. package/src/app/lib/components/ui/menubar/menubar-menu.component.ts +1 -0
  147. package/src/app/lib/components/ui/menubar/menubar-radio-group.component.ts +1 -0
  148. package/src/app/lib/components/ui/menubar/menubar-radio-item.component.ts +1 -0
  149. package/src/app/lib/components/ui/menubar/menubar-separator.component.ts +1 -0
  150. package/src/app/lib/components/ui/menubar/menubar-shortcut.component.ts +1 -0
  151. package/src/app/lib/components/ui/menubar/menubar-sub-content.component.ts +1 -0
  152. package/src/app/lib/components/ui/menubar/menubar-sub-trigger.component.ts +1 -0
  153. package/src/app/lib/components/ui/menubar/menubar-sub.component.ts +1 -0
  154. package/src/app/lib/components/ui/menubar/menubar-trigger.component.ts +1 -0
  155. package/src/app/lib/components/ui/menubar/menubar.component.ts +1 -0
  156. package/src/app/lib/components/ui/native-select/native-select.component.ts +1 -1
  157. package/src/app/lib/components/ui/navigation-menu/navigation-menu-content.component.ts +8 -1
  158. package/src/app/lib/components/ui/navigation-menu/navigation-menu-context.ts +14 -0
  159. package/src/app/lib/components/ui/navigation-menu/navigation-menu-indicator.component.ts +1 -0
  160. package/src/app/lib/components/ui/navigation-menu/navigation-menu-item.component.ts +10 -4
  161. package/src/app/lib/components/ui/navigation-menu/navigation-menu-link.component.ts +1 -0
  162. package/src/app/lib/components/ui/navigation-menu/navigation-menu-list.component.ts +1 -0
  163. package/src/app/lib/components/ui/navigation-menu/navigation-menu-trigger.component.ts +70 -2
  164. package/src/app/lib/components/ui/navigation-menu/navigation-menu-viewport.component.ts +1 -0
  165. package/src/app/lib/components/ui/navigation-menu/navigation-menu.component.ts +36 -4
  166. package/src/app/lib/components/ui/pagination/pagination-content.component.ts +1 -0
  167. package/src/app/lib/components/ui/pagination/pagination-ellipsis.component.ts +1 -0
  168. package/src/app/lib/components/ui/pagination/pagination-item.component.ts +1 -0
  169. package/src/app/lib/components/ui/pagination/pagination-link.component.ts +1 -0
  170. package/src/app/lib/components/ui/pagination/pagination-next.component.ts +1 -0
  171. package/src/app/lib/components/ui/pagination/pagination-previous.component.ts +1 -0
  172. package/src/app/lib/components/ui/pagination/pagination.component.ts +4 -1
  173. package/src/app/lib/components/ui/popover/popover-anchor.component.ts +1 -0
  174. package/src/app/lib/components/ui/popover/popover-content.component.ts +12 -0
  175. package/src/app/lib/components/ui/popover/popover-context.ts +2 -0
  176. package/src/app/lib/components/ui/popover/popover-trigger.component.ts +1 -0
  177. package/src/app/lib/components/ui/popover/popover.component.ts +5 -0
  178. package/src/app/lib/components/ui/progress/progress.component.ts +1 -2
  179. package/src/app/lib/components/ui/resizable/resizable-handle.component.ts +1 -0
  180. package/src/app/lib/components/ui/resizable/resizable-panel-group.component.ts +1 -0
  181. package/src/app/lib/components/ui/resizable/resizable-panel.component.ts +1 -0
  182. package/src/app/lib/components/ui/scroll-area/scroll-area.component.ts +8 -6
  183. package/src/app/lib/components/ui/scroll-area/scroll-bar.component.ts +1 -0
  184. package/src/app/lib/components/ui/segmented/segmented-item.component.ts +1 -0
  185. package/src/app/lib/components/ui/segmented/segmented.component.ts +1 -0
  186. package/src/app/lib/components/ui/select/select-content.component.ts +38 -17
  187. package/src/app/lib/components/ui/select/select-context.ts +10 -0
  188. package/src/app/lib/components/ui/select/select-item.component.ts +25 -7
  189. package/src/app/lib/components/ui/select/select-trigger.component.ts +6 -13
  190. package/src/app/lib/components/ui/select/select.component.ts +46 -0
  191. package/src/app/lib/components/ui/sheet/sheet-close.component.ts +1 -0
  192. package/src/app/lib/components/ui/sheet/sheet-content.component.ts +23 -5
  193. package/src/app/lib/components/ui/sheet/sheet-description.component.ts +1 -0
  194. package/src/app/lib/components/ui/sheet/sheet-footer.component.ts +1 -0
  195. package/src/app/lib/components/ui/sheet/sheet-header.component.ts +1 -0
  196. package/src/app/lib/components/ui/sheet/sheet-title.component.ts +1 -0
  197. package/src/app/lib/components/ui/sheet/sheet-trigger.component.ts +1 -0
  198. package/src/app/lib/components/ui/sheet/sheet.component.ts +4 -0
  199. package/src/app/lib/components/ui/sidebar/sidebar-content.component.ts +1 -0
  200. package/src/app/lib/components/ui/sidebar/sidebar-footer.component.ts +1 -0
  201. package/src/app/lib/components/ui/sidebar/sidebar-group-action.component.ts +1 -0
  202. package/src/app/lib/components/ui/sidebar/sidebar-group-content.component.ts +1 -0
  203. package/src/app/lib/components/ui/sidebar/sidebar-group-label.component.ts +1 -0
  204. package/src/app/lib/components/ui/sidebar/sidebar-group.component.ts +1 -0
  205. package/src/app/lib/components/ui/sidebar/sidebar-header.component.ts +1 -0
  206. package/src/app/lib/components/ui/sidebar/sidebar-input.component.ts +1 -0
  207. package/src/app/lib/components/ui/sidebar/sidebar-inset.component.ts +1 -0
  208. package/src/app/lib/components/ui/sidebar/sidebar-menu-action.component.ts +1 -0
  209. package/src/app/lib/components/ui/sidebar/sidebar-menu-badge.component.ts +1 -0
  210. package/src/app/lib/components/ui/sidebar/sidebar-menu-button.component.ts +1 -0
  211. package/src/app/lib/components/ui/sidebar/sidebar-menu-item.component.ts +1 -0
  212. package/src/app/lib/components/ui/sidebar/sidebar-menu-skeleton.component.ts +1 -0
  213. package/src/app/lib/components/ui/sidebar/sidebar-menu-sub-button.component.ts +1 -0
  214. package/src/app/lib/components/ui/sidebar/sidebar-menu-sub-item.component.ts +1 -0
  215. package/src/app/lib/components/ui/sidebar/sidebar-menu-sub.component.ts +1 -0
  216. package/src/app/lib/components/ui/sidebar/sidebar-menu.component.ts +1 -0
  217. package/src/app/lib/components/ui/sidebar/sidebar-provider.component.ts +1 -0
  218. package/src/app/lib/components/ui/sidebar/sidebar-rail.component.ts +1 -0
  219. package/src/app/lib/components/ui/sidebar/sidebar-separator.component.ts +1 -0
  220. package/src/app/lib/components/ui/sidebar/sidebar-trigger.component.ts +1 -0
  221. package/src/app/lib/components/ui/sidebar/sidebar.component.ts +1 -0
  222. package/src/app/lib/components/ui/slider/slider.component.ts +2 -2
  223. package/src/app/lib/components/ui/sonner/index.ts +2 -0
  224. package/src/app/lib/components/ui/sonner/sonner.component.ts +70 -0
  225. package/src/app/lib/components/ui/spinner/spinner.component.ts +1 -0
  226. package/src/app/lib/components/ui/switch/switch.component.ts +1 -14
  227. package/src/app/lib/components/ui/table/table-body.component.ts +1 -0
  228. package/src/app/lib/components/ui/table/table-caption.component.ts +1 -0
  229. package/src/app/lib/components/ui/table/table-cell.component.ts +1 -0
  230. package/src/app/lib/components/ui/table/table-footer.component.ts +1 -0
  231. package/src/app/lib/components/ui/table/table-head.component.ts +1 -0
  232. package/src/app/lib/components/ui/table/table-header.component.ts +1 -0
  233. package/src/app/lib/components/ui/table/table-row.component.ts +1 -0
  234. package/src/app/lib/components/ui/table/table.component.ts +1 -0
  235. package/src/app/lib/components/ui/tabs/tabs-content.component.ts +3 -6
  236. package/src/app/lib/components/ui/tabs/tabs-list.component.ts +19 -0
  237. package/src/app/lib/components/ui/tabs/tabs-trigger.component.ts +1 -1
  238. package/src/app/lib/components/ui/tabs/tabs.component.ts +1 -0
  239. package/src/app/lib/components/ui/toast/toast-action.component.ts +1 -0
  240. package/src/app/lib/components/ui/toast/toast-description.component.ts +1 -0
  241. package/src/app/lib/components/ui/toast/toast-title.component.ts +1 -0
  242. package/src/app/lib/components/ui/toast/toast.component.ts +1 -0
  243. package/src/app/lib/components/ui/toast/toaster.component.ts +1 -0
  244. package/src/app/lib/components/ui/toggle/toggle-variants.ts +1 -1
  245. package/src/app/lib/components/ui/toggle/toggle.component.ts +12 -6
  246. package/src/app/lib/components/ui/tooltip/tooltip-content.component.ts +142 -17
  247. package/src/app/lib/components/ui/tooltip/tooltip-context.ts +3 -1
  248. package/src/app/lib/components/ui/tooltip/tooltip-provider.component.ts +5 -1
  249. package/src/app/lib/components/ui/tooltip/tooltip-trigger.component.ts +6 -2
  250. package/src/app/lib/components/ui/tooltip/tooltip.component.ts +4 -1
  251. package/src/app/lib/components/ui/typography/typography-blockquote.component.ts +1 -0
  252. package/src/app/lib/components/ui/typography/typography-h1.component.ts +1 -0
  253. package/src/app/lib/components/ui/typography/typography-h2.component.ts +1 -0
  254. package/src/app/lib/components/ui/typography/typography-h3.component.ts +1 -0
  255. package/src/app/lib/components/ui/typography/typography-h4.component.ts +1 -0
  256. package/src/app/lib/components/ui/typography/typography-inline-code.component.ts +1 -0
  257. package/src/app/lib/components/ui/typography/typography-large.component.ts +1 -0
  258. package/src/app/lib/components/ui/typography/typography-lead.component.ts +1 -0
  259. package/src/app/lib/components/ui/typography/typography-list.component.ts +1 -0
  260. package/src/app/lib/components/ui/typography/typography-muted.component.ts +1 -0
  261. package/src/app/lib/components/ui/typography/typography-p.component.ts +1 -0
  262. package/src/app/lib/components/ui/typography/typography-small.component.ts +1 -0
@@ -1,12 +1,16 @@
1
- import { cn, Presence } from '@/lib/utils';
1
+ import { Align, cn, computePosition, getTransformOrigin, Presence, Side } from '@/lib/utils';
2
2
  import {
3
+ afterNextRender,
3
4
  ChangeDetectionStrategy,
4
5
  Component,
5
6
  computed,
7
+ effect,
6
8
  ElementRef,
7
9
  inject,
10
+ Injector,
8
11
  input,
9
12
  OnDestroy,
13
+ signal,
10
14
  } from '@angular/core';
11
15
  import { HOVER_CARD_CONTEXT, HoverCardAlign, HoverCardSide } from './hover-card-context';
12
16
 
@@ -38,31 +42,6 @@ export interface HoverCardContentProps {
38
42
  * HoverCardContent displays the preview content. It stays open when
39
43
  * hovered, allowing users to interact with the content.
40
44
  *
41
- * ## Features
42
- * - Stays open when content is hovered
43
- * - Configurable side and alignment
44
- * - Smooth animations
45
- * - Escape key to dismiss
46
- *
47
- * ## Accessibility
48
- * - `role="dialog"` on the content
49
- * - Focusable content items
50
- * - Escape returns focus to trigger
51
- *
52
- * @example Basic usage
53
- * ```html
54
- * <HoverCardContent>
55
- * <p>Preview content</p>
56
- * </HoverCardContent>
57
- * ```
58
- *
59
- * @example With positioning
60
- * ```html
61
- * <HoverCardContent side="right" align="start">
62
- * <p>Right-aligned content</p>
63
- * </HoverCardContent>
64
- * ```
65
- *
66
45
  * @data-attributes
67
46
  * - `data-state` - 'open' | 'closed'
68
47
  * - `data-side` - 'top' | 'right' | 'bottom' | 'left'
@@ -78,9 +57,10 @@ export interface HoverCardContentProps {
78
57
  [attr.aria-modal]="false"
79
58
  tabindex="-1"
80
59
  [class]="computedClass()"
60
+ [style]="positionStyles()"
81
61
  [attr.data-state]="state()"
82
- [attr.data-side]="side()"
83
- [attr.data-align]="align()"
62
+ [attr.data-side]="computedSide()"
63
+ [attr.data-align]="computedAlign()"
84
64
  data-slot="hover-card-content"
85
65
  (mouseenter)="onMouseEnter()"
86
66
  (mouseleave)="onMouseLeave()"
@@ -93,11 +73,31 @@ export interface HoverCardContentProps {
93
73
  </Presence>
94
74
  `,
95
75
  host: {
76
+ 'attr.data-slot': '"hover-card-content"',
96
77
  class: 'contents',
97
78
  },
98
79
  changeDetection: ChangeDetectionStrategy.OnPush,
99
80
  })
100
81
  export class HoverCardContent implements OnDestroy {
82
+ constructor() {
83
+ effect(() => {
84
+ const isOpen = this.context.open();
85
+ if (isOpen) {
86
+ this.isPositioned.set(false);
87
+ afterNextRender(
88
+ () => {
89
+ this.schedulePositionUpdate();
90
+ },
91
+ { injector: this._injector },
92
+ );
93
+ } else {
94
+ this.cancelScheduledPositionUpdate();
95
+ this.isPositioned.set(false);
96
+ this.positionStyles.set({ position: 'fixed', top: '-9999px', left: '-9999px' });
97
+ }
98
+ });
99
+ }
100
+
101
101
  /** The preferred side of the trigger to render against */
102
102
  readonly side = input<HoverCardSide>('bottom');
103
103
  /** The distance in pixels from the trigger */
@@ -108,80 +108,129 @@ export class HoverCardContent implements OnDestroy {
108
108
  readonly class = input<string>('');
109
109
 
110
110
  private readonly _elementRef = inject(ElementRef<HTMLElement>);
111
+ private readonly _injector = inject(Injector);
111
112
 
112
113
  protected readonly context = inject(HOVER_CARD_CONTEXT);
113
114
 
114
- protected readonly computedClass = computed(() => {
115
- const sideClasses = {
116
- top: 'bottom-full mb-2',
117
- bottom: 'top-full mt-2',
118
- left: 'right-full mr-2',
119
- right: 'left-full ml-2',
120
- };
121
-
122
- const alignClasses = {
123
- start: 'left-0',
124
- center: 'left-1/2 -translate-x-1/2',
125
- end: 'right-0',
126
- };
127
-
128
- return cn(
129
- 'absolute z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none',
115
+ protected readonly computedClass = computed(() =>
116
+ cn(
117
+ 'fixed z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none',
130
118
  'data-[state=open]:animate-in data-[state=closed]:animate-out',
131
119
  'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
132
120
  'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
133
121
  'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
134
122
  'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
135
- sideClasses[this.side()],
136
- this.side() === 'top' || this.side() === 'bottom' ? alignClasses[this.align()] : '',
123
+ !this.isPositioned() && 'pointer-events-none opacity-0',
137
124
  this.class(),
138
- );
125
+ ),
126
+ );
127
+
128
+ protected readonly positionStyles = signal<Record<string, string>>({
129
+ position: 'fixed',
130
+ top: '-9999px',
131
+ left: '-9999px',
139
132
  });
133
+ protected readonly isPositioned = signal(false);
134
+ protected readonly computedSide = signal<Side>('bottom');
135
+ protected readonly computedAlign = signal<Align>('center');
140
136
 
141
- private closeTimeout: ReturnType<typeof setTimeout> | null = null;
142
137
  /** Current state: open or closed */
143
138
  protected readonly state = computed<HoverCardContentState>(() =>
144
139
  this.context.open() ? 'open' : 'closed',
145
140
  );
146
141
 
142
+ private closeTimeout: ReturnType<typeof setTimeout> | null = null;
143
+ private positionFrameId: number | null = null;
144
+
147
145
  ngOnDestroy(): void {
148
- this.clearTimeout();
146
+ this.clearCloseTimeout();
147
+ this.cancelScheduledPositionUpdate();
149
148
  }
150
149
 
151
150
  onMouseEnter(): void {
152
- this.clearTimeout();
151
+ this.clearCloseTimeout();
153
152
  }
154
153
  onMouseLeave(): void {
155
154
  this.closeTimeout = setTimeout(() => {
156
155
  this.context.setOpen(false);
157
- }, this.context.closeDelay);
156
+ }, this.context.closeDelay());
158
157
  }
159
158
  onFocusIn(): void {
160
- this.clearTimeout();
159
+ this.clearCloseTimeout();
161
160
  }
162
161
  onFocusOut(event: FocusEvent): void {
163
162
  const relatedTarget = event.relatedTarget as HTMLElement | null;
164
- const trigger = this._elementRef.nativeElement.parentElement?.querySelector('[data-state]');
163
+ const trigger = this.context.triggerRef();
165
164
 
166
- // Check if focus moved to trigger or stayed within content
167
165
  if (relatedTarget && (trigger === relatedTarget || trigger?.contains(relatedTarget))) {
168
166
  return;
169
167
  }
170
168
 
171
169
  this.closeTimeout = setTimeout(() => {
172
170
  this.context.setOpen(false);
173
- }, this.context.closeDelay);
171
+ }, this.context.closeDelay());
174
172
  }
175
173
  onEscape(): void {
176
174
  this.context.setOpen(false);
177
- // Return focus to trigger
178
- const trigger = this._elementRef.nativeElement.parentElement?.querySelector(
179
- '[data-state]',
175
+ this.context.triggerRef()?.focus();
176
+ }
177
+
178
+ private schedulePositionUpdate(): void {
179
+ this.cancelScheduledPositionUpdate();
180
+ this.positionFrameId = requestAnimationFrame(() => {
181
+ this.updatePosition();
182
+ this.positionFrameId = requestAnimationFrame(() => {
183
+ this.updatePosition();
184
+ this.positionFrameId = null;
185
+ });
186
+ });
187
+ }
188
+ private cancelScheduledPositionUpdate(): void {
189
+ if (this.positionFrameId !== null) {
190
+ cancelAnimationFrame(this.positionFrameId);
191
+ this.positionFrameId = null;
192
+ }
193
+ }
194
+ private updatePosition(): void {
195
+ const triggerElement = this.context.triggerRef();
196
+ const contentElement = this._elementRef.nativeElement.querySelector(
197
+ '[role="dialog"]',
180
198
  ) as HTMLElement;
181
- trigger?.focus();
199
+
200
+ if (!triggerElement || !contentElement) return;
201
+
202
+ const triggerRect = triggerElement.getBoundingClientRect();
203
+ const contentRect = contentElement.getBoundingClientRect();
204
+ const overlayWidth = Math.round(contentRect.width || 256);
205
+ const overlayHeight = Math.round(contentRect.height || 100);
206
+
207
+ const result = computePosition(
208
+ triggerRect,
209
+ { width: overlayWidth, height: overlayHeight },
210
+ {
211
+ side: this.side(),
212
+ align: this.align(),
213
+ sideOffset: this.sideOffset(),
214
+ alignOffset: 0,
215
+ avoidCollisions: true,
216
+ collisionPadding: 8,
217
+ },
218
+ );
219
+
220
+ this.computedSide.set(result.side);
221
+ this.computedAlign.set(result.align);
222
+
223
+ const transformOrigin = getTransformOrigin(result.side, result.align);
224
+ this.positionStyles.set({
225
+ position: 'fixed',
226
+ top: result.styles.top || '',
227
+ left: result.styles.left || '',
228
+ '--radix-hover-card-content-transform-origin': transformOrigin,
229
+ });
230
+ this.isPositioned.set(true);
182
231
  }
183
232
 
184
- private clearTimeout(): void {
233
+ private clearCloseTimeout(): void {
185
234
  if (this.closeTimeout) {
186
235
  clearTimeout(this.closeTimeout);
187
236
  this.closeTimeout = null;
@@ -9,9 +9,11 @@ export interface HoverCardContextValue {
9
9
  /** Set open state */
10
10
  setOpen: (open: boolean) => void;
11
11
  /** The duration from when the pointer enters the trigger until the hover card opens (ms) */
12
- openDelay: number;
12
+ openDelay: () => number;
13
13
  /** The duration from when the pointer leaves the trigger/content until the hover card closes (ms) */
14
- closeDelay: number;
14
+ closeDelay: () => number;
15
+ /** Reference to the trigger element for fixed positioning */
16
+ triggerRef: WritableSignal<HTMLElement | null>;
15
17
  }
16
18
 
17
19
  export const HOVER_CARD_CONTEXT = new InjectionToken<HoverCardContextValue>('HOVER_CARD_CONTEXT');
@@ -64,6 +64,7 @@ export interface HoverCardTriggerProps {
64
64
  selector: 'HoverCardTrigger',
65
65
  template: `<ng-content />`,
66
66
  host: {
67
+ 'attr.data-slot': '"hover-card-trigger"',
67
68
  tabindex: '0',
68
69
  role: 'button',
69
70
  '(mouseenter)': 'onMouseEnter()',
@@ -95,18 +96,20 @@ export class HoverCardTrigger implements OnDestroy {
95
96
  }
96
97
 
97
98
  onMouseEnter(): void {
99
+ this.context.triggerRef.set(this._elementRef.nativeElement);
98
100
  this.clearTimeouts();
99
101
  this.openTimeout = setTimeout(() => {
100
102
  this.context.setOpen(true);
101
- }, this.context.openDelay);
103
+ }, this.context.openDelay());
102
104
  }
103
105
  onMouseLeave(): void {
104
106
  this.clearTimeouts();
105
107
  this.closeTimeout = setTimeout(() => {
106
108
  this.context.setOpen(false);
107
- }, this.context.closeDelay);
109
+ }, this.context.closeDelay());
108
110
  }
109
111
  onFocus(): void {
112
+ this.context.triggerRef.set(this._elementRef.nativeElement);
110
113
  this.clearTimeouts();
111
114
  // Open immediately on focus for keyboard users
112
115
  this.context.setOpen(true);
@@ -126,7 +129,7 @@ export class HoverCardTrigger implements OnDestroy {
126
129
  this.clearTimeouts();
127
130
  this.closeTimeout = setTimeout(() => {
128
131
  this.context.setOpen(false);
129
- }, this.context.closeDelay);
132
+ }, this.context.closeDelay());
130
133
  }
131
134
  onKeyDown(event: Event): void {
132
135
  event.preventDefault();
@@ -5,6 +5,8 @@ import {
5
5
  input,
6
6
  output,
7
7
  signal,
8
+ Signal,
9
+ WritableSignal,
8
10
  } from '@angular/core';
9
11
  import { HOVER_CARD_CONTEXT, type HoverCardContextValue } from './hover-card-context';
10
12
 
@@ -92,7 +94,8 @@ export interface HoverCardProps {
92
94
  selector: 'HoverCard',
93
95
  template: `<ng-content />`,
94
96
  host: {
95
- class: 'relative inline-block',
97
+ 'attr.data-slot': '"hover-card"',
98
+ class: 'inline-block',
96
99
  },
97
100
  providers: [
98
101
  {
@@ -119,10 +122,13 @@ export class HoverCard implements HoverCardContextValue {
119
122
 
120
123
  readonly open = signal(false);
121
124
 
125
+ /** Reference to the trigger element for fixed positioning */
126
+ readonly triggerRef: WritableSignal<HTMLElement | null> = signal<HTMLElement | null>(null);
127
+
122
128
  /** The duration from when the pointer enters the trigger until the hover card opens (ms) */
123
- readonly openDelay = 700;
129
+ readonly openDelay = input<number>(700);
124
130
  /** The duration from when the pointer leaves the trigger/content until the hover card closes (ms) */
125
- readonly closeDelay = 300;
131
+ readonly closeDelay = input<number>(300);
126
132
 
127
133
  setOpen(open: boolean): void {
128
134
  if (this.controlledOpen() === undefined) {
@@ -14,6 +14,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
14
14
  selector: 'InputGroupAddon',
15
15
  template: `<ng-content />`,
16
16
  host: {
17
+ 'attr.data-slot': '"input-group-addon"',
17
18
  '[class]': 'computedClass()',
18
19
  },
19
20
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -10,6 +10,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
10
10
  selector: 'InputGroupInput',
11
11
  template: ``,
12
12
  host: {
13
+ 'attr.data-slot': '"input-group-input"',
13
14
  '[class]': 'computedClass()',
14
15
  },
15
16
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -30,6 +30,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
30
30
  selector: 'InputGroup',
31
31
  template: `<ng-content />`,
32
32
  host: {
33
+ 'attr.data-slot': '"input-group"',
33
34
  '[class]': 'computedClass()',
34
35
  },
35
36
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'InputOTPGroup',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"input-otp-group"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -15,6 +15,7 @@ import { LucideAngularModule, Minus } from 'lucide-angular';
15
15
  </div>
16
16
  `,
17
17
  host: {
18
+ 'attr.data-slot': '"input-otp-separator"',
18
19
  '[class]': 'computedClass()',
19
20
  },
20
21
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -19,6 +19,7 @@ import { INPUT_OTP_CONTEXT } from './input-otp-context';
19
19
  }
20
20
  `,
21
21
  host: {
22
+ 'attr.data-slot': '"input-otp-slot"',
22
23
  '[class]': 'computedClass()',
23
24
  '[attr.data-active]': 'isActive() ? "" : null',
24
25
  '[attr.aria-label]': 'ariaLabel()',
@@ -130,6 +130,7 @@ export interface InputOTPProps {
130
130
  },
131
131
  ],
132
132
  host: {
133
+ 'attr.data-slot': '"input-otp"',
133
134
  class: 'contents',
134
135
  '[attr.data-disabled]': 'disabled() ? "" : null',
135
136
  },
@@ -22,6 +22,7 @@ import { kbdVariants, type KbdVariants } from './kbd-variants';
22
22
  selector: 'Kbd',
23
23
  template: `<ng-content />`,
24
24
  host: {
25
+ 'attr.data-slot': '"kbd"',
25
26
  '[class]': 'computedClass()',
26
27
  },
27
28
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -27,6 +27,7 @@ import { MENUBAR_CONTEXT, MENUBAR_MENU_CONTEXT } from './menubar-context';
27
27
  <ng-content />
28
28
  `,
29
29
  host: {
30
+ 'attr.data-slot': '"menubar-checkbox-item"',
30
31
  '[class]': 'computedClass()',
31
32
  '[attr.role]': '"menuitemcheckbox"',
32
33
  '[attr.aria-checked]': 'checked()',
@@ -35,6 +35,7 @@ import { MENUBAR_CONTEXT, MENUBAR_MENU_CONTEXT } from './menubar-context';
35
35
  </Presence>
36
36
  `,
37
37
  host: {
38
+ 'attr.data-slot': '"menubar-content"',
38
39
  class: 'contents',
39
40
  '(document:click)': 'onDocumentClick($event)',
40
41
  '(document:keydown.escape)': 'onEscapeKey()',
@@ -113,7 +114,7 @@ export class MenubarContent implements OnDestroy {
113
114
  if (content) {
114
115
  this.menuItems = Array.from(
115
116
  content.querySelectorAll(
116
- '[role="menuitem"]:not([aria-disabled="true"]):not([data-disabled])',
117
+ '[role="menuitem"]:not([data-disabled=""]), [role="menuitemcheckbox"]:not([data-disabled=""]), [role="menuitemradio"]:not([data-disabled=""])',
117
118
  ),
118
119
  );
119
120
  }
@@ -11,6 +11,7 @@ import { MENUBAR_CONTEXT, MENUBAR_MENU_CONTEXT } from './menubar-context';
11
11
  selector: 'MenubarItem',
12
12
  template: `<ng-content />`,
13
13
  host: {
14
+ 'attr.data-slot': '"menubar-item"',
14
15
  '[class]': 'computedClass()',
15
16
  '[attr.role]': '"menuitem"',
16
17
  '[attr.tabindex]': 'disabled() ? -1 : -1',
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'MenubarLabel',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"menubar-label"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -33,6 +33,7 @@ let menuIdCounter = 0;
33
33
  },
34
34
  ],
35
35
  host: {
36
+ 'attr.data-slot': '"menubar-menu"',
36
37
  class: 'relative',
37
38
  },
38
39
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -35,6 +35,7 @@ export const MENUBAR_RADIO_GROUP_CONTEXT = new InjectionToken<MenubarRadioGroupC
35
35
  },
36
36
  ],
37
37
  host: {
38
+ 'attr.data-slot': '"menubar-radio-group"',
38
39
  '[attr.role]': '"group"',
39
40
  },
40
41
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -19,6 +19,7 @@ import { MENUBAR_RADIO_GROUP_CONTEXT } from './menubar-radio-group.component';
19
19
  <ng-content />
20
20
  `,
21
21
  host: {
22
+ 'attr.data-slot': '"menubar-radio-item"',
22
23
  '[class]': 'computedClass()',
23
24
  '[attr.role]': '"menuitemradio"',
24
25
  '[attr.aria-checked]': 'isSelected()',
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'MenubarSeparator',
10
10
  template: ``,
11
11
  host: {
12
+ 'attr.data-slot': '"menubar-separator"',
12
13
  '[class]': 'computedClass()',
13
14
  '[attr.role]': '"separator"',
14
15
  },
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'MenubarShortcut',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"menubar-shortcut"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -21,6 +21,7 @@ import { MENUBAR_SUB_CONTEXT } from './menubar-sub.component';
21
21
  </Presence>
22
22
  `,
23
23
  host: {
24
+ 'attr.data-slot': '"menubar-sub-content"',
24
25
  class: 'contents',
25
26
  '(mouseenter)': 'onMouseEnter()',
26
27
  '(mouseleave)': 'onMouseLeave()',
@@ -15,6 +15,7 @@ import { MENUBAR_SUB_CONTEXT } from './menubar-sub.component';
15
15
  <lucide-icon [img]="ChevronRightIcon" class="ml-auto h-4 w-4" />
16
16
  `,
17
17
  host: {
18
+ 'attr.data-slot': '"menubar-sub-trigger"',
18
19
  '[class]': 'computedClass()',
19
20
  '[attr.data-state]': 'subContext.open() ? "open" : "closed"',
20
21
  '(mouseenter)': 'onMouseEnter()',
@@ -28,6 +28,7 @@ export const MENUBAR_SUB_CONTEXT = new InjectionToken<MenubarSubContext>('MENUBA
28
28
  },
29
29
  ],
30
30
  host: {
31
+ 'attr.data-slot': '"menubar-sub"',
31
32
  class: 'relative',
32
33
  },
33
34
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -19,6 +19,7 @@ import { MENUBAR_CONTEXT, MENUBAR_MENU_CONTEXT } from './menubar-context';
19
19
  selector: 'MenubarTrigger',
20
20
  template: `<ng-content />`,
21
21
  host: {
22
+ 'attr.data-slot': '"menubar-trigger"',
22
23
  '[class]': 'computedClass()',
23
24
  '[attr.role]': '"menuitem"',
24
25
  '[attr.tabindex]': 'isFocused() ? 0 : -1',
@@ -131,6 +131,7 @@ export interface MenubarProps {
131
131
  },
132
132
  ],
133
133
  host: {
134
+ 'attr.data-slot': '"menubar"',
134
135
  '[class]': 'computedClass()',
135
136
  role: 'menubar',
136
137
  'aria-orientation': 'horizontal',
@@ -42,7 +42,7 @@ import { nativeSelectVariants, type NativeSelectVariants } from './native-select
42
42
  template: `<ng-content />`,
43
43
  host: {
44
44
  '[class]': 'computedClass()',
45
- 'data-slot': 'native-select',
45
+ 'attr.data-slot': '"native-select"',
46
46
  },
47
47
  changeDetection: ChangeDetectionStrategy.OnPush,
48
48
  })
@@ -18,12 +18,19 @@ import { NAVIGATION_MENU_CONTEXT, NAVIGATION_MENU_ITEM_CONTEXT } from './navigat
18
18
  imports: [Presence],
19
19
  template: `
20
20
  <Presence [present]="itemContext.open()">
21
- <div [class]="computedClass()" [attr.data-state]="itemContext.open() ? 'open' : 'closed'">
21
+ <div
22
+ [class]="computedClass()"
23
+ [attr.id]="itemContext.contentId"
24
+ [attr.data-state]="itemContext.open() ? 'open' : 'closed'"
25
+ [attr.aria-labelledby]="itemContext.triggerId"
26
+ role="region"
27
+ >
22
28
  <ng-content />
23
29
  </div>
24
30
  </Presence>
25
31
  `,
26
32
  host: {
33
+ 'attr.data-slot': '"navigation-menu-content"',
27
34
  class: 'contents',
28
35
  '(document:click)': 'onDocumentClick($event)',
29
36
  '(document:keydown.escape)': 'onEscapeKey()',
@@ -22,6 +22,16 @@ export interface NavigationMenuContextValue {
22
22
  activeItem: WritableSignal<string | null>;
23
23
  /** Layout orientation of the menu */
24
24
  orientation: WritableSignal<NavigationMenuOrientation>;
25
+ /** Ordered list of registered trigger element IDs */
26
+ triggerIds: WritableSignal<string[]>;
27
+ /** Register a trigger ID (called by NavigationMenuTrigger on init) */
28
+ registerTrigger: (triggerId: string) => void;
29
+ /** Unregister a trigger ID (called by NavigationMenuTrigger on destroy) */
30
+ unregisterTrigger: (triggerId: string) => void;
31
+ /** Move DOM focus to the next trigger in document order */
32
+ focusNextTrigger: (currentTriggerId: string) => void;
33
+ /** Move DOM focus to the previous trigger in document order */
34
+ focusPreviousTrigger: (currentTriggerId: string) => void;
25
35
  }
26
36
 
27
37
  export const NAVIGATION_MENU_CONTEXT = new InjectionToken<NavigationMenuContextValue>(
@@ -38,6 +48,10 @@ export const NAVIGATION_MENU_CONTEXT = new InjectionToken<NavigationMenuContextV
38
48
  export interface NavigationMenuItemContextValue {
39
49
  /** Unique identifier for this item */
40
50
  itemId: string;
51
+ /** DOM id for the trigger element (used for aria-controls / aria-labelledby) */
52
+ triggerId: string;
53
+ /** DOM id for the content element (used for aria-controls) */
54
+ contentId: string;
41
55
  /** Whether this item's content is open */
42
56
  open: WritableSignal<boolean>;
43
57
  }
@@ -11,6 +11,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
11
11
  <div class="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md"></div>
12
12
  `,
13
13
  host: {
14
+ 'attr.data-slot': '"navigation-menu-indicator"',
14
15
  '[class]': 'computedClass()',
15
16
  },
16
17
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -17,13 +17,19 @@ let itemIdCounter = 0;
17
17
  providers: [
18
18
  {
19
19
  provide: NAVIGATION_MENU_ITEM_CONTEXT,
20
- useFactory: (): NavigationMenuItemContextValue => ({
21
- itemId: `nav-item-${itemIdCounter++}`,
22
- open: signal(false),
23
- }),
20
+ useFactory: (): NavigationMenuItemContextValue => {
21
+ const id = itemIdCounter++;
22
+ return {
23
+ itemId: `nav-item-${id}`,
24
+ triggerId: `nav-trigger-${id}`,
25
+ contentId: `nav-content-${id}`,
26
+ open: signal(false),
27
+ };
28
+ },
24
29
  },
25
30
  ],
26
31
  host: {
32
+ 'attr.data-slot': '"navigation-menu-item"',
27
33
  class: 'relative',
28
34
  },
29
35
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -12,6 +12,7 @@ import { navigationMenuTriggerStyle } from './navigation-menu-trigger-style';
12
12
  selector: 'NavigationMenuLink',
13
13
  template: `<ng-content />`,
14
14
  host: {
15
+ 'attr.data-slot': '"navigation-menu-link"',
15
16
  '[class]': 'computedClass()',
16
17
  '[attr.aria-current]': 'ariaCurrent()',
17
18
  '[attr.data-active]': 'active() || null',