ng-hub-ui-modal 1.2.4 → 22.1.1
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 +643 -249
- package/fesm2022/ng-hub-ui-modal.mjs +431 -211
- package/fesm2022/ng-hub-ui-modal.mjs.map +1 -1
- package/ng-hub-ui-modal-22.1.1.tgz +0 -0
- package/package.json +8 -8
- package/src/lib/styles/mixins/_modal-theme.scss +142 -0
- package/types/ng-hub-ui-modal.d.ts +546 -0
- package/esm2022/lib/modal-backdrop.mjs +0 -51
- package/esm2022/lib/modal-config.mjs +0 -32
- package/esm2022/lib/modal-dismiss-reasons.mjs +0 -6
- package/esm2022/lib/modal-ref.mjs +0 -216
- package/esm2022/lib/modal-stack.mjs +0 -321
- package/esm2022/lib/modal-window.mjs +0 -275
- package/esm2022/lib/modal.mjs +0 -59
- package/esm2022/lib/modal.module.mjs +0 -17
- package/esm2022/ng-hub-ui-modal.mjs +0 -5
- package/esm2022/public-api.mjs +0 -5
- package/index.d.ts +0 -5
- package/lib/modal-backdrop.d.ts +0 -13
- package/lib/modal-config.d.ts +0 -175
- package/lib/modal-dismiss-reasons.d.ts +0 -4
- package/lib/modal-ref.d.ts +0 -115
- package/lib/modal-stack.d.ts +0 -62
- package/lib/modal-window.d.ts +0 -39
- package/lib/modal.d.ts +0 -42
- package/lib/modal.module.d.ts +0 -10
- package/ng-hub-ui-modal-1.2.4.tgz +0 -0
- package/public-api.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,343 +1,737 @@
|
|
|
1
1
|
# ng-hub-ui-modal
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
[Español](./README.es.md) | **English**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/ng-hub-ui-modal)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://angular.io)
|
|
8
|
+
|
|
9
|
+
> A standalone, fully-featured Angular modal library with flexible content projection, placement support, and full CSS variable theming. No Bootstrap or ng-bootstrap dependency required.
|
|
10
|
+
|
|
11
|
+
> **⚠️ WARNING: BREAKING CHANGES IN VERSION 21.0.0**
|
|
12
|
+
> If you are upgrading from `1.x.x` to `21.x.x` and you have overridden the `.modal` or `.modal-dialog` CSS classes in your global stylesheets, please review the [BREAKING_CHANGES.md](./BREAKING_CHANGES.md) document to migrate your styles to the new `hub-modal` BEM classes.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Documentation and Live Examples
|
|
17
|
+
|
|
18
|
+
This package is part of [Hub UI](https://hubui.dev/), a collection of Angular component libraries for standalone apps.
|
|
19
|
+
|
|
20
|
+
- Docs: https://hubui.dev/modal/overview/
|
|
21
|
+
- Live examples: https://hubui.dev/modal/examples/
|
|
22
|
+
- Hub UI: https://hubui.dev/
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🧩 Library Family `ng-hub-ui`
|
|
27
|
+
|
|
28
|
+
This library is part of the **ng-hub-ui** ecosystem:
|
|
29
|
+
|
|
30
|
+
- [**ng-hub-ui-accordion**](https://www.npmjs.com/package/ng-hub-ui-accordion) _(deprecated — use ng-hub-ui-panels)_
|
|
31
|
+
- [**ng-hub-ui-action-sheet**](https://www.npmjs.com/package/ng-hub-ui-action-sheet)
|
|
32
|
+
- [**ng-hub-ui-avatar**](https://www.npmjs.com/package/ng-hub-ui-avatar)
|
|
33
|
+
- [**ng-hub-ui-board**](https://www.npmjs.com/package/ng-hub-ui-board)
|
|
34
|
+
- [**ng-hub-ui-breadcrumbs**](https://www.npmjs.com/package/ng-hub-ui-breadcrumbs)
|
|
35
|
+
- [**ng-hub-ui-calendar**](https://www.npmjs.com/package/ng-hub-ui-calendar)
|
|
36
|
+
- [**ng-hub-ui-dropdown**](https://www.npmjs.com/package/ng-hub-ui-dropdown)
|
|
37
|
+
- [**ng-hub-ui-ds**](https://www.npmjs.com/package/ng-hub-ui-ds)
|
|
38
|
+
- [**ng-hub-ui-forms**](https://www.npmjs.com/package/ng-hub-ui-forms)
|
|
39
|
+
- [**ng-hub-ui-history**](https://www.npmjs.com/package/ng-hub-ui-history)
|
|
40
|
+
- [**ng-hub-ui-milestones**](https://www.npmjs.com/package/ng-hub-ui-milestones)
|
|
41
|
+
- [**➡️ ng-hub-ui-modal**](https://www.npmjs.com/package/ng-hub-ui-modal) ← _you are here_
|
|
42
|
+
- [**ng-hub-ui-nav**](https://www.npmjs.com/package/ng-hub-ui-nav)
|
|
43
|
+
- [**ng-hub-ui-paginable**](https://www.npmjs.com/package/ng-hub-ui-paginable)
|
|
44
|
+
- [**ng-hub-ui-panels**](https://www.npmjs.com/package/ng-hub-ui-panels)
|
|
45
|
+
- [**ng-hub-ui-portal**](https://www.npmjs.com/package/ng-hub-ui-portal)
|
|
46
|
+
- [**ng-hub-ui-skeleton**](https://www.npmjs.com/package/ng-hub-ui-skeleton)
|
|
47
|
+
- [**ng-hub-ui-sortable**](https://www.npmjs.com/package/ng-hub-ui-sortable)
|
|
48
|
+
- [**ng-hub-ui-stepper**](https://www.npmjs.com/package/ng-hub-ui-stepper)
|
|
49
|
+
- [**ng-hub-ui-utils**](https://www.npmjs.com/package/ng-hub-ui-utils)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 📋 Table of Contents
|
|
54
|
+
|
|
55
|
+
- [Features](#features)
|
|
56
|
+
- [Installation](#installation)
|
|
57
|
+
- [Quick Start](#quick-start)
|
|
58
|
+
- [Examples](#examples)
|
|
59
|
+
- [Open with TemplateRef](#open-with-templateref)
|
|
60
|
+
- [Open with Component](#open-with-component)
|
|
61
|
+
- [Open with String](#open-with-string)
|
|
62
|
+
- [Placement](#placement)
|
|
63
|
+
- [Size and Fullscreen](#size-and-fullscreen)
|
|
64
|
+
- [Scrollable Content](#scrollable-content)
|
|
65
|
+
- [Static Backdrop](#static-backdrop)
|
|
66
|
+
- [Before Dismiss Guard](#before-dismiss-guard)
|
|
67
|
+
- [HubActiveModal in Content Component](#hubactivemodal-in-content-component)
|
|
68
|
+
- [Dismiss and Close Selectors](#dismiss-and-close-selectors)
|
|
69
|
+
- [Multiple Stacked Modals](#multiple-stacked-modals)
|
|
70
|
+
- [Observables: dismissAll and hasOpenModals](#observables-dismissall-and-hasopenmodals)
|
|
71
|
+
- [API Reference](#api-reference)
|
|
72
|
+
- [HubModal Service](#hubmodal-service)
|
|
73
|
+
- [HubModalRef](#hubmodalref)
|
|
74
|
+
- [HubActiveModal](#hubactivemodal-1)
|
|
75
|
+
- [HubModalOptions](#hubmodaloptions)
|
|
76
|
+
- [HubModalUpdatableOptions](#hubmodalupdatableoptions)
|
|
77
|
+
- [HubModalPlacement](#hubmodalplacement-1)
|
|
78
|
+
- [ModalDismissReasons](#modaldismissreasons)
|
|
79
|
+
- [HubModalConfig](#hubmodalconfig)
|
|
80
|
+
- [Styling](#styling)
|
|
81
|
+
- [Contributing](#contributing)
|
|
82
|
+
- [Support & License](#support--license)
|
|
83
|
+
|
|
84
|
+
---
|
|
8
85
|
|
|
9
86
|
## Features
|
|
10
87
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **Flexible
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
88
|
+
- **Zero external dependencies**: No ng-bootstrap, no Bootstrap JS.
|
|
89
|
+
- **Three content types**: Open modals with a `TemplateRef`, a `Component` class, or a plain `string`.
|
|
90
|
+
- **Flexible content projection**: Use CSS selectors to route content to `header`, `body`, and `footer` slots.
|
|
91
|
+
- **Placement support**: Anchor modals to any viewport edge — `start`, `end`, `top`, `bottom` — or keep them `center`.
|
|
92
|
+
- **Modal stacking**: Open multiple modals; focus management and aria-hidden are handled automatically.
|
|
93
|
+
- **Programmatic dismiss/close guards**: The `beforeDismiss` callback lets you intercept and prevent dismissal.
|
|
94
|
+
- **Full keyboard & backdrop interaction**: ESC key, static backdrop, backdrop click — all configurable.
|
|
95
|
+
- **CSS Variable theming**: Deep customization without overriding internal classes.
|
|
96
|
+
- **BEM class architecture**: All structural classes use the `hub-modal__*` prefix to avoid conflicts.
|
|
97
|
+
- **Lifecycle Observables**: `shown`, `hidden`, `closed`, `dismissed` streams for precise reactive flow.
|
|
98
|
+
- **Global defaults**: Inject `HubModalConfig` to set application-wide defaults.
|
|
99
|
+
|
|
100
|
+
---
|
|
17
101
|
|
|
18
102
|
## Installation
|
|
19
103
|
|
|
20
|
-
```
|
|
104
|
+
```bash
|
|
21
105
|
npm install ng-hub-ui-modal
|
|
22
106
|
```
|
|
23
107
|
|
|
24
|
-
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Quick Start
|
|
25
111
|
|
|
26
|
-
|
|
112
|
+
### Standalone (recommended)
|
|
27
113
|
|
|
28
114
|
```typescript
|
|
29
|
-
import {
|
|
115
|
+
import { Component, inject, TemplateRef } from '@angular/core';
|
|
116
|
+
import { HubModal } from 'ng-hub-ui-modal';
|
|
117
|
+
|
|
118
|
+
@Component({
|
|
119
|
+
selector: 'app-root',
|
|
120
|
+
standalone: true,
|
|
121
|
+
template: `
|
|
122
|
+
<button (click)="open(tpl)">Open Modal</button>
|
|
123
|
+
|
|
124
|
+
<ng-template #tpl let-close="close">
|
|
125
|
+
<div class="hub-modal__header"><h5>Hello!</h5></div>
|
|
126
|
+
<div class="hub-modal__body">Modal content goes here.</div>
|
|
127
|
+
<div class="hub-modal__footer">
|
|
128
|
+
<button (click)="close('done')">Close</button>
|
|
129
|
+
</div>
|
|
130
|
+
</ng-template>
|
|
131
|
+
`
|
|
132
|
+
})
|
|
133
|
+
export class AppComponent {
|
|
134
|
+
private modal = inject(HubModal);
|
|
135
|
+
|
|
136
|
+
open(tpl: TemplateRef<unknown>) {
|
|
137
|
+
this.modal
|
|
138
|
+
.open(tpl, { headerSelector: '.hub-modal__header', footerSelector: '.hub-modal__footer' })
|
|
139
|
+
.result.catch(() => {});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### NgModule (classic)
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { HubModalModule } from 'ng-hub-ui-modal';
|
|
30
148
|
|
|
31
149
|
@NgModule({
|
|
32
|
-
|
|
33
|
-
// ...
|
|
34
|
-
ModalModule
|
|
35
|
-
]
|
|
150
|
+
imports: [HubModalModule]
|
|
36
151
|
})
|
|
37
152
|
export class AppModule {}
|
|
38
153
|
```
|
|
39
154
|
|
|
40
|
-
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Examples
|
|
158
|
+
|
|
159
|
+
### Open with TemplateRef
|
|
160
|
+
|
|
161
|
+
Open a modal whose content is defined inline as a template.
|
|
162
|
+
The template context exposes `close` and `dismiss` functions.
|
|
41
163
|
|
|
42
164
|
```typescript
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
165
|
+
import { Component, inject, TemplateRef } from '@angular/core';
|
|
166
|
+
import { HubModal } from 'ng-hub-ui-modal';
|
|
167
|
+
|
|
168
|
+
@Component({
|
|
169
|
+
selector: 'app-example',
|
|
170
|
+
standalone: true,
|
|
171
|
+
template: `
|
|
172
|
+
<button (click)="open(tpl)">Open Template Modal</button>
|
|
173
|
+
|
|
174
|
+
<ng-template #tpl let-close="close" let-dismiss="dismiss">
|
|
175
|
+
<div class="hub-modal__body">
|
|
176
|
+
<p>This is a template modal.</p>
|
|
177
|
+
<button (click)="dismiss('cancel')">Cancel</button>
|
|
178
|
+
<button (click)="close('ok')">OK</button>
|
|
179
|
+
</div>
|
|
180
|
+
</ng-template>
|
|
181
|
+
`
|
|
182
|
+
})
|
|
183
|
+
export class TemplateModalComponent {
|
|
184
|
+
private modal = inject(HubModal);
|
|
185
|
+
|
|
186
|
+
open(tpl: TemplateRef<unknown>) {
|
|
187
|
+
this.modal
|
|
188
|
+
.open(tpl)
|
|
189
|
+
.result.then((result) => console.log('Closed with', result))
|
|
190
|
+
.catch((reason) => console.log('Dismissed:', reason));
|
|
191
|
+
}
|
|
57
192
|
}
|
|
58
193
|
```
|
|
59
194
|
|
|
60
|
-
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### Open with Component
|
|
198
|
+
|
|
199
|
+
Pass any Angular component class to display it inside the modal.
|
|
200
|
+
The component can inject `HubActiveModal` to close or dismiss the modal from within.
|
|
61
201
|
|
|
62
202
|
```typescript
|
|
63
|
-
import { Component } from '@angular/core';
|
|
203
|
+
import { Component, inject } from '@angular/core';
|
|
204
|
+
import { HubModal, HubActiveModal } from 'ng-hub-ui-modal';
|
|
64
205
|
|
|
206
|
+
/** Content component displayed inside the modal */
|
|
65
207
|
@Component({
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
`
|
|
208
|
+
selector: 'app-confirm-dialog',
|
|
209
|
+
standalone: true,
|
|
210
|
+
template: `
|
|
211
|
+
<div class="hub-modal__header"><h5>Confirm action</h5></div>
|
|
212
|
+
<div class="hub-modal__body">Are you sure you want to proceed?</div>
|
|
213
|
+
<div class="hub-modal__footer">
|
|
214
|
+
<button (click)="activeModal.dismiss('no')">Cancel</button>
|
|
215
|
+
<button (click)="activeModal.close(true)">Confirm</button>
|
|
216
|
+
</div>
|
|
217
|
+
`
|
|
77
218
|
})
|
|
78
|
-
export class
|
|
219
|
+
export class ConfirmDialogComponent {
|
|
220
|
+
activeModal = inject(HubActiveModal);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Host component that opens the modal */
|
|
224
|
+
@Component({ selector: 'app-host', standalone: true, template: `<button (click)="openConfirm()">Delete</button>` })
|
|
225
|
+
export class HostComponent {
|
|
226
|
+
private modal = inject(HubModal);
|
|
227
|
+
|
|
228
|
+
openConfirm() {
|
|
229
|
+
this.modal
|
|
230
|
+
.open(ConfirmDialogComponent, {
|
|
231
|
+
headerSelector: '.hub-modal__header',
|
|
232
|
+
footerSelector: '.hub-modal__footer'
|
|
233
|
+
})
|
|
234
|
+
.result.then((confirmed) => {
|
|
235
|
+
if (confirmed) {
|
|
236
|
+
/* perform deletion */
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
.catch(() => {});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
79
242
|
```
|
|
80
243
|
|
|
81
|
-
|
|
244
|
+
---
|
|
82
245
|
|
|
83
|
-
###
|
|
246
|
+
### Open with String
|
|
84
247
|
|
|
85
|
-
|
|
248
|
+
Display a quick text message without any additional component or template.
|
|
86
249
|
|
|
87
|
-
|
|
250
|
+
```typescript
|
|
251
|
+
this.modal.open('This is a simple string modal.');
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
88
255
|
|
|
89
|
-
|
|
256
|
+
### Placement
|
|
90
257
|
|
|
91
|
-
|
|
92
|
-
- `component` (`ComponentType<any>`): The component to be displayed in the modal.
|
|
93
|
-
- `options` (`ModalOptions` | *optional*): An object containing the configuration options for the modal.
|
|
258
|
+
Anchor the modal to any edge of the viewport using `HubModalPlacement`.
|
|
94
259
|
|
|
95
|
-
|
|
260
|
+
```typescript
|
|
261
|
+
import { HubModal, HubModalPlacement } from 'ng-hub-ui-modal';
|
|
96
262
|
|
|
97
|
-
|
|
263
|
+
// Right side panel
|
|
264
|
+
this.modal.open(MyComponent, { placement: HubModalPlacement.End });
|
|
98
265
|
|
|
99
|
-
|
|
266
|
+
// Bottom sheet
|
|
267
|
+
this.modal.open(MyComponent, { placement: HubModalPlacement.Bottom });
|
|
268
|
+
|
|
269
|
+
// Left drawer, vertically centered
|
|
270
|
+
this.modal.open(MyComponent, {
|
|
271
|
+
placement: HubModalPlacement.Start,
|
|
272
|
+
centered: true
|
|
273
|
+
});
|
|
274
|
+
```
|
|
100
275
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
276
|
+
| Value | Effect |
|
|
277
|
+
| -------------------------- | ----------------------------- |
|
|
278
|
+
| `HubModalPlacement.Center` | Centred in viewport (default) |
|
|
279
|
+
| `HubModalPlacement.Start` | Left-anchored drawer |
|
|
280
|
+
| `HubModalPlacement.End` | Right-anchored drawer |
|
|
281
|
+
| `HubModalPlacement.Top` | Top sheet |
|
|
282
|
+
| `HubModalPlacement.Bottom` | Bottom sheet |
|
|
106
283
|
|
|
107
|
-
|
|
284
|
+
---
|
|
108
285
|
|
|
109
|
-
|
|
286
|
+
### Size and Fullscreen
|
|
110
287
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
288
|
+
```typescript
|
|
289
|
+
// Predefined sizes
|
|
290
|
+
this.modal.open(MyComponent, { size: 'sm' }); // 'sm' | 'lg' | 'xl'
|
|
291
|
+
|
|
292
|
+
// Always fullscreen
|
|
293
|
+
this.modal.open(MyComponent, { fullscreen: true });
|
|
294
|
+
|
|
295
|
+
// Fullscreen only below 'md' breakpoint
|
|
296
|
+
this.modal.open(MyComponent, { fullscreen: 'md' });
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
### Scrollable Content
|
|
302
|
+
|
|
303
|
+
When the modal content overflows, enable internal scrolling.
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
this.modal.open(LongContentComponent, { scrollable: true });
|
|
307
|
+
```
|
|
114
308
|
|
|
115
|
-
|
|
309
|
+
---
|
|
116
310
|
|
|
117
|
-
|
|
311
|
+
### Static Backdrop
|
|
118
312
|
|
|
119
|
-
|
|
313
|
+
Prevent dismissal when clicking outside the modal.
|
|
120
314
|
|
|
121
315
|
```typescript
|
|
122
|
-
|
|
316
|
+
this.modal.open(MyComponent, { backdrop: 'static' });
|
|
317
|
+
|
|
318
|
+
// Also disable ESC key
|
|
319
|
+
this.modal.open(MyComponent, { backdrop: 'static', keyboard: false });
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
### Before Dismiss Guard
|
|
325
|
+
|
|
326
|
+
Use `beforeDismiss` to prevent or delay modal closure, e.g. to show a confirmation first.
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
this.modal.open(MyFormComponent, {
|
|
330
|
+
beforeDismiss: () => {
|
|
331
|
+
if (this.formIsDirty) {
|
|
332
|
+
return confirm('You have unsaved changes. Really close?');
|
|
333
|
+
}
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Async guard using a Promise
|
|
339
|
+
this.modal.open(MyComponent, {
|
|
340
|
+
beforeDismiss: () => this.confirmService.ask('Discard changes?')
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
### HubActiveModal in Content Component
|
|
347
|
+
|
|
348
|
+
Inject `HubActiveModal` into any component used as modal content to control it from within.
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
import { Component, inject } from '@angular/core';
|
|
352
|
+
import { HubActiveModal, HubModalUpdatableOptions } from 'ng-hub-ui-modal';
|
|
123
353
|
|
|
124
354
|
@Component({
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<div class="modal-body">
|
|
134
|
-
{{ body }}
|
|
135
|
-
</div>
|
|
136
|
-
<div class="modal-footer">
|
|
137
|
-
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
|
138
|
-
<button type="button" class="btn btn-primary" (click)="confirm()">Confirm</button>
|
|
139
|
-
</div>
|
|
140
|
-
`
|
|
355
|
+
selector: 'app-my-modal',
|
|
356
|
+
standalone: true,
|
|
357
|
+
template: `
|
|
358
|
+
<div class="hub-modal__body">
|
|
359
|
+
<button (click)="save()">Save</button>
|
|
360
|
+
<button (click)="cancel()">Cancel</button>
|
|
361
|
+
</div>
|
|
362
|
+
`
|
|
141
363
|
})
|
|
142
364
|
export class MyModalComponent {
|
|
143
|
-
|
|
144
|
-
body: string;
|
|
365
|
+
activeModal = inject(HubActiveModal);
|
|
145
366
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
367
|
+
save() {
|
|
368
|
+
this.activeModal.close({ saved: true });
|
|
369
|
+
}
|
|
150
370
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// Dismiss the modal
|
|
156
|
-
this.modalRef.dismiss('confirmed');
|
|
157
|
-
}
|
|
371
|
+
cancel() {
|
|
372
|
+
this.activeModal.dismiss('user_cancelled');
|
|
373
|
+
}
|
|
158
374
|
}
|
|
159
375
|
```
|
|
160
376
|
|
|
161
|
-
|
|
377
|
+
---
|
|
162
378
|
|
|
163
|
-
|
|
379
|
+
### Dismiss and Close Selectors
|
|
164
380
|
|
|
165
|
-
|
|
381
|
+
Automatically bind dismiss/close behaviour to DOM elements inside the modal content using CSS selectors.
|
|
166
382
|
|
|
167
|
-
|
|
383
|
+
```typescript
|
|
384
|
+
this.modal.open(MyComponent, {
|
|
385
|
+
dismissSelector: '[data-dismiss="modal"]',
|
|
386
|
+
closeSelector: '[data-close="modal"]'
|
|
387
|
+
});
|
|
388
|
+
```
|
|
168
389
|
|
|
169
390
|
```html
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
<div class="modal-header">
|
|
174
|
-
<!-- Header content projected here -->
|
|
175
|
-
</div>
|
|
176
|
-
<div class="modal-body">
|
|
177
|
-
<!-- Body content projected here -->
|
|
178
|
-
</div>
|
|
179
|
-
<div class="modal-footer">
|
|
180
|
-
<!-- Footer content projected here -->
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
184
|
-
</div>
|
|
391
|
+
<!-- Inside MyComponent template -->
|
|
392
|
+
<button data-dismiss="modal">Cancel</button>
|
|
393
|
+
<button data-close="modal">OK</button>
|
|
185
394
|
```
|
|
186
395
|
|
|
187
|
-
|
|
396
|
+
---
|
|
188
397
|
|
|
189
|
-
|
|
398
|
+
### Multiple Stacked Modals
|
|
190
399
|
|
|
191
|
-
|
|
400
|
+
Open modals from within a modal — the stack is managed automatically and focus is trapped to the topmost one.
|
|
192
401
|
|
|
193
402
|
```typescript
|
|
194
|
-
|
|
195
|
-
|
|
403
|
+
@Component({ ... })
|
|
404
|
+
export class ParentModalComponent {
|
|
405
|
+
private modal = inject(HubModal);
|
|
196
406
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
template: `
|
|
200
|
-
<button (click)="openModal()">Open Modal</button>
|
|
201
|
-
`
|
|
202
|
-
})
|
|
203
|
-
export class ExampleComponent {
|
|
204
|
-
constructor(private modalService: ModalService) {}
|
|
205
|
-
|
|
206
|
-
openModal() {
|
|
207
|
-
const modalRef = this.modalService.open(BasicModalComponent);
|
|
407
|
+
openNested() {
|
|
408
|
+
this.modal.open(ChildModalComponent);
|
|
208
409
|
}
|
|
209
410
|
}
|
|
411
|
+
```
|
|
210
412
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
### Observables: dismissAll and hasOpenModals
|
|
416
|
+
|
|
417
|
+
Use the service methods to interact with the entire modal stack.
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { HubModal } from 'ng-hub-ui-modal';
|
|
421
|
+
|
|
422
|
+
export class AppComponent {
|
|
423
|
+
private modal = inject(HubModal);
|
|
424
|
+
|
|
425
|
+
closeAll() {
|
|
426
|
+
this.modal.dismissAll('route_change');
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
get anyOpen(): boolean {
|
|
430
|
+
return this.modal.hasOpenModals();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
226
433
|
```
|
|
227
434
|
|
|
228
|
-
|
|
435
|
+
Listen to `activeInstances` for reactive updates:
|
|
229
436
|
|
|
230
437
|
```typescript
|
|
231
|
-
|
|
232
|
-
|
|
438
|
+
this.modal.activeInstances.subscribe((refs) => {
|
|
439
|
+
console.log(`${refs.length} modals open`);
|
|
440
|
+
});
|
|
441
|
+
```
|
|
233
442
|
|
|
234
|
-
|
|
235
|
-
selector: 'app-example',
|
|
236
|
-
template: `
|
|
237
|
-
<button (click)="openModal()">Open Modal</button>
|
|
238
|
-
`
|
|
239
|
-
})
|
|
240
|
-
export class ExampleComponent {
|
|
241
|
-
constructor(private modalService: ModalService) {}
|
|
443
|
+
---
|
|
242
444
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
445
|
+
## API Reference
|
|
446
|
+
|
|
447
|
+
### HubModal Service
|
|
448
|
+
|
|
449
|
+
The main entry point for opening and managing modals.
|
|
450
|
+
|
|
451
|
+
| Method | Signature | Description |
|
|
452
|
+
| ----------------- | -------------------------------------- | ----------------------------------------------------- |
|
|
453
|
+
| `open` | `open(content, options?): HubModalRef` | Opens a new modal with the given content and options. |
|
|
454
|
+
| `dismissAll` | `dismissAll(reason?): void` | Dismisses all currently open modals. |
|
|
455
|
+
| `hasOpenModals` | `hasOpenModals(): boolean` | Returns `true` if at least one modal is open. |
|
|
456
|
+
| `activeInstances` | `EventEmitter<HubModalRef[]>` | Emits whenever the stack of open modals changes. |
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
### HubModalRef
|
|
461
|
+
|
|
462
|
+
A reference to an open modal returned by `HubModal.open()`.
|
|
463
|
+
|
|
464
|
+
| Member | Type | Description |
|
|
465
|
+
| ------------------- | ------------------ | ----------------------------------------------------------- |
|
|
466
|
+
| `result` | `Promise<any>` | Resolves on `close()`, rejects on `dismiss()`. |
|
|
467
|
+
| `componentInstance` | `T \| void` | Instance of the content component (if used). |
|
|
468
|
+
| `close(result?)` | `void` | Closes the modal and resolves `result`. |
|
|
469
|
+
| `dismiss(reason?)` | `void` | Dismisses the modal and rejects `result`. |
|
|
470
|
+
| `update(options)` | `void` | Updates modal options after opening. |
|
|
471
|
+
| `closed` | `Observable<any>` | Emits when the modal is closed via `close()`. |
|
|
472
|
+
| `dismissed` | `Observable<any>` | Emits when dismissed via `dismiss()` or user interaction. |
|
|
473
|
+
| `shown` | `Observable<void>` | Emits once the open animation finishes. |
|
|
474
|
+
| `hidden` | `Observable<void>` | Emits once the close animation finishes and DOM is removed. |
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
### HubActiveModal
|
|
479
|
+
|
|
480
|
+
Inject into your content component to control the modal from within.
|
|
481
|
+
|
|
482
|
+
| Method | Description |
|
|
483
|
+
| ------------------ | ---------------------------------------------------- |
|
|
484
|
+
| `close(result?)` | Closes the modal with an optional result. |
|
|
485
|
+
| `dismiss(reason?)` | Dismisses the modal with an optional reason. |
|
|
486
|
+
| `update(options)` | Updates live options (same as `HubModalRef.update`). |
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
### HubModalOptions
|
|
491
|
+
|
|
492
|
+
All options accepted by `HubModal.open()`.
|
|
493
|
+
|
|
494
|
+
| Option | Type | Default | Description |
|
|
495
|
+
| ------------------ | ------------------------------------------------------------ | ------------------------ | ----------------------------------------------------------- |
|
|
496
|
+
| `animation` | `boolean` | `true` | Enables fade in/out transitions. |
|
|
497
|
+
| `ariaLabelledBy` | `string` | — | ID of the element that labels the modal. |
|
|
498
|
+
| `ariaDescribedBy` | `string` | — | ID of the element that describes the modal. |
|
|
499
|
+
| `backdrop` | `boolean \| 'static'` | `true` | `false` = no backdrop, `'static'` = click does not close. |
|
|
500
|
+
| `beforeDismiss` | `() => boolean \| Promise<boolean>` | — | Guard called before dismissal. Return `false` to cancel. |
|
|
501
|
+
| `centered` | `boolean` | `false` | Centers modal on the cross-axis for side placements. |
|
|
502
|
+
| `placement` | `HubModalPlacement` | `Center` | Viewport anchor for the modal. |
|
|
503
|
+
| `container` | `string \| HTMLElement` | `body` | CSS selector or element to which modals are appended. |
|
|
504
|
+
| `fullscreen` | `boolean \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'xxl' \| string` | `false` | Fullscreen always or below a specific breakpoint. |
|
|
505
|
+
| `injector` | `Injector` | — | Custom injector for content component dependencies. |
|
|
506
|
+
| `keyboard` | `boolean` | `true` | Whether ESC key dismisses the modal. |
|
|
507
|
+
| `scrollable` | `boolean` | `false` | Makes the modal body scroll internally. |
|
|
508
|
+
| `size` | `'sm' \| 'lg' \| 'xl' \| string` | — | Controls the width of the modal dialog. |
|
|
509
|
+
| `variant` | `'primary' \| 'success' \| 'danger' \| 'warning' \| 'info' \| string` | — | Semantic accent for meaningful dialogs: top accent bar + accent-tinted surface, borders and title. Any custom string reads `--hub-sys-color-<variant>` from the host. |
|
|
510
|
+
| `windowClass` | `string` | — | Extra class added to the `hub-modal` host element. |
|
|
511
|
+
| `modalDialogClass` | `string` | — | Extra class added to the `hub-modal__dialog` element. |
|
|
512
|
+
| `backdropClass` | `string` | — | Extra class added to the `hub-modal__backdrop` element. |
|
|
513
|
+
| `headerSelector` | `string` | — | CSS selector for nodes to project into the header slot. |
|
|
514
|
+
| `footerSelector` | `string` | — | CSS selector for nodes to project into the footer slot. |
|
|
515
|
+
| `dismissSelector` | `string` | `[data-dismiss="modal"]` | Selector for elements that auto-dismiss the modal on click. |
|
|
516
|
+
| `closeSelector` | `string` | `[data-close="modal"]` | Selector for elements that auto-close the modal on click. |
|
|
517
|
+
| `data` | `any` | — | Arbitrary data bound to the content component instance. |
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
### HubModalUpdatableOptions
|
|
522
|
+
|
|
523
|
+
A subset of `HubModalOptions` that can be updated on an already-open modal via `HubModalRef.update()`.
|
|
524
|
+
|
|
525
|
+
`ariaLabelledBy`, `ariaDescribedBy`, `centered`, `placement`, `fullscreen`, `backdropClass`, `size`, `variant`, `windowClass`, `modalDialogClass`.
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
### HubModalPlacement
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
import { HubModalPlacement } from 'ng-hub-ui-modal';
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
| Value | CSS class applied | Description |
|
|
536
|
+
| -------------------------- | ----------------------------- | ---------------------------------------- |
|
|
537
|
+
| `HubModalPlacement.Center` | _(none)_ | Modal centred in the viewport (default). |
|
|
538
|
+
| `HubModalPlacement.Start` | `hub-modal--placement-start` | Left edge anchor. |
|
|
539
|
+
| `HubModalPlacement.End` | `hub-modal--placement-end` | Right edge anchor. |
|
|
540
|
+
| `HubModalPlacement.Top` | `hub-modal--placement-top` | Top edge anchor. |
|
|
541
|
+
| `HubModalPlacement.Bottom` | `hub-modal--placement-bottom` | Bottom edge anchor. |
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
### ModalDismissReasons
|
|
546
|
+
|
|
547
|
+
Built-in dismiss reason constants.
|
|
548
|
+
|
|
549
|
+
```typescript
|
|
550
|
+
import { ModalDismissReasons } from 'ng-hub-ui-modal';
|
|
551
|
+
|
|
552
|
+
modalRef.dismissed.subscribe((reason) => {
|
|
553
|
+
if (reason === ModalDismissReasons.ESC) {
|
|
554
|
+
/* ESC key */
|
|
555
|
+
}
|
|
556
|
+
if (reason === ModalDismissReasons.BACKDROP_CLICK) {
|
|
557
|
+
/* backdrop */
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
|
|
564
|
+
### HubModalConfig
|
|
565
|
+
|
|
566
|
+
Inject `HubModalConfig` to provide application-wide default options.
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
import { HubModalConfig } from 'ng-hub-ui-modal';
|
|
570
|
+
|
|
571
|
+
@Injectable({ providedIn: 'root' })
|
|
572
|
+
export class AppModalDefaults {
|
|
573
|
+
constructor(config: HubModalConfig) {
|
|
574
|
+
config.animation = true;
|
|
575
|
+
config.keyboard = false;
|
|
576
|
+
config.backdrop = 'static';
|
|
577
|
+
}
|
|
248
578
|
}
|
|
579
|
+
```
|
|
249
580
|
|
|
250
|
-
|
|
251
|
-
selector: 'app-data-modal',
|
|
252
|
-
template: `
|
|
253
|
-
<div class="modal-header">
|
|
254
|
-
<h4 class="modal-title">Modal with Data</h4>
|
|
255
|
-
</div>
|
|
256
|
-
<div class="modal-body">
|
|
257
|
-
Hello, {{ name }}!
|
|
258
|
-
</div>
|
|
259
|
-
<div class="modal-footer">
|
|
260
|
-
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
|
261
|
-
</div>
|
|
262
|
-
`
|
|
263
|
-
})
|
|
264
|
-
export class DataModalComponent {
|
|
265
|
-
name: string;
|
|
581
|
+
---
|
|
266
582
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
583
|
+
## Styling
|
|
584
|
+
|
|
585
|
+
The library publishes a self-contained stylesheet. Import it once in your application:
|
|
586
|
+
|
|
587
|
+
```scss
|
|
588
|
+
@import 'ng-hub-ui-modal/src/lib/modal.scss';
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### CSS Variables
|
|
592
|
+
|
|
593
|
+
All visual aspects are controlled via `--hub-modal-*` tokens.
|
|
594
|
+
Full reference: [docs/css-variables-reference.md](./docs/css-variables-reference.md)
|
|
595
|
+
|
|
596
|
+
**Quick reference (most common tokens):**
|
|
597
|
+
|
|
598
|
+
| Variable | Default | Description |
|
|
599
|
+
| ------------------------------ | ------------------ | ------------------------- |
|
|
600
|
+
| `--hub-modal-max-width` | `500px` | Max dialog width |
|
|
601
|
+
| `--hub-modal-border-radius` | `0.5rem` | Dialog corner radius |
|
|
602
|
+
| `--hub-modal-bg` | system surface | Background color |
|
|
603
|
+
| `--hub-modal-color` | system text | Text color |
|
|
604
|
+
| `--hub-modal-padding-x` | `1rem` | Content horizontal padding|
|
|
605
|
+
| `--hub-modal-padding-y` | `1rem` | Content vertical padding |
|
|
606
|
+
| `--hub-modal-backdrop-opacity` | `0.5` | Backdrop opacity |
|
|
607
|
+
| `--hub-modal-transition` | `0.2s ease-in-out` | Animation speed |
|
|
608
|
+
|
|
609
|
+
### Customization Example
|
|
610
|
+
|
|
611
|
+
```scss
|
|
612
|
+
/* Override at the host element level */
|
|
613
|
+
hub-modal-window {
|
|
614
|
+
--hub-modal-max-width: 720px;
|
|
615
|
+
--hub-modal-border-radius: 1rem;
|
|
616
|
+
--hub-modal-backdrop-opacity: 0.7;
|
|
270
617
|
}
|
|
271
618
|
```
|
|
272
619
|
|
|
273
|
-
###
|
|
620
|
+
### Semantic Variants
|
|
621
|
+
|
|
622
|
+
Set `variant` to give a dialog a semantic accent (a destructive confirm, a success notice…). A variant recolours the whole dialog: a top accent bar, an accent-tinted background, accent-tinted borders (outer + header/footer rules) and an accent title.
|
|
274
623
|
|
|
275
624
|
```typescript
|
|
276
|
-
|
|
277
|
-
|
|
625
|
+
this.modal.open(ConfirmDialogComponent, { variant: 'danger' });
|
|
626
|
+
```
|
|
278
627
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
});
|
|
307
|
-
}
|
|
628
|
+
The built-in values (`primary` · `success` · `danger` · `warning` · `info`) map to the design-system colours, but **any string is accepted** — the modal reads `--hub-sys-color-<variant>` from the host application. The variant is also updatable via `HubModalRef.update()` / `HubActiveModal.update()`, and can be applied directly with `windowClass: 'hub-modal--<variant>'`.
|
|
629
|
+
|
|
630
|
+
These tokens drive the accent system:
|
|
631
|
+
|
|
632
|
+
| Variable | Default | Description |
|
|
633
|
+
| ------------------------------ | -------------------------------------- | --------------------------------------------------------------------- |
|
|
634
|
+
| `--hub-modal-accent` | `var(--hub-sys-color-primary)` | Base accent colour; a variant re-bases it from `--hub-sys-color-<v>`. |
|
|
635
|
+
| `--hub-modal-accent-subtle` | `color-mix(accent 8%, surface)` | Accent-tinted dialog background used under a variant. |
|
|
636
|
+
| `--hub-modal-accent-border` | `color-mix(accent 35%, surface)` | Accent-tinted border colour (outer + header/footer rules). |
|
|
637
|
+
| `--hub-modal-accent-bar-width` | `var(--hub-ref-space-1, 4px)` | Thickness of the top accent bar. |
|
|
638
|
+
| `--hub-modal-title-color` | neutral (`--hub-modal-color`) | Title colour; a variant re-points it to the accent. |
|
|
639
|
+
|
|
640
|
+
### Sass theme mixin
|
|
641
|
+
|
|
642
|
+
For full one-call theming, use the `hub-modal-theme()` mixin. Every parameter is optional and defaults to `null`, so only the ones you pass are emitted as `--hub-modal-*` overrides; the rest keep their defaults. Apply it to the class you pass as `windowClass` (or to `.hub-modal` to theme every dialog).
|
|
643
|
+
|
|
644
|
+
```scss
|
|
645
|
+
@use 'ng-hub-ui-modal/styles/mixins/modal-theme' as *;
|
|
646
|
+
|
|
647
|
+
.branded-dialog {
|
|
648
|
+
@include hub-modal-theme(
|
|
649
|
+
$accent: var(--hub-sys-color-success),
|
|
650
|
+
$bg: #f6fff9,
|
|
651
|
+
$border-color: #b7e4c7,
|
|
652
|
+
$border-radius: 0.75rem,
|
|
653
|
+
$box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.2)
|
|
654
|
+
);
|
|
308
655
|
}
|
|
656
|
+
|
|
657
|
+
// this.modal.open(MyDialog, { windowClass: 'branded-dialog' });
|
|
309
658
|
```
|
|
310
659
|
|
|
311
|
-
|
|
660
|
+
It covers accent, surfaces, colour, title, borders/radius/shadow, header/body/footer padding & gaps and the backdrop — token-based, with no Bootstrap dependency.
|
|
312
661
|
|
|
313
|
-
|
|
314
|
-
import { Component } from '@angular/core';
|
|
315
|
-
import { ModalService } from 'ng-hub-ui-modal';
|
|
662
|
+
### Bootstrap Integration (optional)
|
|
316
663
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
<ng-template modalContent>
|
|
323
|
-
<div class="modal-header">
|
|
324
|
-
<h4 class="modal-title">Modal with Dismiss Trigger</h4>
|
|
325
|
-
</div>
|
|
326
|
-
<div class="modal-body">
|
|
327
|
-
<p>Click the button below to dismiss the modal.</p>
|
|
328
|
-
<button type="button" class="btn btn-primary" myDismissTrigger>Dismiss</button>
|
|
329
|
-
</div>
|
|
330
|
-
</ng-template>
|
|
331
|
-
`
|
|
332
|
-
})
|
|
333
|
-
export class ExampleComponent {
|
|
334
|
-
constructor(private modalService: ModalService) {}
|
|
335
|
-
|
|
336
|
-
openModal() {
|
|
337
|
-
const modalRef = this.modalService.open(null, {
|
|
338
|
-
bodySelector: '[modalContent]',
|
|
339
|
-
dismissSelector: '[myDismissTrigger]'
|
|
340
|
-
});
|
|
341
|
-
}
|
|
664
|
+
```scss
|
|
665
|
+
hub-modal-window {
|
|
666
|
+
--hub-modal-bg: var(--bs-body-bg);
|
|
667
|
+
--hub-modal-color: var(--bs-body-color);
|
|
668
|
+
--hub-modal-border-color: var(--bs-border-color);
|
|
342
669
|
}
|
|
343
|
-
```
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### BEM Class Reference
|
|
673
|
+
|
|
674
|
+
| Class | Element |
|
|
675
|
+
| -------------------------------- | --------------------- |
|
|
676
|
+
| `.hub-modal` | Modal window host |
|
|
677
|
+
| `.hub-modal__backdrop` | Backdrop overlay |
|
|
678
|
+
| `.hub-modal__dialog` | Dialog container |
|
|
679
|
+
| `.hub-modal__content` | Content wrapper |
|
|
680
|
+
| `.hub-modal__header` | Header region |
|
|
681
|
+
| `.hub-modal__body` | Body region |
|
|
682
|
+
| `.hub-modal__footer` | Footer region |
|
|
683
|
+
| `.hub-modal__close` | Built-in close button |
|
|
684
|
+
| `.hub-modal--placement-{value}` | Placement modifier |
|
|
685
|
+
| `.hub-modal__dialog--centered` | Vertical centering |
|
|
686
|
+
| `.hub-modal__dialog--scrollable` | Scrollable body |
|
|
687
|
+
| `.hub-modal__dialog--fullscreen` | Fullscreen modifier |
|
|
688
|
+
|
|
689
|
+
---
|
|
690
|
+
|
|
691
|
+
## Contributing
|
|
692
|
+
|
|
693
|
+
### Development Setup
|
|
694
|
+
|
|
695
|
+
```bash
|
|
696
|
+
git clone https://github.com/carlos-morcillo/ng-hub-ui-modal.git
|
|
697
|
+
cd ng-hub-ui-modal
|
|
698
|
+
npm install
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
Build the library in watch mode:
|
|
702
|
+
|
|
703
|
+
```bash
|
|
704
|
+
ng build modal --watch
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
Serve the demo application:
|
|
708
|
+
|
|
709
|
+
```bash
|
|
710
|
+
ng serve
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### Testing
|
|
714
|
+
|
|
715
|
+
```bash
|
|
716
|
+
ng test modal
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
### Commit Guidelines
|
|
720
|
+
|
|
721
|
+
Commits follow the [Conventional Commits](https://www.conventionalcommits.org/) format:
|
|
722
|
+
|
|
723
|
+
```
|
|
724
|
+
feat(modal): add new placement option
|
|
725
|
+
fix(modal): correct backdrop z-index
|
|
726
|
+
docs(modal): update CSS variable table
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
---
|
|
730
|
+
|
|
731
|
+
## Support & License
|
|
732
|
+
|
|
733
|
+
If this library saves you time, consider supporting further development:
|
|
734
|
+
|
|
735
|
+
☕ [Buy me a coffee](https://www.buymeacoffee.com/carlosmorcillo)
|
|
736
|
+
|
|
737
|
+
**MIT License** — © [Carlos Morcillo](https://www.carlosmorcillo.com)
|