@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.
- package/combobox/combobox-module.d.ts +3 -4
- package/combobox/combobox-popup.d.ts +6 -8
- package/combobox/combobox.d.ts +15 -18
- package/combobox/public-api.d.ts +0 -1
- package/dialog/dialog-config.d.ts +6 -1
- package/esm2020/column-resize/column-resize-directives/column-resize-flex.mjs +4 -4
- package/esm2020/column-resize/column-resize-directives/column-resize.mjs +4 -4
- package/esm2020/column-resize/column-resize-directives/default-enabled-column-resize-flex.mjs +4 -4
- package/esm2020/column-resize/column-resize-directives/default-enabled-column-resize.mjs +4 -4
- package/esm2020/column-resize/column-resize-module.mjs +9 -9
- package/esm2020/column-resize/column-resize-notifier.mjs +7 -7
- package/esm2020/column-resize/column-resize.mjs +4 -4
- package/esm2020/column-resize/column-size-store.mjs +4 -4
- package/esm2020/column-resize/event-dispatcher.mjs +4 -4
- package/esm2020/column-resize/overlay-handle.mjs +4 -4
- package/esm2020/column-resize/resizable.mjs +4 -4
- package/esm2020/column-resize/resize-strategy.mjs +10 -10
- package/esm2020/combobox/combobox-module.mjs +6 -7
- package/esm2020/combobox/combobox-popup.mjs +12 -23
- package/esm2020/combobox/combobox.mjs +50 -34
- package/esm2020/combobox/public-api.mjs +1 -2
- package/esm2020/dialog/dialog-config.mjs +1 -1
- package/esm2020/dialog/dialog-container.mjs +5 -5
- package/esm2020/dialog/dialog-module.mjs +5 -5
- package/esm2020/dialog/dialog.mjs +5 -5
- package/esm2020/listbox/listbox-module.mjs +5 -5
- package/esm2020/listbox/listbox.mjs +26 -44
- package/esm2020/menu/context-menu-trigger.mjs +227 -0
- package/esm2020/menu/menu-aim.mjs +24 -19
- package/esm2020/menu/menu-bar.mjs +72 -165
- package/esm2020/menu/menu-base.mjs +203 -0
- package/esm2020/menu/menu-errors.mjs +1 -9
- package/esm2020/menu/menu-group.mjs +7 -45
- package/esm2020/menu/menu-interface.mjs +1 -1
- package/esm2020/menu/menu-item-checkbox.mjs +11 -11
- package/esm2020/menu/menu-item-radio.mjs +50 -26
- package/esm2020/menu/menu-item-selectable.mjs +18 -33
- package/esm2020/menu/menu-item.mjs +133 -88
- package/esm2020/menu/menu-module.mjs +12 -14
- package/esm2020/menu/menu-stack.mjs +69 -19
- package/esm2020/menu/menu-trigger-base.mjs +114 -0
- package/esm2020/menu/menu-trigger.mjs +302 -0
- package/esm2020/menu/menu.mjs +75 -208
- package/esm2020/menu/pointer-focus-tracker.mjs +11 -9
- package/esm2020/menu/public-api.mjs +10 -7
- package/esm2020/popover-edit/edit-event-dispatcher.mjs +4 -4
- package/esm2020/popover-edit/edit-ref.mjs +4 -4
- package/esm2020/popover-edit/edit-services.mjs +4 -4
- package/esm2020/popover-edit/focus-dispatcher.mjs +4 -4
- package/esm2020/popover-edit/focus-escape-notifier.mjs +4 -4
- package/esm2020/popover-edit/lens-directives.mjs +10 -10
- package/esm2020/popover-edit/popover-edit-module.mjs +5 -5
- package/esm2020/popover-edit/popover-edit-position-strategy-factory.mjs +7 -7
- package/esm2020/popover-edit/table-directives.mjs +16 -16
- package/esm2020/scrolling/auto-size-virtual-scroll.mjs +4 -4
- package/esm2020/scrolling/scrolling-module.mjs +5 -5
- package/esm2020/selection/row-selection.mjs +4 -4
- package/esm2020/selection/select-all.mjs +4 -4
- package/esm2020/selection/selection-column.mjs +4 -4
- package/esm2020/selection/selection-module.mjs +5 -5
- package/esm2020/selection/selection-toggle.mjs +4 -4
- package/esm2020/selection/selection.mjs +4 -4
- package/esm2020/table-scroll-container/table-scroll-container-module.mjs +5 -5
- package/esm2020/table-scroll-container/table-scroll-container.mjs +4 -4
- package/esm2020/version.mjs +1 -1
- package/fesm2015/cdk-experimental.mjs +1 -1
- package/fesm2015/cdk-experimental.mjs.map +1 -1
- package/fesm2015/column-resize.mjs +50 -50
- package/fesm2015/column-resize.mjs.map +1 -1
- package/fesm2015/combobox.mjs +63 -106
- package/fesm2015/combobox.mjs.map +1 -1
- package/fesm2015/dialog.mjs +13 -12
- package/fesm2015/dialog.mjs.map +1 -1
- package/fesm2015/listbox.mjs +31 -47
- package/fesm2015/listbox.mjs.map +1 -1
- package/fesm2015/menu.mjs +1109 -1140
- package/fesm2015/menu.mjs.map +1 -1
- package/fesm2015/popover-edit.mjs +49 -49
- package/fesm2015/popover-edit.mjs.map +1 -1
- package/fesm2015/scrolling.mjs +7 -7
- package/fesm2015/scrolling.mjs.map +1 -1
- package/fesm2015/selection.mjs +19 -19
- package/fesm2015/selection.mjs.map +1 -1
- package/fesm2015/table-scroll-container.mjs +7 -7
- package/fesm2015/table-scroll-container.mjs.map +1 -1
- package/fesm2020/cdk-experimental.mjs +1 -1
- package/fesm2020/cdk-experimental.mjs.map +1 -1
- package/fesm2020/column-resize.mjs +50 -50
- package/fesm2020/column-resize.mjs.map +1 -1
- package/fesm2020/combobox.mjs +62 -103
- package/fesm2020/combobox.mjs.map +1 -1
- package/fesm2020/dialog.mjs +12 -12
- package/fesm2020/dialog.mjs.map +1 -1
- package/fesm2020/listbox.mjs +29 -46
- package/fesm2020/listbox.mjs.map +1 -1
- package/fesm2020/menu.mjs +1031 -1054
- package/fesm2020/menu.mjs.map +1 -1
- package/fesm2020/popover-edit.mjs +49 -49
- package/fesm2020/popover-edit.mjs.map +1 -1
- package/fesm2020/scrolling.mjs +7 -7
- package/fesm2020/scrolling.mjs.map +1 -1
- package/fesm2020/selection.mjs +19 -19
- package/fesm2020/selection.mjs.map +1 -1
- package/fesm2020/table-scroll-container.mjs +7 -7
- package/fesm2020/table-scroll-container.mjs.map +1 -1
- package/listbox/listbox.d.ts +16 -13
- package/menu/context-menu-trigger.d.ts +98 -0
- package/menu/menu-aim.d.ts +20 -9
- package/menu/menu-bar.d.ts +24 -60
- package/menu/menu-base.d.ts +104 -0
- package/menu/menu-errors.d.ts +0 -5
- package/menu/menu-group.d.ts +3 -28
- package/menu/menu-interface.d.ts +7 -5
- package/menu/menu-item-checkbox.d.ts +8 -2
- package/menu/menu-item-radio.d.ts +32 -8
- package/menu/menu-item-selectable.d.ts +4 -14
- package/menu/menu-item.d.ts +64 -24
- package/menu/menu-module.d.ts +10 -10
- package/menu/menu-stack.d.ts +64 -20
- package/menu/menu-trigger-base.d.ts +77 -0
- package/menu/menu-trigger.d.ts +104 -0
- package/menu/menu.d.ts +34 -87
- package/menu/pointer-focus-tracker.d.ts +7 -4
- package/menu/public-api.d.ts +9 -6
- package/package.json +3 -3
- package/combobox/combobox-panel.d.ts +0 -27
- package/esm2020/combobox/combobox-panel.mjs +0 -48
- package/esm2020/menu/context-menu.mjs +0 -294
- package/esm2020/menu/menu-item-trigger.mjs +0 -361
- package/esm2020/menu/menu-panel.mjs +0 -37
- package/menu/context-menu.d.ts +0 -123
- package/menu/menu-item-trigger.d.ts +0 -119
- 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,
|
|
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 {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
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 {
|
|
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
|
-
/**
|
|
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
|
|
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,
|
|
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(
|
|
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
|
|
98
|
-
* not emit if the stack was initially empty.
|
|
159
|
+
* @param options Options that configure behavior on close.
|
|
99
160
|
*/
|
|
100
|
-
closeAll(
|
|
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(
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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: "
|
|
333
|
-
TargetMenuAim.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
334
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
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
|
|
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: "
|
|
344
|
-
CdkTargetMenuAim.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "
|
|
345
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
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
|
-
*
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
378
|
-
*
|
|
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
|
-
*
|
|
383
|
-
*
|
|
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
|
-
|
|
386
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
428
|
-
this.isMenuOpen() ? this.closeMenu() : this.openMenu();
|
|
429
|
-
}
|
|
581
|
+
this.isOpen() ? this.close() : this.open();
|
|
430
582
|
}
|
|
431
583
|
/** Open the attached menu. */
|
|
432
|
-
|
|
433
|
-
if (!this.
|
|
584
|
+
open() {
|
|
585
|
+
if (!this.isOpen()) {
|
|
434
586
|
this.opened.next();
|
|
435
|
-
this.
|
|
436
|
-
this.
|
|
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
|
-
|
|
442
|
-
if (this.
|
|
593
|
+
close() {
|
|
594
|
+
if (this.isOpen()) {
|
|
443
595
|
this.closed.next();
|
|
444
|
-
this.
|
|
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
|
|
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.
|
|
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
|
|
488
|
-
*
|
|
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
|
|
497
|
-
|
|
498
|
-
|
|
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 (
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
this.
|
|
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 (
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
this.
|
|
517
|
-
this.
|
|
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 (!
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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) &&
|
|
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.
|
|
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
|
-
/**
|
|
717
|
+
/** Get the preferred positions for the opened menu relative to the menu item. */
|
|
568
718
|
_getOverlayPositions() {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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.
|
|
608
|
-
if (item === this.
|
|
609
|
-
this.
|
|
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.
|
|
645
|
-
this.
|
|
742
|
+
if (this.overlayRef) {
|
|
743
|
+
this.overlayRef
|
|
646
744
|
.outsidePointerEvents()
|
|
647
|
-
.pipe(
|
|
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 (!
|
|
650
|
-
this.
|
|
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
|
-
/**
|
|
656
|
-
|
|
657
|
-
if (this.
|
|
658
|
-
this.
|
|
659
|
-
|
|
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
|
-
|
|
664
|
-
|
|
665
|
-
|
|
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]': '
|
|
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:
|
|
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:
|
|
817
|
+
}] }, { type: i1$1.Directionality, decorators: [{
|
|
689
818
|
type: Optional
|
|
690
|
-
}] }]; }
|
|
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(
|
|
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.
|
|
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
|
-
|
|
900
|
+
trigger(options) {
|
|
901
|
+
const { keepOpen } = { ...options };
|
|
902
|
+
if (!this.disabled && !this.hasMenu) {
|
|
790
903
|
this.triggered.next();
|
|
791
|
-
|
|
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?.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
832
|
-
|
|
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 (
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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 (
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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.
|
|
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.
|
|
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: "
|
|
890
|
-
CdkMenuItem.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "
|
|
891
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
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:
|
|
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:
|
|
1079
|
+
}] }, { type: i1$1.Directionality, decorators: [{
|
|
921
1080
|
type: Optional
|
|
922
|
-
}] }, { type:
|
|
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
|
-
*
|
|
944
|
-
*
|
|
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
|
|
947
|
-
constructor(
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
this.
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
this.
|
|
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
|
-
/**
|
|
958
|
-
|
|
959
|
-
|
|
1124
|
+
/** Stop the managers listeners. */
|
|
1125
|
+
destroy() {
|
|
1126
|
+
this._destroyed.next();
|
|
1127
|
+
this._destroyed.complete();
|
|
960
1128
|
}
|
|
961
|
-
|
|
962
|
-
|
|
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
|
-
/**
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
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
|
-
*
|
|
995
|
-
*
|
|
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
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
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.
|
|
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
|
-
*
|
|
1009
|
-
*
|
|
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
|
-
|
|
1012
|
-
this.
|
|
1013
|
-
this.
|
|
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
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
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
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
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
|
-
*
|
|
1066
|
-
*
|
|
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
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
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
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
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
|
-
*
|
|
1112
|
-
*
|
|
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
|
-
|
|
1115
|
-
|
|
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
|
-
*
|
|
1119
|
-
*
|
|
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
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
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
|
|
1146
|
-
constructor(
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
this.
|
|
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
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
this.
|
|
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.
|
|
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
|
-
|
|
1182
|
-
|
|
1183
|
-
this.
|
|
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
|
-
/**
|
|
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.
|
|
1387
|
+
const keyManager = this.keyManager;
|
|
1194
1388
|
switch (event.keyCode) {
|
|
1195
1389
|
case LEFT_ARROW:
|
|
1196
1390
|
case RIGHT_ARROW:
|
|
1197
|
-
if (
|
|
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.
|
|
1400
|
+
this.menuStack.close(this, {
|
|
1401
|
+
focusNextOnEmpty: 2 /* currentItem */,
|
|
1402
|
+
focusParentTrigger: true,
|
|
1403
|
+
});
|
|
1215
1404
|
}
|
|
1216
1405
|
break;
|
|
1217
1406
|
case TAB:
|
|
1218
|
-
|
|
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
|
-
*
|
|
1291
|
-
* @param
|
|
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
|
-
|
|
1294
|
-
const keyManager = this.
|
|
1295
|
-
|
|
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
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
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: "
|
|
1366
|
-
CdkMenu.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "
|
|
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
|
-
|
|
1370
|
-
|
|
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]': '
|
|
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.
|
|
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:
|
|
1483
|
+
}] }, { type: i1$1.Directionality, decorators: [{
|
|
1397
1484
|
type: Optional
|
|
1398
|
-
}] },
|
|
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
|
|
1430
|
-
constructor(
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
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
|
-
/**
|
|
1443
|
-
this.
|
|
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.
|
|
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
|
|
1465
|
-
*
|
|
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.
|
|
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
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
(
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
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
|
|
1493
|
-
|
|
1553
|
+
if (!hasModifierKey(event)) {
|
|
1554
|
+
event.preventDefault();
|
|
1555
|
+
keyManager.activeItem?.getMenuTrigger()?.close();
|
|
1556
|
+
}
|
|
1494
1557
|
break;
|
|
1495
1558
|
case TAB:
|
|
1496
|
-
|
|
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(
|
|
1559
|
-
const keyManager = this.
|
|
1560
|
-
switch (
|
|
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()?.
|
|
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()?.
|
|
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
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
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: "
|
|
1612
|
-
CdkMenuBar.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "
|
|
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:
|
|
1616
|
-
],
|
|
1617
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
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:
|
|
1619
|
+
{ provide: MENU_STACK, useFactory: () => MenuStack.inline('horizontal') },
|
|
1634
1620
|
],
|
|
1635
1621
|
}]
|
|
1636
|
-
}], ctorParameters: function () { return [{ type:
|
|
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:
|
|
1632
|
+
}] }, { type: i1$1.Directionality, decorators: [{
|
|
1644
1633
|
type: Optional
|
|
1645
|
-
}] }]; }
|
|
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: ['
|
|
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(
|
|
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
|
-
|
|
1676
|
-
|
|
1677
|
-
this._removeDispatcherListener
|
|
1713
|
+
ngOnDestroy() {
|
|
1714
|
+
super.ngOnDestroy();
|
|
1715
|
+
this._removeDispatcherListener();
|
|
1678
1716
|
}
|
|
1679
|
-
/**
|
|
1680
|
-
|
|
1681
|
-
|
|
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.
|
|
1725
|
+
this._selectionDispatcher.notify(this._id, '');
|
|
1684
1726
|
}
|
|
1685
1727
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
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: "
|
|
1692
|
-
CdkMenuItemRadio.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "
|
|
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: "
|
|
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:
|
|
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:
|
|
1766
|
+
}] }, { type: i1$1.Directionality, decorators: [{
|
|
1724
1767
|
type: Optional
|
|
1725
|
-
}] }, { type:
|
|
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
|
-
/**
|
|
1744
|
-
|
|
1745
|
-
|
|
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: "
|
|
1752
|
-
CdkMenuItemCheckbox.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "
|
|
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: "
|
|
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
|
|
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: "
|
|
1796
|
-
ContextMenuTracker.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1797
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
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
|
|
1805
|
-
*
|
|
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(
|
|
1810
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
1893
|
+
this.menuStack.closeAll();
|
|
1884
1894
|
}
|
|
1885
1895
|
/**
|
|
1886
|
-
* Open the context menu and
|
|
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.
|
|
1911
|
+
this.childMenu?.focusFirstItem('mouse');
|
|
1902
1912
|
}
|
|
1903
1913
|
else if (event.button === 0) {
|
|
1904
|
-
this.
|
|
1914
|
+
this.childMenu?.focusFirstItem('keyboard');
|
|
1905
1915
|
}
|
|
1906
1916
|
else {
|
|
1907
|
-
this.
|
|
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
|
-
*
|
|
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
|
-
.
|
|
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
|
-
|
|
1963
|
-
this.
|
|
1964
|
-
if (item === this.
|
|
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.
|
|
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.
|
|
1976
|
-
let outsideClicks = this.
|
|
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.
|
|
1984
|
-
if (!
|
|
1985
|
-
this.
|
|
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
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
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
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
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: "
|
|
2017
|
-
CdkContextMenuTrigger.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
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
|
-
|
|
2032
|
-
|
|
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:
|
|
2023
|
+
}], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.ViewContainerRef }, { type: i1.Overlay }, { type: ContextMenuTracker }, { type: MenuStack, decorators: [{
|
|
2037
2024
|
type: Inject,
|
|
2038
|
-
args: [
|
|
2039
|
-
}] }, { type:
|
|
2025
|
+
args: [MENU_STACK]
|
|
2026
|
+
}] }, { type: i1$1.Directionality, decorators: [{
|
|
2040
2027
|
type: Optional
|
|
2041
|
-
}] }]; }, propDecorators: {
|
|
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
|
-
|
|
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: "
|
|
2077
|
-
CdkMenuModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "
|
|
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
|
-
|
|
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
|
-
|
|
2069
|
+
CdkMenuTrigger,
|
|
2093
2070
|
CdkMenuGroup,
|
|
2094
2071
|
CdkContextMenuTrigger,
|
|
2095
2072
|
CdkTargetMenuAim] });
|
|
2096
|
-
CdkMenuModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "
|
|
2097
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
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 {
|
|
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
|