@open-rlb/ng-bootstrap 3.3.24 → 3.3.26

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.
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: rlb-design
3
+ description: Bootstrap 5.3 + @open-rlb/ng-bootstrap design guidance for layout, spacing, typography, color palette, and responsive design. Use when making visual or design decisions for UIs in this project.
4
+ ---
5
+
6
+ # RLB Bootstrap 5.3 Design Skill
7
+
8
+ You are an expert in designing Angular UIs using **Bootstrap 5.3** with the **@open-rlb/ng-bootstrap** component library. This skill guides layout, spacing, typography, color, and responsive design decisions.
9
+
10
+ ## Bootstrap 5.3 Color Palette
11
+
12
+ The library uses a typed `Color` union:
13
+ ```typescript
14
+ type Color = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark';
15
+ ```
16
+
17
+ All components that accept a `color` input use this type.
18
+
19
+ ## Layout & Grid
20
+
21
+ Use Bootstrap's 12-column grid system:
22
+ ```html
23
+ <div class="container">
24
+ <div class="row">
25
+ <div class="col-12 col-md-6 col-lg-4">...</div>
26
+ </div>
27
+ </div>
28
+ ```
29
+
30
+ Breakpoints: `xs` (default), `sm` (576px), `md` (768px), `lg` (992px), `xl` (1200px), `xxl` (1400px).
31
+
32
+ ## Spacing
33
+
34
+ Use Bootstrap spacing utilities `m-{size}`, `p-{size}`, `gap-{size}` (0–5, auto).
35
+ Directional: `mt`, `mb`, `ms`, `me`, `mx`, `my`, `pt`, `pb`, `ps`, `pe`, `px`, `py`.
36
+
37
+ ## Typography
38
+
39
+ - Headings: `h1`–`h6`, `.display-1`–`.display-6`
40
+ - Text utilities: `fw-bold`, `fw-semibold`, `fst-italic`, `text-{color}`, `text-{align}`
41
+ - Size: `fs-1`–`fs-6`
42
+ - Truncation: `text-truncate`, `text-break`
43
+
44
+ ## Flexbox & Grid Utilities
45
+
46
+ ```html
47
+ <div class="d-flex align-items-center justify-content-between gap-2">...</div>
48
+ <div class="d-grid gap-2">...</div>
49
+ ```
50
+
51
+ ## Background & Border
52
+
53
+ - Background: `bg-{color}`, `bg-opacity-{10|25|50|75|100}`
54
+ - Border: `border`, `border-{color}`, `border-{size}`, `rounded`, `rounded-{size}`, `rounded-pill`
55
+ - Shadow: `shadow-sm`, `shadow`, `shadow-lg`
56
+
57
+ ## Sizing Utilities
58
+
59
+ - Width: `w-25`, `w-50`, `w-75`, `w-100`, `w-auto`, `mw-100`, `vw-100`
60
+ - Height: `h-25`, `h-50`, `h-75`, `h-100`, `h-auto`, `vh-100`, `min-vh-100`
61
+
62
+ ## Overflow & Position
63
+
64
+ - `overflow-auto`, `overflow-hidden`, `overflow-scroll`
65
+ - `position-relative`, `position-absolute`, `position-fixed`, `position-sticky`
66
+ - `top-0`, `start-0`, `bottom-0`, `end-0`, `translate-middle`
67
+
68
+ ## Visibility & Display
69
+
70
+ - `d-none`, `d-{bp}-block`, `d-{bp}-flex`, `d-{bp}-none`
71
+ - `visible`, `invisible`
72
+
73
+ ## RLB Theme Conventions
74
+
75
+ - Use `color="primary"` for main actions, `color="secondary"` for secondary.
76
+ - Use `color="danger"` for destructive actions (delete, remove).
77
+ - Use `color="success"` / `color="warning"` / `color="info"` for status feedback.
78
+ - Prefer `outline` buttons for secondary actions inside cards or toolbars.
79
+ - Use `size="sm"` for table rows and dense UIs; `size="lg"` for hero sections.
80
+
81
+ ## Page Layout Pattern
82
+
83
+ ```html
84
+ <div class="container-fluid p-0 d-flex flex-column min-vh-100">
85
+ <rlb-navbar>...</rlb-navbar>
86
+ <div class="d-flex flex-grow-1 overflow-hidden">
87
+ <rlb-sidebar>...</rlb-sidebar>
88
+ <main class="flex-grow-1 overflow-auto p-3">
89
+ <!-- page content -->
90
+ </main>
91
+ </div>
92
+ </div>
93
+ ```
94
+
95
+ ## Card-based Sections
96
+
97
+ ```html
98
+ <div class="row g-3">
99
+ <div class="col-12 col-md-6 col-xl-4">
100
+ <rlb-card>
101
+ <rlb-card-header>Title</rlb-card-header>
102
+ <rlb-card-body>Content</rlb-card-body>
103
+ </rlb-card>
104
+ </div>
105
+ </div>
106
+ ```
107
+
108
+ ## Responsive Helpers
109
+
110
+ - Hide on mobile: `d-none d-md-block`
111
+ - Show only on mobile: `d-block d-md-none`
112
+ - Stack to row on md+: `flex-column flex-md-row`
113
+
114
+ ## Design Principles
115
+
116
+ 1. Mobile-first: design for small screens, enhance for larger ones.
117
+ 2. Prefer utility classes over custom CSS.
118
+ 3. Use semantic HTML (`main`, `section`, `nav`, `aside`).
119
+ 4. Keep component `color` consistent with the app's design tokens.
120
+ 5. Use `gap-*` on flex/grid containers instead of margin on children.
@@ -0,0 +1,326 @@
1
+ ---
2
+ name: rlb-inputs
3
+ description: Expert guidance for @open-rlb/ng-bootstrap form input components (ControlValueAccessor-based, integrate with reactive and template-driven forms). Use when building forms or using input components.
4
+ ---
5
+
6
+ # RLB ng-Bootstrap Form Inputs Skill
7
+
8
+ You are an expert in the **@open-rlb/ng-bootstrap** form input components. All inputs implement `ControlValueAccessor` and integrate seamlessly with Angular Reactive Forms and Template-driven forms. They use Angular 18+ signals and `ChangeDetectionStrategy.OnPush`.
9
+
10
+ ## Common Pattern
11
+
12
+ All inputs share these base inputs:
13
+ - `disabled: boolean` — disables the control
14
+ - `readonly: boolean` — read-only display
15
+ - `size: 'small' | 'large' | undefined` — field size variant
16
+ - `id: string` — HTML id (auto-generated if omitted)
17
+ - `enable-validation: boolean` — show Bootstrap validation styles (invalid/valid)
18
+
19
+ All inputs bind via `formControlName` or `[(ngModel)]`.
20
+
21
+ ---
22
+
23
+ ## rlb-input — Text / Number / Date
24
+
25
+ ```html
26
+ <!-- Text -->
27
+ <rlb-input formControlName="username" placeholder="Enter username" [enable-validation]="true"></rlb-input>
28
+
29
+ <!-- Number with bounds -->
30
+ <rlb-input formControlName="age" type="number" [min]="0" [max]="120" [step]="1"></rlb-input>
31
+
32
+ <!-- Date (stores as DateTz, timezone-aware) -->
33
+ <rlb-input formControlName="birthDate" type="datetime-local" date-type="date-tz" timezone="Europe/Rome"></rlb-input>
34
+
35
+ <!-- Date stored as ISO string -->
36
+ <rlb-input formControlName="startDate" type="datetime-local" date-type="string" timezone="UTC"></rlb-input>
37
+
38
+ <!-- Password -->
39
+ <rlb-input formControlName="password" type="password" [enable-validation]="true"></rlb-input>
40
+ ```
41
+
42
+ **Inputs:**
43
+ | Input | Type | Default | Description |
44
+ |-------|------|---------|-------------|
45
+ | `type` | `string` | `'text'` | HTML input type |
46
+ | `placeholder` | `string` | — | Placeholder text |
47
+ | `size` | `'small'\|'large'` | — | Field size |
48
+ | `min` / `max` / `step` | `number` | — | Number constraints |
49
+ | `date-type` | `'date'\|'string'\|'number'\|'date-tz'` | `'date-tz'` | Date value format |
50
+ | `timezone` | `string` | `'UTC'` | Timezone for date handling |
51
+ | `readonly` | `boolean` | `false` | — |
52
+ | `disabled` | `boolean` | `false` | — |
53
+ | `enable-validation` | `boolean` | `false` | Show validation state |
54
+ | `ext-validation` | `boolean` | `false` | External validation display |
55
+
56
+ ---
57
+
58
+ ## rlb-select — Dropdown Select
59
+
60
+ ```html
61
+ <!-- Single select -->
62
+ <rlb-select formControlName="country" placeholder="Choose country" [enable-validation]="true">
63
+ <rlb-option value="it">Italy</rlb-option>
64
+ <rlb-option value="de">Germany</rlb-option>
65
+ <rlb-option value="fr">France</rlb-option>
66
+ </rlb-select>
67
+
68
+ <!-- Multiple select -->
69
+ <rlb-select formControlName="roles" [multiple]="true" [display]="5">
70
+ <rlb-option value="admin">Admin</rlb-option>
71
+ <rlb-option value="editor">Editor</rlb-option>
72
+ <rlb-option value="viewer">Viewer</rlb-option>
73
+ </rlb-select>
74
+ ```
75
+
76
+ **Inputs:** `placeholder`, `size`, `disabled`, `readonly`, `multiple`, `display` (visible rows for multiple), `inputId`, `enable-validation`
77
+ **Returns:** `string` (single) or `string[]` (multiple)
78
+
79
+ ---
80
+
81
+ ## rlb-checkbox — Checkbox
82
+
83
+ ```html
84
+ <rlb-checkbox formControlName="accepted" id="terms">
85
+ I accept the terms and conditions
86
+ </rlb-checkbox>
87
+
88
+ <!-- Indeterminate state -->
89
+ <rlb-checkbox formControlName="selectAll" [indeterminate]="someSelected" id="select-all">
90
+ Select all
91
+ </rlb-checkbox>
92
+ ```
93
+
94
+ **Inputs:** `disabled`, `readonly`, `indeterminate`, `id`
95
+ **Returns:** `boolean | undefined`
96
+
97
+ ---
98
+
99
+ ## rlb-switch — Toggle Switch
100
+
101
+ ```html
102
+ <rlb-switch formControlName="notifications" id="notif-toggle">
103
+ Enable notifications
104
+ </rlb-switch>
105
+ ```
106
+
107
+ **Inputs:** `disabled`, `readonly`, `size`, `id`
108
+ **Returns:** `boolean`
109
+
110
+ ---
111
+
112
+ ## rlb-radio — Radio Group
113
+
114
+ ```html
115
+ <rlb-radio formControlName="gender" id="gender">
116
+ <rlb-option value="m">Male</rlb-option>
117
+ <rlb-option value="f">Female</rlb-option>
118
+ <rlb-option value="o">Other</rlb-option>
119
+ </rlb-radio>
120
+ ```
121
+
122
+ **Inputs:** `disabled`, `readonly`, `id`
123
+ **Returns:** `string`
124
+
125
+ ---
126
+
127
+ ## rlb-textarea — Multi-line Text
128
+
129
+ ```html
130
+ <rlb-textarea formControlName="notes" placeholder="Add notes..." [rows]="5" [enable-validation]="true"></rlb-textarea>
131
+ ```
132
+
133
+ **Inputs:** `disabled`, `readonly`, `placeholder`, `size`, `rows` (default: 3), `id`
134
+
135
+ ---
136
+
137
+ ## rlb-autocomplete — Searchable Input
138
+
139
+ ```html
140
+ <rlb-autocomplete
141
+ formControlName="city"
142
+ placeholder="Search city..."
143
+ [autocomplete]="searchCities"
144
+ [chars-to-search]="2"
145
+ [loading]="isSearching"
146
+ [enable-validation]="true"
147
+ (selected)="onCitySelected($event)"
148
+ ></rlb-autocomplete>
149
+ ```
150
+
151
+ ```typescript
152
+ // Search function — supports sync array, Promise, or Observable
153
+ searchCities: AutocompleteFn = (query: string) => {
154
+ return this.cityService.search(query); // returns Observable<AutocompleteItem[]>
155
+ };
156
+
157
+ // AutocompleteItem interface
158
+ interface AutocompleteItem {
159
+ label: string;
160
+ value: any;
161
+ icon?: string;
162
+ }
163
+ ```
164
+
165
+ **Inputs:**
166
+ | Input | Type | Default | Description |
167
+ |-------|------|---------|-------------|
168
+ | `autocomplete` | `AutocompleteFn` | — | Search function (required) |
169
+ | `placeholder` | `string` | — | — |
170
+ | `chars-to-search` | `number` | `3` | Min chars before search |
171
+ | `loading` | `boolean` | `false` | Show loading indicator |
172
+ | `max-height` | `number` | `200` | Dropdown max height (px) |
173
+ | `type` | `string` | `'text'` | Input type |
174
+ | `size` | `'small'\|'large'` | — | — |
175
+ | `menu-max-width` | `number\|null` | — | Dropdown max width |
176
+ | `input-autocomplete` | `string` | `'off'` | HTML autocomplete attr |
177
+ | `enable-validation` | `boolean` | `false` | — |
178
+
179
+ **Outputs:** `selected: AutocompleteItem`
180
+ **Note:** Debounces search by 300ms automatically.
181
+
182
+ ---
183
+
184
+ ## rlb-file — File Upload
185
+
186
+ ```html
187
+ <!-- Single file -->
188
+ <rlb-file formControlName="document" accept=".pdf,.doc" [enable-validation]="true"></rlb-file>
189
+
190
+ <!-- Multiple files -->
191
+ <rlb-file formControlName="images" [multiple]="true" accept="image/*"></rlb-file>
192
+ ```
193
+
194
+ **Inputs:** `disabled`, `readonly`, `multiple`, `size`, `accept`, `id`
195
+ **Returns:** `File | File[] | null`
196
+
197
+ ---
198
+
199
+ ## rlb-dnd-file — Drag & Drop File
200
+
201
+ ```html
202
+ <rlb-dnd-file formControlName="upload" [multiple]="true" accept="image/*"></rlb-dnd-file>
203
+ ```
204
+
205
+ ---
206
+
207
+ ## rlb-color — Color Picker
208
+
209
+ ```html
210
+ <rlb-color formControlName="themeColor"></rlb-color>
211
+ ```
212
+
213
+ **Returns:** `string` (hex color, e.g. `#ff0000`)
214
+
215
+ ---
216
+
217
+ ## rlb-range — Range Slider
218
+
219
+ ```html
220
+ <rlb-range formControlName="volume" [min]="0" [max]="100" [step]="5"></rlb-range>
221
+ ```
222
+
223
+ ---
224
+
225
+ ## rlb-input-group — Input with Addons
226
+
227
+ ```html
228
+ <rlb-input-group>
229
+ <span class="input-group-text">@</span>
230
+ <rlb-input formControlName="username"></rlb-input>
231
+ </rlb-input-group>
232
+
233
+ <rlb-input-group>
234
+ <rlb-input formControlName="amount" type="number"></rlb-input>
235
+ <span class="input-group-text">EUR</span>
236
+ </rlb-input-group>
237
+ ```
238
+
239
+ ---
240
+
241
+ ## rlb-input-validation — Validation Messages
242
+
243
+ ```html
244
+ <rlb-input-validation [control]="form.get('email')">
245
+ <ng-container *rlbError="'required'">Email is required</ng-container>
246
+ <ng-container *rlbError="'email'">Invalid email format</ng-container>
247
+ </rlb-input-validation>
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Form Field Wrapper (rlb-form-fields)
253
+
254
+ ```html
255
+ <rlb-form-field label="Email" [required]="true">
256
+ <rlb-input formControlName="email" type="email" [enable-validation]="true"></rlb-input>
257
+ </rlb-form-field>
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Reactive Form Integration Pattern
263
+
264
+ ```typescript
265
+ import { FormBuilder, Validators } from '@angular/forms';
266
+
267
+ @Component({
268
+ template: `
269
+ <form [formGroup]="form" (ngSubmit)="onSubmit()">
270
+ <div class="mb-3">
271
+ <label class="form-label">Name *</label>
272
+ <rlb-input formControlName="name" [enable-validation]="true"></rlb-input>
273
+ </div>
274
+ <div class="mb-3">
275
+ <label class="form-label">Role</label>
276
+ <rlb-select formControlName="role" [enable-validation]="true">
277
+ <rlb-option value="admin">Admin</rlb-option>
278
+ <rlb-option value="user">User</rlb-option>
279
+ </rlb-select>
280
+ </div>
281
+ <div class="mb-3">
282
+ <rlb-checkbox formControlName="active" id="active-check">Active</rlb-checkbox>
283
+ </div>
284
+ <button rlb-button type="submit" color="primary" [disabled]="form.invalid">Save</button>
285
+ </form>
286
+ `
287
+ })
288
+ export class MyFormComponent {
289
+ form = this.fb.group({
290
+ name: ['', Validators.required],
291
+ role: ['user', Validators.required],
292
+ active: [true]
293
+ });
294
+
295
+ constructor(private fb: FormBuilder) {}
296
+
297
+ onSubmit() {
298
+ if (this.form.valid) {
299
+ console.log(this.form.value);
300
+ }
301
+ }
302
+ }
303
+ ```
304
+
305
+ ---
306
+
307
+ ## Validators
308
+
309
+ The library provides custom validators:
310
+ ```typescript
311
+ import { RequiredAutocompleteValidator } from '@open-rlb/ng-bootstrap';
312
+
313
+ form = this.fb.group({
314
+ city: ['', [Validators.required, RequiredAutocompleteValidator]]
315
+ });
316
+ ```
317
+
318
+ ---
319
+
320
+ ## Best Practices
321
+
322
+ 1. Always set `[enable-validation]="true"` on inputs inside forms with validation.
323
+ 2. Use `rlb-autocomplete` for any remote search — pass an `Observable`-returning function.
324
+ 3. For date inputs, always specify `timezone` explicitly; default is `'UTC'`.
325
+ 4. Use `date-type="date-tz"` when the model uses `@open-rlb/date-tz` DateTz objects.
326
+ 5. Wrap inputs in a `<div class="mb-3">` with a `<label class="form-label">` for correct Bootstrap spacing.
@@ -0,0 +1,236 @@
1
+ ---
2
+ name: rlb-modals
3
+ description: Expert guidance for the @open-rlb/ng-bootstrap modal system: programmatic modals via ModalService and the declarative [rlb-modal] directive. Use when opening, building, or configuring modals.
4
+ ---
5
+
6
+ # RLB ng-Bootstrap Modals Skill
7
+
8
+ You are an expert in the **@open-rlb/ng-bootstrap** modal system. Modals support programmatic opening via `ModalService` and declarative usage via the `[rlb-modal]` directive.
9
+
10
+ ## Core Types
11
+
12
+ ```typescript
13
+ type ModalCloseReason = 'ok' | 'cancel' | 'close';
14
+ type ModalType = 'success' | 'info' | 'warning' | 'error';
15
+
16
+ interface ModalOptions {
17
+ backdrop?: boolean | 'static'; // 'static' = click outside doesn't close
18
+ scrollable?: boolean;
19
+ verticalcentered?: boolean;
20
+ animation?: boolean;
21
+ size?: 'sm' | 'md' | 'lg' | 'xl';
22
+ fullscreen?: boolean | 'sm-down' | 'md-down' | 'lg-down' | 'xl-down' | 'xxl-down';
23
+ focus?: boolean;
24
+ keyboard?: boolean; // Esc key closes modal
25
+ }
26
+
27
+ interface ModalData<T = any> {
28
+ data?: T;
29
+ }
30
+
31
+ interface ModalResult<T = any> {
32
+ reason: ModalCloseReason;
33
+ data?: T;
34
+ }
35
+ ```
36
+
37
+ ---
38
+
39
+ ## ModalService — Programmatic API
40
+
41
+ ```typescript
42
+ import { ModalService } from '@open-rlb/ng-bootstrap';
43
+
44
+ @Component({ ... })
45
+ export class MyComponent {
46
+ constructor(private modal: ModalService) {}
47
+
48
+ // Open a named modal (registered component)
49
+ openCustomModal() {
50
+ this.modal.openModal<InputData, OutputData>(
51
+ 'my-modal-name',
52
+ { data: { userId: 42 } },
53
+ { size: 'lg', verticalcentered: true }
54
+ ).subscribe(result => {
55
+ if (result.reason === 'ok') {
56
+ console.log(result.data); // OutputData
57
+ }
58
+ });
59
+ }
60
+
61
+ // Simple info/alert modal
62
+ showAlert() {
63
+ this.modal.openSimpleModal(
64
+ 'Title',
65
+ 'Message body text.',
66
+ 'Optional header',
67
+ 'Close', // ok button label
68
+ 'info' // ModalType
69
+ ).subscribe();
70
+ }
71
+
72
+ // Confirmation modal (ok + cancel)
73
+ confirmDelete() {
74
+ this.modal.openConfirmModal(
75
+ 'Delete item?',
76
+ 'This action cannot be undone.',
77
+ 'Confirm', // header
78
+ 'Delete', // ok label
79
+ 'Cancel', // cancel label
80
+ 'warning' // ModalType
81
+ ).subscribe(result => {
82
+ if (result.reason === 'ok') {
83
+ this.deleteItem();
84
+ }
85
+ });
86
+ }
87
+ }
88
+ ```
89
+
90
+ ---
91
+
92
+ ## [rlb-modal] Directive — Declarative Usage
93
+
94
+ ```html
95
+ <!-- Trigger button -->
96
+ <button rlb-button color="primary" (click)="myModal.show()">Open Modal</button>
97
+
98
+ <!-- Modal template -->
99
+ <div rlb-modal id="my-modal" [data-instance]="myModal" [data-options]="modalOptions">
100
+ <div class="modal-header">
101
+ <h5 class="modal-title">Modal Title</h5>
102
+ <button type="button" class="btn-close" data-modal-reason="close"></button>
103
+ </div>
104
+ <div class="modal-body">
105
+ Modal body content here.
106
+ </div>
107
+ <div class="modal-footer">
108
+ <button rlb-button color="secondary" data-modal-reason="cancel">Cancel</button>
109
+ <button rlb-button color="primary" data-modal-reason="ok">Confirm</button>
110
+ </div>
111
+ </div>
112
+ ```
113
+
114
+ ```typescript
115
+ @Component({ ... })
116
+ export class MyComponent {
117
+ myModal!: IModal; // reference set by the directive
118
+ modalOptions: ModalOptions = {
119
+ size: 'md',
120
+ backdrop: 'static',
121
+ verticalcentered: true
122
+ };
123
+ }
124
+ ```
125
+
126
+ **Close reason attributes:** add `data-modal-reason="ok"`, `data-modal-reason="cancel"`, or `data-modal-reason="close"` to any button inside the modal to auto-close with that reason.
127
+
128
+ ---
129
+
130
+ ## Modal Events
131
+
132
+ Listen to Bootstrap modal lifecycle events on the modal element:
133
+
134
+ ```typescript
135
+ // 'show.bs.modal' | 'shown.bs.modal' | 'hide.bs.modal' | 'hidden.bs.modal' | 'hidePrevented.bs.modal'
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Modal with Form
141
+
142
+ Pattern for a modal that collects data and returns it on confirm:
143
+
144
+ ```typescript
145
+ @Component({
146
+ template: `
147
+ <div rlb-modal id="edit-modal" [data-instance]="modal">
148
+ <div class="modal-header">
149
+ <h5 class="modal-title">Edit User</h5>
150
+ <button class="btn-close" data-modal-reason="close"></button>
151
+ </div>
152
+ <div class="modal-body">
153
+ <form [formGroup]="form">
154
+ <div class="mb-3">
155
+ <label class="form-label">Name</label>
156
+ <rlb-input formControlName="name" [enable-validation]="true"></rlb-input>
157
+ </div>
158
+ </form>
159
+ </div>
160
+ <div class="modal-footer">
161
+ <button rlb-button color="secondary" data-modal-reason="cancel">Cancel</button>
162
+ <button rlb-button color="primary" (click)="save()">Save</button>
163
+ </div>
164
+ </div>
165
+ `
166
+ })
167
+ export class EditModalComponent {
168
+ modal!: IModal;
169
+ form = new FormGroup({ name: new FormControl('') });
170
+
171
+ save() {
172
+ if (this.form.valid) {
173
+ this.modal.hide('ok');
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Multi-step (wizard) modal
182
+
183
+ For a multi-step form inside a modal, put a **carousel driven as a controlled stepper** in the
184
+ `modal-body` and Back / Next / Finish buttons in the `modal-footer`. The carousel runs in manual mode
185
+ (`autoplay="none" no-touch hide-controls hide-indicators`); bind `[current-slide]` to a signal and
186
+ update it from the buttons, gate `Next` on the current step's validity, and call `modal.hide('ok')`
187
+ on finish. See the **Wizard pattern** in the `rlb-components` skill for the full carousel details.
188
+
189
+ ```html
190
+ <div rlb-modal id="wizard-modal" [data-instance]="modal">
191
+ <div class="modal-header"><h5 class="modal-title">Create</h5>
192
+ <button class="btn-close" data-modal-reason="close"></button>
193
+ </div>
194
+ <div class="modal-body" [formGroup]="form">
195
+ <rlb-carousel autoplay="none" no-touch hide-controls hide-indicators
196
+ [current-slide]="page()" (current-slideChange)="page.set($event)"
197
+ (slide-count)="count.set($event)" id="wizard">
198
+ <rlb-carousel-slide active><div formGroupName="step1"><!-- … --></div></rlb-carousel-slide>
199
+ <rlb-carousel-slide><div formGroupName="step2"><!-- … --></div></rlb-carousel-slide>
200
+ </rlb-carousel>
201
+ </div>
202
+ <div class="modal-footer">
203
+ <button rlb-button [disabled]="page() === 0" (click)="prev()">Back</button>
204
+ @if (page() < count() - 1) {
205
+ <button rlb-button color="primary" [disabled]="currentStepInvalid()" (click)="next()">Next</button>
206
+ } @else {
207
+ <button rlb-button color="primary" [disabled]="form.invalid" (click)="onFinish()">Finish</button>
208
+ }
209
+ </div>
210
+ </div>
211
+ ```
212
+
213
+ ---
214
+
215
+ ## ModalOptions Reference
216
+
217
+ | Option | Type | Default | Description |
218
+ |--------|------|---------|-------------|
219
+ | `backdrop` | `boolean \| 'static'` | `true` | `'static'` prevents closing on backdrop click |
220
+ | `scrollable` | `boolean` | `false` | Modal body scrolls independently |
221
+ | `verticalcentered` | `boolean` | `false` | Vertically center modal |
222
+ | `animation` | `boolean` | `true` | Fade animation |
223
+ | `size` | `'sm'\|'md'\|'lg'\|'xl'` | `'md'` | Modal width |
224
+ | `fullscreen` | `boolean\|string` | `false` | Full-screen mode |
225
+ | `focus` | `boolean` | `true` | Auto-focus when opened |
226
+ | `keyboard` | `boolean` | `true` | Esc closes modal |
227
+
228
+ ---
229
+
230
+ ## Best Practices
231
+
232
+ 1. Use `openConfirmModal()` for destructive actions — never skip a confirmation step.
233
+ 2. Use `backdrop: 'static'` for forms where accidental close would lose data.
234
+ 3. Subscribe to the modal result observable and handle all three reasons: `'ok'`, `'cancel'`, `'close'`.
235
+ 4. For complex modals with forms, use the declarative `[rlb-modal]` approach and control close programmatically after validation.
236
+ 5. Use `size: 'lg'` or `size: 'xl'` for forms with many fields; `size: 'sm'` for simple confirmations.