@villedemontreal/angular-ui 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/lib/bao.module.mjs +8 -4
- package/esm2020/lib/icon/icon.component.mjs +14 -18
- package/esm2020/lib/modal/index.mjs +12 -0
- package/esm2020/lib/modal/modal-animations.mjs +29 -0
- package/esm2020/lib/modal/modal-config.mjs +65 -0
- package/esm2020/lib/modal/modal-container.mjs +254 -0
- package/esm2020/lib/modal/modal-directives.mjs +84 -0
- package/esm2020/lib/modal/modal-ref.mjs +195 -0
- package/esm2020/lib/modal/modal.mjs +291 -0
- package/esm2020/lib/modal/module.mjs +46 -0
- package/esm2020/public-api.mjs +2 -1
- package/fesm2015/villedemontreal-angular-ui.mjs +972 -23
- package/fesm2015/villedemontreal-angular-ui.mjs.map +1 -1
- package/fesm2020/villedemontreal-angular-ui.mjs +962 -21
- package/fesm2020/villedemontreal-angular-ui.mjs.map +1 -1
- package/lib/bao.module.d.ts +2 -1
- package/lib/icon/icon.component.d.ts +2 -2
- package/lib/modal/index.d.ts +6 -0
- package/lib/modal/modal-animations.d.ts +8 -0
- package/lib/modal/modal-config.d.ts +105 -0
- package/lib/modal/modal-container.d.ts +106 -0
- package/lib/modal/modal-directives.d.ts +25 -0
- package/lib/modal/modal-ref.d.ts +91 -0
- package/lib/modal/modal.d.ts +91 -0
- package/lib/modal/module.d.ts +12 -0
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { SecurityContext, Injectable, Inject, Component, ViewEncapsulation, ChangeDetectionStrategy,
|
|
2
|
+
import { SecurityContext, Injectable, Inject, Component, ViewEncapsulation, ChangeDetectionStrategy, Input, NgModule, Directive, EventEmitter, Output, CUSTOM_ELEMENTS_SCHEMA, ViewChild, forwardRef, InjectionToken, ContentChildren, Optional, HostListener, ContentChild, Injector, TemplateRef, SkipSelf } from '@angular/core';
|
|
3
3
|
import * as i1$1 from '@angular/common';
|
|
4
4
|
import { DOCUMENT, CommonModule } from '@angular/common';
|
|
5
5
|
import * as i1 from '@angular/platform-browser';
|
|
@@ -9,6 +9,16 @@ import { NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/fo
|
|
|
9
9
|
import * as i1$3 from '@angular/cdk/a11y';
|
|
10
10
|
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
11
11
|
import * as i2 from '@angular/cdk/collections';
|
|
12
|
+
import * as i1$4 from '@angular/cdk/overlay';
|
|
13
|
+
import { OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
|
|
14
|
+
import * as i3 from '@angular/cdk/portal';
|
|
15
|
+
import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, TemplatePortal, PortalModule } from '@angular/cdk/portal';
|
|
16
|
+
import { Subject, filter, take, defer, startWith, Subscription } from 'rxjs';
|
|
17
|
+
import { __awaiter } from 'tslib';
|
|
18
|
+
import { _getFocusedElementPierceShadowDom } from '@angular/cdk/platform';
|
|
19
|
+
import { trigger, state, style, transition, group, animate, query, animateChild } from '@angular/animations';
|
|
20
|
+
import { ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
|
|
21
|
+
import { ANIMATION_MODULE_TYPE, BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
12
22
|
|
|
13
23
|
function baoColorToHex(baoColor) {
|
|
14
24
|
switch (baoColor) {
|
|
@@ -215,7 +225,7 @@ const TITLE = 'title';
|
|
|
215
225
|
* `<bao-icon svgIcon="heart"></bao-icon>`
|
|
216
226
|
*/
|
|
217
227
|
class BaoIconComponent {
|
|
218
|
-
constructor(elementRef, iconRegistry, renderer
|
|
228
|
+
constructor(elementRef, iconRegistry, renderer) {
|
|
219
229
|
this.elementRef = elementRef;
|
|
220
230
|
this.iconRegistry = iconRegistry;
|
|
221
231
|
this.renderer = renderer;
|
|
@@ -223,11 +233,6 @@ class BaoIconComponent {
|
|
|
223
233
|
* The size of the icon
|
|
224
234
|
*/
|
|
225
235
|
this.size = 'x-small';
|
|
226
|
-
// If the user has not explicitly set aria-hidden, mark the icon as hidden, as this is
|
|
227
|
-
// the right thing to do for the majority of icon use-cases.
|
|
228
|
-
if (!ariaHidden) {
|
|
229
|
-
this.elementRef.nativeElement.setAttribute('aria-hidden', 'true');
|
|
230
|
-
}
|
|
231
236
|
this._titleId = '';
|
|
232
237
|
this._title = '';
|
|
233
238
|
this._svgIcon = '';
|
|
@@ -308,6 +313,9 @@ class BaoIconComponent {
|
|
|
308
313
|
if (title) {
|
|
309
314
|
svg = this.addTitleToSVG(svg, title);
|
|
310
315
|
}
|
|
316
|
+
if (!title) {
|
|
317
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
318
|
+
}
|
|
311
319
|
this.setSvgElement(svg);
|
|
312
320
|
}
|
|
313
321
|
}
|
|
@@ -317,16 +325,19 @@ class BaoIconComponent {
|
|
|
317
325
|
const titleText = this.renderer.createText(title);
|
|
318
326
|
this.renderer.appendChild(titleNode, titleText);
|
|
319
327
|
this.renderer.appendChild(svg, titleNode);
|
|
328
|
+
svg.setAttribute('aria-labelledby', this._titleId);
|
|
320
329
|
return svg;
|
|
321
330
|
}
|
|
322
331
|
generateUniqueTitleId() {
|
|
323
332
|
return this.title
|
|
324
|
-
? `${this.title
|
|
333
|
+
? `${this.title
|
|
334
|
+
.replace(/[^A-Z0-9]+/gi, '')
|
|
335
|
+
.toLocaleLowerCase()}-${Math.floor(Math.random() * 10000000000000000)}`
|
|
325
336
|
: '';
|
|
326
337
|
}
|
|
327
338
|
}
|
|
328
|
-
BaoIconComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoIconComponent, deps: [{ token: i0.ElementRef }, { token: BaoIconDictionary }, { token: i0.Renderer2 }
|
|
329
|
-
BaoIconComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.1", type: BaoIconComponent, selector: "bao-icon", inputs: { color: "color", size: "size", svgIcon: "svgIcon", title: "title" }, host: { attributes: { "role": "img" }, properties: { "class.bao-icon": "true", "class.notranslate": "true", "class.bao-icon-medium": "size === \"medium\"", "class.bao-icon-small": "size === \"small\"", "class.bao-icon-x-small": "size === \"x-small\"", "class.bao-icon-xx-small": "size === \"xx-small\"", "attr.data-bao-icon-type": "\"svg\"", "
|
|
339
|
+
BaoIconComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoIconComponent, deps: [{ token: i0.ElementRef }, { token: BaoIconDictionary }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
|
|
340
|
+
BaoIconComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.1", type: BaoIconComponent, selector: "bao-icon", inputs: { color: "color", size: "size", svgIcon: "svgIcon", title: "title" }, host: { attributes: { "role": "img" }, properties: { "class.bao-icon": "true", "class.notranslate": "true", "class.bao-icon-medium": "size === \"medium\"", "class.bao-icon-small": "size === \"small\"", "class.bao-icon-x-small": "size === \"x-small\"", "class.bao-icon-xx-small": "size === \"xx-small\"", "attr.data-bao-icon-type": "\"svg\"", "style.color": "hexColor" } }, exportAs: ["baoIcon"], ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: ["bao-icon{background-repeat:no-repeat;display:inline-block;fill:currentColor;line-height:normal}bao-icon svg{height:100%;width:100%}bao-icon.bao-icon-medium{height:2.5rem;width:2.5rem}bao-icon.bao-icon-small{height:2rem;width:2rem;line-height:1.75rem}bao-icon.bao-icon-x-small{height:1.5rem;width:1.5rem;line-height:.5rem}bao-icon.bao-icon-xx-small{height:1rem;width:1rem;line-height:.85rem}bao-icon.bao-icon-spinner{animation:spin .75s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
330
341
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoIconComponent, decorators: [{
|
|
331
342
|
type: Component,
|
|
332
343
|
args: [{ template: '<ng-content></ng-content>', selector: 'bao-icon', exportAs: 'baoIcon', host: {
|
|
@@ -339,16 +350,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImpor
|
|
|
339
350
|
'[class.bao-icon-x-small]': 'size === "x-small"',
|
|
340
351
|
'[class.bao-icon-xx-small]': 'size === "xx-small"',
|
|
341
352
|
'[attr.data-bao-icon-type]': '"svg"',
|
|
342
|
-
'[attr.aria-labelledby]': 'titleId',
|
|
343
|
-
'[attr.aria-hidden]': '!title',
|
|
344
353
|
'[style.color]': 'hexColor'
|
|
345
|
-
}, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["bao-icon{background-repeat:no-repeat;display:inline-block;fill:currentColor;line-height:normal}bao-icon.bao-icon-medium{height:2.5rem;width:2.5rem}bao-icon.bao-icon-small{height:2rem;width:2rem;line-height:1.75rem}bao-icon.bao-icon-x-small{height:1.5rem;width:1.5rem;line-height:.5rem}bao-icon.bao-icon-xx-small{height:1rem;width:1rem;line-height:.85rem}bao-icon.bao-icon-spinner{animation:spin .75s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
346
|
-
}], ctorParameters: function () {
|
|
347
|
-
return [{ type: i0.ElementRef }, { type: BaoIconDictionary }, { type: i0.Renderer2 }, { type: undefined, decorators: [{
|
|
348
|
-
type: Attribute,
|
|
349
|
-
args: ['aria-hidden']
|
|
350
|
-
}] }];
|
|
351
|
-
}, propDecorators: { color: [{
|
|
354
|
+
}, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["bao-icon{background-repeat:no-repeat;display:inline-block;fill:currentColor;line-height:normal}bao-icon svg{height:100%;width:100%}bao-icon.bao-icon-medium{height:2.5rem;width:2.5rem}bao-icon.bao-icon-small{height:2rem;width:2rem;line-height:1.75rem}bao-icon.bao-icon-x-small{height:1.5rem;width:1.5rem;line-height:.5rem}bao-icon.bao-icon-xx-small{height:1rem;width:1rem;line-height:.85rem}bao-icon.bao-icon-spinner{animation:spin .75s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
355
|
+
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: BaoIconDictionary }, { type: i0.Renderer2 }]; }, propDecorators: { color: [{
|
|
352
356
|
type: Input
|
|
353
357
|
}], size: [{
|
|
354
358
|
type: Input
|
|
@@ -2936,6 +2940,942 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImpor
|
|
|
2936
2940
|
* See LICENSE file in the project root for full license information.
|
|
2937
2941
|
*/
|
|
2938
2942
|
|
|
2943
|
+
/*
|
|
2944
|
+
* Copyright (c) 2022 Ville de Montreal. All rights reserved.
|
|
2945
|
+
* Licensed under the MIT license.
|
|
2946
|
+
* See LICENSE file in the project root for full license information.
|
|
2947
|
+
*/
|
|
2948
|
+
/**
|
|
2949
|
+
* Animations used by MatDialog.
|
|
2950
|
+
* @docs-private
|
|
2951
|
+
*/
|
|
2952
|
+
const baoModalAnimations = {
|
|
2953
|
+
/** Animation that is applied on the modal container by default. */
|
|
2954
|
+
modalContainer: trigger('modalContainer', [
|
|
2955
|
+
// Note: The `enter` animation transitions to `transform: none`, because for some reason
|
|
2956
|
+
// specifying the transform explicitly, causes IE both to blur the modal content and
|
|
2957
|
+
// decimate the animation performance. Leaving it as `none` solves both issues.
|
|
2958
|
+
state('void, exit', style({ opacity: 0, transform: 'scale(0.7)' })),
|
|
2959
|
+
state('enter', style({ transform: 'none' })),
|
|
2960
|
+
transition('* => enter', group([
|
|
2961
|
+
animate('150ms cubic-bezier(0, 0, 0.2, 1)', style({ transform: 'none', opacity: 1 })),
|
|
2962
|
+
query('@*', animateChild(), { optional: true })
|
|
2963
|
+
])),
|
|
2964
|
+
transition('* => void, * => exit', group([
|
|
2965
|
+
animate('75ms cubic-bezier(0.4, 0.0, 0.2, 1)', style({ opacity: 0 })),
|
|
2966
|
+
query('@*', animateChild(), { optional: true })
|
|
2967
|
+
]))
|
|
2968
|
+
])
|
|
2969
|
+
};
|
|
2970
|
+
|
|
2971
|
+
/*
|
|
2972
|
+
* Copyright (c) 2022 Ville de Montreal. All rights reserved.
|
|
2973
|
+
* Licensed under the MIT license.
|
|
2974
|
+
* See LICENSE file in the project root for full license information.
|
|
2975
|
+
*/
|
|
2976
|
+
var eModalDesktopWidthSize;
|
|
2977
|
+
(function (eModalDesktopWidthSize) {
|
|
2978
|
+
eModalDesktopWidthSize["SMALL"] = "bao-modal-sm";
|
|
2979
|
+
eModalDesktopWidthSize["MEDIUM"] = "bao-modal-md";
|
|
2980
|
+
eModalDesktopWidthSize["LARGE"] = "bao-modal-lg"; // Full width minus 32px (global margin : left and rigth)
|
|
2981
|
+
})(eModalDesktopWidthSize || (eModalDesktopWidthSize = {}));
|
|
2982
|
+
var eModalMobileWidthSize;
|
|
2983
|
+
(function (eModalMobileWidthSize) {
|
|
2984
|
+
eModalMobileWidthSize["FULL"] = "bao-modal-mobil-full";
|
|
2985
|
+
eModalMobileWidthSize["COMPACT"] = "bao-modal-mobil-compact"; // 300px
|
|
2986
|
+
})(eModalMobileWidthSize || (eModalMobileWidthSize = {}));
|
|
2987
|
+
/**
|
|
2988
|
+
* Configuration for opening a modal dialog with the BaoModal service.
|
|
2989
|
+
*/
|
|
2990
|
+
class BaoModalInitialConfig {
|
|
2991
|
+
constructor() {
|
|
2992
|
+
/** The ARIA role of the dialog element. */
|
|
2993
|
+
this.role = 'dialog';
|
|
2994
|
+
/** Custom class for the overlay pane. */
|
|
2995
|
+
this.panelClass = '';
|
|
2996
|
+
/** Whether the dialog has a backdrop. */
|
|
2997
|
+
this.hasBackdrop = true;
|
|
2998
|
+
/** Custom class for the backdrop. */
|
|
2999
|
+
this.backdropClass = '';
|
|
3000
|
+
/** Whether the user can use escape or clicking on the backdrop to close the modal. */
|
|
3001
|
+
this.disableClose = false;
|
|
3002
|
+
/** Width of the dialog. */
|
|
3003
|
+
this.width = '';
|
|
3004
|
+
/** Height of the dialog. */
|
|
3005
|
+
this.height = '';
|
|
3006
|
+
/** Max-width of the dialog. If a number is provided, assumes pixel units. Defaults to 80vw. */
|
|
3007
|
+
this.maxWidth = '80vw';
|
|
3008
|
+
/** Data being injected into the child component. */
|
|
3009
|
+
this.data = null;
|
|
3010
|
+
/** ID of the element that describes the dialog. */
|
|
3011
|
+
this.ariaDescribedBy = null;
|
|
3012
|
+
/** ID of the element that labels the dialog. */
|
|
3013
|
+
this.ariaLabelledBy = null;
|
|
3014
|
+
/** Aria label to assign to the dialog element. */
|
|
3015
|
+
this.ariaLabel = null;
|
|
3016
|
+
/**
|
|
3017
|
+
* Where the dialog should focus on open.
|
|
3018
|
+
*/
|
|
3019
|
+
this.autoFocus = 'first-tabbable';
|
|
3020
|
+
/**
|
|
3021
|
+
* Whether the dialog should restore focus to the
|
|
3022
|
+
* previously-focused element, after it's closed.
|
|
3023
|
+
*/
|
|
3024
|
+
this.restoreFocus = true;
|
|
3025
|
+
/** Whether to wait for the opening animation to finish before trapping focus. */
|
|
3026
|
+
this.delayFocusTrap = true;
|
|
3027
|
+
/**
|
|
3028
|
+
* Whether the dialog should close when the user goes backwards/forwards in history.
|
|
3029
|
+
* Note that this usually doesn't include clicking on links (unless the user is using
|
|
3030
|
+
* the `HashLocationStrategy`).
|
|
3031
|
+
*/
|
|
3032
|
+
this.closeOnNavigation = true;
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
/**
|
|
3037
|
+
* Throws an exception for the case when a ComponentPortal is
|
|
3038
|
+
* attached to a DomPortalOutlet without an origin.
|
|
3039
|
+
* @docs-private
|
|
3040
|
+
*/
|
|
3041
|
+
function throwBaoModalContentAlreadyAttachedError() {
|
|
3042
|
+
throw Error('Attempting to attach modal content after content is already attached');
|
|
3043
|
+
}
|
|
3044
|
+
/**
|
|
3045
|
+
* Base class for the `BaoModalContainer`. The base class does not implement
|
|
3046
|
+
* animations as these are left to implementers of the modal container.
|
|
3047
|
+
*/
|
|
3048
|
+
class _BaoModalContainerBase extends BasePortalOutlet {
|
|
3049
|
+
constructor(_elementRef, _focusTrapFactory, _changeDetectorRef, _document,
|
|
3050
|
+
/** The modal configuration. */
|
|
3051
|
+
_config, _interactivityChecker, _ngZone, _focusMonitor) {
|
|
3052
|
+
super();
|
|
3053
|
+
this._elementRef = _elementRef;
|
|
3054
|
+
this._focusTrapFactory = _focusTrapFactory;
|
|
3055
|
+
this._changeDetectorRef = _changeDetectorRef;
|
|
3056
|
+
this._config = _config;
|
|
3057
|
+
this._interactivityChecker = _interactivityChecker;
|
|
3058
|
+
this._ngZone = _ngZone;
|
|
3059
|
+
this._focusMonitor = _focusMonitor;
|
|
3060
|
+
/** Emits when an animation state changes. */
|
|
3061
|
+
this._animationStateChanged = new EventEmitter();
|
|
3062
|
+
/**
|
|
3063
|
+
* Type of interaction that led to the modal being closed. This is used to determine
|
|
3064
|
+
* whether the focus style will be applied when returning focus to its original location
|
|
3065
|
+
* after the modal is closed.
|
|
3066
|
+
*/
|
|
3067
|
+
this._closeInteractionType = null;
|
|
3068
|
+
/** Element that was focused before the modal was opened. Save this to restore upon close. */
|
|
3069
|
+
this._elementFocusedBeforeDialogWasOpened = null;
|
|
3070
|
+
this._ariaLabelledBy = _config.ariaLabelledBy || null;
|
|
3071
|
+
this._document = _document;
|
|
3072
|
+
}
|
|
3073
|
+
/** Initializes the modal container with the attached content. */
|
|
3074
|
+
_initializeWithAttachedContent() {
|
|
3075
|
+
this._setupFocusTrap();
|
|
3076
|
+
// Save the previously focused element. This element will be re-focused
|
|
3077
|
+
// when the modal closes.
|
|
3078
|
+
this._capturePreviouslyFocusedElement();
|
|
3079
|
+
}
|
|
3080
|
+
/**
|
|
3081
|
+
* Attach a ComponentPortal as content to this modal container.
|
|
3082
|
+
*/
|
|
3083
|
+
attachComponentPortal(portal) {
|
|
3084
|
+
return this._portalOutlet.attachComponentPortal(portal);
|
|
3085
|
+
}
|
|
3086
|
+
/**
|
|
3087
|
+
* Attach a TemplatePortal as content to this modal container.
|
|
3088
|
+
*/
|
|
3089
|
+
attachTemplatePortal(portal) {
|
|
3090
|
+
return this._portalOutlet.attachTemplatePortal(portal);
|
|
3091
|
+
}
|
|
3092
|
+
/** Moves focus back into the modal if it was moved out. */
|
|
3093
|
+
_recaptureFocus() {
|
|
3094
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3095
|
+
if (!this._containsFocus()) {
|
|
3096
|
+
yield this._trapFocus();
|
|
3097
|
+
}
|
|
3098
|
+
});
|
|
3099
|
+
}
|
|
3100
|
+
/**
|
|
3101
|
+
* Moves the focus inside the focus trap. When autoFocus is not set to 'modal', if focus
|
|
3102
|
+
* cannot be moved then focus will go to the modal container.
|
|
3103
|
+
*/
|
|
3104
|
+
_trapFocus() {
|
|
3105
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3106
|
+
const element = this._elementRef.nativeElement;
|
|
3107
|
+
// If were to attempt to focus immediately, then the content of the modal would not yet be
|
|
3108
|
+
// ready in instances where change detection has to run first. To deal with this, we simply
|
|
3109
|
+
// wait for the microtask queue to be empty when setting focus when autoFocus isn't set to
|
|
3110
|
+
// modal. If the element inside the modal can't be focused, then the container is focused
|
|
3111
|
+
// so the user can't tab into other elements behind it.
|
|
3112
|
+
switch (this._config.autoFocus) {
|
|
3113
|
+
case 'modal':
|
|
3114
|
+
// Ensure that focus is on the modal container. It's possible that a different
|
|
3115
|
+
// component tried to move focus while the open animation was running. See:
|
|
3116
|
+
// https://github.com/angular/components/issues/16215. Note that we only want to do this
|
|
3117
|
+
// if the focus isn't inside the modal already, because it's possible that the consumer
|
|
3118
|
+
// turned off `autoFocus` in order to move focus themselves.
|
|
3119
|
+
if (!this._containsFocus()) {
|
|
3120
|
+
element.focus();
|
|
3121
|
+
}
|
|
3122
|
+
break;
|
|
3123
|
+
case 'first-tabbable':
|
|
3124
|
+
// If we weren't able to find a focusable element in the modal, then focus the modal
|
|
3125
|
+
// container instead.
|
|
3126
|
+
const focusedSuccessfully = yield this._focusTrap.focusInitialElementWhenReady();
|
|
3127
|
+
if (!focusedSuccessfully) {
|
|
3128
|
+
this._focusDialogContainer();
|
|
3129
|
+
}
|
|
3130
|
+
break;
|
|
3131
|
+
case 'first-heading':
|
|
3132
|
+
this._focusByCssSelector('h1, h2, h3, h4, h5, h6, [role="heading"]');
|
|
3133
|
+
break;
|
|
3134
|
+
default:
|
|
3135
|
+
this._focusByCssSelector(this._config.autoFocus);
|
|
3136
|
+
break;
|
|
3137
|
+
}
|
|
3138
|
+
});
|
|
3139
|
+
}
|
|
3140
|
+
/** Restores focus to the element that was focused before the modal opened. */
|
|
3141
|
+
_restoreFocus() {
|
|
3142
|
+
const previousElement = this._elementFocusedBeforeDialogWasOpened;
|
|
3143
|
+
// We need the extra check, because IE can set the `activeElement` to null in some cases.
|
|
3144
|
+
if (this._config.restoreFocus &&
|
|
3145
|
+
previousElement &&
|
|
3146
|
+
typeof previousElement.focus === 'function') {
|
|
3147
|
+
const activeElement = _getFocusedElementPierceShadowDom();
|
|
3148
|
+
const element = this._elementRef.nativeElement;
|
|
3149
|
+
// Make sure that focus is still inside the modal or is on the body (usually because a
|
|
3150
|
+
// non-focusable element like the backdrop was clicked) before moving it. It's possible that
|
|
3151
|
+
// the consumer moved it themselves before the animation was done, in which case we shouldn't
|
|
3152
|
+
// do anything.
|
|
3153
|
+
if (!activeElement ||
|
|
3154
|
+
activeElement === this._document.body ||
|
|
3155
|
+
activeElement === element ||
|
|
3156
|
+
element.contains(activeElement)) {
|
|
3157
|
+
if (this._focusMonitor) {
|
|
3158
|
+
this._focusMonitor.focusVia(previousElement, this._closeInteractionType);
|
|
3159
|
+
this._closeInteractionType = null;
|
|
3160
|
+
}
|
|
3161
|
+
else {
|
|
3162
|
+
previousElement.focus();
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
if (this._focusTrap) {
|
|
3167
|
+
this._focusTrap.destroy();
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
/**
|
|
3171
|
+
* Focuses the provided element. If the element is not focusable, it will add a tabIndex
|
|
3172
|
+
* attribute to forcefully focus it. The attribute is removed after focus is moved.
|
|
3173
|
+
*/
|
|
3174
|
+
_forceFocus(element, options) {
|
|
3175
|
+
if (!this._interactivityChecker.isFocusable(element)) {
|
|
3176
|
+
element.tabIndex = -1;
|
|
3177
|
+
// The tabindex attribute should be removed to avoid navigating to that element again
|
|
3178
|
+
this._ngZone.runOutsideAngular(() => {
|
|
3179
|
+
element.addEventListener('blur', () => element.removeAttribute('tabindex'));
|
|
3180
|
+
element.addEventListener('mousedown', () => element.removeAttribute('tabindex'));
|
|
3181
|
+
});
|
|
3182
|
+
}
|
|
3183
|
+
element.focus(options);
|
|
3184
|
+
}
|
|
3185
|
+
/**
|
|
3186
|
+
* Focuses the first element that matches the given selector within the focus trap.
|
|
3187
|
+
*/
|
|
3188
|
+
_focusByCssSelector(selector, options) {
|
|
3189
|
+
const elementToFocus = this._elementRef.nativeElement.querySelector(selector);
|
|
3190
|
+
if (elementToFocus) {
|
|
3191
|
+
this._forceFocus(elementToFocus, options);
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
/** Sets up the focus trap. */
|
|
3195
|
+
_setupFocusTrap() {
|
|
3196
|
+
this._focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement);
|
|
3197
|
+
}
|
|
3198
|
+
/** Captures the element that was focused before the modal was opened. */
|
|
3199
|
+
_capturePreviouslyFocusedElement() {
|
|
3200
|
+
if (this._document) {
|
|
3201
|
+
this._elementFocusedBeforeDialogWasOpened =
|
|
3202
|
+
_getFocusedElementPierceShadowDom();
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
/** Focuses the modal container. */
|
|
3206
|
+
_focusDialogContainer() {
|
|
3207
|
+
// Note that there is no focus method when rendering on the server.
|
|
3208
|
+
if (this._elementRef.nativeElement.focus) {
|
|
3209
|
+
this._elementRef.nativeElement.focus();
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
/** Returns whether focus is inside the modal. */
|
|
3213
|
+
_containsFocus() {
|
|
3214
|
+
const element = this._elementRef.nativeElement;
|
|
3215
|
+
const activeElement = _getFocusedElementPierceShadowDom();
|
|
3216
|
+
return element === activeElement || element.contains(activeElement);
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
_BaoModalContainerBase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: _BaoModalContainerBase, deps: [{ token: i0.ElementRef }, { token: i1$3.ConfigurableFocusTrapFactory }, { token: i0.ChangeDetectorRef }, { token: DOCUMENT, optional: true }, { token: BaoModalInitialConfig }, { token: i1$3.InteractivityChecker }, { token: i0.NgZone }, { token: i1$3.FocusMonitor }], target: i0.ɵɵFactoryTarget.Directive });
|
|
3220
|
+
_BaoModalContainerBase.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.1.1", type: _BaoModalContainerBase, viewQueries: [{ propertyName: "_portalOutlet", first: true, predicate: CdkPortalOutlet, descendants: true, static: true }], usesInheritance: true, ngImport: i0 });
|
|
3221
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: _BaoModalContainerBase, decorators: [{
|
|
3222
|
+
type: Directive
|
|
3223
|
+
}], ctorParameters: function () {
|
|
3224
|
+
return [{ type: i0.ElementRef }, { type: i1$3.ConfigurableFocusTrapFactory }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
|
|
3225
|
+
type: Optional
|
|
3226
|
+
}, {
|
|
3227
|
+
type: Inject,
|
|
3228
|
+
args: [DOCUMENT]
|
|
3229
|
+
}] }, { type: BaoModalInitialConfig }, { type: i1$3.InteractivityChecker }, { type: i0.NgZone }, { type: i1$3.FocusMonitor }];
|
|
3230
|
+
}, propDecorators: { _portalOutlet: [{
|
|
3231
|
+
type: ViewChild,
|
|
3232
|
+
args: [CdkPortalOutlet, { static: true }]
|
|
3233
|
+
}] } });
|
|
3234
|
+
class BaoModalContainer extends _BaoModalContainerBase {
|
|
3235
|
+
constructor() {
|
|
3236
|
+
super(...arguments);
|
|
3237
|
+
/** State of the modal animation. */
|
|
3238
|
+
this._state = 'enter';
|
|
3239
|
+
}
|
|
3240
|
+
/** Callback, invoked whenever an animation on the host completes. */
|
|
3241
|
+
_onAnimationDone({ toState, totalTime }) {
|
|
3242
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3243
|
+
if (toState === 'enter') {
|
|
3244
|
+
yield this._trapFocus();
|
|
3245
|
+
this._animationStateChanged.next({ state: 'opened', totalTime });
|
|
3246
|
+
}
|
|
3247
|
+
else if (toState === 'exit') {
|
|
3248
|
+
this._restoreFocus();
|
|
3249
|
+
this._animationStateChanged.next({ state: 'closed', totalTime });
|
|
3250
|
+
}
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
/** Callback, invoked when an animation on the host starts. */
|
|
3254
|
+
_onAnimationStart({ toState, totalTime }) {
|
|
3255
|
+
if (toState === 'enter') {
|
|
3256
|
+
this._animationStateChanged.next({ state: 'opening', totalTime });
|
|
3257
|
+
}
|
|
3258
|
+
else if (toState === 'exit' || toState === 'void') {
|
|
3259
|
+
this._animationStateChanged.next({ state: 'closing', totalTime });
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
/** Starts the modal exit animation. */
|
|
3263
|
+
_startExitAnimation() {
|
|
3264
|
+
this._state = 'exit';
|
|
3265
|
+
// Mark the container for check so it can react if the
|
|
3266
|
+
// view container is using OnPush change detection.
|
|
3267
|
+
this._changeDetectorRef.markForCheck();
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
BaoModalContainer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModalContainer, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
3271
|
+
BaoModalContainer.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.1", type: BaoModalContainer, selector: "bao-modal-container", host: { attributes: { "tabindex": "-1", "aria-modal": "true" }, listeners: { "@modalContainer.start": "_onAnimationStart($event)", "@modalContainer.done": "_onAnimationDone($event)" }, properties: { "id": "_id", "attr.role": "_config.role", "attr.aria-labelledby": "_config.ariaLabel ? null : _ariaLabelledBy", "attr.aria-label": "_config.ariaLabel", "attr.aria-describedby": "_config.ariaDescribedBy || null", "@modalContainer": "_state" }, classAttribute: "bao-modal-container" }, usesInheritance: true, ngImport: i0, template: "<ng-template cdkPortalOutlet></ng-template>\n", styles: [".bao-container{padding-right:16px;padding-left:16px;margin-right:auto;margin-left:auto;width:100%}@media (min-width: 576px){.bao-container{max-width:576px}}@media (min-width: 768px){.bao-container{max-width:768px}}@media (min-width: 992px){.bao-container{max-width:992px}}@media (min-width: 1200px){.bao-container{max-width:1200px}}.bao-row{display:flex;flex-wrap:wrap;margin-right:-16px;margin-left:-16px}.bao-col-12,.bao-col-lg-7{position:relative;width:100%;padding-right:1rem;padding-left:1rem}@media (min-width: 992px){.bao-col-lg-7{flex:0 0 58.33333%;max-width:58.33333%}}.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed;z-index:1000}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;z-index:1000;display:flex}.cdk-overlay-pane.bao-modal-mobil-full{width:100%;height:100%}.cdk-overlay-pane.bao-modal-mobil-compact{width:300px;height:100%}@media (min-width: 768px){.cdk-overlay-pane{width:500px;height:auto}.cdk-overlay-pane.bao-modal-lg{width:calc(100% - 4rem);height:calc(100% - 4rem)}.cdk-overlay-pane.bao-modal-md,.cdk-overlay-pane.bao-modal-sm{width:500px;height:auto}}@media (min-width: 992px){.cdk-overlay-pane{width:500px;height:auto}.cdk-overlay-pane.bao-modal-lg{width:calc(100% - 4rem);height:calc(100% - 4rem)}.cdk-overlay-pane.bao-modal-md{width:800px;height:auto}.cdk-overlay-pane.bao-modal-sm{width:500px;height:auto}}.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;z-index:1000;pointer-events:auto;-webkit-tap-highlight-color:transparent;transition:opacity .4s cubic-bezier(.25,.8,.25,1);opacity:0}.cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:.5}.cdk-high-contrast-active .cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:.6}.cdk-overlay-dark-backdrop{background:black}.cdk-overlay-transparent-backdrop,.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing{opacity:.5}.cdk-overlay-connected-position-bounding-box{position:absolute;z-index:1000;display:flex;flex-direction:column;min-width:1px;min-height:1px}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}.bao-modal-container{display:block;overflow:auto;width:100%;height:100%;min-height:inherit;max-height:inherit;background-color:#fff;background-clip:padding-box;border:0 solid rgba(0,0,0,.2);border-radius:.5rem;outline:0}.bao-modal-content{display:flex;flex-direction:column;height:100%}.bao-modal-header{flex:0 0 auto;display:flex;align-items:flex-start;justify-content:space-between;border-bottom:1px solid #ced4da;border-top-left-radius:.5rem;border-top-right-radius:.5rem}.bao-modal-header .bao-modal-title{font-size:1rem;line-height:1.5rem;margin:1rem 0 1rem 1rem}.bao-modal-header button{margin:.5rem}.bao-modal-body{display:block;padding:1rem;overflow:auto;flex-grow:1}@media (min-width: 768px){.bao-modal-body{padding:2rem}}.bao-modal-footer{display:flex;flex-wrap:wrap;padding:1rem;border-top:1px solid #ced4da;border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem}.bao-modal-footer.bao-modal-footer-order{justify-content:unset;flex-direction:column-reverse}.bao-modal-footer button{text-align:center}.bao-modal-footer .bao-button-primary,.bao-modal-footer .bao-button-secondary{width:100%;display:block}.bao-modal-footer .bao-button-secondary{margin-bottom:.5rem}.bao-modal-footer .bao-button-tertiary{display:none}@media (min-width: 768px){.bao-modal-footer{justify-content:flex-end}.bao-modal-footer.bao-modal-footer-order{flex-direction:row-reverse}.bao-modal-footer .bao-button-primary{margin-left:1rem}.bao-modal-footer .bao-button-secondary{margin-bottom:0}.bao-modal-footer .bao-button-primary,.bao-modal-footer .bao-button-secondary{width:auto}.bao-modal-footer .bao-button-tertiary{display:block}}\n"], directives: [{ type: i3.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }], animations: [baoModalAnimations.modalContainer], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None });
|
|
3272
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModalContainer, decorators: [{
|
|
3273
|
+
type: Component,
|
|
3274
|
+
args: [{ selector: 'bao-modal-container', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, animations: [baoModalAnimations.modalContainer], host: {
|
|
3275
|
+
class: 'bao-modal-container',
|
|
3276
|
+
tabindex: '-1',
|
|
3277
|
+
'aria-modal': 'true',
|
|
3278
|
+
'[id]': '_id',
|
|
3279
|
+
'[attr.role]': '_config.role',
|
|
3280
|
+
'[attr.aria-labelledby]': '_config.ariaLabel ? null : _ariaLabelledBy',
|
|
3281
|
+
'[attr.aria-label]': '_config.ariaLabel',
|
|
3282
|
+
'[attr.aria-describedby]': '_config.ariaDescribedBy || null',
|
|
3283
|
+
'[@modalContainer]': '_state',
|
|
3284
|
+
'(@modalContainer.start)': '_onAnimationStart($event)',
|
|
3285
|
+
'(@modalContainer.done)': '_onAnimationDone($event)'
|
|
3286
|
+
}, template: "<ng-template cdkPortalOutlet></ng-template>\n", styles: [".bao-container{padding-right:16px;padding-left:16px;margin-right:auto;margin-left:auto;width:100%}@media (min-width: 576px){.bao-container{max-width:576px}}@media (min-width: 768px){.bao-container{max-width:768px}}@media (min-width: 992px){.bao-container{max-width:992px}}@media (min-width: 1200px){.bao-container{max-width:1200px}}.bao-row{display:flex;flex-wrap:wrap;margin-right:-16px;margin-left:-16px}.bao-col-12,.bao-col-lg-7{position:relative;width:100%;padding-right:1rem;padding-left:1rem}@media (min-width: 992px){.bao-col-lg-7{flex:0 0 58.33333%;max-width:58.33333%}}.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed;z-index:1000}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;z-index:1000;display:flex}.cdk-overlay-pane.bao-modal-mobil-full{width:100%;height:100%}.cdk-overlay-pane.bao-modal-mobil-compact{width:300px;height:100%}@media (min-width: 768px){.cdk-overlay-pane{width:500px;height:auto}.cdk-overlay-pane.bao-modal-lg{width:calc(100% - 4rem);height:calc(100% - 4rem)}.cdk-overlay-pane.bao-modal-md,.cdk-overlay-pane.bao-modal-sm{width:500px;height:auto}}@media (min-width: 992px){.cdk-overlay-pane{width:500px;height:auto}.cdk-overlay-pane.bao-modal-lg{width:calc(100% - 4rem);height:calc(100% - 4rem)}.cdk-overlay-pane.bao-modal-md{width:800px;height:auto}.cdk-overlay-pane.bao-modal-sm{width:500px;height:auto}}.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;z-index:1000;pointer-events:auto;-webkit-tap-highlight-color:transparent;transition:opacity .4s cubic-bezier(.25,.8,.25,1);opacity:0}.cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:.5}.cdk-high-contrast-active .cdk-overlay-backdrop.cdk-overlay-backdrop-showing{opacity:.6}.cdk-overlay-dark-backdrop{background:black}.cdk-overlay-transparent-backdrop,.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing{opacity:.5}.cdk-overlay-connected-position-bounding-box{position:absolute;z-index:1000;display:flex;flex-direction:column;min-width:1px;min-height:1px}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}.bao-modal-container{display:block;overflow:auto;width:100%;height:100%;min-height:inherit;max-height:inherit;background-color:#fff;background-clip:padding-box;border:0 solid rgba(0,0,0,.2);border-radius:.5rem;outline:0}.bao-modal-content{display:flex;flex-direction:column;height:100%}.bao-modal-header{flex:0 0 auto;display:flex;align-items:flex-start;justify-content:space-between;border-bottom:1px solid #ced4da;border-top-left-radius:.5rem;border-top-right-radius:.5rem}.bao-modal-header .bao-modal-title{font-size:1rem;line-height:1.5rem;margin:1rem 0 1rem 1rem}.bao-modal-header button{margin:.5rem}.bao-modal-body{display:block;padding:1rem;overflow:auto;flex-grow:1}@media (min-width: 768px){.bao-modal-body{padding:2rem}}.bao-modal-footer{display:flex;flex-wrap:wrap;padding:1rem;border-top:1px solid #ced4da;border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem}.bao-modal-footer.bao-modal-footer-order{justify-content:unset;flex-direction:column-reverse}.bao-modal-footer button{text-align:center}.bao-modal-footer .bao-button-primary,.bao-modal-footer .bao-button-secondary{width:100%;display:block}.bao-modal-footer .bao-button-secondary{margin-bottom:.5rem}.bao-modal-footer .bao-button-tertiary{display:none}@media (min-width: 768px){.bao-modal-footer{justify-content:flex-end}.bao-modal-footer.bao-modal-footer-order{flex-direction:row-reverse}.bao-modal-footer .bao-button-primary{margin-left:1rem}.bao-modal-footer .bao-button-secondary{margin-bottom:0}.bao-modal-footer .bao-button-primary,.bao-modal-footer .bao-button-secondary{width:auto}.bao-modal-footer .bao-button-tertiary{display:block}}\n"] }]
|
|
3287
|
+
}] });
|
|
3288
|
+
|
|
3289
|
+
// Counter for unique modal ids.
|
|
3290
|
+
let uniqueId = 0;
|
|
3291
|
+
/**
|
|
3292
|
+
* Reference to a modal opened via the BaoModalService.
|
|
3293
|
+
*/
|
|
3294
|
+
class BaoModalRef {
|
|
3295
|
+
constructor(_overlayRef, _containerInstance,
|
|
3296
|
+
/** Id of the modal. */
|
|
3297
|
+
id = `bao-modal-${uniqueId++}`) {
|
|
3298
|
+
this._overlayRef = _overlayRef;
|
|
3299
|
+
this._containerInstance = _containerInstance;
|
|
3300
|
+
this.id = id;
|
|
3301
|
+
/** Whether the user is allowed to close the modal. */
|
|
3302
|
+
this.disableClose = this._containerInstance._config.disableClose;
|
|
3303
|
+
/** Subject for notifying the user that the modal has finished opening. */
|
|
3304
|
+
this._afterOpened = new Subject();
|
|
3305
|
+
/** Subject for notifying the user that the modal has finished closing. */
|
|
3306
|
+
this._afterClosed = new Subject();
|
|
3307
|
+
/** Subject for notifying the user that the modal has started closing. */
|
|
3308
|
+
this._beforeClosed = new Subject();
|
|
3309
|
+
/** Current state of the modal. */
|
|
3310
|
+
this._state = 0 /* OPEN */;
|
|
3311
|
+
// Pass the id along to the container.
|
|
3312
|
+
_containerInstance._id = id;
|
|
3313
|
+
// Emit when opening animation completes
|
|
3314
|
+
_containerInstance._animationStateChanged
|
|
3315
|
+
.pipe(filter(event => event.state === 'opened'), take(1))
|
|
3316
|
+
.subscribe(() => {
|
|
3317
|
+
this._afterOpened.next();
|
|
3318
|
+
this._afterOpened.complete();
|
|
3319
|
+
});
|
|
3320
|
+
// Dispose overlay when closing animation is complete
|
|
3321
|
+
_containerInstance._animationStateChanged
|
|
3322
|
+
.pipe(filter(event => event.state === 'closed'), take(1))
|
|
3323
|
+
.subscribe(() => {
|
|
3324
|
+
clearTimeout(this._closeFallbackTimeout);
|
|
3325
|
+
this._finishModalClose();
|
|
3326
|
+
});
|
|
3327
|
+
_overlayRef.detachments().subscribe(() => {
|
|
3328
|
+
this._beforeClosed.next(this._result);
|
|
3329
|
+
this._beforeClosed.complete();
|
|
3330
|
+
this._afterClosed.next(this._result);
|
|
3331
|
+
this._afterClosed.complete();
|
|
3332
|
+
this.componentInstance = null;
|
|
3333
|
+
this._overlayRef.dispose();
|
|
3334
|
+
});
|
|
3335
|
+
_overlayRef
|
|
3336
|
+
.keydownEvents()
|
|
3337
|
+
.pipe(filter(event => {
|
|
3338
|
+
return (event.keyCode === ESCAPE &&
|
|
3339
|
+
!this.disableClose &&
|
|
3340
|
+
!hasModifierKey(event));
|
|
3341
|
+
}))
|
|
3342
|
+
.subscribe(event => {
|
|
3343
|
+
event.preventDefault();
|
|
3344
|
+
_closeModalVia(this, 'keyboard');
|
|
3345
|
+
});
|
|
3346
|
+
_overlayRef.backdropClick().subscribe(() => __awaiter(this, void 0, void 0, function* () {
|
|
3347
|
+
if (this.disableClose) {
|
|
3348
|
+
yield this._containerInstance._recaptureFocus();
|
|
3349
|
+
}
|
|
3350
|
+
else {
|
|
3351
|
+
_closeModalVia(this, 'mouse');
|
|
3352
|
+
}
|
|
3353
|
+
}));
|
|
3354
|
+
}
|
|
3355
|
+
/**
|
|
3356
|
+
* Close the modal.
|
|
3357
|
+
* @param modalResult Optional result to return to the modal opener.
|
|
3358
|
+
*/
|
|
3359
|
+
close(modalResult) {
|
|
3360
|
+
this._result = modalResult;
|
|
3361
|
+
// Transition the backdrop in parallel to the modal.
|
|
3362
|
+
this._containerInstance._animationStateChanged
|
|
3363
|
+
.pipe(filter(event => event.state === 'closing'), take(1))
|
|
3364
|
+
.subscribe(event => {
|
|
3365
|
+
this._beforeClosed.next(modalResult);
|
|
3366
|
+
this._beforeClosed.complete();
|
|
3367
|
+
this._overlayRef.detachBackdrop();
|
|
3368
|
+
// The logic that disposes of the overlay depends on the exit animation completing, however
|
|
3369
|
+
// it isn't guaranteed if the parent view is destroyed while it's running. Add a fallback
|
|
3370
|
+
// timeout which will clean everything up if the animation hasn't fired within the specified
|
|
3371
|
+
// amount of time plus 100ms. We don't need to run this outside the NgZone, because for the
|
|
3372
|
+
// vast majority of cases the timeout will have been cleared before it has the chance to fire.
|
|
3373
|
+
this._closeFallbackTimeout = setTimeout(() => this._finishModalClose(), event.totalTime + 100);
|
|
3374
|
+
});
|
|
3375
|
+
this._state = 1 /* CLOSING */;
|
|
3376
|
+
this._containerInstance._startExitAnimation();
|
|
3377
|
+
}
|
|
3378
|
+
/**
|
|
3379
|
+
* Gets an observable that is notified when the modal is finished opening.
|
|
3380
|
+
*/
|
|
3381
|
+
afterOpened() {
|
|
3382
|
+
return this._afterOpened;
|
|
3383
|
+
}
|
|
3384
|
+
/**
|
|
3385
|
+
* Gets an observable that is notified when the modal is finished closing.
|
|
3386
|
+
*/
|
|
3387
|
+
afterClosed() {
|
|
3388
|
+
return this._afterClosed;
|
|
3389
|
+
}
|
|
3390
|
+
/**
|
|
3391
|
+
* Gets an observable that is notified when the modal has started closing.
|
|
3392
|
+
*/
|
|
3393
|
+
beforeClosed() {
|
|
3394
|
+
return this._beforeClosed;
|
|
3395
|
+
}
|
|
3396
|
+
/**
|
|
3397
|
+
* Gets an observable that emits when the overlay's backdrop has been clicked.
|
|
3398
|
+
*/
|
|
3399
|
+
backdropClick() {
|
|
3400
|
+
return this._overlayRef.backdropClick();
|
|
3401
|
+
}
|
|
3402
|
+
/**
|
|
3403
|
+
* Gets an observable that emits when keydown events are targeted on the overlay.
|
|
3404
|
+
*/
|
|
3405
|
+
keydownEvents() {
|
|
3406
|
+
return this._overlayRef.keydownEvents();
|
|
3407
|
+
}
|
|
3408
|
+
/**
|
|
3409
|
+
* Updates the dialog's position.
|
|
3410
|
+
*/
|
|
3411
|
+
updatePosition(position) {
|
|
3412
|
+
const strategy = this._getPositionStrategy();
|
|
3413
|
+
if (position && (position.left || position.right)) {
|
|
3414
|
+
position.left
|
|
3415
|
+
? strategy.left(position.left)
|
|
3416
|
+
: strategy.right(position.right);
|
|
3417
|
+
}
|
|
3418
|
+
else {
|
|
3419
|
+
strategy.centerHorizontally();
|
|
3420
|
+
}
|
|
3421
|
+
if (position && (position.top || position.bottom)) {
|
|
3422
|
+
position.top
|
|
3423
|
+
? strategy.top(position.top)
|
|
3424
|
+
: strategy.bottom(position.bottom);
|
|
3425
|
+
}
|
|
3426
|
+
else {
|
|
3427
|
+
strategy.centerVertically();
|
|
3428
|
+
}
|
|
3429
|
+
this._overlayRef.updatePosition();
|
|
3430
|
+
return this;
|
|
3431
|
+
}
|
|
3432
|
+
/**
|
|
3433
|
+
* Updates the modal's width and height.
|
|
3434
|
+
*/
|
|
3435
|
+
updateSize(width = '', height = '') {
|
|
3436
|
+
this._overlayRef.updateSize({ width, height });
|
|
3437
|
+
this._overlayRef.updatePosition();
|
|
3438
|
+
return this;
|
|
3439
|
+
}
|
|
3440
|
+
/** Add a CSS class or an array of classes to the overlay pane. */
|
|
3441
|
+
addPanelClass(classes) {
|
|
3442
|
+
this._overlayRef.addPanelClass(classes);
|
|
3443
|
+
return this;
|
|
3444
|
+
}
|
|
3445
|
+
/** Remove a CSS class or an array of classes from the overlay pane. */
|
|
3446
|
+
removePanelClass(classes) {
|
|
3447
|
+
this._overlayRef.removePanelClass(classes);
|
|
3448
|
+
return this;
|
|
3449
|
+
}
|
|
3450
|
+
/** Gets the current state of the modal's lifecycle. */
|
|
3451
|
+
getState() {
|
|
3452
|
+
return this._state;
|
|
3453
|
+
}
|
|
3454
|
+
/**
|
|
3455
|
+
* Finishes the modal close by updating the state of the modal
|
|
3456
|
+
* and disposing the overlay.
|
|
3457
|
+
*/
|
|
3458
|
+
_finishModalClose() {
|
|
3459
|
+
this._state = 2 /* CLOSED */;
|
|
3460
|
+
this._overlayRef.dispose();
|
|
3461
|
+
}
|
|
3462
|
+
/** Fetches the position strategy object from the overlay ref. */
|
|
3463
|
+
_getPositionStrategy() {
|
|
3464
|
+
return this._overlayRef.getConfig()
|
|
3465
|
+
.positionStrategy;
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
/**
|
|
3469
|
+
* Closes the modal with the specified interaction type. This is currently not part of
|
|
3470
|
+
* `BaoModalRef` as that would conflict with custom modal ref mocks provided in tests.
|
|
3471
|
+
* More details. See: https://github.com/angular/components/pull/9257#issuecomment-651342226.
|
|
3472
|
+
*/
|
|
3473
|
+
function _closeModalVia(ref, interactionType, result) {
|
|
3474
|
+
// Some mock modal ref instances in tests do not have the `_containerInstance` property.
|
|
3475
|
+
// For those, we keep the behavior as is and do not deal with the interaction type.
|
|
3476
|
+
if (ref._containerInstance !== undefined) {
|
|
3477
|
+
ref._containerInstance._closeInteractionType = interactionType;
|
|
3478
|
+
}
|
|
3479
|
+
return ref.close(result);
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
/*
|
|
3483
|
+
* Copyright (c) 2022 Ville de Montreal. All rights reserved.
|
|
3484
|
+
* Licensed under the MIT license.
|
|
3485
|
+
* See LICENSE file in the project root for full license information.
|
|
3486
|
+
*/
|
|
3487
|
+
/** Injection token that can be used to access the data that was passed in to a modal. */
|
|
3488
|
+
const BAO_MODAL_DATA = new InjectionToken('BaoModalData');
|
|
3489
|
+
class BaoModalBase {
|
|
3490
|
+
constructor(_overlay, _injector, _parentModal, _overlayContainer, _modalRefConstructor, _modalContainerType, _modalDataToken, _animationMode) {
|
|
3491
|
+
this._overlay = _overlay;
|
|
3492
|
+
this._injector = _injector;
|
|
3493
|
+
this._parentModal = _parentModal;
|
|
3494
|
+
this._overlayContainer = _overlayContainer;
|
|
3495
|
+
this._modalRefConstructor = _modalRefConstructor;
|
|
3496
|
+
this._modalContainerType = _modalContainerType;
|
|
3497
|
+
this._modalDataToken = _modalDataToken;
|
|
3498
|
+
this._animationMode = _animationMode;
|
|
3499
|
+
this.afterAllClosed = defer(() => this.openModals.length
|
|
3500
|
+
? this.getAfterAllClosed()
|
|
3501
|
+
: this.getAfterAllClosed().pipe(startWith(undefined)));
|
|
3502
|
+
this._openModalsAtThisLevel = [];
|
|
3503
|
+
this._afterAllClosedAtThisLevel = new Subject();
|
|
3504
|
+
this._afterOpenedAtThisLevel = new Subject();
|
|
3505
|
+
this._ariaHiddenElements = new Map();
|
|
3506
|
+
this._modalAnimatingOpen = false;
|
|
3507
|
+
}
|
|
3508
|
+
/** Keeps track of the currently-open modals. */
|
|
3509
|
+
get openModals() {
|
|
3510
|
+
return this._parentModal
|
|
3511
|
+
? this._parentModal.openModals
|
|
3512
|
+
: this._openModalsAtThisLevel;
|
|
3513
|
+
}
|
|
3514
|
+
/** Stream that emits when a modal has been opened. */
|
|
3515
|
+
get afterOpened() {
|
|
3516
|
+
// Maybe typescript version difference
|
|
3517
|
+
return this._parentModal
|
|
3518
|
+
? this._parentModal.afterOpened
|
|
3519
|
+
: this._afterOpenedAtThisLevel;
|
|
3520
|
+
}
|
|
3521
|
+
getAfterAllClosed() {
|
|
3522
|
+
const parent = this._parentModal;
|
|
3523
|
+
return (parent ? parent.getAfterAllClosed() : this._afterAllClosedAtThisLevel);
|
|
3524
|
+
}
|
|
3525
|
+
/**
|
|
3526
|
+
* Opens a modal modal containing the given template.
|
|
3527
|
+
*/
|
|
3528
|
+
open(componentOrTemplateRef, config) {
|
|
3529
|
+
const conf = this._applyConfigDefaults(config, new BaoModalInitialConfig());
|
|
3530
|
+
// If there is a modal that is currently animating open, return the MatmodalRef of that modal
|
|
3531
|
+
if (this._modalAnimatingOpen) {
|
|
3532
|
+
return this._lastModalRef;
|
|
3533
|
+
}
|
|
3534
|
+
const overlayRef = this._createOverlay(conf);
|
|
3535
|
+
const modalContainer = this._attachModalContainer(overlayRef, conf);
|
|
3536
|
+
if (this._animationMode !== 'NoopAnimations') {
|
|
3537
|
+
const animationStateSubscription = modalContainer._animationStateChanged.subscribe(modalAnimationEvent => {
|
|
3538
|
+
if (modalAnimationEvent.state === 'opening') {
|
|
3539
|
+
this._modalAnimatingOpen = true;
|
|
3540
|
+
}
|
|
3541
|
+
if (modalAnimationEvent.state === 'opened') {
|
|
3542
|
+
this._modalAnimatingOpen = false;
|
|
3543
|
+
animationStateSubscription.unsubscribe();
|
|
3544
|
+
}
|
|
3545
|
+
});
|
|
3546
|
+
if (!this._animationStateSubscriptions) {
|
|
3547
|
+
this._animationStateSubscriptions = new Subscription();
|
|
3548
|
+
}
|
|
3549
|
+
this._animationStateSubscriptions.add(animationStateSubscription);
|
|
3550
|
+
}
|
|
3551
|
+
const modalRef = this._attachModalContent(componentOrTemplateRef, modalContainer, overlayRef, conf);
|
|
3552
|
+
this._lastModalRef = modalRef;
|
|
3553
|
+
// If this is the first modal that we're opening, hide all the non-overlay content.
|
|
3554
|
+
if (!this.openModals.length) {
|
|
3555
|
+
this._hideNonModalContentFromAssistiveTechnology();
|
|
3556
|
+
}
|
|
3557
|
+
this.openModals.push(modalRef);
|
|
3558
|
+
modalRef.afterClosed().subscribe(() => this._removeOpenModal(modalRef));
|
|
3559
|
+
this.afterOpened.next(modalRef);
|
|
3560
|
+
// Notify the modal container that the content has been attached.
|
|
3561
|
+
modalContainer._initializeWithAttachedContent();
|
|
3562
|
+
return modalRef;
|
|
3563
|
+
}
|
|
3564
|
+
/**
|
|
3565
|
+
* Closes all of the currently-open modals.
|
|
3566
|
+
*/
|
|
3567
|
+
closeAll() {
|
|
3568
|
+
this._closeModals(this.openModals);
|
|
3569
|
+
}
|
|
3570
|
+
/**
|
|
3571
|
+
* Finds an open modal by its id.
|
|
3572
|
+
*/
|
|
3573
|
+
getModalById(id) {
|
|
3574
|
+
return this.openModals.find(modal => modal.id === id);
|
|
3575
|
+
}
|
|
3576
|
+
ngOnDestroy() {
|
|
3577
|
+
// Only close the modals at this level on destroy
|
|
3578
|
+
// since the parent service may still be active.
|
|
3579
|
+
this._closeModals(this._openModalsAtThisLevel);
|
|
3580
|
+
this._afterAllClosedAtThisLevel.complete();
|
|
3581
|
+
this._afterOpenedAtThisLevel.complete();
|
|
3582
|
+
// Clean up any subscriptions to modals that never finished opening.
|
|
3583
|
+
if (this._animationStateSubscriptions) {
|
|
3584
|
+
this._animationStateSubscriptions.unsubscribe();
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
/**
|
|
3588
|
+
* Creates the overlay into which the modal will be loaded.
|
|
3589
|
+
*/
|
|
3590
|
+
_createOverlay(config) {
|
|
3591
|
+
const overlayConfig = this._getOverlayConfig(config);
|
|
3592
|
+
return this._overlay.create(overlayConfig);
|
|
3593
|
+
}
|
|
3594
|
+
/**
|
|
3595
|
+
* Creates an overlay config from a modal config.
|
|
3596
|
+
*/
|
|
3597
|
+
_getOverlayConfig(config) {
|
|
3598
|
+
const state = new OverlayConfig({
|
|
3599
|
+
positionStrategy: this._overlay.position().global(),
|
|
3600
|
+
scrollStrategy: this._overlay.scrollStrategies.block(),
|
|
3601
|
+
panelClass: config.panelClass,
|
|
3602
|
+
hasBackdrop: config.hasBackdrop,
|
|
3603
|
+
disposeOnNavigation: config.closeOnNavigation
|
|
3604
|
+
});
|
|
3605
|
+
if (config.backdropClass) {
|
|
3606
|
+
state.backdropClass = config.backdropClass;
|
|
3607
|
+
}
|
|
3608
|
+
return state;
|
|
3609
|
+
}
|
|
3610
|
+
/**
|
|
3611
|
+
* Attaches a modal container to a modal's already-created overlay.
|
|
3612
|
+
*/
|
|
3613
|
+
_attachModalContainer(overlay, config) {
|
|
3614
|
+
const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector;
|
|
3615
|
+
const injector = Injector.create({
|
|
3616
|
+
parent: userInjector || this._injector,
|
|
3617
|
+
providers: [{ provide: BaoModalInitialConfig, useValue: config }]
|
|
3618
|
+
});
|
|
3619
|
+
const containerPortal = new ComponentPortal(this._modalContainerType, config.viewContainerRef, injector);
|
|
3620
|
+
const containerRef = overlay.attach(containerPortal);
|
|
3621
|
+
return containerRef.instance;
|
|
3622
|
+
}
|
|
3623
|
+
/**
|
|
3624
|
+
* Attaches the user-provided component to the already-created modal container.
|
|
3625
|
+
*/
|
|
3626
|
+
_attachModalContent(componentOrTemplateRef, modalContainer, overlayRef, config) {
|
|
3627
|
+
// Create a reference to the modal we're creating in order to give the user a handle
|
|
3628
|
+
// to modify and close it.
|
|
3629
|
+
const modalRef = new this._modalRefConstructor(overlayRef, modalContainer, config.id);
|
|
3630
|
+
if (componentOrTemplateRef instanceof TemplateRef) {
|
|
3631
|
+
modalContainer.attachTemplatePortal(new TemplatePortal(componentOrTemplateRef, null, {
|
|
3632
|
+
$implicit: config.data,
|
|
3633
|
+
modalRef
|
|
3634
|
+
}));
|
|
3635
|
+
}
|
|
3636
|
+
else {
|
|
3637
|
+
const injector = this._createInjector(config, modalRef, modalContainer);
|
|
3638
|
+
const contentRef = modalContainer.attachComponentPortal(new ComponentPortal(componentOrTemplateRef, config.viewContainerRef, injector));
|
|
3639
|
+
modalRef.componentInstance = contentRef.instance;
|
|
3640
|
+
}
|
|
3641
|
+
modalRef
|
|
3642
|
+
.updateSize(config.width, config.height)
|
|
3643
|
+
.updatePosition(config.position);
|
|
3644
|
+
return modalRef;
|
|
3645
|
+
}
|
|
3646
|
+
/**
|
|
3647
|
+
* Creates a custom injector to be used inside the modal. This allows a component loaded inside
|
|
3648
|
+
* of a modal to close itself and, optionally, to return a value.
|
|
3649
|
+
*/
|
|
3650
|
+
_createInjector(config, modalRef, modalContainer) {
|
|
3651
|
+
const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector;
|
|
3652
|
+
// The modal container should be provided as the modal container and the modal's
|
|
3653
|
+
// content are created out of the same `ViewContainerRef` and as such, are siblings
|
|
3654
|
+
// for injector purposes. To allow the hierarchy that is expected, the modal
|
|
3655
|
+
// container is explicitly provided in the injector.
|
|
3656
|
+
const providers = [
|
|
3657
|
+
{ provide: this._modalContainerType, useValue: modalContainer },
|
|
3658
|
+
{ provide: this._modalDataToken, useValue: config.data },
|
|
3659
|
+
{ provide: this._modalRefConstructor, useValue: modalRef }
|
|
3660
|
+
];
|
|
3661
|
+
return Injector.create({
|
|
3662
|
+
parent: userInjector || this._injector,
|
|
3663
|
+
providers
|
|
3664
|
+
});
|
|
3665
|
+
}
|
|
3666
|
+
/**
|
|
3667
|
+
* Removes a modal from the array of open modals.
|
|
3668
|
+
*/
|
|
3669
|
+
_removeOpenModal(modalRef) {
|
|
3670
|
+
const index = this.openModals.indexOf(modalRef);
|
|
3671
|
+
if (index > -1) {
|
|
3672
|
+
this.openModals.splice(index, 1);
|
|
3673
|
+
// If all the modals were closed, remove/restore the `aria-hidden`
|
|
3674
|
+
// to a the siblings and emit to the `afterAllClosed` stream.
|
|
3675
|
+
if (!this.openModals.length) {
|
|
3676
|
+
this._ariaHiddenElements.forEach((previousValue, element) => {
|
|
3677
|
+
if (previousValue) {
|
|
3678
|
+
element.setAttribute('aria-hidden', previousValue);
|
|
3679
|
+
}
|
|
3680
|
+
else {
|
|
3681
|
+
element.removeAttribute('aria-hidden');
|
|
3682
|
+
}
|
|
3683
|
+
});
|
|
3684
|
+
this._ariaHiddenElements.clear();
|
|
3685
|
+
this.getAfterAllClosed().next();
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
/**
|
|
3690
|
+
* Hides all of the content that isn't an overlay from assistive technology.
|
|
3691
|
+
*/
|
|
3692
|
+
_hideNonModalContentFromAssistiveTechnology() {
|
|
3693
|
+
const overlayContainer = this._overlayContainer.getContainerElement();
|
|
3694
|
+
// Ensure that the overlay container is attached to the DOM.
|
|
3695
|
+
if (overlayContainer.parentElement) {
|
|
3696
|
+
const siblings = overlayContainer.parentElement.children;
|
|
3697
|
+
for (let i = siblings.length - 1; i > -1; i--) {
|
|
3698
|
+
const sibling = siblings[i];
|
|
3699
|
+
if (sibling !== overlayContainer &&
|
|
3700
|
+
sibling.nodeName !== 'SCRIPT' &&
|
|
3701
|
+
sibling.nodeName !== 'STYLE' &&
|
|
3702
|
+
!sibling.hasAttribute('aria-live')) {
|
|
3703
|
+
this._ariaHiddenElements.set(sibling, sibling.getAttribute('aria-hidden'));
|
|
3704
|
+
sibling.setAttribute('aria-hidden', 'true');
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
/** Closes all of the modals in an array. */
|
|
3710
|
+
_closeModals(modals) {
|
|
3711
|
+
let i = modals.length;
|
|
3712
|
+
while (i--) {
|
|
3713
|
+
modals[i].close();
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
/**
|
|
3717
|
+
* Applies default options to the modal config.
|
|
3718
|
+
*/
|
|
3719
|
+
_applyConfigDefaults(config, defaultOptions) {
|
|
3720
|
+
const desktopClass = (config === null || config === void 0 ? void 0 : config.size) || eModalDesktopWidthSize.SMALL;
|
|
3721
|
+
const mobilClass = (config === null || config === void 0 ? void 0 : config.mobileSize) || eModalMobileWidthSize.FULL;
|
|
3722
|
+
const data = (config === null || config === void 0 ? void 0 : config.data) || null;
|
|
3723
|
+
const ariaLabelledBy = (config === null || config === void 0 ? void 0 : config.ariaLabelledBy) || null;
|
|
3724
|
+
return Object.assign(Object.assign({}, defaultOptions), {
|
|
3725
|
+
panelClass: [desktopClass, mobilClass],
|
|
3726
|
+
ariaLabelledBy,
|
|
3727
|
+
data
|
|
3728
|
+
});
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
BaoModalBase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModalBase, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
|
|
3732
|
+
BaoModalBase.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.1.1", type: BaoModalBase, ngImport: i0 });
|
|
3733
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModalBase, decorators: [{
|
|
3734
|
+
type: Directive
|
|
3735
|
+
}], ctorParameters: function () { return [{ type: i1$4.Overlay }, { type: i0.Injector }, { type: undefined }, { type: i1$4.OverlayContainer }, { type: i0.Type }, { type: i0.Type }, { type: i0.InjectionToken }, { type: undefined }]; } });
|
|
3736
|
+
/**
|
|
3737
|
+
* Service to open modal.
|
|
3738
|
+
*/
|
|
3739
|
+
class BaoModal extends BaoModalBase {
|
|
3740
|
+
constructor(overlay, injector, parentModal, overlayContainer, animationMode) {
|
|
3741
|
+
super(overlay, injector, parentModal, overlayContainer, BaoModalRef, BaoModalContainer, BAO_MODAL_DATA, animationMode);
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
BaoModal.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModal, deps: [{ token: i1$4.Overlay }, { token: i0.Injector }, { token: BaoModal, optional: true, skipSelf: true }, { token: i1$4.OverlayContainer }, { token: ANIMATION_MODULE_TYPE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3745
|
+
BaoModal.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModal });
|
|
3746
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModal, decorators: [{
|
|
3747
|
+
type: Injectable
|
|
3748
|
+
}], ctorParameters: function () {
|
|
3749
|
+
return [{ type: i1$4.Overlay }, { type: i0.Injector }, { type: BaoModal, decorators: [{
|
|
3750
|
+
type: Optional
|
|
3751
|
+
}, {
|
|
3752
|
+
type: SkipSelf
|
|
3753
|
+
}] }, { type: i1$4.OverlayContainer }, { type: undefined, decorators: [{
|
|
3754
|
+
type: Optional
|
|
3755
|
+
}, {
|
|
3756
|
+
type: Inject,
|
|
3757
|
+
args: [ANIMATION_MODULE_TYPE]
|
|
3758
|
+
}] }];
|
|
3759
|
+
} });
|
|
3760
|
+
|
|
3761
|
+
/*
|
|
3762
|
+
* Copyright (c) 2022 Ville de Montreal. All rights reserved.
|
|
3763
|
+
* Licensed under the MIT license.
|
|
3764
|
+
* See LICENSE file in the project root for full license information.
|
|
3765
|
+
*/
|
|
3766
|
+
/**
|
|
3767
|
+
* Button that will close the current dialog.
|
|
3768
|
+
*/
|
|
3769
|
+
class BaoModalClose {
|
|
3770
|
+
constructor(modalRef, _elementRef, _dialog) {
|
|
3771
|
+
this.modalRef = modalRef;
|
|
3772
|
+
this._elementRef = _elementRef;
|
|
3773
|
+
this._dialog = _dialog;
|
|
3774
|
+
/** Default to "button" to prevents accidental form submits. */
|
|
3775
|
+
this.type = 'button';
|
|
3776
|
+
}
|
|
3777
|
+
ngOnInit() {
|
|
3778
|
+
if (!this.modalRef) {
|
|
3779
|
+
// When this directive is included in a dialog via TemplateRef (rather than being
|
|
3780
|
+
// in a Component), the modalRef isn't available via injection because embedded
|
|
3781
|
+
// views cannot be given a custom injector. Instead, we look up the modalRef by
|
|
3782
|
+
// ID. This must occur in `onInit`, as the ID binding for the dialog container won't
|
|
3783
|
+
// be resolved at constructor time.
|
|
3784
|
+
this.modalRef =
|
|
3785
|
+
getClosestDialog(this._elementRef, this._dialog.openModals) || null;
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
ngOnChanges(changes) {
|
|
3789
|
+
const proxiedChange = changes['_baoModalClose'] || changes['_baoModalCloseResult'];
|
|
3790
|
+
if (proxiedChange) {
|
|
3791
|
+
this.dialogResult = proxiedChange.currentValue;
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
_onButtonClick(event) {
|
|
3795
|
+
// Determinate the focus origin using the click event, because using the FocusMonitor will
|
|
3796
|
+
// result in incorrect origins. Most of the time, close buttons will be auto focused in the
|
|
3797
|
+
// dialog, and therefore clicking the button won't result in a focus change. This means that
|
|
3798
|
+
// the FocusMonitor won't detect any origin change, and will always output `program`.
|
|
3799
|
+
_closeModalVia(this.modalRef, event.screenX === 0 && event.screenY === 0 ? 'keyboard' : 'mouse', this.dialogResult);
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
BaoModalClose.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModalClose, deps: [{ token: BaoModalRef, optional: true }, { token: i0.ElementRef }, { token: BaoModal }], target: i0.ɵɵFactoryTarget.Directive });
|
|
3803
|
+
BaoModalClose.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.1.1", type: BaoModalClose, selector: "[bao-modal-close], [baoModalClose]", inputs: { ariaLabel: ["aria-label", "ariaLabel"], type: "type", dialogResult: ["bao-modal-close", "dialogResult"], _baoModalClose: ["baoModalClose", "_baoModalClose"] }, host: { listeners: { "click": "_onButtonClick($event)" }, properties: { "attr.aria-label": "ariaLabel || null", "attr.type": "type" } }, exportAs: ["BaoModalClose"], usesOnChanges: true, ngImport: i0 });
|
|
3804
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModalClose, decorators: [{
|
|
3805
|
+
type: Directive,
|
|
3806
|
+
args: [{
|
|
3807
|
+
selector: '[bao-modal-close], [baoModalClose]',
|
|
3808
|
+
exportAs: 'BaoModalClose',
|
|
3809
|
+
host: {
|
|
3810
|
+
'(click)': '_onButtonClick($event)',
|
|
3811
|
+
'[attr.aria-label]': 'ariaLabel || null',
|
|
3812
|
+
'[attr.type]': 'type'
|
|
3813
|
+
}
|
|
3814
|
+
}]
|
|
3815
|
+
}], ctorParameters: function () {
|
|
3816
|
+
return [{ type: BaoModalRef, decorators: [{
|
|
3817
|
+
type: Optional
|
|
3818
|
+
}] }, { type: i0.ElementRef }, { type: BaoModal }];
|
|
3819
|
+
}, propDecorators: { ariaLabel: [{
|
|
3820
|
+
type: Input,
|
|
3821
|
+
args: ['aria-label']
|
|
3822
|
+
}], type: [{
|
|
3823
|
+
type: Input
|
|
3824
|
+
}], dialogResult: [{
|
|
3825
|
+
type: Input,
|
|
3826
|
+
args: ['bao-modal-close']
|
|
3827
|
+
}], _baoModalClose: [{
|
|
3828
|
+
type: Input,
|
|
3829
|
+
args: ['baoModalClose']
|
|
3830
|
+
}] } });
|
|
3831
|
+
/**
|
|
3832
|
+
* Finds the closest BaoModalRef to an element by looking at the DOM.
|
|
3833
|
+
*/
|
|
3834
|
+
function getClosestDialog(element, openDialogs) {
|
|
3835
|
+
let parent = element.nativeElement.parentElement;
|
|
3836
|
+
while (parent && !parent.classList.contains('bao-modal-container')) {
|
|
3837
|
+
parent = parent.parentElement;
|
|
3838
|
+
}
|
|
3839
|
+
return parent ? openDialogs.find(dialog => dialog.id === parent.id) : null;
|
|
3840
|
+
}
|
|
3841
|
+
|
|
3842
|
+
/*
|
|
3843
|
+
* Copyright (c) 2022 Ville de Montreal. All rights reserved.
|
|
3844
|
+
* Licensed under the MIT license.
|
|
3845
|
+
* See LICENSE file in the project root for full license information.
|
|
3846
|
+
*/
|
|
3847
|
+
const MODAL_DIRECTIVES = [BaoModalContainer, BaoModalClose];
|
|
3848
|
+
class BaoModalModule {
|
|
3849
|
+
}
|
|
3850
|
+
BaoModalModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModalModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
3851
|
+
BaoModalModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModalModule, declarations: [BaoModalContainer, BaoModalClose], imports: [CommonModule,
|
|
3852
|
+
OverlayModule,
|
|
3853
|
+
PortalModule,
|
|
3854
|
+
BrowserAnimationsModule,
|
|
3855
|
+
NoopAnimationsModule], exports: [BaoModalContainer, BaoModalClose] });
|
|
3856
|
+
BaoModalModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModalModule, providers: [BaoModal], imports: [[
|
|
3857
|
+
CommonModule,
|
|
3858
|
+
OverlayModule,
|
|
3859
|
+
PortalModule,
|
|
3860
|
+
BrowserAnimationsModule,
|
|
3861
|
+
NoopAnimationsModule
|
|
3862
|
+
]] });
|
|
3863
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: BaoModalModule, decorators: [{
|
|
3864
|
+
type: NgModule,
|
|
3865
|
+
args: [{
|
|
3866
|
+
imports: [
|
|
3867
|
+
CommonModule,
|
|
3868
|
+
OverlayModule,
|
|
3869
|
+
PortalModule,
|
|
3870
|
+
BrowserAnimationsModule,
|
|
3871
|
+
NoopAnimationsModule
|
|
3872
|
+
],
|
|
3873
|
+
declarations: MODAL_DIRECTIVES,
|
|
3874
|
+
exports: MODAL_DIRECTIVES,
|
|
3875
|
+
providers: [BaoModal]
|
|
3876
|
+
}]
|
|
3877
|
+
}] });
|
|
3878
|
+
|
|
2939
3879
|
/*
|
|
2940
3880
|
* Copyright (c) 2022 Ville de Montreal. All rights reserved.
|
|
2941
3881
|
* Licensed under the MIT license.
|
|
@@ -2961,7 +3901,8 @@ BaoModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.
|
|
|
2961
3901
|
BaoRadioModule,
|
|
2962
3902
|
BaoSummaryModule,
|
|
2963
3903
|
BaoAvatarModule,
|
|
2964
|
-
BaoTabsModule
|
|
3904
|
+
BaoTabsModule,
|
|
3905
|
+
BaoModalModule
|
|
2965
3906
|
// TODO: reactivate once component does not depend on global css BaoBadgeModule,
|
|
2966
3907
|
// TODO: reactivate once component does not depend on global css BaoSnackBarModule,
|
|
2967
3908
|
] });
|
|
@@ -2984,7 +3925,8 @@ BaoModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.
|
|
|
2984
3925
|
BaoRadioModule,
|
|
2985
3926
|
BaoSummaryModule,
|
|
2986
3927
|
BaoAvatarModule,
|
|
2987
|
-
BaoTabsModule
|
|
3928
|
+
BaoTabsModule,
|
|
3929
|
+
BaoModalModule
|
|
2988
3930
|
// TODO: reactivate once component does not depend on global css BaoBadgeModule,
|
|
2989
3931
|
// TODO: reactivate once component does not depend on global css BaoSnackBarModule,
|
|
2990
3932
|
] });
|
|
@@ -3012,7 +3954,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImpor
|
|
|
3012
3954
|
BaoRadioModule,
|
|
3013
3955
|
BaoSummaryModule,
|
|
3014
3956
|
BaoAvatarModule,
|
|
3015
|
-
BaoTabsModule
|
|
3957
|
+
BaoTabsModule,
|
|
3958
|
+
BaoModalModule
|
|
3016
3959
|
// TODO: reactivate once component does not depend on global css BaoBadgeModule,
|
|
3017
3960
|
// TODO: reactivate once component does not depend on global css BaoSnackBarModule,
|
|
3018
3961
|
]
|
|
@@ -3082,9 +4025,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImpor
|
|
|
3082
4025
|
* See LICENSE file in the project root for full license information.
|
|
3083
4026
|
*/
|
|
3084
4027
|
|
|
4028
|
+
/*
|
|
4029
|
+
* Copyright (c) 2022 Ville de Montreal. All rights reserved.
|
|
4030
|
+
* Licensed under the MIT license.
|
|
4031
|
+
* See LICENSE file in the project root for full license information.
|
|
4032
|
+
*/
|
|
4033
|
+
|
|
3085
4034
|
/**
|
|
3086
4035
|
* Generated bundle index. Do not edit.
|
|
3087
4036
|
*/
|
|
3088
4037
|
|
|
3089
|
-
export { BAO_RADIO_GROUP, BaoAlertActions, BaoAlertComponent, BaoAlertContent, BaoAlertLink, BaoAlertModule, BaoAlertTitle, BaoAvatarComponent, BaoAvatarContent, BaoAvatarModule, BaoBadgeComponent, BaoBadgeModule, BaoBreadcrumbComponent, BaoBreadcrumbModule, BaoButtonComponent, BaoButtonModule, BaoCardComponent, BaoCardContent, BaoCardHeader, BaoCardModule, BaoCardTextInterface, BaoCardTitle, BaoCheckBoxDescription, BaoCheckboxComponent, BaoCheckboxGroupComponent, BaoCheckboxModule, BaoCommonComponentsModule, BaoErrorTextComponent, BaoGuidingTextComponent, BaoHeaderInfoComponent, BaoHeaderInfoContent, BaoHeaderInfoModule, BaoHeaderInfoSubtitle, BaoHeaderInfoSurtitle, BaoHeaderInfoTitle, BaoHeaderInfoTitleGroupComponent, BaoIconComponent, BaoIconModule, BaoLabelTextComponent, BaoList, BaoListItem, BaoListItemDescription, BaoListItemTitle, BaoListModule, BaoListSummary, BaoListSummaryItem, BaoModule, BaoNavList, BaoRadioButtonComponent, BaoRadioButtonGroupComponent, BaoRadioDescription, BaoRadioModule, BaoSummaryComponent, BaoSummaryDescription, BaoSummaryModule, BaoTabHeader, BaoTabPanel, BaoTablistComponent, BaoTabsContainer, BaoTabsModule, BaoTagComponent, BaoTagModule, BaoTitleTextComponent };
|
|
4038
|
+
export { BAO_MODAL_DATA, BAO_RADIO_GROUP, BaoAlertActions, BaoAlertComponent, BaoAlertContent, BaoAlertLink, BaoAlertModule, BaoAlertTitle, BaoAvatarComponent, BaoAvatarContent, BaoAvatarModule, BaoBadgeComponent, BaoBadgeModule, BaoBreadcrumbComponent, BaoBreadcrumbModule, BaoButtonComponent, BaoButtonModule, BaoCardComponent, BaoCardContent, BaoCardHeader, BaoCardModule, BaoCardTextInterface, BaoCardTitle, BaoCheckBoxDescription, BaoCheckboxComponent, BaoCheckboxGroupComponent, BaoCheckboxModule, BaoCommonComponentsModule, BaoErrorTextComponent, BaoGuidingTextComponent, BaoHeaderInfoComponent, BaoHeaderInfoContent, BaoHeaderInfoModule, BaoHeaderInfoSubtitle, BaoHeaderInfoSurtitle, BaoHeaderInfoTitle, BaoHeaderInfoTitleGroupComponent, BaoIconComponent, BaoIconModule, BaoLabelTextComponent, BaoList, BaoListItem, BaoListItemDescription, BaoListItemTitle, BaoListModule, BaoListSummary, BaoListSummaryItem, BaoModal, BaoModalBase, BaoModalClose, BaoModalContainer, BaoModalInitialConfig, BaoModalModule, BaoModalRef, BaoModule, BaoNavList, BaoRadioButtonComponent, BaoRadioButtonGroupComponent, BaoRadioDescription, BaoRadioModule, BaoSummaryComponent, BaoSummaryDescription, BaoSummaryModule, BaoTabHeader, BaoTabPanel, BaoTablistComponent, BaoTabsContainer, BaoTabsModule, BaoTagComponent, BaoTagModule, BaoTitleTextComponent, _BaoModalContainerBase, _closeModalVia, eModalDesktopWidthSize, eModalMobileWidthSize, throwBaoModalContentAlreadyAttachedError };
|
|
3090
4039
|
//# sourceMappingURL=villedemontreal-angular-ui.mjs.map
|