@devjuliovilla/jv-ui 1.12.0 → 1.14.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/README.md CHANGED
@@ -9,6 +9,7 @@ npm install @devjuliovilla/jv-ui
9
9
  ```
10
10
 
11
11
  Import the theme CSS in your `angular.json` or `styles` array:
12
+
12
13
  ```json
13
14
  "styles": ["@devjuliovilla/jv-ui/styles/jv-theme.css"]
14
15
  ```
@@ -39,23 +40,23 @@ export class App {}
39
40
 
40
41
  ## Components
41
42
 
42
- | Category | Components |
43
- |---|---|
44
- | Buttons | `JvButton`, `JvButtonGroup`, `JvIconButton` |
45
- | Calendar | `JvMonthCalendar`, `JvDayPlanner` — reusable month view and agenda primitives, usable together or independently |
46
- | Forms | `JvInput`, `JvTextarea`, `JvCode`, `JvSelect`, `JvCheckbox`, `JvRadio`, `JvSwitch`, `JvDatePicker`, `JvModalSelector`, `JvFileInput`, `JvFormContainer` |
47
- | Data Grid | `JvGrid` — full-featured data table (see below) |
48
- | Containers | `JvCard`, `JvSection`, `JvDivider` |
49
- | List | `JvList`, `JvListItem` — list with title, description, icons, badges, and interactive items |
50
- | Tabs | `JvTabs`, `JvTabPanelDirective` — horizontal tabs with keyboard navigation and animated transitions |
51
- | Slider | `JvImageSlider` — responsive image slider with thumbnails and keyboard navigation |
52
- | Gallery | `JvImageGallery` — horizontal image gallery with wheel scrolling and full-screen viewer |
53
- | Feedback | `JvAlert`, `JvBadge`, `JvLoader`, `JvToast` |
54
- | Dialogs | `JvDialog`, `JvConfirmDialog`, `JvWindowModal` — compact dialogs plus a larger workflow overlay surface |
55
- | Layout | `JvDashboardShell`, `JvSidebar`, `JvTopbar`, `JvBreadcrumb`, `JvPage` |
56
- | Auth Pages | `JvLoginPage`, `JvForgotPasswordPage`, `JvChangePasswordPage` |
57
- | Icon | `JvIcon` — Lucide-based icon component with 1500+ auto-generated icons |
58
- | Pagination | `JvPagination` — standalone pagination with i18n |
43
+ | Category | Components |
44
+ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
45
+ | Buttons | `JvButton`, `JvButtonGroup`, `JvIconButton` |
46
+ | Calendar | `JvMonthCalendar`, `JvDayPlanner` — reusable month view and agenda primitives, usable together or independently |
47
+ | Forms | `JvInput`, `JvTextarea`, `JvCode`, `JvSelect`, `JvCheckbox`, `JvRadio`, `JvSwitch`, `JvDatePicker`, `JvModalSelector`, `JvFileInput`, `JvFormContainer` |
48
+ | Data Grid | `JvGrid` — full-featured data table (see below) |
49
+ | Containers | `JvCard`, `JvSection`, `JvDivider` |
50
+ | List | `JvList`, `JvListItem` — list with title, description, icons, badges, and interactive items |
51
+ | Tabs | `JvTabs`, `JvTabPanelDirective` — horizontal tabs with keyboard navigation and animated transitions |
52
+ | Slider | `JvImageSlider` — responsive image slider with thumbnails and keyboard navigation |
53
+ | Gallery | `JvImageGallery` — horizontal image gallery with wheel scrolling and full-screen viewer |
54
+ | Feedback | `JvAlert`, `JvBadge`, `JvLoader`, `JvToast` |
55
+ | Dialogs | `JvDialog`, `JvConfirmDialog`, `JvWindowModal` — compact dialogs plus a larger workflow overlay surface |
56
+ | Layout | `JvDashboardShell`, `JvSidebar`, `JvTopbar`, `JvBreadcrumb`, `JvPage` |
57
+ | Auth Pages | `JvLoginPage`, `JvForgotPasswordPage`, `JvChangePasswordPage` |
58
+ | Icon | `JvIcon` — Lucide-based icon component with 1500+ auto-generated icons |
59
+ | Pagination | `JvPagination` — standalone pagination with i18n |
59
60
 
60
61
  ## Calendar API
61
62
 
@@ -78,27 +79,33 @@ import {
78
79
 
79
80
  **Inputs:**
80
81
 
81
- | Input | Type | Default | Description |
82
- |---|---|---|---|
83
- | `value` | `Date \| string \| null` | `undefined` | Selected day |
84
- | `dayMeta` | `JvMonthCalendarDayMeta[]` | `[]` | Marker/counter metadata per day |
85
- | `ariaLabel` | `string` | localized default | Accessible label for the grid |
82
+ | Input | Type | Default | Description |
83
+ | -------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------- |
84
+ | `value` | `Date \| string \| null` | `undefined` | Selected day |
85
+ | `visibleMonth` | `Date \| string \| null` | `undefined` | Controlled visible month independent from the selected day |
86
+ | `dayMeta` | `JvMonthCalendarDayMeta[]` | `[]` | Marker/counter metadata per day |
87
+ | `ariaLabel` | `string` | localized default | Accessible label for the grid |
88
+ | `monthLabel` | `string` | `Month` | Accessible label for the month selector |
89
+ | `yearLabel` | `string` | `Year` | Accessible label for the year selector |
90
+ | `minYear` | `number` | current year - 10 | Lower year bound for the year selector |
91
+ | `maxYear` | `number` | current year + 10 | Upper year bound for the year selector |
92
+ | `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 |
86
93
 
87
94
  **Outputs:**
88
95
 
89
- | Output | Type |
90
- |---|---|
91
- | `selectionChange` | `Date` |
96
+ | Output | Type |
97
+ | -------------------- | ------ |
98
+ | `selectionChange` | `Date` |
92
99
  | `visibleMonthChange` | `Date` |
93
100
 
94
101
  **Day metadata:**
95
102
 
96
- | Property | Type | Description |
97
- |---|---|---|
98
- | `date` | `Date \| string` | Day associated with the marker |
99
- | `count` | `number` | Optional numeric badge rendered inside the cell |
100
- | `tone` | `'primary' \| 'secondary' \| 'success' \| 'warning' \| 'danger' \| 'info' \| 'neutral'` | Visual tone for the badge or dot |
101
- | `label` | `string` | Extra accessibility text appended to the day `aria-label` |
103
+ | Property | Type | Description |
104
+ | -------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------- |
105
+ | `date` | `Date \| string` | Day associated with the marker |
106
+ | `count` | `number` | Optional numeric badge rendered inside the cell |
107
+ | `tone` | `'primary' \| 'secondary' \| 'success' \| 'warning' \| 'danger' \| 'info' \| 'neutral'` | Visual tone for the badge or dot |
108
+ | `label` | `string` | Extra accessibility text appended to the day `aria-label` |
102
109
 
