ryzen-ui 0.1.4
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 +1063 -0
- package/fesm2022/ryzen-ui.mjs +2337 -0
- package/fesm2022/ryzen-ui.mjs.map +1 -0
- package/index.d.ts +995 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,1063 @@
|
|
|
1
|
+
# Ryzen UI
|
|
2
|
+
|
|
3
|
+
A lightweight, standalone Angular component library built with **signals**, **OnPush** change detection, **pure component-scoped CSS** with oklch color theming.
|
|
4
|
+
|
|
5
|
+
- **27 components** across Form, Data Display, Feedback, Navigation, and Overlay categories
|
|
6
|
+
- Zero runtime dependencies beyond `@angular/core` and `primeicons`
|
|
7
|
+
- No `@angular/animations` — CSS keyframes for all animations
|
|
8
|
+
- No CDK — manual document listeners and `position: fixed / absolute` for overlays
|
|
9
|
+
- Tree-shakeable, side-effect-free
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add ryzen-ui primeicons
|
|
17
|
+
# or
|
|
18
|
+
npm install ryzen-ui primeicons
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Optional peer dependencies (for Table export)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add exceljs jspdf html2canvas
|
|
25
|
+
# or
|
|
26
|
+
npm install exceljs jspdf html2canvas
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { Component } from '@angular/core';
|
|
35
|
+
import { ToastService } from 'ryzen-ui';
|
|
36
|
+
import { RzButtonComponent } from 'ryzen-ui';
|
|
37
|
+
// etc. — all components are standalone and tree-shakeable
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Global styles
|
|
41
|
+
|
|
42
|
+
```css
|
|
43
|
+
/* styles.css */
|
|
44
|
+
@import 'primeicons/primeicons.css';
|
|
45
|
+
|
|
46
|
+
:root {
|
|
47
|
+
--color-primary: oklch(0.32 0.09 258);
|
|
48
|
+
--color-surface: oklch(0.99 0 0);
|
|
49
|
+
--color-border: oklch(0.83 0.015 260);
|
|
50
|
+
--color-text: oklch(0.14 0.01 260);
|
|
51
|
+
--color-text-muted: oklch(0.48 0.01 260);
|
|
52
|
+
--color-surface-alt: oklch(0.975 0.005 260);
|
|
53
|
+
--color-secondary: oklch(0.55 0.12 40);
|
|
54
|
+
--color-accent: oklch(0.64 0.2 50);
|
|
55
|
+
--color-success: oklch(0.55 0.18 145);
|
|
56
|
+
--color-warning: oklch(0.68 0.18 75);
|
|
57
|
+
--color-danger: oklch(0.55 0.22 25);
|
|
58
|
+
--color-info: oklch(0.55 0.15 235);
|
|
59
|
+
--radius-md: 0.5rem;
|
|
60
|
+
--font-sans: ui-sans-serif, system-ui, sans-serif;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.dark {
|
|
64
|
+
--color-primary: oklch(0.65 0.15 258);
|
|
65
|
+
--color-surface: oklch(0.18 0.01 260);
|
|
66
|
+
--color-border: oklch(0.3 0.01 260);
|
|
67
|
+
--color-text: oklch(0.95 0.005 260);
|
|
68
|
+
--color-text-muted: oklch(0.65 0.01 260);
|
|
69
|
+
--color-surface-alt: oklch(0.22 0.01 260);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Theming
|
|
76
|
+
|
|
77
|
+
All components inherit colors through CSS custom properties. Each component's `:host` maps theme variables into internal `--rz-*` variables with oklch fallbacks, so components work **without any theme setup**:
|
|
78
|
+
|
|
79
|
+
| Variable | Default | Description |
|
|
80
|
+
|----------|---------|-------------|
|
|
81
|
+
| `--rz-accent` | `oklch(0.32 0.09 258)` | Accent/highlight color |
|
|
82
|
+
| `--rz-bg` | `oklch(0.99 0 0)` | Input/component background |
|
|
83
|
+
| `--rz-border` | `oklch(0.83 0.015 260)` | Border color |
|
|
84
|
+
| `--rz-text` | `oklch(0.14 0.01 260)` | Text color |
|
|
85
|
+
| `--rz-muted` | `oklch(0.48 0.01 260)` | Muted/secondary text |
|
|
86
|
+
| `--rz-radius` | `0.5rem` | Border radius |
|
|
87
|
+
|
|
88
|
+
### Dark mode
|
|
89
|
+
|
|
90
|
+
Add `.dark` class to `<html>`:
|
|
91
|
+
|
|
92
|
+
```html
|
|
93
|
+
<html class="dark">...</html>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The library uses `@custom-variant dark (&:where(.dark, .dark *))` — no media query, fully manual toggle.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Components
|
|
101
|
+
|
|
102
|
+
### Common Conventions
|
|
103
|
+
|
|
104
|
+
- **Size**: `'sm' | 'md' | 'lg'` — component `size` input, defaults to `'md'`
|
|
105
|
+
- **Accent**: applies via `accentColor` input (named `ThemeColor` or any raw CSS color string) or `config.accentColor` where available
|
|
106
|
+
- **Error/Hint**: every form component supports `error`, `errorMessage`, and `hint` inputs
|
|
107
|
+
- **Animations**: CSS `@keyframes` prefixed with `rz-*`, triggered by Angular animation triggers (`[animate.enter]`, `[animate.leave]`)
|
|
108
|
+
- **Panel positioning**: panels (dropdown, datepicker) use `position: fixed` with manual `(document:click)` listener — no CDK
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### Form
|
|
113
|
+
|
|
114
|
+
#### `<app-text-input>`
|
|
115
|
+
|
|
116
|
+
Standard text input field.
|
|
117
|
+
|
|
118
|
+
| Input | Type | Default |
|
|
119
|
+
|-------|------|---------|
|
|
120
|
+
| `value` | `model<string>` | `''` |
|
|
121
|
+
| `placeholder` | `string` | `''` |
|
|
122
|
+
| `size` | `FieldSize` | `'md'` |
|
|
123
|
+
| `disabled` | `boolean` | `false` |
|
|
124
|
+
| `accentColor` | `string \| null` | `null` |
|
|
125
|
+
| `hint` | `string` | `''` |
|
|
126
|
+
| `errorMessage` | `string` | `''` |
|
|
127
|
+
| `error` | `boolean` | `false` |
|
|
128
|
+
| `width` | `string` | `''` |
|
|
129
|
+
|
|
130
|
+
```html
|
|
131
|
+
<app-text-input [(value)]="name" placeholder="Enter name" [accentColor]="'primary'" />
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
#### `<app-password-input>`
|
|
137
|
+
|
|
138
|
+
Password field with visibility toggle.
|
|
139
|
+
|
|
140
|
+
| Input | Type | Default |
|
|
141
|
+
|-------|------|---------|
|
|
142
|
+
| `value` | `model<string>` | `''` |
|
|
143
|
+
| `placeholder` | `string` | `''` |
|
|
144
|
+
| `size` | `FieldSize` | `'md'` |
|
|
145
|
+
| `disabled` | `boolean` | `false` |
|
|
146
|
+
| `accentColor` | `string \| null` | `null` |
|
|
147
|
+
| `hint` | `string` | `''` |
|
|
148
|
+
| `errorMessage` | `string` | `''` |
|
|
149
|
+
| `error` | `boolean` | `false` |
|
|
150
|
+
| `hideToggle` | `boolean` | `false` |
|
|
151
|
+
| `width` | `string` | `''` |
|
|
152
|
+
|
|
153
|
+
**Methods**: `toggleVisibility()`
|
|
154
|
+
|
|
155
|
+
```html
|
|
156
|
+
<app-password-input [(value)]="password" placeholder="Password" />
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
#### `<app-search-input>`
|
|
162
|
+
|
|
163
|
+
Search field with search icon and clear button.
|
|
164
|
+
|
|
165
|
+
| Input | Type | Default |
|
|
166
|
+
|-------|------|---------|
|
|
167
|
+
| `value` | `model<string>` | `''` |
|
|
168
|
+
| `placeholder` | `string` | `'Search...'` |
|
|
169
|
+
| `size` | `FieldSize` | `'md'` |
|
|
170
|
+
| `disabled` | `boolean` | `false` |
|
|
171
|
+
| `accentColor` | `string \| null` | `null` |
|
|
172
|
+
| `hint` | `string` | `''` |
|
|
173
|
+
| `errorMessage` | `string` | `''` |
|
|
174
|
+
| `error` | `boolean` | `false` |
|
|
175
|
+
| `width` | `string` | `''` |
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<app-search-input [(value)]="query" placeholder="Search users..." />
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
#### `<app-checkbox>`
|
|
184
|
+
|
|
185
|
+
Checkbox with optional label, indeterminate state.
|
|
186
|
+
|
|
187
|
+
| Input | Type | Default |
|
|
188
|
+
|-------|------|---------|
|
|
189
|
+
| `checked` | `model<boolean>` | `false` |
|
|
190
|
+
| `label` | `string` | `''` |
|
|
191
|
+
| `size` | `FieldSize` | `'md'` |
|
|
192
|
+
| `disabled` | `boolean` | `false` |
|
|
193
|
+
| `accentColor` | `string \| null` | `null` |
|
|
194
|
+
| `indeterminate` | `boolean` | `false` |
|
|
195
|
+
| `hint` | `string` | `''` |
|
|
196
|
+
| `errorMessage` | `string` | `''` |
|
|
197
|
+
| `error` | `boolean` | `false` |
|
|
198
|
+
| `width` | `string` | `''` |
|
|
199
|
+
|
|
200
|
+
**Methods**: `toggle()`
|
|
201
|
+
|
|
202
|
+
```html
|
|
203
|
+
<app-checkbox [(checked)]="agreed" label="I agree to the terms" />
|
|
204
|
+
<app-checkbox [indeterminate]="true" label="Select all" />
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
#### `<app-toggle>`
|
|
210
|
+
|
|
211
|
+
Switch/toggle control.
|
|
212
|
+
|
|
213
|
+
| Input | Type | Default |
|
|
214
|
+
|-------|------|---------|
|
|
215
|
+
| `checked` | `model<boolean>` | `false` |
|
|
216
|
+
| `label` | `string` | `''` |
|
|
217
|
+
| `size` | `FieldSize` | `'md'` |
|
|
218
|
+
| `disabled` | `boolean` | `false` |
|
|
219
|
+
| `accentColor` | `string \| null` | `null` |
|
|
220
|
+
| `hint` | `string` | `''` |
|
|
221
|
+
| `errorMessage` | `string` | `''` |
|
|
222
|
+
| `error` | `boolean` | `false` |
|
|
223
|
+
| `width` | `string` | `''` |
|
|
224
|
+
|
|
225
|
+
**Methods**: `toggle()`
|
|
226
|
+
|
|
227
|
+
```html
|
|
228
|
+
<app-toggle [(checked)]="notifications" label="Enable notifications" />
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
#### `<app-date-picker>`
|
|
234
|
+
|
|
235
|
+
Inline calendar with month/year navigation, today button.
|
|
236
|
+
|
|
237
|
+
| Input | Type | Default |
|
|
238
|
+
|-------|------|---------|
|
|
239
|
+
| `value` | `model<Date \| null>` | `null` |
|
|
240
|
+
| `placeholder` | `string` | `'Select date'` |
|
|
241
|
+
| `size` | `FieldSize` | `'md'` |
|
|
242
|
+
| `disabled` | `boolean` | `false` |
|
|
243
|
+
| `accentColor` | `string \| null` | `null` |
|
|
244
|
+
| `hint` | `string` | `''` |
|
|
245
|
+
| `errorMessage` | `string` | `''` |
|
|
246
|
+
| `error` | `boolean` | `false` |
|
|
247
|
+
| `format` | `'dd/MM/yyyy' \| 'MM/dd/yyyy' \| 'yyyy-MM-dd'` | `'dd/MM/yyyy'` |
|
|
248
|
+
| `width` | `string` | `''` |
|
|
249
|
+
| `config` | `DatePickerConfig` | `{}` |
|
|
250
|
+
|
|
251
|
+
**`DatePickerConfig`:**
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
interface DatePickerConfig {
|
|
255
|
+
minDate?: Date;
|
|
256
|
+
maxDate?: Date;
|
|
257
|
+
disabledDates?: Date[];
|
|
258
|
+
highlightedDates?: Date[];
|
|
259
|
+
firstDayOfWeek?: 0 | 1; // 0=Sunday, 1=Monday
|
|
260
|
+
showTodayButton?: boolean; // default true
|
|
261
|
+
showYearNavigation?: boolean; // default true
|
|
262
|
+
appendToParent?: boolean; // default false
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
```html
|
|
267
|
+
<app-date-picker [(value)]="dob" placeholder="Date of birth" />
|
|
268
|
+
<app-date-picker [(value)]="range" [config]="{ minDate: today, firstDayOfWeek: 1 }" />
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
#### `<app-drop-down>`
|
|
274
|
+
|
|
275
|
+
Searchable dropdown with smart panel positioning.
|
|
276
|
+
|
|
277
|
+
| Input | Type | Default |
|
|
278
|
+
|-------|------|---------|
|
|
279
|
+
| `value` | `model<string \| null>` | `null` |
|
|
280
|
+
| `options` | `(DropdownOption \| string)[]` | `[]` |
|
|
281
|
+
| `placeholder` | `string` | `'Select...'` |
|
|
282
|
+
| `size` | `FieldSize` | `'md'` |
|
|
283
|
+
| `disabled` | `boolean` | `false` |
|
|
284
|
+
| `accentColor` | `string \| null` | `null` |
|
|
285
|
+
| `searchable` | `boolean` | `false` |
|
|
286
|
+
| `hint` | `string` | `''` |
|
|
287
|
+
| `errorMessage` | `string` | `''` |
|
|
288
|
+
| `error` | `boolean` | `false` |
|
|
289
|
+
| `width` | `string` | `''` |
|
|
290
|
+
| `config` | `DropdownConfig` | `{}` |
|
|
291
|
+
|
|
292
|
+
**`DropdownOption<T>`:**
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
interface DropdownOption<T = unknown> {
|
|
296
|
+
label: string;
|
|
297
|
+
value: T;
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**`DropdownConfig`:**
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
interface DropdownConfig {
|
|
305
|
+
appendToParent?: boolean;
|
|
306
|
+
maxHeight?: string;
|
|
307
|
+
direction?: 'up' | 'down' | 'auto';
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```html
|
|
312
|
+
<app-drop-down [(value)]="country" :options="['Egypt', 'UAE', 'KSA']" />
|
|
313
|
+
<app-drop-down
|
|
314
|
+
[(value)]="user"
|
|
315
|
+
[options]="[{ label: 'Alice', value: 1 }, { label: 'Bob', value: 2 }]"
|
|
316
|
+
searchable
|
|
317
|
+
/>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
#### `<app-multi-select>`
|
|
323
|
+
|
|
324
|
+
Multi-select with chips, search, select all.
|
|
325
|
+
|
|
326
|
+
| Input | Type | Default |
|
|
327
|
+
|-------|------|---------|
|
|
328
|
+
| `value` | `model<string[]>` | `[]` |
|
|
329
|
+
| `options` | `(DropdownOption \| string)[]` | `[]` |
|
|
330
|
+
| `placeholder` | `string` | `'Select...'` |
|
|
331
|
+
| `size` | `FieldSize` | `'md'` |
|
|
332
|
+
| `disabled` | `boolean` | `false` |
|
|
333
|
+
| `selectAll` | `boolean` | `true` |
|
|
334
|
+
| `selectAllLabel` | `string` | `'Select All'` |
|
|
335
|
+
| `accentColor` | `string \| null` | `null` |
|
|
336
|
+
| `searchable` | `boolean` | `false` |
|
|
337
|
+
| `hint` | `string` | `''` |
|
|
338
|
+
| `errorMessage` | `string` | `''` |
|
|
339
|
+
| `error` | `boolean` | `false` |
|
|
340
|
+
| `width` | `string` | `''` |
|
|
341
|
+
| `config` | `MultiSelectConfig` | `{}` |
|
|
342
|
+
|
|
343
|
+
**`MultiSelectConfig`:**
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
interface MultiSelectConfig {
|
|
347
|
+
appendToParent?: boolean;
|
|
348
|
+
maxHeight?: string;
|
|
349
|
+
direction?: 'up' | 'down' | 'auto';
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
```html
|
|
354
|
+
<app-multi-select
|
|
355
|
+
[(value)]="roles"
|
|
356
|
+
[options]="['Admin', 'Editor', 'Viewer']"
|
|
357
|
+
searchable
|
|
358
|
+
/>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
#### `<app-file-upload>`
|
|
364
|
+
|
|
365
|
+
Drag-and-drop file upload with validation.
|
|
366
|
+
|
|
367
|
+
| Input | Type | Default |
|
|
368
|
+
|-------|------|---------|
|
|
369
|
+
| `files` | `model<File[]>` | `[]` |
|
|
370
|
+
| `accept` | `string` | `''` |
|
|
371
|
+
| `multiple` | `boolean` | `true` |
|
|
372
|
+
| `maxSize` | `number` (bytes) | `0` (unlimited) |
|
|
373
|
+
| `maxFiles` | `number` | `0` (unlimited) |
|
|
374
|
+
| `disabled` | `boolean` | `false` |
|
|
375
|
+
| `placeholder` | `string` | `'Drop files here or click to browse'` |
|
|
376
|
+
| `size` | `FieldSize` | `'md'` |
|
|
377
|
+
| `hint` | `string` | `''` |
|
|
378
|
+
| `errorMessage` | `string` | `''` |
|
|
379
|
+
| `error` | `boolean` | `false` |
|
|
380
|
+
|
|
381
|
+
```html
|
|
382
|
+
<app-file-upload [(files)]="docs" accept=".pdf,.docx" [maxSize]="5 * 1024 * 1024" />
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
### Data Display
|
|
388
|
+
|
|
389
|
+
#### `<app-badge>`
|
|
390
|
+
|
|
391
|
+
Small label/badge with variants.
|
|
392
|
+
|
|
393
|
+
| Input | Type | Default |
|
|
394
|
+
|-------|------|---------|
|
|
395
|
+
| `accentColor` | `string` | `'primary'` |
|
|
396
|
+
| `variant` | `'filled' \| 'outlined' \| 'subtle'` | `'filled'` |
|
|
397
|
+
| `size` | `FieldSize` | `'sm'` |
|
|
398
|
+
| `dot` | `boolean` | `false` |
|
|
399
|
+
|
|
400
|
+
```html
|
|
401
|
+
<app-badge variant="subtle" accentColor="success">Active</app-badge>
|
|
402
|
+
<app-badge [dot]="true" accentColor="danger" />
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
#### `<app-skeleton>`
|
|
408
|
+
|
|
409
|
+
Placeholder loader.
|
|
410
|
+
|
|
411
|
+
| Input | Type | Default |
|
|
412
|
+
|-------|------|---------|
|
|
413
|
+
| `variant` | `'text' \| 'circle' \| 'rect'` | `'text'` |
|
|
414
|
+
| `width` | `string` | `''` |
|
|
415
|
+
| `height` | `string` | `''` |
|
|
416
|
+
| `count` | `number` | `1` |
|
|
417
|
+
| `borderRadius` | `string` | `''` |
|
|
418
|
+
| `accentColor` | `string` | `''` |
|
|
419
|
+
|
|
420
|
+
```html
|
|
421
|
+
<app-skeleton variant="circle" width="40px" height="40px" />
|
|
422
|
+
<app-skeleton variant="text" count="3" />
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
#### `<app-table-skeleton>`
|
|
428
|
+
|
|
429
|
+
Table skeleton loader.
|
|
430
|
+
|
|
431
|
+
| Input | Type | Default |
|
|
432
|
+
|-------|------|---------|
|
|
433
|
+
| `rows` | `number` | `5` |
|
|
434
|
+
| `columns` | `number \| TableSkeletonColumn[]` | `4` |
|
|
435
|
+
|
|
436
|
+
```html
|
|
437
|
+
<app-table-skeleton [rows]="8" [columns]="6" />
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
#### `<app-tooltip>`
|
|
443
|
+
|
|
444
|
+
Overflow-detection tooltip.
|
|
445
|
+
|
|
446
|
+
| Input | Type | Default |
|
|
447
|
+
|-------|------|---------|
|
|
448
|
+
| `text` | `string` | `''` |
|
|
449
|
+
| `position` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'top'` |
|
|
450
|
+
| `disabled` | `boolean` | `false` |
|
|
451
|
+
| `alwaysShow` | `boolean` | `false` |
|
|
452
|
+
|
|
453
|
+
```html
|
|
454
|
+
<app-tooltip text="This is a tooltip" position="top">
|
|
455
|
+
<span>Hover me</span>
|
|
456
|
+
</app-tooltip>
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
#### `<app-table>`
|
|
462
|
+
|
|
463
|
+
Full-featured data table with sorting, column filtering, text search, pagination, row selection, column resize, column toggle, Excel/PDF export, row expansion, sticky header, and skeleton loading.
|
|
464
|
+
|
|
465
|
+
| Input | Type | Default | Description |
|
|
466
|
+
|-------|------|---------|-------------|
|
|
467
|
+
| `columns` | `ColumnDef[]` | `[]` | Column definitions |
|
|
468
|
+
| `rows` | `Record<string, unknown>[]` | `[]` | Data rows |
|
|
469
|
+
| `striped` | `boolean` | `false` | Alternating row colors |
|
|
470
|
+
| `hoverable` | `boolean` | `true` | Highlight row on hover |
|
|
471
|
+
| `sortable` | `boolean` | `false` | Enable column sorting |
|
|
472
|
+
| `emptyMessage` | `string` | `'No data available'` | Text when no rows match |
|
|
473
|
+
| `width` | `string` | `''` | CSS width |
|
|
474
|
+
| `loading` | `boolean` | `false` | Show skeleton loader |
|
|
475
|
+
| `searchable` | `boolean` | `false` | Global text search bar |
|
|
476
|
+
| `filterable` | `boolean` | `false` | Per-column filter dropdowns |
|
|
477
|
+
| `skeletonRows` | `number` | `5` | Skeleton row count during loading |
|
|
478
|
+
| `resizable` | `boolean` | `false` | Drag column borders to resize |
|
|
479
|
+
| `pageable` | `boolean` | `false` | Pagination controls |
|
|
480
|
+
| `pageSize` | `number` | `10` | Rows per page |
|
|
481
|
+
| `pageSizeOptions` | `number[]` | `[5, 10, 20, 50]` | Page size choices |
|
|
482
|
+
| `selectable` | `boolean` | `false` | Checkbox row selection |
|
|
483
|
+
| `selectionMode` | `'single' \| 'multiple'` | `'multiple'` | Single or multi select |
|
|
484
|
+
| `selectionKey` | `string` | `''` | Row property for selection identity |
|
|
485
|
+
| `stickyHeader` | `boolean` | `false` | Fixed header on scroll |
|
|
486
|
+
| `exportable` | `boolean` | `false` | Excel (xlsx) + PDF export buttons |
|
|
487
|
+
| `columnToggle` | `boolean` | `false` | Show/hide columns menu |
|
|
488
|
+
| `expandable` | `boolean` | `false` | Expandable row detail |
|
|
489
|
+
|
|
490
|
+
| Output | Emit Type | Description |
|
|
491
|
+
|--------|-----------|-------------|
|
|
492
|
+
| `sortChange` | `SortChange` | Emitted when sort column/direction changes |
|
|
493
|
+
| `pageChange` | `number` | Emitted on page navigation |
|
|
494
|
+
| `selectedRows` | `Record<string, unknown>[]` | Emitted when selection changes |
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
interface SortChange {
|
|
498
|
+
key: string;
|
|
499
|
+
direction: 'asc' | 'desc' | '';
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**`ColumnDef`:**
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
interface ColumnDef {
|
|
507
|
+
key: string; // Property name in row data
|
|
508
|
+
header: string; // Display header text
|
|
509
|
+
sortable?: boolean; // Allow sorting on this column
|
|
510
|
+
width?: string; // CSS width (e.g. '120px', '2fr')
|
|
511
|
+
align?: 'left' | 'center' | 'right'; // Text alignment
|
|
512
|
+
cell?: (row: Record<string, unknown>) => string; // Custom cell renderer
|
|
513
|
+
rowSpan?: (row, index, rows) => number; // Dynamic rowspan (0=hidden, >1=span)
|
|
514
|
+
actions?: ActionDef[]; // Action buttons per row
|
|
515
|
+
filterable?: boolean; // Show filter dropdown
|
|
516
|
+
filterOptions?: { label: string; value: string }[]; // Filter choices
|
|
517
|
+
footer?: (rows: Record<string, unknown>[]) => string; // Footer text aggregator
|
|
518
|
+
truncate?: boolean; // Truncate long text
|
|
519
|
+
maxChars?: number; // Max chars before truncation
|
|
520
|
+
}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**`ActionDef`:**
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
interface ActionDef {
|
|
527
|
+
icon?: string; // PrimeIcon class (e.g. 'pi pi-pencil')
|
|
528
|
+
label?: string; // Button text (falls back to icon-only)
|
|
529
|
+
accentColor?: string; // Button color
|
|
530
|
+
visible?: (row: Record<string, unknown>) => boolean; // Conditional visibility
|
|
531
|
+
disabled?: (row: Record<string, unknown>) => boolean; // Conditional disable
|
|
532
|
+
click: (row: Record<string, unknown>) => void; // Click handler
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**Complete usage example:**
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
import { Component } from '@angular/core';
|
|
540
|
+
import { ColumnDef, SortChange } from 'ryzen-ui';
|
|
541
|
+
|
|
542
|
+
@Component({ ... })
|
|
543
|
+
export class MyComponent {
|
|
544
|
+
cols: ColumnDef[] = [
|
|
545
|
+
{ key: 'name', header: 'Name', sortable: true, width: '150px' },
|
|
546
|
+
{ key: 'email', header: 'Email', sortable: true, filterable: true, filterOptions: [] },
|
|
547
|
+
{ key: 'role', header: 'Role', sortable: true },
|
|
548
|
+
{
|
|
549
|
+
key: 'actions',
|
|
550
|
+
header: '',
|
|
551
|
+
width: '100px',
|
|
552
|
+
align: 'center',
|
|
553
|
+
actions: [
|
|
554
|
+
{
|
|
555
|
+
icon: 'pi pi-pencil',
|
|
556
|
+
accentColor: 'primary',
|
|
557
|
+
click: row => this.edit(row),
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
icon: 'pi pi-trash',
|
|
561
|
+
accentColor: 'danger',
|
|
562
|
+
visible: row => row['status'] !== 'archived',
|
|
563
|
+
click: row => this.delete(row),
|
|
564
|
+
},
|
|
565
|
+
],
|
|
566
|
+
},
|
|
567
|
+
];
|
|
568
|
+
|
|
569
|
+
data = [
|
|
570
|
+
{ name: 'Alice', email: 'alice@example.com', role: 'Admin', status: 'active' },
|
|
571
|
+
{ name: 'Bob', email: 'bob@example.com', role: 'User', status: 'active' },
|
|
572
|
+
];
|
|
573
|
+
|
|
574
|
+
onSort(ev: SortChange) { console.log('sort', ev); }
|
|
575
|
+
onPage(page: number) { console.log('page', page); }
|
|
576
|
+
onSelect(rows: Record<string, unknown>[]) { console.log('selected', rows); }
|
|
577
|
+
edit(row: any) { /* ... */ }
|
|
578
|
+
delete(row: any) { /* ... */ }
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
```html
|
|
583
|
+
<app-table
|
|
584
|
+
[columns]="cols"
|
|
585
|
+
[rows]="data"
|
|
586
|
+
sortable
|
|
587
|
+
pageable
|
|
588
|
+
searchable
|
|
589
|
+
filterable
|
|
590
|
+
selectable
|
|
591
|
+
selectionKey="email"
|
|
592
|
+
exportable
|
|
593
|
+
columnToggle
|
|
594
|
+
resizable
|
|
595
|
+
striped
|
|
596
|
+
stickyHeader
|
|
597
|
+
(sortChange)="onSort($event)"
|
|
598
|
+
(pageChange)="onPage($event)"
|
|
599
|
+
(selectedRows)="onSelect($event)"
|
|
600
|
+
/>
|
|
601
|
+
|
|
602
|
+
<!-- With expandable rows -->
|
|
603
|
+
<app-table [columns]="cols" [rows]="data" expandable>
|
|
604
|
+
<ng-template #rowDetail let-row>
|
|
605
|
+
<div style="padding: 1rem">
|
|
606
|
+
<p><strong>Email:</strong> {{ row['email'] }}</p>
|
|
607
|
+
<p><strong>Role:</strong> {{ row['role'] }}</p>
|
|
608
|
+
</div>
|
|
609
|
+
</ng-template>
|
|
610
|
+
</app-table>
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
#### `<app-image-upload>`
|
|
616
|
+
|
|
617
|
+
Image uploader with thumbnail previews.
|
|
618
|
+
|
|
619
|
+
| Input | Type | Default |
|
|
620
|
+
|-------|------|---------|
|
|
621
|
+
| `images` | `model<ImageFile[]>` | `[]` |
|
|
622
|
+
| `multiple` | `boolean` | `true` |
|
|
623
|
+
| `maxFiles` | `number` | `4` |
|
|
624
|
+
| `maxSize` | `number` (bytes) | `2097152` (2 MB) |
|
|
625
|
+
| `disabled` | `boolean` | `false` |
|
|
626
|
+
| `size` | `FieldSize` | `'md'` |
|
|
627
|
+
| `placeholder` | `string` | `'Drop images or click'` |
|
|
628
|
+
| `errorMessage` | `string` | `''` |
|
|
629
|
+
| `error` | `boolean` | `false` |
|
|
630
|
+
|
|
631
|
+
```typescript
|
|
632
|
+
interface ImageFile {
|
|
633
|
+
file: File;
|
|
634
|
+
url: string;
|
|
635
|
+
}
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
```html
|
|
639
|
+
<app-image-upload [(images)]="photos" [maxFiles]="6" />
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
#### `<app-carousel>`
|
|
645
|
+
|
|
646
|
+
Image carousel with autoplay, dots, arrows.
|
|
647
|
+
|
|
648
|
+
| Input | Type | Default |
|
|
649
|
+
|-------|------|---------|
|
|
650
|
+
| `items` | `CarouselItem[]` | **required** |
|
|
651
|
+
| `height` | `string` | `'320px'` |
|
|
652
|
+
| `autoPlay` | `boolean` | `true` |
|
|
653
|
+
| `interval` | `number` (ms) | `4000` |
|
|
654
|
+
| `showArrows` | `boolean` | `true` |
|
|
655
|
+
| `showDots` | `boolean` | `true` |
|
|
656
|
+
|
|
657
|
+
```typescript
|
|
658
|
+
interface CarouselItem {
|
|
659
|
+
src: string;
|
|
660
|
+
alt?: string;
|
|
661
|
+
title?: string;
|
|
662
|
+
description?: string;
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
```html
|
|
667
|
+
<app-carousel [items]="slides" [autoPlay]="false" />
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
#### `<app-banner-slider>`
|
|
673
|
+
|
|
674
|
+
Hero banner with CTA button and text overlays.
|
|
675
|
+
|
|
676
|
+
| Input | Type | Default |
|
|
677
|
+
|-------|------|---------|
|
|
678
|
+
| `items` | `BannerItem[]` | **required** |
|
|
679
|
+
| `height` | `string` | `'400px'` |
|
|
680
|
+
| `autoPlay` | `boolean` | `true` |
|
|
681
|
+
| `interval` | `number` (ms) | `5000` |
|
|
682
|
+
| `showArrows` | `boolean` | `true` |
|
|
683
|
+
| `showDots` | `boolean` | `true` |
|
|
684
|
+
| `overlayMode` | `'left' \| 'center' \| 'right'` | `'left'` |
|
|
685
|
+
|
|
686
|
+
| Output | Emit Type |
|
|
687
|
+
|--------|-----------|
|
|
688
|
+
| `ctaClick` | `BannerItem` |
|
|
689
|
+
|
|
690
|
+
```typescript
|
|
691
|
+
interface BannerItem {
|
|
692
|
+
src: string;
|
|
693
|
+
alt?: string;
|
|
694
|
+
title?: string;
|
|
695
|
+
subtitle?: string;
|
|
696
|
+
description?: string;
|
|
697
|
+
ctaLabel?: string;
|
|
698
|
+
ctaLink?: string;
|
|
699
|
+
}
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
```html
|
|
703
|
+
<app-banner-slider [items]="banners" (ctaClick)="onCtaClick($event)" />
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
#### `<app-empty-state>`
|
|
709
|
+
|
|
710
|
+
Centered placeholder with icon and action.
|
|
711
|
+
|
|
712
|
+
| Input | Type | Default |
|
|
713
|
+
|-------|------|---------|
|
|
714
|
+
| `icon` | `string` | `''` |
|
|
715
|
+
| `title` | `string` | `''` |
|
|
716
|
+
| `message` | `string` | `''` |
|
|
717
|
+
| `width` | `string` | `''` |
|
|
718
|
+
|
|
719
|
+
```html
|
|
720
|
+
<app-empty-state icon="pi pi-inbox" title="No messages" message="You have no unread messages" />
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
---
|
|
724
|
+
|
|
725
|
+
### Feedback
|
|
726
|
+
|
|
727
|
+
#### `<app-alert>`
|
|
728
|
+
|
|
729
|
+
Colored alert banner with icon and dismiss.
|
|
730
|
+
|
|
731
|
+
| Input | Type | Default |
|
|
732
|
+
|-------|------|---------|
|
|
733
|
+
| `type` | `'success' \| 'warning' \| 'danger' \| 'info'` | `'info'` |
|
|
734
|
+
| `title` | `string` | `''` |
|
|
735
|
+
| `dismissible` | `boolean` | `true` |
|
|
736
|
+
| `showIcon` | `boolean` | `true` |
|
|
737
|
+
| `width` | `string` | `''` |
|
|
738
|
+
|
|
739
|
+
| Output | Emit Type |
|
|
740
|
+
|--------|-----------|
|
|
741
|
+
| `dismiss` | `void` |
|
|
742
|
+
|
|
743
|
+
```html
|
|
744
|
+
<app-alert type="success" title="Operation completed successfully" (dismiss)="onDismiss()" />
|
|
745
|
+
<app-alert type="danger" [dismissible]="false" title="Critical error" />
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
---
|
|
749
|
+
|
|
750
|
+
#### `<app-spinner>`
|
|
751
|
+
|
|
752
|
+
Loading spinner with 5 variants, image/text support, overlay mode.
|
|
753
|
+
|
|
754
|
+
| Input | Type | Default |
|
|
755
|
+
|-------|------|---------|
|
|
756
|
+
| `variant` | `'circle' \| 'dual-ring' \| 'dots' \| 'pulse' \| 'bars'` | `'dual-ring'` |
|
|
757
|
+
| `size` | `FieldSize` | `'md'` |
|
|
758
|
+
| `accentColor` | `string` | `'primary'` |
|
|
759
|
+
| `overlay` | `boolean` | `false` |
|
|
760
|
+
| `text` | `string` | `''` |
|
|
761
|
+
| `imageUrl` | `string` | `''` |
|
|
762
|
+
|
|
763
|
+
```html
|
|
764
|
+
<app-spinner variant="dots" text="Loading..." />
|
|
765
|
+
<app-spinner [overlay]="true" variant="bars" />
|
|
766
|
+
<app-spinner variant="circle" [imageUrl]="'/assets/logo.png'" />
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
---
|
|
770
|
+
|
|
771
|
+
#### `<app-toast>`
|
|
772
|
+
|
|
773
|
+
Toast notification container (used with `ToastService`).
|
|
774
|
+
|
|
775
|
+
| Input | Type | Default |
|
|
776
|
+
|-------|------|---------|
|
|
777
|
+
| `position` | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left'` | `'top-right'` |
|
|
778
|
+
| `maxVisible` | `number` | `5` |
|
|
779
|
+
|
|
780
|
+
```html
|
|
781
|
+
<app-toast position="top-right" />
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
**`ToastService`** (providedIn: 'root'):
|
|
785
|
+
|
|
786
|
+
```typescript
|
|
787
|
+
class ToastService {
|
|
788
|
+
readonly toasts: Signal<ToastMessage[]>;
|
|
789
|
+
|
|
790
|
+
show(type: ToastType, message: string, config?: { title?: string; duration?: number }): void;
|
|
791
|
+
success(message: string, config?: { title?: string; duration?: number }): void;
|
|
792
|
+
error(message: string, config?: { title?: string; duration?: number }): void;
|
|
793
|
+
warning(message: string, config?: { title?: string; duration?: number }): void;
|
|
794
|
+
info(message: string, config?: { title?: string; duration?: number }): void;
|
|
795
|
+
remove(id: number): void;
|
|
796
|
+
clear(): void;
|
|
797
|
+
}
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
```typescript
|
|
801
|
+
@Component({ ... })
|
|
802
|
+
export class MyComponent {
|
|
803
|
+
private toast = inject(ToastService);
|
|
804
|
+
|
|
805
|
+
save() {
|
|
806
|
+
this.toast.success('Data saved', { title: 'Success', duration: 3000 });
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
813
|
+
### Navigation
|
|
814
|
+
|
|
815
|
+
#### `<app-accordion>` + `<app-accordion-panel>`
|
|
816
|
+
|
|
817
|
+
Collapsible accordion with single/multi mode.
|
|
818
|
+
|
|
819
|
+
**`<app-accordion>`:**
|
|
820
|
+
|
|
821
|
+
| Input | Type | Default |
|
|
822
|
+
|-------|------|---------|
|
|
823
|
+
| `multi` | `boolean` | `false` |
|
|
824
|
+
|
|
825
|
+
**`<app-accordion-panel>`:**
|
|
826
|
+
|
|
827
|
+
| Input | Type | Default |
|
|
828
|
+
|-------|------|---------|
|
|
829
|
+
| `expanded` | `model<boolean>` | `false` |
|
|
830
|
+
| `title` | `string` | `''` |
|
|
831
|
+
| `disabled` | `boolean` | `false` |
|
|
832
|
+
|
|
833
|
+
```html
|
|
834
|
+
<app-accordion [multi]="true">
|
|
835
|
+
<app-accordion-panel title="Section 1" [(expanded)]="sec1">
|
|
836
|
+
Content for section 1
|
|
837
|
+
</app-accordion-panel>
|
|
838
|
+
<app-accordion-panel title="Section 2" [disabled]="true">
|
|
839
|
+
Disabled content
|
|
840
|
+
</app-accordion-panel>
|
|
841
|
+
</app-accordion>
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
---
|
|
845
|
+
|
|
846
|
+
#### `<app-sidebar>`
|
|
847
|
+
|
|
848
|
+
Responsive sidebar with collapse, submenus, logout.
|
|
849
|
+
|
|
850
|
+
| Input | Type | Default |
|
|
851
|
+
|-------|------|---------|
|
|
852
|
+
| `isOpen` | `boolean` | `false` |
|
|
853
|
+
| `isCollapsed` | `boolean` | `false` |
|
|
854
|
+
| `items` | `SidebarItem[]` | `[]` |
|
|
855
|
+
| `title` | `string` | `'Menu'` |
|
|
856
|
+
| `width` | `string` | `'280px'` |
|
|
857
|
+
| `activeId` | `string \| null` | `null` |
|
|
858
|
+
|
|
859
|
+
| Output | Emit Type |
|
|
860
|
+
|--------|-----------|
|
|
861
|
+
| `toggleOpen` | `void` |
|
|
862
|
+
| `toggleCollapse` | `void` |
|
|
863
|
+
| `itemClick` | `SidebarItem` |
|
|
864
|
+
| `logout` | `void` |
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
interface SidebarItem {
|
|
868
|
+
id: string;
|
|
869
|
+
label: string;
|
|
870
|
+
icon?: string;
|
|
871
|
+
route?: string;
|
|
872
|
+
children?: SidebarItem[];
|
|
873
|
+
}
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
```html
|
|
877
|
+
<app-sidebar
|
|
878
|
+
[isOpen]="sidebarOpen"
|
|
879
|
+
[isCollapsed]="sidebarCollapsed"
|
|
880
|
+
[items]="menuItems"
|
|
881
|
+
[activeId]="currentRoute"
|
|
882
|
+
(itemClick)="onNav($event)"
|
|
883
|
+
(toggleCollapse)="sidebarCollapsed = !sidebarCollapsed"
|
|
884
|
+
(logout)="onLogout()"
|
|
885
|
+
/>
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
---
|
|
889
|
+
|
|
890
|
+
#### `<app-nav>`
|
|
891
|
+
|
|
892
|
+
Top navbar with desktop dropdowns, mobile hamburger menu.
|
|
893
|
+
|
|
894
|
+
| Input | Type | Default |
|
|
895
|
+
|-------|------|---------|
|
|
896
|
+
| `items` | `NavItem[]` | `[]` |
|
|
897
|
+
| `brandText` | `string` | `'Brand'` |
|
|
898
|
+
| `brandIcon` | `string` | `'pi pi-box'` |
|
|
899
|
+
| `fixed` | `boolean` | `true` |
|
|
900
|
+
|
|
901
|
+
| Output | Emit Type |
|
|
902
|
+
|--------|-----------|
|
|
903
|
+
| `logout` | `void` |
|
|
904
|
+
|
|
905
|
+
```typescript
|
|
906
|
+
interface NavItem {
|
|
907
|
+
id: string;
|
|
908
|
+
label: string;
|
|
909
|
+
icon?: string;
|
|
910
|
+
route?: string;
|
|
911
|
+
children?: NavItem[];
|
|
912
|
+
}
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
```html
|
|
916
|
+
<app-nav
|
|
917
|
+
[items]="navItems"
|
|
918
|
+
brandText="MyApp"
|
|
919
|
+
brandIcon="pi pi-cog"
|
|
920
|
+
(logout)="onLogout()"
|
|
921
|
+
/>
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
---
|
|
925
|
+
|
|
926
|
+
### Overlay
|
|
927
|
+
|
|
928
|
+
#### `<app-modal>`
|
|
929
|
+
|
|
930
|
+
Modal dialog with title slot, footer slot, close on backdrop/escape.
|
|
931
|
+
|
|
932
|
+
| Input | Type | Default |
|
|
933
|
+
|-------|------|---------|
|
|
934
|
+
| `open` | `model<boolean>` | `false` |
|
|
935
|
+
| `title` | `string` | `''` |
|
|
936
|
+
| `width` | `string` | `''` |
|
|
937
|
+
| `config` | `ModalConfig` | `{}` |
|
|
938
|
+
|
|
939
|
+
| Output | Emit Type |
|
|
940
|
+
|--------|-----------|
|
|
941
|
+
| `closed` | `void` |
|
|
942
|
+
|
|
943
|
+
**Methods**: `close()`
|
|
944
|
+
|
|
945
|
+
```typescript
|
|
946
|
+
interface ModalConfig {
|
|
947
|
+
closeOnBackdrop?: boolean; // default true
|
|
948
|
+
closeOnEscape?: boolean; // default true
|
|
949
|
+
showCloseButton?: boolean; // default true
|
|
950
|
+
maxWidth?: string; // default '500px'
|
|
951
|
+
}
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
```html
|
|
955
|
+
<app-modal [(open)]="modalOpen" title="Edit User" [config]="{ maxWidth: '600px' }">
|
|
956
|
+
<p>Modal body content</p>
|
|
957
|
+
<ng-template #modalFooter>
|
|
958
|
+
<button (click)="save()">Save</button>
|
|
959
|
+
</ng-template>
|
|
960
|
+
</app-modal>
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
---
|
|
964
|
+
|
|
965
|
+
#### `<app-confirm-dialog>`
|
|
966
|
+
|
|
967
|
+
Pre-built confirmation dialog with customizable icon, accent, and button text.
|
|
968
|
+
|
|
969
|
+
| Input | Type | Default |
|
|
970
|
+
|-------|------|---------|
|
|
971
|
+
| `open` | `model<boolean>` | `false` |
|
|
972
|
+
| `message` | `string` | `''` |
|
|
973
|
+
| `config` | `ConfirmDialogConfig` | `{}` |
|
|
974
|
+
|
|
975
|
+
| Output | Emit Type |
|
|
976
|
+
|--------|-----------|
|
|
977
|
+
| `confirm` | `void` |
|
|
978
|
+
| `cancel` | `void` |
|
|
979
|
+
|
|
980
|
+
```typescript
|
|
981
|
+
interface ConfirmDialogConfig {
|
|
982
|
+
title?: string;
|
|
983
|
+
confirmText?: string;
|
|
984
|
+
cancelText?: string;
|
|
985
|
+
icon?: string;
|
|
986
|
+
confirmAccent?: ThemeColor | string;
|
|
987
|
+
closeOnBackdrop?: boolean;
|
|
988
|
+
closeOnEscape?: boolean;
|
|
989
|
+
}
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
```html
|
|
993
|
+
<app-confirm-dialog
|
|
994
|
+
[(open)]="confirmOpen"
|
|
995
|
+
message="Are you sure you want to delete this item?"
|
|
996
|
+
[config]="{ title: 'Confirm Delete', confirmAccent: 'danger', confirmText: 'Delete' }"
|
|
997
|
+
(confirm)="deleteItem()"
|
|
998
|
+
(cancel)="confirmOpen = false"
|
|
999
|
+
/>
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
---
|
|
1003
|
+
|
|
1004
|
+
## Shared Types
|
|
1005
|
+
|
|
1006
|
+
```typescript
|
|
1007
|
+
type FieldSize = 'sm' | 'md' | 'lg';
|
|
1008
|
+
type ThemeColor = 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'danger' | 'info';
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
---
|
|
1012
|
+
|
|
1013
|
+
## Development
|
|
1014
|
+
|
|
1015
|
+
```bash
|
|
1016
|
+
# Build the library
|
|
1017
|
+
npm run build
|
|
1018
|
+
|
|
1019
|
+
# Watch mode
|
|
1020
|
+
npm run watch
|
|
1021
|
+
|
|
1022
|
+
# Publish
|
|
1023
|
+
npm run publish
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
Build artifacts go to `dist/rz-lib/`.
|
|
1027
|
+
|
|
1028
|
+
---
|
|
1029
|
+
|
|
1030
|
+
## Conventions
|
|
1031
|
+
|
|
1032
|
+
| Convention | Value |
|
|
1033
|
+
|------------|-------|
|
|
1034
|
+
| Change detection | `ChangeDetectionStrategy.OnPush` |
|
|
1035
|
+
| State management | Angular `signal()` / `model()` / `computed()` / `input()` |
|
|
1036
|
+
| Styling | Pure CSS custom properties — no `@angular/animations` |
|
|
1037
|
+
| Theming | CSS variables with oklch fallbacks (light + dark) |
|
|
1038
|
+
| Positioning | CSS `position: fixed` / `absolute` — no CDK overlay |
|
|
1039
|
+
| Animation | `@keyframes` + `setTimeout` for close sequence |
|
|
1040
|
+
| Panel pattern | Open: `setTimeout(0)` → enter animation. Close: `_isClosing` signal → wait 150ms → remove |
|
|
1041
|
+
| `document:click` | Manually bound/unbound on open/close |
|
|
1042
|
+
| Timer cleanup | `_closeTimer` field + `ngOnDestroy` |
|
|
1043
|
+
| Field sizes | `sm` / `md` / `lg` |
|
|
1044
|
+
| Accent colors | Named theme colors or any raw CSS color string |
|
|
1045
|
+
|
|
1046
|
+
---
|
|
1047
|
+
|
|
1048
|
+
## Dependencies
|
|
1049
|
+
|
|
1050
|
+
| Dependency | Type | Required |
|
|
1051
|
+
|------------|------|----------|
|
|
1052
|
+
| `@angular/core` | peer | Yes |
|
|
1053
|
+
| `@angular/common` | peer | Yes |
|
|
1054
|
+
| `primeicons` | peer | Yes |
|
|
1055
|
+
| `exceljs` | optional peer | Table export (XLSX) |
|
|
1056
|
+
| `jspdf` | optional peer | Table export (PDF) |
|
|
1057
|
+
| `html2canvas` | optional peer | Table export (PDF) |
|
|
1058
|
+
|
|
1059
|
+
---
|
|
1060
|
+
|
|
1061
|
+
## License
|
|
1062
|
+
|
|
1063
|
+
MIT
|