ng-hub-ui-modal 1.0.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/LICENSE +21 -0
- package/README.md +343 -0
- package/esm2022/lib/modal-backdrop.mjs +51 -0
- package/esm2022/lib/modal-config.mjs +31 -0
- package/esm2022/lib/modal-dismiss-reasons.mjs +6 -0
- package/esm2022/lib/modal-ref.mjs +216 -0
- package/esm2022/lib/modal-stack.mjs +294 -0
- package/esm2022/lib/modal-window.mjs +275 -0
- package/esm2022/lib/modal.mjs +59 -0
- package/esm2022/ng-hub-ui-modal.mjs +5 -0
- package/esm2022/public-api.mjs +5 -0
- package/fesm2022/ng-hub-ui-modal.mjs +920 -0
- package/fesm2022/ng-hub-ui-modal.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/modal-backdrop.d.ts +13 -0
- package/lib/modal-config.d.ts +158 -0
- package/lib/modal-dismiss-reasons.d.ts +4 -0
- package/lib/modal-ref.d.ts +115 -0
- package/lib/modal-stack.d.ts +49 -0
- package/lib/modal-window.d.ts +39 -0
- package/lib/modal.d.ts +42 -0
- package/ng-hub-ui-modal-1.0.0.tgz +0 -0
- package/package.json +50 -0
- package/public-api.d.ts +1 -0
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, inject, ElementRef, NgZone, Component, ViewEncapsulation, Input, EventEmitter, ViewChild, Output, ApplicationRef, Injector, EnvironmentInjector, createComponent, TemplateRef } from '@angular/core';
|
|
3
|
+
import { DOCUMENT, NgIf } from '@angular/common';
|
|
4
|
+
import { hubRunTransition, reflow, isDefined, isPromise, isString, getFocusableBoundaryElements, ScrollBar, hubFocusTrap, ContentRef } from 'ng-hub-ui-utils';
|
|
5
|
+
import { Subject, of, zip, fromEvent } from 'rxjs';
|
|
6
|
+
import { take, takeUntil, filter, tap, switchMap } from 'rxjs/operators';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A configuration service for the [`HubModal`](#/components/modal/api#HubModal) service.
|
|
10
|
+
*
|
|
11
|
+
* You can inject this service, typically in your root component, and customize the values of its properties in
|
|
12
|
+
* order to provide default values for all modals used in the application.
|
|
13
|
+
*
|
|
14
|
+
* @since 3.1.0
|
|
15
|
+
*/
|
|
16
|
+
class HubModalConfig {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.backdrop = true;
|
|
19
|
+
this.fullscreen = false;
|
|
20
|
+
this.keyboard = true;
|
|
21
|
+
this.dismissSelector = '[data-dismiss="modal"]';
|
|
22
|
+
}
|
|
23
|
+
get animation() {
|
|
24
|
+
return this._animation ?? true /* this._hubConfig.animation */;
|
|
25
|
+
}
|
|
26
|
+
set animation(animation) {
|
|
27
|
+
this._animation = animation;
|
|
28
|
+
}
|
|
29
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModalConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
30
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModalConfig, providedIn: 'root' }); }
|
|
31
|
+
}
|
|
32
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModalConfig, decorators: [{
|
|
33
|
+
type: Injectable,
|
|
34
|
+
args: [{ providedIn: 'root' }]
|
|
35
|
+
}] });
|
|
36
|
+
|
|
37
|
+
class HubModalBackdrop {
|
|
38
|
+
constructor() {
|
|
39
|
+
this._nativeElement = inject(ElementRef).nativeElement;
|
|
40
|
+
this._zone = inject(NgZone);
|
|
41
|
+
}
|
|
42
|
+
ngOnInit() {
|
|
43
|
+
this._zone.onStable
|
|
44
|
+
.asObservable()
|
|
45
|
+
.pipe(take(1))
|
|
46
|
+
.subscribe(() => {
|
|
47
|
+
hubRunTransition(this._zone, this._nativeElement, (element, animation) => {
|
|
48
|
+
if (animation) {
|
|
49
|
+
reflow(element);
|
|
50
|
+
}
|
|
51
|
+
element.classList.add('show');
|
|
52
|
+
}, { animation: this.animation, runningTransition: 'continue' });
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
hide() {
|
|
56
|
+
return hubRunTransition(this._zone, this._nativeElement, ({ classList }) => classList.remove('show'), {
|
|
57
|
+
animation: this.animation,
|
|
58
|
+
runningTransition: 'stop'
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModalBackdrop, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
62
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.5", type: HubModalBackdrop, isStandalone: true, selector: "hub-modal-backdrop", inputs: { animation: "animation", backdropClass: "backdropClass" }, host: { properties: { "class": "\"modal-backdrop\" + (backdropClass ? \" \" + backdropClass : \"\")", "class.show": "!animation", "class.fade": "animation" }, styleAttribute: "z-index: 1055" }, ngImport: i0, template: '', isInline: true, encapsulation: i0.ViewEncapsulation.None }); }
|
|
63
|
+
}
|
|
64
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModalBackdrop, decorators: [{
|
|
65
|
+
type: Component,
|
|
66
|
+
args: [{
|
|
67
|
+
selector: 'hub-modal-backdrop',
|
|
68
|
+
standalone: true,
|
|
69
|
+
encapsulation: ViewEncapsulation.None,
|
|
70
|
+
template: '',
|
|
71
|
+
host: {
|
|
72
|
+
'[class]': '"modal-backdrop" + (backdropClass ? " " + backdropClass : "")',
|
|
73
|
+
'[class.show]': '!animation',
|
|
74
|
+
'[class.fade]': 'animation',
|
|
75
|
+
style: 'z-index: 1055'
|
|
76
|
+
}
|
|
77
|
+
}]
|
|
78
|
+
}], propDecorators: { animation: [{
|
|
79
|
+
type: Input
|
|
80
|
+
}], backdropClass: [{
|
|
81
|
+
type: Input
|
|
82
|
+
}] } });
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A reference to the currently opened (active) modal.
|
|
86
|
+
*
|
|
87
|
+
* Instances of this class can be injected into your component passed as modal content.
|
|
88
|
+
* So you can `.update()`, `.close()` or `.dismiss()` the modal window from your component.
|
|
89
|
+
*/
|
|
90
|
+
class HubActiveModal {
|
|
91
|
+
/**
|
|
92
|
+
* Updates options of an opened modal.
|
|
93
|
+
*
|
|
94
|
+
* @since 14.2.0
|
|
95
|
+
*/
|
|
96
|
+
update(options) { }
|
|
97
|
+
/**
|
|
98
|
+
* Closes the modal with an optional `result` value.
|
|
99
|
+
*
|
|
100
|
+
* The `HubModalRef.result` promise will be resolved with the provided value.
|
|
101
|
+
*/
|
|
102
|
+
close(result) { }
|
|
103
|
+
/**
|
|
104
|
+
* Dismisses the modal with an optional `reason` value.
|
|
105
|
+
*
|
|
106
|
+
* The `HubModalRef.result` promise will be rejected with the provided value.
|
|
107
|
+
*/
|
|
108
|
+
dismiss(reason) { }
|
|
109
|
+
}
|
|
110
|
+
const WINDOW_ATTRIBUTES = [
|
|
111
|
+
'animation',
|
|
112
|
+
'ariaLabelledBy',
|
|
113
|
+
'ariaDescribedBy',
|
|
114
|
+
'backdrop',
|
|
115
|
+
'centered',
|
|
116
|
+
'fullscreen',
|
|
117
|
+
'keyboard',
|
|
118
|
+
'scrollable',
|
|
119
|
+
'size',
|
|
120
|
+
'windowClass',
|
|
121
|
+
'modalDialogClass'
|
|
122
|
+
];
|
|
123
|
+
const BACKDROP_ATTRIBUTES = ['animation', 'backdropClass'];
|
|
124
|
+
/**
|
|
125
|
+
* A reference to the newly opened modal returned by the `HubModal.open()` method.
|
|
126
|
+
*/
|
|
127
|
+
class HubModalRef {
|
|
128
|
+
_applyWindowOptions(windowInstance, options) {
|
|
129
|
+
WINDOW_ATTRIBUTES.forEach((optionName) => {
|
|
130
|
+
if (isDefined(options[optionName])) {
|
|
131
|
+
windowInstance[optionName] = options[optionName];
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
_applyBackdropOptions(backdropInstance, options) {
|
|
136
|
+
BACKDROP_ATTRIBUTES.forEach((optionName) => {
|
|
137
|
+
if (isDefined(options[optionName])) {
|
|
138
|
+
backdropInstance[optionName] = options[optionName];
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Updates options of an opened modal.
|
|
144
|
+
*
|
|
145
|
+
* @since 14.2.0
|
|
146
|
+
*/
|
|
147
|
+
update(options) {
|
|
148
|
+
this._applyWindowOptions(this._windowCmptRef.instance, options);
|
|
149
|
+
if (this._backdropCmptRef && this._backdropCmptRef.instance) {
|
|
150
|
+
this._applyBackdropOptions(this._backdropCmptRef.instance, options);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The instance of a component used for the modal content.
|
|
155
|
+
*
|
|
156
|
+
* When a `TemplateRef` is used as the content or when the modal is closed, will return `undefined`.
|
|
157
|
+
*/
|
|
158
|
+
get componentInstance() {
|
|
159
|
+
if (this._contentRef && this._contentRef.componentRef) {
|
|
160
|
+
return this._contentRef.componentRef.instance;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The observable that emits when the modal is closed via the `.close()` method.
|
|
165
|
+
*
|
|
166
|
+
* It will emit the result passed to the `.close()` method.
|
|
167
|
+
*
|
|
168
|
+
* @since 8.0.0
|
|
169
|
+
*/
|
|
170
|
+
get closed() {
|
|
171
|
+
return this._closed.asObservable().pipe(takeUntil(this._hidden));
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The observable that emits when the modal is dismissed via the `.dismiss()` method.
|
|
175
|
+
*
|
|
176
|
+
* It will emit the reason passed to the `.dismissed()` method by the user, or one of the internal
|
|
177
|
+
* reasons like backdrop click or ESC key press.
|
|
178
|
+
*
|
|
179
|
+
* @since 8.0.0
|
|
180
|
+
*/
|
|
181
|
+
get dismissed() {
|
|
182
|
+
return this._dismissed.asObservable().pipe(takeUntil(this._hidden));
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The observable that emits when both modal window and backdrop are closed and animations were finished.
|
|
186
|
+
* At this point modal and backdrop elements will be removed from the DOM tree.
|
|
187
|
+
*
|
|
188
|
+
* This observable will be completed after emitting.
|
|
189
|
+
*
|
|
190
|
+
* @since 8.0.0
|
|
191
|
+
*/
|
|
192
|
+
get hidden() {
|
|
193
|
+
return this._hidden.asObservable();
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* The observable that emits when modal is fully visible and animation was finished.
|
|
197
|
+
* Modal DOM element is always available synchronously after calling 'modal.open()' service.
|
|
198
|
+
*
|
|
199
|
+
* This observable will be completed after emitting.
|
|
200
|
+
* It will not emit, if modal is closed before open animation is finished.
|
|
201
|
+
*
|
|
202
|
+
* @since 8.0.0
|
|
203
|
+
*/
|
|
204
|
+
get shown() {
|
|
205
|
+
return this._windowCmptRef.instance.shown.asObservable();
|
|
206
|
+
}
|
|
207
|
+
constructor(_windowCmptRef, _contentRef, _backdropCmptRef, _beforeDismiss) {
|
|
208
|
+
this._windowCmptRef = _windowCmptRef;
|
|
209
|
+
this._contentRef = _contentRef;
|
|
210
|
+
this._backdropCmptRef = _backdropCmptRef;
|
|
211
|
+
this._beforeDismiss = _beforeDismiss;
|
|
212
|
+
this._closed = new Subject();
|
|
213
|
+
this._dismissed = new Subject();
|
|
214
|
+
this._hidden = new Subject();
|
|
215
|
+
_windowCmptRef.instance.dismissEvent.subscribe((reason) => {
|
|
216
|
+
this.dismiss(reason);
|
|
217
|
+
});
|
|
218
|
+
this.result = new Promise((resolve, reject) => {
|
|
219
|
+
this._resolve = resolve;
|
|
220
|
+
this._reject = reject;
|
|
221
|
+
});
|
|
222
|
+
this.result.then(null, () => { });
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Closes the modal with an optional `result` value.
|
|
226
|
+
*
|
|
227
|
+
* The `HubMobalRef.result` promise will be resolved with the provided value.
|
|
228
|
+
*/
|
|
229
|
+
close(result) {
|
|
230
|
+
if (this._windowCmptRef) {
|
|
231
|
+
this._closed.next(result);
|
|
232
|
+
this._resolve(result);
|
|
233
|
+
this._removeModalElements();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
_dismiss(reason) {
|
|
237
|
+
this._dismissed.next(reason);
|
|
238
|
+
this._reject(reason);
|
|
239
|
+
this._removeModalElements();
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Dismisses the modal with an optional `reason` value.
|
|
243
|
+
*
|
|
244
|
+
* The `HubModalRef.result` promise will be rejected with the provided value.
|
|
245
|
+
*/
|
|
246
|
+
dismiss(reason) {
|
|
247
|
+
if (this._windowCmptRef) {
|
|
248
|
+
if (!this._beforeDismiss) {
|
|
249
|
+
this._dismiss(reason);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
const dismiss = this._beforeDismiss();
|
|
253
|
+
if (isPromise(dismiss)) {
|
|
254
|
+
dismiss.then((result) => {
|
|
255
|
+
if (result !== false) {
|
|
256
|
+
this._dismiss(reason);
|
|
257
|
+
}
|
|
258
|
+
}, () => { });
|
|
259
|
+
}
|
|
260
|
+
else if (dismiss !== false) {
|
|
261
|
+
this._dismiss(reason);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
_removeModalElements() {
|
|
267
|
+
const windowTransition$ = this._windowCmptRef.instance.hide();
|
|
268
|
+
const backdropTransition$ = this._backdropCmptRef
|
|
269
|
+
? this._backdropCmptRef.instance.hide()
|
|
270
|
+
: of(undefined);
|
|
271
|
+
// hiding window
|
|
272
|
+
windowTransition$.subscribe(() => {
|
|
273
|
+
const { nativeElement } = this._windowCmptRef.location;
|
|
274
|
+
nativeElement.parentNode.removeChild(nativeElement);
|
|
275
|
+
this._windowCmptRef.destroy();
|
|
276
|
+
this._contentRef?.viewRef?.destroy();
|
|
277
|
+
this._windowCmptRef = null;
|
|
278
|
+
this._contentRef = null;
|
|
279
|
+
});
|
|
280
|
+
// hiding backdrop
|
|
281
|
+
backdropTransition$.subscribe(() => {
|
|
282
|
+
if (this._backdropCmptRef) {
|
|
283
|
+
const { nativeElement } = this._backdropCmptRef.location;
|
|
284
|
+
nativeElement.parentNode.removeChild(nativeElement);
|
|
285
|
+
this._backdropCmptRef.destroy();
|
|
286
|
+
this._backdropCmptRef = null;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
// all done
|
|
290
|
+
zip(windowTransition$, backdropTransition$).subscribe(() => {
|
|
291
|
+
this._hidden.next();
|
|
292
|
+
this._hidden.complete();
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
var ModalDismissReasons;
|
|
298
|
+
(function (ModalDismissReasons) {
|
|
299
|
+
ModalDismissReasons[ModalDismissReasons["BACKDROP_CLICK"] = 0] = "BACKDROP_CLICK";
|
|
300
|
+
ModalDismissReasons[ModalDismissReasons["ESC"] = 1] = "ESC";
|
|
301
|
+
})(ModalDismissReasons || (ModalDismissReasons = {}));
|
|
302
|
+
|
|
303
|
+
class HubModalWindow {
|
|
304
|
+
constructor() {
|
|
305
|
+
this._document = inject(DOCUMENT);
|
|
306
|
+
this._elRef = inject((ElementRef));
|
|
307
|
+
this._zone = inject(NgZone);
|
|
308
|
+
this._closed$ = new Subject();
|
|
309
|
+
this._elWithFocus = null; // element that is focused prior to modal opening
|
|
310
|
+
this.backdrop = true;
|
|
311
|
+
this.keyboard = true;
|
|
312
|
+
this.dismissEvent = new EventEmitter();
|
|
313
|
+
this.shown = new Subject();
|
|
314
|
+
this.hidden = new Subject();
|
|
315
|
+
}
|
|
316
|
+
get fullscreenClass() {
|
|
317
|
+
return this.fullscreen === true
|
|
318
|
+
? ' modal-fullscreen'
|
|
319
|
+
: isString(this.fullscreen)
|
|
320
|
+
? ` modal-fullscreen-${this.fullscreen}-down`
|
|
321
|
+
: '';
|
|
322
|
+
}
|
|
323
|
+
dismiss(reason) {
|
|
324
|
+
this.dismissEvent.emit(reason);
|
|
325
|
+
}
|
|
326
|
+
ngOnInit() {
|
|
327
|
+
this._elWithFocus = this._document.activeElement;
|
|
328
|
+
this._zone.onStable
|
|
329
|
+
.asObservable()
|
|
330
|
+
.pipe(take(1))
|
|
331
|
+
.subscribe(() => {
|
|
332
|
+
this._show();
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
ngOnDestroy() {
|
|
336
|
+
this._disableEventHandling();
|
|
337
|
+
}
|
|
338
|
+
hide() {
|
|
339
|
+
const { nativeElement } = this._elRef;
|
|
340
|
+
const context = {
|
|
341
|
+
animation: this.animation,
|
|
342
|
+
runningTransition: 'stop'
|
|
343
|
+
};
|
|
344
|
+
const windowTransition$ = hubRunTransition(this._zone, nativeElement, () => nativeElement.classList.remove('show'), context);
|
|
345
|
+
const dialogTransition$ = hubRunTransition(this._zone, this._dialogEl.nativeElement, () => { }, context);
|
|
346
|
+
const transitions$ = zip(windowTransition$, dialogTransition$);
|
|
347
|
+
transitions$.subscribe(() => {
|
|
348
|
+
this.hidden.next();
|
|
349
|
+
this.hidden.complete();
|
|
350
|
+
});
|
|
351
|
+
this._disableEventHandling();
|
|
352
|
+
this._restoreFocus();
|
|
353
|
+
return transitions$;
|
|
354
|
+
}
|
|
355
|
+
_show() {
|
|
356
|
+
const context = {
|
|
357
|
+
animation: this.animation,
|
|
358
|
+
runningTransition: 'continue'
|
|
359
|
+
};
|
|
360
|
+
const windowTransition$ = hubRunTransition(this._zone, this._elRef.nativeElement, (element, animation) => {
|
|
361
|
+
if (animation) {
|
|
362
|
+
reflow(element);
|
|
363
|
+
}
|
|
364
|
+
element.classList.add('show');
|
|
365
|
+
}, context);
|
|
366
|
+
const dialogTransition$ = hubRunTransition(this._zone, this._dialogEl.nativeElement, () => { }, context);
|
|
367
|
+
zip(windowTransition$, dialogTransition$).subscribe(() => {
|
|
368
|
+
this.shown.next();
|
|
369
|
+
this.shown.complete();
|
|
370
|
+
});
|
|
371
|
+
this._enableEventHandling();
|
|
372
|
+
this._setFocus();
|
|
373
|
+
}
|
|
374
|
+
_enableEventHandling() {
|
|
375
|
+
const { nativeElement } = this._elRef;
|
|
376
|
+
this._zone.runOutsideAngular(() => {
|
|
377
|
+
fromEvent(nativeElement, 'keydown')
|
|
378
|
+
.pipe(takeUntil(this._closed$), filter((e) => e.key === 'Escape'))
|
|
379
|
+
.subscribe((event) => {
|
|
380
|
+
if (this.keyboard) {
|
|
381
|
+
requestAnimationFrame(() => {
|
|
382
|
+
if (!event.defaultPrevented) {
|
|
383
|
+
this._zone.run(() => this.dismiss(ModalDismissReasons.ESC));
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
else if (this.backdrop === 'static') {
|
|
388
|
+
this._bumpBackdrop();
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
// We're listening to 'mousedown' and 'mouseup' to prevent modal from closing when pressing the mouse
|
|
392
|
+
// inside the modal dialog and releasing it outside
|
|
393
|
+
let preventClose = false;
|
|
394
|
+
fromEvent(this._dialogEl.nativeElement, 'mousedown')
|
|
395
|
+
.pipe(takeUntil(this._closed$), tap(() => (preventClose = false)), switchMap(() => fromEvent(nativeElement, 'mouseup').pipe(takeUntil(this._closed$), take(1))), filter(({ target }) => nativeElement === target))
|
|
396
|
+
.subscribe(() => {
|
|
397
|
+
preventClose = true;
|
|
398
|
+
});
|
|
399
|
+
// We're listening to 'click' to dismiss modal on modal window click, except when:
|
|
400
|
+
// 1. clicking on modal dialog itself
|
|
401
|
+
// 2. closing was prevented by mousedown/up handlers
|
|
402
|
+
// 3. clicking on scrollbar when the viewport is too small and modal doesn't fit (click is not triggered at all)
|
|
403
|
+
fromEvent(nativeElement, 'click')
|
|
404
|
+
.pipe(takeUntil(this._closed$))
|
|
405
|
+
.subscribe(({ target }) => {
|
|
406
|
+
if (nativeElement === target) {
|
|
407
|
+
if (this.backdrop === 'static') {
|
|
408
|
+
this._bumpBackdrop();
|
|
409
|
+
}
|
|
410
|
+
else if (this.backdrop === true && !preventClose) {
|
|
411
|
+
this._zone.run(() => this.dismiss(ModalDismissReasons.BACKDROP_CLICK));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
preventClose = false;
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
_disableEventHandling() {
|
|
419
|
+
this._closed$.next();
|
|
420
|
+
}
|
|
421
|
+
_setFocus() {
|
|
422
|
+
const { nativeElement } = this._elRef;
|
|
423
|
+
if (!nativeElement.contains(document.activeElement)) {
|
|
424
|
+
const autoFocusable = nativeElement.querySelector(`[hubAutofocus]`);
|
|
425
|
+
const firstFocusable = getFocusableBoundaryElements(nativeElement)[0];
|
|
426
|
+
const elementToFocus = autoFocusable || firstFocusable || nativeElement;
|
|
427
|
+
elementToFocus.focus();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
_restoreFocus() {
|
|
431
|
+
const body = this._document.body;
|
|
432
|
+
const elWithFocus = this._elWithFocus;
|
|
433
|
+
let elementToFocus;
|
|
434
|
+
if (elWithFocus && elWithFocus['focus'] && body.contains(elWithFocus)) {
|
|
435
|
+
elementToFocus = elWithFocus;
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
elementToFocus = body;
|
|
439
|
+
}
|
|
440
|
+
this._zone.runOutsideAngular(() => {
|
|
441
|
+
setTimeout(() => elementToFocus.focus());
|
|
442
|
+
this._elWithFocus = null;
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
_bumpBackdrop() {
|
|
446
|
+
if (this.backdrop === 'static') {
|
|
447
|
+
hubRunTransition(this._zone, this._elRef.nativeElement, ({ classList }) => {
|
|
448
|
+
classList.add('modal-static');
|
|
449
|
+
return () => classList.remove('modal-static');
|
|
450
|
+
}, { animation: this.animation, runningTransition: 'continue' });
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModalWindow, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
454
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.5", type: HubModalWindow, isStandalone: true, selector: "hub-modal-window", inputs: { animation: "animation", ariaLabelledBy: "ariaLabelledBy", ariaDescribedBy: "ariaDescribedBy", backdrop: "backdrop", centered: "centered", fullscreen: "fullscreen", keyboard: "keyboard", scrollable: "scrollable", size: "size", windowClass: "windowClass", modalDialogClass: "modalDialogClass" }, outputs: { dismissEvent: "dismiss" }, host: { attributes: { "role": "dialog", "tabindex": "-1" }, properties: { "class": "\"modal d-block\" + (windowClass ? \" \" + windowClass : \"\")", "class.fade": "animation", "attr.aria-modal": "true", "attr.aria-labelledby": "ariaLabelledBy", "attr.aria-describedby": "ariaDescribedBy" } }, viewQueries: [{ propertyName: "_dialogEl", first: true, predicate: ["dialog"], descendants: true, static: true }], ngImport: i0, template: `
|
|
455
|
+
<div
|
|
456
|
+
#dialog
|
|
457
|
+
[class]="
|
|
458
|
+
'modal-dialog' +
|
|
459
|
+
(size ? ' modal-' + size : '') +
|
|
460
|
+
(centered ? ' modal-dialog-centered' : '') +
|
|
461
|
+
fullscreenClass +
|
|
462
|
+
(scrollable ? ' modal-dialog-scrollable' : '') +
|
|
463
|
+
(modalDialogClass ? ' ' + modalDialogClass : '')
|
|
464
|
+
"
|
|
465
|
+
role="document"
|
|
466
|
+
>
|
|
467
|
+
<div class="modal-content">
|
|
468
|
+
<ng-container *ngIf="singleContent; else multipleContent">
|
|
469
|
+
<ng-content></ng-content>
|
|
470
|
+
</ng-container>
|
|
471
|
+
<ng-template #multipleContent>
|
|
472
|
+
<div class="modal-header">
|
|
473
|
+
<ng-content />
|
|
474
|
+
<button
|
|
475
|
+
type="button"
|
|
476
|
+
class="btn-close"
|
|
477
|
+
data-bs-dismiss="modal"
|
|
478
|
+
aria-label="Close"
|
|
479
|
+
(click)="dismiss(null)"
|
|
480
|
+
></button>
|
|
481
|
+
</div>
|
|
482
|
+
<div class="modal-body">
|
|
483
|
+
<ng-content />
|
|
484
|
+
</div>
|
|
485
|
+
<div class="modal-footer">
|
|
486
|
+
<ng-content />
|
|
487
|
+
</div>
|
|
488
|
+
</ng-template>
|
|
489
|
+
</div>
|
|
490
|
+
</div>
|
|
491
|
+
`, isInline: true, styles: ["hub-modal-window .component-host-scrollable{display:flex;flex-direction:column;overflow:hidden}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
492
|
+
}
|
|
493
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModalWindow, decorators: [{
|
|
494
|
+
type: Component,
|
|
495
|
+
args: [{ selector: 'hub-modal-window', standalone: true, imports: [NgIf], host: {
|
|
496
|
+
'[class]': '"modal d-block" + (windowClass ? " " + windowClass : "")',
|
|
497
|
+
'[class.fade]': 'animation',
|
|
498
|
+
role: 'dialog',
|
|
499
|
+
tabindex: '-1',
|
|
500
|
+
'[attr.aria-modal]': 'true',
|
|
501
|
+
'[attr.aria-labelledby]': 'ariaLabelledBy',
|
|
502
|
+
'[attr.aria-describedby]': 'ariaDescribedBy'
|
|
503
|
+
}, template: `
|
|
504
|
+
<div
|
|
505
|
+
#dialog
|
|
506
|
+
[class]="
|
|
507
|
+
'modal-dialog' +
|
|
508
|
+
(size ? ' modal-' + size : '') +
|
|
509
|
+
(centered ? ' modal-dialog-centered' : '') +
|
|
510
|
+
fullscreenClass +
|
|
511
|
+
(scrollable ? ' modal-dialog-scrollable' : '') +
|
|
512
|
+
(modalDialogClass ? ' ' + modalDialogClass : '')
|
|
513
|
+
"
|
|
514
|
+
role="document"
|
|
515
|
+
>
|
|
516
|
+
<div class="modal-content">
|
|
517
|
+
<ng-container *ngIf="singleContent; else multipleContent">
|
|
518
|
+
<ng-content></ng-content>
|
|
519
|
+
</ng-container>
|
|
520
|
+
<ng-template #multipleContent>
|
|
521
|
+
<div class="modal-header">
|
|
522
|
+
<ng-content />
|
|
523
|
+
<button
|
|
524
|
+
type="button"
|
|
525
|
+
class="btn-close"
|
|
526
|
+
data-bs-dismiss="modal"
|
|
527
|
+
aria-label="Close"
|
|
528
|
+
(click)="dismiss(null)"
|
|
529
|
+
></button>
|
|
530
|
+
</div>
|
|
531
|
+
<div class="modal-body">
|
|
532
|
+
<ng-content />
|
|
533
|
+
</div>
|
|
534
|
+
<div class="modal-footer">
|
|
535
|
+
<ng-content />
|
|
536
|
+
</div>
|
|
537
|
+
</ng-template>
|
|
538
|
+
</div>
|
|
539
|
+
</div>
|
|
540
|
+
`, encapsulation: ViewEncapsulation.None, styles: ["hub-modal-window .component-host-scrollable{display:flex;flex-direction:column;overflow:hidden}\n"] }]
|
|
541
|
+
}], propDecorators: { _dialogEl: [{
|
|
542
|
+
type: ViewChild,
|
|
543
|
+
args: ['dialog', { static: true }]
|
|
544
|
+
}], animation: [{
|
|
545
|
+
type: Input
|
|
546
|
+
}], ariaLabelledBy: [{
|
|
547
|
+
type: Input
|
|
548
|
+
}], ariaDescribedBy: [{
|
|
549
|
+
type: Input
|
|
550
|
+
}], backdrop: [{
|
|
551
|
+
type: Input
|
|
552
|
+
}], centered: [{
|
|
553
|
+
type: Input
|
|
554
|
+
}], fullscreen: [{
|
|
555
|
+
type: Input
|
|
556
|
+
}], keyboard: [{
|
|
557
|
+
type: Input
|
|
558
|
+
}], scrollable: [{
|
|
559
|
+
type: Input
|
|
560
|
+
}], size: [{
|
|
561
|
+
type: Input
|
|
562
|
+
}], windowClass: [{
|
|
563
|
+
type: Input
|
|
564
|
+
}], modalDialogClass: [{
|
|
565
|
+
type: Input
|
|
566
|
+
}], dismissEvent: [{
|
|
567
|
+
type: Output,
|
|
568
|
+
args: ['dismiss']
|
|
569
|
+
}] } });
|
|
570
|
+
|
|
571
|
+
class HubModalStack {
|
|
572
|
+
constructor() {
|
|
573
|
+
this._applicationRef = inject(ApplicationRef);
|
|
574
|
+
this._injector = inject(Injector);
|
|
575
|
+
this._environmentInjector = inject(EnvironmentInjector);
|
|
576
|
+
this._document = inject(DOCUMENT);
|
|
577
|
+
this._scrollBar = inject(ScrollBar);
|
|
578
|
+
this._activeWindowCmptHasChanged = new Subject();
|
|
579
|
+
this._ariaHiddenValues = new Map();
|
|
580
|
+
this._scrollBarRestoreFn = null;
|
|
581
|
+
this._modalRefs = [];
|
|
582
|
+
this._windowCmpts = [];
|
|
583
|
+
this._activeInstances = new EventEmitter();
|
|
584
|
+
const ngZone = inject(NgZone);
|
|
585
|
+
// Trap focus on active WindowCmpt
|
|
586
|
+
this._activeWindowCmptHasChanged.subscribe(() => {
|
|
587
|
+
if (this._windowCmpts.length) {
|
|
588
|
+
const activeWindowCmpt = this._windowCmpts[this._windowCmpts.length - 1];
|
|
589
|
+
hubFocusTrap(ngZone, activeWindowCmpt.location.nativeElement, this._activeWindowCmptHasChanged);
|
|
590
|
+
this._revertAriaHidden();
|
|
591
|
+
this._setAriaHidden(activeWindowCmpt.location.nativeElement);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
_restoreScrollBar() {
|
|
596
|
+
const scrollBarRestoreFn = this._scrollBarRestoreFn;
|
|
597
|
+
if (scrollBarRestoreFn) {
|
|
598
|
+
this._scrollBarRestoreFn = null;
|
|
599
|
+
scrollBarRestoreFn();
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
_hideScrollBar() {
|
|
603
|
+
if (!this._scrollBarRestoreFn) {
|
|
604
|
+
this._scrollBarRestoreFn = this._scrollBar.hide();
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
open(contentInjector, content, options) {
|
|
608
|
+
const containerEl = options.container instanceof HTMLElement
|
|
609
|
+
? options.container
|
|
610
|
+
: isDefined(options.container)
|
|
611
|
+
? this._document.querySelector(options.container)
|
|
612
|
+
: this._document.body;
|
|
613
|
+
if (!containerEl) {
|
|
614
|
+
throw new Error(`The specified modal container "${options.container || 'body'}" was not found in the DOM.`);
|
|
615
|
+
}
|
|
616
|
+
this._hideScrollBar();
|
|
617
|
+
const activeModal = new HubActiveModal();
|
|
618
|
+
contentInjector = options.injector || contentInjector;
|
|
619
|
+
const environmentInjector = contentInjector.get(EnvironmentInjector, null) ||
|
|
620
|
+
this._environmentInjector;
|
|
621
|
+
const contentRef = this._getContentRef(contentInjector, environmentInjector, content, activeModal, options);
|
|
622
|
+
const backdropCmptRef = options.backdrop !== false
|
|
623
|
+
? this._attachBackdrop(containerEl)
|
|
624
|
+
: undefined;
|
|
625
|
+
const windowCmptRef = this._attachWindowComponent(containerEl, contentRef.nodes, options);
|
|
626
|
+
const hubModalRef = new HubModalRef(windowCmptRef, contentRef, backdropCmptRef, options.beforeDismiss);
|
|
627
|
+
this._registerModalRef(hubModalRef);
|
|
628
|
+
this._registerWindowCmpt(windowCmptRef);
|
|
629
|
+
// We have to cleanup DOM after the last modal when BOTH 'hidden' was emitted and 'result' promise was resolved:
|
|
630
|
+
// - with animations OFF, 'hidden' emits synchronously, then 'result' is resolved asynchronously
|
|
631
|
+
// - with animations ON, 'result' is resolved asynchronously, then 'hidden' emits asynchronously
|
|
632
|
+
hubModalRef.hidden.pipe(take(1)).subscribe(() => Promise.resolve(true).then(() => {
|
|
633
|
+
if (!this._modalRefs.length) {
|
|
634
|
+
this._document.body.classList.remove('modal-open');
|
|
635
|
+
this._restoreScrollBar();
|
|
636
|
+
this._revertAriaHidden();
|
|
637
|
+
}
|
|
638
|
+
}));
|
|
639
|
+
activeModal.close = (result) => {
|
|
640
|
+
hubModalRef.close(result);
|
|
641
|
+
};
|
|
642
|
+
activeModal.dismiss = (reason) => {
|
|
643
|
+
hubModalRef.dismiss(reason);
|
|
644
|
+
};
|
|
645
|
+
activeModal.update = (options) => {
|
|
646
|
+
hubModalRef.update(options);
|
|
647
|
+
};
|
|
648
|
+
hubModalRef.update(options);
|
|
649
|
+
if (this._modalRefs.length === 1) {
|
|
650
|
+
this._document.body.classList.add('modal-open');
|
|
651
|
+
}
|
|
652
|
+
if (backdropCmptRef && backdropCmptRef.instance) {
|
|
653
|
+
backdropCmptRef.changeDetectorRef.detectChanges();
|
|
654
|
+
}
|
|
655
|
+
windowCmptRef.changeDetectorRef.detectChanges();
|
|
656
|
+
return hubModalRef;
|
|
657
|
+
}
|
|
658
|
+
get activeInstances() {
|
|
659
|
+
return this._activeInstances;
|
|
660
|
+
}
|
|
661
|
+
dismissAll(reason) {
|
|
662
|
+
this._modalRefs.forEach((hubModalRef) => hubModalRef.dismiss(reason));
|
|
663
|
+
}
|
|
664
|
+
hasOpenModals() {
|
|
665
|
+
return this._modalRefs.length > 0;
|
|
666
|
+
}
|
|
667
|
+
_attachBackdrop(containerEl) {
|
|
668
|
+
let backdropCmptRef = createComponent(HubModalBackdrop, {
|
|
669
|
+
environmentInjector: this._applicationRef.injector,
|
|
670
|
+
elementInjector: this._injector
|
|
671
|
+
});
|
|
672
|
+
this._applicationRef.attachView(backdropCmptRef.hostView);
|
|
673
|
+
containerEl.appendChild(backdropCmptRef.location.nativeElement);
|
|
674
|
+
return backdropCmptRef;
|
|
675
|
+
}
|
|
676
|
+
_attachWindowComponent(containerEl, [headerNodes, bodyNodes, footerNodes], options) {
|
|
677
|
+
const singleContent = !options.headerSelector && !options.footerSelector;
|
|
678
|
+
let windowCmptRef = createComponent(HubModalWindow, {
|
|
679
|
+
environmentInjector: this._applicationRef.injector,
|
|
680
|
+
elementInjector: this._injector,
|
|
681
|
+
projectableNodes: singleContent
|
|
682
|
+
? [bodyNodes]
|
|
683
|
+
: [[], headerNodes, bodyNodes, footerNodes]
|
|
684
|
+
});
|
|
685
|
+
Object.assign(windowCmptRef.instance, { singleContent });
|
|
686
|
+
this._applicationRef.attachView(windowCmptRef.hostView);
|
|
687
|
+
containerEl.appendChild(windowCmptRef.location.nativeElement);
|
|
688
|
+
return windowCmptRef;
|
|
689
|
+
}
|
|
690
|
+
_getContentRef(contentInjector, environmentInjector, content, activeModal, options) {
|
|
691
|
+
if (!content) {
|
|
692
|
+
return new ContentRef([]);
|
|
693
|
+
}
|
|
694
|
+
else if (content instanceof TemplateRef) {
|
|
695
|
+
return this._createFromTemplateRef(content, activeModal, options);
|
|
696
|
+
}
|
|
697
|
+
else if (isString(content)) {
|
|
698
|
+
return this._createFromString(content);
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
return this._createFromComponent(contentInjector, environmentInjector, content, activeModal, options);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
_createFromTemplateRef(templateRef, activeModal, options) {
|
|
705
|
+
const context = {
|
|
706
|
+
$implicit: activeModal,
|
|
707
|
+
close(result) {
|
|
708
|
+
activeModal.close(result);
|
|
709
|
+
},
|
|
710
|
+
dismiss(reason) {
|
|
711
|
+
activeModal.dismiss(reason);
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
const viewRef = templateRef.createEmbeddedView(context);
|
|
715
|
+
this._applicationRef.attachView(viewRef);
|
|
716
|
+
const containerNode = document.createElement('ng-container');
|
|
717
|
+
containerNode.append(...viewRef.rootNodes);
|
|
718
|
+
this._addDismissEventListener(containerNode, context, options);
|
|
719
|
+
return new ContentRef([
|
|
720
|
+
options.headerSelector
|
|
721
|
+
? extractAndRemoveNodesBySelector(containerNode, options.headerSelector)
|
|
722
|
+
: [],
|
|
723
|
+
containerNode.childNodes,
|
|
724
|
+
options.footerSelector
|
|
725
|
+
? extractAndRemoveNodesBySelector(containerNode, options.footerSelector)
|
|
726
|
+
: []
|
|
727
|
+
], viewRef);
|
|
728
|
+
}
|
|
729
|
+
_createFromString(content) {
|
|
730
|
+
const component = this._document.createTextNode(`${content}`);
|
|
731
|
+
return new ContentRef([[component]]);
|
|
732
|
+
}
|
|
733
|
+
_createFromComponent(contentInjector, environmentInjector, componentType, context, options) {
|
|
734
|
+
const elementInjector = Injector.create({
|
|
735
|
+
providers: [{ provide: HubActiveModal, useValue: context }],
|
|
736
|
+
parent: contentInjector
|
|
737
|
+
});
|
|
738
|
+
const componentRef = createComponent(componentType, {
|
|
739
|
+
environmentInjector,
|
|
740
|
+
elementInjector
|
|
741
|
+
});
|
|
742
|
+
const componentNativeEl = componentRef.location.nativeElement;
|
|
743
|
+
if (options.scrollable) {
|
|
744
|
+
componentNativeEl.classList.add('component-host-scrollable');
|
|
745
|
+
}
|
|
746
|
+
this._applicationRef.attachView(componentRef.hostView);
|
|
747
|
+
this._addDismissEventListener(componentNativeEl, context, options);
|
|
748
|
+
// FIXME: we should here get rid of the component nativeElement
|
|
749
|
+
// and use `[Array.from(componentNativeEl.childNodes)]` instead and remove the above CSS class.
|
|
750
|
+
return new ContentRef([
|
|
751
|
+
options.headerSelector
|
|
752
|
+
? extractAndRemoveNodesBySelector(componentNativeEl, options.headerSelector)
|
|
753
|
+
: [],
|
|
754
|
+
componentNativeEl.childNodes,
|
|
755
|
+
options.footerSelector
|
|
756
|
+
? extractAndRemoveNodesBySelector(componentNativeEl, options.footerSelector)
|
|
757
|
+
: []
|
|
758
|
+
], componentRef.hostView, componentRef);
|
|
759
|
+
}
|
|
760
|
+
_setAriaHidden(element) {
|
|
761
|
+
const parent = element.parentElement;
|
|
762
|
+
if (parent && element !== this._document.body) {
|
|
763
|
+
Array.from(parent.children).forEach((sibling) => {
|
|
764
|
+
if (sibling !== element && sibling.nodeName !== 'SCRIPT') {
|
|
765
|
+
this._ariaHiddenValues.set(sibling, sibling.getAttribute('aria-hidden'));
|
|
766
|
+
sibling.setAttribute('aria-hidden', 'true');
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
this._setAriaHidden(parent);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
_revertAriaHidden() {
|
|
773
|
+
this._ariaHiddenValues.forEach((value, element) => {
|
|
774
|
+
if (value) {
|
|
775
|
+
element.setAttribute('aria-hidden', value);
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
element.removeAttribute('aria-hidden');
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
this._ariaHiddenValues.clear();
|
|
782
|
+
}
|
|
783
|
+
_registerModalRef(hubModalRef) {
|
|
784
|
+
const unregisterModalRef = () => {
|
|
785
|
+
const index = this._modalRefs.indexOf(hubModalRef);
|
|
786
|
+
if (index > -1) {
|
|
787
|
+
this._modalRefs.splice(index, 1);
|
|
788
|
+
this._activeInstances.emit(this._modalRefs);
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
this._modalRefs.push(hubModalRef);
|
|
792
|
+
this._activeInstances.emit(this._modalRefs);
|
|
793
|
+
hubModalRef.result.then(unregisterModalRef, unregisterModalRef);
|
|
794
|
+
}
|
|
795
|
+
_registerWindowCmpt(hubWindowCmpt) {
|
|
796
|
+
this._windowCmpts.push(hubWindowCmpt);
|
|
797
|
+
this._activeWindowCmptHasChanged.next();
|
|
798
|
+
hubWindowCmpt.onDestroy(() => {
|
|
799
|
+
const index = this._windowCmpts.indexOf(hubWindowCmpt);
|
|
800
|
+
if (index > -1) {
|
|
801
|
+
this._windowCmpts.splice(index, 1);
|
|
802
|
+
this._activeWindowCmptHasChanged.next();
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Attaches click event listeners to elements within a container based on a specified dismiss selector to dismiss a modal.
|
|
808
|
+
*
|
|
809
|
+
* @param {HTMLElement} container - The `container` parameter is an HTMLElement that represents the DOM element which contains the
|
|
810
|
+
* modal content.
|
|
811
|
+
* @param {HubActiveModal} context - The `context` parameter in the `_addDismissEventListener` function refers to the active modal
|
|
812
|
+
* instance that is being displayed. It is used to call the `dismiss` method on the modal instance when a dismissible element is
|
|
813
|
+
* clicked.
|
|
814
|
+
* @param {HubModalOptions} options - The `options` parameter is an object that contains configuration options for the modal. It
|
|
815
|
+
* may include properties such as `dismissSelector`, which is used to specify a CSS selector for elements that, when clicked, will
|
|
816
|
+
* dismiss the modal by calling the `dismiss` method on the `context` object.
|
|
817
|
+
*/
|
|
818
|
+
_addDismissEventListener(container, context, options) {
|
|
819
|
+
if (options.dismissSelector) {
|
|
820
|
+
const dismissaable = container.querySelectorAll(options.dismissSelector);
|
|
821
|
+
for (const item of Array.from(dismissaable)) {
|
|
822
|
+
item.addEventListener('click', () => context.dismiss());
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModalStack, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
827
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModalStack, providedIn: 'root' }); }
|
|
828
|
+
}
|
|
829
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModalStack, decorators: [{
|
|
830
|
+
type: Injectable,
|
|
831
|
+
args: [{ providedIn: 'root' }]
|
|
832
|
+
}], ctorParameters: () => [] });
|
|
833
|
+
/**
|
|
834
|
+
* Extracts child nodes matching a selector from a container element, removes those nodes from the DOM, and returns them as an array.
|
|
835
|
+
*
|
|
836
|
+
* @param {HTMLElement} container - The `container` parameter in the `extractAndRemoveNodesBySelector` function is an HTMLElement
|
|
837
|
+
* that represents the parent element within which we want to search for nodes matching a specific selector and remove them.
|
|
838
|
+
* @param {string} selector - The `selector` parameter in the `extractAndRemoveNodesBySelector` function is a string that
|
|
839
|
+
* represents a CSS selector. This selector is used to query and select specific elements within the `container` HTMLElement.
|
|
840
|
+
*
|
|
841
|
+
* @returns An array of nodes that were extracted from the container element based on the provided selector, and then removes those
|
|
842
|
+
* nodes from the DOM.
|
|
843
|
+
*/
|
|
844
|
+
function extractAndRemoveNodesBySelector(container, selector) {
|
|
845
|
+
let containerNodes = container.querySelectorAll(selector);
|
|
846
|
+
const nodes = Array.from(containerNodes).reduce((acc, c) => {
|
|
847
|
+
return [...acc, ...Array.from(c.childNodes)];
|
|
848
|
+
}, []);
|
|
849
|
+
// Selecciona los nodos dentro del contenedor que coincidan con el selector
|
|
850
|
+
const nodesToRemove = container.querySelectorAll(selector);
|
|
851
|
+
// Convertir NodeList a array y eliminar cada nodo del DOM
|
|
852
|
+
Array.from(nodesToRemove).forEach((node) => node.remove());
|
|
853
|
+
return nodes;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* A service for opening modal windows.
|
|
858
|
+
*
|
|
859
|
+
* Creating a modal is straightforward: create a component or a template and pass it as an argument to
|
|
860
|
+
* the `.open()` method.
|
|
861
|
+
*/
|
|
862
|
+
class HubModal {
|
|
863
|
+
constructor() {
|
|
864
|
+
this._injector = inject(Injector);
|
|
865
|
+
this._modalStack = inject(HubModalStack);
|
|
866
|
+
this._config = inject(HubModalConfig);
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Opens a new modal window with the specified content and supplied options.
|
|
870
|
+
*
|
|
871
|
+
* Content can be provided as a `TemplateRef` or a component type. If you pass a component type as content,
|
|
872
|
+
* then instances of those components can be injected with an instance of the `HubActiveModal` class. You can then
|
|
873
|
+
* use `HubActiveModal` methods to close / dismiss modals from "inside" of your component.
|
|
874
|
+
*
|
|
875
|
+
* Also see the [`HubModalOptions`](#/components/modal/api#HubModalOptions) for the list of supported options.
|
|
876
|
+
*/
|
|
877
|
+
open(content, options = {}) {
|
|
878
|
+
const combinedOptions = { ...this._config, animation: this._config.animation, ...options };
|
|
879
|
+
return this._modalStack.open(this._injector, content, combinedOptions);
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Returns an observable that holds the active modal instances.
|
|
883
|
+
*/
|
|
884
|
+
get activeInstances() {
|
|
885
|
+
return this._modalStack.activeInstances;
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Dismisses all currently displayed modal windows with the supplied reason.
|
|
889
|
+
*
|
|
890
|
+
* @since 3.1.0
|
|
891
|
+
*/
|
|
892
|
+
dismissAll(reason) {
|
|
893
|
+
this._modalStack.dismissAll(reason);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Indicates if there are currently any open modal windows in the application.
|
|
897
|
+
*
|
|
898
|
+
* @since 3.3.0
|
|
899
|
+
*/
|
|
900
|
+
hasOpenModals() {
|
|
901
|
+
return this._modalStack.hasOpenModals();
|
|
902
|
+
}
|
|
903
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModal, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
904
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModal, providedIn: 'root' }); }
|
|
905
|
+
}
|
|
906
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubModal, decorators: [{
|
|
907
|
+
type: Injectable,
|
|
908
|
+
args: [{ providedIn: 'root' }]
|
|
909
|
+
}] });
|
|
910
|
+
|
|
911
|
+
/*
|
|
912
|
+
* Public API Surface of modal
|
|
913
|
+
*/
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Generated bundle index. Do not edit.
|
|
917
|
+
*/
|
|
918
|
+
|
|
919
|
+
export { HubModal };
|
|
920
|
+
//# sourceMappingURL=ng-hub-ui-modal.mjs.map
|