103
110
  **Template hook:**
104
111
 
@@ -144,37 +151,37 @@ import {
144
151
 
145
152
  **Inputs:**
146
153
 
147
- | Input | Type | Default | Description |
148
- |---|---|---|---|
149
- | `items` | `JvDayPlannerItem[]` | `[]` | Agenda items |
150
- | `groupByDate` | `boolean` | `false` | Group items under a date heading |
151
- | `emptyMessage` | `string` | localized default | Empty state copy |
152
- | `ariaLabel` | `string` | localized default | Accessible label for the agenda |
153
- | `defaultExpandedItemIds` | `(string \| number)[]` | `[]` | Items whose detail should start expanded |
154
+ | Input | Type | Default | Description |
155
+ | ------------------------ | ---------------------- | ----------------- | ---------------------------------------- |
156
+ | `items` | `JvDayPlannerItem[]` | `[]` | Agenda items |
157
+ | `groupByDate` | `boolean` | `false` | Group items under a date heading |
158
+ | `emptyMessage` | `string` | localized default | Empty state copy |
159
+ | `ariaLabel` | `string` | localized default | Accessible label for the agenda |
160
+ | `defaultExpandedItemIds` | `(string \| number)[]` | `[]` | Items whose detail should start expanded |
154
161
 
155
162
  **Outputs:**
156
163
 
157
- | Output | Type |
158
- |---|---|
164
+ | Output | Type |
165
+ | ------------ | ------------------ |
159
166
  | `itemSelect` | `JvDayPlannerItem` |
160
167
 
161
168
  **Planner item shape:**
162
169
 
163
- | Property | Type | Description |
164
- |---|---|---|
165
- | `id` | `string \| number` | Stable identity |
166
- | `title` | `string` | Primary label |
167
- | `date` | `Date \| string \| null` | Logical day when start time is not enough |
168
- | `start` | `Date \| string \| null` | Optional start datetime |
169
- | `end` | `Date \| string \| null` | Optional end datetime |
170
- | `status` | `string` | Status badge label |
171
- | `statusTone` | `JvTone` | Visual tone for status badge |
172
- | `type` | `string` | Type/category badge label |
173
- | `typeTone` | `JvTone` | Visual tone for type badge |
174
- | `responsible` | `string` | Responsible person |
175
- | `location` | `string` | Location metadata |
176
- | `details` | `string` | Expandable detail text |
177
- | `completed` | `boolean` | Reduced emphasis for completed items |
170
+ | Property | Type | Description |
171
+ | ------------- | ------------------------ | ----------------------------------------- |
172
+ | `id` | `string \| number` | Stable identity |
173
+ | `title` | `string` | Primary label |
174
+ | `date` | `Date \| string \| null` | Logical day when start time is not enough |
175
+ | `start` | `Date \| string \| null` | Optional start datetime |
176
+ | `end` | `Date \| string \| null` | Optional end datetime |
177
+ | `status` | `string` | Status badge label |
178
+ | `statusTone` | `JvTone` | Visual tone for status badge |
179
+ | `type` | `string` | Type/category badge label |
180
+ | `typeTone` | `JvTone` | Visual tone for type badge |
181
+ | `responsible` | `string` | Responsible person |
182
+ | `location` | `string` | Location metadata |
183
+ | `details` | `string` | Expandable detail text |
184
+ | `completed` | `boolean` | Reduced emphasis for completed items |
178
185
 
179
186
  **Template hooks:**
180
187
 
@@ -192,11 +199,7 @@ import { JvDayPlannerComponent, JvDayPlannerItem } from '@devjuliovilla/jv-ui';
192
199
  standalone: true,
193
200
  imports: [JvDayPlannerComponent],
194
201
  template: `
195
- <jv-day-planner
196
- [items]="items"
197
- [groupByDate]="true"
198
- (itemSelect)="handleSelect($event)"
199
- />
202
+ <jv-day-planner [items]="items" [groupByDate]="true" (itemSelect)="handleSelect($event)" />
200
203
  `,
201
204
  })
