@ojiepermana/angular 22.1.14 → 22.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/README.md +8 -6
  2. package/SUPPORT.md +3 -3
  3. package/chart/README.md +1 -1
  4. package/component/README.md +2 -2
  5. package/component/calendar/README.md +3 -15
  6. package/component/drawer/README.md +1 -2
  7. package/fesm2022/ojiepermana-angular-chart-area.mjs +352 -137
  8. package/fesm2022/ojiepermana-angular-chart-bar.mjs +744 -524
  9. package/fesm2022/ojiepermana-angular-chart-core.mjs +1140 -1344
  10. package/fesm2022/ojiepermana-angular-chart-line.mjs +393 -147
  11. package/fesm2022/ojiepermana-angular-chart-pie.mjs +408 -179
  12. package/fesm2022/ojiepermana-angular-chart-primitives.mjs +1262 -920
  13. package/fesm2022/ojiepermana-angular-chart-radar.mjs +473 -205
  14. package/fesm2022/ojiepermana-angular-chart-radial.mjs +425 -182
  15. package/fesm2022/ojiepermana-angular-chart-scatter.mjs +386 -210
  16. package/fesm2022/ojiepermana-angular-chart.mjs +3 -19
  17. package/fesm2022/ojiepermana-angular-component-accordion.mjs +485 -243
  18. package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs +963 -325
  19. package/fesm2022/ojiepermana-angular-component-alert.mjs +266 -103
  20. package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs +80 -35
  21. package/fesm2022/ojiepermana-angular-component-avatar.mjs +402 -135
  22. package/fesm2022/ojiepermana-angular-component-badge.mjs +109 -59
  23. package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs +410 -142
  24. package/fesm2022/ojiepermana-angular-component-button-group.mjs +249 -96
  25. package/fesm2022/ojiepermana-angular-component-button.mjs +153 -75
  26. package/fesm2022/ojiepermana-angular-component-calendar.mjs +531 -418
  27. package/fesm2022/ojiepermana-angular-component-card.mjs +457 -171
  28. package/fesm2022/ojiepermana-angular-component-carousel.mjs +642 -272
  29. package/fesm2022/ojiepermana-angular-component-checkbox.mjs +312 -108
  30. package/fesm2022/ojiepermana-angular-component-collapsible.mjs +299 -115
  31. package/fesm2022/ojiepermana-angular-component-combobox.mjs +396 -183
  32. package/fesm2022/ojiepermana-angular-component-command.mjs +807 -347
  33. package/fesm2022/ojiepermana-angular-component-composer.mjs +807 -330
  34. package/fesm2022/ojiepermana-angular-component-context-menu.mjs +196 -161
  35. package/fesm2022/ojiepermana-angular-component-date-picker.mjs +650 -338
  36. package/fesm2022/ojiepermana-angular-component-dialog.mjs +710 -316
  37. package/fesm2022/ojiepermana-angular-component-drawer.mjs +2 -5
  38. package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs +1212 -646
  39. package/fesm2022/ojiepermana-angular-component-editor.mjs +987 -502
  40. package/fesm2022/ojiepermana-angular-component-empty.mjs +350 -137
  41. package/fesm2022/ojiepermana-angular-component-form.mjs +813 -434
  42. package/fesm2022/ojiepermana-angular-component-hover-card.mjs +486 -277
  43. package/fesm2022/ojiepermana-angular-component-icon.mjs +241 -140
  44. package/fesm2022/ojiepermana-angular-component-input-group.mjs +449 -176
  45. package/fesm2022/ojiepermana-angular-component-input-otp.mjs +891 -488
  46. package/fesm2022/ojiepermana-angular-component-input.mjs +86 -55
  47. package/fesm2022/ojiepermana-angular-component-item.mjs +713 -261
  48. package/fesm2022/ojiepermana-angular-component-kanban.mjs +714 -216
  49. package/fesm2022/ojiepermana-angular-component-kbd.mjs +124 -52
  50. package/fesm2022/ojiepermana-angular-component-label.mjs +82 -31
  51. package/fesm2022/ojiepermana-angular-component-menubar.mjs +479 -329
  52. package/fesm2022/ojiepermana-angular-component-native-select.mjs +167 -65
  53. package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs +683 -380
  54. package/fesm2022/ojiepermana-angular-component-pagination.mjs +277 -94
  55. package/fesm2022/ojiepermana-angular-component-pillbox.mjs +1283 -607
  56. package/fesm2022/ojiepermana-angular-component-popover.mjs +291 -204
  57. package/fesm2022/ojiepermana-angular-component-progress.mjs +176 -53
  58. package/fesm2022/ojiepermana-angular-component-radio.mjs +449 -142
  59. package/fesm2022/ojiepermana-angular-component-resizable.mjs +659 -453
  60. package/fesm2022/ojiepermana-angular-component-scroll-area.mjs +117 -39
  61. package/fesm2022/ojiepermana-angular-component-segmented-control.mjs +351 -172
  62. package/fesm2022/ojiepermana-angular-component-select.mjs +475 -345
  63. package/fesm2022/ojiepermana-angular-component-separator.mjs +102 -35
  64. package/fesm2022/ojiepermana-angular-component-sheet.mjs +797 -347
  65. package/fesm2022/ojiepermana-angular-component-skeleton.mjs +62 -29
  66. package/fesm2022/ojiepermana-angular-component-slider.mjs +592 -376
  67. package/fesm2022/ojiepermana-angular-component-spinner.mjs +135 -38
  68. package/fesm2022/ojiepermana-angular-component-switch.mjs +315 -103
  69. package/fesm2022/ojiepermana-angular-component-table.mjs +440 -153
  70. package/fesm2022/ojiepermana-angular-component-tabs.mjs +590 -317
  71. package/fesm2022/ojiepermana-angular-component-textarea.mjs +85 -50
  72. package/fesm2022/ojiepermana-angular-component-timeline.mjs +609 -231
  73. package/fesm2022/ojiepermana-angular-component-toast.mjs +229 -243
  74. package/fesm2022/ojiepermana-angular-component-toggle-group.mjs +573 -288
  75. package/fesm2022/ojiepermana-angular-component-toggle.mjs +187 -78
  76. package/fesm2022/ojiepermana-angular-component-tooltip.mjs +798 -517
  77. package/fesm2022/ojiepermana-angular-component-utils-core.mjs +159 -292
  78. package/fesm2022/ojiepermana-angular-component-utils-overlay.mjs +46 -58
  79. package/fesm2022/ojiepermana-angular-component-utils-scale.mjs +76 -38
  80. package/fesm2022/ojiepermana-angular-component-utils.mjs +2 -8
  81. package/fesm2022/ojiepermana-angular-component.mjs +0 -10
  82. package/fesm2022/ojiepermana-angular-navigation-core.mjs +13 -23
  83. package/fesm2022/ojiepermana-angular-navigation-icon.mjs +35 -24
  84. package/fesm2022/ojiepermana-angular-navigation-navigation-badge-count.component-BKCeWzml.mjs +129 -0
  85. package/fesm2022/ojiepermana-angular-navigation-navigation-dockbar-menu.component-DFEJK2jD.mjs +811 -0
  86. package/fesm2022/{ojiepermana-angular-navigation-navigation-entry-grid.component-COQ-60DS.mjs → ojiepermana-angular-navigation-navigation-entry-grid.component-mpK9Ar08.mjs} +387 -183
  87. package/fesm2022/ojiepermana-angular-navigation-navigation-flyout-menu.component-BPgH7FJk.mjs +650 -0
  88. package/fesm2022/ojiepermana-angular-navigation-navigation-horizontal.component-DMpYcD9t.mjs +662 -0
  89. package/fesm2022/ojiepermana-angular-navigation-navigation-item.component-Basp5SIk.mjs +1228 -0
  90. package/fesm2022/ojiepermana-angular-navigation-navigation-list.component-YUzHI9Pj.mjs +311 -0
  91. package/fesm2022/ojiepermana-angular-navigation-service.mjs +542 -724
  92. package/fesm2022/ojiepermana-angular-navigation-types.mjs +27 -56
  93. package/fesm2022/ojiepermana-angular-navigation.mjs +3052 -4
  94. package/fesm2022/ojiepermana-angular-sdk.mjs +91 -201
  95. package/fesm2022/ojiepermana-angular-theme-component-settings.mjs +1115 -737
  96. package/fesm2022/ojiepermana-angular-theme-layout-services.mjs +245 -328
  97. package/fesm2022/ojiepermana-angular-theme-layout-types.mjs +35 -38
  98. package/fesm2022/ojiepermana-angular-theme-layout-wrapper.mjs +1719 -552
  99. package/fesm2022/ojiepermana-angular-theme-layout.mjs +847 -496
  100. package/fesm2022/ojiepermana-angular-theme-page-core.mjs +288 -358
  101. package/fesm2022/ojiepermana-angular-theme-page-filter.mjs +362 -224
  102. package/fesm2022/ojiepermana-angular-theme-page-overview.mjs +88 -0
  103. package/fesm2022/ojiepermana-angular-theme-page-root.mjs +576 -254
  104. package/fesm2022/ojiepermana-angular-theme-page-side.mjs +317 -177
  105. package/fesm2022/ojiepermana-angular-theme-page-slots.mjs +256 -112
  106. package/fesm2022/ojiepermana-angular-theme-page.mjs +7 -12
  107. package/fesm2022/ojiepermana-angular-theme-shell-services.mjs +215 -326
  108. package/fesm2022/ojiepermana-angular-theme-shell-types.mjs +68 -129
  109. package/fesm2022/ojiepermana-angular-theme-shell.mjs +907 -378
  110. package/fesm2022/ojiepermana-angular-theme-styles.mjs +521 -720
  111. package/fesm2022/ojiepermana-angular-theme.mjs +0 -10
  112. package/fesm2022/ojiepermana-angular.mjs +5 -24
  113. package/navigation/README.md +1 -1
  114. package/package.json +16 -12
  115. package/sdk/README.md +1 -1
  116. package/sdk/src/writer/index.js +2 -2
  117. package/theme/README.md +1 -1
  118. package/theme/layout/README.md +2 -4
  119. package/theme/page/README.md +50 -0
  120. package/theme/styles/README.md +10 -10
  121. package/theme/styles/css/base/prefers-dark.css +1 -1
  122. package/theme/styles/css/base/status.css +1 -1
  123. package/theme/styles/css/color/amber.css +1 -1
  124. package/theme/styles/css/color/blue.css +1 -1
  125. package/theme/styles/css/color/cyan.css +1 -1
  126. package/theme/styles/css/color/emerald.css +1 -1
  127. package/theme/styles/css/color/fuchsia.css +1 -1
  128. package/theme/styles/css/color/green.css +1 -1
  129. package/theme/styles/css/color/indigo.css +1 -1
  130. package/theme/styles/css/color/lime.css +1 -1
  131. package/theme/styles/css/color/orange.css +1 -1
  132. package/theme/styles/css/color/pink.css +1 -1
  133. package/theme/styles/css/color/purple.css +1 -1
  134. package/theme/styles/css/color/red.css +1 -1
  135. package/theme/styles/css/color/rose.css +1 -1
  136. package/theme/styles/css/color/sky.css +1 -1
  137. package/theme/styles/css/color/teal.css +1 -1
  138. package/theme/styles/css/color/violet.css +1 -1
  139. package/theme/styles/css/color/yellow.css +1 -1
  140. package/theme/styles/css/neutral/gray.css +1 -1
  141. package/theme/styles/css/neutral/mauve.css +1 -1
  142. package/theme/styles/css/neutral/mist.css +1 -1
  143. package/theme/styles/css/neutral/neutral.css +1 -1
  144. package/theme/styles/css/neutral/olive.css +1 -1
  145. package/theme/styles/css/neutral/slate.css +1 -1
  146. package/theme/styles/css/neutral/stone.css +1 -1
  147. package/theme/styles/css/neutral/taupe.css +1 -1
  148. package/theme/styles/css/neutral/zinc.css +1 -1
  149. package/types/ojiepermana-angular-chart-area.d.ts +126 -63
  150. package/types/ojiepermana-angular-chart-bar.d.ts +244 -154
  151. package/types/ojiepermana-angular-chart-core.d.ts +409 -398
  152. package/types/ojiepermana-angular-chart-line.d.ts +141 -68
  153. package/types/ojiepermana-angular-chart-pie.d.ts +192 -121
  154. package/types/ojiepermana-angular-chart-primitives.d.ts +283 -214
  155. package/types/ojiepermana-angular-chart-radar.d.ts +189 -108
  156. package/types/ojiepermana-angular-chart-radial.d.ts +177 -106
  157. package/types/ojiepermana-angular-chart-scatter.d.ts +166 -110
  158. package/types/ojiepermana-angular-chart.d.ts +3 -2
  159. package/types/ojiepermana-angular-component-accordion.d.ts +135 -81
  160. package/types/ojiepermana-angular-component-alert-dialog.d.ts +271 -123
  161. package/types/ojiepermana-angular-component-alert.d.ts +69 -39
  162. package/types/ojiepermana-angular-component-aspect-ratio.d.ts +20 -12
  163. package/types/ojiepermana-angular-component-avatar.d.ts +103 -49
  164. package/types/ojiepermana-angular-component-badge.d.ts +36 -24
  165. package/types/ojiepermana-angular-component-breadcrumb.d.ts +79 -40
  166. package/types/ojiepermana-angular-component-button-group.d.ts +48 -24
  167. package/types/ojiepermana-angular-component-button.d.ts +44 -27
  168. package/types/ojiepermana-angular-component-calendar.d.ts +145 -107
  169. package/types/ojiepermana-angular-component-card.d.ts +112 -63
  170. package/types/ojiepermana-angular-component-carousel.d.ts +150 -81
  171. package/types/ojiepermana-angular-component-checkbox.d.ts +96 -38
  172. package/types/ojiepermana-angular-component-collapsible.d.ts +65 -39
  173. package/types/ojiepermana-angular-component-combobox.d.ts +100 -55
  174. package/types/ojiepermana-angular-component-command.d.ts +206 -121
  175. package/types/ojiepermana-angular-component-composer.d.ts +184 -84
  176. package/types/ojiepermana-angular-component-context-menu.d.ts +51 -41
  177. package/types/ojiepermana-angular-component-date-picker.d.ts +199 -109
  178. package/types/ojiepermana-angular-component-dialog.d.ts +200 -105
  179. package/types/ojiepermana-angular-component-drawer.d.ts +2 -1
  180. package/types/ojiepermana-angular-component-dropdown-menu.d.ts +324 -202
  181. package/types/ojiepermana-angular-component-editor.d.ts +215 -117
  182. package/types/ojiepermana-angular-component-empty.d.ts +80 -45
  183. package/types/ojiepermana-angular-component-form.d.ts +185 -121
  184. package/types/ojiepermana-angular-component-hover-card.d.ts +130 -72
  185. package/types/ojiepermana-angular-component-icon.d.ts +87 -57
  186. package/types/ojiepermana-angular-component-input-group.d.ts +93 -46
  187. package/types/ojiepermana-angular-component-input-otp.d.ts +243 -150
  188. package/types/ojiepermana-angular-component-input.d.ts +27 -17
  189. package/types/ojiepermana-angular-component-item.d.ts +160 -84
  190. package/types/ojiepermana-angular-component-kanban.d.ts +157 -62
  191. package/types/ojiepermana-angular-component-kbd.d.ts +24 -15
  192. package/types/ojiepermana-angular-component-label.d.ts +19 -11
  193. package/types/ojiepermana-angular-component-menubar.d.ts +120 -90
  194. package/types/ojiepermana-angular-component-native-select.d.ts +37 -24
  195. package/types/ojiepermana-angular-component-navigation-menu.d.ts +157 -98
  196. package/types/ojiepermana-angular-component-pagination.d.ts +78 -28
  197. package/types/ojiepermana-angular-component-pillbox.d.ts +361 -204
  198. package/types/ojiepermana-angular-component-popover.d.ts +94 -64
  199. package/types/ojiepermana-angular-component-progress.d.ts +45 -17
  200. package/types/ojiepermana-angular-component-radio.d.ts +129 -53
  201. package/types/ojiepermana-angular-component-resizable.d.ts +160 -112
  202. package/types/ojiepermana-angular-component-scroll-area.d.ts +33 -15
  203. package/types/ojiepermana-angular-component-segmented-control.d.ts +94 -50
  204. package/types/ojiepermana-angular-component-select.d.ts +94 -77
  205. package/types/ojiepermana-angular-component-separator.d.ts +26 -14
  206. package/types/ojiepermana-angular-component-sheet.d.ts +226 -116
  207. package/types/ojiepermana-angular-component-skeleton.d.ts +13 -10
  208. package/types/ojiepermana-angular-component-slider.d.ts +140 -75
  209. package/types/ojiepermana-angular-component-spinner.d.ts +31 -13
  210. package/types/ojiepermana-angular-component-switch.d.ts +97 -40
  211. package/types/ojiepermana-angular-component-table.d.ts +102 -50
  212. package/types/ojiepermana-angular-component-tabs.d.ts +174 -115
  213. package/types/ojiepermana-angular-component-textarea.d.ts +27 -17
  214. package/types/ojiepermana-angular-component-timeline.d.ts +141 -59
  215. package/types/ojiepermana-angular-component-toast.d.ts +51 -55
  216. package/types/ojiepermana-angular-component-toggle-group.d.ts +159 -88
  217. package/types/ojiepermana-angular-component-toggle.d.ts +47 -25
  218. package/types/ojiepermana-angular-component-tooltip.d.ts +222 -132
  219. package/types/ojiepermana-angular-component-utils-core.d.ts +81 -90
  220. package/types/ojiepermana-angular-component-utils-overlay.d.ts +7 -11
  221. package/types/ojiepermana-angular-component-utils-scale.d.ts +20 -12
  222. package/types/ojiepermana-angular-component-utils.d.ts +2 -2
  223. package/types/ojiepermana-angular-component.d.ts +1 -2
  224. package/types/ojiepermana-angular-navigation-core.d.ts +46 -50
  225. package/types/ojiepermana-angular-navigation-icon.d.ts +9 -12
  226. package/types/ojiepermana-angular-navigation-service.d.ts +112 -115
  227. package/types/ojiepermana-angular-navigation-types.d.ts +121 -125
  228. package/types/ojiepermana-angular-navigation.d.ts +663 -374
  229. package/types/ojiepermana-angular-sdk.d.ts +69 -71
  230. package/types/ojiepermana-angular-theme-component-settings.d.ts +115 -83
  231. package/types/ojiepermana-angular-theme-layout-services.d.ts +119 -124
  232. package/types/ojiepermana-angular-theme-layout-types.d.ts +27 -31
  233. package/types/ojiepermana-angular-theme-layout-wrapper.d.ts +399 -187
  234. package/types/ojiepermana-angular-theme-layout.d.ts +201 -113
  235. package/types/ojiepermana-angular-theme-page-core.d.ts +136 -142
  236. package/types/ojiepermana-angular-theme-page-filter.d.ts +112 -62
  237. package/types/ojiepermana-angular-theme-page-overview.d.ts +28 -0
  238. package/types/ojiepermana-angular-theme-page-root.d.ts +172 -88
  239. package/types/ojiepermana-angular-theme-page-side.d.ts +89 -49
  240. package/types/ojiepermana-angular-theme-page-slots.d.ts +58 -37
  241. package/types/ojiepermana-angular-theme-page.d.ts +7 -5
  242. package/types/ojiepermana-angular-theme-shell-services.d.ts +119 -125
  243. package/types/ojiepermana-angular-theme-shell-types.d.ts +38 -41
  244. package/types/ojiepermana-angular-theme-shell.d.ts +139 -61
  245. package/types/ojiepermana-angular-theme-styles.d.ts +184 -196
  246. package/types/ojiepermana-angular-theme.d.ts +1 -2
  247. package/types/ojiepermana-angular.d.ts +5 -5
  248. package/fesm2022/ojiepermana-angular-navigation-navigation-badge-count.component-Be3IqzG4.mjs +0 -91
  249. package/fesm2022/ojiepermana-angular-navigation-navigation-dockbar-menu.component-Bc7DWK3R.mjs +0 -522
  250. package/fesm2022/ojiepermana-angular-navigation-navigation-flyout-menu.component-DA8VPEn7.mjs +0 -404
  251. package/fesm2022/ojiepermana-angular-navigation-navigation-horizontal.component-U2TJTzlW.mjs +0 -466
  252. package/fesm2022/ojiepermana-angular-navigation-ojiepermana-angular-navigation-CIUhmytH.mjs +0 -2668
