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