@open-rlb/ng-bootstrap 3.3.25 → 3.3.27
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 +5 -2
- package/package.json +6 -6
- package/schematics/ng-add/claude-skills/date-tz/SKILL.md +375 -0
- package/schematics/ng-add/claude-skills/rlb-calendar/SKILL.md +450 -0
- package/schematics/ng-add/claude-skills/rlb-components/SKILL.md +364 -0
- package/schematics/ng-add/claude-skills/rlb-datatable/SKILL.md +295 -0
- package/schematics/ng-add/claude-skills/rlb-design/SKILL.md +120 -0
- package/schematics/ng-add/claude-skills/rlb-inputs/SKILL.md +326 -0
- package/schematics/ng-add/claude-skills/rlb-modals/SKILL.md +236 -0
- package/schematics/ng-add/index.js +19 -5
- package/schematics/ng-add/schema.json +5 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rlb-components
|
|
3
|
+
description: Expert guidance for the @open-rlb/ng-bootstrap Angular component library (buttons, cards, dropdowns, toasts, loaders, badges, progress, etc.), built on Angular signals, OnPush, and Bootstrap 5. Use when using or composing these UI components.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# RLB ng-Bootstrap Components Skill
|
|
7
|
+
|
|
8
|
+
You are an expert in the **@open-rlb/ng-bootstrap** Angular component library. All components use Angular 18+ signals, `ChangeDetectionStrategy.OnPush`, and Bootstrap 5. Import via `RlbBootstrapModule` or individual standalone imports.
|
|
9
|
+
|
|
10
|
+
## Shared Types
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
type Color = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark';
|
|
14
|
+
type Size = 'sm' | 'md' | 'lg';
|
|
15
|
+
type TextAlignment = 'left' | 'center' | 'right';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Buttons
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<!-- Basic button -->
|
|
24
|
+
<button rlb-button color="primary" size="md">Click me</button>
|
|
25
|
+
<button rlb-button color="danger" outline>Delete</button>
|
|
26
|
+
<button rlb-button color="secondary" [disabled]="true">Disabled</button>
|
|
27
|
+
<a rlb-button color="info" isLink>Link</a>
|
|
28
|
+
|
|
29
|
+
<!-- Button group -->
|
|
30
|
+
<rlb-button-group orientation="horizontal" size="md">
|
|
31
|
+
<button rlb-button color="primary">A</button>
|
|
32
|
+
<button rlb-button color="primary">B</button>
|
|
33
|
+
</rlb-button-group>
|
|
34
|
+
|
|
35
|
+
<!-- FAB (Floating Action Button) -->
|
|
36
|
+
<rlb-fab color="primary" size="md" position="br">
|
|
37
|
+
<i class="bi bi-plus"></i>
|
|
38
|
+
</rlb-fab>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Button inputs:** `color`, `size`, `disabled`, `outline`, `isLink`
|
|
42
|
+
**FAB inputs:** `color`, `size`, `disabled`, `outline`, `position` ('br'|'bl'|'tr'|'tl')
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Alerts
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<rlb-alert color="success">Operation successful.</rlb-alert>
|
|
50
|
+
<rlb-alert color="danger" [dismissible]="true" (dismissed)="onDismiss()">
|
|
51
|
+
An error occurred!
|
|
52
|
+
</rlb-alert>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Inputs:** `color`, `dismissible` (boolean), `class`
|
|
56
|
+
**Outputs:** `dismissed`
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Badges
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<span rlb-badge color="primary">New</span>
|
|
64
|
+
<span rlb-badge color="success" [pill]="true">Active</span>
|
|
65
|
+
<span rlb-badge color="danger" [border]="true">3</span>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Inputs:** `color`, `pill`, `border`, `hidden-text`, `class`, `badge-text-color`
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Avatar
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<rlb-avatar [size]="48" shape="circle" src="/assets/user.png"></rlb-avatar>
|
|
76
|
+
<rlb-avatar [size]="40" shape="round">AB</rlb-avatar>
|
|
77
|
+
<rlb-avatar [size]="56" shape="square" src="/assets/logo.png"></rlb-avatar>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Inputs:** `size` (px number), `shape` ('circle'|'round'|'square'), `src`, `class`
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Cards
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<rlb-card align="left" background="light">
|
|
88
|
+
<rlb-card-header>Card Title</rlb-card-header>
|
|
89
|
+
<rlb-card-image src="/img.png"></rlb-card-image>
|
|
90
|
+
<rlb-card-body>
|
|
91
|
+
<rlb-card-title>Title</rlb-card-title>
|
|
92
|
+
<rlb-card-subtitle>Subtitle</rlb-card-subtitle>
|
|
93
|
+
<rlb-card-text>Body text here.</rlb-card-text>
|
|
94
|
+
<rlb-card-link href="/more">Read more</rlb-card-link>
|
|
95
|
+
</rlb-card-body>
|
|
96
|
+
<rlb-card-footer>Footer</rlb-card-footer>
|
|
97
|
+
</rlb-card>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Card inputs:** `align`, `overlay`, `background` (Color), `border` (Color)
|
|
101
|
+
**Sub-components:** `rlb-card-body`, `rlb-card-header`, `rlb-card-footer`, `rlb-card-image`, `rlb-card-title`, `rlb-card-subtitle`, `rlb-card-text`, `rlb-card-link`
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Accordion
|
|
106
|
+
|
|
107
|
+
```html
|
|
108
|
+
<rlb-accordion [flush]="false" [always-open]="false" [card-style]="true">
|
|
109
|
+
<rlb-accordion-item>
|
|
110
|
+
<ng-template rlb-accordion-header>Section 1</ng-template>
|
|
111
|
+
<ng-template rlb-accordion-body>Content 1</ng-template>
|
|
112
|
+
</rlb-accordion-item>
|
|
113
|
+
<rlb-accordion-item>
|
|
114
|
+
<ng-template rlb-accordion-header>Section 2</ng-template>
|
|
115
|
+
<ng-template rlb-accordion-body>Content 2</ng-template>
|
|
116
|
+
</rlb-accordion-item>
|
|
117
|
+
</rlb-accordion>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Inputs:** `flush`, `always-open`, `card-style`, `id`
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Tabs
|
|
125
|
+
|
|
126
|
+
```html
|
|
127
|
+
<rlb-tabs view="tab" [vertical]="false" fill="fill">
|
|
128
|
+
<rlb-tab title="Tab 1">Content for tab 1</rlb-tab>
|
|
129
|
+
<rlb-tab title="Tab 2">Content for tab 2</rlb-tab>
|
|
130
|
+
<rlb-tab title="Tab 3" [disabled]="true">Disabled</rlb-tab>
|
|
131
|
+
</rlb-tabs>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Inputs:** `view` ('tab'|'pills'|'underline'|'none'), `vertical`, `fill` ('fill'|'justified'), `horizontal-alignment` ('center'|'end'), `id`, `class`
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Carousel
|
|
139
|
+
|
|
140
|
+
```html
|
|
141
|
+
<rlb-carousel
|
|
142
|
+
autoplay="auto"
|
|
143
|
+
[interval]="3000"
|
|
144
|
+
pause="hover"
|
|
145
|
+
[wrap]="true"
|
|
146
|
+
[hide-indicators]="false"
|
|
147
|
+
[hide-controls]="false"
|
|
148
|
+
[(current-slide)]="currentSlide"
|
|
149
|
+
(slid)="onSlid($event)"
|
|
150
|
+
>
|
|
151
|
+
<rlb-carousel-slide active>
|
|
152
|
+
Slide 1
|
|
153
|
+
<rlb-carousel-caption>
|
|
154
|
+
<h5>First slide</h5>
|
|
155
|
+
</rlb-carousel-caption>
|
|
156
|
+
</rlb-carousel-slide>
|
|
157
|
+
<rlb-carousel-slide>Slide 2</rlb-carousel-slide>
|
|
158
|
+
</rlb-carousel>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Children:** `rlb-carousel-slide` (input `active`, `id`), `rlb-carousel-caption`
|
|
162
|
+
**Inputs:** `autoplay` (`'auto' | 'manual' | 'none'`), `interval`, `pause`, `wrap`, `cross-fade`, `hide-indicators`, `hide-controls`, `no-touch`, `keyboard`, `id`
|
|
163
|
+
**Two-way:** `current-slide`
|
|
164
|
+
**Outputs:** `slid`, `slide`, `slide-count`
|
|
165
|
+
**Methods (via viewChild):** `prev()`, `next()`, `to(index)`, `pause()`, `cycle()`
|
|
166
|
+
|
|
167
|
+
### Wizard pattern (multi-step form)
|
|
168
|
+
|
|
169
|
+
There is no separate wizard component — a wizard **is** a carousel used as a controlled stepper.
|
|
170
|
+
Disable autoplay/controls/indicators/touch, drive `current-slide` from your own Back / Next buttons,
|
|
171
|
+
and gate `Next` on the current step's validity. Often embedded in a modal (see the `rlb-modals` skill).
|
|
172
|
+
|
|
173
|
+
```html
|
|
174
|
+
<div [formGroup]="form">
|
|
175
|
+
<rlb-carousel
|
|
176
|
+
autoplay="none"
|
|
177
|
+
no-touch
|
|
178
|
+
hide-controls
|
|
179
|
+
hide-indicators
|
|
180
|
+
[current-slide]="page()"
|
|
181
|
+
(current-slideChange)="page.set($event)"
|
|
182
|
+
(slide-count)="count.set($event)"
|
|
183
|
+
id="wizard"
|
|
184
|
+
>
|
|
185
|
+
<rlb-carousel-slide active>
|
|
186
|
+
<div formGroupName="account"><!-- step 1 fields --></div>
|
|
187
|
+
</rlb-carousel-slide>
|
|
188
|
+
<rlb-carousel-slide>
|
|
189
|
+
<div formGroupName="profile"><!-- step 2 fields --></div>
|
|
190
|
+
</rlb-carousel-slide>
|
|
191
|
+
</rlb-carousel>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<button rlb-button outline [disabled]="page() === 0" (click)="prev()">Back</button>
|
|
195
|
+
@if (page() < count() - 1) {
|
|
196
|
+
<button rlb-button color="primary" [disabled]="currentStepInvalid()" (click)="next()">Next</button>
|
|
197
|
+
} @else {
|
|
198
|
+
<button rlb-button color="success" [disabled]="form.invalid" (click)="onFinish()">Finish</button>
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
readonly page = signal(0); // bound one-way to [current-slide]
|
|
204
|
+
readonly count = signal(0); // set from (slide-count)
|
|
205
|
+
readonly groupNames = ['account', 'profile'];
|
|
206
|
+
|
|
207
|
+
// Validate one step at a time to gate the Next button.
|
|
208
|
+
currentStepInvalid(): boolean {
|
|
209
|
+
return !!this.form.get(this.groupNames[this.page()])?.invalid;
|
|
210
|
+
}
|
|
211
|
+
prev() { this.page.update(p => Math.max(0, p - 1)); }
|
|
212
|
+
next() { this.page.update(p => Math.min(this.count() - 1, p + 1)); }
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
> Key points: bind `[current-slide]` **one-way** and update the signal yourself (don't use `[(...)]`
|
|
216
|
+
> two-way when you drive navigation manually); use `(slide-count)` to detect the last step; keep each
|
|
217
|
+
> step in its own nested `FormGroup` so `currentStepInvalid()` can validate steps independently.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Breadcrumb
|
|
222
|
+
|
|
223
|
+
```html
|
|
224
|
+
<rlb-breadcrumb
|
|
225
|
+
divider="/"
|
|
226
|
+
[items]="[
|
|
227
|
+
{ id: '1', label: 'Home', link: '/' },
|
|
228
|
+
{ id: '2', label: 'Products', link: '/products' },
|
|
229
|
+
{ id: '3', label: 'Detail' }
|
|
230
|
+
]"
|
|
231
|
+
/>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Inputs:** `divider`, `items: BreadcrumbItem[]`, `cssClasses`
|
|
235
|
+
**BreadcrumbItem:** `{ id: string; label: string; link?: string }`
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Dropdown
|
|
240
|
+
|
|
241
|
+
```html
|
|
242
|
+
<rlb-dropdown direction="down">
|
|
243
|
+
<button rlb-button color="primary" rlb-dropdown>Actions</button>
|
|
244
|
+
<div rlb-dropdown-menu>
|
|
245
|
+
<a class="dropdown-item" href="#">Option 1</a>
|
|
246
|
+
<a class="dropdown-item" href="#">Option 2</a>
|
|
247
|
+
</div>
|
|
248
|
+
</rlb-dropdown>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Inputs:** `direction` ('up'|'down'|'left'|'right'|'up-center'|'down-center')
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Pagination
|
|
256
|
+
|
|
257
|
+
```html
|
|
258
|
+
<rlb-pagination size="md" alignment="center">
|
|
259
|
+
<rlb-pagination-item [active]="true">1</rlb-pagination-item>
|
|
260
|
+
<rlb-pagination-item>2</rlb-pagination-item>
|
|
261
|
+
</rlb-pagination>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Inputs:** `size`, `alignment` ('start'|'center'|'end'), `class`
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Navigation (Navbar + Sidebar)
|
|
269
|
+
|
|
270
|
+
```html
|
|
271
|
+
<rlb-navbar color="primary" [dark]="true" expand="lg" placement="fixed-top">
|
|
272
|
+
<a rlb-navbar-brand href="/">MyApp</a>
|
|
273
|
+
<rlb-navbar-items>
|
|
274
|
+
<a class="nav-link" routerLink="/home">Home</a>
|
|
275
|
+
</rlb-navbar-items>
|
|
276
|
+
</rlb-navbar>
|
|
277
|
+
|
|
278
|
+
<rlb-sidebar [dark]="true" [rounded]="false">
|
|
279
|
+
<rlb-sidebar-item routerLink="/dashboard">Dashboard</rlb-sidebar-item>
|
|
280
|
+
<rlb-sidebar-item routerLink="/settings">Settings</rlb-sidebar-item>
|
|
281
|
+
</rlb-sidebar>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Navbar inputs:** `dark`, `color`, `placement`, `expand`, `show-sidebar-toggler`, `enable-dropdown-toggler`, `class`
|
|
285
|
+
**Sidebar inputs:** `dark`, `rounded`
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Loaders
|
|
290
|
+
|
|
291
|
+
```html
|
|
292
|
+
<!-- Spinner -->
|
|
293
|
+
<rlb-spinner style="border" color="primary" size="md"></rlb-spinner>
|
|
294
|
+
<rlb-spinner style="grow" color="success" size="sm"></rlb-spinner>
|
|
295
|
+
|
|
296
|
+
<!-- Progress bar -->
|
|
297
|
+
<rlb-progress
|
|
298
|
+
[value]="65"
|
|
299
|
+
[max]="100"
|
|
300
|
+
color="primary"
|
|
301
|
+
[striped]="true"
|
|
302
|
+
[animated]="true"
|
|
303
|
+
[showValue]="true"
|
|
304
|
+
></rlb-progress>
|
|
305
|
+
|
|
306
|
+
<!-- Infinite progress -->
|
|
307
|
+
<rlb-progress [infinite]="true" color="info"></rlb-progress>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**Spinner inputs:** `style` ('border'|'grow'), `color`, `size`
|
|
311
|
+
**Progress inputs:** `value`, `max`, `min`, `height`, `animated`, `striped`, `infinite`, `showValue`, `color`, `text-color`, `aria-label`
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Toast
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
// In component
|
|
319
|
+
constructor(private toast: ToastService) {}
|
|
320
|
+
|
|
321
|
+
showSuccess() {
|
|
322
|
+
this.toast.openToast('my-toast', 'success-toast').subscribe();
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Tooltip
|
|
329
|
+
|
|
330
|
+
```html
|
|
331
|
+
<button rlb-button rlb-tooltip title="Helpful tip" placement="top">Hover me</button>
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Collapse & Offcanvas
|
|
337
|
+
|
|
338
|
+
```html
|
|
339
|
+
<!-- Collapse -->
|
|
340
|
+
<button rlb-button rlb-collapse-toggle target="myCollapse">Toggle</button>
|
|
341
|
+
<rlb-collapse id="myCollapse">Hidden content</rlb-collapse>
|
|
342
|
+
|
|
343
|
+
<!-- Offcanvas -->
|
|
344
|
+
<button rlb-button rlb-offcanvas-toggle target="myPanel">Open Panel</button>
|
|
345
|
+
<rlb-offcanvas id="myPanel" placement="start">Panel content</rlb-offcanvas>
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Module Import
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
import { RlbBootstrapModule } from '@open-rlb/ng-bootstrap';
|
|
354
|
+
|
|
355
|
+
@NgModule({
|
|
356
|
+
imports: [RlbBootstrapModule]
|
|
357
|
+
})
|
|
358
|
+
export class AppModule {}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Or import individual components for standalone:
|
|
362
|
+
```typescript
|
|
363
|
+
import { ButtonComponent, AlertComponent, CardComponent } from '@open-rlb/ng-bootstrap';
|
|
364
|
+
```
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rlb-datatable
|
|
3
|
+
description: Expert guidance for the @open-rlb/ng-bootstrap rlb-dt-table datatable: pagination (pages or load-more), sorting, filtering, row actions, loading and empty states. Use when building or configuring data tables.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# RLB ng-Bootstrap DataTable Skill
|
|
7
|
+
|
|
8
|
+
You are an expert in the **@open-rlb/ng-bootstrap** datatable component (`rlb-dt-table`). It supports pagination (pages or load-more), sorting, filtering, row actions, loading states, and empty states.
|
|
9
|
+
|
|
10
|
+
## Component Tree
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
rlb-dt-table
|
|
14
|
+
├── rlb-dt-header — column header (sortable/filterable)
|
|
15
|
+
├── rlb-dt-row — data row (repeated with *ngFor)
|
|
16
|
+
│ ├── rlb-dt-cell — data cell
|
|
17
|
+
│ └── rlb-dt-actions — row action menu
|
|
18
|
+
│ └── rlb-dt-action — individual action item
|
|
19
|
+
├── rlb-dt-loading — custom loading template
|
|
20
|
+
└── rlb-dt-noitems — custom empty state template
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## rlb-dt-table Inputs
|
|
26
|
+
|
|
27
|
+
| Input | Type | Default | Description |
|
|
28
|
+
|-------|------|---------|-------------|
|
|
29
|
+
| `title` | `string` | — | Table title |
|
|
30
|
+
| `items` | `any[]` | `[]` | Data rows |
|
|
31
|
+
| `loading` | `boolean` | `false` | Loading overlay |
|
|
32
|
+
| `card-style` | `boolean` | `true` | Wrap in card |
|
|
33
|
+
| `table-hover` | `boolean` | `false` | Hover highlight |
|
|
34
|
+
| `table-striped` | `boolean` | `false` | Alternating row colors |
|
|
35
|
+
| `table-striped-columns` | `boolean` | `false` | Alternating column colors |
|
|
36
|
+
| `table-bordered` | `boolean` | `false` | Cell borders |
|
|
37
|
+
| `table-borderless` | `boolean` | `false` | Remove all borders |
|
|
38
|
+
| `table-small` | `boolean` | `false` | Compact rows |
|
|
39
|
+
| `pagination-mode` | `'none'\|'load-more'\|'pages'` | `'none'` | Pagination strategy |
|
|
40
|
+
| `total-items` | `number` | — | Total record count |
|
|
41
|
+
| `current-page` | `number` | — | Current page (1-based) |
|
|
42
|
+
| `page-size` | `number` | — | Rows per page |
|
|
43
|
+
| `show-refresh` | `boolean` | `false` | Show refresh button |
|
|
44
|
+
| `show-actions` | `'row'\|'head'` | `'row'` | Actions column position |
|
|
45
|
+
| `creation-strategy` | `'none'\|'modal'\|'page'` | `'none'` | Create button behavior |
|
|
46
|
+
| `creation-url` | `any[]\|string\|null` | — | Router link for 'page' strategy |
|
|
47
|
+
| `load-more-label` | `string` | `'Load more'` | Load more button text |
|
|
48
|
+
|
|
49
|
+
## rlb-dt-table Outputs
|
|
50
|
+
|
|
51
|
+
| Output | Type | Description |
|
|
52
|
+
|--------|------|-------------|
|
|
53
|
+
| `create-item` | `void` | Fired when create button clicked |
|
|
54
|
+
| `refresh-item` | `void` | Fired when refresh button clicked |
|
|
55
|
+
| `load-more` | `void` | Fired when load-more clicked |
|
|
56
|
+
| `current-pageChange` | `number` | Two-way page binding |
|
|
57
|
+
| `page-sizeChange` | `number` | Two-way page-size binding |
|
|
58
|
+
| `pagination` | `{ page: number; size: number }` | Combined pagination event |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## rlb-dt-header Inputs
|
|
63
|
+
|
|
64
|
+
| Input | Type | Default | Description |
|
|
65
|
+
|-------|------|---------|-------------|
|
|
66
|
+
| `field` | `string` | — | Data field name (for sort/filter) |
|
|
67
|
+
| `sortable` | `boolean` | `false` | Enable column sorting |
|
|
68
|
+
| `filtrable` | `boolean` | `false` | Enable column filtering |
|
|
69
|
+
| `type` | `'number'\|'string'` | — | Column data type |
|
|
70
|
+
| `class` | `string` | — | CSS classes |
|
|
71
|
+
| `style` | `string` | — | Inline styles |
|
|
72
|
+
|
|
73
|
+
## rlb-dt-row Outputs
|
|
74
|
+
|
|
75
|
+
| Output | Type | Description |
|
|
76
|
+
|--------|------|-------------|
|
|
77
|
+
| `row-click` | `MouseEvent` | Row click event |
|
|
78
|
+
|
|
79
|
+
## rlb-dt-actions Inputs
|
|
80
|
+
|
|
81
|
+
| Input | Type | Default | Description |
|
|
82
|
+
|-------|------|---------|-------------|
|
|
83
|
+
| `disabled` | `boolean` | `false` | Disable all actions |
|
|
84
|
+
|
|
85
|
+
## rlb-dt-action Inputs
|
|
86
|
+
|
|
87
|
+
| Input | Type | Default | Description |
|
|
88
|
+
|-------|------|---------|-------------|
|
|
89
|
+
| `label` | `string` | — | Action label (required) |
|
|
90
|
+
| `disabled` | `boolean` | `false` | Disable this action |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Basic Table (no pagination)
|
|
95
|
+
|
|
96
|
+
```html
|
|
97
|
+
<rlb-dt-table
|
|
98
|
+
title="Users"
|
|
99
|
+
[items]="users"
|
|
100
|
+
[loading]="loading"
|
|
101
|
+
[table-hover]="true"
|
|
102
|
+
[table-striped]="true"
|
|
103
|
+
[show-refresh]="true"
|
|
104
|
+
(refresh-item)="loadUsers()"
|
|
105
|
+
>
|
|
106
|
+
<rlb-dt-header field="name" [sortable]="true">Name</rlb-dt-header>
|
|
107
|
+
<rlb-dt-header field="email">Email</rlb-dt-header>
|
|
108
|
+
<rlb-dt-header field="role">Role</rlb-dt-header>
|
|
109
|
+
|
|
110
|
+
<rlb-dt-row *ngFor="let user of users" (row-click)="onRowClick(user)">
|
|
111
|
+
<rlb-dt-cell>{{ user.name }}</rlb-dt-cell>
|
|
112
|
+
<rlb-dt-cell>{{ user.email }}</rlb-dt-cell>
|
|
113
|
+
<rlb-dt-cell>
|
|
114
|
+
<span rlb-badge [color]="user.role === 'admin' ? 'danger' : 'primary'">{{ user.role }}</span>
|
|
115
|
+
</rlb-dt-cell>
|
|
116
|
+
<rlb-dt-actions>
|
|
117
|
+
<rlb-dt-action label="Edit" (click)="editUser(user)"></rlb-dt-action>
|
|
118
|
+
<rlb-dt-action label="Delete" (click)="deleteUser(user)" [disabled]="user.role === 'admin'"></rlb-dt-action>
|
|
119
|
+
</rlb-dt-actions>
|
|
120
|
+
</rlb-dt-row>
|
|
121
|
+
|
|
122
|
+
<rlb-dt-noitems>
|
|
123
|
+
<p class="text-muted text-center py-4">No users found.</p>
|
|
124
|
+
</rlb-dt-noitems>
|
|
125
|
+
</rlb-dt-table>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Server-side Pagination (pages)
|
|
131
|
+
|
|
132
|
+
```html
|
|
133
|
+
<rlb-dt-table
|
|
134
|
+
title="Orders"
|
|
135
|
+
[items]="orders"
|
|
136
|
+
[loading]="loading"
|
|
137
|
+
pagination-mode="pages"
|
|
138
|
+
[total-items]="totalOrders"
|
|
139
|
+
[(current-page)]="currentPage"
|
|
140
|
+
[(page-size)]="pageSize"
|
|
141
|
+
(pagination)="onPaginate($event)"
|
|
142
|
+
creation-strategy="modal"
|
|
143
|
+
(create-item)="openCreateModal()"
|
|
144
|
+
>
|
|
145
|
+
<rlb-dt-header field="id" type="number">#</rlb-dt-header>
|
|
146
|
+
<rlb-dt-header field="customer" [sortable]="true">Customer</rlb-dt-header>
|
|
147
|
+
<rlb-dt-header field="amount" type="number" [sortable]="true">Amount</rlb-dt-header>
|
|
148
|
+
<rlb-dt-header field="status">Status</rlb-dt-header>
|
|
149
|
+
|
|
150
|
+
<rlb-dt-row *ngFor="let order of orders">
|
|
151
|
+
<rlb-dt-cell>{{ order.id }}</rlb-dt-cell>
|
|
152
|
+
<rlb-dt-cell>{{ order.customer }}</rlb-dt-cell>
|
|
153
|
+
<rlb-dt-cell>{{ order.amount | currency:'EUR' }}</rlb-dt-cell>
|
|
154
|
+
<rlb-dt-cell>
|
|
155
|
+
<span rlb-badge [color]="statusColor(order.status)">{{ order.status }}</span>
|
|
156
|
+
</rlb-dt-cell>
|
|
157
|
+
<rlb-dt-actions>
|
|
158
|
+
<rlb-dt-action label="View" (click)="viewOrder(order)"></rlb-dt-action>
|
|
159
|
+
<rlb-dt-action label="Cancel" (click)="cancelOrder(order)" [disabled]="order.status !== 'pending'"></rlb-dt-action>
|
|
160
|
+
</rlb-dt-actions>
|
|
161
|
+
</rlb-dt-row>
|
|
162
|
+
</rlb-dt-table>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
@Component({ ... })
|
|
167
|
+
export class OrderListComponent {
|
|
168
|
+
orders: Order[] = [];
|
|
169
|
+
totalOrders = 0;
|
|
170
|
+
currentPage = 1;
|
|
171
|
+
pageSize = 10;
|
|
172
|
+
loading = false;
|
|
173
|
+
|
|
174
|
+
ngOnInit() { this.loadOrders(); }
|
|
175
|
+
|
|
176
|
+
onPaginate({ page, size }: { page: number; size: number }) {
|
|
177
|
+
this.currentPage = page;
|
|
178
|
+
this.pageSize = size;
|
|
179
|
+
this.loadOrders();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private loadOrders() {
|
|
183
|
+
this.loading = true;
|
|
184
|
+
this.orderService.getPage(this.currentPage, this.pageSize).subscribe(res => {
|
|
185
|
+
this.orders = res.items;
|
|
186
|
+
this.totalOrders = res.total;
|
|
187
|
+
this.loading = false;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
statusColor(status: string): Color {
|
|
192
|
+
const map: Record<string, Color> = {
|
|
193
|
+
pending: 'warning',
|
|
194
|
+
confirmed: 'primary',
|
|
195
|
+
shipped: 'info',
|
|
196
|
+
delivered: 'success',
|
|
197
|
+
cancelled: 'danger'
|
|
198
|
+
};
|
|
199
|
+
return map[status] ?? 'secondary';
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Load-more Pattern (infinite scroll style)
|
|
207
|
+
|
|
208
|
+
```html
|
|
209
|
+
<rlb-dt-table
|
|
210
|
+
[items]="items"
|
|
211
|
+
[loading]="loading"
|
|
212
|
+
pagination-mode="load-more"
|
|
213
|
+
[total-items]="total"
|
|
214
|
+
load-more-label="Load more items"
|
|
215
|
+
(load-more)="loadMore()"
|
|
216
|
+
>
|
|
217
|
+
<rlb-dt-header>Name</rlb-dt-header>
|
|
218
|
+
<rlb-dt-row *ngFor="let item of items">
|
|
219
|
+
<rlb-dt-cell>{{ item.name }}</rlb-dt-cell>
|
|
220
|
+
</rlb-dt-row>
|
|
221
|
+
</rlb-dt-table>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
page = 1;
|
|
226
|
+
items: any[] = [];
|
|
227
|
+
total = 0;
|
|
228
|
+
|
|
229
|
+
loadMore() {
|
|
230
|
+
this.page++;
|
|
231
|
+
this.service.getPage(this.page).subscribe(res => {
|
|
232
|
+
this.items = [...this.items, ...res.items]; // append, don't replace
|
|
233
|
+
this.total = res.total;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Custom Loading State
|
|
241
|
+
|
|
242
|
+
```html
|
|
243
|
+
<rlb-dt-table [items]="items" [loading]="loading">
|
|
244
|
+
<!-- headers and rows... -->
|
|
245
|
+
|
|
246
|
+
<rlb-dt-loading>
|
|
247
|
+
<div class="d-flex justify-content-center p-4">
|
|
248
|
+
<rlb-spinner color="primary" size="lg"></rlb-spinner>
|
|
249
|
+
</div>
|
|
250
|
+
</rlb-dt-loading>
|
|
251
|
+
</rlb-dt-table>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Creation Strategies
|
|
257
|
+
|
|
258
|
+
```html
|
|
259
|
+
<!-- Modal: fires (create-item) output -->
|
|
260
|
+
<rlb-dt-table creation-strategy="modal" (create-item)="openCreateModal()">...</rlb-dt-table>
|
|
261
|
+
|
|
262
|
+
<!-- Page: navigates to route -->
|
|
263
|
+
<rlb-dt-table creation-strategy="page" creation-url="/users/new">...</rlb-dt-table>
|
|
264
|
+
<rlb-dt-table creation-strategy="page" [creation-url]="['/users', 'new']">...</rlb-dt-table>
|
|
265
|
+
|
|
266
|
+
<!-- None (default): no create button -->
|
|
267
|
+
<rlb-dt-table creation-strategy="none">...</rlb-dt-table>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Table Style Combinations
|
|
273
|
+
|
|
274
|
+
```html
|
|
275
|
+
<!-- Compact bordered table -->
|
|
276
|
+
<rlb-dt-table [table-small]="true" [table-bordered]="true" [card-style]="false">
|
|
277
|
+
|
|
278
|
+
<!-- Striped hover table (most common for lists) -->
|
|
279
|
+
<rlb-dt-table [table-striped]="true" [table-hover]="true">
|
|
280
|
+
|
|
281
|
+
<!-- Borderless minimal table -->
|
|
282
|
+
<rlb-dt-table [table-borderless]="true" [card-style]="false">
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Best Practices
|
|
288
|
+
|
|
289
|
+
1. Always provide `[total-items]` when using `pagination-mode="pages"` — the paginator needs total count.
|
|
290
|
+
2. Use `[(current-page)]` and `[(page-size)]` for two-way binding; or listen to `(pagination)` for both at once.
|
|
291
|
+
3. Reset `currentPage = 1` whenever filters/sort change, then reload.
|
|
292
|
+
4. For append-style lists use `load-more` mode and concat arrays: `this.items = [...this.items, ...newItems]`.
|
|
293
|
+
5. Provide `<rlb-dt-noitems>` with a helpful message — empty tables are confusing without context.
|
|
294
|
+
6. Use `[disabled]` on `rlb-dt-action` for conditional actions (e.g., only admins can delete).
|
|
295
|
+
7. Avoid putting complex logic inside `*ngFor` templates — compute display values in the component.
|