@filip.mazev/modal 0.1.27 → 0.1.28
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/README.md +12 -32
- package/fesm2022/filip.mazev-modal.mjs +156 -211
- package/fesm2022/filip.mazev-modal.mjs.map +1 -1
- package/package.json +1 -1
- package/types/filip.mazev-modal.d.ts +262 -211
package/README.md
CHANGED
|
@@ -58,12 +58,12 @@ Other themes are also available such as:
|
|
|
58
58
|
|
|
59
59
|
### Create a Modal Component
|
|
60
60
|
|
|
61
|
-
Your modal content components must extend `
|
|
61
|
+
Your modal content components must extend `IModal<TData, TResult>`:
|
|
62
62
|
|
|
63
63
|
Typescript:
|
|
64
64
|
|
|
65
65
|
```typescript
|
|
66
|
-
import { Modal,
|
|
66
|
+
import { Modal, ModalHeaderDirective, ModalFooterDirective } from '@filip.mazev/modal';
|
|
67
67
|
|
|
68
68
|
@Component({
|
|
69
69
|
selector: 'app-my-modal-component',
|
|
@@ -75,19 +75,7 @@ import { Modal, MODAL_DATA, ModalHeaderDirective, ModalFooterDirective } from '@
|
|
|
75
75
|
styleUrl: './my-modal-component.scss',
|
|
76
76
|
})
|
|
77
77
|
export class MyModalComponent extends Modal<MyData, MyResult> {
|
|
78
|
-
protected data = inject<MyData>(MODAL_DATA);
|
|
79
78
|
|
|
80
|
-
constructor() {
|
|
81
|
-
super();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
override afterModalGet(): void {
|
|
85
|
-
// This is called after the modal service has returned the instance of the generated modal to this component. You can access this.modal only after this
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
override onDestroy(): void {
|
|
89
|
-
// Perform onDestroy logic here as this method is called on ngOnDestroy on Modal
|
|
90
|
-
}
|
|
91
79
|
}
|
|
92
80
|
```
|
|
93
81
|
|
|
@@ -98,9 +86,13 @@ HTML (Template):
|
|
|
98
86
|
Example Header
|
|
99
87
|
</div>
|
|
100
88
|
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
</
|
|
89
|
+
<h1>
|
|
90
|
+
{{ data.Title }}
|
|
91
|
+
</h1>
|
|
92
|
+
|
|
93
|
+
<p>
|
|
94
|
+
{{ data.Body }}
|
|
95
|
+
</p>
|
|
104
96
|
|
|
105
97
|
<div *modalFooter>
|
|
106
98
|
Example Footer
|
|
@@ -127,7 +119,6 @@ Accessible via this.modal inside any component that extends Modal. This referenc
|
|
|
127
119
|
* `componentRef: (ComponentRef<C>)` The Angular ComponentRef of the content component (your component) inside the modal.
|
|
128
120
|
* `modalContainerRef: (ComponentRef<ModalComponent>)` The Angular ComponentRef of the wrapper container (the shell responsible for the backdrop, animations, and layout).
|
|
129
121
|
* `modalContainerElement: (HTMLElement)` The native HTML element of the modal container.
|
|
130
|
-
* `selfIdentifier: ({ constructor: Function })` An object identifying the constructor of the component, used internally for instance tracking.
|
|
131
122
|
|
|
132
123
|
#### `IModalCloseResult`
|
|
133
124
|
|
|
@@ -171,8 +162,9 @@ modalRef.afterClosed().subscribe(result: IModalCloseResult<MyData> => {
|
|
|
171
162
|
Controls the behavior and content of the modal container:
|
|
172
163
|
|
|
173
164
|
* `open` |`boolean`|: (optional) Whether the modal should be open or not, will default to true.
|
|
174
|
-
* `afterClose`
|
|
175
|
-
* `
|
|
165
|
+
* `afterClose` |`Function`|: (optional) The function to run after the modal closes.
|
|
166
|
+
* `closeGuard` |`ModalCloseGuard`| (optional) The guard that will determine whether the modal can be closed or not
|
|
167
|
+
* `closeGuardOnlyOnCancel` |`boolean`| (optional) Whether the close guard should only be checked on cancel actions, will default to true
|
|
176
168
|
* `disableClose` |`boolean`|: (optional) Whether the modal should be closable or not, will default to false. This applies to the close button, escape key, and backdrop.
|
|
177
169
|
* `disableCloseOnBackdropClick` |`boolean`|: (optional) Whether the modal shouldn't be closable specifically when the user clicks on the backdrop, will default to false.
|
|
178
170
|
* `disableCloseOnNavigation` |`boolean`|: (optional) Whether the modal should remain open when the user navigates away from the current page, will default to false.
|
|
@@ -203,18 +195,6 @@ Controls the visual appearance:
|
|
|
203
195
|
* `wrapperStyles` |`string`|: (optional) Inline CSS styles to apply to the wrapper of the modal.
|
|
204
196
|
* `overrideFullHeight` |`boolean`|: (optional) Whether the modal should override the default full-height restriction or not, will default to false.
|
|
205
197
|
|
|
206
|
-
### `IModalConfirmCloseConfig`
|
|
207
|
-
|
|
208
|
-
Configuration for the confirmation modal triggered when a user attempts to close the parent modal (e.g., "Unsaved Changes"):
|
|
209
|
-
|
|
210
|
-
* `confirmModalComponent` |`ComponentType<ConfirmModal>`|: (required) The component class to use for the confirmation modal.
|
|
211
|
-
* `confirmClose` |`boolean`|: (required) Whether the confirmation flow should be active. If false, the modal closes immediately without confirmation.
|
|
212
|
-
* `confirmOnSubmit` |`boolean`|: (optional) Whether the confirmation should also trigger when the modal is closed via a "submit" (success) action. Defaults to false (meaning confirmation is only required for 'cancel' or backdrop closure).
|
|
213
|
-
* `style` |`IModalStyleConfig`|: (optional) The visual style configuration for the confirmation modal instance.
|
|
214
|
-
* `data` |`ConfirmModalData`|: (optional) The data to pass to the confirmation component. The component needs to use the @Inject(MODAL_DATA) decorator to receive this.
|
|
215
|
-
* `bannerText` |`string`|: (optional) The text to display in the header banner of the confirmation modal.
|
|
216
|
-
* `bypassSelfCheck` |`boolean`|: (optional) Whether the modal should bypass the internal check that ensures the confirmation is attached to the specific closing modal. Defaults to false.
|
|
217
|
-
|
|
218
198
|
### `IBottomSheetModalConfig`
|
|
219
199
|
|
|
220
200
|
Configuration for the mobile-optimized bottom sheet modal (bottom sheet):
|
|
@@ -1,30 +1,11 @@
|
|
|
1
|
-
import { DOCUMENT, NgClass, NgTemplateOutlet } from '@angular/common';
|
|
2
1
|
import * as i0 from '@angular/core';
|
|
3
|
-
import {
|
|
4
|
-
import { BehaviorSubject, Subject,
|
|
2
|
+
import { InjectionToken, inject, Injector, ApplicationRef, EnvironmentInjector, RendererFactory2, createComponent, Injectable, input, output, Component, signal, computed, ViewChild, ViewChildren, effect, ViewContainerRef, TemplateRef, Directive } from '@angular/core';
|
|
3
|
+
import { BehaviorSubject, Subject, filter, skip, fromEvent, Observable, from, of, take } from 'rxjs';
|
|
4
|
+
import { DOCUMENT, NgClass, NgTemplateOutlet } from '@angular/common';
|
|
5
5
|
import { Router, NavigationEnd } from '@angular/router';
|
|
6
6
|
import { uuidv4, WindowDimensionsService, ScrollLockService, DeviceTypeService } from '@filip.mazev/blocks-core';
|
|
7
7
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
8
8
|
|
|
9
|
-
const EMPTY_STRING = '';
|
|
10
|
-
|
|
11
|
-
var ModalWarnings;
|
|
12
|
-
(function (ModalWarnings) {
|
|
13
|
-
//#region Multi-level modals
|
|
14
|
-
ModalWarnings["NO_PARENT_PROVIDED"] = "No parent modal provided for multilevel modal. Please provide a parent modal or set openAsMultilevel to false.";
|
|
15
|
-
ModalWarnings["MULTILEVEL_INLINE_NOT_SUPPORTED"] = "As of this version, opening a multi-level modal from inline HTML is not possible. Please set openAsMultilevel to false or open your modal through the ModalService! Opening modal as normal modal.";
|
|
16
|
-
ModalWarnings["MULTILEVEL_NO_PARENT"] = "Cannot open a multilevel modal with only one modal open. Please open another modal before opening a multilevel modal or set openAsMultilevel to false.";
|
|
17
|
-
ModalWarnings["PARENT_MODAL_NOT_SET"] = "Error setting parent modal. Opening modal as normal modal";
|
|
18
|
-
//#endregion
|
|
19
|
-
//#region Confirm close
|
|
20
|
-
ModalWarnings["CONFIRM_MODAL_NESTING_NOT_SUPPORTED"] = "Cannot open a confirm modal from within a confirm modal. If you want to allow this behaviour, set bypassSelfCheck to true in the confirmCloseConfig.";
|
|
21
|
-
//#endregion
|
|
22
|
-
//#region Directive usage
|
|
23
|
-
ModalWarnings["FOOTER_DIRECTIVE_OUTSIDE_MODAL"] = "[ModalFooter] Directive used outside of a ModalComponent.";
|
|
24
|
-
ModalWarnings["HEADER_DIRECTIVE_OUTSIDE_MODAL"] = "[ModalHeader] Directive used outside of a ModalComponent.";
|
|
25
|
-
//#endregion
|
|
26
|
-
})(ModalWarnings || (ModalWarnings = {}));
|
|
27
|
-
|
|
28
9
|
class ModalStyleConfig {
|
|
29
10
|
layout;
|
|
30
11
|
breakpoints;
|
|
@@ -46,8 +27,8 @@ class ModalStyleConfig {
|
|
|
46
27
|
this.showCloseButton = config?.showCloseButton ?? true;
|
|
47
28
|
this.mobileConfig = config?.mobileConfig ?? {};
|
|
48
29
|
this.contentWrapper = config?.contentWrapper ?? true;
|
|
49
|
-
this.wrapperClasses = config?.wrapperClasses ??
|
|
50
|
-
this.wrapperStyles = config?.wrapperStyles ??
|
|
30
|
+
this.wrapperClasses = config?.wrapperClasses ?? "";
|
|
31
|
+
this.wrapperStyles = config?.wrapperStyles ?? "";
|
|
51
32
|
this.overrideFullHeight = config?.overrideFullHeight ?? false;
|
|
52
33
|
}
|
|
53
34
|
}
|
|
@@ -55,7 +36,8 @@ class ModalStyleConfig {
|
|
|
55
36
|
class ModalConfig {
|
|
56
37
|
open;
|
|
57
38
|
afterClose;
|
|
58
|
-
|
|
39
|
+
closeGuard;
|
|
40
|
+
closeGuardOnlyOnCancel;
|
|
59
41
|
disableClose;
|
|
60
42
|
disableCloseOnBackdropClick;
|
|
61
43
|
disableCloseOnNavigation;
|
|
@@ -72,7 +54,8 @@ class ModalConfig {
|
|
|
72
54
|
constructor(config) {
|
|
73
55
|
this.open = config?.open ?? true;
|
|
74
56
|
this.afterClose = config?.afterClose;
|
|
75
|
-
this.
|
|
57
|
+
this.closeGuard = config?.closeGuard;
|
|
58
|
+
this.closeGuardOnlyOnCancel = config?.closeGuardOnlyOnCancel ?? true;
|
|
76
59
|
this.disableClose = config?.disableClose ?? false;
|
|
77
60
|
this.disableCloseOnBackdropClick = config?.disableCloseOnBackdropClick ?? false;
|
|
78
61
|
this.disableCloseOnNavigation = config?.disableCloseOnNavigation ?? false;
|
|
@@ -80,9 +63,9 @@ class ModalConfig {
|
|
|
80
63
|
this.webkitOnlyOverflowMobileHandling = config?.webkitOnlyOverflowMobileHandling ?? true;
|
|
81
64
|
this.data = config?.data ?? null;
|
|
82
65
|
this.style = new ModalStyleConfig(config?.style);
|
|
83
|
-
this.bannerText = config?.bannerText ??
|
|
84
|
-
this.contentClasses = config?.contentClasses ??
|
|
85
|
-
this.contentStyles = config?.contentStyles ??
|
|
66
|
+
this.bannerText = config?.bannerText ?? "";
|
|
67
|
+
this.contentClasses = config?.contentClasses ?? "";
|
|
68
|
+
this.contentStyles = config?.contentStyles ?? "";
|
|
86
69
|
this.disableConsoleWarnings = config?.disableConsoleWarnings ?? false;
|
|
87
70
|
this.disableConsoleInfo = config?.disableConsoleInfo ?? false;
|
|
88
71
|
this.id = config?.id ?? uuidv4();
|
|
@@ -98,6 +81,7 @@ var ModalState;
|
|
|
98
81
|
})(ModalState || (ModalState = {}));
|
|
99
82
|
|
|
100
83
|
class ModalRef {
|
|
84
|
+
componentType;
|
|
101
85
|
modalService;
|
|
102
86
|
//#region Modal Container
|
|
103
87
|
_modalContainer = {};
|
|
@@ -147,13 +131,6 @@ class ModalRef {
|
|
|
147
131
|
getStatus() {
|
|
148
132
|
return this._modalState;
|
|
149
133
|
}
|
|
150
|
-
_selfIdentifier = {};
|
|
151
|
-
set selfIdentifier(selfIdentifier) {
|
|
152
|
-
this._selfIdentifier = selfIdentifier;
|
|
153
|
-
}
|
|
154
|
-
get selfIdentifier() {
|
|
155
|
-
return this._selfIdentifier;
|
|
156
|
-
}
|
|
157
134
|
_modalConfig = undefined;
|
|
158
135
|
set modalConfig(modalConfig) {
|
|
159
136
|
this._modalConfig = modalConfig;
|
|
@@ -171,6 +148,10 @@ class ModalRef {
|
|
|
171
148
|
this.backdropClickSubject.next(event);
|
|
172
149
|
}
|
|
173
150
|
afterCloseSubject = new Subject();
|
|
151
|
+
/**
|
|
152
|
+
* Observable that emits when the modal has been closed.
|
|
153
|
+
* @returns An Observable that emits an IModalCloseResult<R> when the modal is closed.
|
|
154
|
+
*/
|
|
174
155
|
afterClosed() {
|
|
175
156
|
return this.afterCloseSubject.asObservable();
|
|
176
157
|
}
|
|
@@ -178,13 +159,13 @@ class ModalRef {
|
|
|
178
159
|
this.afterCloseSubject.next(result);
|
|
179
160
|
}
|
|
180
161
|
//#endregion
|
|
181
|
-
constructor(componentRef,
|
|
162
|
+
constructor(componentRef, componentType, modalContainerRef, modalService, modalConfig) {
|
|
163
|
+
this.componentType = componentType;
|
|
182
164
|
this.modalService = modalService;
|
|
183
165
|
this.modalConfig = modalConfig;
|
|
184
166
|
this.modalContainerRef = modalContainerRef;
|
|
185
167
|
this.modalContainerElement = modalContainerRef.location.nativeElement;
|
|
186
168
|
this.componentRef = componentRef;
|
|
187
|
-
this.selfIdentifier = selfIdentifier;
|
|
188
169
|
}
|
|
189
170
|
//#region Public Methods
|
|
190
171
|
async open() {
|
|
@@ -217,62 +198,11 @@ class ModalRef {
|
|
|
217
198
|
this._modalState = ModalState.CLOSED;
|
|
218
199
|
this.modalState.next(ModalState.CLOSED);
|
|
219
200
|
this.afterClose(result);
|
|
220
|
-
this.modalService?.
|
|
201
|
+
this.modalService?.unregister(this);
|
|
221
202
|
}, this.modalContainerRef.instance.animationDuration);
|
|
222
203
|
}
|
|
223
204
|
}
|
|
224
205
|
|
|
225
|
-
class Modal {
|
|
226
|
-
ModalService = inject(ModalService);
|
|
227
|
-
modal;
|
|
228
|
-
modalGetSubscription$ = new Subject();
|
|
229
|
-
constructor() {
|
|
230
|
-
this.createModalSubscription();
|
|
231
|
-
}
|
|
232
|
-
ngOnDestroy() {
|
|
233
|
-
this.onDestroy();
|
|
234
|
-
this.unsubscribeModalGet();
|
|
235
|
-
}
|
|
236
|
-
createModalSubscription() {
|
|
237
|
-
this.modalGetSubscription$ = new Subject();
|
|
238
|
-
this.ModalService
|
|
239
|
-
.getSubscribe(this.constructor)
|
|
240
|
-
.pipe(takeUntil(this.modalGetSubscription$))
|
|
241
|
-
.subscribe((modal) => {
|
|
242
|
-
if (modal instanceof ModalRef) {
|
|
243
|
-
this.modal = modal;
|
|
244
|
-
this.afterModalGet();
|
|
245
|
-
this.modalGetSubscription$.next();
|
|
246
|
-
this.modalGetSubscription$.complete();
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
unsubscribeModalGet() {
|
|
251
|
-
this.modalGetSubscription$.next();
|
|
252
|
-
this.modalGetSubscription$.complete();
|
|
253
|
-
}
|
|
254
|
-
close() {
|
|
255
|
-
this.modal?.close();
|
|
256
|
-
}
|
|
257
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: Modal, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
258
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: Modal });
|
|
259
|
-
}
|
|
260
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: Modal, decorators: [{
|
|
261
|
-
type: Injectable
|
|
262
|
-
}], ctorParameters: () => [] });
|
|
263
|
-
|
|
264
|
-
var ModalErrors;
|
|
265
|
-
(function (ModalErrors) {
|
|
266
|
-
//#region General
|
|
267
|
-
ModalErrors["MODAL_DOESNT_MATCH_THE_REQUESTED_TYPES"] = "The modal doesn't match the requested types.";
|
|
268
|
-
//#endregion
|
|
269
|
-
//#region Multi-level modals
|
|
270
|
-
ModalErrors["PARENT_MODAL_CANT_BE_THE_SAME_AS_CHILD"] = "Parent modal cannot be the same as the child modal";
|
|
271
|
-
ModalErrors["PARENT_MODAL_NOT_FOUND"] = "Parent modal does not exist";
|
|
272
|
-
ModalErrors["PARENT_MODAL_NOT_PROVIDED"] = "No parent modal provided for multilevel modal";
|
|
273
|
-
//#endregion
|
|
274
|
-
})(ModalErrors || (ModalErrors = {}));
|
|
275
|
-
|
|
276
206
|
const MODAL_DATA = new InjectionToken("MODAL_DATA");
|
|
277
207
|
|
|
278
208
|
class ModalService {
|
|
@@ -284,14 +214,14 @@ class ModalService {
|
|
|
284
214
|
renderer;
|
|
285
215
|
document = inject(DOCUMENT);
|
|
286
216
|
//#region Properties
|
|
287
|
-
modals = new
|
|
217
|
+
modals = new Set();
|
|
288
218
|
modalsSubject = new BehaviorSubject(this.modals);
|
|
289
219
|
//#endregion
|
|
290
220
|
constructor() {
|
|
291
221
|
this.renderer = this.rendererFactory.createRenderer(null, null);
|
|
292
222
|
this.createSubscriptions();
|
|
293
223
|
}
|
|
294
|
-
//#region
|
|
224
|
+
//#region Subscription Methods
|
|
295
225
|
createSubscriptions() {
|
|
296
226
|
this.router.events
|
|
297
227
|
.pipe(filter((event) => event instanceof NavigationEnd), skip(1), takeUntilDestroyed())
|
|
@@ -303,6 +233,12 @@ class ModalService {
|
|
|
303
233
|
}
|
|
304
234
|
//#endregion
|
|
305
235
|
//#region Public Methods
|
|
236
|
+
/**
|
|
237
|
+
* Opens a modal with the specified component and configuration.
|
|
238
|
+
* @param component The component to be displayed in the modal.
|
|
239
|
+
* @param config Optional configuration for the modal.
|
|
240
|
+
* @returns A ModalRef instance representing the opened modal.
|
|
241
|
+
*/
|
|
306
242
|
open(component, config) {
|
|
307
243
|
const dataInjector = Injector.create({
|
|
308
244
|
providers: [{ provide: MODAL_DATA, useValue: config?.data }],
|
|
@@ -327,6 +263,10 @@ class ModalService {
|
|
|
327
263
|
this.appRef.attachView(wrapperRef.hostView);
|
|
328
264
|
this.document.body.appendChild(wrapperRef.location.nativeElement);
|
|
329
265
|
const modal = new ModalRef(contentRef, component, wrapperRef, this, new ModalConfig(config));
|
|
266
|
+
if (this.isIModal(contentRef.instance)) {
|
|
267
|
+
contentRef.instance.modal = modal;
|
|
268
|
+
contentRef.instance.onModalInit();
|
|
269
|
+
}
|
|
330
270
|
wrapperRef.onDestroy(() => {
|
|
331
271
|
this.appRef.detachView(wrapperRef.hostView);
|
|
332
272
|
wrapperRef.location.nativeElement.remove();
|
|
@@ -336,66 +276,64 @@ class ModalService {
|
|
|
336
276
|
this.renderer.setStyle(modalElement, 'width', '100%');
|
|
337
277
|
this.renderer.setStyle(modalElement, 'display', 'flex');
|
|
338
278
|
this.renderer.setStyle(modalElement, 'flex-grow', '1');
|
|
339
|
-
this.modals.
|
|
279
|
+
this.modals.add(modal);
|
|
340
280
|
this.modalsSubject.next(this.modals);
|
|
341
281
|
modal.open();
|
|
342
282
|
return modal;
|
|
343
283
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
284
|
+
/**
|
|
285
|
+
* Closes the specified modal.
|
|
286
|
+
* @param modal The ModalRef instance representing the modal to be closed.
|
|
287
|
+
*/
|
|
288
|
+
close(modal) {
|
|
289
|
+
modal.close();
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Unregisters the specified modal from the service.
|
|
293
|
+
* @param modal The ModalRef instance representing the modal to be unregistered.
|
|
294
|
+
*/
|
|
295
|
+
unregister(modal) {
|
|
296
|
+
this.modals.delete(modal);
|
|
297
|
+
this.modalsSubject.next(this.modals);
|
|
355
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Closes all open modals.
|
|
301
|
+
* @param onNavigate Indicates if the closeAll is triggered by navigation event.
|
|
302
|
+
*/
|
|
356
303
|
closeAll(onNavigate = false) {
|
|
357
304
|
this.modals.forEach((modal) => {
|
|
358
|
-
if (modal
|
|
359
|
-
|
|
360
|
-
modal.close("cancel", undefined, true);
|
|
361
|
-
}
|
|
305
|
+
if (modal.modalConfig?.disableCloseOnNavigation !== true || !onNavigate) {
|
|
306
|
+
modal.close("cancel", undefined, true);
|
|
362
307
|
}
|
|
363
308
|
});
|
|
364
|
-
this.modals.clear();
|
|
365
|
-
this.modalsSubject.next(this.modals);
|
|
366
|
-
}
|
|
367
|
-
get(self) {
|
|
368
|
-
const modal = this.modals.get(self);
|
|
369
|
-
this.modalRequestedTypeCheck(modal);
|
|
370
|
-
return modal;
|
|
371
309
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
310
|
+
/**
|
|
311
|
+
* Finds if a modal with the specified component type is currently open.
|
|
312
|
+
* @param componentType The component type to search for.
|
|
313
|
+
* @returns True if a modal with the specified component type is open, false otherwise.
|
|
314
|
+
*/
|
|
315
|
+
find(componentType) {
|
|
316
|
+
for (const modal of this.modals) {
|
|
317
|
+
if (modal.componentRef.componentType === componentType) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
378
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Gets the count of currently open modals.
|
|
325
|
+
* @returns The number of open modals.
|
|
326
|
+
*/
|
|
379
327
|
modalsCount() {
|
|
380
328
|
return this.modals.size;
|
|
381
329
|
}
|
|
382
|
-
find(self) {
|
|
383
|
-
return this.modals.has(self);
|
|
384
|
-
}
|
|
385
330
|
//#endregion
|
|
386
331
|
//#region Helper Methods
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
else if (!(modal instanceof ModalCore)) {
|
|
395
|
-
throw new Error(ModalErrors.MODAL_DOESNT_MATCH_THE_REQUESTED_TYPES);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
return true;
|
|
332
|
+
isIModal(component) {
|
|
333
|
+
return (typeof component === 'object' &&
|
|
334
|
+
component !== null &&
|
|
335
|
+
'onModalInit' in component &&
|
|
336
|
+
typeof component.onModalInit === 'function');
|
|
399
337
|
}
|
|
400
338
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ModalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
401
339
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ModalService, providedIn: "root" });
|
|
@@ -699,23 +637,27 @@ class ModalCore {
|
|
|
699
637
|
deviceTypeService = inject(DeviceTypeService);
|
|
700
638
|
afterClose = output();
|
|
701
639
|
animationDuration = MODAL_DEFAULT_ANIM_DURATION;
|
|
640
|
+
/** The component reference (the component that is being displayed in the modal) */
|
|
702
641
|
componentRef = {};
|
|
642
|
+
/** The configuration for the modal */
|
|
703
643
|
config;
|
|
704
644
|
closeFunction;
|
|
705
645
|
backdropClickSubject = new Subject();
|
|
646
|
+
/* Observable for backdrop clicks */
|
|
706
647
|
backdropClick = this.backdropClickSubject.asObservable();
|
|
648
|
+
/* Whether the modal is open or not */
|
|
707
649
|
isOpen = signal(true, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
708
|
-
isBottomSheetModalActive = signal(false, ...(ngDevMode ? [{ debugName: "isBottomSheetModalActive" }] : []));
|
|
709
650
|
effectiveLayout = signal('center', ...(ngDevMode ? [{ debugName: "effectiveLayout" }] : []));
|
|
710
651
|
isCentered = signal(false, ...(ngDevMode ? [{ debugName: "isCentered" }] : []));
|
|
711
652
|
isSide = signal(false, ...(ngDevMode ? [{ debugName: "isSide" }] : []));
|
|
712
653
|
headerTemplate = signal(null, ...(ngDevMode ? [{ debugName: "headerTemplate" }] : []));
|
|
713
654
|
footerTemplate = signal(null, ...(ngDevMode ? [{ debugName: "footerTemplate" }] : []));
|
|
655
|
+
isBottomSheetModalActive = signal(false, ...(ngDevMode ? [{ debugName: "isBottomSheetModalActive" }] : []));
|
|
714
656
|
isAnimated = false;
|
|
715
657
|
hasBanner = false;
|
|
716
658
|
hasDefaultContentWrapperClass = false;
|
|
717
659
|
isConfirmCloseModalOpen = false;
|
|
718
|
-
|
|
660
|
+
isScrollHandlingEnabled = false;
|
|
719
661
|
isScrollDisabled = false;
|
|
720
662
|
_sortedBreakpoints = [];
|
|
721
663
|
windowDimensions = this.windowDimensionsService.dimensions;
|
|
@@ -765,7 +707,10 @@ class ModalCore {
|
|
|
765
707
|
//#endregion
|
|
766
708
|
//#region Initialization Methods
|
|
767
709
|
initParamsFromConfig() {
|
|
768
|
-
this.
|
|
710
|
+
this.isScrollHandlingEnabled =
|
|
711
|
+
this.deviceTypeService.getDeviceState().isAppleDevice
|
|
712
|
+
|| this.config?.webkitOnlyOverflowMobileHandling === false
|
|
713
|
+
|| this.config?.enableExtremeOverflowHandling === true;
|
|
769
714
|
this.hasBanner =
|
|
770
715
|
this.config !== undefined &&
|
|
771
716
|
((this.config.bannerText !== undefined && this.config.bannerText.length > 0) ||
|
|
@@ -795,50 +740,40 @@ class ModalCore {
|
|
|
795
740
|
}
|
|
796
741
|
//#endregion
|
|
797
742
|
//#region Closing Methods
|
|
743
|
+
/**
|
|
744
|
+
* Closes the modal with the specified state and result.
|
|
745
|
+
* @param {ModalCloseMode} state The state of the modal close (e.g., 'confirm', 'cancel').
|
|
746
|
+
* @param {R | undefined} result The result to be returned when the modal closes.
|
|
747
|
+
* @param {boolean} fromInsideInteraction Whether the close was triggered from inside the modal interaction.
|
|
748
|
+
* @param {boolean} forceClose Whether to force close the modal, bypassing the close guards.
|
|
749
|
+
*/
|
|
798
750
|
close(state = "cancel", result = undefined, fromInsideInteraction = false, forceClose = false) {
|
|
799
|
-
if (
|
|
751
|
+
if (forceClose) {
|
|
752
|
+
this.handleClose(state, result);
|
|
800
753
|
return;
|
|
801
|
-
if (this.isScrollDisabled) {
|
|
802
|
-
this.scrollLockService.enableScroll(this.config?.enableExtremeOverflowHandling ?? false);
|
|
803
754
|
}
|
|
804
|
-
if ((this.config
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
this.
|
|
816
|
-
if (this.isBottomSheetModalActive()) {
|
|
817
|
-
this.resetBottomSheetModalLayout();
|
|
818
|
-
}
|
|
819
|
-
modal.afterClosed()
|
|
820
|
-
.pipe(take(1))
|
|
821
|
-
.subscribe((_result) => {
|
|
822
|
-
this.isConfirmCloseModalOpen = false;
|
|
823
|
-
if (_result.state === 'confirm') {
|
|
824
|
-
this.handleClose(state, result);
|
|
825
|
-
}
|
|
826
|
-
});
|
|
755
|
+
if ((this.config?.disableClose === true) && fromInsideInteraction) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
const shouldCheckCloseGuard = this.config?.closeGuardOnlyOnCancel !== true || state !== "confirm";
|
|
759
|
+
if (shouldCheckCloseGuard && this.config?.closeGuard) {
|
|
760
|
+
const guardResult = this.config.closeGuard.canClose(this.modalService);
|
|
761
|
+
const canClose$ = guardResult instanceof Observable ? guardResult :
|
|
762
|
+
guardResult instanceof Promise ? from(guardResult) :
|
|
763
|
+
of(guardResult);
|
|
764
|
+
canClose$.pipe(take(1)).subscribe((canClose) => {
|
|
765
|
+
if (canClose) {
|
|
766
|
+
this.handleClose(state, result);
|
|
827
767
|
}
|
|
828
768
|
else {
|
|
829
|
-
if (this.
|
|
830
|
-
|
|
769
|
+
if (this.isBottomSheetModalActive()) {
|
|
770
|
+
this.resetBottomSheetModalLayout();
|
|
831
771
|
}
|
|
832
|
-
this.handleClose(state, result);
|
|
833
772
|
}
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
this.handleClose(state, result);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
else if (this.isBottomSheetModalActive()) {
|
|
840
|
-
this.resetBottomSheetModalLayout();
|
|
773
|
+
});
|
|
774
|
+
return;
|
|
841
775
|
}
|
|
776
|
+
this.handleClose(state, result);
|
|
842
777
|
}
|
|
843
778
|
async handleClose(state, result) {
|
|
844
779
|
this.isOpen.set(false);
|
|
@@ -858,28 +793,6 @@ class ModalCore {
|
|
|
858
793
|
}
|
|
859
794
|
}
|
|
860
795
|
//#endregion
|
|
861
|
-
//#region Logical Assertions
|
|
862
|
-
shouldOpenConfirmCloseModal(forceClose, state) {
|
|
863
|
-
if (this.config?.confirmCloseConfig) {
|
|
864
|
-
const closeNotCalledWithForceClose = !forceClose;
|
|
865
|
-
if (closeNotCalledWithForceClose) {
|
|
866
|
-
const configuredForConfirmClose = this.config.confirmCloseConfig.confirmClose === true;
|
|
867
|
-
const closeCalledWithCancelState = state === "cancel";
|
|
868
|
-
const configuredForConfirmCloseOnSubmit = this.config.confirmCloseConfig.confirmOnSubmit === true;
|
|
869
|
-
return configuredForConfirmClose && (closeCalledWithCancelState || configuredForConfirmCloseOnSubmit);
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
return false;
|
|
873
|
-
}
|
|
874
|
-
shouldOpenConfirmCloseModalSelfCheck() {
|
|
875
|
-
if (this.config?.confirmCloseConfig) {
|
|
876
|
-
const hasSelfIdentifier = this.componentRef && this.componentRef.instance && this.componentRef.instance.modal && this.componentRef.instance.modal.selfIdentifier !== EMPTY_STRING;
|
|
877
|
-
const bypassSelfCheck = this.config.confirmCloseConfig.bypassSelfCheck === true;
|
|
878
|
-
return hasSelfIdentifier || bypassSelfCheck;
|
|
879
|
-
}
|
|
880
|
-
return false;
|
|
881
|
-
}
|
|
882
|
-
//#endregion
|
|
883
796
|
//#region Layout Methods
|
|
884
797
|
handleLayout(width) {
|
|
885
798
|
if (!this.config)
|
|
@@ -914,18 +827,20 @@ class ModalCore {
|
|
|
914
827
|
}
|
|
915
828
|
handleBottomSheetModalOpened() {
|
|
916
829
|
this.isBottomSheetModalActive.set(true);
|
|
917
|
-
this.
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
830
|
+
if (this.isScrollHandlingEnabled && !this.isScrollDisabled) {
|
|
831
|
+
this.scrollLockService.disableScroll({
|
|
832
|
+
mainContainer: this.modalContainer?.nativeElement,
|
|
833
|
+
handleExtremeOverflow: this.config?.enableExtremeOverflowHandling ?? false,
|
|
834
|
+
animationDuration: this.animationDuration,
|
|
835
|
+
handleTouchInput: true,
|
|
836
|
+
mobileOnlyTouchPrevention: true,
|
|
837
|
+
});
|
|
838
|
+
this.isScrollDisabled = true;
|
|
839
|
+
}
|
|
925
840
|
}
|
|
926
841
|
handleBottomSheetModalClosed() {
|
|
927
842
|
this.isBottomSheetModalActive.set(false);
|
|
928
|
-
if (this.isOpen() && this.
|
|
843
|
+
if (this.isOpen() && this.isScrollHandlingEnabled && this.isScrollDisabled) {
|
|
929
844
|
this.scrollLockService.enableScroll(this.config?.enableExtremeOverflowHandling ?? false);
|
|
930
845
|
this.isScrollDisabled = false;
|
|
931
846
|
}
|
|
@@ -976,10 +891,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
976
891
|
args: [ModalCentered]
|
|
977
892
|
}] } });
|
|
978
893
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
894
|
+
class Modal {
|
|
895
|
+
/**
|
|
896
|
+
* Data injected into the modal component.
|
|
897
|
+
*/
|
|
898
|
+
data = inject(MODAL_DATA);
|
|
899
|
+
/**
|
|
900
|
+
* Reference to the ModalRef instance associated with this modal.
|
|
901
|
+
*/
|
|
902
|
+
modal;
|
|
903
|
+
/**
|
|
904
|
+
* Called when the modal is initialized.
|
|
905
|
+
*/
|
|
906
|
+
onModalInit() { }
|
|
907
|
+
/**
|
|
908
|
+
* Closes the modal (with "cancel" reason) with an optional result.
|
|
909
|
+
* @param result The result to be passed when closing the modal.
|
|
910
|
+
*/
|
|
911
|
+
close(result) {
|
|
912
|
+
this.modal?.close('cancel', result);
|
|
913
|
+
}
|
|
914
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: Modal, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
915
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: Modal });
|
|
916
|
+
}
|
|
917
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: Modal, decorators: [{
|
|
918
|
+
type: Injectable
|
|
919
|
+
}] });
|
|
920
|
+
|
|
921
|
+
var ModalWarnings;
|
|
922
|
+
(function (ModalWarnings) {
|
|
923
|
+
//#region Directive usage
|
|
924
|
+
ModalWarnings["FOOTER_DIRECTIVE_OUTSIDE_MODAL"] = "[ModalFooter] Directive used outside of a ModalComponent.";
|
|
925
|
+
ModalWarnings["HEADER_DIRECTIVE_OUTSIDE_MODAL"] = "[ModalHeader] Directive used outside of a ModalComponent.";
|
|
926
|
+
//#endregion
|
|
927
|
+
})(ModalWarnings || (ModalWarnings = {}));
|
|
983
928
|
|
|
984
929
|
class ModalFooterDirective {
|
|
985
930
|
templateRef = inject(TemplateRef);
|
|
@@ -1011,5 +956,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
1011
956
|
* Generated bundle index. Do not edit.
|
|
1012
957
|
*/
|
|
1013
958
|
|
|
1014
|
-
export {
|
|
959
|
+
export { MODAL_DATA, MODAL_DEFAULT_ANIM_DURATION, MODAL_DEFAULT_ANIM_DURATION_MULTIPLIER_LARGE, MODAL_DEFAULT_ANIM_DURATION_MULTIPLIER_SMALL, MODAL_DEFAULT_INTERNAL_ANIM_DURATION, MODAL_DOWN_SWIPE_LIMIT, MODAL_SWIPE_VELOCITY_THRESHOLD, Modal, ModalBackdrop, ModalBanner, ModalBottomSheet, ModalCentered, ModalConfig, ModalCore, ModalFooterDirective, ModalRef, ModalService, ModalSide, ModalState, ModalStyleConfig, ModalWarnings };
|
|
1015
960
|
//# sourceMappingURL=filip.mazev-modal.mjs.map
|