@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
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'NavigationMenuList',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"navigation-menu-list"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -1,5 +1,13 @@
1
1
  import { cn } from '@/lib/utils';
2
- import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';
2
+ import {
3
+ ChangeDetectionStrategy,
4
+ Component,
5
+ computed,
6
+ inject,
7
+ input,
8
+ OnDestroy,
9
+ OnInit,
10
+ } from '@angular/core';
3
11
  import { ChevronDown, LucideAngularModule } from 'lucide-angular';
4
12
  import { NAVIGATION_MENU_CONTEXT, NAVIGATION_MENU_ITEM_CONTEXT } from './navigation-menu-context';
5
13
  import { navigationMenuTriggerStyle } from './navigation-menu-trigger-style';
@@ -20,14 +28,21 @@ import { navigationMenuTriggerStyle } from './navigation-menu-trigger-style';
20
28
  />
21
29
  `,
22
30
  host: {
31
+ 'attr.data-slot': '"navigation-menu-trigger"',
23
32
  '[class]': 'computedClass()',
33
+ '[attr.id]': 'itemContext.triggerId',
24
34
  '[attr.data-state]': 'itemContext.open() ? "open" : "closed"',
35
+ '[attr.role]': '"button"',
36
+ '[attr.aria-expanded]': 'itemContext.open()',
37
+ '[attr.aria-haspopup]': '"menu"',
38
+ '[attr.aria-controls]': 'itemContext.contentId',
25
39
  '(click)': 'toggle()',
26
40
  '(mouseenter)': 'onMouseEnter()',
41
+ '(keydown)': 'onKeyDown($event)',
27
42
  },
28
43
  changeDetection: ChangeDetectionStrategy.OnPush,
29
44
  })
30
- export class NavigationMenuTrigger {
45
+ export class NavigationMenuTrigger implements OnInit, OnDestroy {
31
46
  /** Additional CSS classes */
32
47
  readonly class = input<string>('');
33
48
 
@@ -40,6 +55,14 @@ export class NavigationMenuTrigger {
40
55
 
41
56
  protected readonly ChevronDownIcon = ChevronDown;
42
57
 
58
+ ngOnInit(): void {
59
+ this.context.registerTrigger(this.itemContext.triggerId);
60
+ }
61
+
62
+ ngOnDestroy(): void {
63
+ this.context.unregisterTrigger(this.itemContext.triggerId);
64
+ }
65
+
43
66
  protected toggle(): void {
44
67
  this.itemContext.open.update((v) => !v);
45
68
  if (this.itemContext.open()) {
@@ -48,6 +71,7 @@ export class NavigationMenuTrigger {
48
71
  this.context.activeItem.set(null);
49
72
  }
50
73
  }
74
+
51
75
  protected onMouseEnter(): void {
52
76
  const activeItem = this.context.activeItem();
53
77
  if (activeItem && activeItem !== this.itemContext.itemId) {
@@ -55,4 +79,48 @@ export class NavigationMenuTrigger {
55
79
  this.itemContext.open.set(true);
56
80
  }
57
81
  }
82
+
83
+ protected onKeyDown(event: KeyboardEvent): void {
84
+ switch (event.key) {
85
+ case 'Enter':
86
+ case ' ':
87
+ event.preventDefault();
88
+ this.toggle();
89
+ if (this.itemContext.open()) {
90
+ this.focusFirstContentItem();
91
+ }
92
+ break;
93
+ case 'ArrowDown':
94
+ event.preventDefault();
95
+ if (!this.itemContext.open()) {
96
+ this.itemContext.open.set(true);
97
+ this.context.activeItem.set(this.itemContext.itemId);
98
+ }
99
+ this.focusFirstContentItem();
100
+ break;
101
+ case 'ArrowRight':
102
+ event.preventDefault();
103
+ this.context.focusNextTrigger(this.itemContext.triggerId);
104
+ break;
105
+ case 'ArrowLeft':
106
+ event.preventDefault();
107
+ this.context.focusPreviousTrigger(this.itemContext.triggerId);
108
+ break;
109
+ case 'Escape':
110
+ if (this.itemContext.open()) {
111
+ this.itemContext.open.set(false);
112
+ this.context.activeItem.set(null);
113
+ document.getElementById(this.itemContext.triggerId)?.focus();
114
+ }
115
+ break;
116
+ }
117
+ }
118
+
119
+ private focusFirstContentItem(): void {
120
+ setTimeout(() => {
121
+ const content = document.getElementById(this.itemContext.contentId);
122
+ const focusable = content?.querySelector<HTMLElement>('a, button, [tabindex]');
123
+ focusable?.focus();
124
+ }, 10);
125
+ }
58
126
  }
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'NavigationMenuViewport',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"navigation-menu-viewport"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -7,6 +7,37 @@ import {
7
7
  } from './navigation-menu-context';
8
8
  import { NavigationMenuViewport } from './navigation-menu-viewport.component';
9
9
 
10
+ function createNavigationMenuContext(): NavigationMenuContextValue {
11
+ const triggerIds = signal<string[]>([]);
12
+ return {
13
+ activeItem: signal(null),
14
+ orientation: signal('horizontal'),
15
+ triggerIds,
16
+ registerTrigger: (triggerId: string) => {
17
+ triggerIds.update((ids) => [...ids, triggerId]);
18
+ },
19
+ unregisterTrigger: (triggerId: string) => {
20
+ triggerIds.update((ids) => ids.filter((id) => id !== triggerId));
21
+ },
22
+ focusNextTrigger: (currentTriggerId: string) => {
23
+ const ids = triggerIds();
24
+ const idx = ids.indexOf(currentTriggerId);
25
+ const nextId = idx < ids.length - 1 ? ids[idx + 1] : ids[0];
26
+ if (nextId) {
27
+ document.getElementById(nextId)?.focus();
28
+ }
29
+ },
30
+ focusPreviousTrigger: (currentTriggerId: string) => {
31
+ const ids = triggerIds();
32
+ const idx = ids.indexOf(currentTriggerId);
33
+ const prevId = idx > 0 ? ids[idx - 1] : ids[ids.length - 1];
34
+ if (prevId) {
35
+ document.getElementById(prevId)?.focus();
36
+ }
37
+ },
38
+ };
39
+ }
40
+
10
41
  /**
11
42
  * Props for the NavigationMenu component
12
43
  */
@@ -114,13 +145,14 @@ export interface NavigationMenuProps {
114
145
  <NavigationMenuViewport />
115
146
  </div>
116
147
  `,