202
205
  export class PlannerOnlyComponent {
@@ -241,25 +244,25 @@ Use both components together when you need a left-side month selector and a righ
241
244
 
242
245
  ## Grid Features
243
246
 
244
- | Feature | Description |
245
- |---|---|
246
- | Sorting | Client-side multi-column sorting with sort icons and `aria-sort` |
247
- | Search | Global text search across visible columns |
248
- | Pagination | Configurable page size, page range buttons, first/last/prev/next |
249
- | Selection | Checkbox row selection with `selectedIds` / `selectionChange` |
250
- | Row actions | Custom action buttons per row |
251
- | Density modes | Compact, normal, comfortable |
252
- | Sticky columns | Pin columns to left or right |
253
- | Column resizing | Drag column header borders to resize |
254
- | Column reordering | Drag & drop column headers |
255
- | Grouped headers | Multi-row header with `colspan` via `children` |
256
- | Per-column filters | Filter input row with operators |
257
- | Inline editing | Double-click or Enter to edit: text, number, select, boolean |
258
- | Virtual scrolling | Render only visible rows for large datasets |
259
- | CSV/Excel export | Built-in toolbar buttons, no external deps |
260
- | Server-side mode | Delegate sort/filter/page to parent component |
261
- | TrackBy | Custom trackBy key or function |
262
- | i18n | All labels configurable via translation dictionary |
247
+ | Feature | Description |
248
+ | ------------------ | ---------------------------------------------------------------- |
249
+ | Sorting | Client-side multi-column sorting with sort icons and `aria-sort` |
250
+ | Search | Global text search across visible columns |
251
+ | Pagination | Configurable page size, page range buttons, first/last/prev/next |
252
+ | Selection | Checkbox row selection with `selectedIds` / `selectionChange` |
253
+ | Row actions | Custom action buttons per row |
254
+ | Density modes | Compact, normal, comfortable |
255
+ | Sticky columns | Pin columns to left or right |
256
+ | Column resizing | Drag column header borders to resize |
257
+ | Column reordering | Drag & drop column headers |
258
+ | Grouped headers | Multi-row header with `colspan` via `children` |
259
+ | Per-column filters | Filter input row with operators |
260
+ | Inline editing | Double-click or Enter to edit: text, number, select, boolean |
261
+ | Virtual scrolling | Render only visible rows for large datasets |
262
+ | CSV/Excel export | Built-in toolbar buttons, no external deps |
263
+ | Server-side mode | Delegate sort/filter/page to parent component |
264
+ | TrackBy | Custom trackBy key or function |
265
+ | i18n | All labels configurable via translation dictionary |
263
266
 
264
267
  ### Grid API
265
268
 
@@ -269,81 +272,82 @@ import { JvGridComponent } from '@devjuliovilla/jv-ui';
269
272
 
270
273
  **Inputs:**
271
274
 
272
- | Input | Type | Default | Description |
273
- |---|---|---|---|
274
- | `data` | `T[]` | `[]` | Row data array |
275
- | `columns` | `JvGridColumn<T>[]` | `[]` | Column definitions |
276
- | `actions` | `JvGridAction<T>[]` | `[]` | Row action button configs |
277
- | `options` | `Partial<JvGridOptions>` | `{}` | Feature toggles and config |
278
- | `trackBy` | `keyof T \| Function \| null` | `null` | Row identity |
279
- | `selectedIds` | `(string \| number)[]` | `[]` | Controlled selection IDs |
275
+ | Input | Type | Default | Description |
276
+ | ------------- | ----------------------------- | ------- | -------------------------- |
277
+ | `data` | `T[]` | `[]` | Row data array |
278
+ | `columns` | `JvGridColumn<T>[]` | `[]` | Column definitions |
279
+ | `actions` | `JvGridAction<T>[]` | `[]` | Row action button configs |
280
+ | `options` | `Partial<JvGridOptions>` | `{}` | Feature toggles and config |
281
+ | `trackBy` | `keyof T \| Function \| null` | `null` | Row identity |
282
+ | `selectedIds` | `(string \| number)[]` | `[]` | Controlled selection IDs |
280
283
 
281
284
  **Outputs:**
282
285
 
283
- | Output | Type |
284
- |---|---|
285
- | `rowClick` | `T` |
286
- | `rowDoubleClick` | `T` |
287
- | `actionClick` | `{ actionId: string; row: T }` |
288
- | `selectionChange` | `(string \| number)[]` |
289
- | `pageChange` | `{ pageIndex: number; pageSize: number }` |
290
- | `searchChange` | `string` |
291
- | `sortChange` | `{ columnKey: string; direction: 'asc' \| 'desc' \| null }` |
292
- | `columnFilter` | `{ columnKey: string; value: string }` |
293
- | `columnResize` | `{ columnKey: string; width: string }` |
294
- | `columnReorder` | `{ columnKey: string; newIndex: number }` |
295
- | `rowEdit` | `{ row: T; column: JvGridColumn<T>; value: unknown }` |
286
+ | Output | Type |
287
+ | ----------------- | ----------------------------------------------------------- |
288
+ | `rowClick` | `T` |
289
+ | `rowDoubleClick` | `T` |
290
+ | `actionClick` | `{ actionId: string; row: T }` |
291
+ | `selectionChange` | `(string \| number)[]` |
292
+ | `pageChange` | `{ pageIndex: number; pageSize: number }` |
293
+ | `searchChange` | `string` |
294
+ | `sortChange` | `{ columnKey: string; direction: 'asc' \| 'desc' \| null }` |
295
+ | `columnFilter` | `{ columnKey: string; value: string }` |
296
+ | `columnResize` | `{ columnKey: string; width: string }` |
297
+ | `columnReorder` | `{ columnKey: string; newIndex: number }` |
298
+ | `rowEdit` | `{ row: T; column: JvGridColumn<T>; value: unknown }` |
296
299
 
297
300
  **JvGridColumn options:**
298
301
 
299
- | Property | Type | Description |
300
- |---|---|---|
301
- | `key` | `keyof T \| string` | Data field key |
302
- | `header` | `string` | Column display header |
303
- | `sortable` | `boolean` | Enable column sorting |
304
- | `searchable` | `boolean` | Include in global search |
305
- | `filterable` | `boolean` | Show per-column filter input |
306
- | `hidden` | `boolean` | Hide column from display |
307
- | `sticky` | `'left' \| 'right'` | Pin column position |
308
- | `editable` | `boolean` | Allow inline cell editing |
309
- | `editType` | `'text' \| 'number' \| 'select' \| 'boolean'` | Editor type |
310
- | `editOptions` | `{ label: string; value: unknown }[]` | Options for select editor |
311
- | `width` | `string` | Column width (e.g. '8rem') |
312
- | `align` | `'start' \| 'center' \| 'end'` | Text alignment |
313
- | `type` | `'text' \| 'integer' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'time' \| 'datetime' \| 'boolean'` | Data type for formatting |
314
- | `formatOptions` | `Intl.NumberFormatOptions & Intl.DateTimeFormatOptions` | Per-column formatting overrides |
315
- | `format` | `(value, row) => string` | Custom display formatter |
316
- | `children` | `JvGridColumn<T>[]` | Child columns for grouped headers |
317
- | `resizable` | `boolean` | Allow column resize |
318
- | `cellClass` | `string` | CSS class for cells |
319
- | `headerClass` | `string` | CSS class for header |
302
+ | Property | Type | Description |
303
+ | --------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------- |
304
+ | `key` | `keyof T \| string` | Data field key |
305
+ | `header` | `string` | Column display header |
306
+ | `sortable` | `boolean` | Enable column sorting |
307
+ | `searchable` | `boolean` | Include in global search |
308
+ | `filterable` | `boolean` | Show per-column filter input |
309
+ | `hidden` | `boolean` | Hide column from display |
310
+ | `sticky` | `'left' \| 'right'` | Pin column position |
311
+ | `editable` | `boolean` | Allow inline cell editing |
312
+ | `editType` | `'text' \| 'number' \| 'select' \| 'boolean'` | Editor type |
313
+ | `editOptions` | `{ label: string; value: unknown }[]` | Options for select editor |
314
+ | `width` | `string` | Column width (e.g. '8rem') |
315
+ | `align` | `'start' \| 'center' \| 'end'` | Text alignment |
316
+ | `type` | `'text' \| 'integer' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'time' \| 'datetime' \| 'boolean'` | Data type for formatting |
317
+ | `formatOptions` | `Intl.NumberFormatOptions & Intl.DateTimeFormatOptions` | Per-column formatting overrides |
318
+ | `format` | `(value, row) => string` | Custom display formatter |
319
+ | `children` | `JvGridColumn<T>[]` | Child columns for grouped headers |
320
+ | `resizable` | `boolean` | Allow column resize |
321
+ | `cellClass` | `string` | CSS class for cells |
322
+ | `headerClass` | `string` | CSS class for header |
320
323
 
321
324
  **JvGridOptions:**
322
325
 
323
- | Option | Type | Default | Description |
324
- |---|---|---|---|
325
- | `searchable` | `boolean` | `true` | Show search bar |
326
- | `sortable` | `boolean` | `true` | Enable column sorting |
327
- | `pageable` | `boolean` | `true` | Enable pagination |
328
- | `selectable` | `boolean` | `false` | Show selection checkboxes |
329
- | `pageSize` | `number` | `20` | Default page size |
330
- | `pageSizeOptions` | `number[]` | `[10, 20, 50, 100]` | Page size selector options |
331
- | `density` | `'compact' \| 'normal' \| 'comfortable'` | `'normal'` | Row density |
332
- | `loading` | `boolean` | `false` | Show loading spinner |
333
- | `emptyMessage` | `string` | — | Empty state message |
334
- | `exportable` | `boolean` | `false` | Show CSV/Excel export buttons |
335
- | `exportFileName` | `string` | `''` | Base filename used for CSV/Excel exports; the component applies the correct extension |
336
- | `serverSide` | `boolean` | `false` | Disable client-side processing |
337
- | `totalItems` | `number` | `0` | Total items for server-side mode |
338
- | `stickyColumns` | `boolean` | `false` | Enable sticky column support |
339
- | `resizableColumns` | `boolean` | `false` | Enable column resizing |
340
- | `reorderableColumns` | `boolean` | `false` | Enable column reordering |
341
- | `editable` | `boolean` | `false` | Enable inline editing |
342
- | `virtualScroll` | `boolean` | `false` | Enable virtual scrolling |
343
- | `virtualScrollRowHeight` | `number` | `48` | Row height for virtual scroll |
344
- | `columnFilters` | `boolean` | `false` | Show filter row below headers |
345
- | `rowDoubleClick` | `boolean` | `false` | Enable row double-click event |
346
- | `ariaLabel` | `string` | — | Accessible label for the grid |
326
+ | Option | Type | Default | Description |
327
+ | ------------------------ | ---------------------------------------- | ------------------- | ------------------------------------------------------------------------------------- |
328
+ | `searchable` | `boolean` | `true` | Show search bar |
329
+ | `sortable` | `boolean` | `true` | Enable column sorting |
330
+ | `pageable` | `boolean` | `true` | Enable pagination |
331
+ | `selectable` | `boolean` | `false` | Show selection checkboxes |
332
+ | `pageSize` | `number` | `20` | Default page size |
333
+ | `pageSizeOptions` | `number[]` | `[10, 20, 50, 100]` | Page size selector options |
334
+ | `density` | `'compact' \| 'normal' \| 'comfortable'` | `'normal'` | Row density |
335
+ | `loading` | `boolean` | `false` | Show loading spinner |
336
+ | `emptyMessage` | `string` | — | Empty state message |
337
+ | `exportable` | `boolean` | `false` | Show CSV/Excel export buttons |
338
+ | `exportFileName` | `string` | `''` | Base filename used for CSV/Excel exports; the component applies the correct extension |
339
+ | `serverSide` | `boolean` | `false` | Disable client-side processing |
340
+ | `totalItems` | `number` | `0` | Total items for server-side mode |
341
+ | `stickyColumns` | `boolean` | `false` | Enable sticky column support |
342
+ | `resizableColumns` | `boolean` | `false` | Enable column resizing |
343
+ | `reorderableColumns` | `boolean` | `false` | Enable column reordering |
344
+ | `editable` | `boolean` | `false` | Enable inline editing |
345
+ | `virtualScroll` | `boolean` | `false` | Enable virtual scrolling |
346
+ | `virtualScrollRowHeight` | `number` | `48` | Row height for virtual scroll |
347
+ | `columnFilters` | `boolean` | `false` | Show filter row below headers |
348
+ | `rowDoubleClick` | `boolean` | `false` | Enable row double-click event |
349
+ | `keyboardNavigation` | `boolean` | `false` | Enable arrow-key cell navigation and `F2` editing flow |
350
+ | `ariaLabel` | `string` | — | Accessible label for the grid |
347
351
 
348
352
  ### Grid example
349
353
 
@@ -374,7 +378,9 @@ interface Product {
374
378
  `,
375
379
  })
376
380
  export class ProductsComponent {
377
- products: Product[] = [ /* ... */ ];
381
+ products: Product[] = [
382
+ /* ... */
383
+ ];
378
384
  selectedIds: (string | number)[] = [];
379
385
  columns: JvGridColumn<Product>[] = [
380
386
  { key: 'id', header: 'ID', width: '4rem' },
@@ -385,6 +391,51 @@ export class ProductsComponent {
385
391
  }
386
392
  ```
387
393
 
394
+ ### Grid behavior notes
395
+
396
+ - **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.
397
+ - **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.
398
+ - **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.
399
+ - **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.
400
+ - **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.
401
+ - **Formatting:** column `type` and `formatOptions` affect display only. Sorting and export use the raw field value, not the localized text shown in the cell.
402
+ - **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.
403
+
404
+ ### Keyboard navigation example
405
+
406
+ ```typescript
407
+ const gridOptions: JvGridOptions = {
408
+ keyboardNavigation: true,
409
+ editable: true,
410
+ pageable: false,
411
+ };
412
+
413
+ const columns: JvGridColumn<Product>[] = [
414
+ { key: 'name', header: 'Product', editable: true, editType: 'text' },
415
+ {
416
+ key: 'status',
417
+ header: 'Status',
418
+ editable: true,
419
+ editType: 'select',
420
+ editOptions: [
421
+ { label: 'Active', value: 'active' },
422
+ { label: 'Paused', value: 'paused' },
423
+ ],
424
+ },
425
+ ];
426
+ ```
427
+
428
+ ```html
429
+ <jv-grid
430
+ [data]="products"
431
+ [columns]="columns"
432
+ [options]="gridOptions"
433
+ (rowEdit)="onRowEdit($event)"
434
+ />
435
+ ```
436
+
437
+ 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.
438
+
388
439
  ## List API
389
440
 
390
441
  ```typescript
@@ -393,26 +444,26 @@ import { JvListComponent, JvListItemComponent } from '@devjuliovilla/jv-ui';
393
444
 
394
445
  **JvList inputs:**
395
446
 
396
- | Input | Type | Default | Description |
397
- |---|---|---|---|
398
- | `role` | `'list' \| 'navigation'` | `'list'` | ARIA role for the list |
399
- | `variant` | `'bordered' \| null` | `null` | Outer border + border-radius around the list (items always have bottom border) |
400
- | `ariaLabel` | `string` | `''` | Accessible label |
447
+ | Input | Type | Default | Description |
448
+ | ----------- | ------------------------ | -------- | ------------------------------------------------------------------------------ |
449
+ | `role` | `'list' \| 'navigation'` | `'list'` | ARIA role for the list |
450
+ | `variant` | `'bordered' \| null` | `null` | Outer border + border-radius around the list (items always have bottom border) |
451
+ | `ariaLabel` | `string` | `''` | Accessible label |
401
452
 
402
453
  **JvListItem inputs:**
403
454
 
404
- | Input | Type | Default | Description |
405
- |---|---|---|---|
406
- | `title` | `string` (required) | — | Primary text |
407
- | `description` | `string` | `''` | Secondary text below title |
408
- | `leadingIcon` | `string \| null` | `null` | Lucide icon name on the left |
409
- | `disabled` | `boolean` | `false` | Gray out and block interaction |
410
- | `active` | `boolean` | `false` | Highlight with primary color and left border |
455
+ | Input | Type | Default | Description |
456
+ | ------------- | ------------------- | ------- | -------------------------------------------- |
457
+ | `title` | `string` (required) | — | Primary text |
458
+ | `description` | `string` | `''` | Secondary text below title |
459
+ | `leadingIcon` | `string \| null` | `null` | Lucide icon name on the left |
460
+ | `disabled` | `boolean` | `false` | Gray out and block interaction |
461
+ | `active` | `boolean` | `false` | Highlight with primary color and left border |
411
462
 
412
463
  **JvListItem outputs:**
413
464
 
414
- | Output | Type |
415
- |---|---|
465
+ | Output | Type |
466
+ | ----------- | ------ |
416
467
  | `activated` | `void` |
417
468
 
418
469
  **Content projection:** use the `meta` attribute selector for badges, icons, or any content on the right side:
@@ -442,23 +493,17 @@ import { JvListComponent, JvListItemComponent, JvBadgeComponent } from '@devjuli
442
493
  leadingIcon="layout-dashboard"
443
494
  [active]="true"
444
495
  />
445
- <jv-list-item
446
- title="Notifications"
447
- description="3 pending"
448
- (activated)="onOpen()"
449
- >
496
+ <jv-list-item title="Notifications" description="3 pending" (activated)="onOpen()">
450
497
  <jv-badge tone="primary" meta>3</jv-badge>
451
498
  </jv-list-item>
452
- <jv-list-item
453
- title="Reports"
454
- description="Coming soon"
455
- [disabled]="true"
456
- />
499
+ <jv-list-item title="Reports" description="Coming soon" [disabled]="true" />
457
500
  </jv-list>
458
501
  `,
459
502
  })
