@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
@@ -25,6 +25,7 @@ let itemIndexCounter = 0;
25
25
  selector: 'CommandItem',
26
26
  template: `<ng-content />`,
27
27
  host: {
28
+ 'attr.data-slot': '"command-item"',
28
29
  '[class]': 'computedClass()',
29
30
  '[attr.role]': '"option"',
30
31
  '[attr.id]': 'itemId',
@@ -92,7 +93,7 @@ export class CommandItem implements OnInit, OnDestroy {
92
93
  });
93
94
  protected readonly computedClass = computed(() =>
94
95
  cn(
95
- 'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[selected]:bg-accent data-[selected]:text-accent-foreground data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
96
+ 'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[selected]:bg-accent data-[selected]:text-accent-foreground data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0 [&>lucide-icon]:size-4 [&>lucide-icon]:shrink-0',
96
97
  !this.disabled() && 'cursor-pointer hover:bg-accent hover:text-accent-foreground',
97
98
  this.class(),
98
99
  ),
@@ -11,6 +11,7 @@ import { COMMAND_CONTEXT } from './command-context';
11
11
  selector: 'CommandList',
12
12
  template: `<ng-content />`,
13
13
  host: {
14
+ 'attr.data-slot': '"command-list"',
14
15
  '[class]': 'computedClass()',
15
16
  '[attr.id]': 'context.listId',
16
17
  role: 'listbox',
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'CommandSeparator',
10
10
  template: ``,
11
11
  host: {
12
+ 'attr.data-slot': '"command-separator"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'CommandShortcut',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"command-shortcut"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -63,6 +63,7 @@ const defaultFilterFn: CommandFilterFunction = (
63
63
  },
64
64
  ],
65
65
  host: {
66
+ 'attr.data-slot': '"command"',
66
67
  '[class]': 'computedClass()',
67
68
  },
68
69
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -27,6 +27,7 @@ import { CONTEXT_MENU_CONTEXT } from './context-menu-context';
27
27
  <ng-content />
28
28
  `,
29
29
  host: {
30
+ 'attr.data-slot': '"context-menu-checkbox-item"',
30
31
  '[class]': 'computedClass()',
31
32
  '[attr.role]': '"menuitemcheckbox"',
32
33
  '[attr.aria-checked]': 'checked()',
@@ -7,8 +7,10 @@ import {
7
7
  effect,
8
8
  ElementRef,
9
9
  inject,
10
+ Injector,
10
11
  input,
11
12
  OnDestroy,
13
+ signal,
12
14
  } from '@angular/core';
13
15
  import { CONTEXT_MENU_CONTEXT } from './context-menu-context';
14
16
 
@@ -26,8 +28,8 @@ import { CONTEXT_MENU_CONTEXT } from './context-menu-context';
26
28
  [class]="computedClass()"
27
29
  [attr.data-state]="context.open() ? 'open' : 'closed'"
28
30
  [style.position]="'fixed'"
29
- [style.left.px]="context.position().x"
30
- [style.top.px]="context.position().y"
31
+ [style.left.px]="displayPosition().x"
32
+ [style.top.px]="displayPosition().y"
31
33
  role="menu"
32
34
  aria-orientation="vertical"
33
35
  tabindex="-1"
@@ -38,8 +40,9 @@ import { CONTEXT_MENU_CONTEXT } from './context-menu-context';
38
40
  </Presence>
39
41
  `,
40
42
  host: {
43
+ 'attr.data-slot': '"context-menu-content"',
41
44
  class: 'contents',
42
- '(document:click)': 'onDocumentClick()',
45
+ '(document:click)': 'onDocumentClick($event)',
43
46
  '(document:keydown.escape)': 'onEscapeKey()',
44
47
  '(document:contextmenu)': 'onAnotherContextMenu()',
45
48
  },
@@ -47,19 +50,28 @@ import { CONTEXT_MENU_CONTEXT } from './context-menu-context';
47
50
  })