148
+ host: {
149
+ 'attr.data-slot': '"navigation-menu"',
150
+ style: 'display: contents',
151
+ },
117
152
  providers: [
118
153
  {
119
154
  provide: NAVIGATION_MENU_CONTEXT,
120
- useFactory: (): NavigationMenuContextValue => ({
121
- activeItem: signal(null),
122
- orientation: signal('horizontal'),
123
- }),
155
+ useFactory: createNavigationMenuContext,
124
156
  },
125
157
  ],
126
158
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'PaginationContent',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"pagination-content"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -27,6 +27,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
27
27
  <span class="sr-only">More pages</span>
28
28
  `,
29
29
  host: {
30
+ 'attr.data-slot': '"pagination-ellipsis"',
30
31
  '[class]': 'computedClass()',
31
32
  '[attr.aria-hidden]': '"true"',
32
33
  },
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'PaginationItem',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"pagination-item"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -10,6 +10,7 @@ import { buttonVariants } from '../button/button-variants';
10
10
  selector: 'PaginationLink',
11
11
  template: `<ng-content />`,
12
12
  host: {
13
+ 'attr.data-slot': '"pagination-link"',
13
14
  '[class]': 'computedClass()',
14
15
  '[attr.href]': 'href()',
15
16
  '[attr.aria-current]': 'isActive() ? "page" : null',
@@ -26,6 +26,7 @@ import { buttonVariants } from '../button/button-variants';
26
26
  </svg>
27
27
  `,
