@angular/cdk-experimental 13.3.3 → 14.0.0-next.10

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 (133) hide show
  1. package/combobox/combobox-module.d.ts +3 -4
  2. package/combobox/combobox-popup.d.ts +6 -8
  3. package/combobox/combobox.d.ts +15 -18
  4. package/combobox/public-api.d.ts +0 -1
  5. package/dialog/dialog-config.d.ts +6 -1
  6. package/esm2020/column-resize/column-resize-directives/column-resize-flex.mjs +4 -4
  7. package/esm2020/column-resize/column-resize-directives/column-resize.mjs +4 -4
  8. package/esm2020/column-resize/column-resize-directives/default-enabled-column-resize-flex.mjs +4 -4
  9. package/esm2020/column-resize/column-resize-directives/default-enabled-column-resize.mjs +4 -4
  10. package/esm2020/column-resize/column-resize-module.mjs +9 -9
  11. package/esm2020/column-resize/column-resize-notifier.mjs +7 -7
  12. package/esm2020/column-resize/column-resize.mjs +4 -4
  13. package/esm2020/column-resize/column-size-store.mjs +4 -4
  14. package/esm2020/column-resize/event-dispatcher.mjs +4 -4
  15. package/esm2020/column-resize/overlay-handle.mjs +4 -4
  16. package/esm2020/column-resize/resizable.mjs +4 -4
  17. package/esm2020/column-resize/resize-strategy.mjs +10 -10
  18. package/esm2020/combobox/combobox-module.mjs +6 -7
  19. package/esm2020/combobox/combobox-popup.mjs +12 -23
  20. package/esm2020/combobox/combobox.mjs +50 -34
  21. package/esm2020/combobox/public-api.mjs +1 -2
  22. package/esm2020/dialog/dialog-config.mjs +1 -1
  23. package/esm2020/dialog/dialog-container.mjs +5 -5
  24. package/esm2020/dialog/dialog-module.mjs +5 -5
  25. package/esm2020/dialog/dialog.mjs +5 -5
  26. package/esm2020/listbox/listbox-module.mjs +5 -5
  27. package/esm2020/listbox/listbox.mjs +26 -44
  28. package/esm2020/menu/context-menu-trigger.mjs +227 -0
  29. package/esm2020/menu/menu-aim.mjs +24 -19
  30. package/esm2020/menu/menu-bar.mjs +72 -165
  31. package/esm2020/menu/menu-base.mjs +203 -0
  32. package/esm2020/menu/menu-errors.mjs +1 -9
  33. package/esm2020/menu/menu-group.mjs +7 -45
  34. package/esm2020/menu/menu-interface.mjs +1 -1
  35. package/esm2020/menu/menu-item-checkbox.mjs +11 -11
  36. package/esm2020/menu/menu-item-radio.mjs +50 -26
  37. package/esm2020/menu/menu-item-selectable.mjs +18 -33
  38. package/esm2020/menu/menu-item.mjs +133 -88
  39. package/esm2020/menu/menu-module.mjs +12 -14
  40. package/esm2020/menu/menu-stack.mjs +69 -19
  41. package/esm2020/menu/menu-trigger-base.mjs +114 -0
  42. package/esm2020/menu/menu-trigger.mjs +302 -0
  43. package/esm2020/menu/menu.mjs +75 -208
  44. package/esm2020/menu/pointer-focus-tracker.mjs +11 -9
  45. package/esm2020/menu/public-api.mjs +10 -7
  46. package/esm2020/popover-edit/edit-event-dispatcher.mjs +4 -4
  47. package/esm2020/popover-edit/edit-ref.mjs +4 -4
  48. package/esm2020/popover-edit/edit-services.mjs +4 -4
  49. package/esm2020/popover-edit/focus-dispatcher.mjs +4 -4
  50. package/esm2020/popover-edit/focus-escape-notifier.mjs +4 -4
  51. package/esm2020/popover-edit/lens-directives.mjs +10 -10
  52. package/esm2020/popover-edit/popover-edit-module.mjs +5 -5
  53. package/esm2020/popover-edit/popover-edit-position-strategy-factory.mjs +7 -7
  54. package/esm2020/popover-edit/table-directives.mjs +16 -16
  55. package/esm2020/scrolling/auto-size-virtual-scroll.mjs +4 -4
  56. package/esm2020/scrolling/scrolling-module.mjs +5 -5
  57. package/esm2020/selection/row-selection.mjs +4 -4
  58. package/esm2020/selection/select-all.mjs +4 -4
  59. package/esm2020/selection/selection-column.mjs +4 -4
  60. package/esm2020/selection/selection-module.mjs +5 -5
  61. package/esm2020/selection/selection-toggle.mjs +4 -4
  62. package/esm2020/selection/selection.mjs +4 -4
  63. package/esm2020/table-scroll-container/table-scroll-container-module.mjs +5 -5
  64. package/esm2020/table-scroll-container/table-scroll-container.mjs +4 -4
  65. package/esm2020/version.mjs +1 -1
  66. package/fesm2015/cdk-experimental.mjs +1 -1
  67. package/fesm2015/cdk-experimental.mjs.map +1 -1
  68. package/fesm2015/column-resize.mjs +50 -50
  69. package/fesm2015/column-resize.mjs.map +1 -1
  70. package/fesm2015/combobox.mjs +63 -106
  71. package/fesm2015/combobox.mjs.map +1 -1
  72. package/fesm2015/dialog.mjs +13 -12
  73. package/fesm2015/dialog.mjs.map +1 -1
  74. package/fesm2015/listbox.mjs +31 -47
  75. package/fesm2015/listbox.mjs.map +1 -1
  76. package/fesm2015/menu.mjs +1109 -1140
  77. package/fesm2015/menu.mjs.map +1 -1
  78. package/fesm2015/popover-edit.mjs +49 -49
  79. package/fesm2015/popover-edit.mjs.map +1 -1
  80. package/fesm2015/scrolling.mjs +7 -7
  81. package/fesm2015/scrolling.mjs.map +1 -1
  82. package/fesm2015/selection.mjs +19 -19
  83. package/fesm2015/selection.mjs.map +1 -1
  84. package/fesm2015/table-scroll-container.mjs +7 -7
  85. package/fesm2015/table-scroll-container.mjs.map +1 -1
  86. package/fesm2020/cdk-experimental.mjs +1 -1
  87. package/fesm2020/cdk-experimental.mjs.map +1 -1
  88. package/fesm2020/column-resize.mjs +50 -50
  89. package/fesm2020/column-resize.mjs.map +1 -1
  90. package/fesm2020/combobox.mjs +62 -103
  91. package/fesm2020/combobox.mjs.map +1 -1
  92. package/fesm2020/dialog.mjs +12 -12
  93. package/fesm2020/dialog.mjs.map +1 -1
  94. package/fesm2020/listbox.mjs +29 -46
  95. package/fesm2020/listbox.mjs.map +1 -1
  96. package/fesm2020/menu.mjs +1031 -1054
  97. package/fesm2020/menu.mjs.map +1 -1
  98. package/fesm2020/popover-edit.mjs +49 -49
  99. package/fesm2020/popover-edit.mjs.map +1 -1
  100. package/fesm2020/scrolling.mjs +7 -7
  101. package/fesm2020/scrolling.mjs.map +1 -1
  102. package/fesm2020/selection.mjs +19 -19
  103. package/fesm2020/selection.mjs.map +1 -1
  104. package/fesm2020/table-scroll-container.mjs +7 -7
  105. package/fesm2020/table-scroll-container.mjs.map +1 -1
  106. package/listbox/listbox.d.ts +16 -13
  107. package/menu/context-menu-trigger.d.ts +98 -0
  108. package/menu/menu-aim.d.ts +20 -9
  109. package/menu/menu-bar.d.ts +24 -60
  110. package/menu/menu-base.d.ts +104 -0
  111. package/menu/menu-errors.d.ts +0 -5
  112. package/menu/menu-group.d.ts +3 -28
  113. package/menu/menu-interface.d.ts +7 -5
  114. package/menu/menu-item-checkbox.d.ts +8 -2
  115. package/menu/menu-item-radio.d.ts +32 -8
  116. package/menu/menu-item-selectable.d.ts +4 -14
  117. package/menu/menu-item.d.ts +64 -24
  118. package/menu/menu-module.d.ts +10 -10
  119. package/menu/menu-stack.d.ts +64 -20
  120. package/menu/menu-trigger-base.d.ts +77 -0
  121. package/menu/menu-trigger.d.ts +104 -0
  122. package/menu/menu.d.ts +34 -87
  123. package/menu/pointer-focus-tracker.d.ts +7 -4
  124. package/menu/public-api.d.ts +9 -6
  125. package/package.json +3 -3
  126. package/combobox/combobox-panel.d.ts +0 -27
  127. package/esm2020/combobox/combobox-panel.mjs +0 -48
  128. package/esm2020/menu/context-menu.mjs +0 -294
  129. package/esm2020/menu/menu-item-trigger.mjs +0 -361
  130. package/esm2020/menu/menu-panel.mjs +0 -37
  131. package/menu/context-menu.d.ts +0 -123
  132. package/menu/menu-item-trigger.d.ts +0 -119
  133. package/menu/menu-panel.d.ts +0 -30
package/fesm2020/menu.mjs CHANGED
@@ -1,16 +1,43 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Injectable, Directive, EventEmitter, Optional, Inject, Input, Output, Self, ContentChildren, NgModule } from '@angular/core';
2
+ import { Directive, InjectionToken, Optional, SkipSelf, Inject, Injectable, EventEmitter, Injector, Self, Input, Output, ContentChildren, NgModule } from '@angular/core';
3
3
  import * as i1 from '@angular/cdk/overlay';
4
- import { OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
5
- import { FocusKeyManager } from '@angular/cdk/a11y';
6
- import { UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW, ENTER, SPACE, TAB, ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
7
- import * as i2 from '@angular/cdk/bidi';
8
- import { Subject, fromEvent, merge, defer, partition } from 'rxjs';
9
- import { filter, takeUntil, startWith, mergeMap, mapTo, mergeAll, take, switchMap, skip } from 'rxjs/operators';
10
- import * as i1$1 from '@angular/cdk/collections';
4
+ import { OverlayConfig, STANDARD_DROPDOWN_BELOW_POSITIONS, STANDARD_DROPDOWN_ADJACENT_POSITIONS, OverlayModule } from '@angular/cdk/overlay';
5
+ import { UP_ARROW, hasModifierKey, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW, ENTER, SPACE, TAB, ESCAPE } from '@angular/cdk/keycodes';
6
+ import * as i1$1 from '@angular/cdk/bidi';
7
+ import { startWith, debounceTime, distinctUntilChanged, filter, takeUntil, mergeMap, mapTo, mergeAll, switchMap, skip } from 'rxjs/operators';
8
+ import * as i1$2 from '@angular/cdk/collections';
11
9
  import { UniqueSelectionDispatcher } from '@angular/cdk/collections';
12
- import { coerceBooleanProperty } from '@angular/cdk/coercion';
10
+ import { Subject, fromEvent, merge, defer, partition } from 'rxjs';
13
11
  import { TemplatePortal } from '@angular/cdk/portal';
12
+ import { FocusKeyManager } from '@angular/cdk/a11y';
13
+ import { coerceBooleanProperty } from '@angular/cdk/coercion';
14
+
15
+ /**
16
+ * @license
17
+ * Copyright Google LLC All Rights Reserved.
18
+ *
19
+ * Use of this source code is governed by an MIT-style license that can be
20
+ * found in the LICENSE file at https://angular.io/license
21
+ */
22
+ /**
23
+ * A grouping container for `CdkMenuItemRadio` instances, similar to a `role="radiogroup"` element.
24
+ */
25
+ class CdkMenuGroup {
26
+ }
27
+ CdkMenuGroup.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive });
28
+ CdkMenuGroup.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkMenuGroup, selector: "[cdkMenuGroup]", host: { attributes: { "role": "group" }, classAttribute: "cdk-menu-group" }, providers: [{ provide: UniqueSelectionDispatcher, useClass: UniqueSelectionDispatcher }], exportAs: ["cdkMenuGroup"], ngImport: i0 });
29
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuGroup, decorators: [{
30
+ type: Directive,
31
+ args: [{
32
+ selector: '[cdkMenuGroup]',
33
+ exportAs: 'cdkMenuGroup',
34
+ host: {
35
+ 'role': 'group',
36
+ 'class': 'cdk-menu-group',
37
+ },
38
+ providers: [{ provide: UniqueSelectionDispatcher, useClass: UniqueSelectionDispatcher }],
39
+ }]
40
+ }] });
14
41
 
15
42
  /**
16
43
  * @license
@@ -29,6 +56,22 @@ const CDK_MENU = new InjectionToken('cdk-menu');
29
56
  * Use of this source code is governed by an MIT-style license that can be
30
57
  * found in the LICENSE file at https://angular.io/license
31
58
  */
59
+ /** Injection token used for an implementation of MenuStack. */
60
+ const MENU_STACK = new InjectionToken('cdk-menu-stack');
61
+ /** Provider that provides the parent menu stack, or a new menu stack if there is no parent one. */
62
+ const PARENT_OR_NEW_MENU_STACK_PROVIDER = {
63
+ provide: MENU_STACK,
64
+ deps: [[new Optional(), new SkipSelf(), new Inject(MENU_STACK)]],
65
+ useFactory: (parentMenuStack) => parentMenuStack || new MenuStack(),
66
+ };
67
+ /** Provider that provides the parent menu stack, or a new inline menu stack if there is no parent one. */
68
+ const PARENT_OR_NEW_INLINE_MENU_STACK_PROVIDER = (orientation) => ({
69
+ provide: MENU_STACK,
70
+ deps: [[new Optional(), new SkipSelf(), new Inject(MENU_STACK)]],
71
+ useFactory: (parentMenuStack) => parentMenuStack || MenuStack.inline(orientation),
72
+ });
73
+ /** The next available menu stack ID. */
74
+ let nextId$2 = 0;
32
75
  /**
33
76
  * MenuStack allows subscribers to listen for close events (when a MenuStackItem is popped off
34
77
  * of the stack) in order to perform closing actions. Upon the MenuStack being empty it emits
@@ -37,22 +80,42 @@ const CDK_MENU = new InjectionToken('cdk-menu');
37
80
  */
38
81
  class MenuStack {
39
82
  constructor() {
83
+ /** The ID of this menu stack. */
84
+ this.id = `${nextId$2++}`;
40
85
  /** All MenuStackItems tracked by this MenuStack. */
41
86
  this._elements = [];
42
87
  /** Emits the element which was popped off of the stack when requested by a closer. */
43
88
  this._close = new Subject();
44
89
  /** Emits once the MenuStack has become empty after popping off elements. */
45
90
  this._empty = new Subject();
91
+ /** Emits whether any menu in the menu stack has focus. */
92
+ this._hasFocus = new Subject();
46
93
  /** Observable which emits the MenuStackItem which has been requested to close. */
47
94
  this.closed = this._close;
95
+ /** Observable which emits whether any menu in the menu stack has focus. */
96
+ this.hasFocus = this._hasFocus.pipe(startWith(false), debounceTime(0), distinctUntilChanged());
48
97
  /**
49
98
  * Observable which emits when the MenuStack is empty after popping off the last element. It
50
99
  * emits a FocusNext event which specifies the action the closer has requested the listener
51
100
  * perform.
52
101
  */
53
102
  this.emptied = this._empty;
103
+ /**
104
+ * Whether the inline menu associated with this menu stack is vertical or horizontal.
105
+ * `null` indicates there is no inline menu associated with this menu stack.
106
+ */
107
+ this._inlineMenuOrientation = null;
108
+ }
109
+ /** Creates a menu stack that originates from an inline menu. */
110
+ static inline(orientation) {
111
+ const stack = new MenuStack();
112
+ stack._inlineMenuOrientation = orientation;
113
+ return stack;
54
114
  }
55
- /** @param menu the MenuStackItem to put on the stack. */
115
+ /**
116
+ * Adds an item to the menu stack.
117
+ * @param menu the MenuStackItem to put on the stack.
118
+ */
56
119
  push(menu) {
57
120
  this._elements.push(menu);
58
121
  }
@@ -60,19 +123,18 @@ class MenuStack {
60
123
  * Pop items off of the stack up to and including `lastItem` and emit each on the close
61
124
  * observable. If the stack is empty or `lastItem` is not on the stack it does nothing.
62
125
  * @param lastItem the last item to pop off the stack.
63
- * @param focusNext the event to emit on the `empty` observable if the method call resulted in an
64
- * empty stack. Does not emit if the stack was initially empty or if `lastItem` was not on the
65
- * stack.
126
+ * @param options Options that configure behavior on close.
66
127
  */
67
- close(lastItem, focusNext) {
128
+ close(lastItem, options) {
129
+ const { focusNextOnEmpty, focusParentTrigger } = { ...options };
68
130
  if (this._elements.indexOf(lastItem) >= 0) {
69
131
  let poppedElement;
70
132
  do {
71
133
  poppedElement = this._elements.pop();
72
- this._close.next(poppedElement);
134
+ this._close.next({ item: poppedElement, focusParentTrigger });
73
135
  } while (poppedElement !== lastItem);
74
136
  if (this.isEmpty()) {
75
- this._empty.next(focusNext);
137
+ this._empty.next(focusNextOnEmpty);
76
138
  }
77
139
  }
78
140
  }
@@ -87,25 +149,25 @@ class MenuStack {
87
149
  if (this._elements.indexOf(lastItem) >= 0) {
88
150
  removed = this.peek() !== lastItem;
89
151
  while (this.peek() !== lastItem) {
90
- this._close.next(this._elements.pop());
152
+ this._close.next({ item: this._elements.pop() });
91
153
  }
92
154
  }
93
155
  return removed;
94
156
  }
95
157
  /**
96
158
  * Pop off all MenuStackItems and emit each one on the `close` observable one by one.
97
- * @param focusNext the event to emit on the `empty` observable once the stack is emptied. Does
98
- * not emit if the stack was initially empty.
159
+ * @param options Options that configure behavior on close.
99
160
  */
100
- closeAll(focusNext) {
161
+ closeAll(options) {
162
+ const { focusNextOnEmpty, focusParentTrigger } = { ...options };
101
163
  if (!this.isEmpty()) {
102
164
  while (!this.isEmpty()) {
103
165
  const menuStackItem = this._elements.pop();
104
166
  if (menuStackItem) {
105
- this._close.next(menuStackItem);
167
+ this._close.next({ item: menuStackItem, focusParentTrigger });
106
168
  }
107
169
  }
108
- this._empty.next(focusNext);
170
+ this._empty.next(focusNextOnEmpty);
109
171
  }
110
172
  }
111
173
  /** Return true if this stack is empty. */
@@ -120,12 +182,24 @@ class MenuStack {
120
182
  peek() {
121
183
  return this._elements[this._elements.length - 1];
122
184
  }
185
+ /** Whether the menu stack is associated with an inline menu. */
186
+ hasInlineMenu() {
187
+ return this._inlineMenuOrientation != null;
188
+ }
189
+ /** The orientation of the associated inline menu. */
190
+ inlineMenuOrientation() {
191
+ return this._inlineMenuOrientation;
192
+ }
193
+ /** Sets whether the menu stack contains the focused element. */
194
+ setHasFocus(hasFocus) {
195
+ this._hasFocus.next(hasFocus);
196
+ }
123
197
  }
124
- /** NoopMenuStack is a placeholder MenuStack used for inline menus. */
125
- class NoopMenuStack extends MenuStack {
126
- /** Noop push - does not add elements to the MenuStack. */
127
- push(_) { }
128
- }
198
+ MenuStack.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: MenuStack, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
199
+ MenuStack.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: MenuStack });
200
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: MenuStack, decorators: [{
201
+ type: Injectable
202
+ }] });
129
203
 
