/
dialog-content-directives.ts
190 lines (169 loc) · 6.49 KB
/
dialog-content-directives.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {
Directive,
Input,
OnChanges,
OnInit,
Optional,
SimpleChanges,
ElementRef,
} from '@angular/core';
import {MatLegacyDialog} from './dialog';
import {MatLegacyDialogRef} from './dialog-ref';
import {_closeDialogVia} from '@angular/material/dialog';
/** Counter used to generate unique IDs for dialog elements. */
let dialogElementUid = 0;
/**
* Button that will close the current dialog.
* @deprecated Use `MatDialogClose` from `@angular/material/dialog` instead. See https://material.angular.io/guide/mdc-migration for information about migrating.
* @breaking-change 17.0.0
*/
@Directive({
selector: '[mat-dialog-close], [matDialogClose]',
exportAs: 'matDialogClose',
host: {
'(click)': '_onButtonClick($event)',
'[attr.aria-label]': 'ariaLabel || null',
'[attr.type]': 'type',
},
})
export class MatLegacyDialogClose implements OnInit, OnChanges {
/** Screen reader label for the button. */
@Input('aria-label') ariaLabel: string;
/** Default to "button" to prevents accidental form submits. */
@Input() type: 'submit' | 'button' | 'reset' = 'button';
/** Dialog close input. */
@Input('mat-dialog-close') dialogResult: any;
@Input('matDialogClose') _matDialogClose: any;
constructor(
/**
* Reference to the containing dialog.
* @deprecated `dialogRef` property to become private.
* @breaking-change 13.0.0
*/
// The dialog title directive is always used in combination with a `MatDialogRef`.
// tslint:disable-next-line: lightweight-tokens
@Optional() public dialogRef: MatLegacyDialogRef<any>,
private _elementRef: ElementRef<HTMLElement>,
private _dialog: MatLegacyDialog,
) {}
ngOnInit() {
if (!this.dialogRef) {
// When this directive is included in a dialog via TemplateRef (rather than being
// in a Component), the DialogRef isn't available via injection because embedded
// views cannot be given a custom injector. Instead, we look up the DialogRef by
// ID. This must occur in `onInit`, as the ID binding for the dialog container won't
// be resolved at constructor time.
this.dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs)!;
}
}
ngOnChanges(changes: SimpleChanges) {
const proxiedChange = changes['_matDialogClose'] || changes['_matDialogCloseResult'];
if (proxiedChange) {
this.dialogResult = proxiedChange.currentValue;
}
}
_onButtonClick(event: MouseEvent) {
// Determinate the focus origin using the click event, because using the FocusMonitor will
// result in incorrect origins. Most of the time, close buttons will be auto focused in the
// dialog, and therefore clicking the button won't result in a focus change. This means that
// the FocusMonitor won't detect any origin change, and will always output `program`.
_closeDialogVia(
this.dialogRef,
event.screenX === 0 && event.screenY === 0 ? 'keyboard' : 'mouse',
this.dialogResult,
);
}
}
/**
* Title of a dialog element. Stays fixed to the top of the dialog when scrolling.
* @deprecated Use `MatDialogTitle` from `@angular/material/dialog` instead. See https://material.angular.io/guide/mdc-migration for information about migrating.
* @breaking-change 17.0.0
*/
@Directive({
selector: '[mat-dialog-title], [matDialogTitle]',
exportAs: 'matDialogTitle',
host: {
'class': 'mat-dialog-title',
'[id]': 'id',
},
})
export class MatLegacyDialogTitle implements OnInit {
/** Unique id for the dialog title. If none is supplied, it will be auto-generated. */
@Input() id: string = `mat-dialog-title-${dialogElementUid++}`;
constructor(
// The dialog title directive is always used in combination with a `MatDialogRef`.
// tslint:disable-next-line: lightweight-tokens
@Optional() private _dialogRef: MatLegacyDialogRef<any>,
private _elementRef: ElementRef<HTMLElement>,
private _dialog: MatLegacyDialog,
) {}
ngOnInit() {
if (!this._dialogRef) {
this._dialogRef = getClosestDialog(this._elementRef, this._dialog.openDialogs)!;
}
if (this._dialogRef) {
Promise.resolve().then(() => {
const container = this._dialogRef._containerInstance;
if (container && !container._ariaLabelledBy) {
container._ariaLabelledBy = this.id;
}
});
}
}
}
/**
* Scrollable content container of a dialog.
* @deprecated Use `MatDialogContent` from `@angular/material/dialog` instead. See https://material.angular.io/guide/mdc-migration for information about migrating.
* @breaking-change 17.0.0
*/
@Directive({
selector: `[mat-dialog-content], mat-dialog-content, [matDialogContent]`,
host: {'class': 'mat-dialog-content'},
})
export class MatLegacyDialogContent {}
/**
* Container for the bottom action buttons in a dialog.
* Stays fixed to the bottom when scrolling.
* @deprecated Use `MatDialogActions` from `@angular/material/dialog` instead. See https://material.angular.io/guide/mdc-migration for information about migrating.
* @breaking-change 17.0.0
*/
@Directive({
selector: `[mat-dialog-actions], mat-dialog-actions, [matDialogActions]`,
host: {
'class': 'mat-dialog-actions',
'[class.mat-dialog-actions-align-center]': 'align === "center"',
'[class.mat-dialog-actions-align-end]': 'align === "end"',
},
})
export class MatLegacyDialogActions {
/**
* Horizontal alignment of action buttons.
*/
@Input() align?: 'start' | 'center' | 'end' = 'start';
}
// TODO(crisbeto): this utility shouldn't be necessary anymore, because the dialog ref is provided
// both to component and template dialogs through DI. We need to keep it around, because there are
// some internal wrappers around `MatDialog` that happened to work by accident, because we had this
// fallback logic in place.
/**
* Finds the closest MatDialogRef to an element by looking at the DOM.
* @param element Element relative to which to look for a dialog.
* @param openDialogs References to the currently-open dialogs.
*/
function getClosestDialog(
element: ElementRef<HTMLElement>,
openDialogs: MatLegacyDialogRef<any>[],
) {
let parent: HTMLElement | null = element.nativeElement.parentElement;
while (parent && !parent.classList.contains('mat-dialog-container')) {
parent = parent.parentElement;
}
return parent ? openDialogs.find(dialog => dialog.id === parent!.id) : null;
}