28
28
  host: {
29
+ 'attr.data-slot': '"pagination-next"',
29
30
  '[class]': 'computedClass()',
30
31
  '[attr.href]': 'href()',
31
32
  '[attr.aria-label]': '"Go to next page"',
@@ -26,6 +26,7 @@ import { buttonVariants } from '../button/button-variants';
26
26
  <span>Previous</span>
27
27
  `,
28
28
  host: {
29
+ 'attr.data-slot': '"pagination-previous"',
29
30
  '[class]': 'computedClass()',
30
31
  '[attr.href]': 'href()',
31
32
  '[attr.aria-label]': '"Go to previous page"',
@@ -27,13 +27,16 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
27
27
  selector: 'Pagination',
28
28
  template: `<ng-content />`,
29
29
  host: {
30
+ 'attr.data-slot': '"pagination"',
30
31
  role: 'navigation',
31
- '[attr.aria-label]': '"pagination"',
32
+ '[attr.aria-label]': 'ariaLabel()',
32
33
  '[class]': 'computedClass()',
33
34
  },
34
35
  changeDetection: ChangeDetectionStrategy.OnPush,
35
36
  })
36
37
  export class Pagination {
38
+ /** Accessible label for the navigation landmark */
39
+ readonly ariaLabel = input<string>('Pagination');
37
40
  /** Additional CSS classes */
38
41
  readonly class = input<string>('');
39
42
 
@@ -42,6 +42,7 @@ export interface PopoverAnchorProps {
42
42
  selector: 'PopoverAnchor',
43
43
  template: `<ng-content />`,
44
44
  host: {
45
+ 'attr.data-slot': '"popover-anchor"',
45
46
  class: 'inline-block',
46
47
  },
47
48
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -100,6 +100,7 @@ export interface PopoverContentProps {
100
100
  [attr.data-align]="computedAlign()"
101
101
  [style]="mergedStyles()"
102
102
  role="dialog"
103
+ tabindex="-1"
103
104
  [attr.aria-modal]="context.modal() || null"
104
105
  >
105
106
  <ng-content />
@@ -107,6 +108,7 @@ export interface PopoverContentProps {
107
108
  </Presence>
108
109
  `,
109
110
  host: {
111
+ 'attr.data-slot': '"popover-content"',
110
112
  class: 'contents',
111
113
  '(document:click)': 'onDocumentClick($event)',
112
114
  '(document:keydown.escape)': 'onEscapeKey()',
@@ -276,5 +278,15 @@ export class PopoverContent {
276
278
  '--radix-popover-content-transform-origin': transformOrigin,
277
279
  });
278
280
  this.isPositioned.set(true);
281
+
282
+ // Move focus into the popover for keyboard accessibility
283
+ setTimeout(() => {
284
+ const dialog = this._elementRef.nativeElement.querySelector('[role="dialog"]') as HTMLElement;
285
+ if (!dialog) return;
286
+ const firstFocusable = dialog.querySelector<HTMLElement>(
287
+ 'button:not([disabled]):not([data-disabled=""]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',
288
+ );
289
+ (firstFocusable ?? dialog).focus({ preventScroll: true });
290
+ }, 0);
279
291
  }
280
292
  }
@@ -16,6 +16,8 @@ export interface PopoverContextValue {
16
16
  triggerRef?: Signal<HTMLElement | null>;
17
17
  /** Set the trigger element reference */
18
18
  setTriggerRef?: (element: HTMLElement | null) => void;
19
+ /** Unique ID for aria-controls relationship */
20
+ contentId: string;
19
21
  }
20
22
 
21
23
  export const POPOVER_CONTEXT = new InjectionToken<PopoverContextValue>('POPOVER_CONTEXT');