130
204
  /**
131
205
  * @license
@@ -134,14 +208,6 @@ class NoopMenuStack extends MenuStack {
134
208
  * Use of this source code is governed by an MIT-style license that can be
135
209
  * found in the LICENSE file at https://angular.io/license
136
210
  */
137
- /**
138
- * Throws an exception when a menu panel already has a menu stack.
139
- * @docs-private
140
- */
141
- function throwExistingMenuStackError() {
142
- throw Error('CdkMenuPanel is already referenced by different CdkMenuTrigger. Ensure that a menu is' +
143
- ' opened by a single trigger only.');
144
- }
145
211
  /**
146
212
  * Throws an exception when an instance of the PointerFocusTracker is not provided.
147
213
  * @docs-private
@@ -189,7 +255,6 @@ function getYIntercept(point, slope) {
189
255
  * @param submenuPoints the submenu DOMRect points.
190
256
  * @param m the slope of the trajectory line.
191
257
  * @param b the y intercept of the trajectory line.
192
- *
193
258
  * @return true if any point on the line falls within the submenu.
194
259
  */
195
260
  function isWithinSubmenu(submenuPoints, m, b) {
@@ -213,14 +278,24 @@ function isWithinSubmenu(submenuPoints, m, b) {
213
278
  * to submenu.
214
279
  */
215
280
  class TargetMenuAim {
216
- constructor(_ngZone) {
281
+ constructor(
282
+ /** The Angular zone. */
283
+ _ngZone) {
217
284
  this._ngZone = _ngZone;
218
285
  /** The last NUM_POINTS mouse move events. */
219
286
  this._points = [];
220
287
  /** Emits when this service is destroyed. */
221
288
  this._destroyed = new Subject();
222
289
  }
223
- /** Set the Menu and its PointerFocusTracker. */
290
+ ngOnDestroy() {
291
+ this._destroyed.next();
292
+ this._destroyed.complete();
293
+ }
294
+ /**
295
+ * Set the Menu and its PointerFocusTracker.
296
+ * @param menu The menu that this menu aim service controls.
297
+ * @param pointerTracker The `PointerFocusTracker` for the given menu.
298
+ */
224
299
  initialize(menu, pointerTracker) {
225
300
  this._menu = menu;
226
301
  this._pointerTracker = pointerTracker;
@@ -257,6 +332,8 @@ class TargetMenuAim {
257
332
  *
258
333
  * The delayed toggle handler executes the `doToggle` callback after some period of time iff the
259
334
  * users mouse is on an item in the current menu.
335
+ *
336
+ * @param doToggle the function called when the user is not moving towards the submenu.
260
337
  */
261
338
  _startTimeout(doToggle) {
262
339
  // If the users mouse is moving towards a submenu we don't want to immediately resolve.
@@ -293,9 +370,7 @@ class TargetMenuAim {
293
370
  }
294
371
  /** Get the bounding DOMRect for the open submenu. */
295
372
  _getSubmenuBounds() {
296
- return this._pointerTracker?.previousElement
297
- ?.getMenu()
298
- ?._elementRef.nativeElement.getBoundingClientRect();
373
+ return this._pointerTracker?.previousElement?.getMenu()?.nativeElement.getBoundingClientRect();
299
374
  }
300
375
  /**
301
376
  * Check if a reference to the PointerFocusTracker and menu element is provided.
@@ -314,7 +389,7 @@ class TargetMenuAim {
314
389
  /** Subscribe to the root menus mouse move events and update the tracked mouse points. */
315
390
  _subscribeToMouseMoves() {
316
391
  this._ngZone.runOutsideAngular(() => {
317
- fromEvent(this._menu._elementRef.nativeElement, 'mousemove')
392
+ fromEvent(this._menu.nativeElement, 'mousemove')
318
393
  .pipe(filter((_, index) => index % MOUSE_MOVE_SAMPLE_FREQUENCY === 0), takeUntil(this._destroyed))
319
394
  .subscribe((event) => {
320
395
  this._points.push({ x: event.clientX, y: event.clientY });
@@ -324,25 +399,21 @@ class TargetMenuAim {
324
399
  });
325
400
  });
326
401
  }
327
- ngOnDestroy() {
328
- this._destroyed.next();
329
- this._destroyed.complete();
330
- }
331
402
  }
332
- TargetMenuAim.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: TargetMenuAim, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
333
- TargetMenuAim.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: TargetMenuAim });
334
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: TargetMenuAim, decorators: [{
403
+ TargetMenuAim.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: TargetMenuAim, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
404
+ TargetMenuAim.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: TargetMenuAim });
405
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: TargetMenuAim, decorators: [{
335
406
  type: Injectable
336
407
  }], ctorParameters: function () { return [{ type: i0.NgZone }]; } });
337
408
  /**
338
- * CdkTargetMenuAim is a provider for the TargetMenuAim service. It should be added to an
409
+ * CdkTargetMenuAim is a provider for the TargetMenuAim service. It can be added to an
339
410
  * element with either the `cdkMenu` or `cdkMenuBar` directive and child menu items.
340
411
  */
341
412
  class CdkTargetMenuAim {
342
413
  }
343
- CdkTargetMenuAim.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkTargetMenuAim, deps: [], target: i0.ɵɵFactoryTarget.Directive });
344
- CdkTargetMenuAim.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkTargetMenuAim, selector: "[cdkTargetMenuAim]", providers: [{ provide: MENU_AIM, useClass: TargetMenuAim }], exportAs: ["cdkTargetMenuAim"], ngImport: i0 });
345
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkTargetMenuAim, decorators: [{
414
+ CdkTargetMenuAim.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkTargetMenuAim, deps: [], target: i0.ɵɵFactoryTarget.Directive });
415
+ CdkTargetMenuAim.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkTargetMenuAim, selector: "[cdkTargetMenuAim]", providers: [{ provide: MENU_AIM, useClass: TargetMenuAim }], exportAs: ["cdkTargetMenuAim"], ngImport: i0 });
416
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkTargetMenuAim, decorators: [{
346
417
  type: Directive,
347
418
  args: [{
348
419
  selector: '[cdkTargetMenuAim]',
@@ -358,198 +429,277 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
358
429
  * Use of this source code is governed by an MIT-style license that can be
359
430
  * found in the LICENSE file at https://angular.io/license
360
431
  */
432
+ /** Injection token used for an implementation of MenuStack. */
433
+ const MENU_TRIGGER = new InjectionToken('cdk-menu-trigger');
361
434
  /**
362
- * Whether the target element is a menu item to be ignored by the overlay background click handler.
435
+ * Abstract directive that implements shared logic common to all menu triggers.
436
+ * This class can be extended to create custom menu trigger types.
363
437
  */
364
- function isClickInsideMenuOverlay(target) {
365
- while (target?.parentElement) {
366
- const isOpenTrigger = target.getAttribute('aria-expanded') === 'true' &&
367
- target.classList.contains('cdk-menu-trigger');
368
- const isOverlayMenu = target.classList.contains('cdk-menu') && !target.classList.contains('cdk-menu-inline');
369
- if (isOpenTrigger || isOverlayMenu) {
370
- return true;
438
+ class CdkMenuTriggerBase {
439
+ constructor(
440
+ /** The DI injector for this component */
441
+ injector,
442
+ /** The view container ref for this component */
443
+ viewContainerRef,
444
+ /** The menu stack this menu is part of. */
445
+ menuStack) {
446
+ this.injector = injector;
447
+ this.viewContainerRef = viewContainerRef;
448
+ this.menuStack = menuStack;
449
+ /** Emits when the attached menu is requested to open */
450
+ this.opened = new EventEmitter();
451
+ /** Emits when the attached menu is requested to close */
452
+ this.closed = new EventEmitter();
453
+ /** A reference to the overlay which manages the triggered menu */
454
+ this.overlayRef = null;
455
+ /** Emits when this trigger is destroyed. */
456
+ this.destroyed = new Subject();
457
+ /** Emits when the outside pointer events listener on the overlay should be stopped. */
458
+ this.stopOutsideClicksListener = merge(this.closed, this.destroyed);
459
+ }
460
+ ngOnDestroy() {
461
+ this._destroyOverlay();
462
+ this.destroyed.next();
463
+ this.destroyed.complete();
464
+ }
465
+ /** Whether the attached menu is open. */
466
+ isOpen() {
467
+ return !!this.overlayRef?.hasAttached();
468
+ }
469
+ /** Registers a child menu as having been opened by this trigger. */
470
+ registerChildMenu(child) {
471
+ this.childMenu = child;
472
+ }
473
+ /**
474
+ * Get the portal to be attached to the overlay which contains the menu. Allows for the menu
475
+ * content to change dynamically and be reflected in the application.
476
+ */
477
+ getMenuContentPortal() {
478
+ const hasMenuContentChanged = this.menuTemplateRef !== this._menuPortal?.templateRef;
479
+ if (this.menuTemplateRef && (!this._menuPortal || hasMenuContentChanged)) {
480
+ this._menuPortal = new TemplatePortal(this.menuTemplateRef, this.viewContainerRef, undefined, this._getChildMenuInjector());
481
+ }
482
+ return this._menuPortal;
483
+ }
484
+ /**
485
+ * Whether the given element is inside the scope of this trigger's menu stack.
486
+ * @param element The element to check.
487
+ * @return Whether the element is inside the scope of this trigger's menu stack.
488
+ */
489
+ isElementInsideMenuStack(element) {
490
+ for (let el = element; el; el = el?.parentElement ?? null) {
491
+ if (el.getAttribute('data-cdk-menu-stack-id') === this.menuStack.id) {
492
+ return true;
493
+ }
494
+ }
495
+ return false;
496
+ }
497
+ /** Destroy and unset the overlay reference it if exists */
498
+ _destroyOverlay() {
499
+ if (this.overlayRef) {
500
+ this.overlayRef.dispose();
501
+ this.overlayRef = null;
371
502
  }
372
- target = target.parentElement;
373
503
  }
374
- return false;
504
+ /** Gets the injector to use when creating a child menu. */
505
+ _getChildMenuInjector() {
506
+ this._childMenuInjector =
507
+ this._childMenuInjector ||
508
+ Injector.create({
509
+ providers: [
510
+ { provide: MENU_TRIGGER, useValue: this },
511
+ { provide: MENU_STACK, useValue: this.menuStack },
512
+ ],
513
+ parent: this.injector,
514
+ });
515
+ return this._childMenuInjector;
516
+ }
375
517
  }
518
+ CdkMenuTriggerBase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuTriggerBase, deps: [{ token: i0.Injector }, { token: i0.ViewContainerRef }, { token: MENU_STACK }], target: i0.ɵɵFactoryTarget.Directive });
519
+ CdkMenuTriggerBase.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkMenuTriggerBase, host: { properties: { "attr.aria-controls": "childMenu?.id", "attr.data-cdk-menu-stack-id": "menuStack.id" } }, ngImport: i0 });
520
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuTriggerBase, decorators: [{
521
+ type: Directive,
522
+ args: [{
523
+ host: {
524
+ '[attr.aria-controls]': 'childMenu?.id',
525
+ '[attr.data-cdk-menu-stack-id]': 'menuStack.id',
526
+ },
527
+ }]
528
+ }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.ViewContainerRef }, { type: MenuStack, decorators: [{
529
+ type: Inject,
530
+ args: [MENU_STACK]
531
+ }] }]; } });
532
+
376
533
  /**
377
- * A directive to be combined with CdkMenuItem which opens the Menu it is bound to. If the
378
- * element is in a top level MenuBar it will open the menu on click, or if a sibling is already
379
- * opened it will open on hover. If it is inside of a Menu it will open the attached Submenu on
380
- * hover regardless of its sibling state.
534
+ * @license
535
+ * Copyright Google LLC All Rights Reserved.
381
536
  *
382
- * The directive must be placed along with the `cdkMenuItem` directive in order to enable full
383
- * functionality.
537
+ * Use of this source code is governed by an MIT-style license that can be
538
+ * found in the LICENSE file at https://angular.io/license
384
539
  */