460
503
  export class NavListComponent {
461
- onOpen() { /* handle activation */ }
504
+ onOpen() {
505
+ /* handle activation */
506
+ }
462
507
  }
463
508
  ```
464
509
 
@@ -468,16 +513,16 @@ export class NavListComponent {
468
513
  import { JvPaginationComponent } from '@devjuliovilla/jv-ui';
469
514
  ```
470
515
 
471
- | Input | Type | Default | Description |
472
- |---|---|---|---|
473
- | `totalItems` | `number` | `0` | Total items |
474
- | `pageSize` | `number` | `20` | Items per page |
475
- | `pageSizeOptions` | `number[]` | `[10, 20, 50, 100]` | Page size options |
476
- | `pageIndex` | `number` | `0` | Current page (0-based) |
477
- | `maxVisiblePages` | `number` | `5` | Max page buttons to show |
516
+ | Input | Type | Default | Description |
517
+ | ----------------- | ---------- | ------------------- | ------------------------ |
518
+ | `totalItems` | `number` | `0` | Total items |
519
+ | `pageSize` | `number` | `20` | Items per page |
520
+ | `pageSizeOptions` | `number[]` | `[10, 20, 50, 100]` | Page size options |
521
+ | `pageIndex` | `number` | `0` | Current page (0-based) |
522
+ | `maxVisiblePages` | `number` | `5` | Max page buttons to show |
478
523
 