@@ -59,6 +59,7 @@ export interface PopoverTriggerProps {
59
59
  selector: 'PopoverTrigger',
60
60
  template: `<ng-content />`,
61
61
  host: {
62
+ 'attr.data-slot': '"popover-trigger"',
62
63
  '(click)': 'onClick($event)',
63
64
  '[attr.aria-expanded]': 'context.open()',
64
65
  '[attr.aria-haspopup]': '"dialog"',
@@ -10,6 +10,8 @@ import { POPOVER_CONTEXT, type PopoverContextValue } from './popover-context';
10
10
 
11
11
  export type PopoverState = 'open' | 'closed';
12
12
 
13
+ let idCounter = 0;
14
+
13
15
  /**
14
16
  * Props for the Popover component
15
17
  */
@@ -98,6 +100,7 @@ export interface PopoverProps {
98
100
  selector: 'Popover',
99
101
  template: `<ng-content />`,
100
102
  host: {
103
+ 'attr.data-slot': '"popover"',
101
104
  class: 'relative',
102
105
  },
103
106
  providers: [
@@ -127,6 +130,8 @@ export class Popover implements PopoverContextValue {
127
130
  readonly controlledOpen = input<boolean | undefined>(undefined, { alias: 'open' });
128
131
 
129
132
  readonly open = signal(false);
133
+ /** Unique ID for aria-controls relationship */
134
+ readonly contentId = `popover-content-${++idCounter}`;
130
135
  /** Reference to the trigger element for positioning */
131
136
  readonly triggerRef = signal<HTMLElement | null>(null);
132
137
 
@@ -102,9 +102,8 @@ export type ProgressProps = {
102
102
  '[attr.aria-label]': 'ariaLabel()',
103
103
  '[attr.aria-valuemin]': '0',
104
104
  '[attr.aria-valuemax]': 'max()',
105
- '[attr.aria-valuenow]': 'value()',
105
+ '[attr.aria-valuenow]': 'value() !== null ? value() : null',
106
106
  '[attr.aria-valuetext]': 'computedValueText()',
107
- '[attr.aria-live]': '"polite"',
108
107
  '[attr.data-state]': 'state()',
109
108
  '[attr.data-value]': 'value()',
110
109
  '[attr.data-max]': 'max()',
@@ -43,6 +43,7 @@ let handleIdCounter = 0;
43
43
  }
44
44
  `,
45
45
  host: {
46
+ 'attr.data-slot': '"resizable-handle"',
46
47
  '[class]': 'computedClass()',
47
48
  '[attr.data-panel-resize-handle]': 'true',
48
49
  '[attr.data-panel-resize-handle-id]': 'handleId',
@@ -28,6 +28,7 @@ import { RESIZABLE_CONTEXT, type ResizableContextValue } from './resizable-conte
28
28
  selector: 'ResizablePanelGroup',
29
29
  template: `<ng-content />`,
30
30
  host: {
31
+ 'attr.data-slot': '"resizable-panel-group"',
31
32
  '[class]': 'computedClass()',
32
33
  '[attr.data-panel-group]': 'true',
33
34
  '[attr.data-panel-group-direction]': 'direction()',
@@ -17,6 +17,7 @@ let panelIdCounter = 0;
17
17
  selector: 'ResizablePanel',
18
18
  template: `<ng-content />`,
19
19
  host: {
20
+ 'attr.data-slot': '"resizable-panel"',
20
21
  '[class]': 'computedClass()',
21
22
  '[style.flex-basis.%]': 'currentSize()',
22
23
  '[attr.data-panel]': 'true',
@@ -84,6 +84,7 @@ export interface ScrollAreaProps {
84
84
  <ng-content select="ScrollBar" />
85
85
  `,
86
86
  host: {
87
+ 'attr.data-slot': '"scroll-area"',
87
88
  '[class]': 'computedClass()',
88
89
  },
89
90
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -97,15 +98,16 @@ export class ScrollArea {
97
98
 
98
99
  protected readonly computedClass = computed(() => cn('relative overflow-hidden', this.class()));
99
100
 
100
- protected readonly viewportClass = computed(() =>
101
- cn(
101
+ protected readonly viewportClass = computed(() => {
102
+ const type = this.type();
103
+ return cn(
102
104
  'h-full w-full rounded-[inherit]',
103
105
  '[&>div]:!block',
104
- // Hide native scrollbar
106
+ // Hide native scrollbar (custom ScrollBar component provides visual feedback)
105
107
  '[&::-webkit-scrollbar]:hidden',
106
108
  '[-ms-overflow-style:none]',
107
109
  '[scrollbar-width:none]',
108
- 'overflow-auto',
109
- ),
110
- );
110
+ type === 'always' ? 'overflow-scroll' : 'overflow-auto',
111
+ );
112
+ });
111
113
  }
@@ -46,6 +46,7 @@ export interface ScrollBarProps {
46
46
  selector: 'ScrollBar',
47
47
  template: ` <div [class]="thumbClass()"></div> `,
48
48
  host: {
49
+ 'attr.data-slot': '"scroll-bar"',
49
50
  '[class]': 'computedClass()',
50
51
  '[attr.data-orientation]': 'orientation()',
51
52
  },
@@ -13,6 +13,7 @@ import { segmentedItemVariants } from './segmented-variants';
13
13
  selector: 'SegmentedItem',
14
14
  template: `<ng-content />`,
15
15
  host: {
16
+ 'attr.data-slot': '"segmented-item"',
16
17
  '[class]': 'computedClass()',
17
18
  '[attr.role]': 'itemRole()',
18
19
  '[attr.aria-selected]': 'itemRole() === "tab" ? isSelected() : null',
@@ -33,6 +33,7 @@ import { segmentedVariants, type SegmentedVariants } from './segmented-variants'
33
33
  selector: 'Segmented',
34
34
  template: `<ng-content />`,
35
35
  host: {
36
+ 'attr.data-slot': '"segmented"',
36
37
  '[class]': 'computedClass()',
37
38
  role: 'tablist',
38
39
  '[attr.aria-orientation]': '"horizontal"',
@@ -1,4 +1,4 @@
1
- import { cn } from '@/lib/utils';
1
+ import { cn, Presence } from '@/lib/utils';
2
2
  import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';
3
3
  import { SELECT_CONTEXT } from './select-context';
4
4
 
@@ -9,25 +9,29 @@ import { SELECT_CONTEXT } from './select-context';
9
9
  */
10
10
  @Component({
11
11
  selector: 'SelectContent',
12
+ imports: [Presence],
12
13
  template: `
13
- <div
14
- [class]="dropdownClass()"
15
- [attr.id]="context?.contentId"
16
- [attr.data-state]="context?.open() ? 'open' : 'closed'"
17
- [attr.data-side]="side()"
18
- [attr.data-align]="align()"
19
- role="listbox"
20
- aria-label="Options"
21
- (keydown.escape)="onEscape()"
22
- >
23
- <div [class]="viewportClass()">
24
- <ng-content />
14
+ <Presence [present]="context?.open() ?? false">
15
+ <div
16
+ [class]="dropdownClass()"
17
+ [attr.id]="context?.contentId"
18
+ [attr.data-state]="context?.open() ? 'open' : 'closed'"
19
+ [attr.data-side]="side()"
20
+ [attr.data-align]="align()"
21
+ [attr.aria-activedescendant]="focusedItemId()"
22
+ role="listbox"
23
+ (keydown.escape)="onEscape()"
24
+ (keydown)="onKeydown($event)"
25
+ >
26
+ <div [class]="viewportClass()">
27
+ <ng-content />
28
+ </div>
25
29
  </div>
26
- </div>
30
+ </Presence>
27
31
  `,
28
32
  host: {
29
33
  class: 'contents',
30
- 'data-slot': 'select-content',
34
+ 'attr.data-slot': '"select-content"',
31
35
  },
32
36
  changeDetection: ChangeDetectionStrategy.OnPush,
33
37
  })
@@ -49,8 +53,9 @@ export class SelectContent {
49
53
  protected readonly dropdownClass = computed(() =>
50
54
  cn(
51
55
  'bg-popover text-popover-foreground absolute left-0 top-full z-50 mt-1 w-full overflow-hidden rounded-md border shadow-md',
52
- 'animate-in fade-in-0 zoom-in-95',
53
- !this.context?.open() && 'pointer-events-none invisible opacity-0',
56
+ 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
57
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
58
+ 'data-[state=closed]:pointer-events-none',
54
59
  this.class(),
55
60
  ),
56
61
  );
@@ -58,6 +63,15 @@ export class SelectContent {
58
63
  /** Viewport class */
59
64
  protected readonly viewportClass = computed(() => cn('max-h-60 overflow-y-auto p-1'));
60
65
 
66
+ /** ID of the currently focused item for aria-activedescendant */
67
+ protected readonly focusedItemId = computed(() => {
68
+ if (!this.context) return null;
69
+ const values = this.context.itemValues();
70
+ const focusedIndex = this.context.focusedIndex();
71
+ const focusedValue = values[focusedIndex];
72
+ return focusedValue ? `select-item-${focusedValue}` : null;
73
+ });
74
+
61
75
  protected onEscape(): void {
62
76
  this.context?.setOpen(false);
63
77
  const trigger = this.context?.triggerElement();
@@ -65,4 +79,11 @@ export class SelectContent {
65
79
  setTimeout(() => trigger.focus());
66
80
  }
67
81
  }
82
+
83
+ /** Handle printable character keys for typeahead search */
84
+ onKeydown(event: KeyboardEvent): void {
85
+ if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
86
+ this.context?.handleTypeahead(event.key);
87
+ }
88
+ }
68
89
  }
@@ -31,6 +31,16 @@ export interface SelectContext {
31
31
  required?: () => boolean;
32
32
  /** Name for form submission */
33
33
  name?: () => string;
34
+ /** Map of item value → display label for typeahead */
35
+ itemLabels: WritableSignal<Map<string, string>>;
36
+ /** Accumulated typeahead characters */
37
+ typeaheadBuffer: WritableSignal<string>;
38
+ /** Timeout handle for clearing the typeahead buffer */
39
+ typeaheadTimeout: WritableSignal<ReturnType<typeof setTimeout> | null>;
40
+ /** Handle a typed character for typeahead search */
41
+ handleTypeahead: (char: string) => void;
42
+ /** Focus the first item whose label starts with query (case-insensitive) */
43
+ focusMatchingItem: (query: string) => void;
34
44
  }
35
45
 
36
46
  export interface SelectGroupContext {
@@ -47,6 +47,7 @@ import { SELECT_CONTEXT } from './select-context';
47
47
  host: {
48
48
  '[class]': 'computedClass()',
49
49
  role: 'option',
50
+ '[attr.id]': 'itemId()',
50
51
  '[attr.aria-selected]': 'isSelected()',
51
52
  '[attr.data-state]': 'isSelected() ? "checked" : "unchecked"',
52
53
  '[attr.data-disabled]': 'disabled() ? "" : null',
@@ -55,7 +56,7 @@ import { SELECT_CONTEXT } from './select-context';
55
56
  '[attr.tabindex]': 'disabled() ? -1 : 0',
56
57
  '(click)': 'select()',
57
58
  '(keydown)': 'onKeyDown($event)',
58
- 'data-slot': 'select-item',
59
+ 'attr.data-slot': '"select-item"',
59
60
  },
60
61
  changeDetection: ChangeDetectionStrategy.OnPush,
61
62
  })
@@ -72,6 +73,9 @@ export class SelectItem implements OnInit, OnDestroy {
72
73
 
73
74
  private readonly _context = inject(SELECT_CONTEXT, { optional: true });
74
75
 
76
+ /** Stable DOM id for aria-activedescendant */
77
+ readonly itemId = computed(() => `select-item-${this.value()}`);
78
+
75
79
  /** Whether this item is selected */
76
80
  protected readonly isSelected = computed(() => {
77
81
  return this._context?.value() === this.value();
@@ -94,17 +98,28 @@ export class SelectItem implements OnInit, OnDestroy {
94
98
  });
95
99
 
96
100
  ngOnInit(): void {
97
- // Register this item
101
+ // Register this item's value
98
102
  this._context?.itemValues.update((values) => {
99
103
  if (!values.includes(this.value())) {
100
104
  return [...values, this.value()];
101
105
  }
102
106
  return values;
103
107
  });
108
+ // Register this item's label for typeahead
109
+ this._context?.itemLabels.update((m) => {
110
+ const label = this.textContent()?.nativeElement?.textContent?.trim() || this.value();
111
+ m.set(this.value(), label);
112
+ return m;
113
+ });
104
114
  }
105
115
  ngOnDestroy(): void {
106
- // Unregister this item
116
+ // Unregister this item's value
107
117
  this._context?.itemValues.update((values) => values.filter((v) => v !== this.value()));
118
+ // Unregister this item's label
119
+ this._context?.itemLabels.update((m) => {
120
+ m.delete(this.value());
121
+ return m;
122
+ });
108
123
  }
109
124
 
110
125
  /** Handle keyboard navigation */
@@ -117,17 +132,20 @@ export class SelectItem implements OnInit, OnDestroy {
117
132
  event.preventDefault();
118
133
  this.select();
119
134
  break;
120
- case 'ArrowDown':
135
+ case 'ArrowDown': {
121
136
  event.preventDefault();
122
137
  const currentIndex = this._context.focusedIndex();
123
138
  const itemCount = this._context.itemValues().length;
124
- this._context.focusItem(Math.min(currentIndex + 1, itemCount - 1));
139
+ this._context.focusItem((currentIndex + 1) % itemCount);
125
140
  break;
126
- case 'ArrowUp':
141
+ }
142
+ case 'ArrowUp': {
127
143
  event.preventDefault();
128
144
  const idx = this._context.focusedIndex();
129
- this._context.focusItem(Math.max(idx - 1, 0));
145
+ const count = this._context.itemValues().length;
146
+ this._context.focusItem(idx > 0 ? idx - 1 : count - 1);
130
147
  break;
148
+ }
131
149
  case 'Home':
132
150
  event.preventDefault();
133
151
  this._context.focusItem(0);