48
51
  export class ContextMenuContent implements OnDestroy {
49
52
  constructor() {
50
- // Focus first item when menu opens
53
+ // Clamp position and focus first item when menu opens
51
54
  effect(() => {
52
55
  if (this.context.open()) {
53
- setTimeout(() => {
54
- this.updateMenuItems();
55
- const focusedIdx = this.context.focusedIndex();
56
- if (focusedIdx >= 0 && this.menuItems[focusedIdx]) {
57
- this.menuItems[focusedIdx].focus();
58
- } else if (this.menuItems.length > 0) {
59
- this.menuItems[0].focus();
60
- this.context.focusedIndex.set(0);
61
- }
62
- }, 0);
56
+ this.isPositioned.set(false);
57
+ afterNextRender(
58
+ () => {
59
+ this.clampPosition();
60
+ this.isPositioned.set(true);
61
+ this.updateMenuItems();
62
+ const focusedIdx = this.context.focusedIndex();
63
+ if (focusedIdx >= 0 && this.menuItems[focusedIdx]) {
64
+ this.menuItems[focusedIdx].focus();
65
+ } else if (this.menuItems.length > 0) {
66
+ this.menuItems[0].focus();
67
+ this.context.focusedIndex.set(0);
68
+ }
69
+ },
70
+ { injector: this._injector },
71
+ );
72
+ } else {
73
+ this.isPositioned.set(false);
74
+ this.clampedPos.set(null);
63
75
  }
64
76
  });
65
77
 
@@ -73,15 +85,21 @@ export class ContextMenuContent implements OnDestroy {
73
85
  readonly class = input<string>('');
74
86
 
75
87
  private readonly _elementRef = inject(ElementRef);
88
+ private readonly _injector = inject(Injector);
76
89
 
77
90
  protected readonly context = inject(CONTEXT_MENU_CONTEXT);
78
91
 
92
+ protected readonly isPositioned = signal(false);
93
+ protected readonly clampedPos = signal<{ x: number; y: number } | null>(null);
94
+ protected readonly displayPosition = computed(() => this.clampedPos() ?? this.context.position());
95
+
79
96
  protected readonly computedClass = computed(() =>
80
97
  cn(
81
98
  'z-50 min-w-[12rem] overflow-hidden rounded-xl border bg-popover p-2 text-popover-foreground shadow-lg',
82
99
  'data-[state=open]:animate-in data-[state=closed]:animate-out',
83
100
  'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
84
101
  'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
102
+ !this.isPositioned() && 'pointer-events-none opacity-0',
85
103
  this.class(),
86
104
  ),
87
105
  );
@@ -96,12 +114,22 @@ export class ContextMenuContent implements OnDestroy {
96
114
  }
97
115
  }
98
116
 
117
+ private clampPosition(): void {
118
+ const menu = this._elementRef.nativeElement.querySelector('[role="menu"]') as HTMLElement;
119
+ if (!menu) return;
120
+ const pos = this.context.position();
121
+ const rect = menu.getBoundingClientRect();
122
+ const padding = 8;
123
+ const x = Math.max(padding, Math.min(pos.x, window.innerWidth - rect.width - padding));
124
+ const y = Math.max(padding, Math.min(pos.y, window.innerHeight - rect.height - padding));
125
+ this.clampedPos.set({ x, y });
126
+ }
99
127
  private updateMenuItems(): void {
100
128
  const content = this._elementRef.nativeElement.querySelector('[role="menu"]');
101
129
  if (content) {
102
130
  this.menuItems = Array.from(
103
131
  content.querySelectorAll(
104
- '[role="menuitem"]:not([aria-disabled="true"]):not([data-disabled])',
132
+ ':is([role="menuitem"],[role="menuitemcheckbox"],[role="menuitemradio"]):not([aria-disabled="true"]):not([data-disabled=""])',
105
133
  ),
106
134
  );
107
135
  }
@@ -193,8 +221,12 @@ export class ContextMenuContent implements OnDestroy {
193
221
  triggerEl.focus();
194
222
  }
195
223
  }
196
- protected onDocumentClick(): void {
197
- this.close();
224
+ protected onDocumentClick(event: MouseEvent): void {
225
+ const target = event.target as HTMLElement;
226
+ const host = this._elementRef.nativeElement;
227
+ if (!host.contains(target)) {
228
+ this.close();
229
+ }
198
230
  }
199
231
  protected onEscapeKey(): void {
200
232
  this.close();
@@ -11,6 +11,7 @@ import { CONTEXT_MENU_CONTEXT } from './context-menu-context';
11
11
  selector: 'ContextMenuItem',
12
12
  template: `<ng-content />`,
13
13
  host: {
14
+ 'attr.data-slot': '"context-menu-item"',
14
15
  '[class]': 'computedClass()',
15
16
  '[attr.role]': '"menuitem"',
16
17
  '[attr.tabindex]': 'disabled() ? -1 : -1',
@@ -37,7 +38,7 @@ export class ContextMenuItem {
37
38
 
38
39
  protected readonly computedClass = computed(() =>
39
40
  cn(
40
- 'relative flex cursor-default select-none items-center gap-3 rounded-md px-3 py-2.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
41
+ 'relative flex cursor-default select-none items-center gap-3 rounded-md px-3 py-2.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0 [&>lucide-icon]:size-4 [&>lucide-icon]:shrink-0',
41
42
  this.inset() && 'pl-8',
42
43
  !this.disabled() && 'cursor-pointer hover:bg-accent hover:text-accent-foreground',
43
44
  this.class(),
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'ContextMenuLabel',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"context-menu-label"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -35,6 +35,7 @@ export const CONTEXT_MENU_RADIO_GROUP_CONTEXT = new InjectionToken<ContextMenuRa
35
35
  },
36
36
  ],
37
37
  host: {
38
+ 'attr.data-slot': '"context-menu-radio-group"',
38
39
  '[attr.role]': '"group"',
39
40
  },
40
41
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -20,6 +20,7 @@ import { CONTEXT_MENU_RADIO_GROUP_CONTEXT } from './context-menu-radio-group.com
20
20
  <ng-content />
21
21
  `,
22
22
  host: {
23
+ 'attr.data-slot': '"context-menu-radio-item"',
23
24
  '[class]': 'computedClass()',
24
25
  '[attr.role]': '"menuitemradio"',
25
26
  '[attr.aria-checked]': 'isSelected()',
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'ContextMenuSeparator',
10
10
  template: ``,
11
11
  host: {
12
+ 'attr.data-slot': '"context-menu-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: 'ContextMenuShortcut',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"context-menu-shortcut"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -21,6 +21,7 @@ import { CONTEXT_MENU_SUB_CONTEXT } from './context-menu-sub.component';
21
21
  </Presence>
22
22
  `,
23
23
  host: {
24
+ 'attr.data-slot': '"context-menu-sub-content"',
24
25
  class: 'contents',
25
26
  '(mouseenter)': 'onMouseEnter()',
26
27
  '(mouseleave)': 'onMouseLeave()',
@@ -42,9 +43,11 @@ export class ContextMenuSubContent {
42
43
  );
43
44
 
44
45
  protected onMouseEnter(): void {
46
+ this.subContext.isMouseInSubContent.set(true);
45
47
  this.subContext.open.set(true);
46
48
  }
47
49
  protected onMouseLeave(): void {
50
+ this.subContext.isMouseInSubContent.set(false);
48
51
  this.subContext.open.set(false);
49
52
  }
50
53
  }
@@ -15,10 +15,16 @@ import { CONTEXT_MENU_SUB_CONTEXT } from './context-menu-sub.component';
15
15
  <lucide-icon [img]="ChevronRightIcon" class="ml-auto h-4 w-4" />
16
16
  `,
17
17
  host: {
18
+ 'attr.data-slot': '"context-menu-sub-trigger"',
18
19
  '[class]': 'computedClass()',
19
20
  '(mouseenter)': 'onMouseEnter()',
20
21
  '(mouseleave)': 'onMouseLeave()',
22
+ '(keydown)': 'onKeyDown($event)',
21
23
  '[attr.data-state]': 'subContext.open() ? "open" : "closed"',
24
+ 'role': 'menuitem',
25
+ '[attr.aria-haspopup]': '"menu"',
26
+ '[attr.aria-expanded]': 'subContext.open()',
27
+ '[attr.tabindex]': '"-1"',
22
28
  },
23
29
  changeDetection: ChangeDetectionStrategy.OnPush,
24
30
  })
@@ -33,7 +39,7 @@ export class ContextMenuSubTrigger {
33
39
 
34
40
  protected readonly computedClass = computed(() =>
35
41
  cn(
36
- 'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent [&>svg]:size-4 [&>svg]:shrink-0',
42
+ 'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent [&>svg]:size-4 [&>svg]:shrink-0 [&>lucide-icon]:size-4 [&>lucide-icon]:shrink-0',
37
43
  this.inset() && 'pl-8',
38
44
  this.class(),
39
45
  ),
@@ -45,6 +51,30 @@ export class ContextMenuSubTrigger {
45
51
  this.subContext.open.set(true);
46
52
  }
47
53
  protected onMouseLeave(): void {
48
- // Keep open to allow mouse movement to subcontent
54
+ setTimeout(() => {
55
+ if (!this.subContext.isMouseInSubContent()) {
56
+ this.subContext.open.set(false);
57
+ }
58
+ }, 100);
59
+ }
60
+ protected onKeyDown(event: KeyboardEvent): void {
61
+ if (event.key === 'ArrowRight' || event.key === 'Enter' || event.key === ' ') {
62
+ event.preventDefault();
63
+ event.stopPropagation();
64
+ this.subContext.open.set(true);
65
+ // Focus first focusable item in sub-content after it renders
66
+ setTimeout(() => {
67
+ const subContent = (event.target as HTMLElement)
68
+ .closest('ContextMenuSub')
69
+ ?.querySelector<HTMLElement>('[role="menu"] [role="menuitem"]:not([aria-disabled="true"]):not([data-disabled=""])');
70
+ if (subContent) {
71
+ subContent.focus();
72
+ }
73
+ }, 10);
74
+ }
75
+ if (event.key === 'ArrowLeft') {
76
+ event.preventDefault();
77
+ this.subContext.open.set(false);
78
+ }
49
79
  }
50
80
  }
@@ -8,6 +8,8 @@ import {
8
8
 
9
9
  export interface ContextMenuSubContext {
10
10
  open: WritableSignal<boolean>;
11
+ /** True while the mouse is hovering over the sub-content panel */
12
+ isMouseInSubContent: WritableSignal<boolean>;
11
13
  }
12
14
 
13
15
  export const CONTEXT_MENU_SUB_CONTEXT = new InjectionToken<ContextMenuSubContext>(
@@ -26,10 +28,12 @@ export const CONTEXT_MENU_SUB_CONTEXT = new InjectionToken<ContextMenuSubContext
26
28
  provide: CONTEXT_MENU_SUB_CONTEXT,
27
29
  useFactory: (): ContextMenuSubContext => ({
28
30
  open: signal(false),
31
+ isMouseInSubContent: signal(false),
29
32
  }),
30
33
  },
31
34
  ],
32
35
  host: {
36
+ 'attr.data-slot': '"context-menu-sub"',
33
37
  class: 'relative',
34
38
  },
35
39
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -10,6 +10,7 @@ import { CONTEXT_MENU_CONTEXT } from './context-menu-context';
10
10
  selector: 'ContextMenuTrigger',
11
11
  template: `<ng-content />`,
12
12
  host: {
13
+ 'attr.data-slot': '"context-menu-trigger"',
13
14
  '[attr.tabindex]': '0',
14
15
  '(contextmenu)': 'onContextMenu($event)',
15
16
  '(keydown)': 'onKeydown($event)',
@@ -20,6 +20,7 @@ import { CONTEXT_MENU_CONTEXT, type ContextMenuContextValue } from './context-me
20
20
  },
21
21
  ],
22
22
  host: {
23
+ 'attr.data-slot': '"context-menu"',
23
24
  class: 'relative inline-block',
24
25
  },
25
26
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -96,6 +96,7 @@ import {
96
96
  </div>
97
97
  `,
98
98
  host: {
99
+ 'attr.data-slot': '"data-table-content"',
99
100
  '[class]': 'computedClass()',
100
101
  ngSkipHydration: 'true',
101
102
  },
@@ -74,6 +74,7 @@ import { DATA_TABLE_CONTEXT } from './data-table-context';
74
74
  </div>
75
75
  `,
76
76
  host: {
77
+ 'attr.data-slot': '"data-table-pagination"',
77
78
  '[class]': 'computedClass()',
78
79
  },
79
80
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -16,6 +16,7 @@ import { DATA_TABLE_CONTEXT } from './data-table-context';
16
16
  />
17
17
  `,
18
18
  host: {
19
+ 'attr.data-slot': '"data-table-search"',
19
20
  '[class]': 'computedClass()',
20
21
  },
21
22
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -8,6 +8,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
8
8
  selector: 'DataTableToolbar',
9
9
  template: `<ng-content />`,
10
10
  host: {
11
+ 'attr.data-slot': '"data-table-toolbar"',
11
12
  '[class]': 'computedClass()',
12
13
  },
13
14
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -42,6 +42,7 @@ import { DATA_TABLE_CONTEXT } from './data-table-context';
42
42
  </div>
43
43
  `,
44
44
  host: {
45
+ 'attr.data-slot': '"data-table-view-options"',
45
46
  '[class]': 'computedClass()',
46
47
  '(document:click)': 'onDocumentClick($event)',
47
48
  },
@@ -36,7 +36,7 @@ import {
36
36
  template: `<ng-content />`,
37
37
  host: {
38
38
  '[class]': 'computedClass()',
39
- 'data-slot': 'data-table',
39
+ 'attr.data-slot': '"data-table"',
40
40
  ngSkipHydration: 'true',
41
41
  },
42
42
  providers: [
@@ -44,6 +44,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '../popover';
44
44
  </Popover>
45
45
  `,
46
46
  host: {
47
+ 'attr.data-slot': '"date-picker"',
47
48
  class: 'contents',
48
49
  },
49
50
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -9,6 +9,7 @@ import { DIALOG_CONTEXT } from './dialog-context';
9
9
  selector: 'DialogClose',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"dialog-close"',
12
13
  '(click)': 'onClick($event)',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -1,4 +1,4 @@
1
- import { cn } from '@/lib/utils';
1
+ import { cn, Presence } from '@/lib/utils';
2
2
  import { FocusTrapDirective } from '@/lib/utils/accessibility';
3
3
  import {
4
4
  ChangeDetectionStrategy,
@@ -16,15 +16,23 @@ import { DIALOG_CONTEXT } from './dialog-context';
16
16
  /**
17
17
  * DialogContent component - the content of the dialog.
18
18
  * Matches shadcn/ui React DialogContent exactly.
19
+ *
20
+ * Features:
21
+ * - Escape key closes the dialog
22
+ * - Overlay click closes the dialog
23
+ * - Focus is trapped within the dialog
24
+ * - Exit animations handled by Presence component (no setTimeout needed)
25
+ * - Focus restored on any close path (overlay click, close button, Escape, programmatic)
19
26
  */
20
27
  @Component({
21
28
  selector: 'DialogContent',
22
- imports: [FocusTrapDirective],
29
+ imports: [FocusTrapDirective, Presence],
23
30
  template: `
24
- @if (context.isOpen()) {
31
+ <Presence [present]="context.isOpen()">
25
32
  <!-- Overlay -->
26
33
  <div
27
- class="fixed inset-0 z-50 bg-black/80"
34
+ class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-200"
35
+ [attr.data-state]="context.isOpen() ? 'open' : 'closed'"
28
36
  (click)="onOverlayClick($event)"
29
37
  aria-hidden="true"
30
38
  ></div>
@@ -33,9 +41,10 @@ import { DIALOG_CONTEXT } from './dialog-context';
33
41
  hlmFocusTrap
34
42
  [trapFocus]="context.isOpen()"
35
43
  [autoFocus]="true"
36
- [restoreFocus]="true"
44
+ [restoreFocus]="false"
37
45
  [initialFocus]="initialFocus()"
38
46
  [class]="computedClass()"
47
+ [attr.data-state]="context.isOpen() ? 'open' : 'closed'"
39
48
  [attr.id]="context.contentId"
40
49
  [attr.aria-labelledby]="context.titleId"
41
50
  [attr.aria-describedby]="context.descriptionId"
@@ -43,7 +52,6 @@ import { DIALOG_CONTEXT } from './dialog-context';
43
52
  aria-modal="true"
44
53
  >
45
54
  <ng-content />
46
- <!-- Close button -->
47
55
  @if (showClose()) {
48
56
  <button
49
57
  type="button"
@@ -71,44 +79,44 @@ import { DIALOG_CONTEXT } from './dialog-context';
71
79
  </button>
72
80
  }
73
81
  </div>
74
- }
82
+ </Presence>
75
83
  `,
76
84
  host: {
85
+ 'attr.data-slot': '"dialog-content"',
77
86
  class: 'contents',
78
87
  },
79
88
  changeDetection: ChangeDetectionStrategy.OnPush,
80
89
  })
81
90
  export class DialogContent {
82
91
  constructor() {
83
- // Handle body scroll lock based on open state
92
+ let wasOpen = false;
93
+
84
94
  effect(() => {
85
95
  const isOpen = this.context.isOpen();
86
-
87
- // Force change detection since context.isOpen() might update outside of this component's hierarchy
88
- // and we are using OnPush
89
96
  this._cdr.markForCheck();
90
97
 
91
98
  if (isOpen) {
99
+ wasOpen = true;
92
100
  this.lockBodyScroll();
93
101
  } else {
94
102
  this.unlockBodyScroll();
103
+ // Restore focus on any close path (overlay click, close button, Escape, programmatic)
104
+ if (wasOpen) {
105
+ this.restoreFocus();
106
+ }
107
+ wasOpen = false;
95
108
  }
96
109
  });
97
110
 
98
- // Cleanup on destroy
99
111
  this._destroyRef.onDestroy(() => {
100
112
  this.unlockBodyScroll();
101
113
  this.restoreFocus();
102
114
  });
103
115
  }
104
116
 
105
- /** Additional CSS classes */
106
117
  readonly class = input<string>('');
107
- /** Whether to show close button */
108
118
  readonly showClose = input<boolean>(true);
109
- /** Aria label for close button */
110
119
  readonly ariaCloselabel = input<string>('Close dialog');
111
- /** Selector for initial focus element */
112
120
  readonly initialFocus = input<string | undefined>(undefined);
113
121
 
114
122
  private readonly _destroyRef = inject(DestroyRef);
@@ -129,8 +137,8 @@ export class DialogContent {
129
137
  ),
130
138
  );
131
139
 
132
- /** Previous body overflow for restoration */
133
140
  private previousBodyOverflow = '';
141
+ private previousBodyPaddingRight = '';
134
142
 
135
143
  @HostListener('document:keydown.escape')
136
144
  onEscapeKey(): void {
@@ -148,25 +156,28 @@ export class DialogContent {
148
156
 
149
157
  private lockBodyScroll(): void {
150
158
  if (typeof document !== 'undefined') {
159
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
151
160
  this.previousBodyOverflow = document.body.style.overflow;
161
+ this.previousBodyPaddingRight = document.body.style.paddingRight;
152
162
  document.body.style.overflow = 'hidden';
163
+ if (scrollbarWidth > 0) {
164
+ document.body.style.paddingRight = scrollbarWidth + 'px';
165
+ }
153
166
  }
154
167
  }
155
168
  private unlockBodyScroll(): void {
156
169
  if (typeof document !== 'undefined') {
157
170
  document.body.style.overflow = this.previousBodyOverflow;
171
+ document.body.style.paddingRight = this.previousBodyPaddingRight;
158
172
  }
159
173
  }
160
174
  private close(): void {
161
- this.restoreFocus();
162
175
  this.context.setOpen(false);
163
176
  }
164
177
  private restoreFocus(): void {
165
178
  const triggerEl = this.context.getTriggerElement();
166
179
  if (triggerEl) {
167
- setTimeout(() => {
168
- triggerEl.focus();
169
- }, 0);
180
+ setTimeout(() => triggerEl.focus(), 0);
170
181
  }
171
182
  }
172
183
  }
@@ -11,6 +11,7 @@ import { DIALOG_CONTEXT } from './dialog-context';
11
11
  selector: 'DialogDescription',
12
12
  template: `<ng-content />`,
13
13
  host: {
14
+ 'attr.data-slot': '"dialog-description"',
14
15
  '[class]': 'computedClass()',
15
16
  '[attr.id]': 'context.descriptionId',
16
17
  },
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'DialogFooter',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"dialog-footer"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -9,6 +9,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
9
9
  selector: 'DialogHeader',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"dialog-header"',
12
13
  '[class]': 'computedClass()',
13
14
  },
14
15
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -11,6 +11,7 @@ import { DIALOG_CONTEXT } from './dialog-context';
11
11
  selector: 'DialogTitle',
12
12
  template: `<ng-content />`,
13
13
  host: {
14
+ 'attr.data-slot': '"dialog-title"',
14
15
  '[class]': 'computedClass()',
15
16
  '[attr.id]': 'context.titleId',
16
17
  },
@@ -9,6 +9,7 @@ import { DIALOG_CONTEXT } from './dialog-context';
9
9
  selector: 'DialogTrigger',
10
10
  template: `<ng-content />`,
11
11
  host: {
12
+ 'attr.data-slot': '"dialog-trigger"',
12
13
  '(click)': 'onClick($event)',
13
14
  '[attr.aria-haspopup]': '"dialog"',
14
15
  '[attr.aria-expanded]': 'context.isOpen()',