@@ -1,208 +1,453 @@
1
- import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, ElementRef, input, output, Directive, computed, Component, model, forwardRef, contentChildren, Injector, TemplateRef, ViewContainerRef, DestroyRef, signal } from '@angular/core';
3
- import { cn, injectUniqueId } from '@ojiepermana/angular/component/utils/core';
4
- import { FocusKeyManager } from '@angular/cdk/a11y';
5
- import * as i1 from '@ojiepermana/angular/component/utils/scale';
6
- import { ComponentScaleDirective } from '@ojiepermana/angular/component/utils/scale';
7
- import { Overlay } from '@angular/cdk/overlay';
8
- import { TemplatePortal } from '@angular/cdk/portal';
9
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
10
- import { merge } from 'rxjs';
11
- import { filter } from 'rxjs/operators';
12
- import { connectedPositionFor, oppositeSide } from '@ojiepermana/angular/component/utils/overlay';
13
-
14
- const MENU_OVERLAY = new InjectionToken('MenuOverlayController');
15
- /**
16
- * Marker every `MenuTrigger` provides on its own element.
17
- *
18
- * A submenu is a menu item that also opens a menu. Choosing it must open the
19
- * submenu, not close the menu it lives in, so an item that finds this marker on
20
- * itself skips the close-after-select step.
21
- */
22
- const MENU_SUBMENU_TRIGGER = new InjectionToken('MenuSubmenuTrigger');
23
-
24
- class MenuFocusableItem {
25
- el = inject(ElementRef);
26
- /** Present whenever the item is rendered inside a `MenuTrigger` overlay. */
27
- overlay = inject(MENU_OVERLAY, { optional: true });
28
- /** True when this same element also opens a submenu. */
29
- opensSubmenu = inject(MENU_SUBMENU_TRIGGER, { optional: true, self: true }) ?? false;
30
- disabledInput = input(false, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), alias: 'disabled' });
31
- inset = input(false, /* @ts-ignore */
32
- ...(ngDevMode ? [{ debugName: "inset" }] : /* istanbul ignore next */ []));
33
- class = input('', /* @ts-ignore */
34
- ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
35
- variant = input('default', /* @ts-ignore */
36
- ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
37
- selected = output();
38
- get disabled() {
39
- return this.disabledInput();
40
- }
41
- focus() {
42
- this.el.nativeElement.focus();
43
- }
44
- getLabel() {
45
- return this.el.nativeElement.textContent?.trim() ?? '';
46
- }
47
- buildItemClasses(extraClass = '') {
48
- return cn('relative flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none', 'transition-colors focus:bg-accent focus:text-accent-foreground', 'aria-disabled:pointer-events-none aria-disabled:opacity-50', 'data-[inset=true]:pl-8', 'data-[variant=destructive]:text-destructive', 'data-[variant=destructive]:focus:bg-destructive/10', 'data-[variant=destructive]:focus:text-destructive', extraClass, this.class());
49
- }
50
- /**
51
- * Consume the keystroke that activated the item.
52
- *
53
- * Items render on `button` hosts, and a button turns Enter and Space into an
54
- * activation click of its own. With `(click)` and `(keydown.enter|space)` both
55
- * wired to the same handler that click runs the handler a SECOND time: a
56
- * checkbox toggled on keydown flipped straight back, so `aria-checked` never
57
- * moved, and a plain item emitted `selected` twice whenever the menu was told
58
- * not to close. Preventing the default suppresses the synthesised click, which
59
- * leaves exactly one activation per keystroke on native and non-native hosts
60
- * alike. It also stops Space from scrolling the page behind the menu.
61
- */
62
- emitSelection(event) {
63
- if (this.disabled) {
64
- event.preventDefault();
65
- return false;
66
- }
67
- if (event.type === 'keydown') {
68
- event.preventDefault();
69
- }
70
- this.selected.emit(event);
71
- return true;
72
- }
73
- /**
74
- * Close the menu and return focus to the trigger, which is the menu pattern.
75
- *
76
- * Call this AFTER the item has updated its own state: closing tears down the
77
- * overlay view, and a checkbox or radio that wrote its model afterwards would
78
- * never propagate the change. A consumer that already closes the menu by hand
79
- * is fine, closing twice is a no-op.
80
- */
81
- finishSelection() {
82
- if (this.opensSubmenu) {
83
- return;
84
- }
85
- this.overlay?.closeAfterSelect();
86
- }
87
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuFocusableItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
88
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: MenuFocusableItem, isStandalone: true, inputs: { disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, inset: { classPropertyName: "inset", publicName: "inset", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selected" }, ngImport: i0 });
89
- }
90
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuFocusableItem, decorators: [{
91
- type: Directive
92
- }], propDecorators: { disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], inset: [{ type: i0.Input, args: [{ isSignal: true, alias: "inset", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], selected: [{ type: i0.Output, args: ["selected"] }] } });
93
- class MenuGroupComponent {
94
- class = input('', /* @ts-ignore */
95
- ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
96
- classes = computed(() => cn('block', this.class()), /* @ts-ignore */
97
- ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
98
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
99
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: MenuGroupComponent, isStandalone: true, selector: "MenuGroup", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.role": "\"group\"" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
100
- }
101
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuGroupComponent, decorators: [{
102
- type: Component,
103
- args: [{
104
- selector: 'MenuGroup',
105
- host: {
106
- '[class]': 'classes()',
107
- '[attr.role]': '"group"',
108
- },
109
- template: `<ng-content />`,
110
- }]
111
- }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
112
- class MenuRadioGroupBase {
113
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuRadioGroupBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
114
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: MenuRadioGroupBase, isStandalone: true, ngImport: i0 });
115
- }
116
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuRadioGroupBase, decorators: [{
117
- type: Directive
118
- }] });
119
- class MenuRadioGroupComponent extends MenuRadioGroupBase {
120
- value = model(null, /* @ts-ignore */
121
- ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
122
- class = input('', /* @ts-ignore */
123
- ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
124
- classes = computed(() => cn('block', this.class()), /* @ts-ignore */
125
- ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
126
- isChecked(value) {
127
- return this.value() === value;
128
- }
129
- select(value) {
130
- this.value.set(value);
131
- }
132
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuRadioGroupComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
133
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: MenuRadioGroupComponent, isStandalone: true, selector: "MenuRadioGroup", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class": "classes()", "attr.role": "\"group\"" } }, providers: [
134
- { provide: MenuRadioGroupBase, useExisting: forwardRef(() => MenuRadioGroupComponent) },
135
- ], usesInheritance: true, ngImport: i0, template: `<ng-content />`, isInline: true });
136
- }
137
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuRadioGroupComponent, decorators: [{
138
- type: Component,
139
- args: [{
140
- selector: 'MenuRadioGroup',
141
- providers: [
142
- { provide: MenuRadioGroupBase, useExisting: forwardRef(() => MenuRadioGroupComponent) },
143
- ],
144
- host: {
145
- '[class]': 'classes()',
146
- '[attr.role]': '"group"',
147
- },
148
- template: `<ng-content />`,
149
- }]
150
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
151
- class MenuItemComponent extends MenuFocusableItem {
152
- classes = computed(() => this.buildItemClasses(), /* @ts-ignore */
153
- ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
154
- handleClick(e) {
155
- if (!this.emitSelection(e)) {
156
- return;
157
- }
158
- this.finishSelection();
159
- }
160
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuItemComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
161
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.6", type: MenuItemComponent, isStandalone: true, selector: "MenuItem, button[MenuItem]", host: { attributes: { "tabindex": "-1" }, listeners: { "click": "handleClick($any($event))", "keydown.enter": "handleClick($any($event))", "keydown.space": "handleClick($any($event))" }, properties: { "class": "classes()", "attr.role": "\"menuitem\"", "attr.aria-disabled": "disabled ? \"true\" : null", "attr.data-inset": "inset() ? \"true\" : null", "attr.data-variant": "variant()" } }, providers: [{ provide: MenuFocusableItem, useExisting: forwardRef(() => MenuItemComponent) }], usesInheritance: true, ngImport: i0, template: `<ng-content />`, isInline: true });
162
- }
163
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuItemComponent, decorators: [{
164
- type: Component,
165
- args: [{
166
- selector: 'MenuItem, button[MenuItem]',
167
- providers: [{ provide: MenuFocusableItem, useExisting: forwardRef(() => MenuItemComponent) }],
168
- host: {
169
- '[class]': 'classes()',
170
- '[attr.role]': '"menuitem"',
171
- tabindex: '-1',
172
- '[attr.aria-disabled]': 'disabled ? "true" : null',
173
- '[attr.data-inset]': 'inset() ? "true" : null',
174
- '[attr.data-variant]': 'variant()',
175
- '(click)': 'handleClick($any($event))',
176
- '(keydown.enter)': 'handleClick($any($event))',
177
- '(keydown.space)': 'handleClick($any($event))',
178
- },
179
- template: `<ng-content />`,
180
- }]
181
- }] });
182
- class MenuCheckboxItemComponent extends MenuFocusableItem {
183
- checked = model(false, /* @ts-ignore */
184
- ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
185
- classes = computed(() => this.buildItemClasses('pl-8'), /* @ts-ignore */
186
- ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
187
- // A checkbox item does NOT close the menu, unlike a plain item or a radio.
188
- //
189
- // Ticking boxes is a repeated action by nature -- a column picker, a filter
190
- // list -- and closing after each one turned "hide three columns" into three
191
- // trips through the trigger. Radio stays as it is: choosing one of several
192
- // genuinely ends the interaction, so closing there is right.
193
- //
194
- // Escape, Tab, a click outside, or picking a plain item all still close it, so
195
- // there is no way to get stuck in an open menu.
196
- toggle(event) {
197
- if (!this.emitSelection(event)) {
198
- return;
199
- }
200
- this.checked.set(!this.checked());
201
- }
202
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuCheckboxItemComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
203
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: MenuCheckboxItemComponent, isStandalone: true, selector: "MenuCheckboxItem, button[MenuCheckboxItem]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, host: { attributes: { "tabindex": "-1" }, listeners: { "click": "toggle($any($event))", "keydown.enter": "toggle($any($event))", "keydown.space": "toggle($any($event))" }, properties: { "class": "classes()", "attr.role": "\"menuitemcheckbox\"", "attr.aria-disabled": "disabled ? \"true\" : null", "attr.aria-checked": "checked()", "attr.data-state": "checked() ? \"checked\" : \"unchecked\"", "attr.data-variant": "variant()" } }, providers: [
204
- { provide: MenuFocusableItem, useExisting: forwardRef(() => MenuCheckboxItemComponent) },
205
- ], usesInheritance: true, ngImport: i0, template: `
1
+ import * as i0 from "@angular/core";
2
+ import { Component, DestroyRef, Directive, ElementRef, InjectionToken, Injector, TemplateRef, ViewContainerRef, computed, contentChildren, forwardRef, inject, input, model, output, signal } from "@angular/core";
3
+ import { cn, injectUniqueId } from "@ojiepermana/angular/component/utils/core";
4
+ import { FocusKeyManager } from "@angular/cdk/a11y";
5
+ import * as i1 from "@ojiepermana/angular/component/utils/scale";
6
+ import { ComponentScaleDirective } from "@ojiepermana/angular/component/utils/scale";
7
+ import { Overlay } from "@angular/cdk/overlay";
8
+ import { TemplatePortal } from "@angular/cdk/portal";
9
+ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
10
+ import { merge } from "rxjs";
11
+ import { filter } from "rxjs/operators";
12
+ import { connectedPositionFor, oppositeSide } from "@ojiepermana/angular/component/utils/overlay";
13
+ const MENU_OVERLAY = new InjectionToken("MenuOverlayController");
14
+ const MENU_SUBMENU_TRIGGER = new InjectionToken("MenuSubmenuTrigger");
15
+ var MenuFocusableItem = class MenuFocusableItem {
16
+ el = inject(ElementRef);
17
+ overlay = inject(MENU_OVERLAY, { optional: true });
18
+ opensSubmenu = inject(MENU_SUBMENU_TRIGGER, {
19
+ optional: true,
20
+ self: true
21
+ }) ?? false;
22
+ disabledInput = input(false, {
23
+ ...ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {},
24
+ alias: "disabled"
25
+ });
26
+ inset = input(false, ...ngDevMode ? [{ debugName: "inset" }] : /* istanbul ignore next */ []);
27
+ class = input("", ...ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []);
28
+ variant = input("default", ...ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []);
29
+ selected = output();
30
+ get disabled() {
31
+ return this.disabledInput();
32
+ }
33
+ focus() {
34
+ this.el.nativeElement.focus();
35
+ }
36
+ getLabel() {
37
+ return this.el.nativeElement.textContent?.trim() ?? "";
38
+ }
39
+ buildItemClasses(extraClass = "") {
40
+ return cn("relative flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none", "transition-colors focus:bg-accent focus:text-accent-foreground", "aria-disabled:pointer-events-none aria-disabled:opacity-50", "data-[inset=true]:pl-8", "data-[variant=destructive]:text-destructive", "data-[variant=destructive]:focus:bg-destructive/10", "data-[variant=destructive]:focus:text-destructive", extraClass, this.class());
41
+ }
42
+ emitSelection(event) {
43
+ if (this.disabled) {
44
+ event.preventDefault();
45
+ return false;
46
+ }
47
+ if (event.type === "keydown") event.preventDefault();
48
+ this.selected.emit(event);
49
+ return true;
50
+ }
51
+ finishSelection() {
52
+ if (this.opensSubmenu) return;
53
+ this.overlay?.closeAfterSelect();
54
+ }
55
+ static ɵfac = i0.ɵɵngDeclareFactory({
56
+ minVersion: "12.0.0",
57
+ version: "22.2.0",
58
+ ngImport: i0,
59
+ type: MenuFocusableItem,
60
+ deps: [],
61
+ target: i0.ɵɵFactoryTarget.Directive
62
+ });
63
+ static ɵdir = i0.ɵɵngDeclareDirective({
64
+ minVersion: "17.1.0",
65
+ version: "22.2.0",
66
+ type: MenuFocusableItem,
67
+ isStandalone: true,
68
+ inputs: {
69
+ disabledInput: {
70
+ classPropertyName: "disabledInput",
71
+ publicName: "disabled",
72
+ isSignal: true,
73
+ isRequired: false,
74
+ transformFunction: null
75
+ },
76
+ inset: {
77
+ classPropertyName: "inset",
78
+ publicName: "inset",
79
+ isSignal: true,
80
+ isRequired: false,
81
+ transformFunction: null
82
+ },
83
+ class: {
84
+ classPropertyName: "class",
85
+ publicName: "class",
86
+ isSignal: true,
87
+ isRequired: false,
88
+ transformFunction: null
89
+ },
90
+ variant: {
91
+ classPropertyName: "variant",
92
+ publicName: "variant",
93
+ isSignal: true,
94
+ isRequired: false,
95
+ transformFunction: null
96
+ }
97
+ },
98
+ outputs: { selected: "selected" },
99
+ ngImport: i0
100
+ });
101
+ };
102
+ i0.ɵɵngDeclareClassMetadata({
103
+ minVersion: "12.0.0",
104
+ version: "22.2.0",
105
+ ngImport: i0,
106
+ type: MenuFocusableItem,
107
+ decorators: [{ type: Directive }],
108
+ propDecorators: {
109
+ disabledInput: [{
110
+ type: i0.Input,
111
+ args: [{
112
+ isSignal: true,
113
+ alias: "disabled",
114
+ required: false
115
+ }]
116
+ }],
117
+ inset: [{
118
+ type: i0.Input,
119
+ args: [{
120
+ isSignal: true,
121
+ alias: "inset",
122
+ required: false
123
+ }]
124
+ }],
125
+ class: [{
126
+ type: i0.Input,
127
+ args: [{
128
+ isSignal: true,
129
+ alias: "class",
130
+ required: false
131
+ }]
132
+ }],
133
+ variant: [{
134
+ type: i0.Input,
135
+ args: [{
136
+ isSignal: true,
137
+ alias: "variant",
138
+ required: false
139
+ }]
140
+ }],
141
+ selected: [{
142
+ type: i0.Output,
143
+ args: ["selected"]
144
+ }]
145
+ }
146
+ });
147
+ var MenuGroupComponent = class MenuGroupComponent {
148
+ class = input("", ...ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []);
149
+ classes = computed(() => cn("block", this.class()), ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
150
+ static ɵfac = i0.ɵɵngDeclareFactory({
151
+ minVersion: "12.0.0",
152
+ version: "22.2.0",
153
+ ngImport: i0,
154
+ type: MenuGroupComponent,
155
+ deps: [],
156
+ target: i0.ɵɵFactoryTarget.Component
157
+ });
158
+ static ɵcmp = i0.ɵɵngDeclareComponent({
159
+ minVersion: "17.1.0",
160
+ version: "22.2.0",
161
+ type: MenuGroupComponent,
162
+ isStandalone: true,
163
+ selector: "MenuGroup",
164
+ inputs: { class: {
165
+ classPropertyName: "class",
166
+ publicName: "class",
167
+ isSignal: true,
168
+ isRequired: false,
169
+ transformFunction: null
170
+ } },
171
+ host: { properties: {
172
+ "class": "classes()",
173
+ "attr.role": "\"group\""
174
+ } },
175
+ ngImport: i0,
176
+ template: `<ng-content />`,
177
+ isInline: true
178
+ });
179
+ };
180
+ i0.ɵɵngDeclareClassMetadata({
181
+ minVersion: "12.0.0",
182
+ version: "22.2.0",
183
+ ngImport: i0,
184
+ type: MenuGroupComponent,
185
+ decorators: [{
186
+ type: Component,
187
+ args: [{
188
+ selector: "MenuGroup",
189
+ host: {
190
+ "[class]": "classes()",
191
+ "[attr.role]": "\"group\""
192
+ },
193
+ template: `<ng-content />`
194
+ }]
195
+ }],
196
+ propDecorators: { class: [{
197
+ type: i0.Input,
198
+ args: [{
199
+ isSignal: true,
200
+ alias: "class",
201
+ required: false
202
+ }]
203
+ }] }
204
+ });
205
+ var MenuRadioGroupBase = class MenuRadioGroupBase {
206
+ static ɵfac = i0.ɵɵngDeclareFactory({
207
+ minVersion: "12.0.0",
208
+ version: "22.2.0",
209
+ ngImport: i0,
210
+ type: MenuRadioGroupBase,
211
+ deps: [],
212
+ target: i0.ɵɵFactoryTarget.Directive
213
+ });
214
+ static ɵdir = i0.ɵɵngDeclareDirective({
215
+ minVersion: "14.0.0",
216
+ version: "22.2.0",
217
+ type: MenuRadioGroupBase,
218
+ isStandalone: true,
219
+ ngImport: i0
220
+ });
221
+ };
222
+ i0.ɵɵngDeclareClassMetadata({
223
+ minVersion: "12.0.0",
224
+ version: "22.2.0",
225
+ ngImport: i0,
226
+ type: MenuRadioGroupBase,
227
+ decorators: [{ type: Directive }]
228
+ });
229
+ var MenuRadioGroupComponent = class MenuRadioGroupComponent extends MenuRadioGroupBase {
230
+ value = model(null, ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
231
+ class = input("", ...ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []);
232
+ classes = computed(() => cn("block", this.class()), ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
233
+ isChecked(value) {
234
+ return this.value() === value;
235
+ }
236
+ select(value) {
237
+ this.value.set(value);
238
+ }
239
+ static ɵfac = i0.ɵɵngDeclareFactory({
240
+ minVersion: "12.0.0",
241
+ version: "22.2.0",
242
+ ngImport: i0,
243
+ type: MenuRadioGroupComponent,
244
+ deps: null,
245
+ target: i0.ɵɵFactoryTarget.Component
246
+ });
247
+ static ɵcmp = i0.ɵɵngDeclareComponent({
248
+ minVersion: "17.1.0",
249
+ version: "22.2.0",
250
+ type: MenuRadioGroupComponent,
251
+ isStandalone: true,
252
+ selector: "MenuRadioGroup",
253
+ inputs: {
254
+ value: {
255
+ classPropertyName: "value",
256
+ publicName: "value",
257
+ isSignal: true,
258
+ isRequired: false,
259
+ transformFunction: null
260
+ },
261
+ class: {
262
+ classPropertyName: "class",
263
+ publicName: "class",
264
+ isSignal: true,
265
+ isRequired: false,
266
+ transformFunction: null
267
+ }
268
+ },
269
+ outputs: { value: "valueChange" },
270
+ host: { properties: {
271
+ "class": "classes()",
272
+ "attr.role": "\"group\""
273
+ } },
274
+ providers: [{
275
+ provide: MenuRadioGroupBase,
276
+ useExisting: forwardRef(() => MenuRadioGroupComponent)
277
+ }],
278
+ usesInheritance: true,
279
+ ngImport: i0,
280
+ template: `<ng-content />`,
281
+ isInline: true
282
+ });
283
+ };
284
+ i0.ɵɵngDeclareClassMetadata({
285
+ minVersion: "12.0.0",
286
+ version: "22.2.0",
287
+ ngImport: i0,
288
+ type: MenuRadioGroupComponent,
289
+ decorators: [{
290
+ type: Component,
291
+ args: [{
292
+ selector: "MenuRadioGroup",
293
+ providers: [{
294
+ provide: MenuRadioGroupBase,
295
+ useExisting: forwardRef(() => MenuRadioGroupComponent)
296
+ }],
297
+ host: {
298
+ "[class]": "classes()",
299
+ "[attr.role]": "\"group\""
300
+ },
301
+ template: `<ng-content />`
302
+ }]
303
+ }],
304
+ propDecorators: {
305
+ value: [{
306
+ type: i0.Input,
307
+ args: [{
308
+ isSignal: true,
309
+ alias: "value",
310
+ required: false
311
+ }]
312
+ }, {
313
+ type: i0.Output,
314
+ args: ["valueChange"]
315
+ }],
316
+ class: [{
317
+ type: i0.Input,
318
+ args: [{
319
+ isSignal: true,
320
+ alias: "class",
321
+ required: false
322
+ }]
323
+ }]
324
+ }
325
+ });
326
+ var MenuItemComponent = class MenuItemComponent extends MenuFocusableItem {
327
+ classes = computed(() => this.buildItemClasses(), ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
328
+ handleClick(e) {
329
+ if (!this.emitSelection(e)) return;
330
+ this.finishSelection();
331
+ }
332
+ static ɵfac = i0.ɵɵngDeclareFactory({
333
+ minVersion: "12.0.0",
334
+ version: "22.2.0",
335
+ ngImport: i0,
336
+ type: MenuItemComponent,
337
+ deps: null,
338
+ target: i0.ɵɵFactoryTarget.Component
339
+ });
340
+ static ɵcmp = i0.ɵɵngDeclareComponent({
341
+ minVersion: "14.0.0",
342
+ version: "22.2.0",
343
+ type: MenuItemComponent,
344
+ isStandalone: true,
345
+ selector: "MenuItem, button[MenuItem]",
346
+ host: {
347
+ attributes: { "tabindex": "-1" },
348
+ listeners: {
349
+ "click": "handleClick($any($event))",
350
+ "keydown.enter": "handleClick($any($event))",
351
+ "keydown.space": "handleClick($any($event))"
352
+ },
353
+ properties: {
354
+ "class": "classes()",
355
+ "attr.role": "\"menuitem\"",
356
+ "attr.aria-disabled": "disabled ? \"true\" : null",
357
+ "attr.data-inset": "inset() ? \"true\" : null",
358
+ "attr.data-variant": "variant()"
359
+ }
360
+ },
361
+ providers: [{
362
+ provide: MenuFocusableItem,
363
+ useExisting: forwardRef(() => MenuItemComponent)
364
+ }],
365
+ usesInheritance: true,
366
+ ngImport: i0,
367
+ template: `<ng-content />`,
368
+ isInline: true
369
+ });
370
+ };
371
+ i0.ɵɵngDeclareClassMetadata({
372
+ minVersion: "12.0.0",
373
+ version: "22.2.0",
374
+ ngImport: i0,
375
+ type: MenuItemComponent,
376
+ decorators: [{
377
+ type: Component,
378
+ args: [{
379
+ selector: "MenuItem, button[MenuItem]",
380
+ providers: [{
381
+ provide: MenuFocusableItem,
382
+ useExisting: forwardRef(() => MenuItemComponent)
383
+ }],
384
+ host: {
385
+ "[class]": "classes()",
386
+ "[attr.role]": "\"menuitem\"",
387
+ tabindex: "-1",
388
+ "[attr.aria-disabled]": "disabled ? \"true\" : null",
389
+ "[attr.data-inset]": "inset() ? \"true\" : null",
390
+ "[attr.data-variant]": "variant()",
391
+ "(click)": "handleClick($any($event))",
392
+ "(keydown.enter)": "handleClick($any($event))",
393
+ "(keydown.space)": "handleClick($any($event))"
394
+ },
395
+ template: `<ng-content />`
396
+ }]
397
+ }]
398
+ });
399
+ var MenuCheckboxItemComponent = class MenuCheckboxItemComponent extends MenuFocusableItem {
400
+ checked = model(false, ...ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []);
401
+ classes = computed(() => this.buildItemClasses("pl-8"), ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
402
+ toggle(event) {
403
+ if (!this.emitSelection(event)) return;
404
+ this.checked.set(!this.checked());
405
+ }
406
+ static ɵfac = i0.ɵɵngDeclareFactory({
407
+ minVersion: "12.0.0",
408
+ version: "22.2.0",
409
+ ngImport: i0,
410
+ type: MenuCheckboxItemComponent,
411
+ deps: null,
412
+ target: i0.ɵɵFactoryTarget.Component
413
+ });
414
+ static ɵcmp = i0.ɵɵngDeclareComponent({
415
+ minVersion: "17.0.0",
416
+ version: "22.2.0",
417
+ type: MenuCheckboxItemComponent,
418
+ isStandalone: true,
419
+ selector: "MenuCheckboxItem, button[MenuCheckboxItem]",
420
+ inputs: { checked: {
421
+ classPropertyName: "checked",
422
+ publicName: "checked",
423
+ isSignal: true,
424
+ isRequired: false,
425
+ transformFunction: null
426
+ } },
427
+ outputs: { checked: "checkedChange" },
428
+ host: {
429
+ attributes: { "tabindex": "-1" },
430
+ listeners: {
431
+ "click": "toggle($any($event))",
432
+ "keydown.enter": "toggle($any($event))",
433
+ "keydown.space": "toggle($any($event))"
434
+ },
435
+ properties: {
436
+ "class": "classes()",
437
+ "attr.role": "\"menuitemcheckbox\"",
438
+ "attr.aria-disabled": "disabled ? \"true\" : null",
439
+ "attr.aria-checked": "checked()",
440
+ "attr.data-state": "checked() ? \"checked\" : \"unchecked\"",
441
+ "attr.data-variant": "variant()"
442
+ }
443
+ },
444
+ providers: [{
445
+ provide: MenuFocusableItem,
446
+ useExisting: forwardRef(() => MenuCheckboxItemComponent)
447
+ }],
448
+ usesInheritance: true,
449
+ ngImport: i0,
450
+ template: `
206
451
  <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
207
452
  @if (checked()) {
208
453
  <svg
@@ -220,28 +465,36 @@ class MenuCheckboxItemComponent extends MenuFocusableItem {
220
465
  }
221
466
  </span>
222
467
  <ng-content />
223
- `, isInline: true });
224
- }
225
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuCheckboxItemComponent, decorators: [{
226
- type: Component,
227
- args: [{
228
- selector: 'MenuCheckboxItem, button[MenuCheckboxItem]',
229
- providers: [
230
- { provide: MenuFocusableItem, useExisting: forwardRef(() => MenuCheckboxItemComponent) },
231
- ],
232
- host: {
233
- '[class]': 'classes()',
234
- '[attr.role]': '"menuitemcheckbox"',
235
- tabindex: '-1',
236
- '[attr.aria-disabled]': 'disabled ? "true" : null',
237
- '[attr.aria-checked]': 'checked()',
238
- '[attr.data-state]': 'checked() ? "checked" : "unchecked"',
239
- '[attr.data-variant]': 'variant()',
240
- '(click)': 'toggle($any($event))',
241
- '(keydown.enter)': 'toggle($any($event))',
242
- '(keydown.space)': 'toggle($any($event))',
243
- },
244
- template: `
468
+ `,
469
+ isInline: true
470
+ });
471
+ };
472
+ i0.ɵɵngDeclareClassMetadata({
473
+ minVersion: "12.0.0",
474
+ version: "22.2.0",
475
+ ngImport: i0,
476
+ type: MenuCheckboxItemComponent,
477
+ decorators: [{
478
+ type: Component,
479
+ args: [{
480
+ selector: "MenuCheckboxItem, button[MenuCheckboxItem]",
481
+ providers: [{
482
+ provide: MenuFocusableItem,
483
+ useExisting: forwardRef(() => MenuCheckboxItemComponent)
484
+ }],
485
+ host: {
486
+ "[class]": "classes()",
487
+ "[attr.role]": "\"menuitemcheckbox\"",
488
+ tabindex: "-1",
489
+ "[attr.aria-disabled]": "disabled ? \"true\" : null",
490
+ "[attr.aria-checked]": "checked()",
491
+ "[attr.data-state]": "checked() ? \"checked\" : \"unchecked\"",
492
+ "[attr.data-variant]": "variant()",
493
+ "(click)": "toggle($any($event))",
494
+ "(keydown.enter)": "toggle($any($event))",
495
+ "(keydown.space)": "toggle($any($event))"
496
+ },
497
+ template: `
245
498
  <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
246
499
  @if (checked()) {
247
500
  <svg
@@ -259,435 +512,748 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
259
512
  }
260
513
  </span>
261
514
  <ng-content />
262
- `,
263
- }]
264
- }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }] } });
265
- class MenuRadioItemComponent extends MenuFocusableItem {
266
- group = inject(MenuRadioGroupBase);
267
- value = input.required(/* @ts-ignore */
268
- ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
269
- checked = computed(() => this.group.isChecked(this.value()), /* @ts-ignore */
270
- ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
271
- classes = computed(() => this.buildItemClasses('pl-8'), /* @ts-ignore */
272
- ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
273
- select(event) {
274
- if (!this.emitSelection(event)) {
275
- return;
276
- }
277
- this.group.select(this.value());
278
- this.finishSelection();
279
- }
280
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuRadioItemComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
281
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: MenuRadioItemComponent, isStandalone: true, selector: "MenuRadioItem, button[MenuRadioItem]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "tabindex": "-1" }, listeners: { "click": "select($any($event))", "keydown.enter": "select($any($event))", "keydown.space": "select($any($event))" }, properties: { "class": "classes()", "attr.role": "\"menuitemradio\"", "attr.aria-disabled": "disabled ? \"true\" : null", "attr.aria-checked": "checked()", "attr.data-state": "checked() ? \"checked\" : \"unchecked\"", "attr.data-variant": "variant()" } }, providers: [
282
- { provide: MenuFocusableItem, useExisting: forwardRef(() => MenuRadioItemComponent) },
283
- ], usesInheritance: true, ngImport: i0, template: `
515
+ `
516
+ }]
517
+ }],
518
+ propDecorators: { checked: [{
519
+ type: i0.Input,
520
+ args: [{
521
+ isSignal: true,
522
+ alias: "checked",
523
+ required: false
524
+ }]
525
+ }, {
526
+ type: i0.Output,
527
+ args: ["checkedChange"]
528
+ }] }
529
+ });
530
+ var MenuRadioItemComponent = class MenuRadioItemComponent extends MenuFocusableItem {
531
+ group = inject(MenuRadioGroupBase);
532
+ value = input.required(...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
533
+ checked = computed(() => this.group.isChecked(this.value()), ...ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []);
534
+ classes = computed(() => this.buildItemClasses("pl-8"), ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
535
+ select(event) {
536
+ if (!this.emitSelection(event)) return;
537
+ this.group.select(this.value());
538
+ this.finishSelection();
539
+ }
540
+ static ɵfac = i0.ɵɵngDeclareFactory({
541
+ minVersion: "12.0.0",
542
+ version: "22.2.0",
543
+ ngImport: i0,
544
+ type: MenuRadioItemComponent,
545
+ deps: null,
546
+ target: i0.ɵɵFactoryTarget.Component
547
+ });
548
+ static ɵcmp = i0.ɵɵngDeclareComponent({
549
+ minVersion: "17.0.0",
550
+ version: "22.2.0",
551
+ type: MenuRadioItemComponent,
552
+ isStandalone: true,
553
+ selector: "MenuRadioItem, button[MenuRadioItem]",
554
+ inputs: { value: {
555
+ classPropertyName: "value",
556
+ publicName: "value",
557
+ isSignal: true,
558
+ isRequired: true,
559
+ transformFunction: null
560
+ } },
561
+ host: {
562
+ attributes: { "tabindex": "-1" },
563
+ listeners: {
564
+ "click": "select($any($event))",
565
+ "keydown.enter": "select($any($event))",
566
+ "keydown.space": "select($any($event))"
567
+ },
568
+ properties: {
569
+ "class": "classes()",
570
+ "attr.role": "\"menuitemradio\"",
571
+ "attr.aria-disabled": "disabled ? \"true\" : null",
572
+ "attr.aria-checked": "checked()",
573
+ "attr.data-state": "checked() ? \"checked\" : \"unchecked\"",
574
+ "attr.data-variant": "variant()"
575
+ }
576
+ },
577
+ providers: [{
578
+ provide: MenuFocusableItem,
579
+ useExisting: forwardRef(() => MenuRadioItemComponent)
580
+ }],
581
+ usesInheritance: true,
582
+ ngImport: i0,
583
+ template: `
284
584
  <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
285
585
  @if (checked()) {
286
586
  <span aria-hidden="true" class="h-2 w-2 rounded-full bg-current"></span>
287
587
  }
288
588
  </span>
289
589
  <ng-content />
290
- `, isInline: true });
291
- }
292
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuRadioItemComponent, decorators: [{
293
- type: Component,
294
- args: [{
295
- selector: 'MenuRadioItem, button[MenuRadioItem]',
296
- providers: [
297
- { provide: MenuFocusableItem, useExisting: forwardRef(() => MenuRadioItemComponent) },
298
- ],
299
- host: {
300
- '[class]': 'classes()',
301
- '[attr.role]': '"menuitemradio"',
302
- tabindex: '-1',
303
- '[attr.aria-disabled]': 'disabled ? "true" : null',
304
- '[attr.aria-checked]': 'checked()',
305
- '[attr.data-state]': 'checked() ? "checked" : "unchecked"',
306
- '[attr.data-variant]': 'variant()',
307
- '(click)': 'select($any($event))',
308
- '(keydown.enter)': 'select($any($event))',
309
- '(keydown.space)': 'select($any($event))',
310
- },
311
- template: `
590
+ `,
591
+ isInline: true
592
+ });
593
+ };
594
+ i0.ɵɵngDeclareClassMetadata({
595
+ minVersion: "12.0.0",
596
+ version: "22.2.0",
597
+ ngImport: i0,
598
+ type: MenuRadioItemComponent,
599
+ decorators: [{
600
+ type: Component,
601
+ args: [{
602
+ selector: "MenuRadioItem, button[MenuRadioItem]",
603
+ providers: [{
604
+ provide: MenuFocusableItem,
605
+ useExisting: forwardRef(() => MenuRadioItemComponent)
606
+ }],
607
+ host: {
608
+ "[class]": "classes()",
609
+ "[attr.role]": "\"menuitemradio\"",
610
+ tabindex: "-1",
611
+ "[attr.aria-disabled]": "disabled ? \"true\" : null",
612
+ "[attr.aria-checked]": "checked()",
613
+ "[attr.data-state]": "checked() ? \"checked\" : \"unchecked\"",
614
+ "[attr.data-variant]": "variant()",
615
+ "(click)": "select($any($event))",
616
+ "(keydown.enter)": "select($any($event))",
617
+ "(keydown.space)": "select($any($event))"
618
+ },
619
+ template: `
312
620
  <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
313
621
  @if (checked()) {
314
622
  <span aria-hidden="true" class="h-2 w-2 rounded-full bg-current"></span>
315
623
  }
316
624
  </span>
317
625
  <ng-content />
318
- `,
319
- }]
320
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }] } });
321
- class MenuSeparatorComponent {
322
- class = input('', /* @ts-ignore */
323
- ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
324
- classes = computed(() => cn('-mx-1 my-1 block h-px bg-border', this.class()), /* @ts-ignore */
325
- ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
326
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuSeparatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
327
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: MenuSeparatorComponent, isStandalone: true, selector: "MenuSeparator", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.role": "\"separator\"", "attr.aria-orientation": "\"horizontal\"" } }, ngImport: i0, template: '', isInline: true });
328
- }
329
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuSeparatorComponent, decorators: [{
330
- type: Component,
331
- args: [{
332
- selector: 'MenuSeparator',
333
- host: {
334
- '[class]': 'classes()',
335
- '[attr.role]': '"separator"',
336
- '[attr.aria-orientation]': '"horizontal"',
337
- },
338
- template: '',
339
- }]
340
- }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
341
- class MenuLabelComponent {
342
- inset = input(false, /* @ts-ignore */
343
- ...(ngDevMode ? [{ debugName: "inset" }] : /* istanbul ignore next */ []));
344
- class = input('', /* @ts-ignore */
345
- ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
346
- classes = computed(() => cn('px-2 py-1.5 text-sm font-semibold text-foreground', this.inset() ? 'pl-8' : '', this.class()), /* @ts-ignore */
347
- ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
348
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuLabelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
349
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: MenuLabelComponent, isStandalone: true, selector: "MenuLabel", inputs: { inset: { classPropertyName: "inset", publicName: "inset", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
350
- }
351
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuLabelComponent, decorators: [{
352
- type: Component,
353
- args: [{
354
- selector: 'MenuLabel',
355
- host: { '[class]': 'classes()' },
356
- template: `<ng-content />`,
357
- }]
358
- }], propDecorators: { inset: [{ type: i0.Input, args: [{ isSignal: true, alias: "inset", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
359
- class MenuShortcutComponent {
360
- class = input('', /* @ts-ignore */
361
- ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
362
- classes = computed(() => cn('ml-auto text-xs tracking-widest text-muted-foreground', this.class()), /* @ts-ignore */
363
- ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
364
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuShortcutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
365
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: MenuShortcutComponent, isStandalone: true, selector: "MenuShortcut, span[MenuShortcut]", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
366
- }
367
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuShortcutComponent, decorators: [{
368
- type: Component,
369
- args: [{
370
- selector: 'MenuShortcut, span[MenuShortcut]',
371
- host: { '[class]': 'classes()' },
372
- template: `<ng-content />`,
373
- }]
374
- }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
375
-
376
- /**
377
- * Surface rendered inside the overlay. Hosts keyboard navigation for its items.
378
- */
379
- class MenuSurfaceComponent {
380
- class = input('', /* @ts-ignore */
381
- ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
382
- items = contentChildren(MenuFocusableItem, { ...(ngDevMode ? { debugName: "items" } : /* istanbul ignore next */ {}), descendants: true });
383
- /** Present whenever the surface is rendered by a `MenuTrigger`. */
384
- overlay = inject(MENU_OVERLAY, { optional: true });
385
- /**
386
- * Emitted when the surface asks to be closed (today: on Tab). The trigger now
387
- * closes itself, so consumers no longer have to wire this up; it stays for
388
- * anyone who already listens to it.
389
- */
390
- closeRequested = output();
391
- /** Tied to the trigger's `aria-controls`, so the pair references each other. */
392
- surfaceId = this.overlay?.surfaceId ?? injectUniqueId('MenuSurface');
393
- /** Names the menu after the control that opened it. */
394
- labelledBy = this.overlay?.triggerId ?? null;
395
- keyManager = new FocusKeyManager(this.items, inject(Injector))
396
- .withWrap()
397
- .withTypeAhead()
398
- .skipPredicate((item) => item.disabled);
399
- classes = computed(() => cn('z-50 min-w-32 overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none', this.class()), /* @ts-ignore */
400
- ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
401
- ngAfterContentInit() {
402
- // Focus the first enabled item when the menu opens.
403
- queueMicrotask(() => this.keyManager.setFirstItemActive());
404
- }
405
- onArrow(e, delta) {
406
- e.preventDefault();
407
- this.keyManager.cancelTypeahead();
408
- if (delta > 0) {
409
- this.keyManager.setNextItemActive();
410
- }
411
- else {
412
- this.keyManager.setPreviousItemActive();
413
- }
414
- }
415
- // ArrowLeft closes a SUBMENU and hands focus back to the item that opened it,
416
- // which is the menu pattern.
417
- //
418
- // A root menu leaves the key alone. The row of menus above it owns left and
419
- // right there: a `Menubar` listens for them on its own overlay to walk to the
420
- // neighbouring menu, and it only ever sees the key because this surface let it
421
- // carry on bubbling.
422
- onArrowLeft(e) {
423
- if (!this.overlay?.isSubmenu) {
424
- return;
425
- }
426
- e.preventDefault();
427
- this.overlay.close();
428
- }
429
- onHome(e) {
430
- e.preventDefault();
431
- this.keyManager.cancelTypeahead();
432
- this.keyManager.setFirstItemActive();
433
- }
434
- onEnd(e) {
435
- e.preventDefault();
436
- this.keyManager.cancelTypeahead();
437
- this.keyManager.setLastItemActive();
438
- }
439
- // Routes printable characters into the key manager's type-ahead buffer, which
440
- // is the only thing that fills it: `withTypeAhead()` above configures the
441
- // buffer, but nothing reaches it unless `onKeydown` is called.
442
- //
443
- // Only printable characters are forwarded, and Space is not one of them here.
444
- // Every other key already has an owner -- the arrows and Home/End above, Enter
445
- // and Space on the item, Tab on the trigger -- and handing those to the key
446
- // manager as well would run two handlers for one keystroke.
447
- //
448
- // Space belongs to the item unconditionally. Letting it extend a search in
449
- // progress, so that a two-word label could be matched past its first word,
450
- // would mean intercepting the key BEFORE the focused item acts on it: this
451
- // listener runs on the bubbling phase, by which point `(keydown.space)` on the
452
- // item has already activated it and closed the menu. Reaching it would take a
453
- // capture-phase listener that swallows Space, which is a lot of machinery, and
454
- // a way to activate an item that stops working mid-search, for a matching
455
- // nicety the menu contract never asked for.
456
- onTypeAhead(e) {
457
- if (e.key.length !== 1 || e.key === ' ' || e.ctrlKey || e.metaKey || e.altKey) {
458
- return;
459
- }
460
- this.keyManager.onKeydown(e);
461
- }
462
- onTab(e) {
463
- // No preventDefault: the menu closes, focus goes back to the trigger, and
464
- // the browser carries on with the tab sequence from there.
465
- void e;
466
- this.closeRequested.emit();
467
- this.overlay?.close();
468
- }
469
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuSurfaceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
470
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.6", type: MenuSurfaceComponent, isStandalone: true, selector: "MenuSurface", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closeRequested: "closeRequested" }, host: { attributes: { "tabindex": "-1" }, listeners: { "keydown.arrowDown": "onArrow($any($event), 1)", "keydown.arrowUp": "onArrow($any($event), -1)", "keydown.arrowLeft": "onArrowLeft($any($event))", "keydown.home": "onHome($any($event))", "keydown.end": "onEnd($any($event))", "keydown.tab": "onTab($any($event))", "keydown": "onTypeAhead($any($event))" }, properties: { "class": "classes()", "attr.role": "\"menu\"", "attr.id": "surfaceId", "attr.aria-labelledby": "labelledBy" } }, queries: [{ propertyName: "items", predicate: MenuFocusableItem, descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.ComponentScaleDirective, inputs: ["radius", "radius", "density", "density"] }], ngImport: i0, template: `<ng-content />`, isInline: true });
471
- }
472
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuSurfaceComponent, decorators: [{
473
- type: Component,
474
- args: [{
475
- selector: 'MenuSurface',
476
- hostDirectives: [
477
- {
478
- directive: ComponentScaleDirective,
479
- inputs: ['radius', 'density'],
480
- },
481
- ],
482
- host: {
483
- '[class]': 'classes()',
484
- '[attr.role]': '"menu"',
485
- '[attr.id]': 'surfaceId',
486
- '[attr.aria-labelledby]': 'labelledBy',
487
- tabindex: '-1',
488
- '(keydown.arrowDown)': 'onArrow($any($event), 1)',
489
- '(keydown.arrowUp)': 'onArrow($any($event), -1)',
490
- '(keydown.arrowLeft)': 'onArrowLeft($any($event))',
491
- '(keydown.home)': 'onHome($any($event))',
492
- '(keydown.end)': 'onEnd($any($event))',
493
- '(keydown.tab)': 'onTab($any($event))',
494
- '(keydown)': 'onTypeAhead($any($event))',
495
- },
496
- template: `<ng-content />`,
497
- }]
498
- }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MenuFocusableItem), { ...{ descendants: true }, isSignal: true }] }], closeRequested: [{ type: i0.Output, args: ["closeRequested"] }] } });
499
- /**
500
- * Wraps an `<ng-template>` that holds `<MenuSurface>` + items.
501
- */
502
- class MenuContentDirective {
503
- componentScale = inject(ComponentScaleDirective);
504
- radius = this.componentScale.radius;
505
- density = this.componentScale.density;
506
- template = inject(TemplateRef);
507
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
508
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: MenuContentDirective, isStandalone: true, selector: "ng-template[MenuContent]", exportAs: ["MenuContent"], hostDirectives: [{ directive: i1.ComponentScaleDirective, inputs: ["radius", "radius", "density", "density"] }], ngImport: i0 });
509
- }
510
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuContentDirective, decorators: [{
511
- type: Directive,
512
- args: [{
513
- selector: 'ng-template[MenuContent]',
514
- exportAs: 'MenuContent',
515
- hostDirectives: [
516
- {
517
- directive: ComponentScaleDirective,
518
- inputs: ['radius', 'density'],
519
- },
520
- ],
521
- }]
522
- }] });
523
-
524
- /**
525
- * Zero, because the gap between a trigger and its menu is NOT a position offset.
526
- *
527
- * CDK applies `offsetX`/`offsetY` as a `transform` on the pane, and it does so
528
- * AFTER clamping the pane to the viewport -- so an offset pushes the panel back
529
- * off screen by exactly its own size, which is how the panel's top edge ended up
530
- * at minus four on a phone. The gap is transparent padding on the pane instead
531
- * (`.menu-panel` in the theme's global layer): padding is part of the box CDK
532
- * measures, so the clamp accounts for it.
533
- */
626
+ `
627
+ }]
628
+ }],
629
+ propDecorators: { value: [{
630
+ type: i0.Input,
631
+ args: [{
632
+ isSignal: true,
633
+ alias: "value",
634
+ required: true
635
+ }]
636
+ }] }
637
+ });
638
+ var MenuSeparatorComponent = class MenuSeparatorComponent {
639
+ class = input("", ...ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []);
640
+ classes = computed(() => cn("-mx-1 my-1 block h-px bg-border", this.class()), ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
641
+ static ɵfac = i0.ɵɵngDeclareFactory({
642
+ minVersion: "12.0.0",
643
+ version: "22.2.0",
644
+ ngImport: i0,
645
+ type: MenuSeparatorComponent,
646
+ deps: [],
647
+ target: i0.ɵɵFactoryTarget.Component
648
+ });
649
+ static ɵcmp = i0.ɵɵngDeclareComponent({
650
+ minVersion: "17.1.0",
651
+ version: "22.2.0",
652
+ type: MenuSeparatorComponent,
653
+ isStandalone: true,
654
+ selector: "MenuSeparator",
655
+ inputs: { class: {
656
+ classPropertyName: "class",
657
+ publicName: "class",
658
+ isSignal: true,
659
+ isRequired: false,
660
+ transformFunction: null
661
+ } },
662
+ host: { properties: {
663
+ "class": "classes()",
664
+ "attr.role": "\"separator\"",
665
+ "attr.aria-orientation": "\"horizontal\""
666
+ } },
667
+ ngImport: i0,
668
+ template: "",
669
+ isInline: true
670
+ });
671
+ };
672
+ i0.ɵɵngDeclareClassMetadata({
673
+ minVersion: "12.0.0",
674
+ version: "22.2.0",
675
+ ngImport: i0,
676
+ type: MenuSeparatorComponent,
677
+ decorators: [{
678
+ type: Component,
679
+ args: [{
680
+ selector: "MenuSeparator",
681
+ host: {
682
+ "[class]": "classes()",
683
+ "[attr.role]": "\"separator\"",
684
+ "[attr.aria-orientation]": "\"horizontal\""
685
+ },
686
+ template: ""
687
+ }]
688
+ }],
689
+ propDecorators: { class: [{
690
+ type: i0.Input,
691
+ args: [{
692
+ isSignal: true,
693
+ alias: "class",
694
+ required: false
695
+ }]
696
+ }] }
697
+ });
698
+ var MenuLabelComponent = class MenuLabelComponent {
699
+ inset = input(false, ...ngDevMode ? [{ debugName: "inset" }] : /* istanbul ignore next */ []);
700
+ class = input("", ...ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []);
701
+ classes = computed(() => cn("px-2 py-1.5 text-sm font-semibold text-foreground", this.inset() ? "pl-8" : "", this.class()), ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
702
+ static ɵfac = i0.ɵɵngDeclareFactory({
703
+ minVersion: "12.0.0",
704
+ version: "22.2.0",
705
+ ngImport: i0,
706
+ type: MenuLabelComponent,
707
+ deps: [],
708
+ target: i0.ɵɵFactoryTarget.Component
709
+ });
710
+ static ɵcmp = i0.ɵɵngDeclareComponent({
711
+ minVersion: "17.1.0",
712
+ version: "22.2.0",
713
+ type: MenuLabelComponent,
714
+ isStandalone: true,
715
+ selector: "MenuLabel",
716
+ inputs: {
717
+ inset: {
718
+ classPropertyName: "inset",
719
+ publicName: "inset",
720
+ isSignal: true,
721
+ isRequired: false,
722
+ transformFunction: null
723
+ },
724
+ class: {
725
+ classPropertyName: "class",
726
+ publicName: "class",
727
+ isSignal: true,
728
+ isRequired: false,
729
+ transformFunction: null
730
+ }
731
+ },
732
+ host: { properties: { "class": "classes()" } },
733
+ ngImport: i0,
734
+ template: `<ng-content />`,
735
+ isInline: true
736
+ });
737
+ };
738
+ i0.ɵɵngDeclareClassMetadata({
739
+ minVersion: "12.0.0",
740
+ version: "22.2.0",
741
+ ngImport: i0,
742
+ type: MenuLabelComponent,
743
+ decorators: [{
744
+ type: Component,
745
+ args: [{
746
+ selector: "MenuLabel",
747
+ host: { "[class]": "classes()" },
748
+ template: `<ng-content />`
749
+ }]
750
+ }],
751
+ propDecorators: {
752
+ inset: [{
753
+ type: i0.Input,
754
+ args: [{
755
+ isSignal: true,
756
+ alias: "inset",
757
+ required: false
758
+ }]
759
+ }],
760
+ class: [{
761
+ type: i0.Input,
762
+ args: [{
763
+ isSignal: true,
764
+ alias: "class",
765
+ required: false
766
+ }]
767
+ }]
768
+ }
769
+ });
770
+ var MenuShortcutComponent = class MenuShortcutComponent {
771
+ class = input("", ...ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []);
772
+ classes = computed(() => cn("ml-auto text-xs tracking-widest text-muted-foreground", this.class()), ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
773
+ static ɵfac = i0.ɵɵngDeclareFactory({
774
+ minVersion: "12.0.0",
775
+ version: "22.2.0",
776
+ ngImport: i0,
777
+ type: MenuShortcutComponent,
778
+ deps: [],
779
+ target: i0.ɵɵFactoryTarget.Component
780
+ });
781
+ static ɵcmp = i0.ɵɵngDeclareComponent({
782
+ minVersion: "17.1.0",
783
+ version: "22.2.0",
784
+ type: MenuShortcutComponent,
785
+ isStandalone: true,
786
+ selector: "MenuShortcut, span[MenuShortcut]",
787
+ inputs: { class: {
788
+ classPropertyName: "class",
789
+ publicName: "class",
790
+ isSignal: true,
791
+ isRequired: false,
792
+ transformFunction: null
793
+ } },
794
+ host: { properties: { "class": "classes()" } },
795
+ ngImport: i0,
796
+ template: `<ng-content />`,
797
+ isInline: true
798
+ });
799
+ };
800
+ i0.ɵɵngDeclareClassMetadata({
801
+ minVersion: "12.0.0",
802
+ version: "22.2.0",
803
+ ngImport: i0,
804
+ type: MenuShortcutComponent,
805
+ decorators: [{
806
+ type: Component,
807
+ args: [{
808
+ selector: "MenuShortcut, span[MenuShortcut]",
809
+ host: { "[class]": "classes()" },
810
+ template: `<ng-content />`
811
+ }]
812
+ }],
813
+ propDecorators: { class: [{
814
+ type: i0.Input,
815
+ args: [{
816
+ isSignal: true,
817
+ alias: "class",
818
+ required: false
819
+ }]
820
+ }] }
821
+ });
822
+ var MenuSurfaceComponent = class MenuSurfaceComponent {
823
+ class = input("", ...ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []);
824
+ items = contentChildren(MenuFocusableItem, {
825
+ ...ngDevMode ? { debugName: "items" } : /* istanbul ignore next */ {},
826
+ descendants: true
827
+ });
828
+ overlay = inject(MENU_OVERLAY, { optional: true });
829
+ closeRequested = output();
830
+ surfaceId = this.overlay?.surfaceId ?? injectUniqueId("MenuSurface");
831
+ labelledBy = this.overlay?.triggerId ?? null;
832
+ keyManager = new FocusKeyManager(this.items, inject(Injector)).withWrap().withTypeAhead().skipPredicate((item) => item.disabled);
833
+ classes = computed(() => cn("z-50 min-w-32 overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none", this.class()), ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
834
+ ngAfterContentInit() {
835
+ queueMicrotask(() => this.keyManager.setFirstItemActive());
836
+ }
837
+ onArrow(e, delta) {
838
+ e.preventDefault();
839
+ this.keyManager.cancelTypeahead();
840
+ if (delta > 0) this.keyManager.setNextItemActive();
841
+ else this.keyManager.setPreviousItemActive();
842
+ }
843
+ onArrowLeft(e) {
844
+ if (!this.overlay?.isSubmenu) return;
845
+ e.preventDefault();
846
+ this.overlay.close();
847
+ }
848
+ onHome(e) {
849
+ e.preventDefault();
850
+ this.keyManager.cancelTypeahead();
851
+ this.keyManager.setFirstItemActive();
852
+ }
853
+ onEnd(e) {
854
+ e.preventDefault();
855
+ this.keyManager.cancelTypeahead();
856
+ this.keyManager.setLastItemActive();
857
+ }
858
+ onTypeAhead(e) {
859
+ if (e.key.length !== 1 || e.key === " " || e.ctrlKey || e.metaKey || e.altKey) return;
860
+ this.keyManager.onKeydown(e);
861
+ }
862
+ onTab(e) {
863
+ this.closeRequested.emit();
864
+ this.overlay?.close();
865
+ }
866
+ static ɵfac = i0.ɵɵngDeclareFactory({
867
+ minVersion: "12.0.0",
868
+ version: "22.2.0",
869
+ ngImport: i0,
870
+ type: MenuSurfaceComponent,
871
+ deps: [],
872
+ target: i0.ɵɵFactoryTarget.Component
873
+ });
874
+ static ɵcmp = i0.ɵɵngDeclareComponent({
875
+ minVersion: "17.2.0",
876
+ version: "22.2.0",
877
+ type: MenuSurfaceComponent,
878
+ isStandalone: true,
879
+ selector: "MenuSurface",
880
+ inputs: { class: {
881
+ classPropertyName: "class",
882
+ publicName: "class",
883
+ isSignal: true,
884
+ isRequired: false,
885
+ transformFunction: null
886
+ } },
887
+ outputs: { closeRequested: "closeRequested" },
888
+ host: {
889
+ attributes: { "tabindex": "-1" },
890
+ listeners: {
891
+ "keydown.arrowDown": "onArrow($any($event), 1)",
892
+ "keydown.arrowUp": "onArrow($any($event), -1)",
893
+ "keydown.arrowLeft": "onArrowLeft($any($event))",
894
+ "keydown.home": "onHome($any($event))",
895
+ "keydown.end": "onEnd($any($event))",
896
+ "keydown.tab": "onTab($any($event))",
897
+ "keydown": "onTypeAhead($any($event))"
898
+ },
899
+ properties: {
900
+ "class": "classes()",
901
+ "attr.role": "\"menu\"",
902
+ "attr.id": "surfaceId",
903
+ "attr.aria-labelledby": "labelledBy"
904
+ }
905
+ },
906
+ queries: [{
907
+ propertyName: "items",
908
+ predicate: MenuFocusableItem,
909
+ descendants: true,
910
+ isSignal: true
911
+ }],
912
+ hostDirectives: [{
913
+ directive: i1.ComponentScaleDirective,
914
+ inputs: [
915
+ "radius",
916
+ "radius",
917
+ "density",
918
+ "density"
919
+ ]
920
+ }],
921
+ ngImport: i0,
922
+ template: `<ng-content />`,
923
+ isInline: true
924
+ });
925
+ };
926
+ i0.ɵɵngDeclareClassMetadata({
927
+ minVersion: "12.0.0",
928
+ version: "22.2.0",
929
+ ngImport: i0,
930
+ type: MenuSurfaceComponent,
931
+ decorators: [{
932
+ type: Component,
933
+ args: [{
934
+ selector: "MenuSurface",
935
+ hostDirectives: [{
936
+ directive: ComponentScaleDirective,
937
+ inputs: ["radius", "density"]
938
+ }],
939
+ host: {
940
+ "[class]": "classes()",
941
+ "[attr.role]": "\"menu\"",
942
+ "[attr.id]": "surfaceId",
943
+ "[attr.aria-labelledby]": "labelledBy",
944
+ tabindex: "-1",
945
+ "(keydown.arrowDown)": "onArrow($any($event), 1)",
946
+ "(keydown.arrowUp)": "onArrow($any($event), -1)",
947
+ "(keydown.arrowLeft)": "onArrowLeft($any($event))",
948
+ "(keydown.home)": "onHome($any($event))",
949
+ "(keydown.end)": "onEnd($any($event))",
950
+ "(keydown.tab)": "onTab($any($event))",
951
+ "(keydown)": "onTypeAhead($any($event))"
952
+ },
953
+ template: `<ng-content />`
954
+ }]
955
+ }],
956
+ propDecorators: {
957
+ class: [{
958
+ type: i0.Input,
959
+ args: [{
960
+ isSignal: true,
961
+ alias: "class",
962
+ required: false
963
+ }]
964
+ }],
965
+ items: [{
966
+ type: i0.ContentChildren,
967
+ args: [i0.forwardRef(() => MenuFocusableItem), {
968
+ descendants: true,
969
+ isSignal: true
970
+ }]
971
+ }],
972
+ closeRequested: [{
973
+ type: i0.Output,
974
+ args: ["closeRequested"]
975
+ }]
976
+ }
977
+ });
978
+ var MenuContentDirective = class MenuContentDirective {
979
+ componentScale = inject(ComponentScaleDirective);
980
+ radius = this.componentScale.radius;
981
+ density = this.componentScale.density;
982
+ template = inject(TemplateRef);
983
+ static ɵfac = i0.ɵɵngDeclareFactory({
984
+ minVersion: "12.0.0",
985
+ version: "22.2.0",
986
+ ngImport: i0,
987
+ type: MenuContentDirective,
988
+ deps: [],
989
+ target: i0.ɵɵFactoryTarget.Directive
990
+ });
991
+ static ɵdir = i0.ɵɵngDeclareDirective({
992
+ minVersion: "14.0.0",
993
+ version: "22.2.0",
994
+ type: MenuContentDirective,
995
+ isStandalone: true,
996
+ selector: "ng-template[MenuContent]",
997
+ exportAs: ["MenuContent"],
998
+ hostDirectives: [{
999
+ directive: i1.ComponentScaleDirective,
1000
+ inputs: [
1001
+ "radius",
1002
+ "radius",
1003
+ "density",
1004
+ "density"
1005
+ ]
1006
+ }],
1007
+ ngImport: i0
1008
+ });
1009
+ };
1010
+ i0.ɵɵngDeclareClassMetadata({
1011
+ minVersion: "12.0.0",
1012
+ version: "22.2.0",
1013
+ ngImport: i0,
1014
+ type: MenuContentDirective,
1015
+ decorators: [{
1016
+ type: Directive,
1017
+ args: [{
1018
+ selector: "ng-template[MenuContent]",
1019
+ exportAs: "MenuContent",
1020
+ hostDirectives: [{
1021
+ directive: ComponentScaleDirective,
1022
+ inputs: ["radius", "density"]
1023
+ }]
1024
+ }]
1025
+ }]
1026
+ });
534
1027
  const MENU_SIDE_OFFSET = 0;
535
- /**
536
- * Opens an `<ng-template MenuContent>` anchored to the trigger element.
537
- */
538
- class MenuTriggerDirective {
539
- overlay = inject(Overlay);
540
- vcr = inject(ViewContainerRef);
541
- el = inject(ElementRef);
542
- destroyRef = inject(DestroyRef);
543
- injector = inject(Injector);
544
- /**
545
- * The controller of the menu this trigger lives in, if any. Present only when
546
- * the trigger is itself an item of another menu, which is exactly what makes
547
- * the menu it opens a submenu.
548
- */
549
- parentMenu = inject(MENU_OVERLAY, { optional: true });
550
- MenuTrigger = input.required(/* @ts-ignore */
551
- ...(ngDevMode ? [{ debugName: "MenuTrigger" }] : /* istanbul ignore next */ []));
552
- side = input('bottom', /* @ts-ignore */
553
- ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
554
- align = input('start', /* @ts-ignore */
555
- ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
556
- disabled = input(false, /* @ts-ignore */
557
- ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
558
- /**
559
- * Close the menu when an item is chosen, which is the WAI ARIA menu pattern
560
- * and the default here. Set it to `false` for a menu that stays open, for
561
- * example a list of checkbox items the user toggles several of in a row.
562
- */
563
- closeOnSelect = input(true, /* @ts-ignore */
564
- ...(ngDevMode ? [{ debugName: "closeOnSelect" }] : /* istanbul ignore next */ []));
565
- /**
566
- * Id used for `aria-controls` and for the surface's `aria-labelledby`. A
567
- * consumer's own `id` on the trigger element always wins.
568
- */
569
- triggerId = this.el.nativeElement.id || injectUniqueId('MenuTrigger');
570
- surfaceId = injectUniqueId('MenuSurface');
571
- openedChange = output();
572
- overlayRef = null;
573
- isOpen = signal(false, /* @ts-ignore */
574
- ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
575
- constructor() {
576
- this.destroyRef.onDestroy(() => this.close({ restoreFocus: false }));
577
- }
578
- toggle() {
579
- if (this.disabled())
580
- return;
581
- if (this.isOpen()) {
582
- this.close();
583
- }
584
- else {
585
- this.open();
586
- }
587
- }
588
- openAndFocus(e) {
589
- if (this.disabled())
590
- return;
591
- e.preventDefault();
592
- this.open();
593
- }
594
- openForSide(e, side) {
595
- if (this.disabled() || this.side() !== side)
596
- return;
597
- e.preventDefault();
598
- this.open();
599
- }
600
- open() {
601
- if (this.isOpen() || this.disabled())
602
- return;
603
- const primary = connectedPositionFor(this.side(), this.align(), MENU_SIDE_OFFSET);
604
- const fallbackSide = this.side() === 'top' || this.side() === 'bottom' ? oppositeSide(this.side()) : this.side();
605
- const fallback = connectedPositionFor(fallbackSide, this.align(), MENU_SIDE_OFFSET);
606
- this.overlayRef = this.overlay.create({
607
- positionStrategy: this.overlay
608
- .position()
609
- .flexibleConnectedTo(this.el)
610
- .withPositions([primary, fallback]),
611
- scrollStrategy: this.overlay.scrollStrategies.reposition(),
612
- hasBackdrop: false,
613
- panelClass: 'menu-panel',
614
- });
615
- const controller = {
616
- surfaceId: this.surfaceId,
617
- triggerId: this.triggerId,
618
- isSubmenu: this.parentMenu !== null,
619
- close: () => this.close(),
620
- closeAfterSelect: () => {
621
- if (this.closeOnSelect())
622
- this.close();
623
- },
624
- };
625
- const portal = new TemplatePortal(this.MenuTrigger().template, this.vcr, undefined, Injector.create({
626
- parent: this.injector,
627
- providers: [{ provide: MENU_OVERLAY, useValue: controller }],
628
- }));
629
- this.overlayRef.attach(portal);
630
- merge(this.overlayRef
631
- .outsidePointerEvents()
632
- .pipe(filter((e) => !this.el.nativeElement.contains(e.target))), this.overlayRef.detachments())
633
- .pipe(takeUntilDestroyed(this.destroyRef))
634
- .subscribe(() => this.close({ restoreFocus: false }));
635
- this.overlayRef
636
- .keydownEvents()
637
- .pipe(takeUntilDestroyed(this.destroyRef), filter((e) => e.key === 'Escape'))
638
- .subscribe(() => this.close());
639
- // Tab closes the menu and hands focus back to the trigger WITHOUT preventing
640
- // the default, so the browser then continues the tab sequence from the
641
- // trigger. That is what the menu pattern asks for: Tab leaves the menu and
642
- // lands wherever normal tab order says, rather than being swallowed.
643
- this.overlayRef
644
- .keydownEvents()
645
- .pipe(takeUntilDestroyed(this.destroyRef), filter((e) => e.key === 'Tab'))
646
- .subscribe(() => this.close());
647
- this.isOpen.set(true);
648
- this.openedChange.emit(true);
649
- }
650
- close(options = {}) {
651
- const { restoreFocus = true } = options;
652
- if (!this.isOpen())
653
- return;
654
- const overlayRef = this.overlayRef;
655
- this.overlayRef = null;
656
- this.isOpen.set(false);
657
- overlayRef?.dispose();
658
- if (!this.destroyRef.destroyed) {
659
- this.openedChange.emit(false);
660
- }
661
- if (restoreFocus) {
662
- this.el.nativeElement.focus();
663
- }
664
- }
665
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
666
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: MenuTriggerDirective, isStandalone: true, selector: "[MenuTrigger]", inputs: { MenuTrigger: { classPropertyName: "MenuTrigger", publicName: "MenuTrigger", isSignal: true, isRequired: true, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, closeOnSelect: { classPropertyName: "closeOnSelect", publicName: "closeOnSelect", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openedChange: "openedChange" }, host: { listeners: { "click": "toggle()", "keydown.enter": "openAndFocus($any($event))", "keydown.space": "openAndFocus($any($event))", "keydown.arrowDown": "openAndFocus($any($event))", "keydown.arrowRight": "openForSide($any($event), \"right\")", "keydown.arrowLeft": "openForSide($any($event), \"left\")" }, properties: { "attr.id": "triggerId", "attr.aria-haspopup": "\"menu\"", "attr.aria-expanded": "isOpen()", "attr.aria-controls": "isOpen() ? surfaceId : null" } }, providers: [{ provide: MENU_SUBMENU_TRIGGER, useValue: true }], exportAs: ["MenuTrigger"], ngImport: i0 });
667
- }
668
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: MenuTriggerDirective, decorators: [{
669
- type: Directive,
670
- args: [{
671
- selector: '[MenuTrigger]',
672
- exportAs: 'MenuTrigger',
673
- providers: [{ provide: MENU_SUBMENU_TRIGGER, useValue: true }],
674
- host: {
675
- '[attr.id]': 'triggerId',
676
- '[attr.aria-haspopup]': '"menu"',
677
- '[attr.aria-expanded]': 'isOpen()',
678
- '[attr.aria-controls]': 'isOpen() ? surfaceId : null',
679
- '(click)': 'toggle()',
680
- '(keydown.enter)': 'openAndFocus($any($event))',
681
- '(keydown.space)': 'openAndFocus($any($event))',
682
- '(keydown.arrowDown)': 'openAndFocus($any($event))',
683
- '(keydown.arrowRight)': 'openForSide($any($event), "right")',
684
- '(keydown.arrowLeft)': 'openForSide($any($event), "left")',
685
- },
686
- }]
687
- }], ctorParameters: () => [], propDecorators: { MenuTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "MenuTrigger", required: true }] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], closeOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnSelect", required: false }] }], openedChange: [{ type: i0.Output, args: ["openedChange"] }] } });
688
-
689
- /**
690
- * Generated bundle index. Do not edit.
691
- */
692
-
693
- export { MENU_OVERLAY, MENU_SUBMENU_TRIGGER, MenuCheckboxItemComponent, MenuContentDirective, MenuFocusableItem, MenuGroupComponent, MenuItemComponent, MenuLabelComponent, MenuRadioGroupBase, MenuRadioGroupComponent, MenuRadioItemComponent, MenuSeparatorComponent, MenuShortcutComponent, MenuSurfaceComponent, MenuTriggerDirective };
1028
+ var MenuTriggerDirective = class MenuTriggerDirective {
1029
+ overlay = inject(Overlay);
1030
+ vcr = inject(ViewContainerRef);
1031
+ el = inject(ElementRef);
1032
+ destroyRef = inject(DestroyRef);
1033
+ injector = inject(Injector);
1034
+ parentMenu = inject(MENU_OVERLAY, { optional: true });
1035
+ MenuTrigger = input.required(...ngDevMode ? [{ debugName: "MenuTrigger" }] : /* istanbul ignore next */ []);
1036
+ side = input("bottom", ...ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []);
1037
+ align = input("start", ...ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []);
1038
+ disabled = input(false, ...ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []);
1039
+ closeOnSelect = input(true, ...ngDevMode ? [{ debugName: "closeOnSelect" }] : /* istanbul ignore next */ []);
1040
+ triggerId = this.el.nativeElement.id || injectUniqueId("MenuTrigger");
1041
+ surfaceId = injectUniqueId("MenuSurface");
1042
+ openedChange = output();
1043
+ overlayRef = null;
1044
+ isOpen = signal(false, ...ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []);
1045
+ constructor() {
1046
+ this.destroyRef.onDestroy(() => this.close({ restoreFocus: false }));
1047
+ }
1048
+ toggle() {
1049
+ if (this.disabled()) return;
1050
+ if (this.isOpen()) this.close();
1051
+ else this.open();
1052
+ }
1053
+ openAndFocus(e) {
1054
+ if (this.disabled()) return;
1055
+ e.preventDefault();
1056
+ this.open();
1057
+ }
1058
+ openForSide(e, side) {
1059
+ if (this.disabled() || this.side() !== side) return;
1060
+ e.preventDefault();
1061
+ this.open();
1062
+ }
1063
+ open() {
1064
+ if (this.isOpen() || this.disabled()) return;
1065
+ const primary = connectedPositionFor(this.side(), this.align(), MENU_SIDE_OFFSET);
1066
+ const fallbackSide = this.side() === "top" || this.side() === "bottom" ? oppositeSide(this.side()) : this.side();
1067
+ const fallback = connectedPositionFor(fallbackSide, this.align(), MENU_SIDE_OFFSET);
1068
+ this.overlayRef = this.overlay.create({
1069
+ positionStrategy: this.overlay.position().flexibleConnectedTo(this.el).withPositions([primary, fallback]),
1070
+ scrollStrategy: this.overlay.scrollStrategies.reposition(),
1071
+ hasBackdrop: false,
1072
+ panelClass: "menu-panel"
1073
+ });
1074
+ const controller = {
1075
+ surfaceId: this.surfaceId,
1076
+ triggerId: this.triggerId,
1077
+ isSubmenu: this.parentMenu !== null,
1078
+ close: () => this.close(),
1079
+ closeAfterSelect: () => {
1080
+ if (this.closeOnSelect()) this.close();
1081
+ }
1082
+ };
1083
+ const portal = new TemplatePortal(this.MenuTrigger().template, this.vcr, void 0, Injector.create({
1084
+ parent: this.injector,
1085
+ providers: [{
1086
+ provide: MENU_OVERLAY,
1087
+ useValue: controller
1088
+ }]
1089
+ }));
1090
+ this.overlayRef.attach(portal);
1091
+ merge(this.overlayRef.outsidePointerEvents().pipe(filter((e) => !this.el.nativeElement.contains(e.target))), this.overlayRef.detachments()).pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.close({ restoreFocus: false }));
1092
+ this.overlayRef.keydownEvents().pipe(takeUntilDestroyed(this.destroyRef), filter((e) => e.key === "Escape")).subscribe(() => this.close());
1093
+ this.overlayRef.keydownEvents().pipe(takeUntilDestroyed(this.destroyRef), filter((e) => e.key === "Tab")).subscribe(() => this.close());
1094
+ this.isOpen.set(true);
1095
+ this.openedChange.emit(true);
1096
+ }
1097
+ close(options = {}) {
1098
+ const { restoreFocus = true } = options;
1099
+ if (!this.isOpen()) return;
1100
+ const overlayRef = this.overlayRef;
1101
+ this.overlayRef = null;
1102
+ this.isOpen.set(false);
1103
+ overlayRef?.dispose();
1104
+ if (!this.destroyRef.destroyed) this.openedChange.emit(false);
1105
+ if (restoreFocus) this.el.nativeElement.focus();
1106
+ }
1107
+ static ɵfac = i0.ɵɵngDeclareFactory({
1108
+ minVersion: "12.0.0",
1109
+ version: "22.2.0",
1110
+ ngImport: i0,
1111
+ type: MenuTriggerDirective,
1112
+ deps: [],
1113
+ target: i0.ɵɵFactoryTarget.Directive
1114
+ });
1115
+ static ɵdir = i0.ɵɵngDeclareDirective({
1116
+ minVersion: "17.1.0",
1117
+ version: "22.2.0",
1118
+ type: MenuTriggerDirective,
1119
+ isStandalone: true,
1120
+ selector: "[MenuTrigger]",
1121
+ inputs: {
1122
+ MenuTrigger: {
1123
+ classPropertyName: "MenuTrigger",
1124
+ publicName: "MenuTrigger",
1125
+ isSignal: true,
1126
+ isRequired: true,
1127
+ transformFunction: null
1128
+ },
1129
+ side: {
1130
+ classPropertyName: "side",
1131
+ publicName: "side",
1132
+ isSignal: true,
1133
+ isRequired: false,
1134
+ transformFunction: null
1135
+ },
1136
+ align: {
1137
+ classPropertyName: "align",
1138
+ publicName: "align",
1139
+ isSignal: true,
1140
+ isRequired: false,
1141
+ transformFunction: null
1142
+ },
1143
+ disabled: {
1144
+ classPropertyName: "disabled",
1145
+ publicName: "disabled",
1146
+ isSignal: true,
1147
+ isRequired: false,
1148
+ transformFunction: null
1149
+ },
1150
+ closeOnSelect: {
1151
+ classPropertyName: "closeOnSelect",
1152
+ publicName: "closeOnSelect",
1153
+ isSignal: true,
1154
+ isRequired: false,
1155
+ transformFunction: null
1156
+ }
1157
+ },
1158
+ outputs: { openedChange: "openedChange" },
1159
+ host: {
1160
+ listeners: {
1161
+ "click": "toggle()",
1162
+ "keydown.enter": "openAndFocus($any($event))",
1163
+ "keydown.space": "openAndFocus($any($event))",
1164
+ "keydown.arrowDown": "openAndFocus($any($event))",
1165
+ "keydown.arrowRight": "openForSide($any($event), \"right\")",
1166
+ "keydown.arrowLeft": "openForSide($any($event), \"left\")"
1167
+ },
1168
+ properties: {
1169
+ "attr.id": "triggerId",
1170
+ "attr.aria-haspopup": "\"menu\"",
1171
+ "attr.aria-expanded": "isOpen()",
1172
+ "attr.aria-controls": "isOpen() ? surfaceId : null"
1173
+ }
1174
+ },
1175
+ providers: [{
1176
+ provide: MENU_SUBMENU_TRIGGER,
1177
+ useValue: true
1178
+ }],
1179
+ exportAs: ["MenuTrigger"],
1180
+ ngImport: i0
1181
+ });
1182
+ };
1183
+ i0.ɵɵngDeclareClassMetadata({
1184
+ minVersion: "12.0.0",
1185
+ version: "22.2.0",
1186
+ ngImport: i0,
1187
+ type: MenuTriggerDirective,
1188
+ decorators: [{
1189
+ type: Directive,
1190
+ args: [{
1191
+ selector: "[MenuTrigger]",
1192
+ exportAs: "MenuTrigger",
1193
+ providers: [{
1194
+ provide: MENU_SUBMENU_TRIGGER,
1195
+ useValue: true
1196
+ }],
1197
+ host: {
1198
+ "[attr.id]": "triggerId",
1199
+ "[attr.aria-haspopup]": "\"menu\"",
1200
+ "[attr.aria-expanded]": "isOpen()",
1201
+ "[attr.aria-controls]": "isOpen() ? surfaceId : null",
1202
+ "(click)": "toggle()",
1203
+ "(keydown.enter)": "openAndFocus($any($event))",
1204
+ "(keydown.space)": "openAndFocus($any($event))",
1205
+ "(keydown.arrowDown)": "openAndFocus($any($event))",
1206
+ "(keydown.arrowRight)": "openForSide($any($event), \"right\")",
1207
+ "(keydown.arrowLeft)": "openForSide($any($event), \"left\")"
1208
+ }
1209
+ }]
1210
+ }],
1211
+ ctorParameters: () => [],
1212
+ propDecorators: {
1213
+ MenuTrigger: [{
1214
+ type: i0.Input,
1215
+ args: [{
1216
+ isSignal: true,
1217
+ alias: "MenuTrigger",
1218
+ required: true
1219
+ }]
1220
+ }],
1221
+ side: [{
1222
+ type: i0.Input,
1223
+ args: [{
1224
+ isSignal: true,
1225
+ alias: "side",
1226
+ required: false
1227
+ }]
1228
+ }],
1229
+ align: [{
1230
+ type: i0.Input,
1231
+ args: [{
1232
+ isSignal: true,
1233
+ alias: "align",
1234
+ required: false
1235
+ }]
1236
+ }],
1237
+ disabled: [{
1238
+ type: i0.Input,
1239
+ args: [{
1240
+ isSignal: true,
1241
+ alias: "disabled",
1242
+ required: false
1243
+ }]
1244
+ }],
1245
+ closeOnSelect: [{
1246
+ type: i0.Input,
1247
+ args: [{
1248
+ isSignal: true,
1249
+ alias: "closeOnSelect",
1250
+ required: false
1251
+ }]
1252
+ }],
1253
+ openedChange: [{
1254
+ type: i0.Output,
1255
+ args: ["openedChange"]
1256
+ }]
1257
+ }
1258
+ });
1259
+ export { MENU_OVERLAY, MENU_SUBMENU_TRIGGER, MenuCheckboxItemComponent, MenuContentDirective, MenuFocusableItem, MenuGroupComponent, MenuItemComponent, MenuLabelComponent, MenuRadioGroupBase, MenuRadioGroupComponent, MenuRadioItemComponent, MenuSeparatorComponent, MenuShortcutComponent, MenuSurfaceComponent, MenuTriggerDirective };