479
- | Output | Type |
480
- |---|---|
524
+ | Output | Type |
525
+ | ------------ | ----------------------------------------- |
481
526
  | `pageChange` | `{ pageIndex: number; pageSize: number }` |
482
527
 
483
528
  ## Tabs API
@@ -488,17 +533,17 @@ import { JvTabsComponent, JvTabPanelDirective, JvTabItem } from '@devjuliovilla/
488
533
 
489
534
  **Inputs:**
490
535
 
491
- | Input | Type | Default | Description |
492
- |---|---|---|---|
493
- | `tabs` | `JvTabItem[]` | `[]` | Tab definitions (`{ id, label, icon?, disabled? }`) |
494
- | `activeTabId` | `string \| null` | `null` | Controlled active tab ID |
495
- | `keepMounted` | `boolean` | `true` | Keep all panels in DOM; `false` renders only active panel |
496
- | `transition` | `'none' \| 'fade' \| 'slide' \| null` | `null` | Panel transition animation (falls back to global config) |
536
+ | Input | Type | Default | Description |
537
+ | ------------- | ------------------------------------- | ------- | --------------------------------------------------------- |
538
+ | `tabs` | `JvTabItem[]` | `[]` | Tab definitions (`{ id, label, icon?, disabled? }`) |
539
+ | `activeTabId` | `string \| null` | `null` | Controlled active tab ID |
540
+ | `keepMounted` | `boolean` | `true` | Keep all panels in DOM; `false` renders only active panel |
541
+ | `transition` | `'none' \| 'fade' \| 'slide' \| null` | `null` | Panel transition animation (falls back to global config) |
497
542
 
498
543
  **Outputs:**
499
544
 
500
- | Output | Type |
501
- |---|---|
545
+ | Output | Type |
546
+ | ----------------- | -------- |
502
547
  | `activeTabChange` | `string` |
503
548
 
504
549
  **Content projection:** use `jvTabPanel` directive on `ng-template` to define panel content:
@@ -520,7 +565,7 @@ Tabs support animated transitions between panels. Configure globally via `provid
520
565
 
521
566
  ```typescript
522
567
  // Global default
523
- provideJvUi({ tabsTransition: 'fade' })
568
+ provideJvUi({ tabsTransition: 'fade' });
524
569
  ```
525
570
 
526
571
  ```typescript