385
- class CdkMenuItemTrigger {
386
- constructor(_elementRef, _viewContainerRef, _overlay, _ngZone, _parentMenu, _menuAim, _directionality) {
540
+ /**
541
+ * A directive that turns its host element into a trigger for a popup menu.
542
+ * It can be combined with cdkMenuItem to create sub-menus. If the element is in a top level
543
+ * MenuBar it will open the menu on click, or if a sibling is already opened it will open on hover.
544
+ * If it is inside of a Menu it will open the attached Submenu on hover regardless of its sibling
545
+ * state.
546
+ */
547
+ class CdkMenuTrigger extends CdkMenuTriggerBase {
548
+ constructor(
549
+ /** The DI injector for this component. */
550
+ injector,
551
+ /** The host element. */
552
+ _elementRef,
553
+ /** The view container ref for this component. */
554
+ viewContainerRef,
555
+ /** The CDK overlay service. */
556
+ _overlay,
557
+ /** The Angular zone. */
558
+ _ngZone,
559
+ /** The menu stack this trigger belongs to. */
560
+ menuStack,
561
+ /** The parent menu this trigger belongs to. */
562
+ _parentMenu,
563
+ /** The menu aim service used by this menu. */
564
+ _menuAim,
565
+ /** The directionality of the page. */
566
+ _directionality) {
567
+ super(injector, viewContainerRef, menuStack);
387
568
  this._elementRef = _elementRef;
388
- this._viewContainerRef = _viewContainerRef;
389
569
  this._overlay = _overlay;
390
570
  this._ngZone = _ngZone;
391
571
  this._parentMenu = _parentMenu;
392
572
  this._menuAim = _menuAim;
393
573
  this._directionality = _directionality;
394
- /** Emits when the attached menu is requested to open */
395
- this.opened = new EventEmitter();
396
- /** Emits when the attached menu is requested to close */
397
- this.closed = new EventEmitter();
398
- /** The menu stack for this trigger and its sub-menus. */
399
- this._menuStack = new MenuStack();
400
- /** A reference to the overlay which manages the triggered menu */
401
- this._overlayRef = null;
402
- /** Emits when this trigger is destroyed. */
403
- this._destroyed = new Subject();
404
- /** Emits when the outside pointer events listener on the overlay should be stopped. */
405
- this._stopOutsideClicksListener = merge(this.closed, this._destroyed);
406
574
  this._registerCloseHandler();
575
+ this._subscribeToMenuStackClosed();
407
576
  this._subscribeToMouseEnter();
577
+ this._subscribeToMenuStackHasFocus();
408
578
  }
409
- /** Template reference variable to the menu this trigger opens */
410
- get menuPanel() {
411
- return this._menuPanel;
412
- }
413
- set menuPanel(panel) {
414
- // If the provided panel already has a stack, that means it already has a trigger configured.
415
- // Note however that there are some edge cases where two triggers **may** share the same menu,
416
- // e.g. two triggers in two separate menus.
417
- if ((typeof ngDevMode === 'undefined' || ngDevMode) && panel?._menuStack) {
418
- throwExistingMenuStackError();
419
- }
420
- this._menuPanel = panel;
421
- if (this._menuPanel) {
422
- this._menuPanel._menuStack = this._getMenuStack();
423
- }
424
- }
425
- /** Open/close the attached menu if the trigger has been configured with one */
579
+ /** Toggle the attached menu. */
426
580
  toggle() {
427
- if (this.hasMenu()) {
428
- this.isMenuOpen() ? this.closeMenu() : this.openMenu();
429
- }
581
+ this.isOpen() ? this.close() : this.open();
430
582
  }
431
583
  /** Open the attached menu. */
432
- openMenu() {
433
- if (!this.isMenuOpen()) {
584
+ open() {
585
+ if (!this.isOpen()) {
434
586
  this.opened.next();
435
- this._overlayRef = this._overlayRef || this._overlay.create(this._getOverlayConfig());
436
- this._overlayRef.attach(this._getPortal());
587
+ this.overlayRef = this.overlayRef || this._overlay.create(this._getOverlayConfig());
588
+ this.overlayRef.attach(this.getMenuContentPortal());
437
589
  this._subscribeToOutsideClicks();
438
590
  }
439
591
  }
440
592
  /** Close the opened menu. */
441
- closeMenu() {
442
- if (this.isMenuOpen()) {
593
+ close() {
594
+ if (this.isOpen()) {
443
595
  this.closed.next();
444
- this._overlayRef.detach();
596
+ this.overlayRef.detach();
445
597
  }
446
598
  this._closeSiblingTriggers();
447
599
  }
448
- /** Return true if the trigger has an attached menu */
449
- hasMenu() {
450
- return !!this.menuPanel;
451
- }
452
- /** Whether the menu this button is a trigger for is open */
453
- isMenuOpen() {
454
- return this._overlayRef ? this._overlayRef.hasAttached() : false;
455
- }
456
600
  /**
457
- * Get a reference to the rendered Menu if the Menu is open and it is visible in the DOM.
458
- * @return the menu if it is open, otherwise undefined.
601
+ * Get a reference to the rendered Menu if the Menu is open and rendered in the DOM.
459
602
  */
460
603
  getMenu() {
461
- return this.menuPanel?._menu;
462
- }
463
- /**
464
- * Subscribe to the mouseenter events and close any sibling menu items if this element is moused
465
- * into.
466
- */
467
- _subscribeToMouseEnter() {
468
- // Closes any sibling menu items and opens the menu associated with this trigger.
469
- const toggleMenus = () => this._ngZone.run(() => {
470
- this._closeSiblingTriggers();
471
- this.openMenu();
472
- });
473
- this._ngZone.runOutsideAngular(() => {
474
- fromEvent(this._elementRef.nativeElement, 'mouseenter')
475
- .pipe(filter(() => !this._getMenuStack()?.isEmpty() && !this.isMenuOpen()), takeUntil(this._destroyed))
476
- .subscribe(() => {
477
- if (this._menuAim) {
478
- this._menuAim.toggle(toggleMenus);
479
- }
480
- else {
481
- toggleMenus();
482
- }
483
- });
484
- });
604
+ return this.childMenu;
485
605
  }
486
606
  /**
487
- * Handles keyboard events for the menu item, specifically opening/closing the attached menu and
488
- * focusing the appropriate submenu item.
489
- * @param event the keyboard event to handle
607
+ * Handles keyboard events for the menu item.
608
+ * @param event The keyboard event to handle
490
609
  */
491
610
  _toggleOnKeydown(event) {
611
+ const isParentVertical = this._parentMenu?.orientation === 'vertical';
492
612
  const keyCode = event.keyCode;
493
613
  switch (keyCode) {
494
614
  case SPACE:
495
615
  case ENTER:
496
- event.preventDefault();
497
- this.toggle();
498
- this.menuPanel?._menu?.focusFirstItem('keyboard');
616
+ if (!hasModifierKey(event)) {
617
+ event.preventDefault();
618
+ this.toggle();
619
+ this.childMenu?.focusFirstItem('keyboard');
620
+ }
499
621
  break;
500
622
  case RIGHT_ARROW:
501
- if (this._parentMenu && this._isParentVertical()) {
502
- event.preventDefault();
503
- if (this._directionality?.value === 'rtl') {
504
- this._getMenuStack().close(this._parentMenu, 2 /* currentItem */);
505
- }
506
- else {
507
- this.openMenu();
508
- this.menuPanel?._menu?.focusFirstItem('keyboard');
623
+ if (!hasModifierKey(event)) {
624
+ if (this._parentMenu && isParentVertical && this._directionality?.value !== 'rtl') {
625
+ event.preventDefault();
626
+ this.open();
627
+ this.childMenu?.focusFirstItem('keyboard');
509
628
  }
510
629
  }
511
630
  break;
512
631
  case LEFT_ARROW:
513
- if (this._parentMenu && this._isParentVertical()) {
514
- event.preventDefault();
515
- if (this._directionality?.value === 'rtl') {
516
- this.openMenu();
517
- this.menuPanel?._menu?.focusFirstItem('keyboard');
518
- }
519
- else {
520
- this._getMenuStack().close(this._parentMenu, 2 /* currentItem */);
632
+ if (!hasModifierKey(event)) {
633
+ if (this._parentMenu && isParentVertical && this._directionality?.value === 'rtl') {
634
+ event.preventDefault();
635
+ this.open();
636
+ this.childMenu?.focusFirstItem('keyboard');
521
637
  }
522
638
  }
523
639
  break;
524
640
  case DOWN_ARROW:
525
641
  case UP_ARROW:
526
- if (!this._isParentVertical()) {
527
- event.preventDefault();
528
- this.openMenu();
529
- keyCode === DOWN_ARROW
530
- ? this.menuPanel?._menu?.focusFirstItem('keyboard')
531
- : this.menuPanel?._menu?.focusLastItem('keyboard');
642
+ if (!hasModifierKey(event)) {
643
+ if (!isParentVertical) {
644
+ event.preventDefault();
645
+ this.open();
646
+ keyCode === DOWN_ARROW
647
+ ? this.childMenu?.focusFirstItem('keyboard')
648
+ : this.childMenu?.focusLastItem('keyboard');
649
+ }
532
650
  }
533
651
  break;
534
652
  }
535
653
  }
654
+ /**
655
+ * Sets whether the trigger's menu stack has focus.
656
+ * @param hasFocus Whether the menu stack has focus.
657
+ */
658
+ _setHasFocus(hasFocus) {
659
+ if (!this._parentMenu) {
660
+ this.menuStack.setHasFocus(hasFocus);
661
+ }
662
+ }
663
+ /**
664
+ * Subscribe to the mouseenter events and close any sibling menu items if this element is moused
665
+ * into.
666
+ */
667
+ _subscribeToMouseEnter() {
668
+ // Closes any sibling menu items and opens the menu associated with this trigger.
669
+ const toggleMenus = () => this._ngZone.run(() => {
670
+ this._closeSiblingTriggers();
671
+ this.open();
672
+ });
673
+ this._ngZone.runOutsideAngular(() => {
674
+ fromEvent(this._elementRef.nativeElement, 'mouseenter')
675
+ .pipe(filter(() => !this.menuStack.isEmpty() && !this.isOpen()), takeUntil(this.destroyed))
676
+ .subscribe(() => {
677
+ if (this._menuAim) {
678
+ this._menuAim.toggle(toggleMenus);
679
+ }
680
+ else {
681
+ toggleMenus();
682
+ }
683
+ });
684
+ });
685
+ }
536
686
  /** Close out any sibling menu trigger menus. */
537
687
  _closeSiblingTriggers() {
538
688
  if (this._parentMenu) {
539
- const menuStack = this._getMenuStack();
540
689
  // If nothing was removed from the stack and the last element is not the parent item
541
690
  // that means that the parent menu is a menu bar since we don't put the menu bar on the
542
691
  // stack
543
- const isParentMenuBar = !menuStack.closeSubMenuOf(this._parentMenu) && menuStack.peek() !== this._parentMenu;
692
+ const isParentMenuBar = !this.menuStack.closeSubMenuOf(this._parentMenu) &&
693
+ this.menuStack.peek() !== this._parentMenu;
544
694
  if (isParentMenuBar) {
545
- menuStack.closeAll();
695
+ this.menuStack.closeAll();
546
696
  }
547
697
  }
548
698
  else {
549
- this._getMenuStack().closeAll();
699
+ this.menuStack.closeAll();
550
700
  }
551
701
  }
552
- /** Get the configuration object used to create the overlay */
702
+ /** Get the configuration object used to create the overlay. */
553
703
  _getOverlayConfig() {
554
704
  return new OverlayConfig({
555
705
  positionStrategy: this._getOverlayPositionStrategy(),
@@ -557,46 +707,19 @@ class CdkMenuItemTrigger {
557
707
  direction: this._directionality,
558
708
  });
559
709
  }
560
- /** Build the position strategy for the overlay which specifies where to place the menu */
710
+ /** Build the position strategy for the overlay which specifies where to place the menu. */
561
711
  _getOverlayPositionStrategy() {
562
712
  return this._overlay
563
713
  .position()
564
714
  .flexibleConnectedTo(this._elementRef)
565
715
  .withPositions(this._getOverlayPositions());
566
716
  }
567
- /** Determine and return where to position the opened menu relative to the menu item */
717
+ /** Get the preferred positions for the opened menu relative to the menu item. */
568
718
  _getOverlayPositions() {
569
- // TODO: use a common positioning config from (possibly) cdk/overlay
570
- return !this._parentMenu || this._parentMenu.orientation === 'horizontal'
571
- ? [
572
- { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },
573
- { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' },
574
- { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top' },
575
- { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom' },
576
- ]
577
- : [
578
- { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top' },
579
- { originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'bottom' },
580
- { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top' },
581
- { originX: 'start', originY: 'bottom', overlayX: 'end', overlayY: 'bottom' },
582
- ];
583
- }
584
- /**
585
- * Get the portal to be attached to the overlay which contains the menu. Allows for the menu
586
- * content to change dynamically and be reflected in the application.
587
- */
588
- _getPortal() {
589
- const hasMenuContentChanged = this.menuPanel?._templateRef !== this._panelContent?.templateRef;
590
- if (this.menuPanel && (!this._panelContent || hasMenuContentChanged)) {
591
- this._panelContent = new TemplatePortal(this.menuPanel._templateRef, this._viewContainerRef);
592
- }
593
- return this._panelContent;
594
- }
595
- /**
596
- * @return true if if the enclosing parent menu is configured in a vertical orientation.
597
- */
598
- _isParentVertical() {
599
- return this._parentMenu?.orientation === 'vertical';
719
+ return (this.menuPosition ??
720
+ (!this._parentMenu || this._parentMenu.orientation === 'horizontal'
721
+ ? STANDARD_DROPDOWN_BELOW_POSITIONS
722
+ : STANDARD_DROPDOWN_ADJACENT_POSITIONS));
600
723
  }
601
724
  /**
602
725
  * Subscribe to the MenuStack close events if this is a standalone trigger and close out the menu
@@ -604,78 +727,84 @@ class CdkMenuItemTrigger {
604
727
  */
605
728
  _registerCloseHandler() {
606
729
  if (!this._parentMenu) {
607
- this._menuStack.closed.pipe(takeUntil(this._destroyed)).subscribe(item => {
608
- if (item === this._menuPanel?._menu) {
609
- this.closeMenu();
730
+ this.menuStack.closed.pipe(takeUntil(this.destroyed)).subscribe(({ item }) => {
731
+ if (item === this.childMenu) {
732
+ this.close();
610
733
  }
611
734
  });
612
735
  }
613
736
  }
614
- /** Get the menu stack for this trigger - either from the parent or this trigger. */
615
- _getMenuStack() {
616
- // We use a function since at the construction of the MenuItemTrigger the parent Menu won't have
617
- // its menu stack set. Therefore we need to reference the menu stack from the parent each time
618
- // we want to use it.
619
- return this._parentMenu?._menuStack || this._menuStack;
620
- }
621
- ngOnDestroy() {
622
- this._destroyOverlay();
623
- this._resetPanelMenuStack();
624
- this._destroyed.next();
625
- this._destroyed.complete();
626
- }
627
- /** Set the menu panels menu stack back to null. */
628
- _resetPanelMenuStack() {
629
- // If a CdkMenuTrigger is placed in a submenu, each time the trigger is rendered (its parent
630
- // menu is opened) the panel setter for CdkMenuPanel is called. From the first render onward,
631
- // the attached CdkMenuPanel has the MenuStack set. Since we throw an error if a panel already
632
- // has a stack set, we want to reset the attached stack here to prevent the error from being
633
- // thrown if the trigger re-configures its attached panel (in the case where there is a 1:1
634
- // relationship between the panel and trigger).
635
- if (this._menuPanel) {
636
- this._menuPanel._menuStack = null;
637
- }
638
- }
639
737
  /**
640
738
  * Subscribe to the overlays outside pointer events stream and handle closing out the stack if a
641
739
  * click occurs outside the menus.
642
740
  */
643
741
  _subscribeToOutsideClicks() {
644
- if (this._overlayRef) {
645
- this._overlayRef
742
+ if (this.overlayRef) {
743
+ this.overlayRef
646
744
  .outsidePointerEvents()
647
- .pipe(takeUntil(this._stopOutsideClicksListener))
745
+ .pipe(filter(e => e.target != this._elementRef.nativeElement &&
746
+ !this._elementRef.nativeElement.contains(e.target)), takeUntil(this.stopOutsideClicksListener))
648
747
  .subscribe(event => {
649
- if (!isClickInsideMenuOverlay(event.target)) {
650
- this._getMenuStack().closeAll();
748
+ if (!this.isElementInsideMenuStack(event.target)) {
749
+ this.menuStack.closeAll();
750
+ }
751
+ else {
752
+ this._closeSiblingTriggers();
651
753
  }
652
754
  });
653
755
  }
654
756
  }
655
- /** Destroy and unset the overlay reference it if exists */
656
- _destroyOverlay() {
657
- if (this._overlayRef) {
658
- this._overlayRef.dispose();
659
- this._overlayRef = null;
757
+ /** Subscribe to the MenuStack hasFocus events. */
758
+ _subscribeToMenuStackHasFocus() {
759
+ if (!this._parentMenu) {
760
+ this.menuStack.hasFocus.pipe(takeUntil(this.destroyed)).subscribe(hasFocus => {
761
+ if (!hasFocus) {
762
+ this.menuStack.closeAll();
763
+ }
764
+ });
765
+ }
766
+ }
767
+ /** Subscribe to the MenuStack closed events. */
768
+ _subscribeToMenuStackClosed() {
769
+ if (!this._parentMenu) {
770
+ this.menuStack.closed.subscribe(({ focusParentTrigger }) => {
771
+ if (focusParentTrigger && !this.menuStack.length()) {
772
+ this._elementRef.nativeElement.focus();
773
+ }
774
+ });
660
775
  }
661
776
  }
662
777
  }
663
- CdkMenuItemTrigger.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuItemTrigger, deps: [{ token: i0.ElementRef }, { token: i0.ViewContainerRef }, { token: i1.Overlay }, { token: i0.NgZone }, { token: CDK_MENU, optional: true }, { token: MENU_AIM, optional: true }, { token: i2.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
664
- CdkMenuItemTrigger.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkMenuItemTrigger, selector: "[cdkMenuTriggerFor]", inputs: { menuPanel: ["cdkMenuTriggerFor", "menuPanel"] }, outputs: { opened: "cdkMenuOpened", closed: "cdkMenuClosed" }, host: { attributes: { "aria-haspopup": "menu" }, listeners: { "keydown": "_toggleOnKeydown($event)", "click": "toggle()" }, properties: { "attr.aria-expanded": "isMenuOpen()" }, classAttribute: "cdk-menu-trigger" }, exportAs: ["cdkMenuTriggerFor"], ngImport: i0 });
665
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuItemTrigger, decorators: [{
778
+ CdkMenuTrigger.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuTrigger, deps: [{ token: i0.Injector }, { token: i0.ElementRef }, { token: i0.ViewContainerRef }, { token: i1.Overlay }, { token: i0.NgZone }, { token: MENU_STACK }, { token: CDK_MENU, optional: true }, { token: MENU_AIM, optional: true }, { token: i1$1.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
779
+ CdkMenuTrigger.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkMenuTrigger, selector: "[cdkMenuTriggerFor]", inputs: { menuTemplateRef: ["cdkMenuTriggerFor", "menuTemplateRef"], menuPosition: ["cdkMenuPosition", "menuPosition"] }, outputs: { opened: "cdkMenuOpened", closed: "cdkMenuClosed" }, host: { attributes: { "aria-haspopup": "menu" }, listeners: { "focusin": "_setHasFocus(true)", "focusout": "_setHasFocus(false)", "keydown": "_toggleOnKeydown($event)", "click": "toggle()" }, properties: { "attr.aria-expanded": "isOpen()" }, classAttribute: "cdk-menu-trigger" }, providers: [
780
+ { provide: MENU_TRIGGER, useExisting: CdkMenuTrigger },
781
+ PARENT_OR_NEW_MENU_STACK_PROVIDER,
782
+ ], exportAs: ["cdkMenuTriggerFor"], usesInheritance: true, ngImport: i0 });
783
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuTrigger, decorators: [{
666
784
  type: Directive,
667
785
  args: [{
668
786
  selector: '[cdkMenuTriggerFor]',
669
787
  exportAs: 'cdkMenuTriggerFor',
670
788
  host: {
671
- '(keydown)': '_toggleOnKeydown($event)',
672
- '(click)': 'toggle()',
673
789
  'class': 'cdk-menu-trigger',
674
790
  'aria-haspopup': 'menu',
675
- '[attr.aria-expanded]': 'isMenuOpen()',
791
+ '[attr.aria-expanded]': 'isOpen()',
792
+ '(focusin)': '_setHasFocus(true)',
793
+ '(focusout)': '_setHasFocus(false)',
794
+ '(keydown)': '_toggleOnKeydown($event)',
795
+ '(click)': 'toggle()',
676
796
  },
797
+ inputs: ['menuTemplateRef: cdkMenuTriggerFor', 'menuPosition: cdkMenuPosition'],
798
+ outputs: ['opened: cdkMenuOpened', 'closed: cdkMenuClosed'],
799
+ providers: [
800
+ { provide: MENU_TRIGGER, useExisting: CdkMenuTrigger },
801
+ PARENT_OR_NEW_MENU_STACK_PROVIDER,
802
+ ],
677
803
  }]
678
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ViewContainerRef }, { type: i1.Overlay }, { type: i0.NgZone }, { type: undefined, decorators: [{
804
+ }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.ElementRef }, { type: i0.ViewContainerRef }, { type: i1.Overlay }, { type: i0.NgZone }, { type: MenuStack, decorators: [{
805
+ type: Inject,
806
+ args: [MENU_STACK]
807
+ }] }, { type: undefined, decorators: [{
679
808
  type: Optional
680
809
  }, {
681
810
  type: Inject,
@@ -685,18 +814,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
685
814
  }, {
686
815
  type: Inject,
687
816
  args: [MENU_AIM]
688
- }] }, { type: i2.Directionality, decorators: [{
817
+ }] }, { type: i1$1.Directionality, decorators: [{
689
818
  type: Optional
690
- }] }]; }, propDecorators: { menuPanel: [{
691
- type: Input,
692
- args: ['cdkMenuTriggerFor']
693
- }], opened: [{
694
- type: Output,
695
- args: ['cdkMenuOpened']
696
- }], closed: [{
697
- type: Output,
698
- args: ['cdkMenuClosed']
699
- }] } });
819
+ }] }]; } });
700
820
 
701
821
  /**
702
822
  * @license
@@ -705,26 +825,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
705
825
  * Use of this source code is governed by an MIT-style license that can be
706
826
  * found in the LICENSE file at https://angular.io/license
707
827
  */
708
- // TODO refactor this to be configurable allowing for custom elements to be removed
709
- /** Removes all icons from within the given element. */
710
- function removeIcons(element) {
711
- for (const icon of Array.from(element.querySelectorAll('mat-icon, .material-icons'))) {
712
- icon.remove();
713
- }
714
- }
715
828
  /**
716
829
  * Directive which provides the ability for an element to be focused and navigated to using the
717
830
  * keyboard when residing in a CdkMenu, CdkMenuBar, or CdkMenuGroup. It performs user defined
718
831
  * behavior when clicked.
719
832
  */
720
833
  class CdkMenuItem {
721
- constructor(_elementRef, _ngZone, _parentMenu, _menuAim, _dir,
834
+ constructor(
835
+ /** The host element for this item. */
836
+ _elementRef,
837
+ /** The Angular zone. */
838
+ _ngZone,
839
+ /** The menu stack this item belongs to. */
840
+ _menuStack,
841
+ /** The parent menu this item belongs to. */
842
+ _parentMenu,
843
+ /** The menu aim service used for this item. */
844
+ _menuAim,
845
+ /** The directionality of the page. */
846
+ _dir,
722
847
  /** Reference to the CdkMenuItemTrigger directive if one is added to the same element */
723
- // `CdkMenuItem` is commonly used in combination with a `CdkMenuItemTrigger`.
724
848
  // tslint:disable-next-line: lightweight-tokens
725
849
  _menuTrigger) {
726
850
  this._elementRef = _elementRef;
727
851
  this._ngZone = _ngZone;
852
+ this._menuStack = _menuStack;
728
853
  this._parentMenu = _parentMenu;
729
854
  this._menuAim = _menuAim;
730
855
  this._dir = _dir;
@@ -735,13 +860,17 @@ class CdkMenuItem {
735
860
  * event.
736
861
  */
737
862
  this.triggered = new EventEmitter();
863
+ /** Whether the menu item opens a menu. */
864
+ this.hasMenu = !!this._menuTrigger;
738
865
  /**
739
866
  * The tabindex for this menu item managed internally and used for implementing roving a
740
867
  * tab index.
741
868
  */
742
869
  this._tabindex = -1;
870
+ /** Whether the item should close the menu if triggered by the spacebar. */
871
+ this.closeOnSpacebarTrigger = true;
743
872
  /** Emits when the menu item is destroyed. */
744
- this._destroyed = new Subject();
873
+ this.destroyed = new Subject();
745
874
  this._setupMouseEnter();
746
875
  if (this._isStandaloneItem()) {
747
876
  this._tabindex = 0;
@@ -754,50 +883,32 @@ class CdkMenuItem {
754
883
  set disabled(value) {
755
884
  this._disabled = coerceBooleanProperty(value);
756
885
  }
886
+ ngOnDestroy() {
887
+ this.destroyed.next();
888
+ this.destroyed.complete();
889
+ }
757
890
  /** Place focus on the element. */
758
891
  focus() {
759
892
  this._elementRef.nativeElement.focus();
760
893
  }
761
- /** Reset the _tabindex to -1. */
762
- _resetTabIndex() {
763
- if (!this._isStandaloneItem()) {
764
- this._tabindex = -1;
765
- }
766
- }
767
- /**
768
- * Set the tab index to 0 if not disabled and it's a focus event, or a mouse enter if this element
769
- * is not in a menu bar.
770
- */
771
- _setTabIndex(event) {
772
- if (this.disabled) {
773
- return;
774
- }
775
- // don't set the tabindex if there are no open sibling or parent menus
776
- if (!event || !this._getMenuStack()?.isEmpty()) {
777
- this._tabindex = 0;
778
- }
779
- }
780
- /** Whether this menu item is standalone or within a menu or menu bar. */
781
- _isStandaloneItem() {
782
- return !this._parentMenu;
783
- }
784
894
  /**
785
895
  * If the menu item is not disabled and the element does not have a menu trigger attached, emit
786
896
  * on the cdkMenuItemTriggered emitter and close all open menus.
897
+ * @param options Options the configure how the item is triggered
898
+ * - keepOpen: specifies that the menu should be kept open after triggering the item.
787
899
  */
788
- trigger() {
789
- if (!this.disabled && !this.hasMenu()) {
900
+ trigger(options) {
901
+ const { keepOpen } = { ...options };
902
+ if (!this.disabled && !this.hasMenu) {
790
903
  this.triggered.next();
791
- this._getMenuStack()?.closeAll();
904
+ if (!keepOpen) {
905
+ this._menuStack.closeAll({ focusParentTrigger: true });
906
+ }
792
907
  }
793
908
  }
794
- /** Whether the menu item opens a menu. */
795
- hasMenu() {
796
- return !!this._menuTrigger?.hasMenu();
797
- }
798
909
  /** Return true if this MenuItem has an attached menu and it is open. */
799
910
  isMenuOpen() {
800
- return !!this._menuTrigger?.isMenuOpen();
911
+ return !!this._menuTrigger?.isOpen();
801
912
  }
802
913
  /**
803
914
  * Get a reference to the rendered Menu if the Menu is open and it is visible in the DOM.
@@ -806,20 +917,35 @@ class CdkMenuItem {
806
917
  getMenu() {
807
918
  return this._menuTrigger?.getMenu();
808
919
  }
809
- /** Get the MenuItemTrigger associated with this element. */
920
+ /** Get the CdkMenuTrigger associated with this element. */
810
921
  getMenuTrigger() {
811
922
  return this._menuTrigger;
812
923
  }
813
924
  /** Get the label for this element which is required by the FocusableOption interface. */
814
925
  getLabel() {
815
- // TODO cloning the tree may be expensive; implement a better method
816
- // we know that the current node is an element type
817
- const clone = this._elementRef.nativeElement.cloneNode(true);
818
- removeIcons(clone);
819
- return clone.textContent?.trim() || '';
926
+ return this.typeaheadLabel || this._elementRef.nativeElement.textContent?.trim() || '';
820
927
  }
821
- /**
822
- * Handles keyboard events for the menu item, specifically either triggering the user defined
928
+ /** Reset the tabindex to -1. */
929
+ _resetTabIndex() {
930
+ if (!this._isStandaloneItem()) {
931
+ this._tabindex = -1;
932
+ }
933
+ }
934
+ /**
935
+ * Set the tab index to 0 if not disabled and it's a focus event, or a mouse enter if this element
936
+ * is not in a menu bar.
937
+ */
938
+ _setTabIndex(event) {
939
+ if (this.disabled) {
940
+ return;
941
+ }
942
+ // don't set the tabindex if there are no open sibling or parent menus
943
+ if (!event || !this._menuStack.isEmpty()) {
944
+ this._tabindex = 0;
945
+ }
946
+ }
947
+ /**
948
+ * Handles keyboard events for the menu item, specifically either triggering the user defined
823
949
  * callback or opening/closing the current menu based on whether the left or right arrow key was
824
950
  * pressed.
825
951
  * @param event the keyboard event to handle
@@ -828,36 +954,79 @@ class CdkMenuItem {
828
954
  switch (event.keyCode) {
829
955
  case SPACE:
830
956
  case ENTER:
831
- event.preventDefault();
832
- this.trigger();
957
+ if (!hasModifierKey(event)) {
958
+ event.preventDefault();
959
+ this.trigger({ keepOpen: event.keyCode === SPACE && !this.closeOnSpacebarTrigger });
960
+ }
833
961
  break;
834
962
  case RIGHT_ARROW:
835
- if (this._parentMenu && this._isParentVertical() && !this.hasMenu()) {
836
- event.preventDefault();
837
- this._dir?.value === 'rtl'
838
- ? this._getMenuStack()?.close(this._parentMenu, 1 /* previousItem */)
839
- : this._getMenuStack()?.closeAll(0 /* nextItem */);
963
+ if (!hasModifierKey(event)) {
964
+ if (this._parentMenu && this._isParentVertical()) {
965
+ if (this._dir?.value !== 'rtl') {
966
+ this._forwardArrowPressed(event);
967
+ }
968
+ else {
969
+ this._backArrowPressed(event);
970
+ }
971
+ }
840
972
  }
841
973
  break;
842
974
  case LEFT_ARROW:
843
- if (this._parentMenu && this._isParentVertical() && !this.hasMenu()) {
844
- event.preventDefault();
845
- this._dir?.value === 'rtl'
846
- ? this._getMenuStack()?.closeAll(0 /* nextItem */)
847
- : this._getMenuStack()?.close(this._parentMenu, 1 /* previousItem */);
975
+ if (!hasModifierKey(event)) {
976
+ if (this._parentMenu && this._isParentVertical()) {
977
+ if (this._dir?.value !== 'rtl') {
978
+ this._backArrowPressed(event);
979
+ }
980
+ else {
981
+ this._forwardArrowPressed(event);
982
+ }
983
+ }
848
984
  }
849
985
  break;
850
986
  }
851
987
  }
988
+ /** Whether this menu item is standalone or within a menu or menu bar. */
989
+ _isStandaloneItem() {
990
+ return !this._parentMenu;
991
+ }
992
+ /**
993
+ * Handles the user pressing the back arrow key.
994
+ * @param event The keyboard event.
995
+ */
996
+ _backArrowPressed(event) {
997
+ const parentMenu = this._parentMenu;
998
+ if (this._menuStack.hasInlineMenu() || this._menuStack.length() > 1) {
999
+ event.preventDefault();
1000
+ this._menuStack.close(parentMenu, {
1001
+ focusNextOnEmpty: this._menuStack.inlineMenuOrientation() === 'horizontal'
1002
+ ? 1 /* previousItem */
1003
+ : 2 /* currentItem */,
1004
+ focusParentTrigger: true,
1005
+ });
1006
+ }
1007
+ }
1008
+ /**
1009
+ * Handles the user pressing the forward arrow key.
1010
+ * @param event The keyboard event.
1011
+ */
1012
+ _forwardArrowPressed(event) {
1013
+ if (!this.hasMenu && this._menuStack.inlineMenuOrientation() === 'horizontal') {
1014
+ event.preventDefault();
1015
+ this._menuStack.closeAll({
1016
+ focusNextOnEmpty: 0 /* nextItem */,
1017
+ focusParentTrigger: true,
1018
+ });
1019
+ }
1020
+ }
852
1021
  /**
853
1022
  * Subscribe to the mouseenter events and close any sibling menu items if this element is moused
854
1023
  * into.
855
1024
  */
856
1025
  _setupMouseEnter() {
857
1026
  if (!this._isStandaloneItem()) {
858
- const closeOpenSiblings = () => this._ngZone.run(() => this._getMenuStack()?.closeSubMenuOf(this._parentMenu));
1027
+ const closeOpenSiblings = () => this._ngZone.run(() => this._menuStack.closeSubMenuOf(this._parentMenu));
859
1028
  this._ngZone.runOutsideAngular(() => fromEvent(this._elementRef.nativeElement, 'mouseenter')
860
- .pipe(filter(() => !this._getMenuStack()?.isEmpty() && !this.hasMenu()), takeUntil(this._destroyed))
1029
+ .pipe(filter(() => !this._menuStack.isEmpty() && !this.hasMenu), takeUntil(this.destroyed))
861
1030
  .subscribe(() => {
862
1031
  if (this._menuAim) {
863
1032
  this._menuAim.toggle(closeOpenSiblings);
@@ -875,39 +1044,29 @@ class CdkMenuItem {
875
1044
  _isParentVertical() {
876
1045
  return this._parentMenu?.orientation === 'vertical';
877
1046
  }
878
- /** Get the MenuStack from the parent menu. */
879
- _getMenuStack() {
880
- // We use a function since at the construction of the MenuItemTrigger the parent Menu won't have
881
- // its menu stack set. Therefore we need to reference the menu stack from the parent each time
882
- // we want to use it.
883
- return this._parentMenu?._menuStack;
884
- }
885
- ngOnDestroy() {
886
- this._destroyed.next();
887
- }
888
1047
  }
889
- CdkMenuItem.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuItem, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: CDK_MENU, optional: true }, { token: MENU_AIM, optional: true }, { token: i2.Directionality, optional: true }, { token: CdkMenuItemTrigger, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive });
890
- CdkMenuItem.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkMenuItem, selector: "[cdkMenuItem]", inputs: { disabled: "disabled" }, outputs: { triggered: "cdkMenuItemTriggered" }, host: { attributes: { "type": "button", "role": "menuitem" }, listeners: { "blur": "_resetTabIndex()", "mouseout": "_resetTabIndex()", "focus": "_setTabIndex()", "mouseenter": "_setTabIndex($event)", "click": "trigger()", "keydown": "_onKeydown($event)" }, properties: { "tabindex": "_tabindex", "attr.aria-disabled": "disabled || null" }, classAttribute: "cdk-menu-item" }, exportAs: ["cdkMenuItem"], ngImport: i0 });
891
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuItem, decorators: [{
1048
+ CdkMenuItem.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuItem, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: MENU_STACK }, { token: CDK_MENU, optional: true }, { token: MENU_AIM, optional: true }, { token: i1$1.Directionality, optional: true }, { token: CdkMenuTrigger, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive });
1049
+ CdkMenuItem.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkMenuItem, selector: "[cdkMenuItem]", inputs: { disabled: ["cdkMenuItemDisabled", "disabled"], typeaheadLabel: ["cdkMenuitemTypeaheadLabel", "typeaheadLabel"] }, outputs: { triggered: "cdkMenuItemTriggered" }, host: { attributes: { "role": "menuitem" }, listeners: { "blur": "_resetTabIndex()", "focus": "_setTabIndex()", "click": "trigger()", "keydown": "_onKeydown($event)" }, properties: { "tabindex": "_tabindex", "attr.aria-disabled": "disabled || null" }, classAttribute: "cdk-menu-item" }, exportAs: ["cdkMenuItem"], ngImport: i0 });
1050
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuItem, decorators: [{
892
1051
  type: Directive,
893
1052
  args: [{
894
1053
  selector: '[cdkMenuItem]',
895
1054
  exportAs: 'cdkMenuItem',
896
1055
  host: {
897
- '[tabindex]': '_tabindex',
898
- 'type': 'button',
899
1056
  'role': 'menuitem',
900
1057
  'class': 'cdk-menu-item',
1058
+ '[tabindex]': '_tabindex',
901
1059
  '[attr.aria-disabled]': 'disabled || null',
902
1060
  '(blur)': '_resetTabIndex()',
903
- '(mouseout)': '_resetTabIndex()',
904
1061
  '(focus)': '_setTabIndex()',
905
- '(mouseenter)': '_setTabIndex($event)',
906
1062
  '(click)': 'trigger()',
907
1063
  '(keydown)': '_onKeydown($event)',
908
1064
  },
909
1065
  }]
910
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: undefined, decorators: [{
1066
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: MenuStack, decorators: [{
1067
+ type: Inject,
1068
+ args: [MENU_STACK]
1069
+ }] }, { type: undefined, decorators: [{
911
1070
  type: Optional
912
1071
  }, {
913
1072
  type: Inject,
@@ -917,14 +1076,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
917
1076
  }, {
918
1077
  type: Inject,
919
1078
  args: [MENU_AIM]
920
- }] }, { type: i2.Directionality, decorators: [{
1079
+ }] }, { type: i1$1.Directionality, decorators: [{
921
1080
  type: Optional
922
- }] }, { type: CdkMenuItemTrigger, decorators: [{
1081
+ }] }, { type: CdkMenuTrigger, decorators: [{
923
1082
  type: Self
924
1083
  }, {
925
1084
  type: Optional
926
1085
  }] }]; }, propDecorators: { disabled: [{
927
- type: Input
1086
+ type: Input,
1087
+ args: ['cdkMenuItemDisabled']
1088
+ }], typeaheadLabel: [{
1089
+ type: Input,
1090
+ args: ['cdkMenuitemTypeaheadLabel']
928
1091
  }], triggered: [{
929
1092
  type: Output,
930
1093
  args: ['cdkMenuItemTriggered']
@@ -937,51 +1100,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
937
1100
  * Use of this source code is governed by an MIT-style license that can be
938
1101
  * found in the LICENSE file at https://angular.io/license
939
1102
  */
940
- /** Counter used to set a unique id and name for a selectable item */
941
- let nextId = 0;
942
1103
  /**
943
- * Base class providing checked state for MenuItems along with outputting a clicked event when the
944
- * element is triggered. It provides functionality for selectable elements.
1104
+ * PointerFocusTracker keeps track of the currently active item under mouse focus. It also has
1105
+ * observables which emit when the users mouse enters and leaves a tracked element.
945
1106
  */
946
- class CdkMenuItemSelectable extends CdkMenuItem {
947
- constructor() {
948
- super(...arguments);
949
- /** Event emitted when the selectable item is clicked */
950
- this.toggled = new EventEmitter();
951
- this._checked = false;
952
- /** The name of the selectable element with a default value */
953
- this.name = `cdk-selectable-item-${nextId++}`;
954
- /** The id of the selectable element with a default value */
955
- this.id = `cdk-selectable-item-${nextId++}`;
1107
+ class PointerFocusTracker {
1108
+ constructor(
1109
+ /** The list of items being tracked. */
1110
+ _items) {
1111
+ this._items = _items;
1112
+ /** Emits when an element is moused into. */
1113
+ this.entered = this._getItemPointerEntries();
1114
+ /** Emits when an element is moused out. */
1115
+ this.exited = this._getItemPointerExits();
1116
+ /** Emits when this is destroyed. */
1117
+ this._destroyed = new Subject();
1118
+ this.entered.subscribe(element => (this.activeElement = element));
1119
+ this.exited.subscribe(() => {
1120
+ this.previousElement = this.activeElement;
1121
+ this.activeElement = undefined;
1122
+ });
956
1123
  }
957
- /** Whether the element is checked */
958
- get checked() {
959
- return this._checked;
1124
+ /** Stop the managers listeners. */
1125
+ destroy() {
1126
+ this._destroyed.next();
1127
+ this._destroyed.complete();
960
1128
  }
961
- set checked(value) {
962
- this._checked = coerceBooleanProperty(value);
1129
+ /**
1130
+ * Gets a stream of pointer (mouse) entries into the given items.
1131
+ * This should typically run outside the Angular zone.
1132
+ */
1133
+ _getItemPointerEntries() {
1134
+ return defer(() => this._items.changes.pipe(startWith(this._items), mergeMap((list) => list.map(element => fromEvent(element._elementRef.nativeElement, 'mouseenter').pipe(mapTo(element), takeUntil(this._items.changes)))), mergeAll()));
963
1135
  }
964
- /** If the element is not disabled emit the click event */
965
- trigger() {
966
- if (!this.disabled) {
967
- this.toggled.next(this);
968
- }
1136
+ /**
1137
+ * Gets a stream of pointer (mouse) exits out of the given items.
1138
+ * This should typically run outside the Angular zone.
1139
+ */
1140
+ _getItemPointerExits() {
1141
+ return defer(() => this._items.changes.pipe(startWith(this._items), mergeMap((list) => list.map(element => fromEvent(element._elementRef.nativeElement, 'mouseout').pipe(mapTo(element), takeUntil(this._items.changes)))), mergeAll()));
969
1142
  }
970
1143
  }
971
- CdkMenuItemSelectable.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuItemSelectable, deps: null, target: i0.ɵɵFactoryTarget.Directive });
972
- CdkMenuItemSelectable.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkMenuItemSelectable, inputs: { checked: "checked", name: "name", id: "id" }, outputs: { toggled: "cdkMenuItemToggled" }, usesInheritance: true, ngImport: i0 });
973
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuItemSelectable, decorators: [{
974
- type: Directive
975
- }], propDecorators: { toggled: [{
976
- type: Output,
977
- args: ['cdkMenuItemToggled']
978
- }], checked: [{
979
- type: Input
980
- }], name: [{
981
- type: Input
982
- }], id: [{
983
- type: Input
984
- }] } });
985
1144
 
986
1145
  /**
987
1146
  * @license
@@ -990,143 +1149,188 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
990
1149
  * Use of this source code is governed by an MIT-style license that can be
991
1150
  * found in the LICENSE file at https://angular.io/license
992
1151
  */
1152
+ /** Counter used to create unique IDs for menus. */
1153
+ let nextId$1 = 0;
993
1154
  /**
994
- * Directive which acts as a grouping container for `CdkMenuItem` instances with
995
- * `role="menuitemradio"`, similar to a `role="radiogroup"` element.
1155
+ * Abstract directive that implements shared logic common to all menus.
1156
+ * This class can be extended to create custom menu types.
996
1157
  */
997
- class CdkMenuGroup {
998
- constructor() {
999
- /** Emits the element when checkbox or radiobutton state changed */
1000
- this.change = new EventEmitter();
1001
- /** Emits when the _selectableItems QueryList triggers a change */
1002
- this._selectableChanges = new EventEmitter();
1158
+ class CdkMenuBase extends CdkMenuGroup {
1159
+ constructor(
1160
+ /** The host element. */
1161
+ elementRef,
1162
+ /** The Angular zone. */
1163
+ ngZone,
1164
+ /** The stack of menus this menu belongs to. */
1165
+ menuStack,
1166
+ /** The menu aim service used by this menu. */
1167
+ menuAim,
1168
+ /** The directionality of the current page. */
1169
+ dir) {
1170
+ super();
1171
+ this.ngZone = ngZone;
1172
+ this.menuStack = menuStack;
1173
+ this.menuAim = menuAim;
1174
+ this.dir = dir;
1175
+ /** The id of the menu's host element. */
1176
+ this.id = `cdk-menu-${nextId$1++}`;
1177
+ /** The direction items in the menu flow. */
1178
+ this.orientation = 'vertical';
1179
+ /** Whether the menu is displayed inline (i.e. always present vs a conditional popup that the user triggers with a trigger element). */
1180
+ this.isInline = false;
1181
+ /** Emits when the MenuBar is destroyed. */
1182
+ this.destroyed = new Subject();
1183
+ /** Whether this menu's menu stack has focus. */
1184
+ this._menuStackHasFocus = false;
1185
+ this.nativeElement = elementRef.nativeElement;
1003
1186
  }
1004
1187
  ngAfterContentInit() {
1005
- this._registerMenuSelectionListeners();
1188
+ if (!this.isInline) {
1189
+ this.menuStack.push(this);
1190
+ }
1191
+ this._setKeyManager();
1192
+ this._subscribeToMenuStackHasFocus();
1193
+ this._subscribeToMenuOpen();
1194
+ this._subscribeToMenuStackClosed();
1195
+ this._setUpPointerTracker();
1196
+ }
1197
+ ngOnDestroy() {
1198
+ this.destroyed.next();
1199
+ this.destroyed.complete();
1200
+ this.pointerTracker?.destroy();
1006
1201
  }
1007
1202
  /**
1008
- * Register the child selectable elements with the change emitter and ensure any new child
1009
- * elements do so as well.
1203
+ * Place focus on the first MenuItem in the menu and set the focus origin.
1204
+ * @param focusOrigin The origin input mode of the focus event.
1010
1205
  */
1011
- _registerMenuSelectionListeners() {
1012
- this._selectableItems.forEach(selectable => this._registerClickListener(selectable));
1013
- this._selectableItems.changes.subscribe((selectableItems) => {
1014
- this._selectableChanges.next();
1015
- selectableItems.forEach(selectable => this._registerClickListener(selectable));
1016
- });
1017
- }
1018
- /** Register each selectable to emit on the change Emitter when clicked */
1019
- _registerClickListener(selectable) {
1020
- selectable.toggled
1021
- .pipe(takeUntil(this._selectableChanges))
1022
- .subscribe(() => this.change.next(selectable));
1206
+ focusFirstItem(focusOrigin = 'program') {
1207
+ this.keyManager.setFocusOrigin(focusOrigin);
1208
+ this.keyManager.setFirstItemActive();
1023
1209
  }
1024
- ngOnDestroy() {
1025
- this._selectableChanges.next();
1026
- this._selectableChanges.complete();
1210
+ /**
1211
+ * Place focus on the last MenuItem in the menu and set the focus origin.
1212
+ * @param focusOrigin The origin input mode of the focus event.
1213
+ */
1214
+ focusLastItem(focusOrigin = 'program') {
1215
+ this.keyManager.setFocusOrigin(focusOrigin);
1216
+ this.keyManager.setLastItemActive();
1027
1217
  }
1028
- }
1029
- CdkMenuGroup.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1030
- CdkMenuGroup.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkMenuGroup, selector: "[cdkMenuGroup]", outputs: { change: "change" }, host: { attributes: { "role": "group" }, classAttribute: "cdk-menu-group" }, providers: [{ provide: UniqueSelectionDispatcher, useClass: UniqueSelectionDispatcher }], queries: [{ propertyName: "_selectableItems", predicate: CdkMenuItemSelectable, descendants: true }], exportAs: ["cdkMenuGroup"], ngImport: i0 });
1031
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuGroup, decorators: [{
1032
- type: Directive,
1033
- args: [{
1034
- selector: '[cdkMenuGroup]',
1035
- exportAs: 'cdkMenuGroup',
1036
- host: {
1037
- 'role': 'group',
1038
- 'class': 'cdk-menu-group',
1039
- },
1040
- providers: [{ provide: UniqueSelectionDispatcher, useClass: UniqueSelectionDispatcher }],
1041
- }]
1042
- }], propDecorators: { change: [{
1043
- type: Output
1044
- }], _selectableItems: [{
1045
- type: ContentChildren,
1046
- args: [CdkMenuItemSelectable, { descendants: true }]
1047
- }] } });
1048
-
1049
- /**
1050
- * @license
1051
- * Copyright Google LLC All Rights Reserved.
1052
- *
1053
- * Use of this source code is governed by an MIT-style license that can be
1054
- * found in the LICENSE file at https://angular.io/license
1055
- */
1056
- /**
1057
- * Directive applied to an ng-template which wraps a CdkMenu and provides a reference to the
1058
- * child element it wraps which allows for opening of the CdkMenu in an overlay.
1059
- */
1060
- class CdkMenuPanel {
1061
- constructor(_templateRef) {
1062
- this._templateRef = _templateRef;
1218
+ /** Gets the tabindex for this menu. */
1219
+ _getTabIndex() {
1220
+ const tabindexIfInline = this._menuStackHasFocus ? -1 : 0;
1221
+ return this.isInline ? tabindexIfInline : null;
1063
1222
  }
1064
1223
  /**
1065
- * Set the Menu component on the menu panel. Since we cannot use ContentChild to fetch the
1066
- * child Menu component, the child Menu must register its self with the parent MenuPanel.
1224
+ * Close the open menu if the current active item opened the requested MenuStackItem.
1225
+ * @param menu The menu requested to be closed.
1226
+ * @param options Options to configure the behavior on close.
1227
+ * - `focusParentTrigger` Whether to focus the parent trigger after closing the menu.
1067
1228
  */
1068
- _registerMenu(child) {
1069
- this._menu = child;
1070
- // The ideal solution would be to affect the CdkMenuPanel injector from the CdkMenuTrigger and
1071
- // inject the menu stack reference into the child menu and menu items, however this isn't
1072
- // possible at this time.
1073
- this._menu._menuStack = this._menuStack;
1074
- this._menuStack?.push(child);
1229
+ closeOpenMenu(menu, options) {
1230
+ const { focusParentTrigger } = { ...options };
1231
+ const keyManager = this.keyManager;
1232
+ const trigger = this.triggerItem;
1233
+ if (menu === trigger?.getMenuTrigger()?.getMenu()) {
1234
+ trigger?.getMenuTrigger()?.close();
1235
+ // If the user has moused over a sibling item we want to focus the element under mouse focus
1236
+ // not the trigger which previously opened the now closed menu.
1237
+ if (focusParentTrigger) {
1238
+ if (trigger) {
1239
+ keyManager.setActiveItem(trigger);
1240
+ }
1241
+ else {
1242
+ keyManager.setFirstItemActive();
1243
+ }
1244
+ }
1245
+ }
1075
1246
  }
1076
- }
1077
- CdkMenuPanel.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuPanel, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
1078
- CdkMenuPanel.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkMenuPanel, selector: "ng-template[cdkMenuPanel]", exportAs: ["cdkMenuPanel"], ngImport: i0 });
1079
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuPanel, decorators: [{
1080
- type: Directive,
1081
- args: [{ selector: 'ng-template[cdkMenuPanel]', exportAs: 'cdkMenuPanel' }]
1082
- }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
1083
-
1084
- /**
1085
- * @license
1086
- * Copyright Google LLC All Rights Reserved.
1087
- *
1088
- * Use of this source code is governed by an MIT-style license that can be
1089
- * found in the LICENSE file at https://angular.io/license
1090
- */
1091
- /**
1092
- * PointerFocusTracker keeps track of the currently active item under mouse focus. It also has
1093
- * observables which emit when the users mouse enters and leaves a tracked element.
1094
- */
1095
- class PointerFocusTracker {
1096
- constructor(_items) {
1097
- this._items = _items;
1098
- /** Emits when an element is moused into. */
1099
- this.entered = this._getItemPointerEntries();
1100
- /** Emits when an element is moused out. */
1101
- this.exited = this._getItemPointerExits();
1102
- /** Emits when this is destroyed. */
1103
- this._destroyed = new Subject();
1104
- this.entered.subscribe(element => (this.activeElement = element));
1105
- this.exited.subscribe(() => {
1106
- this.previousElement = this.activeElement;
1107
- this.activeElement = undefined;
1108
- });
1247
+ /** Setup the FocusKeyManager with the correct orientation for the menu. */
1248
+ _setKeyManager() {
1249
+ this.keyManager = new FocusKeyManager(this.items).withWrap().withTypeAhead().withHomeAndEnd();
1250
+ if (this.orientation === 'horizontal') {
1251
+ this.keyManager.withHorizontalOrientation(this.dir?.value || 'ltr');
1252
+ }
1253
+ else {
1254
+ this.keyManager.withVerticalOrientation();
1255
+ }
1109
1256
  }
1110
1257
  /**
1111
- * Gets a stream of pointer (mouse) entries into the given items.
1112
- * This should typically run outside the Angular zone.
1258
+ * Subscribe to the menu trigger's open events in order to track the trigger which opened the menu
1259
+ * and stop tracking it when the menu is closed.
1113
1260
  */
1114
- _getItemPointerEntries() {
1115
- return defer(() => this._items.changes.pipe(startWith(this._items), mergeMap((list) => list.map(element => fromEvent(element._elementRef.nativeElement, 'mouseenter').pipe(mapTo(element), takeUntil(this._items.changes)))), mergeAll()));
1261
+ _subscribeToMenuOpen() {
1262
+ const exitCondition = merge(this.items.changes, this.destroyed);
1263
+ this.items.changes
1264
+ .pipe(startWith(this.items), mergeMap((list) => list
1265
+ .filter(item => item.hasMenu)
1266
+ .map(item => item.getMenuTrigger().opened.pipe(mapTo(item), takeUntil(exitCondition)))), mergeAll(), switchMap((item) => {
1267
+ this.triggerItem = item;
1268
+ return item.getMenuTrigger().closed;
1269
+ }), takeUntil(this.destroyed))
1270
+ .subscribe(() => (this.triggerItem = undefined));
1271
+ }
1272
+ /** Subscribe to the MenuStack close events. */
1273
+ _subscribeToMenuStackClosed() {
1274
+ this.menuStack.closed
1275
+ .pipe(takeUntil(this.destroyed))
1276
+ .subscribe(({ item, focusParentTrigger }) => this.closeOpenMenu(item, { focusParentTrigger }));
1277
+ }
1278
+ /** Subscribe to the MenuStack hasFocus events. */
1279
+ _subscribeToMenuStackHasFocus() {
1280
+ if (this.isInline) {
1281
+ this.menuStack.hasFocus.pipe(takeUntil(this.destroyed)).subscribe(hasFocus => {
1282
+ this._menuStackHasFocus = hasFocus;
1283
+ });
1284
+ }
1116
1285
  }
1117
1286
  /**
1118
- * Gets a stream of pointer (mouse) exits out of the given items.
1119
- * This should typically run outside the Angular zone.
1287
+ * Set the PointerFocusTracker and ensure that when mouse focus changes the key manager is updated
1288
+ * with the latest menu item under mouse focus.
1120
1289
  */
1121
- _getItemPointerExits() {
1122
- return defer(() => this._items.changes.pipe(startWith(this._items), mergeMap((list) => list.map(element => fromEvent(element._elementRef.nativeElement, 'mouseout').pipe(mapTo(element), takeUntil(this._items.changes)))), mergeAll()));
1123
- }
1124
- /** Stop the managers listeners. */
1125
- destroy() {
1126
- this._destroyed.next();
1127
- this._destroyed.complete();
1290
+ _setUpPointerTracker() {
1291
+ if (this.menuAim) {
1292
+ this.ngZone.runOutsideAngular(() => {
1293
+ this.pointerTracker = new PointerFocusTracker(this.items);
1294
+ });
1295
+ this.menuAim.initialize(this, this.pointerTracker);
1296
+ }
1128
1297
  }
1129
1298
  }
1299
+ CdkMenuBase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuBase, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: MENU_STACK }, { token: MENU_AIM, optional: true, self: true }, { token: i1$1.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
1300
+ CdkMenuBase.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkMenuBase, inputs: { id: "id" }, host: { attributes: { "role": "menu" }, listeners: { "focus": "focusFirstItem()", "focusin": "menuStack.setHasFocus(true)", "focusout": "menuStack.setHasFocus(false)" }, properties: { "tabindex": "_getTabIndex()", "id": "id", "attr.aria-orientation": "orientation", "attr.data-cdk-menu-stack-id": "menuStack.id" } }, queries: [{ propertyName: "items", predicate: CdkMenuItem, descendants: true }], usesInheritance: true, ngImport: i0 });
1301
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuBase, decorators: [{
1302
+ type: Directive,
1303
+ args: [{
1304
+ host: {
1305
+ 'role': 'menu',
1306
+ 'class': '',
1307
+ '[tabindex]': '_getTabIndex()',
1308
+ '[id]': 'id',
1309
+ '[attr.aria-orientation]': 'orientation',
1310
+ '[attr.data-cdk-menu-stack-id]': 'menuStack.id',
1311
+ '(focus)': 'focusFirstItem()',
1312
+ '(focusin)': 'menuStack.setHasFocus(true)',
1313
+ '(focusout)': 'menuStack.setHasFocus(false)',
1314
+ },
1315
+ }]
1316
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: MenuStack, decorators: [{
1317
+ type: Inject,
1318
+ args: [MENU_STACK]
1319
+ }] }, { type: undefined, decorators: [{
1320
+ type: Self
1321
+ }, {
1322
+ type: Optional
1323
+ }, {
1324
+ type: Inject,
1325
+ args: [MENU_AIM]
1326
+ }] }, { type: i1$1.Directionality, decorators: [{
1327
+ type: Optional
1328
+ }] }]; }, propDecorators: { id: [{
1329
+ type: Input
1330
+ }], items: [{
1331
+ type: ContentChildren,
1332
+ args: [CdkMenuItem, { descendants: true }]
1333
+ }] } });
1130
1334
 
1131
1335
  /**
1132
1336
  * @license
@@ -1142,67 +1346,49 @@ class PointerFocusTracker {
1142
1346
  *
1143
1347
  * It also acts as a RadioGroup for elements marked with role `menuitemradio`.
1144
1348
  */
1145
- class CdkMenu extends CdkMenuGroup {
1146
- constructor(_ngZone, _elementRef, _menuAim, _dir,
1147
- // `CdkMenuPanel` is always used in combination with a `CdkMenu`.
1148
- // tslint:disable-next-line: lightweight-tokens
1149
- _menuPanel) {
1150
- super();
1151
- this._ngZone = _ngZone;
1152
- this._elementRef = _elementRef;
1153
- this._menuAim = _menuAim;
1154
- this._dir = _dir;
1155
- this._menuPanel = _menuPanel;
1156
- /**
1157
- * Sets the aria-orientation attribute and determines where menus will be opened.
1158
- * Does not affect styling/layout.
1159
- */
1160
- this.orientation = 'vertical';
1349
+ class CdkMenu extends CdkMenuBase {
1350
+ constructor(
1351
+ /** The host element. */
1352
+ elementRef,
1353
+ /** The Angular zone. */
1354
+ ngZone,
1355
+ /** The menu stack this menu is part of. */
1356
+ menuStack,
1357
+ /** The trigger that opened this menu. */
1358
+ _parentTrigger,
1359
+ /** The menu aim service used by this menu. */
1360
+ menuAim,
1361
+ /** The directionality of the page. */
1362
+ dir) {
1363
+ super(elementRef, ngZone, menuStack, menuAim, dir);
1364
+ this._parentTrigger = _parentTrigger;
1161
1365
  /** Event emitted when the menu is closed. */
1162
1366
  this.closed = new EventEmitter();
1163
- // We provide a default MenuStack implementation in case the menu is an inline menu.
1164
- // For Menus part of a MenuBar nested within a MenuPanel this will be overwritten
1165
- // to the correct parent MenuStack.
1166
- /** Track the Menus making up the open menu stack. */
1167
- this._menuStack = new NoopMenuStack();
1168
- }
1169
- ngOnInit() {
1170
- this._registerWithParentPanel();
1367
+ /** The direction items in the menu flow. */
1368
+ this.orientation = 'vertical';
1369
+ /** Whether the menu is displayed inline (i.e. always present vs a conditional popup that the user triggers with a trigger element). */
1370
+ this.isInline = !this._parentTrigger;
1371
+ this.destroyed.subscribe(this.closed);
1372
+ this._parentTrigger?.registerChildMenu(this);
1171
1373
  }
1172
1374
  ngAfterContentInit() {
1173
1375
  super.ngAfterContentInit();
1174
- this._completeChangeEmitter();
1175
- this._setKeyManager();
1176
- this._subscribeToMenuOpen();
1177
- this._subscribeToMenuStack();
1178
- this._subscribeToMouseManager();
1179
- this._menuAim?.initialize(this, this._pointerTracker);
1376
+ this._subscribeToMenuStackEmptied();
1180
1377
  }
1181
- /** Place focus on the first MenuItem in the menu and set the focus origin. */
1182
- focusFirstItem(focusOrigin = 'program') {
1183
- this._keyManager.setFocusOrigin(focusOrigin);
1184
- this._keyManager.setFirstItemActive();
1185
- }
1186
- /** Place focus on the last MenuItem in the menu and set the focus origin. */
1187
- focusLastItem(focusOrigin = 'program') {
1188
- this._keyManager.setFocusOrigin(focusOrigin);
1189
- this._keyManager.setLastItemActive();
1378
+ ngOnDestroy() {
1379
+ super.ngOnDestroy();
1380
+ this.closed.complete();
1190
1381
  }
1191
- /** Handle keyboard events for the Menu. */
1382
+ /**
1383
+ * Handle keyboard events for the Menu.
1384
+ * @param event The keyboard event to be handled.
1385
+ */
1192
1386
  _handleKeyEvent(event) {
1193
- const keyManager = this._keyManager;
1387
+ const keyManager = this.keyManager;
1194
1388
  switch (event.keyCode) {
1195
1389
  case LEFT_ARROW:
1196
1390
  case RIGHT_ARROW:
1197
- if (this._isHorizontal()) {
1198
- event.preventDefault();
1199
- keyManager.setFocusOrigin('keyboard');
1200
- keyManager.onKeydown(event);
1201
- }
1202
- break;
1203
- case UP_ARROW:
1204
- case DOWN_ARROW:
1205
- if (!this._isHorizontal()) {
1391
+ if (!hasModifierKey(event)) {
1206
1392
  event.preventDefault();
1207
1393
  keyManager.setFocusOrigin('keyboard');
1208
1394
  keyManager.onKeydown(event);
@@ -1211,101 +1397,28 @@ class CdkMenu extends CdkMenuGroup {
1211
1397
  case ESCAPE:
1212
1398
  if (!hasModifierKey(event)) {
1213
1399
  event.preventDefault();
1214
- this._menuStack.close(this, 2 /* currentItem */);
1400
+ this.menuStack.close(this, {
1401
+ focusNextOnEmpty: 2 /* currentItem */,
1402
+ focusParentTrigger: true,
1403
+ });
1215
1404
  }
1216
1405
  break;
1217
1406
  case TAB:
1218
- this._menuStack.closeAll();
1407
+ if (!hasModifierKey(event, 'altKey', 'metaKey', 'ctrlKey')) {
1408
+ this.menuStack.closeAll({ focusParentTrigger: true });
1409
+ }
1219
1410
  break;
1220
1411
  default:
1221
1412
  keyManager.onKeydown(event);
1222
1413
  }
1223
1414
  }
1224
- /** Register this menu with its enclosing parent menu panel */
1225
- _registerWithParentPanel() {
1226
- this._getMenuPanel()?._registerMenu(this);
1227
- }
1228
- /**
1229
- * Get the enclosing CdkMenuPanel defaulting to the injected reference over the developer
1230
- * provided reference.
1231
- */
1232
- _getMenuPanel() {
1233
- return this._menuPanel || this._explicitPanel;
1234
- }
1235
- /**
1236
- * Complete the change emitter if there are any nested MenuGroups or register to complete the
1237
- * change emitter if a MenuGroup is rendered at some point
1238
- */
1239
- _completeChangeEmitter() {
1240
- if (this._hasNestedGroups()) {
1241
- this.change.complete();
1242
- }
1243
- else {
1244
- this._nestedGroups.changes.pipe(take(1)).subscribe(() => this.change.complete());
1245
- }
1246
- }
1247
- /** Return true if there are nested CdkMenuGroup elements within the Menu */
1248
- _hasNestedGroups() {
1249
- // view engine has a bug where @ContentChildren will return the current element
1250
- // along with children if the selectors match - not just the children.
1251
- // Here, if there is at least one element, we check to see if the first element is a CdkMenu in
1252
- // order to ensure that we return true iff there are child CdkMenuGroup elements.
1253
- return this._nestedGroups.length > 0 && !(this._nestedGroups.first instanceof CdkMenu);
1254
- }
1255
- /** Setup the FocusKeyManager with the correct orientation for the menu. */
1256
- _setKeyManager() {
1257
- this._keyManager = new FocusKeyManager(this._allItems)
1258
- .withWrap()
1259
- .withTypeAhead()
1260
- .withHomeAndEnd();
1261
- if (this._isHorizontal()) {
1262
- this._keyManager.withHorizontalOrientation(this._dir?.value || 'ltr');
1263
- }
1264
- else {
1265
- this._keyManager.withVerticalOrientation();
1266
- }
1267
- }
1268
- /**
1269
- * Set the PointerFocusTracker and ensure that when mouse focus changes the key manager is updated
1270
- * with the latest menu item under mouse focus.
1271
- */
1272
- _subscribeToMouseManager() {
1273
- this._ngZone.runOutsideAngular(() => {
1274
- this._pointerTracker = new PointerFocusTracker(this._allItems);
1275
- this._pointerTracker.entered
1276
- .pipe(takeUntil(this.closed))
1277
- .subscribe(item => this._keyManager.setActiveItem(item));
1278
- });
1279
- }
1280
- /** Subscribe to the MenuStack close and empty observables. */
1281
- _subscribeToMenuStack() {
1282
- this._menuStack.closed
1283
- .pipe(takeUntil(this.closed))
1284
- .subscribe(item => this._closeOpenMenu(item));
1285
- this._menuStack.emptied
1286
- .pipe(takeUntil(this.closed))
1287
- .subscribe(event => this._toggleMenuFocus(event));
1288
- }
1289
1415
  /**
1290
- * Close the open menu if the current active item opened the requested MenuStackItem.
1291
- * @param item the MenuStackItem requested to be closed.
1416
+ * Set focus the either the current, previous or next item based on the FocusNext event.
1417
+ * @param focusNext The element to focus.
1292
1418
  */
1293
- _closeOpenMenu(menu) {
1294
- const keyManager = this._keyManager;
1295
- const trigger = this._openItem;
1296
- if (menu === trigger?.getMenuTrigger()?.getMenu()) {
1297
- trigger?.getMenuTrigger()?.closeMenu();
1298
- // If the user has moused over a sibling item we want to focus the element under mouse focus
1299
- // not the trigger which previously opened the now closed menu.
1300
- if (trigger) {
1301
- keyManager.setActiveItem(this._pointerTracker?.activeElement || trigger);
1302
- }
1303
- }
1304
- }
1305
- /** Set focus the either the current, previous or next item based on the FocusNext event. */
1306
- _toggleMenuFocus(event) {
1307
- const keyManager = this._keyManager;
1308
- switch (event) {
1419
+ _toggleMenuFocus(focusNext) {
1420
+ const keyManager = this.keyManager;
1421
+ switch (focusNext) {
1309
1422
  case 0 /* nextItem */:
1310
1423
  keyManager.setFocusOrigin('keyboard');
1311
1424
  keyManager.setNextItemActive();
@@ -1322,95 +1435,55 @@ class CdkMenu extends CdkMenuGroup {
1322
1435
  break;
1323
1436
  }
1324
1437
  }
1325
- // TODO(andy9775): remove duplicate logic between menu an menu bar
1326
- /**
1327
- * Subscribe to the menu trigger's open events in order to track the trigger which opened the menu
1328
- * and stop tracking it when the menu is closed.
1329
- */
1330
- _subscribeToMenuOpen() {
1331
- const exitCondition = merge(this._allItems.changes, this.closed);
1332
- this._allItems.changes
1333
- .pipe(startWith(this._allItems), mergeMap((list) => list
1334
- .filter(item => item.hasMenu())
1335
- .map(item => item.getMenuTrigger().opened.pipe(mapTo(item), takeUntil(exitCondition)))), mergeAll(), switchMap((item) => {
1336
- this._openItem = item;
1337
- return item.getMenuTrigger().closed;
1338
- }), takeUntil(this.closed))
1339
- .subscribe(() => (this._openItem = undefined));
1340
- }
1341
- /** Return true if this menu has been configured in a horizontal orientation. */
1342
- _isHorizontal() {
1343
- return this.orientation === 'horizontal';
1344
- }
1345
- /**
1346
- * Return true if this menu is an inline menu. That is, it does not exist in a pop-up and is
1347
- * always visible in the dom.
1348
- */
1349
- _isInline() {
1350
- // NoopMenuStack is the default. If this menu is not inline than the NoopMenuStack is replaced
1351
- // automatically.
1352
- return this._menuStack instanceof NoopMenuStack;
1353
- }
1354
- ngOnDestroy() {
1355
- super.ngOnDestroy();
1356
- this._emitClosedEvent();
1357
- this._pointerTracker?.destroy();
1358
- }
1359
- /** Emit and complete the closed event emitter */
1360
- _emitClosedEvent() {
1361
- this.closed.next();
1362
- this.closed.complete();
1438
+ /** Subscribe to the MenuStack emptied events. */
1439
+ _subscribeToMenuStackEmptied() {
1440
+ this.menuStack.emptied
1441
+ .pipe(takeUntil(this.destroyed))
1442
+ .subscribe(event => this._toggleMenuFocus(event));
1363
1443
  }
1364
1444
  }
1365
- CdkMenu.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenu, deps: [{ token: i0.NgZone }, { token: i0.ElementRef }, { token: MENU_AIM, optional: true, self: true }, { token: i2.Directionality, optional: true }, { token: CdkMenuPanel, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
1366
- CdkMenu.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkMenu, selector: "[cdkMenu]", inputs: { orientation: ["cdkMenuOrientation", "orientation"], _explicitPanel: ["cdkMenuPanel", "_explicitPanel"] }, outputs: { closed: "closed" }, host: { attributes: { "role": "menu" }, listeners: { "focus": "focusFirstItem()", "keydown": "_handleKeyEvent($event)" }, properties: { "tabindex": "_isInline() ? 0 : null", "class.cdk-menu-inline": "_isInline()", "attr.aria-orientation": "orientation" }, classAttribute: "cdk-menu" }, providers: [
1445
+ CdkMenu.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenu, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: MENU_STACK }, { token: MENU_TRIGGER, optional: true }, { token: MENU_AIM, optional: true, self: true }, { token: i1$1.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
1446
+ CdkMenu.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkMenu, selector: "[cdkMenu]", outputs: { closed: "closed" }, host: { attributes: { "role": "menu" }, listeners: { "keydown": "_handleKeyEvent($event)" }, properties: { "class.cdk-menu-inline": "isInline" }, classAttribute: "cdk-menu" }, providers: [
1367
1447
  { provide: CdkMenuGroup, useExisting: CdkMenu },
1368
1448
  { provide: CDK_MENU, useExisting: CdkMenu },
1369
- ], queries: [{ propertyName: "_nestedGroups", predicate: CdkMenuGroup, descendants: true }, { propertyName: "_allItems", predicate: CdkMenuItem, descendants: true }], exportAs: ["cdkMenu"], usesInheritance: true, ngImport: i0 });
1370
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenu, decorators: [{
1449
+ PARENT_OR_NEW_INLINE_MENU_STACK_PROVIDER('vertical'),
1450
+ ], exportAs: ["cdkMenu"], usesInheritance: true, ngImport: i0 });
1451
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenu, decorators: [{
1371
1452
  type: Directive,
1372
1453
  args: [{
1373
1454
  selector: '[cdkMenu]',
1374
1455
  exportAs: 'cdkMenu',
1375
1456
  host: {
1376
- '[tabindex]': '_isInline() ? 0 : null',
1377
1457
  'role': 'menu',
1378
1458
  'class': 'cdk-menu',
1379
- '[class.cdk-menu-inline]': '_isInline()',
1380
- '[attr.aria-orientation]': 'orientation',
1381
- '(focus)': 'focusFirstItem()',
1459
+ '[class.cdk-menu-inline]': 'isInline',
1382
1460
  '(keydown)': '_handleKeyEvent($event)',
1383
1461
  },
1384
1462
  providers: [
1385
1463
  { provide: CdkMenuGroup, useExisting: CdkMenu },
1386
1464
  { provide: CDK_MENU, useExisting: CdkMenu },
1465
+ PARENT_OR_NEW_INLINE_MENU_STACK_PROVIDER('vertical'),
1387
1466
  ],
1388
1467
  }]
1389
- }], ctorParameters: function () { return [{ type: i0.NgZone }, { type: i0.ElementRef }, { type: undefined, decorators: [{
1468
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: MenuStack, decorators: [{
1469
+ type: Inject,
1470
+ args: [MENU_STACK]
1471
+ }] }, { type: CdkMenuTriggerBase, decorators: [{
1472
+ type: Optional
1473
+ }, {
1474
+ type: Inject,
1475
+ args: [MENU_TRIGGER]
1476
+ }] }, { type: undefined, decorators: [{
1390
1477
  type: Self
1391
1478
  }, {
1392
1479
  type: Optional
1393
1480
  }, {
1394
1481
  type: Inject,
1395
1482
  args: [MENU_AIM]
1396
- }] }, { type: i2.Directionality, decorators: [{
1483
+ }] }, { type: i1$1.Directionality, decorators: [{
1397
1484
  type: Optional
1398
- }] }, { type: CdkMenuPanel, decorators: [{
1399
- type: Optional
1400
- }] }]; }, propDecorators: { orientation: [{
1401
- type: Input,
1402
- args: ['cdkMenuOrientation']
1403
- }], closed: [{
1485
+ }] }]; }, propDecorators: { closed: [{
1404
1486
  type: Output
1405
- }], _nestedGroups: [{
1406
- type: ContentChildren,
1407
- args: [CdkMenuGroup, { descendants: true }]
1408
- }], _allItems: [{
1409
- type: ContentChildren,
1410
- args: [CdkMenuItem, { descendants: true }]
1411
- }], _explicitPanel: [{
1412
- type: Input,
1413
- args: ['cdkMenuPanel']
1414
1487
  }] } });
1415
1488
 
1416
1489
  /**
@@ -1426,147 +1499,88 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
1426
1499
  * this directive is applied to should contain components marked with CdkMenuItem.
1427
1500
  *
1428
1501
  */
1429
- class CdkMenuBar extends CdkMenuGroup {
1430
- constructor(_menuStack, _ngZone, _elementRef, _menuAim, _dir) {
1431
- super();
1432
- this._menuStack = _menuStack;
1433
- this._ngZone = _ngZone;
1434
- this._elementRef = _elementRef;
1435
- this._menuAim = _menuAim;
1436
- this._dir = _dir;
1437
- /**
1438
- * Sets the aria-orientation attribute and determines where menus will be opened.
1439
- * Does not affect styling/layout.
1440
- */
1502
+ class CdkMenuBar extends CdkMenuBase {
1503
+ constructor(
1504
+ /** The host element. */
1505
+ elementRef,
1506
+ /** The Angular zone. */
1507
+ ngZone,
1508
+ /** The menu stack this menu is part of. */
1509
+ menuStack,
1510
+ /** The menu aim service used by this menu. */
1511
+ menuAim,
1512
+ /** The directionality of the page. */
1513
+ dir) {
1514
+ super(elementRef, ngZone, menuStack, menuAim, dir);
1515
+ /** The direction items in the menu flow. */
1441
1516
  this.orientation = 'horizontal';
1442
- /** Emits when the MenuBar is destroyed. */
1443
- this._destroyed = new Subject();
1517
+ /** Whether the menu is displayed inline (i.e. always present vs a conditional popup that the user triggers with a trigger element). */
1518
+ this.isInline = true;
1444
1519
  }
1445
1520
  ngAfterContentInit() {
1446
1521
  super.ngAfterContentInit();
1447
- this._setKeyManager();
1448
- this._subscribeToMenuOpen();
1449
- this._subscribeToMenuStack();
1450
- this._subscribeToMouseManager();
1451
- this._menuAim?.initialize(this, this._pointerTracker);
1452
- }
1453
- /** Place focus on the first MenuItem in the menu and set the focus origin. */
1454
- focusFirstItem(focusOrigin = 'program') {
1455
- this._keyManager.setFocusOrigin(focusOrigin);
1456
- this._keyManager.setFirstItemActive();
1457
- }
1458
- /** Place focus on the last MenuItem in the menu and set the focus origin. */
1459
- focusLastItem(focusOrigin = 'program') {
1460
- this._keyManager.setFocusOrigin(focusOrigin);
1461
- this._keyManager.setLastItemActive();
1522
+ this._subscribeToMenuStackEmptied();
1462
1523
  }
1463
1524
  /**
1464
- * Handle keyboard events, specifically changing the focused element and/or toggling the active
1465
- * items menu.
1466
- * @param event the KeyboardEvent to handle.
1525
+ * Handle keyboard events for the Menu.
1526
+ * @param event The keyboard event to be handled.
1467
1527
  */
1468
1528
  _handleKeyEvent(event) {
1469
- const keyManager = this._keyManager;
1529
+ const keyManager = this.keyManager;
1470
1530
  switch (event.keyCode) {
1471
1531
  case UP_ARROW:
1472
1532
  case DOWN_ARROW:
1473
1533
  case LEFT_ARROW:
1474
1534
  case RIGHT_ARROW:
1475
- const horizontalArrows = event.keyCode === LEFT_ARROW || event.keyCode === RIGHT_ARROW;
1476
- // For a horizontal menu if the left/right keys were clicked, or a vertical menu if the
1477
- // up/down keys were clicked: if the current menu is open, close it then focus and open the
1478
- // next menu.
1479
- if ((this._isHorizontal() && horizontalArrows) ||
1480
- (!this._isHorizontal() && !horizontalArrows)) {
1481
- event.preventDefault();
1482
- const prevIsOpen = keyManager.activeItem?.isMenuOpen();
1483
- keyManager.activeItem?.getMenuTrigger()?.closeMenu();
1484
- keyManager.setFocusOrigin('keyboard');
1485
- keyManager.onKeydown(event);
1486
- if (prevIsOpen) {
1487
- keyManager.activeItem?.getMenuTrigger()?.openMenu();
1535
+ if (!hasModifierKey(event)) {
1536
+ const horizontalArrows = event.keyCode === LEFT_ARROW || event.keyCode === RIGHT_ARROW;
1537
+ // For a horizontal menu if the left/right keys were clicked, or a vertical menu if the
1538
+ // up/down keys were clicked: if the current menu is open, close it then focus and open the
1539
+ // next menu.
1540
+ if (horizontalArrows) {
1541
+ event.preventDefault();
1542
+ const prevIsOpen = keyManager.activeItem?.isMenuOpen();
1543
+ keyManager.activeItem?.getMenuTrigger()?.close();
1544
+ keyManager.setFocusOrigin('keyboard');
1545
+ keyManager.onKeydown(event);
1546
+ if (prevIsOpen) {
1547
+ keyManager.activeItem?.getMenuTrigger()?.open();
1548
+ }
1488
1549
  }
1489
1550
  }
1490
1551
  break;
1491
1552
  case ESCAPE:
1492
- event.preventDefault();
1493
- keyManager.activeItem?.getMenuTrigger()?.closeMenu();
1553
+ if (!hasModifierKey(event)) {
1554
+ event.preventDefault();
1555
+ keyManager.activeItem?.getMenuTrigger()?.close();
1556
+ }
1494
1557
  break;
1495
1558
  case TAB:
1496
- keyManager.activeItem?.getMenuTrigger()?.closeMenu();
1559
+ if (!hasModifierKey(event, 'altKey', 'metaKey', 'ctrlKey')) {
1560
+ keyManager.activeItem?.getMenuTrigger()?.close();
1561
+ }
1497
1562
  break;
1498
1563
  default:
1499
1564
  keyManager.onKeydown(event);
1500
1565
  }
1501
1566
  }
1502
- /** Setup the FocusKeyManager with the correct orientation for the menu bar. */
1503
- _setKeyManager() {
1504
- this._keyManager = new FocusKeyManager(this._allItems)
1505
- .withWrap()
1506
- .withTypeAhead()
1507
- .withHomeAndEnd();
1508
- if (this._isHorizontal()) {
1509
- this._keyManager.withHorizontalOrientation(this._dir?.value || 'ltr');
1510
- }
1511
- else {
1512
- this._keyManager.withVerticalOrientation();
1513
- }
1514
- }
1515
- /**
1516
- * Set the PointerFocusTracker and ensure that when mouse focus changes the key manager is updated
1517
- * with the latest menu item under mouse focus.
1518
- */
1519
- _subscribeToMouseManager() {
1520
- this._ngZone.runOutsideAngular(() => {
1521
- this._pointerTracker = new PointerFocusTracker(this._allItems);
1522
- this._pointerTracker.entered.pipe(takeUntil(this._destroyed)).subscribe(item => {
1523
- if (this._hasOpenSubmenu()) {
1524
- this._keyManager.setActiveItem(item);
1525
- }
1526
- });
1527
- });
1528
- }
1529
- /** Subscribe to the MenuStack close and empty observables. */
1530
- _subscribeToMenuStack() {
1531
- this._menuStack.closed
1532
- .pipe(takeUntil(this._destroyed))
1533
- .subscribe(item => this._closeOpenMenu(item));
1534
- this._menuStack.emptied
1535
- .pipe(takeUntil(this._destroyed))
1536
- .subscribe(event => this._toggleOpenMenu(event));
1537
- }
1538
- /**
1539
- * Close the open menu if the current active item opened the requested MenuStackItem.
1540
- * @param item the MenuStackItem requested to be closed.
1541
- */
1542
- _closeOpenMenu(menu) {
1543
- const trigger = this._openItem;
1544
- const keyManager = this._keyManager;
1545
- if (menu === trigger?.getMenuTrigger()?.getMenu()) {
1546
- trigger?.getMenuTrigger()?.closeMenu();
1547
- // If the user has moused over a sibling item we want to focus the element under mouse focus
1548
- // not the trigger which previously opened the now closed menu.
1549
- if (trigger) {
1550
- keyManager.setActiveItem(this._pointerTracker?.activeElement || trigger);
1551
- }
1552
- }
1553
- }
1554
1567
  /**
1555
1568
  * Set focus to either the current, previous or next item based on the FocusNext event, then
1556
1569
  * open the previous or next item.
1570
+ * @param focusNext The element to focus.
1557
1571
  */
1558
- _toggleOpenMenu(event) {
1559
- const keyManager = this._keyManager;
1560
- switch (event) {
1572
+ _toggleOpenMenu(focusNext) {
1573
+ const keyManager = this.keyManager;
1574
+ switch (focusNext) {
1561
1575
  case 0 /* nextItem */:
1562
1576
  keyManager.setFocusOrigin('keyboard');
1563
1577
  keyManager.setNextItemActive();
1564
- keyManager.activeItem?.getMenuTrigger()?.openMenu();
1578
+ keyManager.activeItem?.getMenuTrigger()?.open();
1565
1579
  break;
1566
1580
  case 1 /* previousItem */:
1567
1581
  keyManager.setFocusOrigin('keyboard');
1568
1582
  keyManager.setPreviousItemActive();
1569
- keyManager.activeItem?.getMenuTrigger()?.openMenu();
1583
+ keyManager.activeItem?.getMenuTrigger()?.open();
1570
1584
  break;
1571
1585
  case 2 /* currentItem */:
1572
1586
  if (keyManager.activeItem) {
@@ -1576,45 +1590,20 @@ class CdkMenuBar extends CdkMenuGroup {
1576
1590
  break;
1577
1591
  }
1578
1592
  }
1579
- /**
1580
- * @return true if the menu bar is configured to be horizontal.
1581
- */
1582
- _isHorizontal() {
1583
- return this.orientation === 'horizontal';
1584
- }
1585
- /**
1586
- * Subscribe to the menu trigger's open events in order to track the trigger which opened the menu
1587
- * and stop tracking it when the menu is closed.
1588
- */
1589
- _subscribeToMenuOpen() {
1590
- const exitCondition = merge(this._allItems.changes, this._destroyed);
1591
- this._allItems.changes
1592
- .pipe(startWith(this._allItems), mergeMap((list) => list
1593
- .filter(item => item.hasMenu())
1594
- .map(item => item.getMenuTrigger().opened.pipe(mapTo(item), takeUntil(exitCondition)))), mergeAll(), switchMap((item) => {
1595
- this._openItem = item;
1596
- return item.getMenuTrigger().closed;
1597
- }), takeUntil(this._destroyed))
1598
- .subscribe(() => (this._openItem = undefined));
1599
- }
1600
- /** Return true if the MenuBar has an open submenu. */
1601
- _hasOpenSubmenu() {
1602
- return !!this._openItem;
1603
- }
1604
- ngOnDestroy() {
1605
- super.ngOnDestroy();
1606
- this._destroyed.next();
1607
- this._destroyed.complete();
1608
- this._pointerTracker?.destroy();
1593
+ /** Subscribe to the MenuStack emptied events. */
1594
+ _subscribeToMenuStackEmptied() {
1595
+ this.menuStack?.emptied
1596
+ .pipe(takeUntil(this.destroyed))
1597
+ .subscribe(event => this._toggleOpenMenu(event));
1609
1598
  }
1610
1599
  }
1611
- CdkMenuBar.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuBar, deps: [{ token: MenuStack }, { token: i0.NgZone }, { token: i0.ElementRef }, { token: MENU_AIM, optional: true, self: true }, { token: i2.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
1612
- CdkMenuBar.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkMenuBar, selector: "[cdkMenuBar]", inputs: { orientation: ["cdkMenuBarOrientation", "orientation"] }, host: { attributes: { "role": "menubar", "tabindex": "0" }, listeners: { "focus": "focusFirstItem()", "keydown": "_handleKeyEvent($event)" }, properties: { "attr.aria-orientation": "orientation" }, classAttribute: "cdk-menu-bar" }, providers: [
1600
+ CdkMenuBar.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuBar, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: MENU_STACK }, { token: MENU_AIM, optional: true, self: true }, { token: i1$1.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
1601
+ CdkMenuBar.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkMenuBar, selector: "[cdkMenuBar]", host: { attributes: { "role": "menubar" }, listeners: { "keydown": "_handleKeyEvent($event)" }, classAttribute: "cdk-menu-bar" }, providers: [
1613
1602
  { provide: CdkMenuGroup, useExisting: CdkMenuBar },
1614
1603
  { provide: CDK_MENU, useExisting: CdkMenuBar },
1615
- { provide: MenuStack, useClass: MenuStack },
1616
- ], queries: [{ propertyName: "_allItems", predicate: CdkMenuItem, descendants: true }], exportAs: ["cdkMenuBar"], usesInheritance: true, ngImport: i0 });
1617
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuBar, decorators: [{
1604
+ { provide: MENU_STACK, useFactory: () => MenuStack.inline('horizontal') },
1605
+ ], exportAs: ["cdkMenuBar"], usesInheritance: true, ngImport: i0 });
1606
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuBar, decorators: [{
1618
1607
  type: Directive,
1619
1608
  args: [{
1620
1609
  selector: '[cdkMenuBar]',
@@ -1622,32 +1611,64 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
1622
1611
  host: {
1623
1612
  'role': 'menubar',
1624
1613
  'class': 'cdk-menu-bar',
1625
- 'tabindex': '0',
1626
- '[attr.aria-orientation]': 'orientation',
1627
- '(focus)': 'focusFirstItem()',
1628
1614
  '(keydown)': '_handleKeyEvent($event)',
1629
1615
  },
1630
1616
  providers: [
1631
1617
  { provide: CdkMenuGroup, useExisting: CdkMenuBar },
1632
1618
  { provide: CDK_MENU, useExisting: CdkMenuBar },
1633
- { provide: MenuStack, useClass: MenuStack },
1619
+ { provide: MENU_STACK, useFactory: () => MenuStack.inline('horizontal') },
1634
1620
  ],
1635
1621
  }]
1636
- }], ctorParameters: function () { return [{ type: MenuStack }, { type: i0.NgZone }, { type: i0.ElementRef }, { type: undefined, decorators: [{
1622
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: MenuStack, decorators: [{
1623
+ type: Inject,
1624
+ args: [MENU_STACK]
1625
+ }] }, { type: undefined, decorators: [{
1637
1626
  type: Self
1638
1627
  }, {
1639
1628
  type: Optional
1640
1629
  }, {
1641
1630
  type: Inject,
1642
1631
  args: [MENU_AIM]
1643
- }] }, { type: i2.Directionality, decorators: [{
1632
+ }] }, { type: i1$1.Directionality, decorators: [{
1644
1633
  type: Optional
1645
- }] }]; }, propDecorators: { orientation: [{
1634
+ }] }]; } });
1635
+
1636
+ /**
1637
+ * @license
1638
+ * Copyright Google LLC All Rights Reserved.
1639
+ *
1640
+ * Use of this source code is governed by an MIT-style license that can be
1641
+ * found in the LICENSE file at https://angular.io/license
1642
+ */
1643
+ /** Base class providing checked state for selectable MenuItems. */
1644
+ class CdkMenuItemSelectable extends CdkMenuItem {
1645
+ constructor() {
1646
+ super(...arguments);
1647
+ this._checked = false;
1648
+ /** Whether the item should close the menu if triggered by the spacebar. */
1649
+ this.closeOnSpacebarTrigger = false;
1650
+ }
1651
+ /** Whether the element is checked */
1652
+ get checked() {
1653
+ return this._checked;
1654
+ }
1655
+ set checked(value) {
1656
+ this._checked = coerceBooleanProperty(value);
1657
+ }
1658
+ }
1659
+ CdkMenuItemSelectable.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuItemSelectable, deps: null, target: i0.ɵɵFactoryTarget.Directive });
1660
+ CdkMenuItemSelectable.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkMenuItemSelectable, inputs: { checked: ["cdkMenuItemChecked", "checked"] }, host: { properties: { "attr.aria-checked": "!!checked", "attr.aria-disabled": "disabled || null" } }, usesInheritance: true, ngImport: i0 });
1661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuItemSelectable, decorators: [{
1662
+ type: Directive,
1663
+ args: [{
1664
+ host: {
1665
+ '[attr.aria-checked]': '!!checked',
1666
+ '[attr.aria-disabled]': 'disabled || null',
1667
+ },
1668
+ }]
1669
+ }], propDecorators: { checked: [{
1646
1670
  type: Input,
1647
- args: ['cdkMenuBarOrientation']
1648
- }], _allItems: [{
1649
- type: ContentChildren,
1650
- args: [CdkMenuItem, { descendants: true }]
1671
+ args: ['cdkMenuItemChecked']
1651
1672
  }] } });
1652
1673
 
1653
1674
  /**
@@ -1657,60 +1678,82 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
1657
1678
  * Use of this source code is governed by an MIT-style license that can be
1658
1679
  * found in the LICENSE file at https://angular.io/license
1659
1680
  */
1681
+ /** Counter used to set a unique id and name for a selectable item */
1682
+ let nextId = 0;
1660
1683
  /**
1661
1684
  * A directive providing behavior for the "menuitemradio" ARIA role, which behaves similarly to
1662
1685
  * a conventional radio-button. Any sibling `CdkMenuItemRadio` instances within the same `CdkMenu`
1663
1686
  * or `CdkMenuGroup` comprise a radio group with unique selection enforced.
1664
1687
  */
1665
1688
  class CdkMenuItemRadio extends CdkMenuItemSelectable {
1666
- constructor(_selectionDispatcher, element, ngZone, parentMenu, menuAim, dir,
1689
+ constructor(
1690
+ /** The host element for this radio item. */
1691
+ element,
1692
+ /** The Angular zone. */
1693
+ ngZone,
1694
+ /** The unique selection dispatcher for this radio's `CdkMenuGroup`. */
1695
+ _selectionDispatcher,
1696
+ /** The menu stack this item belongs to. */
1697
+ menuStack,
1698
+ /** The parent menu for this item. */
1699
+ parentMenu,
1700
+ /** The menu aim used for this item. */
1701
+ menuAim,
1702
+ /** The directionality of the page. */
1703
+ dir,
1667
1704
  /** Reference to the CdkMenuItemTrigger directive if one is added to the same element */
1668
- // `CdkMenuItemRadio` is commonly used in combination with a `CdkMenuItemTrigger`.
1669
1705
  // tslint:disable-next-line: lightweight-tokens
1670
1706
  menuTrigger) {
1671
- super(element, ngZone, parentMenu, menuAim, dir, menuTrigger);
1707
+ super(element, ngZone, menuStack, parentMenu, menuAim, dir, menuTrigger);
1672
1708
  this._selectionDispatcher = _selectionDispatcher;
1709
+ /** An ID to identify this radio item to the `UniqueSelectionDisptcher`. */
1710
+ this._id = `${nextId++}`;
1673
1711
  this._registerDispatcherListener();
1674
1712
  }
1675
- /** Configure the unique selection dispatcher listener in order to toggle the checked state */
1676
- _registerDispatcherListener() {
1677
- this._removeDispatcherListener = this._selectionDispatcher.listen((id, name) => (this.checked = this.id === id && this.name === name));
1713
+ ngOnDestroy() {
1714
+ super.ngOnDestroy();
1715
+ this._removeDispatcherListener();
1678
1716
  }
1679
- /** Toggles the checked state of the radio-button. */
1680
- trigger() {
1681
- super.trigger();
1717
+ /**
1718
+ * Toggles the checked state of the radio-button.
1719
+ * @param options Options the configure how the item is triggered
1720
+ * - keepOpen: specifies that the menu should be kept open after triggering the item.
1721
+ */
1722
+ trigger(options) {
1723
+ super.trigger(options);
1682
1724
  if (!this.disabled) {
1683
- this._selectionDispatcher.notify(this.id, this.name);
1725
+ this._selectionDispatcher.notify(this._id, '');
1684
1726
  }
1685
1727
  }
1686
- ngOnDestroy() {
1687
- super.ngOnDestroy();
1688
- this._removeDispatcherListener();
1728
+ /** Configure the unique selection dispatcher listener in order to toggle the checked state */
1729
+ _registerDispatcherListener() {
1730
+ this._removeDispatcherListener = this._selectionDispatcher.listen((id) => {
1731
+ this.checked = this._id === id;
1732
+ });
1689
1733
  }
1690
1734
  }
1691
- CdkMenuItemRadio.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuItemRadio, deps: [{ token: i1$1.UniqueSelectionDispatcher }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: CDK_MENU, optional: true }, { token: MENU_AIM, optional: true }, { token: i2.Directionality, optional: true }, { token: CdkMenuItemTrigger, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive });
1692
- CdkMenuItemRadio.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkMenuItemRadio, selector: "[cdkMenuItemRadio]", host: { attributes: { "type": "button", "role": "menuitemradio" }, properties: { "tabindex": "_tabindex", "attr.aria-checked": "checked || null", "attr.aria-disabled": "disabled || null" } }, providers: [
1735
+ CdkMenuItemRadio.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuItemRadio, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i1$2.UniqueSelectionDispatcher }, { token: MENU_STACK }, { token: CDK_MENU, optional: true }, { token: MENU_AIM, optional: true }, { token: i1$1.Directionality, optional: true }, { token: CdkMenuTrigger, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Directive });
1736
+ CdkMenuItemRadio.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkMenuItemRadio, selector: "[cdkMenuItemRadio]", host: { attributes: { "role": "menuitemradio" } }, providers: [
1693
1737
  { provide: CdkMenuItemSelectable, useExisting: CdkMenuItemRadio },
1694
1738
  { provide: CdkMenuItem, useExisting: CdkMenuItemSelectable },
1695
1739
  ], exportAs: ["cdkMenuItemRadio"], usesInheritance: true, ngImport: i0 });
1696
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuItemRadio, decorators: [{
1740
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuItemRadio, decorators: [{
1697
1741
  type: Directive,
1698
1742
  args: [{
1699
1743
  selector: '[cdkMenuItemRadio]',
1700
1744
  exportAs: 'cdkMenuItemRadio',
1701
1745
  host: {
1702
- '[tabindex]': '_tabindex',
1703
- 'type': 'button',
1704
1746
  'role': 'menuitemradio',
1705
- '[attr.aria-checked]': 'checked || null',
1706
- '[attr.aria-disabled]': 'disabled || null',
1707
1747
  },
1708
1748
  providers: [
1709
1749
  { provide: CdkMenuItemSelectable, useExisting: CdkMenuItemRadio },
1710
1750
  { provide: CdkMenuItem, useExisting: CdkMenuItemSelectable },
1711
1751
  ],
1712
1752
  }]
1713
- }], ctorParameters: function () { return [{ type: i1$1.UniqueSelectionDispatcher }, { type: i0.ElementRef }, { type: i0.NgZone }, { type: undefined, decorators: [{
1753
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i1$2.UniqueSelectionDispatcher }, { type: MenuStack, decorators: [{
1754
+ type: Inject,
1755
+ args: [MENU_STACK]
1756
+ }] }, { type: undefined, decorators: [{
1714
1757
  type: Optional
1715
1758
  }, {
1716
1759
  type: Inject,
@@ -1720,9 +1763,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
1720
1763
  }, {
1721
1764
  type: Inject,
1722
1765
  args: [MENU_AIM]
1723
- }] }, { type: i2.Directionality, decorators: [{
1766
+ }] }, { type: i1$1.Directionality, decorators: [{
1724
1767
  type: Optional
1725
- }] }, { type: CdkMenuItemTrigger, decorators: [{
1768
+ }] }, { type: CdkMenuTrigger, decorators: [{
1726
1769
  type: Self
1727
1770
  }, {
1728
1771
  type: Optional
@@ -1740,30 +1783,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
1740
1783
  * conventional checkbox.
1741
1784
  */
1742
1785
  class CdkMenuItemCheckbox extends CdkMenuItemSelectable {
1743
- /** Toggle the checked state of the checkbox. */
1744
- trigger() {
1745
- super.trigger();
1786
+ /**
1787
+ * Toggle the checked state of the checkbox.
1788
+ * @param options Options the configure how the item is triggered
1789
+ * - keepOpen: specifies that the menu should be kept open after triggering the item.
1790
+ */
1791
+ trigger(options) {
1792
+ super.trigger(options);
1746
1793
  if (!this.disabled) {
1747
1794
  this.checked = !this.checked;
1748
1795
  }
1749
1796
  }
1750
1797
  }
1751
- CdkMenuItemCheckbox.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuItemCheckbox, deps: null, target: i0.ɵɵFactoryTarget.Directive });
1752
- CdkMenuItemCheckbox.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkMenuItemCheckbox, selector: "[cdkMenuItemCheckbox]", host: { attributes: { "type": "button", "role": "menuitemcheckbox" }, properties: { "tabindex": "_tabindex", "attr.aria-checked": "checked || null", "attr.aria-disabled": "disabled || null" } }, providers: [
1798
+ CdkMenuItemCheckbox.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuItemCheckbox, deps: null, target: i0.ɵɵFactoryTarget.Directive });
1799
+ CdkMenuItemCheckbox.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkMenuItemCheckbox, selector: "[cdkMenuItemCheckbox]", host: { attributes: { "role": "menuitemcheckbox" } }, providers: [
1753
1800
  { provide: CdkMenuItemSelectable, useExisting: CdkMenuItemCheckbox },
1754
1801
  { provide: CdkMenuItem, useExisting: CdkMenuItemSelectable },
1755
1802
  ], exportAs: ["cdkMenuItemCheckbox"], usesInheritance: true, ngImport: i0 });
1756
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuItemCheckbox, decorators: [{
1803
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuItemCheckbox, decorators: [{
1757
1804
  type: Directive,
1758
1805
  args: [{
1759
1806
  selector: '[cdkMenuItemCheckbox]',
1760
1807
  exportAs: 'cdkMenuItemCheckbox',
1761
1808
  host: {
1762
- '[tabindex]': '_tabindex',
1763
- 'type': 'button',
1764
1809
  'role': 'menuitemcheckbox',
1765
- '[attr.aria-checked]': 'checked || null',
1766
- '[attr.aria-disabled]': 'disabled || null',
1767
1810
  },
1768
1811
  providers: [
1769
1812
  { provide: CdkMenuItemSelectable, useExisting: CdkMenuItemCheckbox },
@@ -1779,11 +1822,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
1779
1822
  * Use of this source code is governed by an MIT-style license that can be
1780
1823
  * found in the LICENSE file at https://angular.io/license
1781
1824
  */
1825
+ /** The preferred menu positions for the context menu. */
1826
+ const CONTEXT_MENU_POSITIONS = STANDARD_DROPDOWN_BELOW_POSITIONS.map(position => {
1827
+ // In cases where the first menu item in the context menu is a trigger the submenu opens on a
1828
+ // hover event. We offset the context menu 2px by default to prevent this from occurring.
1829
+ const offsetX = position.overlayX === 'start' ? 2 : -2;
1830
+ const offsetY = position.overlayY === 'top' ? 2 : -2;
1831
+ return { ...position, offsetX, offsetY };
1832
+ });
1782
1833
  /** Tracks the last open context menu trigger across the entire application. */
1783
1834
  class ContextMenuTracker {
1784
1835
  /**
1785
1836
  * Close the previous open context menu and set the given one as being open.
1786
- * @param trigger the trigger for the currently open Context Menu.
1837
+ * @param trigger The trigger for the currently open Context Menu.
1787
1838
  */
1788
1839
  update(trigger) {
1789
1840
  if (ContextMenuTracker._openContextMenuTrigger !== trigger) {
@@ -1792,55 +1843,38 @@ class ContextMenuTracker {
1792
1843
  }
1793
1844
  }
1794
1845
  }
1795
- ContextMenuTracker.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: ContextMenuTracker, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1796
- ContextMenuTracker.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: ContextMenuTracker, providedIn: 'root' });
1797
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: ContextMenuTracker, decorators: [{
1846
+ ContextMenuTracker.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: ContextMenuTracker, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1847
+ ContextMenuTracker.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: ContextMenuTracker, providedIn: 'root' });
1848
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: ContextMenuTracker, decorators: [{
1798
1849
  type: Injectable,
1799
1850
  args: [{ providedIn: 'root' }]
1800
1851
  }] });
1801
- /** Injection token for the ContextMenu options object. */
1802
- const CDK_CONTEXT_MENU_DEFAULT_OPTIONS = new InjectionToken('cdk-context-menu-default-options');
1803
1852
  /**
1804
- * A directive which when placed on some element opens a the Menu it is bound to when a user
1805
- * right-clicks within that element. It is aware of nested Context Menus and the lowest level
1806
- * non-disabled context menu will trigger.
1853
+ * A directive that opens a menu when a user right-clicks within its host element.
1854
+ * It is aware of nested context menus and will trigger only the lowest level non-disabled context menu.
1807
1855
  */
1808
- class CdkContextMenuTrigger {
1809
- constructor(_viewContainerRef, _overlay, _contextMenuTracker, _options, _directionality) {
1810
- this._viewContainerRef = _viewContainerRef;
1856
+ class CdkContextMenuTrigger extends CdkMenuTriggerBase {
1857
+ constructor(
1858
+ /** The DI injector for this component */
1859
+ injector,
1860
+ /** The view container ref for this component */
1861
+ viewContainerRef,
1862
+ /** The CDK overlay service */
1863
+ _overlay,
1864
+ /** The app's context menu tracking registry */
1865
+ _contextMenuTracker,
1866
+ /** The menu stack this menu is part of. */
1867
+ menuStack,
1868
+ /** The directionality of the current page */
1869
+ _directionality) {
1870
+ super(injector, viewContainerRef, menuStack);
1811
1871
  this._overlay = _overlay;
1812
1872
  this._contextMenuTracker = _contextMenuTracker;
1813
- this._options = _options;
1814
1873
  this._directionality = _directionality;
1815
- /** Emits when the attached menu is requested to open. */
1816
- this.opened = new EventEmitter();
1817
- /** Emits when the attached menu is requested to close. */
1818
- this.closed = new EventEmitter();
1819
1874
  this._disabled = false;
1820
- /** A reference to the overlay which manages the triggered menu. */
1821
- this._overlayRef = null;
1822
- /** Emits when the element is destroyed. */
1823
- this._destroyed = new Subject();
1824
- /** The menu stack for this trigger and its associated menus. */
1825
- this._menuStack = new MenuStack();
1826
- /** Emits when the outside pointer events listener on the overlay should be stopped. */
1827
- this._stopOutsideClicksListener = merge(this.closed, this._destroyed);
1828
- this._setMenuStackListener();
1829
- }
1830
- /** Template reference variable to the menu to open on right click. */
1831
- get menuPanel() {
1832
- return this._menuPanel;
1875
+ this._setMenuStackCloseListener();
1833
1876
  }
1834
- set menuPanel(panel) {
1835
- if ((typeof ngDevMode === 'undefined' || ngDevMode) && panel._menuStack) {
1836
- throwExistingMenuStackError();
1837
- }
1838
- this._menuPanel = panel;
1839
- if (this._menuPanel) {
1840
- this._menuPanel._menuStack = this._menuStack;
1841
- }
1842
- }
1843
- /** Whether the context menu should be disabled. */
1877
+ /** Whether the context menu is disabled. */
1844
1878
  get disabled() {
1845
1879
  return this._disabled;
1846
1880
  }
@@ -1854,36 +1888,12 @@ class CdkContextMenuTrigger {
1854
1888
  open(coordinates) {
1855
1889
  this._open(coordinates, false);
1856
1890
  }
1857
- _open(coordinates, ignoreFirstOutsideAuxClick) {
1858
- if (this.disabled) {
1859
- return;
1860
- }
1861
- else if (this.isOpen()) {
1862
- // since we're moving this menu we need to close any submenus first otherwise they end up
1863
- // disconnected from this one.
1864
- this._menuStack.closeSubMenuOf(this._menuPanel._menu);
1865
- this._overlayRef.getConfig().positionStrategy.setOrigin(coordinates);
1866
- this._overlayRef.updatePosition();
1867
- }
1868
- else {
1869
- this.opened.next();
1870
- if (this._overlayRef) {
1871
- this._overlayRef.getConfig().positionStrategy.setOrigin(coordinates);
1872
- this._overlayRef.updatePosition();
1873
- }
1874
- else {
1875
- this._overlayRef = this._overlay.create(this._getOverlayConfig(coordinates));
1876
- }
1877
- this._overlayRef.attach(this._getMenuContent());
1878
- this._subscribeToOutsideClicks(ignoreFirstOutsideAuxClick);
1879
- }
1880
- }
1881
- /** Close the opened menu. */
1891
+ /** Close the currently opened context menu. */
1882
1892
  close() {
1883
- this._menuStack.closeAll();
1893
+ this.menuStack.closeAll();
1884
1894
  }
1885
1895
  /**
1886
- * Open the context menu and close any previously open menus.
1896
+ * Open the context menu and closes any previously open menus.
1887
1897
  * @param event the mouse event which opens the context menu.
1888
1898
  */
1889
1899
  _openOnContextMenu(event) {
@@ -1898,20 +1908,16 @@ class CdkContextMenuTrigger {
1898
1908
  this._open({ x: event.clientX, y: event.clientY }, true);
1899
1909
  // A context menu can be triggered via a mouse right click or a keyboard shortcut.
1900
1910
  if (event.button === 2) {
1901
- this._menuPanel._menu?.focusFirstItem('mouse');
1911
+ this.childMenu?.focusFirstItem('mouse');
1902
1912
  }
1903
1913
  else if (event.button === 0) {
1904
- this._menuPanel._menu?.focusFirstItem('keyboard');
1914
+ this.childMenu?.focusFirstItem('keyboard');
1905
1915
  }
1906
1916
  else {
1907
- this._menuPanel._menu?.focusFirstItem('program');
1917
+ this.childMenu?.focusFirstItem('program');
1908
1918
  }
1909
1919
  }
1910
1920
  }
1911
- /** Whether the attached menu is open. */
1912
- isOpen() {
1913
- return !!this._overlayRef?.hasAttached();
1914
- }
1915
1921
  /**
1916
1922
  * Get the configuration object used to create the overlay.
1917
1923
  * @param coordinates the location to place the opened menu
@@ -1924,130 +1930,102 @@ class CdkContextMenuTrigger {
1924
1930
  });
1925
1931
  }
1926
1932
  /**
1927
- * Build the position strategy for the overlay which specifies where to place the menu.
1933
+ * Get the position strategy for the overlay which specifies where to place the menu.
1928
1934
  * @param coordinates the location to place the opened menu
1929
1935
  */
1930
1936
  _getOverlayPositionStrategy(coordinates) {
1931
1937
  return this._overlay
1932
1938
  .position()
1933
1939
  .flexibleConnectedTo(coordinates)
1934
- .withDefaultOffsetX(this._options.offsetX)
1935
- .withDefaultOffsetY(this._options.offsetY)
1936
- .withPositions(this._getOverlayPositions());
1937
- }
1938
- /**
1939
- * Determine and return where to position the opened menu relative to the mouse location.
1940
- */
1941
- _getOverlayPositions() {
1942
- // TODO: this should be configurable through the injected context menu options
1943
- return [
1944
- { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top' },
1945
- { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top' },
1946
- { originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'bottom' },
1947
- { originX: 'start', originY: 'bottom', overlayX: 'end', overlayY: 'bottom' },
1948
- ];
1949
- }
1950
- /**
1951
- * Get the portal to be attached to the overlay which contains the menu. Allows for the menu
1952
- * content to change dynamically and be reflected in the application.
1953
- */
1954
- _getMenuContent() {
1955
- const hasMenuContentChanged = this.menuPanel._templateRef !== this._panelContent?.templateRef;
1956
- if (this.menuPanel && (!this._panelContent || hasMenuContentChanged)) {
1957
- this._panelContent = new TemplatePortal(this.menuPanel._templateRef, this._viewContainerRef);
1958
- }
1959
- return this._panelContent;
1940
+ .withPositions(this.menuPosition ?? CONTEXT_MENU_POSITIONS);
1960
1941
  }
1961
1942
  /** Subscribe to the menu stack close events and close this menu when requested. */
1962
- _setMenuStackListener() {
1963
- this._menuStack.closed.pipe(takeUntil(this._destroyed)).subscribe(item => {
1964
- if (item === this._menuPanel._menu && this.isOpen()) {
1943
+ _setMenuStackCloseListener() {
1944
+ this.menuStack.closed.pipe(takeUntil(this.destroyed)).subscribe(({ item }) => {
1945
+ if (item === this.childMenu && this.isOpen()) {
1965
1946
  this.closed.next();
1966
- this._overlayRef.detach();
1947
+ this.overlayRef.detach();
1967
1948
  }
1968
1949
  });
1969
1950
  }
1970
1951
  /**
1971
1952
  * Subscribe to the overlays outside pointer events stream and handle closing out the stack if a
1972
1953
  * click occurs outside the menus.
1954
+ * @param ignoreFirstAuxClick Whether to ignore the first auxclick event outside the menu.
1973
1955
  */
1974
1956
  _subscribeToOutsideClicks(ignoreFirstAuxClick) {
1975
- if (this._overlayRef) {
1976
- let outsideClicks = this._overlayRef.outsidePointerEvents();
1957
+ if (this.overlayRef) {
1958
+ let outsideClicks = this.overlayRef.outsidePointerEvents();
1977
1959
  // If the menu was triggered by the `contextmenu` event, skip the first `auxclick` event
1978
1960
  // because it fires when the mouse is released on the same click that opened the menu.
1979
1961
  if (ignoreFirstAuxClick) {
1980
1962
  const [auxClicks, nonAuxClicks] = partition(outsideClicks, ({ type }) => type === 'auxclick');
1981
1963
  outsideClicks = merge(nonAuxClicks, auxClicks.pipe(skip(1)));
1982
1964
  }
1983
- outsideClicks.pipe(takeUntil(this._stopOutsideClicksListener)).subscribe(event => {
1984
- if (!isClickInsideMenuOverlay(event.target)) {
1985
- this._menuStack.closeAll();
1965
+ outsideClicks.pipe(takeUntil(this.stopOutsideClicksListener)).subscribe(event => {
1966
+ if (!this.isElementInsideMenuStack(event.target)) {
1967
+ this.menuStack.closeAll();
1986
1968
  }
1987
1969
  });
1988
1970
  }
1989
1971
  }
1990
- ngOnDestroy() {
1991
- this._destroyOverlay();
1992
- this._resetPanelMenuStack();
1993
- this._destroyed.next();
1994
- this._destroyed.complete();
1995
- }
1996
- /** Destroy and unset the overlay reference it if exists. */
1997
- _destroyOverlay() {
1998
- if (this._overlayRef) {
1999
- this._overlayRef.dispose();
2000
- this._overlayRef = null;
1972
+ /**
1973
+ * Open the attached menu at the specified location.
1974
+ * @param coordinates where to open the context menu
1975
+ * @param ignoreFirstOutsideAuxClick Whether to ignore the first auxclick outside the menu after opening.
1976
+ */
1977
+ _open(coordinates, ignoreFirstOutsideAuxClick) {
1978
+ if (this.disabled) {
1979
+ return;
2001
1980
  }
2002
- }
2003
- /** Set the menu panels menu stack back to null. */
2004
- _resetPanelMenuStack() {
2005
- // If a ContextMenuTrigger is placed in a conditionally rendered view, each time the trigger is
2006
- // rendered the panel setter for ContextMenuTrigger is called. From the first render onward,
2007
- // the attached CdkMenuPanel has the MenuStack set. Since we throw an error if a panel already
2008
- // has a stack set, we want to reset the attached stack here to prevent the error from being
2009
- // thrown if the trigger re-configures its attached panel (in the case where there is a 1:1
2010
- // relationship between the panel and trigger).
2011
- if (this._menuPanel) {
2012
- this._menuPanel._menuStack = null;
1981
+ if (this.isOpen()) {
1982
+ // since we're moving this menu we need to close any submenus first otherwise they end up
1983
+ // disconnected from this one.
1984
+ this.menuStack.closeSubMenuOf(this.childMenu);
1985
+ this.overlayRef.getConfig().positionStrategy.setOrigin(coordinates);
1986
+ this.overlayRef.updatePosition();
1987
+ }
1988
+ else {
1989
+ this.opened.next();
1990
+ if (this.overlayRef) {
1991
+ this.overlayRef.getConfig().positionStrategy.setOrigin(coordinates);
1992
+ this.overlayRef.updatePosition();
1993
+ }
1994
+ else {
1995
+ this.overlayRef = this._overlay.create(this._getOverlayConfig(coordinates));
1996
+ }
1997
+ this.overlayRef.attach(this.getMenuContentPortal());
1998
+ this._subscribeToOutsideClicks(ignoreFirstOutsideAuxClick);
2013
1999
  }
2014
2000
  }
2015
2001
  }
2016
- CdkContextMenuTrigger.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkContextMenuTrigger, deps: [{ token: i0.ViewContainerRef }, { token: i1.Overlay }, { token: ContextMenuTracker }, { token: CDK_CONTEXT_MENU_DEFAULT_OPTIONS }, { token: i2.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
2017
- CdkContextMenuTrigger.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.3.0", type: CdkContextMenuTrigger, selector: "[cdkContextMenuTriggerFor]", inputs: { menuPanel: ["cdkContextMenuTriggerFor", "menuPanel"], disabled: ["cdkContextMenuDisabled", "disabled"] }, outputs: { opened: "cdkContextMenuOpened", closed: "cdkContextMenuClosed" }, host: { listeners: { "contextmenu": "_openOnContextMenu($event)" } }, providers: [
2018
- // In cases where the first menu item in the context menu is a trigger the submenu opens on a
2019
- // hover event. Offsetting the opened context menu by 2px prevents this from occurring.
2020
- { provide: CDK_CONTEXT_MENU_DEFAULT_OPTIONS, useValue: { offsetX: 2, offsetY: 2 } },
2021
- ], exportAs: ["cdkContextMenuTriggerFor"], ngImport: i0 });
2022
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkContextMenuTrigger, decorators: [{
2002
+ CdkContextMenuTrigger.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkContextMenuTrigger, deps: [{ token: i0.Injector }, { token: i0.ViewContainerRef }, { token: i1.Overlay }, { token: ContextMenuTracker }, { token: MENU_STACK }, { token: i1$1.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
2003
+ CdkContextMenuTrigger.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "14.0.0-next.9", type: CdkContextMenuTrigger, selector: "[cdkContextMenuTriggerFor]", inputs: { menuTemplateRef: ["cdkContextMenuTriggerFor", "menuTemplateRef"], menuPosition: ["cdkContextMenuPosition", "menuPosition"], disabled: ["cdkContextMenuDisabled", "disabled"] }, outputs: { opened: "cdkContextMenuOpened", closed: "cdkContextMenuClosed" }, host: { listeners: { "contextmenu": "_openOnContextMenu($event)" }, properties: { "attr.data-cdk-menu-stack-id": "null" } }, providers: [
2004
+ { provide: MENU_TRIGGER, useExisting: CdkContextMenuTrigger },
2005
+ { provide: MENU_STACK, useClass: MenuStack },
2006
+ ], exportAs: ["cdkContextMenuTriggerFor"], usesInheritance: true, ngImport: i0 });
2007
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkContextMenuTrigger, decorators: [{
2023
2008
  type: Directive,
2024
2009
  args: [{
2025
2010
  selector: '[cdkContextMenuTriggerFor]',
2026
2011
  exportAs: 'cdkContextMenuTriggerFor',
2027
2012
  host: {
2013
+ '[attr.data-cdk-menu-stack-id]': 'null',
2028
2014
  '(contextmenu)': '_openOnContextMenu($event)',
2029
2015
  },
2016
+ inputs: ['menuTemplateRef: cdkContextMenuTriggerFor', 'menuPosition: cdkContextMenuPosition'],
2017
+ outputs: ['opened: cdkContextMenuOpened', 'closed: cdkContextMenuClosed'],
2030
2018
  providers: [
2031
- // In cases where the first menu item in the context menu is a trigger the submenu opens on a
2032
- // hover event. Offsetting the opened context menu by 2px prevents this from occurring.
2033
- { provide: CDK_CONTEXT_MENU_DEFAULT_OPTIONS, useValue: { offsetX: 2, offsetY: 2 } },
2019
+ { provide: MENU_TRIGGER, useExisting: CdkContextMenuTrigger },
2020
+ { provide: MENU_STACK, useClass: MenuStack },
2034
2021
  ],
2035
2022
  }]
2036
- }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i1.Overlay }, { type: ContextMenuTracker }, { type: undefined, decorators: [{
2023
+ }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.ViewContainerRef }, { type: i1.Overlay }, { type: ContextMenuTracker }, { type: MenuStack, decorators: [{
2037
2024
  type: Inject,
2038
- args: [CDK_CONTEXT_MENU_DEFAULT_OPTIONS]
2039
- }] }, { type: i2.Directionality, decorators: [{
2025
+ args: [MENU_STACK]
2026
+ }] }, { type: i1$1.Directionality, decorators: [{
2040
2027
  type: Optional
2041
- }] }]; }, propDecorators: { menuPanel: [{
2042
- type: Input,
2043
- args: ['cdkContextMenuTriggerFor']
2044
- }], opened: [{
2045
- type: Output,
2046
- args: ['cdkContextMenuOpened']
2047
- }], closed: [{
2048
- type: Output,
2049
- args: ['cdkContextMenuClosed']
2050
- }], disabled: [{
2028
+ }] }]; }, propDecorators: { disabled: [{
2051
2029
  type: Input,
2052
2030
  args: ['cdkContextMenuDisabled']
2053
2031
  }] } });
@@ -2059,42 +2037,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
2059
2037
  * Use of this source code is governed by an MIT-style license that can be
2060
2038
  * found in the LICENSE file at https://angular.io/license
2061
2039
  */
2040
+ /** The list of components and directives that should be declared and exported from this module. */
2062
2041
  const EXPORTED_DECLARATIONS = [
2063
2042
  CdkMenuBar,
2064
2043
  CdkMenu,
2065
- CdkMenuPanel,
2066
2044
  CdkMenuItem,
2067
2045
  CdkMenuItemRadio,
2068
2046
  CdkMenuItemCheckbox,
2069
- CdkMenuItemTrigger,
2047
+ CdkMenuTrigger,
2070
2048
  CdkMenuGroup,
2071
2049
  CdkContextMenuTrigger,
2072
2050
  CdkTargetMenuAim,
2073
2051
  ];
2052
+ /** Module that declares components and directives for the CDK menu. */
2074
2053
  class CdkMenuModule {
2075
2054
  }
2076
- CdkMenuModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2077
- CdkMenuModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuModule, declarations: [CdkMenuBar,
2055
+ CdkMenuModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2056
+ CdkMenuModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuModule, declarations: [CdkMenuBar,
2078
2057
  CdkMenu,
2079
- CdkMenuPanel,
2080
2058
  CdkMenuItem,
2081
2059
  CdkMenuItemRadio,
2082
2060
  CdkMenuItemCheckbox,
2083
- CdkMenuItemTrigger,
2061
+ CdkMenuTrigger,
2084
2062
  CdkMenuGroup,
2085
2063
  CdkContextMenuTrigger,
2086
2064
  CdkTargetMenuAim], imports: [OverlayModule], exports: [CdkMenuBar,
2087
2065
  CdkMenu,
2088
- CdkMenuPanel,
2089
2066
  CdkMenuItem,
2090
2067
  CdkMenuItemRadio,
2091
2068
  CdkMenuItemCheckbox,
2092
- CdkMenuItemTrigger,
2069
+ CdkMenuTrigger,
2093
2070
  CdkMenuGroup,
2094
2071
  CdkContextMenuTrigger,
2095
2072
  CdkTargetMenuAim] });
2096
- CdkMenuModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuModule, imports: [[OverlayModule]] });
2097
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImport: i0, type: CdkMenuModule, decorators: [{
2073
+ CdkMenuModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuModule, imports: [[OverlayModule]] });
2074
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-next.9", ngImport: i0, type: CdkMenuModule, decorators: [{
2098
2075
  type: NgModule,
2099
2076
  args: [{
2100
2077
  imports: [OverlayModule],
@@ -2123,5 +2100,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.0", ngImpor
2123
2100
  * Generated bundle index. Do not edit.
2124
2101
  */
2125
2102
 
2126
- export { CDK_CONTEXT_MENU_DEFAULT_OPTIONS, CDK_MENU, CdkContextMenuTrigger, CdkMenu, CdkMenuBar, CdkMenuGroup, CdkMenuItem, CdkMenuItemCheckbox, CdkMenuItemRadio, CdkMenuItemTrigger, CdkMenuModule, CdkMenuPanel, CdkTargetMenuAim, ContextMenuTracker, MENU_AIM, MenuStack, TargetMenuAim, isClickInsideMenuOverlay };
2103
+ export { CDK_MENU, CdkContextMenuTrigger, CdkMenu, CdkMenuBar, CdkMenuBase, CdkMenuGroup, CdkMenuItem, CdkMenuItemCheckbox, CdkMenuItemRadio, CdkMenuItemSelectable, CdkMenuModule, CdkMenuTrigger, CdkMenuTriggerBase, CdkTargetMenuAim, ContextMenuTracker, MENU_AIM, MENU_STACK, MENU_TRIGGER, MenuStack, PARENT_OR_NEW_INLINE_MENU_STACK_PROVIDER, PARENT_OR_NEW_MENU_STACK_PROVIDER, PointerFocusTracker, TargetMenuAim };
2127
2104
  //# sourceMappingURL=menu.mjs.map