@devjuliovilla/jv-ui 1.14.0 → 1.16.0
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/LICENSE +21 -21
- package/README.md +1059 -1033
- package/fesm2022/devjuliovilla-jv-ui.mjs +3385 -4044
- package/fesm2022/devjuliovilla-jv-ui.mjs.map +1 -1
- package/package.json +85 -1
- package/secondary/auth-pages.d.ts +1 -0
- package/secondary/auth-pages.mjs +1 -0
- package/secondary/buttons.d.ts +1 -0
- package/secondary/buttons.mjs +1 -0
- package/secondary/calendar.d.ts +1 -0
- package/secondary/calendar.mjs +1 -0
- package/secondary/collapsible.d.ts +1 -0
- package/secondary/collapsible.mjs +1 -0
- package/secondary/config.d.ts +1 -0
- package/secondary/config.mjs +1 -0
- package/secondary/containers.d.ts +1 -0
- package/secondary/containers.mjs +1 -0
- package/secondary/core.d.ts +1 -0
- package/secondary/core.mjs +1 -0
- package/secondary/dialogs.d.ts +1 -0
- package/secondary/dialogs.mjs +1 -0
- package/secondary/feedback.d.ts +1 -0
- package/secondary/feedback.mjs +1 -0
- package/secondary/forms.d.ts +1 -0
- package/secondary/forms.mjs +1 -0
- package/secondary/gallery.d.ts +1 -0
- package/secondary/gallery.mjs +1 -0
- package/secondary/grid.d.ts +1 -0
- package/secondary/grid.mjs +1 -0
- package/secondary/i18n.d.ts +1 -0
- package/secondary/i18n.mjs +1 -0
- package/secondary/icons.d.ts +1 -0
- package/secondary/icons.mjs +1 -0
- package/secondary/layout.d.ts +1 -0
- package/secondary/layout.mjs +1 -0
- package/secondary/list.d.ts +1 -0
- package/secondary/list.mjs +1 -0
- package/secondary/pagination.d.ts +1 -0
- package/secondary/pagination.mjs +1 -0
- package/secondary/services.d.ts +1 -0
- package/secondary/services.mjs +1 -0
- package/secondary/slider.d.ts +1 -0
- package/secondary/slider.mjs +1 -0
- package/secondary/tabs.d.ts +1 -0
- package/secondary/tabs.mjs +1 -0
- package/secondary/tooltip.d.ts +1 -0
- package/secondary/tooltip.mjs +1 -0
- package/styles/jv-theme.css +135 -135
- package/types/devjuliovilla-jv-ui.d.ts +13 -21
package/README.md
CHANGED
|
@@ -1,1033 +1,1059 @@
|
|
|
1
|
-
# @devjuliovilla/jv-ui
|
|
2
|
-
|
|
3
|
-
Accessibility-first Angular component library built with signals and standalone APIs. Includes components, forms, data grids, dialogs, layout primitives, services, and i18n infrastructure.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @devjuliovilla/jv-ui
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
import {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
|
181
|
-
|
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
|
276
|
-
|
|
|
277
|
-
|
|
|
278
|
-
|
|
|
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
|
-
| `
|
|
308
|
-
| `
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
|
313
|
-
|
|
|
314
|
-
| `
|
|
315
|
-
| `
|
|
316
|
-
| `
|
|
317
|
-
| `
|
|
318
|
-
| `
|
|
319
|
-
| `
|
|
320
|
-
| `
|
|
321
|
-
| `
|
|
322
|
-
| `
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
|
329
|
-
|
|
|
330
|
-
| `
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
334
|
-
| `
|
|
335
|
-
| `
|
|
336
|
-
| `
|
|
337
|
-
| `
|
|
338
|
-
| `
|
|
339
|
-
| `
|
|
340
|
-
| `
|
|
341
|
-
| `
|
|
342
|
-
| `
|
|
343
|
-
| `
|
|
344
|
-
| `
|
|
345
|
-
| `
|
|
346
|
-
| `
|
|
347
|
-
| `
|
|
348
|
-
| `
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
|
546
|
-
|
|
|
547
|
-
| `
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
|
653
|
-
|
|
|
654
|
-
| `
|
|
655
|
-
| `
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
|
732
|
-
|
|
|
733
|
-
| `
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
```
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
|
954
|
-
|
|
|
955
|
-
| `
|
|
956
|
-
| `
|
|
957
|
-
| `
|
|
958
|
-
| `
|
|
959
|
-
| `
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
|
979
|
-
|
|
|
980
|
-
| `
|
|
981
|
-
| `
|
|
982
|
-
| `
|
|
983
|
-
| `
|
|
984
|
-
| `
|
|
985
|
-
| `
|
|
986
|
-
| `
|
|
987
|
-
|
|
988
|
-
|
|
|
989
|
-
|
|
|
990
|
-
| `
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
|
1007
|
-
|
|
|
1008
|
-
| `
|
|
1009
|
-
| `
|
|
1010
|
-
| `
|
|
1011
|
-
| `
|
|
1012
|
-
| `
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
- `
|
|
1024
|
-
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1
|
+
# @devjuliovilla/jv-ui
|
|
2
|
+
|
|
3
|
+
Accessibility-first Angular component library built with signals and standalone APIs. Includes components, forms, data grids, dialogs, layout primitives, services, and i18n infrastructure.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @devjuliovilla/jv-ui
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This release line targets Angular 21. Future package changes will be tracked in the repository [CHANGELOG.md](https://github.com/devjuliovilla/JV_UI/blob/develop/CHANGELOG.md).
|
|
12
|
+
|
|
13
|
+
Import the theme CSS in your `angular.json` or `styles` array:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
"styles": ["@devjuliovilla/jv-ui/styles/jv-theme.css"]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { ApplicationConfig } from '@angular/core';
|
|
23
|
+
import { provideJvUi } from '@devjuliovilla/jv-ui';
|
|
24
|
+
|
|
25
|
+
export const appConfig: ApplicationConfig = {
|
|
26
|
+
providers: [provideJvUi({ messagesLocale: 'en', formatLocale: 'en-US' })],
|
|
27
|
+
};
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { Component } from '@angular/core';
|
|
32
|
+
import { JvButtonComponent } from '@devjuliovilla/jv-ui';
|
|
33
|
+
|
|
34
|
+
@Component({
|
|
35
|
+
selector: 'app-root',
|
|
36
|
+
standalone: true,
|
|
37
|
+
imports: [JvButtonComponent],
|
|
38
|
+
template: '<jv-button variant="primary">Hello</jv-button>',
|
|
39
|
+
})
|
|
40
|
+
export class App {}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Secondary entry points
|
|
44
|
+
|
|
45
|
+
The main entry point remains supported:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { JvGridComponent } from '@devjuliovilla/jv-ui';
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Feature entry points are also available when you prefer imports grouped by module:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { JvGridComponent } from '@devjuliovilla/jv-ui/grid';
|
|
55
|
+
import { JvButtonComponent } from '@devjuliovilla/jv-ui/buttons';
|
|
56
|
+
import { JvInputComponent } from '@devjuliovilla/jv-ui/forms';
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Available feature paths are `auth-pages`, `buttons`, `calendar`, `collapsible`, `config`,
|
|
60
|
+
`containers`, `core`, `dialogs`, `feedback`, `forms`, `gallery`, `grid`, `i18n`, `icons`,
|
|
61
|
+
`layout`, `list`, `pagination`, `services`, `slider`, `tabs`, and `tooltip`.
|
|
62
|
+
|
|
63
|
+
Both import styles expose the same public symbols. Components keep their own standalone
|
|
64
|
+
dependencies, so importing `JvGridComponent` does not require importing its internal buttons,
|
|
65
|
+
inputs, or icons separately. Existing imports from `@devjuliovilla/jv-ui` remain supported.
|
|
66
|
+
|
|
67
|
+
## Components
|
|
68
|
+
|
|
69
|
+
| Category | Components |
|
|
70
|
+
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
71
|
+
| Buttons | `JvButton`, `JvButtonGroup`, `JvIconButton` |
|
|
72
|
+
| Calendar | `JvMonthCalendar`, `JvDayPlanner` — reusable month view and agenda primitives, usable together or independently |
|
|
73
|
+
| Forms | `JvInput`, `JvTextarea`, `JvCode`, `JvSelect`, `JvCheckbox`, `JvRadio`, `JvSwitch`, `JvDatePicker`, `JvModalSelector`, `JvFileInput`, `JvFormContainer` |
|
|
74
|
+
| Data Grid | `JvGrid` — full-featured data table (see below) |
|
|
75
|
+
| Containers | `JvCard`, `JvSection`, `JvDivider` |
|
|
76
|
+
| List | `JvList`, `JvListItem` — list with title, description, icons, badges, and interactive items |
|
|
77
|
+
| Tabs | `JvTabs`, `JvTabPanelDirective` — horizontal tabs with keyboard navigation and animated transitions |
|
|
78
|
+
| Slider | `JvImageSlider` — responsive image slider with thumbnails and keyboard navigation |
|
|
79
|
+
| Gallery | `JvImageGallery` — horizontal image gallery with wheel scrolling and full-screen viewer |
|
|
80
|
+
| Feedback | `JvAlert`, `JvBadge`, `JvLoader`, `JvToast` |
|
|
81
|
+
| Dialogs | `JvDialog`, `JvConfirmDialog`, `JvWindowModal` — compact dialogs plus a larger workflow overlay surface |
|
|
82
|
+
| Layout | `JvDashboardShell`, `JvSidebar`, `JvTopbar`, `JvBreadcrumb`, `JvPage` |
|
|
83
|
+
| Auth Pages | `JvLoginPage`, `JvForgotPasswordPage`, `JvChangePasswordPage` |
|
|
84
|
+
| Icon | `JvIcon` — Lucide-based icon component with 1500+ auto-generated icons |
|
|
85
|
+
| Pagination | `JvPagination` — standalone pagination with i18n |
|
|
86
|
+
|
|
87
|
+
## Calendar API
|
|
88
|
+
|
|
89
|
+
The scheduling primitives are split into **two separate components**:
|
|
90
|
+
|
|
91
|
+
- `JvMonthCalendar`: inline month grid with selection, navigation, and day markers
|
|
92
|
+
- `JvDayPlanner`: agenda / list view for scheduled items with optional grouping and expandable details
|
|
93
|
+
|
|
94
|
+
You can use either one on its own, or compose both together in a master-detail scheduling layout.
|
|
95
|
+
|
|
96
|
+
### JvMonthCalendar
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import {
|
|
100
|
+
JvMonthCalendarComponent,
|
|
101
|
+
JvMonthCalendarDayDirective,
|
|
102
|
+
JvMonthCalendarDayMeta,
|
|
103
|
+
} from '@devjuliovilla/jv-ui';
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Inputs:**
|
|
107
|
+
|
|
108
|
+
| Input | Type | Default | Description |
|
|
109
|
+
| -------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
110
|
+
| `value` | `Date \| string \| null` | `undefined` | Selected day |
|
|
111
|
+
| `visibleMonth` | `Date \| string \| null` | `undefined` | Controlled visible month independent from the selected day |
|
|
112
|
+
| `dayMeta` | `JvMonthCalendarDayMeta[]` | `[]` | Marker/counter metadata per day |
|
|
113
|
+
| `ariaLabel` | `string` | localized default | Accessible label for the grid |
|
|
114
|
+
| `monthLabel` | `string` | `Month` | Accessible label for the month selector |
|
|
115
|
+
| `yearLabel` | `string` | `Year` | Accessible label for the year selector |
|
|
116
|
+
| `minYear` | `number` | current year - 10 | Lower year bound for the year selector |
|
|
117
|
+
| `maxYear` | `number` | current year + 10 | Upper year bound for the year selector |
|
|
118
|
+
| `navigation` | `{ previousMonth?: boolean; nextMonth?: boolean; previousYear?: boolean; nextYear?: boolean }` | `{ previousMonth: true, nextMonth: true, previousYear: false, nextYear: false }` | Controls which month/year navigation buttons are rendered |
|
|
119
|
+
|
|
120
|
+
**Outputs:**
|
|
121
|
+
|
|
122
|
+
| Output | Type |
|
|
123
|
+
| -------------------- | ------ |
|
|
124
|
+
| `selectionChange` | `Date` |
|
|
125
|
+
| `visibleMonthChange` | `Date` |
|
|
126
|
+
|
|
127
|
+
**Day metadata:**
|
|
128
|
+
|
|
129
|
+
| Property | Type | Description |
|
|
130
|
+
| -------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
131
|
+
| `date` | `Date \| string` | Day associated with the marker |
|
|
132
|
+
| `count` | `number` | Optional numeric badge rendered inside the cell |
|
|
133
|
+
| `tone` | `'primary' \| 'secondary' \| 'success' \| 'warning' \| 'danger' \| 'info' \| 'neutral'` | Visual tone for the badge or dot |
|
|
134
|
+
| `label` | `string` | Extra accessibility text appended to the day `aria-label` |
|
|
135
|
+
|
|
136
|
+
**Template hook:**
|
|
137
|
+
|
|
138
|
+
Use `ng-template[jvMonthCalendarDay]` to fully customize the day cell.
|
|
139
|
+
|
|
140
|
+
**Standalone example:**
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { Component, signal } from '@angular/core';
|
|
144
|
+
import { JvMonthCalendarComponent, JvMonthCalendarDayMeta } from '@devjuliovilla/jv-ui';
|
|
145
|
+
|
|
146
|
+
@Component({
|
|
147
|
+
selector: 'app-calendar-only',
|
|
148
|
+
standalone: true,
|
|
149
|
+
imports: [JvMonthCalendarComponent],
|
|
150
|
+
template: `
|
|
151
|
+
<jv-month-calendar
|
|
152
|
+
[value]="selectedDate()"
|
|
153
|
+
[dayMeta]="calendarMeta"
|
|
154
|
+
(selectionChange)="selectedDate.set($event)"
|
|
155
|
+
/>
|
|
156
|
+
`,
|
|
157
|
+
})
|
|
158
|
+
export class CalendarOnlyComponent {
|
|
159
|
+
readonly selectedDate = signal(new Date(2026, 5, 15));
|
|
160
|
+
readonly calendarMeta: JvMonthCalendarDayMeta[] = [
|
|
161
|
+
{ date: '2026-06-15', count: 2, tone: 'primary', label: '2 activities' },
|
|
162
|
+
{ date: '2026-06-16', count: 1, tone: 'secondary', label: '1 activity' },
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### JvDayPlanner
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import {
|
|
171
|
+
JvDayPlannerComponent,
|
|
172
|
+
JvDayPlannerDetailDirective,
|
|
173
|
+
JvDayPlannerItem,
|
|
174
|
+
JvDayPlannerItemDirective,
|
|
175
|
+
} from '@devjuliovilla/jv-ui';
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Inputs:**
|
|
179
|
+
|
|
180
|
+
| Input | Type | Default | Description |
|
|
181
|
+
| ------------------------ | ---------------------- | ----------------- | ---------------------------------------- |
|
|
182
|
+
| `items` | `JvDayPlannerItem[]` | `[]` | Agenda items |
|
|
183
|
+
| `groupByDate` | `boolean` | `false` | Group items under a date heading |
|
|
184
|
+
| `emptyMessage` | `string` | localized default | Empty state copy |
|
|
185
|
+
| `ariaLabel` | `string` | localized default | Accessible label for the agenda |
|
|
186
|
+
| `defaultExpandedItemIds` | `(string \| number)[]` | `[]` | Items whose detail should start expanded |
|
|
187
|
+
|
|
188
|
+
**Outputs:**
|
|
189
|
+
|
|
190
|
+
| Output | Type |
|
|
191
|
+
| ------------ | ------------------ |
|
|
192
|
+
| `itemSelect` | `JvDayPlannerItem` |
|
|
193
|
+
|
|
194
|
+
**Planner item shape:**
|
|
195
|
+
|
|
196
|
+
| Property | Type | Description |
|
|
197
|
+
| ------------- | ------------------------ | ----------------------------------------- |
|
|
198
|
+
| `id` | `string \| number` | Stable identity |
|
|
199
|
+
| `title` | `string` | Primary label |
|
|
200
|
+
| `date` | `Date \| string \| null` | Logical day when start time is not enough |
|
|
201
|
+
| `start` | `Date \| string \| null` | Optional start datetime |
|
|
202
|
+
| `end` | `Date \| string \| null` | Optional end datetime |
|
|
203
|
+
| `status` | `string` | Status badge label |
|
|
204
|
+
| `statusTone` | `JvTone` | Visual tone for status badge |
|
|
205
|
+
| `type` | `string` | Type/category badge label |
|
|
206
|
+
| `typeTone` | `JvTone` | Visual tone for type badge |
|
|
207
|
+
| `responsible` | `string` | Responsible person |
|
|
208
|
+
| `location` | `string` | Location metadata |
|
|
209
|
+
| `details` | `string` | Expandable detail text |
|
|
210
|
+
| `completed` | `boolean` | Reduced emphasis for completed items |
|
|
211
|
+
|
|
212
|
+
**Template hooks:**
|
|
213
|
+
|
|
214
|
+
- `ng-template[jvDayPlannerItem]` customizes the visible item row
|
|
215
|
+
- `ng-template[jvDayPlannerDetail]` customizes the expanded detail panel
|
|
216
|
+
|
|
217
|
+
**Standalone example:**
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import { Component } from '@angular/core';
|
|
221
|
+
import { JvDayPlannerComponent, JvDayPlannerItem } from '@devjuliovilla/jv-ui';
|
|
222
|
+
|
|
223
|
+
@Component({
|
|
224
|
+
selector: 'app-planner-only',
|
|
225
|
+
standalone: true,
|
|
226
|
+
imports: [JvDayPlannerComponent],
|
|
227
|
+
template: `
|
|
228
|
+
<jv-day-planner [items]="items" [groupByDate]="true" (itemSelect)="handleSelect($event)" />
|
|
229
|
+
`,
|
|
230
|
+
})
|
|
231
|
+
export class PlannerOnlyComponent {
|
|
232
|
+
readonly items: JvDayPlannerItem[] = [
|
|
233
|
+
{
|
|
234
|
+
id: 1,
|
|
235
|
+
title: 'Venue setup',
|
|
236
|
+
start: '2026-06-15T08:30:00',
|
|
237
|
+
end: '2026-06-15T10:00:00',
|
|
238
|
+
status: 'Scheduled',
|
|
239
|
+
statusTone: 'primary',
|
|
240
|
+
type: 'Logistics',
|
|
241
|
+
responsible: 'Ana Lopez',
|
|
242
|
+
location: 'Main hall',
|
|
243
|
+
details: 'Prepare seating and signage.',
|
|
244
|
+
},
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
handleSelect(item: JvDayPlannerItem) {
|
|
248
|
+
console.log(item);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Combined example
|
|
254
|
+
|
|
255
|
+
Use both components together when you need a left-side month selector and a right-side agenda:
|
|
256
|
+
|
|
257
|
+
```html
|
|
258
|
+
<jv-month-calendar
|
|
259
|
+
[value]="selectedDate()"
|
|
260
|
+
[dayMeta]="calendarMeta()"
|
|
261
|
+
(selectionChange)="selectedDate.set($event)"
|
|
262
|
+
/>
|
|
263
|
+
|
|
264
|
+
<jv-day-planner
|
|
265
|
+
[items]="filteredItems()"
|
|
266
|
+
[groupByDate]="showAll()"
|
|
267
|
+
(itemSelect)="selectedItem.set($event)"
|
|
268
|
+
/>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Grid Features
|
|
272
|
+
|
|
273
|
+
| Feature | Description |
|
|
274
|
+
| ------------------ | ---------------------------------------------------------------- |
|
|
275
|
+
| Sorting | Client-side multi-column sorting with sort icons and `aria-sort` |
|
|
276
|
+
| Search | Global text search across visible columns |
|
|
277
|
+
| Pagination | Configurable page size, page range buttons, first/last/prev/next |
|
|
278
|
+
| Selection | Checkbox row selection with `selectedIds` / `selectionChange` |
|
|
279
|
+
| Row actions | Custom action buttons per row |
|
|
280
|
+
| Density modes | Compact, normal, comfortable |
|
|
281
|
+
| Sticky columns | Pin columns to left or right |
|
|
282
|
+
| Column resizing | Drag column header borders to resize |
|
|
283
|
+
| Column reordering | Drag & drop column headers |
|
|
284
|
+
| Grouped headers | Multi-row header with `colspan` via `children` |
|
|
285
|
+
| Per-column filters | Filter input row with operators |
|
|
286
|
+
| Inline editing | Double-click or Enter to edit: text, number, select, boolean |
|
|
287
|
+
| Virtual scrolling | Render only visible rows for large datasets |
|
|
288
|
+
| CSV/Excel export | Built-in toolbar buttons, no external deps |
|
|
289
|
+
| Server-side mode | Delegate sort/filter/page to parent component |
|
|
290
|
+
| TrackBy | Custom trackBy key or function |
|
|
291
|
+
| i18n | All labels configurable via translation dictionary |
|
|
292
|
+
|
|
293
|
+
### Grid API
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { JvGridComponent } from '@devjuliovilla/jv-ui';
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Inputs:**
|
|
300
|
+
|
|
301
|
+
| Input | Type | Default | Description |
|
|
302
|
+
| ------------- | ----------------------------- | ------- | -------------------------- |
|
|
303
|
+
| `data` | `T[]` | `[]` | Row data array |
|
|
304
|
+
| `columns` | `JvGridColumn<T>[]` | `[]` | Column definitions |
|
|
305
|
+
| `actions` | `JvGridAction<T>[]` | `[]` | Row action button configs |
|
|
306
|
+
| `options` | `Partial<JvGridOptions>` | `{}` | Feature toggles and config |
|
|
307
|
+
| `trackBy` | `keyof T \| Function \| null` | `null` | Row identity |
|
|
308
|
+
| `selectedIds` | `(string \| number)[]` | `[]` | Controlled selection IDs |
|
|
309
|
+
|
|
310
|
+
**Outputs:**
|
|
311
|
+
|
|
312
|
+
| Output | Type |
|
|
313
|
+
| ----------------- | ----------------------------------------------------------- |
|
|
314
|
+
| `rowClick` | `T` |
|
|
315
|
+
| `rowDoubleClick` | `T` |
|
|
316
|
+
| `actionClick` | `{ actionId: string; row: T }` |
|
|
317
|
+
| `selectionChange` | `(string \| number)[]` |
|
|
318
|
+
| `pageChange` | `{ pageIndex: number; pageSize: number }` |
|
|
319
|
+
| `searchChange` | `string` |
|
|
320
|
+
| `sortChange` | `{ columnKey: string; direction: 'asc' \| 'desc' \| null }` |
|
|
321
|
+
| `columnFilter` | `{ columnKey: string; value: string }` |
|
|
322
|
+
| `columnResize` | `{ columnKey: string; width: string }` |
|
|
323
|
+
| `columnReorder` | `{ columnKey: string; newIndex: number }` |
|
|
324
|
+
| `rowEdit` | `{ row: T; column: JvGridColumn<T>; value: unknown }` |
|
|
325
|
+
|
|
326
|
+
**JvGridColumn options:**
|
|
327
|
+
|
|
328
|
+
| Property | Type | Description |
|
|
329
|
+
| --------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------- |
|
|
330
|
+
| `key` | `keyof T \| string` | Data field key |
|
|
331
|
+
| `header` | `string` | Column display header |
|
|
332
|
+
| `sortable` | `boolean` | Enable column sorting |
|
|
333
|
+
| `searchable` | `boolean` | Include in global search |
|
|
334
|
+
| `filterable` | `boolean` | Show per-column filter input |
|
|
335
|
+
| `hidden` | `boolean` | Hide column from display |
|
|
336
|
+
| `sticky` | `'left' \| 'right'` | Pin column position |
|
|
337
|
+
| `editable` | `boolean` | Allow inline cell editing |
|
|
338
|
+
| `editType` | `'text' \| 'number' \| 'select' \| 'boolean'` | Editor type |
|
|
339
|
+
| `editOptions` | `{ label: string; value: unknown }[]` | Options for select editor |
|
|
340
|
+
| `width` | `string` | Column width (e.g. '8rem') |
|
|
341
|
+
| `align` | `'start' \| 'center' \| 'end'` | Text alignment |
|
|
342
|
+
| `type` | `'text' \| 'integer' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'time' \| 'datetime' \| 'boolean'` | Data type for formatting |
|
|
343
|
+
| `formatOptions` | `Intl.NumberFormatOptions & Intl.DateTimeFormatOptions` | Per-column formatting overrides |
|
|
344
|
+
| `format` | `(value, row) => string` | Custom display formatter |
|
|
345
|
+
| `children` | `JvGridColumn<T>[]` | Child columns for grouped headers |
|
|
346
|
+
| `resizable` | `boolean` | Allow column resize |
|
|
347
|
+
| `cellClass` | `string` | CSS class for cells |
|
|
348
|
+
| `headerClass` | `string` | CSS class for header |
|
|
349
|
+
|
|
350
|
+
**JvGridOptions:**
|
|
351
|
+
|
|
352
|
+
| Option | Type | Default | Description |
|
|
353
|
+
| ------------------------ | ---------------------------------------- | ------------------- | ------------------------------------------------------------------------------------- |
|
|
354
|
+
| `searchable` | `boolean` | `true` | Show search bar |
|
|
355
|
+
| `sortable` | `boolean` | `true` | Enable column sorting |
|
|
356
|
+
| `pageable` | `boolean` | `true` | Enable pagination |
|
|
357
|
+
| `selectable` | `boolean` | `false` | Show selection checkboxes |
|
|
358
|
+
| `pageSize` | `number` | `20` | Default page size |
|
|
359
|
+
| `pageSizeOptions` | `number[]` | `[10, 20, 50, 100]` | Page size selector options |
|
|
360
|
+
| `density` | `'compact' \| 'normal' \| 'comfortable'` | `'normal'` | Row density |
|
|
361
|
+
| `loading` | `boolean` | `false` | Show loading spinner |
|
|
362
|
+
| `emptyMessage` | `string` | — | Empty state message |
|
|
363
|
+
| `exportable` | `boolean` | `false` | Show CSV/Excel export buttons |
|
|
364
|
+
| `exportFileName` | `string` | `''` | Base filename used for CSV/Excel exports; the component applies the correct extension |
|
|
365
|
+
| `serverSide` | `boolean` | `false` | Disable client-side processing |
|
|
366
|
+
| `totalItems` | `number` | `0` | Total items for server-side mode |
|
|
367
|
+
| `stickyColumns` | `boolean` | `false` | Enable sticky column support |
|
|
368
|
+
| `resizableColumns` | `boolean` | `false` | Enable column resizing |
|
|
369
|
+
| `reorderableColumns` | `boolean` | `false` | Enable column reordering |
|
|
370
|
+
| `editable` | `boolean` | `false` | Enable inline editing |
|
|
371
|
+
| `virtualScroll` | `boolean` | `false` | Enable virtual scrolling |
|
|
372
|
+
| `virtualScrollRowHeight` | `number` | `48` | Row height for virtual scroll |
|
|
373
|
+
| `columnFilters` | `boolean` | `false` | Show filter row below headers |
|
|
374
|
+
| `rowDoubleClick` | `boolean` | `false` | Enable row double-click event |
|
|
375
|
+
| `keyboardNavigation` | `boolean` | `false` | Enable arrow-key cell navigation and `F2` editing flow |
|
|
376
|
+
| `ariaLabel` | `string` | — | Accessible label for the grid |
|
|
377
|
+
|
|
378
|
+
### Grid example
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import { Component } from '@angular/core';
|
|
382
|
+
import { JvGridComponent, JvGridColumn, JvGridOptions } from '@devjuliovilla/jv-ui';
|
|
383
|
+
|
|
384
|
+
interface Product {
|
|
385
|
+
id: number;
|
|
386
|
+
name: string;
|
|
387
|
+
price: number;
|
|
388
|
+
status: string;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
@Component({
|
|
392
|
+
selector: 'app-products',
|
|
393
|
+
standalone: true,
|
|
394
|
+
imports: [JvGridComponent],
|
|
395
|
+
template: `
|
|
396
|
+
<jv-grid
|
|
397
|
+
[data]="products"
|
|
398
|
+
[columns]="columns"
|
|
399
|
+
[options]="{ selectable: true, exportable: true, exportFileName: 'products', pageSize: 10 }"
|
|
400
|
+
[trackBy]="'id'"
|
|
401
|
+
[selectedIds]="selectedIds"
|
|
402
|
+
(selectionChange)="selectedIds = $event"
|
|
403
|
+
/>
|
|
404
|
+
`,
|
|
405
|
+
})
|
|
406
|
+
export class ProductsComponent {
|
|
407
|
+
products: Product[] = [
|
|
408
|
+
/* ... */
|
|
409
|
+
];
|
|
410
|
+
selectedIds: (string | number)[] = [];
|
|
411
|
+
columns: JvGridColumn<Product>[] = [
|
|
412
|
+
{ key: 'id', header: 'ID', width: '4rem' },
|
|
413
|
+
{ key: 'name', header: 'Product', sortable: true, filterable: true },
|
|
414
|
+
{ key: 'price', header: 'Price', type: 'number', align: 'end', format: (v) => '$' + v },
|
|
415
|
+
{ key: 'status', header: 'Status', sortable: true },
|
|
416
|
+
];
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Grid behavior notes
|
|
421
|
+
|
|
422
|
+
- **Row identity:** when `trackBy` is not provided, rows with an `id` property use that value as their identity. Provide `[trackBy]` when the row identity uses another field or a custom function.
|
|
423
|
+
- **Controlled selection:** use `[selectedIds]` together with `(selectionChange)` when the parent owns selection state. The component accepts an empty array, so selection can start fully controlled without special initialization.
|
|
424
|
+
- **Pagination:** `pageSize` is clamped to the configured `pageSizeOptions`, and page indexes are clamped when the data or page size changes. Use `(pageChange)` to synchronize server-side pagination.
|
|
425
|
+
- **Grouped headers:** define parent columns with `children`. The grouped header is rendered with `colspan`, while sorting, filtering, editing, resizing, reordering, and export operate on the leaf columns.
|
|
426
|
+
- **Virtual scrolling:** place the grid inside a container with a constrained height and set `virtualScroll: true`. The default row height is `48`; set `virtualScrollRowHeight` to the actual rendered row height when it differs.
|
|
427
|
+
- **Formatting:** column `type` and `formatOptions` affect display only. Sorting and export use the raw field value, not the localized text shown in the cell.
|
|
428
|
+
- **Inline editing:** set both `editable: true` in `options` and `editable: true` on the column. `select` editors emit the configured option value, while `boolean` editors emit a boolean. Press `F2` to start, `Enter` to commit, and `Escape` to cancel when keyboard navigation is enabled.
|
|
429
|
+
|
|
430
|
+
### Keyboard navigation example
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
const gridOptions: JvGridOptions = {
|
|
434
|
+
keyboardNavigation: true,
|
|
435
|
+
editable: true,
|
|
436
|
+
pageable: false,
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const columns: JvGridColumn<Product>[] = [
|
|
440
|
+
{ key: 'name', header: 'Product', editable: true, editType: 'text' },
|
|
441
|
+
{
|
|
442
|
+
key: 'status',
|
|
443
|
+
header: 'Status',
|
|
444
|
+
editable: true,
|
|
445
|
+
editType: 'select',
|
|
446
|
+
editOptions: [
|
|
447
|
+
{ label: 'Active', value: 'active' },
|
|
448
|
+
{ label: 'Paused', value: 'paused' },
|
|
449
|
+
],
|
|
450
|
+
},
|
|
451
|
+
];
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
```html
|
|
455
|
+
<jv-grid
|
|
456
|
+
[data]="products"
|
|
457
|
+
[columns]="columns"
|
|
458
|
+
[options]="gridOptions"
|
|
459
|
+
(rowEdit)="onRowEdit($event)"
|
|
460
|
+
/>
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
With `keyboardNavigation` enabled, arrow keys move between data cells without trapping row action buttons. `Tab` continues to the row actions, `F2` starts editing, `Enter` commits, and `Escape` cancels.
|
|
464
|
+
|
|
465
|
+
## List API
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
import { JvListComponent, JvListItemComponent } from '@devjuliovilla/jv-ui';
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**JvList inputs:**
|
|
472
|
+
|
|
473
|
+
| Input | Type | Default | Description |
|
|
474
|
+
| ----------- | ------------------------ | -------- | ------------------------------------------------------------------------------ |
|
|
475
|
+
| `role` | `'list' \| 'navigation'` | `'list'` | ARIA role for the list |
|
|
476
|
+
| `variant` | `'bordered' \| null` | `null` | Outer border + border-radius around the list (items always have bottom border) |
|
|
477
|
+
| `ariaLabel` | `string` | `''` | Accessible label |
|
|
478
|
+
|
|
479
|
+
**JvListItem inputs:**
|
|
480
|
+
|
|
481
|
+
| Input | Type | Default | Description |
|
|
482
|
+
| ------------- | ------------------- | ------- | -------------------------------------------- |
|
|
483
|
+
| `title` | `string` (required) | — | Primary text |
|
|
484
|
+
| `description` | `string` | `''` | Secondary text below title |
|
|
485
|
+
| `leadingIcon` | `string \| null` | `null` | Lucide icon name on the left |
|
|
486
|
+
| `disabled` | `boolean` | `false` | Gray out and block interaction |
|
|
487
|
+
| `active` | `boolean` | `false` | Highlight with primary color and left border |
|
|
488
|
+
|
|
489
|
+
**JvListItem outputs:**
|
|
490
|
+
|
|
491
|
+
| Output | Type |
|
|
492
|
+
| ----------- | ------ |
|
|
493
|
+
| `activated` | `void` |
|
|
494
|
+
|
|
495
|
+
**Content projection:** use the `meta` attribute selector for badges, icons, or any content on the right side:
|
|
496
|
+
|
|
497
|
+
```html
|
|
498
|
+
<jv-list-item title="Inbox" description="New messages">
|
|
499
|
+
<jv-badge tone="primary" meta>3</jv-badge>
|
|
500
|
+
<jv-icon name="chevron-right" meta />
|
|
501
|
+
</jv-list-item>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Example
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
import { Component } from '@angular/core';
|
|
508
|
+
import { JvListComponent, JvListItemComponent, JvBadgeComponent } from '@devjuliovilla/jv-ui';
|
|
509
|
+
|
|
510
|
+
@Component({
|
|
511
|
+
selector: 'app-nav-list',
|
|
512
|
+
standalone: true,
|
|
513
|
+
imports: [JvListComponent, JvListItemComponent, JvBadgeComponent],
|
|
514
|
+
template: `
|
|
515
|
+
<jv-list variant="bordered">
|
|
516
|
+
<jv-list-item
|
|
517
|
+
title="Dashboard"
|
|
518
|
+
description="Main overview"
|
|
519
|
+
leadingIcon="layout-dashboard"
|
|
520
|
+
[active]="true"
|
|
521
|
+
/>
|
|
522
|
+
<jv-list-item title="Notifications" description="3 pending" (activated)="onOpen()">
|
|
523
|
+
<jv-badge tone="primary" meta>3</jv-badge>
|
|
524
|
+
</jv-list-item>
|
|
525
|
+
<jv-list-item title="Reports" description="Coming soon" [disabled]="true" />
|
|
526
|
+
</jv-list>
|
|
527
|
+
`,
|
|
528
|
+
})
|
|
529
|
+
export class NavListComponent {
|
|
530
|
+
onOpen() {
|
|
531
|
+
/* handle activation */
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
## Pagination API
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
import { JvPaginationComponent } from '@devjuliovilla/jv-ui';
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
| Input | Type | Default | Description |
|
|
543
|
+
| ----------------- | ---------- | ------------------- | ------------------------ |
|
|
544
|
+
| `totalItems` | `number` | `0` | Total items |
|
|
545
|
+
| `pageSize` | `number` | `20` | Items per page |
|
|
546
|
+
| `pageSizeOptions` | `number[]` | `[10, 20, 50, 100]` | Page size options |
|
|
547
|
+
| `pageIndex` | `number` | `0` | Current page (0-based) |
|
|
548
|
+
| `maxVisiblePages` | `number` | `5` | Max page buttons to show |
|
|
549
|
+
|
|
550
|
+
| Output | Type |
|
|
551
|
+
| ------------ | ----------------------------------------- |
|
|
552
|
+
| `pageChange` | `{ pageIndex: number; pageSize: number }` |
|
|
553
|
+
|
|
554
|
+
## Tabs API
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
import { JvTabsComponent, JvTabPanelDirective, JvTabItem } from '@devjuliovilla/jv-ui';
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Inputs:**
|
|
561
|
+
|
|
562
|
+
| Input | Type | Default | Description |
|
|
563
|
+
| ------------- | ------------------------------------- | ------- | --------------------------------------------------------- |
|
|
564
|
+
| `tabs` | `JvTabItem[]` | `[]` | Tab definitions (`{ id, label, icon?, disabled? }`) |
|
|
565
|
+
| `activeTabId` | `string \| null` | `null` | Controlled active tab ID |
|
|
566
|
+
| `keepMounted` | `boolean` | `true` | Keep all panels in DOM; `false` renders only active panel |
|
|
567
|
+
| `transition` | `'none' \| 'fade' \| 'slide' \| null` | `null` | Panel transition animation (falls back to global config) |
|
|
568
|
+
|
|
569
|
+
**Outputs:**
|
|
570
|
+
|
|
571
|
+
| Output | Type |
|
|
572
|
+
| ----------------- | -------- |
|
|
573
|
+
| `activeTabChange` | `string` |
|
|
574
|
+
|
|
575
|
+
**Content projection:** use `jvTabPanel` directive on `ng-template` to define panel content:
|
|
576
|
+
|
|
577
|
+
```html
|
|
578
|
+
<jv-tabs [tabs]="tabs" [activeTabId]="activeTabId()" (activeTabChange)="activeTabId.set($event)">
|
|
579
|
+
<ng-template jvTabPanel="overview">
|
|
580
|
+
<p>Overview content</p>
|
|
581
|
+
</ng-template>
|
|
582
|
+
<ng-template jvTabPanel="analytics">
|
|
583
|
+
<p>Analytics content</p>
|
|
584
|
+
</ng-template>
|
|
585
|
+
</jv-tabs>
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Tab Transitions
|
|
589
|
+
|
|
590
|
+
Tabs support animated transitions between panels. Configure globally via `provideJvUi()` or per-instance via the `transition` input:
|
|
591
|
+
|
|
592
|
+
```typescript
|
|
593
|
+
// Global default
|
|
594
|
+
provideJvUi({ tabsTransition: 'fade' });
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
```typescript
|
|
598
|
+
// Per-instance override
|
|
599
|
+
<jv-tabs transition="slide" [keepMounted]="false">...</jv-tabs>
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
| Transition | `keepMounted=true` | `keepMounted=false` | Description |
|
|
603
|
+
| ---------- | ---------------------- | ------------------- | ------------------------------------ |
|
|
604
|
+
| `'none'` | Instant switch | Instant switch | No animation |
|
|
605
|
+
| `'fade'` | Opacity cross-fade | Opacity cross-fade | Smooth fade between panels |
|
|
606
|
+
| `'slide'` | Falls back to `'fade'` | Directional slide | Slides left/right based on tab index |
|
|
607
|
+
|
|
608
|
+
**Note:** `slide` requires `keepMounted=false` because it needs to animate both entering and exiting panels simultaneously. When used with `keepMounted=true`, it automatically falls back to `fade`.
|
|
609
|
+
|
|
610
|
+
### Tabs Example
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
import { Component, signal } from '@angular/core';
|
|
614
|
+
import { JvTabItem, JvTabPanelDirective, JvTabsComponent } from '@devjuliovilla/jv-ui';
|
|
615
|
+
|
|
616
|
+
@Component({
|
|
617
|
+
standalone: true,
|
|
618
|
+
imports: [JvTabsComponent, JvTabPanelDirective],
|
|
619
|
+
template: `
|
|
620
|
+
<jv-tabs
|
|
621
|
+
[tabs]="tabs"
|
|
622
|
+
[activeTabId]="activeTabId()"
|
|
623
|
+
transition="fade"
|
|
624
|
+
(activeTabChange)="activeTabId.set($event)"
|
|
625
|
+
>
|
|
626
|
+
<ng-template jvTabPanel="overview">
|
|
627
|
+
<p>Overview content</p>
|
|
628
|
+
</ng-template>
|
|
629
|
+
<ng-template jvTabPanel="analytics">
|
|
630
|
+
<p>Analytics content</p>
|
|
631
|
+
</ng-template>
|
|
632
|
+
</jv-tabs>
|
|
633
|
+
`,
|
|
634
|
+
})
|
|
635
|
+
export class TabsExampleComponent {
|
|
636
|
+
readonly activeTabId = signal('overview');
|
|
637
|
+
readonly tabs: JvTabItem[] = [
|
|
638
|
+
{ id: 'overview', label: 'Overview' },
|
|
639
|
+
{ id: 'analytics', label: 'Analytics', icon: 'chart-column' },
|
|
640
|
+
];
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
## Image Slider API
|
|
645
|
+
|
|
646
|
+
```typescript
|
|
647
|
+
import { JvImageSliderComponent, JvSliderImage } from '@devjuliovilla/jv-ui';
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
**JvSliderImage:**
|
|
651
|
+
|
|
652
|
+
| Property | Type | Required | Description |
|
|
653
|
+
| ------------- | -------- | -------- | ---------------------------------------- |
|
|
654
|
+
| `src` | `string` | Yes | Image URL |
|
|
655
|
+
| `alt` | `string` | Yes | Alternative text for accessibility |
|
|
656
|
+
| `description` | `string` | No | Caption displayed below the image |
|
|
657
|
+
| `thumbnail` | `string` | No | Custom thumbnail URL (defaults to `src`) |
|
|
658
|
+
|
|
659
|
+
**Inputs:**
|
|
660
|
+
|
|
661
|
+
| Input | Type | Default | Description |
|
|
662
|
+
| ----------------- | ----------------- | ------- | --------------------------------- |
|
|
663
|
+
| `images` | `JvSliderImage[]` | `[]` | Array of images to display |
|
|
664
|
+
| `showThumbnails` | `boolean` | `true` | Show thumbnail strip below slider |
|
|
665
|
+
| `showDescription` | `boolean` | `true` | Show image description |
|
|
666
|
+
|
|
667
|
+
**Outputs:**
|
|
668
|
+
|
|
669
|
+
| Output | Type | Description |
|
|
670
|
+
| ------------- | -------- | ------------------------------------- |
|
|
671
|
+
| `imageChange` | `number` | Emits index when active image changes |
|
|
672
|
+
|
|
673
|
+
**i18n support:**
|
|
674
|
+
|
|
675
|
+
The component automatically translates aria labels using `JvTranslationService`:
|
|
676
|
+
|
|
677
|
+
| Key | English | Spanish |
|
|
678
|
+
| ---------------------- | ---------------- | ---------------------- |
|
|
679
|
+
| `slider.prevImage` | Previous image | Imagen anterior |
|
|
680
|
+
| `slider.nextImage` | Next image | Imagen siguiente |
|
|
681
|
+
| `slider.thumbnailList` | Image thumbnails | Miniaturas de imagenes |
|
|
682
|
+
|
|
683
|
+
### Features
|
|
684
|
+
|
|
685
|
+
- **Keyboard navigation:** ArrowLeft/ArrowRight to navigate, Home/End for first/last image
|
|
686
|
+
- **Responsive:** Images scale to fit container with `object-fit: contain`
|
|
687
|
+
- **Thumbnails:** Scrollable thumbnail strip with active state indicator
|
|
688
|
+
- **Single image mode:** Navigation controls hidden when only one image is provided
|
|
689
|
+
- **Lazy loading:** Images use native `loading="lazy"` attribute
|
|
690
|
+
- **i18n:** Aria labels automatically translated based on configured locale
|
|
691
|
+
|
|
692
|
+
### Example
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
import { Component } from '@angular/core';
|
|
696
|
+
import { JvImageSliderComponent, JvSliderImage } from '@devjuliovilla/jv-ui';
|
|
697
|
+
|
|
698
|
+
@Component({
|
|
699
|
+
standalone: true,
|
|
700
|
+
imports: [JvImageSliderComponent],
|
|
701
|
+
template: `
|
|
702
|
+
<jv-image-slider
|
|
703
|
+
[images]="images"
|
|
704
|
+
[showThumbnails]="true"
|
|
705
|
+
[showDescription]="true"
|
|
706
|
+
(imageChange)="onImageChange($event)"
|
|
707
|
+
/>
|
|
708
|
+
`,
|
|
709
|
+
})
|
|
710
|
+
export class SliderComponent {
|
|
711
|
+
readonly images: JvSliderImage[] = [
|
|
712
|
+
{ src: 'photo1.jpg', alt: 'Sunset', description: 'Beautiful sunset over the ocean' },
|
|
713
|
+
{ src: 'photo2.jpg', alt: 'Mountain', description: 'Snow-capped mountain peak' },
|
|
714
|
+
{ src: 'photo3.jpg', alt: 'Forest', description: 'Dense forest trail' },
|
|
715
|
+
];
|
|
716
|
+
|
|
717
|
+
onImageChange(index: number) {
|
|
718
|
+
console.log('Viewing image:', index);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
## Image Gallery API
|
|
724
|
+
|
|
725
|
+
```typescript
|
|
726
|
+
import { JvImageGalleryComponent, JvGalleryImage, JvGalleryAction } from '@devjuliovilla/jv-ui';
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
**JvGalleryImage:**
|
|
730
|
+
|
|
731
|
+
| Property | Type | Required | Description |
|
|
732
|
+
| ----------- | -------- | -------- | ---------------------------------------- |
|
|
733
|
+
| `src` | `string` | Yes | Image URL |
|
|
734
|
+
| `alt` | `string` | Yes | Alternative text for accessibility |
|
|
735
|
+
| `thumbnail` | `string` | No | Custom thumbnail URL (defaults to `src`) |
|
|
736
|
+
|
|
737
|
+
**JvGalleryAction:**
|
|
738
|
+
|
|
739
|
+
| Property | Type | Required | Description |
|
|
740
|
+
| -------- | ---------- | -------- | ------------------------ |
|
|
741
|
+
| `id` | `string` | Yes | Unique action identifier |
|
|
742
|
+
| `label` | `string` | Yes | Button label text |
|
|
743
|
+
| `icon` | `string` | No | Lucide icon name |
|
|
744
|
+
| `tone` | `JvIntent` | No | Button variant |
|
|
745
|
+
|
|
746
|
+
**Inputs:**
|
|
747
|
+
|
|
748
|
+
| Input | Type | Default | Description |
|
|
749
|
+
| --------------- | ------------------- | ------- | ------------------------------------------------------------------------------------- |
|
|
750
|
+
| `images` | `JvGalleryImage[]` | `[]` | Array of images to display |
|
|
751
|
+
| `stripHeightVh` | `number` | `25` | Height of the thumbnail strip in viewport-height units, clamped between `0` and `100` |
|
|
752
|
+
| `actions` | `JvGalleryAction[]` | `[]` | Action buttons shown in dialog footer |
|
|
753
|
+
|
|
754
|
+
**Outputs:**
|
|
755
|
+
|
|
756
|
+
| Output | Type | Description |
|
|
757
|
+
| ------------- | --------------------------------------------- | -------------------------------------- |
|
|
758
|
+
| `imageClick` | `JvGalleryImage` | Emits when an image is clicked |
|
|
759
|
+
| `actionClick` | `{ actionId: string; image: JvGalleryImage }` | Emits when an action button is clicked |
|
|
760
|
+
|
|
761
|
+
### Features
|
|
762
|
+
|
|
763
|
+
- **Horizontal strip:** Scrollable thumbnail strip with configurable height in `vh`
|
|
764
|
+
- **Full-screen viewer:** Click any image to open in a dialog with dark background
|
|
765
|
+
- **Navigation:** Prev/next buttons and keyboard arrows (← →) in the viewer
|
|
766
|
+
- **Configurable actions:** Action buttons in dialog footer (favorite, edit, delete, etc.)
|
|
767
|
+
- **Responsive:** Shows multiple columns on desktop, single column with scroll on mobile
|
|
768
|
+
- **Lazy loading:** All images use native `loading="lazy"` attribute
|
|
769
|
+
- **i18n:** Close and navigation labels automatically translated
|
|
770
|
+
|
|
771
|
+
### Example
|
|
772
|
+
|
|
773
|
+
```typescript
|
|
774
|
+
import { Component } from '@angular/core';
|
|
775
|
+
import { JvImageGalleryComponent, JvGalleryImage, JvGalleryAction } from '@devjuliovilla/jv-ui';
|
|
776
|
+
|
|
777
|
+
@Component({
|
|
778
|
+
standalone: true,
|
|
779
|
+
imports: [JvImageGalleryComponent],
|
|
780
|
+
template: `
|
|
781
|
+
<jv-image-gallery
|
|
782
|
+
[images]="images"
|
|
783
|
+
[stripHeightVh]="30"
|
|
784
|
+
[actions]="actions"
|
|
785
|
+
(imageClick)="onImageClick($event)"
|
|
786
|
+
(actionClick)="onActionClick($event)"
|
|
787
|
+
/>
|
|
788
|
+
`,
|
|
789
|
+
})
|
|
790
|
+
export class GalleryComponent {
|
|
791
|
+
readonly images: JvGalleryImage[] = [
|
|
792
|
+
{ src: 'photo1.jpg', alt: 'Sunset' },
|
|
793
|
+
{ src: 'photo2.jpg', alt: 'Mountain' },
|
|
794
|
+
{ src: 'photo3.jpg', alt: 'Forest' },
|
|
795
|
+
];
|
|
796
|
+
|
|
797
|
+
readonly actions: JvGalleryAction[] = [
|
|
798
|
+
{ id: 'favorite', label: 'Favorite', icon: 'heart', tone: 'danger' },
|
|
799
|
+
{ id: 'edit', label: 'Edit', icon: 'pencil' },
|
|
800
|
+
{ id: 'delete', label: 'Delete', icon: 'trash-2', tone: 'danger' },
|
|
801
|
+
];
|
|
802
|
+
|
|
803
|
+
onImageClick(image: JvGalleryImage) {
|
|
804
|
+
console.log('Viewing:', image.alt);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
onActionClick(event: { actionId: string; image: JvGalleryImage }) {
|
|
808
|
+
console.log(event.actionId, 'on', event.image.alt);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
## Textarea API
|
|
814
|
+
|
|
815
|
+
```typescript
|
|
816
|
+
import { JvTextareaComponent } from '@devjuliovilla/jv-ui';
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
| Input | Type | Default | Description |
|
|
820
|
+
| ------------- | --------- | ----------------- | ---------------------------------------------------- |
|
|
821
|
+
| `placeholder` | `string` | `''` | Placeholder text |
|
|
822
|
+
| `required` | `boolean` | `false` | Mark field as required |
|
|
823
|
+
| `invalid` | `boolean` | `false` | Show invalid state |
|
|
824
|
+
| `readonly` | `boolean` | `false` | Make textarea read-only |
|
|
825
|
+
| `rows` | `number` | `3` | Visible number of rows |
|
|
826
|
+
| `cols` | `number` | — | Visible width in columns |
|
|
827
|
+
| `maxlength` | `number` | — | Maximum character length |
|
|
828
|
+
| `describedBy` | `string` | `''` | ID of description/error element for aria-describedby |
|
|
829
|
+
| `inputId` | `string` | `jv-textarea-{n}` | Auto-generated unique ID |
|
|
830
|
+
|
|
831
|
+
Implements `ControlValueAccessor` — compatible with `[(ngModel)]` and reactive forms via `formControlName`.
|
|
832
|
+
|
|
833
|
+
### Example
|
|
834
|
+
|
|
835
|
+
```typescript
|
|
836
|
+
import { Component } from '@angular/core';
|
|
837
|
+
import { JvTextareaComponent } from '@devjuliovilla/jv-ui';
|
|
838
|
+
import { ReactiveFormsModule, FormBuilder } from '@angular/forms';
|
|
839
|
+
|
|
840
|
+
@Component({
|
|
841
|
+
selector: 'app-profile',
|
|
842
|
+
standalone: true,
|
|
843
|
+
imports: [ReactiveFormsModule, JvTextareaComponent],
|
|
844
|
+
template: `
|
|
845
|
+
<jv-textarea
|
|
846
|
+
formControlName="bio"
|
|
847
|
+
placeholder="Tell us about yourself..."
|
|
848
|
+
[rows]="4"
|
|
849
|
+
[maxlength]="500"
|
|
850
|
+
/>
|
|
851
|
+
`,
|
|
852
|
+
})
|
|
853
|
+
export class ProfileComponent {
|
|
854
|
+
form = inject(FormBuilder).group({ bio: [''] });
|
|
855
|
+
}
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
## Code API
|
|
859
|
+
|
|
860
|
+
```typescript
|
|
861
|
+
import { JvCodeComponent } from '@devjuliovilla/jv-ui';
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
| Input | Type | Default | Description |
|
|
865
|
+
| ---------- | ------------------------------------------------------------------------- | ------------------ | --------------------------------------------------------------------- |
|
|
866
|
+
| `code` | `string` | — | Code or text content to render |
|
|
867
|
+
| `language` | `string` | `''` | Optional language badge |
|
|
868
|
+
| `title` | `string` | `''` | Optional header title |
|
|
869
|
+
| `font` | `'jetbrains-mono' \| 'fira-code' \| 'cascadia' \| 'consolas' \| 'system'` | `'jetbrains-mono'` | Mono font preset for the rendered block |
|
|
870
|
+
| `editable` | `boolean` | `false` | Renders a textarea-style editing surface instead of read-only preview |
|
|
871
|
+
| `width` | `string` | `'100%'` | CSS width applied to the component |
|
|
872
|
+
| `height` | `string` | `'auto'` | CSS height for the editable surface or minimum preview height |
|
|
873
|
+
|
|
874
|
+
Use `JvCode` for read-only payloads or long single-line content where horizontal scrolling should be preserved instead of wrapping.
|
|
875
|
+
The library does not bundle font files. `JvCode` references common mono families and will use any web font you load at the application level.
|
|
876
|
+
|
|
877
|
+
When `editable` is enabled, `JvCode` emits `codeChange` on every edit so the parent can keep the source text in sync.
|
|
878
|
+
|
|
879
|
+
### Example
|
|
880
|
+
|
|
881
|
+
```typescript
|
|
882
|
+
import { Component } from '@angular/core';
|
|
883
|
+
import { JvCodeComponent } from '@devjuliovilla/jv-ui';
|
|
884
|
+
|
|
885
|
+
@Component({
|
|
886
|
+
selector: 'app-payload-preview',
|
|
887
|
+
standalone: true,
|
|
888
|
+
imports: [JvCodeComponent],
|
|
889
|
+
template: `
|
|
890
|
+
<jv-code
|
|
891
|
+
[code]="payload"
|
|
892
|
+
language="json"
|
|
893
|
+
title="HTTP payload preview"
|
|
894
|
+
font="fira-code"
|
|
895
|
+
[editable]="true"
|
|
896
|
+
width="100%"
|
|
897
|
+
height="18rem"
|
|
898
|
+
(codeChange)="payload = $event"
|
|
899
|
+
/>
|
|
900
|
+
`,
|
|
901
|
+
})
|
|
902
|
+
export class PayloadPreviewComponent {
|
|
903
|
+
payload = '{"status":"ok"}';
|
|
904
|
+
}
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
If you want hosted fonts without shipping them in your package, load them at the app level, for example:
|
|
908
|
+
|
|
909
|
+
```css
|
|
910
|
+
@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
## Confirm Dialog Labels
|
|
914
|
+
|
|
915
|
+
`JvDialogService.confirm()` already supports `confirmLabel` and `cancelLabel` overrides per dialog. If you omit them, the defaults come from the active translation dictionary using `dialog.confirm` and `dialog.cancel`.
|
|
916
|
+
|
|
917
|
+
This lets you either:
|
|
918
|
+
|
|
919
|
+
- override labels directly for a specific action like `Guardar evento` / `Descartar cambios`
|
|
920
|
+
- or register translated defaults in your i18n dictionary for each supported language
|
|
921
|
+
|
|
922
|
+
Example with business copy:
|
|
923
|
+
|
|
924
|
+
```typescript
|
|
925
|
+
const confirmed = await dialogService.confirm({
|
|
926
|
+
title: 'Guardar evento',
|
|
927
|
+
message: 'Se aplicaran cambios a la agenda del equipo.',
|
|
928
|
+
confirmLabel: 'Guardar evento',
|
|
929
|
+
cancelLabel: 'Descartar cambios',
|
|
930
|
+
tone: 'primary',
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
if (confirmed) {
|
|
934
|
+
saveEvent();
|
|
935
|
+
}
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
Example using translated defaults from your dictionary:
|
|
939
|
+
|
|
940
|
+
```typescript
|
|
941
|
+
await dialogService.confirm({
|
|
942
|
+
message: 'This action will publish the current draft.',
|
|
943
|
+
tone: 'primary',
|
|
944
|
+
});
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
## Icon System
|
|
948
|
+
|
|
949
|
+
```typescript
|
|
950
|
+
import { JvIconComponent, JV_LUCIDE_ICON_REGISTRY } from '@devjuliovilla/jv-ui';
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
| Input | Type | Default | Description |
|
|
954
|
+
| ------------- | --------- | ------- | ----------------------------------------------- |
|
|
955
|
+
| `name` | `string` | — | Icon name (from auto-generated Lucide registry) |
|
|
956
|
+
| `size` | `number` | `24` | Width and height in pixels |
|
|
957
|
+
| `strokeWidth` | `number` | `2` | SVG stroke width |
|
|
958
|
+
| `decorative` | `boolean` | `true` | `aria-hidden="true"` when decorative |
|
|
959
|
+
| `ariaLabel` | `string` | — | Accessible label when not decorative |
|
|
960
|
+
|
|
961
|
+
Icons are auto-generated from [lucide-static](https://www.npmjs.com/package/lucide-static) via:
|
|
962
|
+
|
|
963
|
+
```bash
|
|
964
|
+
node tools/generate-icons.mjs
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
This reads all SVG files from `lucide-static/icons` and produces `jv-icon-registry.ts` with the complete set (~1500 icons). Run it after updating `lucide-static` to keep icons in sync.
|
|
968
|
+
|
|
969
|
+
## Layout API
|
|
970
|
+
|
|
971
|
+
### JvDashboardShell
|
|
972
|
+
|
|
973
|
+
```typescript
|
|
974
|
+
import { JvDashboardShellComponent } from '@devjuliovilla/jv-ui';
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
| Input | Type | Default | Description |
|
|
978
|
+
| ------------------- | ------------------ | ----------- | ------------------------------------------- |
|
|
979
|
+
| `title` | `string` | — | App / brand title |
|
|
980
|
+
| `sidebarSubtitle` | `string` | `''` | Subtitle below brand |
|
|
981
|
+
| `brandIcon` | `string` | `'monitor'` | Brand Lucide icon |
|
|
982
|
+
| `navItems` | `JvNavItem[]` | `[]` | Navigation items (supports nested children) |
|
|
983
|
+
| `sidebarCollapsed` | `boolean` | `false` | Desktop sidebar collapsed state |
|
|
984
|
+
| `mobileSidebarOpen` | `boolean` | `false` | Mobile sidebar open/closed |
|
|
985
|
+
| `topbarTitle` | `string` | `''` | Topbar page title |
|
|
986
|
+
| `topbarSubtitle` | `string` | `''` | Topbar subtitle |
|
|
987
|
+
| `currentTheme` | `JvTheme` | `'light'` | Active theme value |
|
|
988
|
+
| `themeOptions` | `JvThemeOption[]` | `[]` | Theme selector options |
|
|
989
|
+
| `topbarActions` | `JvTopbarAction[]` | `[]` | Icon buttons in topbar |
|
|
990
|
+
| `showThemeSelector` | `boolean` | `true` | Show theme pills |
|
|
991
|
+
|
|
992
|
+
| Output | Description |
|
|
993
|
+
| ----------------------- | -------------------------------------------------------------- |
|
|
994
|
+
| `menuClick` | Hamburger button clicked — toggle mobile sidebar |
|
|
995
|
+
| `mobileClose` | Backdrop clicked or navigation selected — close mobile sidebar |
|
|
996
|
+
| `sidebarCollapseToggle` | Collapse toggle clicked — collapse/expand sidebar |
|
|
997
|
+
| `themeChange` | Theme pill clicked |
|
|
998
|
+
| `topbarActionClick` | Topbar action button clicked |
|
|
999
|
+
|
|
1000
|
+
**Mobile behavior:** at viewports ≤ 900px the sidebar becomes a fixed overlay with slide-in animation (`transform: translateX`) and a semi-transparent backdrop. The sidebar emits `mobileClose` when the backdrop is clicked or a nav link is followed.
|
|
1001
|
+
|
|
1002
|
+
### JvSidebar
|
|
1003
|
+
|
|
1004
|
+
| Input | Type | Default | Description |
|
|
1005
|
+
| ------------ | ------------- | ---------------------- | --------------------- |
|
|
1006
|
+
| `title` | `string` | — | Brand title |
|
|
1007
|
+
| `subtitle` | `string` | `''` | Brand subtitle |
|
|
1008
|
+
| `brandIcon` | `string` | `'monitor'` | Brand icon |
|
|
1009
|
+
| `items` | `JvNavItem[]` | `[]` | Navigation items |
|
|
1010
|
+
| `collapsed` | `boolean` | `false` | Collapse to icon-only |
|
|
1011
|
+
| `mobileOpen` | `boolean` | `false` | Show on mobile |
|
|
1012
|
+
| `ariaLabel` | `string` | `'Sidebar navigation'` | Accessible label |
|
|
1013
|
+
|
|
1014
|
+
| Output | Description |
|
|
1015
|
+
| ---------------- | ------------------------------------------------ |
|
|
1016
|
+
| `collapseToggle` | Collapse button clicked |
|
|
1017
|
+
| `mobileClose` | Backdrop or nav item interaction — request close |
|
|
1018
|
+
|
|
1019
|
+
## Responsive Design
|
|
1020
|
+
|
|
1021
|
+
The library includes basic responsive behavior:
|
|
1022
|
+
|
|
1023
|
+
- **Breakpoints** defined as CSS custom properties: `--jv-breakpoint-sm` (640px), `--jv-breakpoint-md` (768px), `--jv-breakpoint-lg` (1024px), `--jv-breakpoint-xl` (1280px)
|
|
1024
|
+
- **Dashboard shell** collapses to single column at ≤ 900px; sidebar becomes a slide-in overlay with backdrop
|
|
1025
|
+
- **Topbar** stacks vertically at ≤ 900px
|
|
1026
|
+
- **Grid** pagination and toolbar stack at ≤ 640px; table scrolls horizontally with `overflow-x: auto`
|
|
1027
|
+
- **Flex-wrap** is used extensively for natural overflow handling
|
|
1028
|
+
- **Dialog** and **Toast** constrain width with `min()` to never exceed the viewport
|
|
1029
|
+
|
|
1030
|
+
## Services
|
|
1031
|
+
|
|
1032
|
+
| Service | Import Path | Description |
|
|
1033
|
+
| ----------------------- | ---------------------- | -------------------------------------------- |
|
|
1034
|
+
| `JvThemeService` | `@devjuliovilla/jv-ui` | Light / dark / high-contrast theme switching |
|
|
1035
|
+
| `JvTranslationService` | `@devjuliovilla/jv-ui` | Dictionary-based i18n (en/es) |
|
|
1036
|
+
| `JvToastService` | `@devjuliovilla/jv-ui` | Toast notification system |
|
|
1037
|
+
| `JvLoaderService` | `@devjuliovilla/jv-ui` | Full-screen loader with request counter |
|
|
1038
|
+
| `JvDialogService` | `@devjuliovilla/jv-ui` | Programmatic confirm dialogs |
|
|
1039
|
+
| `JvAnnouncementService` | `@devjuliovilla/jv-ui` | Screen-reader live-region announcements |
|
|
1040
|
+
|
|
1041
|
+
## Accessibility
|
|
1042
|
+
|
|
1043
|
+
- WCAG 2.2 AA target
|
|
1044
|
+
- Keyboard navigation for all interactive components
|
|
1045
|
+
- Focus management with visible focus indicators
|
|
1046
|
+
- Screen reader announcements (loading, empty states, page changes)
|
|
1047
|
+
- `aria-sort` on sortable grid headers
|
|
1048
|
+
- `aria-selected` on selectable grid rows
|
|
1049
|
+
- `aria-label` and `aria-labelledby` on interactive controls
|
|
1050
|
+
- Supports `prefers-reduced-motion`
|
|
1051
|
+
|
|
1052
|
+
## Requirements
|
|
1053
|
+
|
|
1054
|
+
- Angular 21.2+
|
|
1055
|
+
- TypeScript 5.9+
|
|
1056
|
+
|
|
1057
|
+
## License
|
|
1058
|
+
|
|
1059
|
+
MIT
|