@@ -528,11 +573,11 @@ provideJvUi({ tabsTransition: 'fade' })
528
573
  <jv-tabs transition="slide" [keepMounted]="false">...</jv-tabs>
529
574
  ```
530
575
 
531
- | Transition | `keepMounted=true` | `keepMounted=false` | Description |
532
- |---|---|---|---|
533
- | `'none'` | Instant switch | Instant switch | No animation |
534
- | `'fade'` | Opacity cross-fade | Opacity cross-fade | Smooth fade between panels |
535
- | `'slide'` | Falls back to `'fade'` | Directional slide | Slides left/right based on tab index |
576
+ | Transition | `keepMounted=true` | `keepMounted=false` | Description |
577
+ | ---------- | ---------------------- | ------------------- | ------------------------------------ |
578
+ | `'none'` | Instant switch | Instant switch | No animation |
579
+ | `'fade'` | Opacity cross-fade | Opacity cross-fade | Smooth fade between panels |
580
+ | `'slide'` | Falls back to `'fade'` | Directional slide | Slides left/right based on tab index |
536
581
 
537
582
  **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`.
538
583
 
@@ -578,35 +623,35 @@ import { JvImageSliderComponent, JvSliderImage } from '@devjuliovilla/jv-ui';
578
623
 
579
624
  **JvSliderImage:**
580
625
 
581
- | Property | Type | Required | Description |
582
- |---|---|---|---|
583
- | `src` | `string` | Yes | Image URL |
584
- | `alt` | `string` | Yes | Alternative text for accessibility |
585
- | `description` | `string` | No | Caption displayed below the image |
586
- | `thumbnail` | `string` | No | Custom thumbnail URL (defaults to `src`) |
626
+ | Property | Type | Required | Description |
627
+ | ------------- | -------- | -------- | ---------------------------------------- |
628
+ | `src` | `string` | Yes | Image URL |
629
+ | `alt` | `string` | Yes | Alternative text for accessibility |
630
+ | `description` | `string` | No | Caption displayed below the image |
631
+ | `thumbnail` | `string` | No | Custom thumbnail URL (defaults to `src`) |
587
632
 
588
633
  **Inputs:**
589
634
 
590
- | Input | Type | Default | Description |
591
- |---|---|---|---|
592
- | `images` | `JvSliderImage[]` | `[]` | Array of images to display |
593
- | `showThumbnails` | `boolean` | `true` | Show thumbnail strip below slider |
594
- | `showDescription` | `boolean` | `true` | Show image description |
635
+ | Input | Type | Default | Description |
636
+ | ----------------- | ----------------- | ------- | --------------------------------- |
637
+ | `images` | `JvSliderImage[]` | `[]` | Array of images to display |
638
+ | `showThumbnails` | `boolean` | `true` | Show thumbnail strip below slider |
639
+ | `showDescription` | `boolean` | `true` | Show image description |
595
640
 
596
641
  **Outputs:**
597
642
 
598
- | Output | Type | Description |
599
- |---|---|---|
643
+ | Output | Type | Description |
644
+ | ------------- | -------- | ------------------------------------- |
600
645
  | `imageChange` | `number` | Emits index when active image changes |
601
646
 
602
647
  **i18n support:**
603
648
 
604
649
  The component automatically translates aria labels using `JvTranslationService`:
605
650
 
606
- | Key | English | Spanish |
607
- |---|---|---|
608
- | `slider.prevImage` | Previous image | Imagen anterior |
609
- | `slider.nextImage` | Next image | Imagen siguiente |
651
+ | Key | English | Spanish |
652
+ | ---------------------- | ---------------- | ---------------------- |
653
+ | `slider.prevImage` | Previous image | Imagen anterior |
654
+ | `slider.nextImage` | Next image | Imagen siguiente |
610
655
  | `slider.thumbnailList` | Image thumbnails | Miniaturas de imagenes |
611
656
 
612
657
  ### Features
@@ -657,34 +702,34 @@ import { JvImageGalleryComponent, JvGalleryImage, JvGalleryAction } from '@devju
657
702
 
658
703
  **JvGalleryImage:**
659
704
 
660
- | Property | Type | Required | Description |
661
- |---|---|---|---|
662
- | `src` | `string` | Yes | Image URL |
663
- | `alt` | `string` | Yes | Alternative text for accessibility |
664
- | `thumbnail` | `string` | No | Custom thumbnail URL (defaults to `src`) |
705
+ | Property | Type | Required | Description |
706
+ | ----------- | -------- | -------- | ---------------------------------------- |
707
+ | `src` | `string` | Yes | Image URL |
708
+ | `alt` | `string` | Yes | Alternative text for accessibility |
709
+ | `thumbnail` | `string` | No | Custom thumbnail URL (defaults to `src`) |
665
710
 
666
711
  **JvGalleryAction:**
667
712
 
668
- | Property | Type | Required | Description |
669
- |---|---|---|---|
670
- | `id` | `string` | Yes | Unique action identifier |
671
- | `label` | `string` | Yes | Button label text |
672
- | `icon` | `string` | No | Lucide icon name |
673
- | `tone` | `JvIntent` | No | Button variant |
713
+ | Property | Type | Required | Description |
714
+ | -------- | ---------- | -------- | ------------------------ |
715
+ | `id` | `string` | Yes | Unique action identifier |
716
+ | `label` | `string` | Yes | Button label text |
717
+ | `icon` | `string` | No | Lucide icon name |
718
+ | `tone` | `JvIntent` | No | Button variant |
674
719
 
675
720
  **Inputs:**
676
721
 
677
- | Input | Type | Default | Description |
678
- |---|---|---|---|
679
- | `images` | `JvGalleryImage[]` | `[]` | Array of images to display |
680
- | `stripHeightVh` | `number` | `25` | Height of the thumbnail strip in viewport-height units, clamped between `0` and `100` |
681
- | `actions` | `JvGalleryAction[]` | `[]` | Action buttons shown in dialog footer |
722
+ | Input | Type | Default | Description |
723
+ | --------------- | ------------------- | ------- | ------------------------------------------------------------------------------------- |
724
+ | `images` | `JvGalleryImage[]` | `[]` | Array of images to display |
725
+ | `stripHeightVh` | `number` | `25` | Height of the thumbnail strip in viewport-height units, clamped between `0` and `100` |
726
+ | `actions` | `JvGalleryAction[]` | `[]` | Action buttons shown in dialog footer |
682
727
 
683
728
  **Outputs:**
684
729
 
685
- | Output | Type | Description |
686
- |---|---|---|
687
- | `imageClick` | `JvGalleryImage` | Emits when an image is clicked |
730
+ | Output | Type | Description |
731
+ | ------------- | --------------------------------------------- | -------------------------------------- |
732
+ | `imageClick` | `JvGalleryImage` | Emits when an image is clicked |
688
733
  | `actionClick` | `{ actionId: string; image: JvGalleryImage }` | Emits when an action button is clicked |
689
734
 
690
735
  ### Features
@@ -745,17 +790,17 @@ export class GalleryComponent {
745
790
  import { JvTextareaComponent } from '@devjuliovilla/jv-ui';
746
791
  ```
747
792
 
748
- | Input | Type | Default | Description |
749
- |---|---|---|---|
750
- | `placeholder` | `string` | `''` | Placeholder text |
751
- | `required` | `boolean` | `false` | Mark field as required |
752
- | `invalid` | `boolean` | `false` | Show invalid state |
753
- | `readonly` | `boolean` | `false` | Make textarea read-only |
754
- | `rows` | `number` | `3` | Visible number of rows |
755
- | `cols` | `number` | — | Visible width in columns |
756
- | `maxlength` | `number` | — | Maximum character length |
757
- | `describedBy` | `string` | `''` | ID of description/error element for aria-describedby |
758
- | `inputId` | `string` | `jv-textarea-{n}` | Auto-generated unique ID |
793
+ | Input | Type | Default | Description |
794
+ | ------------- | --------- | ----------------- | ---------------------------------------------------- |
795
+ | `placeholder` | `string` | `''` | Placeholder text |
796
+ | `required` | `boolean` | `false` | Mark field as required |
797
+ | `invalid` | `boolean` | `false` | Show invalid state |
798
+ | `readonly` | `boolean` | `false` | Make textarea read-only |
799
+ | `rows` | `number` | `3` | Visible number of rows |
800
+ | `cols` | `number` | — | Visible width in columns |
801
+ | `maxlength` | `number` | — | Maximum character length |
802
+ | `describedBy` | `string` | `''` | ID of description/error element for aria-describedby |
803
+ | `inputId` | `string` | `jv-textarea-{n}` | Auto-generated unique ID |
759
804
 
760
805
  Implements `ControlValueAccessor` — compatible with `[(ngModel)]` and reactive forms via `formControlName`.
761
806
 
@@ -790,15 +835,15 @@ export class ProfileComponent {
790
835
  import { JvCodeComponent } from '@devjuliovilla/jv-ui';
791
836
  ```
792
837
 
793
- | Input | Type | Default | Description |
794
- |---|---|---|---|
795
- | `code` | `string` | — | Code or text content to render |
796
- | `language` | `string` | `''` | Optional language badge |
797
- | `title` | `string` | `''` | Optional header title |
798
- | `font` | `'jetbrains-mono' \| 'fira-code' \| 'cascadia' \| 'consolas' \| 'system'` | `'jetbrains-mono'` | Mono font preset for the rendered block |
799
- | `editable` | `boolean` | `false` | Renders a textarea-style editing surface instead of read-only preview |
800
- | `width` | `string` | `'100%'` | CSS width applied to the component |
801
- | `height` | `string` | `'auto'` | CSS height for the editable surface or minimum preview height |
838
+ | Input | Type | Default | Description |
839
+ | ---------- | ------------------------------------------------------------------------- | ------------------ | --------------------------------------------------------------------- |
840
+ | `code` | `string` | — | Code or text content to render |
841
+ | `language` | `string` | `''` | Optional language badge |
842
+ | `title` | `string` | `''` | Optional header title |
843
+ | `font` | `'jetbrains-mono' \| 'fira-code' \| 'cascadia' \| 'consolas' \| 'system'` | `'jetbrains-mono'` | Mono font preset for the rendered block |
844
+ | `editable` | `boolean` | `false` | Renders a textarea-style editing surface instead of read-only preview |
845
+ | `width` | `string` | `'100%'` | CSS width applied to the component |
846
+ | `height` | `string` | `'auto'` | CSS height for the editable surface or minimum preview height |
802
847
 
803
848
  Use `JvCode` for read-only payloads or long single-line content where horizontal scrolling should be preserved instead of wrapping.
804
849
  The library does not bundle font files. `JvCode` references common mono families and will use any web font you load at the application level.
@@ -879,13 +924,13 @@ await dialogService.confirm({
879
924
  import { JvIconComponent, JV_LUCIDE_ICON_REGISTRY } from '@devjuliovilla/jv-ui';
880
925
  ```
881
926
 
882
- | Input | Type | Default | Description |
883
- |---|---|---|---|
884
- | `name` | `string` | — | Icon name (from auto-generated Lucide registry) |
885
- | `size` | `number` | `24` | Width and height in pixels |
886
- | `strokeWidth` | `number` | `2` | SVG stroke width |
887
- | `decorative` | `boolean` | `true` | `aria-hidden="true"` when decorative |
888
- | `ariaLabel` | `string` | — | Accessible label when not decorative |
927
+ | Input | Type | Default | Description |
928
+ | ------------- | --------- | ------- | ----------------------------------------------- |
929
+ | `name` | `string` | — | Icon name (from auto-generated Lucide registry) |
930
+ | `size` | `number` | `24` | Width and height in pixels |
931
+ | `strokeWidth` | `number` | `2` | SVG stroke width |
932
+ | `decorative` | `boolean` | `true` | `aria-hidden="true"` when decorative |
933
+ | `ariaLabel` | `string` | — | Accessible label when not decorative |
889
934
 
890
935
  Icons are auto-generated from [lucide-static](https://www.npmjs.com/package/lucide-static) via:
891
936
 
@@ -895,7 +940,7 @@ node tools/generate-icons.mjs
895
940
 
896
941
  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.
897
942
 
898
- ## Layout API
943
+ ## Layout API
899
944
 
900
945
  ### JvDashboardShell
901
946
 
@@ -903,47 +948,47 @@ This reads all SVG files from `lucide-static/icons` and produces `jv-icon-regist
903
948
  import { JvDashboardShellComponent } from '@devjuliovilla/jv-ui';
904
949
  ```
905
950
 
906
- | Input | Type | Default | Description |
907
- |---|---|---|---|
908
- | `title` | `string` | — | App / brand title |
909
- | `sidebarSubtitle` | `string` | `''` | Subtitle below brand |
910
- | `brandIcon` | `string` | `'monitor'` | Brand Lucide icon |
911
- | `navItems` | `JvNavItem[]` | `[]` | Navigation items (supports nested children) |
912
- | `sidebarCollapsed` | `boolean` | `false` | Desktop sidebar collapsed state |
913
- | `mobileSidebarOpen` | `boolean` | `false` | Mobile sidebar open/closed |
914
- | `topbarTitle` | `string` | `''` | Topbar page title |
915
- | `topbarSubtitle` | `string` | `''` | Topbar subtitle |
916
- | `currentTheme` | `JvTheme` | `'light'` | Active theme value |
917
- | `themeOptions` | `JvThemeOption[]` | `[]` | Theme selector options |
918
- | `topbarActions` | `JvTopbarAction[]` | `[]` | Icon buttons in topbar |
919
- | `showThemeSelector` | `boolean` | `true` | Show theme pills |
920
-
921
- | Output | Description |
922
- |---|---|
923
- | `menuClick` | Hamburger button clicked — toggle mobile sidebar |
924
- | `mobileClose` | Backdrop clicked or navigation selected — close mobile sidebar |
925
- | `sidebarCollapseToggle` | Collapse toggle clicked — collapse/expand sidebar |
926
- | `themeChange` | Theme pill clicked |
927
- | `topbarActionClick` | Topbar action button clicked |
951
+ | Input | Type | Default | Description |
952
+ | ------------------- | ------------------ | ----------- | ------------------------------------------- |
953
+ | `title` | `string` | — | App / brand title |
954
+ | `sidebarSubtitle` | `string` | `''` | Subtitle below brand |
955
+ | `brandIcon` | `string` | `'monitor'` | Brand Lucide icon |
956
+ | `navItems` | `JvNavItem[]` | `[]` | Navigation items (supports nested children) |
957
+ | `sidebarCollapsed` | `boolean` | `false` | Desktop sidebar collapsed state |
958
+ | `mobileSidebarOpen` | `boolean` | `false` | Mobile sidebar open/closed |
959
+ | `topbarTitle` | `string` | `''` | Topbar page title |
960
+ | `topbarSubtitle` | `string` | `''` | Topbar subtitle |
961
+ | `currentTheme` | `JvTheme` | `'light'` | Active theme value |
962
+ | `themeOptions` | `JvThemeOption[]` | `[]` | Theme selector options |
963
+ | `topbarActions` | `JvTopbarAction[]` | `[]` | Icon buttons in topbar |
964
+ | `showThemeSelector` | `boolean` | `true` | Show theme pills |
965
+
966
+ | Output | Description |
967
+ | ----------------------- | -------------------------------------------------------------- |
968
+ | `menuClick` | Hamburger button clicked — toggle mobile sidebar |
969
+ | `mobileClose` | Backdrop clicked or navigation selected — close mobile sidebar |
970
+ | `sidebarCollapseToggle` | Collapse toggle clicked — collapse/expand sidebar |
971
+ | `themeChange` | Theme pill clicked |
972
+ | `topbarActionClick` | Topbar action button clicked |
928
973
 
929
974
  **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.
930
975
 
931
976
  ### JvSidebar
932
977
 
933
- | Input | Type | Default | Description |
934
- |---|---|---|---|
935
- | `title` | `string` | — | Brand title |
936
- | `subtitle` | `string` | `''` | Brand subtitle |
937
- | `brandIcon` | `string` | `'monitor'` | Brand icon |
938
- | `items` | `JvNavItem[]` | `[]` | Navigation items |
939
- | `collapsed` | `boolean` | `false` | Collapse to icon-only |
940
- | `mobileOpen` | `boolean` | `false` | Show on mobile |
941
- | `ariaLabel` | `string` | `'Sidebar navigation'` | Accessible label |
942
-
943
- | Output | Description |
944
- |---|---|
945
- | `collapseToggle` | Collapse button clicked |
946
- | `mobileClose` | Backdrop or nav item interaction — request close |
978
+ | Input | Type | Default | Description |
979
+ | ------------ | ------------- | ---------------------- | --------------------- |
980
+ | `title` | `string` | — | Brand title |
981
+ | `subtitle` | `string` | `''` | Brand subtitle |
982
+ | `brandIcon` | `string` | `'monitor'` | Brand icon |
983
+ | `items` | `JvNavItem[]` | `[]` | Navigation items |
984
+ | `collapsed` | `boolean` | `false` | Collapse to icon-only |
985
+ | `mobileOpen` | `boolean` | `false` | Show on mobile |
986
+ | `ariaLabel` | `string` | `'Sidebar navigation'` | Accessible label |
987
+
988
+ | Output | Description |
989
+ | ---------------- | ------------------------------------------------ |
990
+ | `collapseToggle` | Collapse button clicked |
991
+ | `mobileClose` | Backdrop or nav item interaction — request close |
947
992
 
948
993
  ## Responsive Design
949
994
 
@@ -958,14 +1003,14 @@ The library includes basic responsive behavior:
958
1003
 
959
1004
  ## Services
960
1005
 
961
- | Service | Import Path | Description |
962
- |---|---|---|
963
- | `JvThemeService` | `@devjuliovilla/jv-ui` | Light / dark / high-contrast theme switching |
964
- | `JvTranslationService` | `@devjuliovilla/jv-ui` | Dictionary-based i18n (en/es) |
965
- | `JvToastService` | `@devjuliovilla/jv-ui` | Toast notification system |
966
- | `JvLoaderService` | `@devjuliovilla/jv-ui` | Full-screen loader with request counter |
967
- | `JvDialogService` | `@devjuliovilla/jv-ui` | Programmatic confirm dialogs |
968
- | `JvAnnouncementService` | `@devjuliovilla/jv-ui` | Screen-reader live-region announcements |
1006
+ | Service | Import Path | Description |
1007
+ | ----------------------- | ---------------------- | -------------------------------------------- |
1008
+ | `JvThemeService` | `@devjuliovilla/jv-ui` | Light / dark / high-contrast theme switching |
1009
+ | `JvTranslationService` | `@devjuliovilla/jv-ui` | Dictionary-based i18n (en/es) |
1010
+ | `JvToastService` | `@devjuliovilla/jv-ui` | Toast notification system |
1011
+ | `JvLoaderService` | `@devjuliovilla/jv-ui` | Full-screen loader with request counter |
1012
+ | `JvDialogService` | `@devjuliovilla/jv-ui` | Programmatic confirm dialogs |
1013
+ | `JvAnnouncementService` | `@devjuliovilla/jv-ui` | Screen-reader live-region announcements |
969
1014
 
970
1015
  ## Accessibility
971
1016