@devjuliovilla/jv-ui 1.6.0 → 1.6.1

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
@@ -44,7 +44,8 @@ export class App {}
44
44
  | Buttons | `JvButton`, `JvButtonGroup`, `JvIconButton` |
45
45
  | Forms | `JvInput`, `JvTextarea`, `JvSelect`, `JvCheckbox`, `JvRadio`, `JvSwitch`, `JvFormContainer` |
46
46
  | Data Grid | `JvGrid` — full-featured data table (see below) |
47
- | Containers | `JvCard`, `JvSection`, `JvDivider` |
47
+ | Containeres | `JvCard`, `JvSection`, `JvDivider` |
48
+ | List | `JvList`, `JvListItem` — list with title, description, icons, badges, and interactive items |
48
49
  | Feedback | `JvAlert`, `JvBadge`, `JvLoader`, `JvToast` |
49
50
  | Dialogs | `JvDialog`, `JvConfirmDialog` |
50
51
  | Layout | `JvDashboardShell`, `JvSidebar`, `JvTopbar`, `JvBreadcrumb`, `JvPage` |
@@ -196,6 +197,83 @@ export class ProductsComponent {
196
197
  }
197
198
  ```
198
199
 
200
+ ## List API
201
+
202
+ ```typescript
203
+ import { JvListComponent, JvListItemComponent } from '@devjuliovilla/jv-ui';
204
+ ```
205
+
206
+ **JvList inputs:**
207
+
208
+ | Input | Type | Default | Description |
209
+ |---|---|---|---|
210
+ | `role` | `'list' \| 'navigation'` | `'list'` | ARIA role for the list |
211
+ | `variant` | `'bordered' \| null` | `null` | Outer border + border-radius around the list (items always have bottom border) |
212
+ | `ariaLabel` | `string` | `''` | Accessible label |
213
+
214
+ **JvListItem inputs:**
215
+
216
+ | Input | Type | Default | Description |
217
+ |---|---|---|---|
218
+ | `title` | `string` (required) | — | Primary text |
219
+ | `description` | `string` | `''` | Secondary text below title |
220
+ | `leadingIcon` | `string \| null` | `null` | Lucide icon name on the left |
221
+ | `disabled` | `boolean` | `false` | Gray out and block interaction |
222
+ | `active` | `boolean` | `false` | Highlight with primary color and left border |
223
+
224
+ **JvListItem outputs:**
225
+
226
+ | Output | Type |
227
+ |---|---|
228
+ | `activated` | `void` |
229
+
230
+ **Content projection:** use the `meta` attribute selector for badges, icons, or any content on the right side:
231
+
232
+ ```html
233
+ <jv-list-item title="Inbox" description="New messages">
234
+ <jv-badge tone="primary" meta>3</jv-badge>
235
+ <jv-icon name="chevron-right" meta />
236
+ </jv-list-item>
237
+ ```
238
+
239
+ ### Example
240
+
241
+ ```typescript
242
+ import { Component } from '@angular/core';
243
+ import { JvListComponent, JvListItemComponent, JvBadgeComponent } from '@devjuliovilla/jv-ui';
244
+
245
+ @Component({
246
+ selector: 'app-nav-list',
247
+ standalone: true,
248
+ imports: [JvListComponent, JvListItemComponent, JvBadgeComponent],
249
+ template: `
250
+ <jv-list variant="bordered">
251
+ <jv-list-item
252
+ title="Dashboard"
253
+ description="Main overview"
254
+ leadingIcon="layout-dashboard"
255
+ [active]="true"
256
+ />
257
+ <jv-list-item
258
+ title="Notifications"
259
+ description="3 pending"
260
+ (activated)="onOpen()"
261
+ >
262
+ <jv-badge tone="primary" meta>3</jv-badge>
263
+ </jv-list-item>
264
+ <jv-list-item
265
+ title="Reports"
266
+ description="Coming soon"
267
+ [disabled]="true"
268
+ />
269
+ </jv-list>
270
+ `,
271
+ })
272
+ export class NavListComponent {
273
+ onOpen() { /* handle activation */ }
274
+ }
275
+ ```
276
+
199
277
  ## Pagination API
200
278
 
201
279
  ```typescript
@@ -20292,29 +20292,29 @@ class JvListComponent {
20292
20292
  return v ? `jv-list--${v}` : '';
20293
20293
  }, ...(ngDevMode ? [{ debugName: "variantClass" }] : /* istanbul ignore next */ []));
20294
20294
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20295
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: JvListComponent, isStandalone: true, selector: "jv-list", inputs: { role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
20296
- <ul
20297
- class="jv-list"
20298
- [class]="variantClass()"
20299
- [attr.role]="role() === 'navigation' ? 'list' : null"
20300
- [attr.aria-label]="ariaLabel()"
20301
- >
20302
- <ng-content />
20303
- </ul>
20304
- `, isInline: true, styles: [".jv-list{display:block;list-style:none;margin:0;padding:0}.jv-list--bordered{border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md)}.jv-list--bordered>:not(:last-child){border-bottom:1px solid var(--jv-color-border)}\n"] });
20295
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: JvListComponent, isStandalone: true, selector: "jv-list", inputs: { role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
20296
+ <ul
20297
+ class="jv-list"
20298
+ [class]="variantClass()"
20299
+ [attr.role]="role() === 'navigation' ? 'list' : null"
20300
+ [attr.aria-label]="ariaLabel()"
20301
+ >
20302
+ <ng-content />
20303
+ </ul>
20304
+ `, isInline: true, styles: [".jv-list{display:block;list-style:none;margin:0;padding:0}.jv-list>:not(:last-child){border-bottom:1px solid var(--jv-color-border)}.jv-list--bordered{border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md)}\n"] });
20305
20305
  }
20306
20306
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvListComponent, decorators: [{
20307
20307
  type: Component,
20308
- args: [{ selector: 'jv-list', standalone: true, template: `
20309
- <ul
20310
- class="jv-list"
20311
- [class]="variantClass()"
20312
- [attr.role]="role() === 'navigation' ? 'list' : null"
20313
- [attr.aria-label]="ariaLabel()"
20314
- >
20315
- <ng-content />
20316
- </ul>
20317
- `, styles: [".jv-list{display:block;list-style:none;margin:0;padding:0}.jv-list--bordered{border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md)}.jv-list--bordered>:not(:last-child){border-bottom:1px solid var(--jv-color-border)}\n"] }]
20308
+ args: [{ selector: 'jv-list', standalone: true, template: `
20309
+ <ul
20310
+ class="jv-list"
20311
+ [class]="variantClass()"
20312
+ [attr.role]="role() === 'navigation' ? 'list' : null"
20313
+ [attr.aria-label]="ariaLabel()"
20314
+ >
20315
+ <ng-content />
20316
+ </ul>
20317
+ `, styles: [".jv-list{display:block;list-style:none;margin:0;padding:0}.jv-list>:not(:last-child){border-bottom:1px solid var(--jv-color-border)}.jv-list--bordered{border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md)}\n"] }]
20318
20318
  }], propDecorators: { role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
20319
20319
 
20320
20320
  class JvListItemComponent {
@@ -20330,58 +20330,58 @@ class JvListItemComponent {
20330
20330
  }
20331
20331
  }
20332
20332
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20333
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvListItemComponent, isStandalone: true, selector: "jv-list-item", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, leadingIcon: { classPropertyName: "leadingIcon", publicName: "leadingIcon", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activated: "activated" }, ngImport: i0, template: `
20334
- <li
20335
- class="jv-list-item"
20336
- [class.jv-list-item--disabled]="disabled()"
20337
- [class.jv-list-item--active]="active()"
20338
- tabindex="0"
20339
- role="listitem"
20340
- (click)="onActivate()"
20341
- (keydown.enter)="onActivate()"
20342
- (keydown.space)="onActivate(); $event.preventDefault()"
20343
- >
20344
- @if (leadingIcon()) {
20345
- <jv-icon [name]="leadingIcon()!" class="jv-list-item-leading" [size]="18" />
20346
- }
20347
- <div class="jv-list-item-text">
20348
- <span class="jv-list-item-title">{{ title() }}</span>
20349
- @if (description()) {
20350
- <span class="jv-list-item-description">{{ description() }}</span>
20351
- }
20352
- </div>
20353
- <div class="jv-list-item-meta">
20354
- <ng-content select="[meta]" />
20355
- </div>
20356
- </li>
20333
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvListItemComponent, isStandalone: true, selector: "jv-list-item", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, leadingIcon: { classPropertyName: "leadingIcon", publicName: "leadingIcon", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activated: "activated" }, ngImport: i0, template: `
20334
+ <li
20335
+ class="jv-list-item"
20336
+ [class.jv-list-item--disabled]="disabled()"
20337
+ [class.jv-list-item--active]="active()"
20338
+ tabindex="0"
20339
+ role="listitem"
20340
+ (click)="onActivate()"
20341
+ (keydown.enter)="onActivate()"
20342
+ (keydown.space)="onActivate(); $event.preventDefault()"
20343
+ >
20344
+ @if (leadingIcon()) {
20345
+ <jv-icon [name]="leadingIcon()!" class="jv-list-item-leading" [size]="18" />
20346
+ }
20347
+ <div class="jv-list-item-text">
20348
+ <span class="jv-list-item-title">{{ title() }}</span>
20349
+ @if (description()) {
20350
+ <span class="jv-list-item-description">{{ description() }}</span>
20351
+ }
20352
+ </div>
20353
+ <div class="jv-list-item-meta">
20354
+ <ng-content select="[meta]" />
20355
+ </div>
20356
+ </li>
20357
20357
  `, isInline: true, styles: [":host{display:block}.jv-list-item{display:flex;align-items:center;gap:var(--jv-spacing-md);padding:var(--jv-spacing-md);background:var(--jv-color-surface);cursor:default;outline:none;transition:background-color .16s ease}.jv-list-item:hover{background:var(--jv-color-surface-muted)}.jv-list-item:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:-2px}.jv-list-item--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.jv-list-item--active{background:color-mix(in srgb,var(--jv-color-primary) 10%,var(--jv-color-surface));border-left:3px solid var(--jv-color-primary);padding-left:calc(var(--jv-spacing-md) - 3px)}.jv-list-item-text{flex:1;display:flex;flex-direction:column;gap:.125rem;min-width:0}.jv-list-item-title{font-weight:600;font-size:var(--jv-density-font-size);color:var(--jv-color-foreground);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.jv-list-item-description{font-size:.85rem;color:var(--jv-color-foreground-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.jv-list-item-meta{display:flex;align-items:center;gap:var(--jv-spacing-sm);flex-shrink:0}.jv-list-item-leading{flex-shrink:0;color:var(--jv-color-foreground-muted)}\n"], dependencies: [{ kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
20358
20358
  }
20359
20359
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvListItemComponent, decorators: [{
20360
20360
  type: Component,
20361
- args: [{ selector: 'jv-list-item', standalone: true, imports: [JvIconComponent], template: `
20362
- <li
20363
- class="jv-list-item"
20364
- [class.jv-list-item--disabled]="disabled()"
20365
- [class.jv-list-item--active]="active()"
20366
- tabindex="0"
20367
- role="listitem"
20368
- (click)="onActivate()"
20369
- (keydown.enter)="onActivate()"
20370
- (keydown.space)="onActivate(); $event.preventDefault()"
20371
- >
20372
- @if (leadingIcon()) {
20373
- <jv-icon [name]="leadingIcon()!" class="jv-list-item-leading" [size]="18" />
20374
- }
20375
- <div class="jv-list-item-text">
20376
- <span class="jv-list-item-title">{{ title() }}</span>
20377
- @if (description()) {
20378
- <span class="jv-list-item-description">{{ description() }}</span>
20379
- }
20380
- </div>
20381
- <div class="jv-list-item-meta">
20382
- <ng-content select="[meta]" />
20383
- </div>
20384
- </li>
20361
+ args: [{ selector: 'jv-list-item', standalone: true, imports: [JvIconComponent], template: `
20362
+ <li
20363
+ class="jv-list-item"
20364
+ [class.jv-list-item--disabled]="disabled()"
20365
+ [class.jv-list-item--active]="active()"
20366
+ tabindex="0"
20367
+ role="listitem"
20368
+ (click)="onActivate()"
20369
+ (keydown.enter)="onActivate()"
20370
+ (keydown.space)="onActivate(); $event.preventDefault()"
20371
+ >
20372
+ @if (leadingIcon()) {
20373
+ <jv-icon [name]="leadingIcon()!" class="jv-list-item-leading" [size]="18" />
20374
+ }
20375
+ <div class="jv-list-item-text">
20376
+ <span class="jv-list-item-title">{{ title() }}</span>
20377
+ @if (description()) {
20378
+ <span class="jv-list-item-description">{{ description() }}</span>
20379
+ }
20380
+ </div>
20381
+ <div class="jv-list-item-meta">
20382
+ <ng-content select="[meta]" />
20383
+ </div>
20384
+ </li>
20385
20385
  `, styles: [":host{display:block}.jv-list-item{display:flex;align-items:center;gap:var(--jv-spacing-md);padding:var(--jv-spacing-md);background:var(--jv-color-surface);cursor:default;outline:none;transition:background-color .16s ease}.jv-list-item:hover{background:var(--jv-color-surface-muted)}.jv-list-item:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:-2px}.jv-list-item--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.jv-list-item--active{background:color-mix(in srgb,var(--jv-color-primary) 10%,var(--jv-color-surface));border-left:3px solid var(--jv-color-primary);padding-left:calc(var(--jv-spacing-md) - 3px)}.jv-list-item-text{flex:1;display:flex;flex-direction:column;gap:.125rem;min-width:0}.jv-list-item-title{font-weight:600;font-size:var(--jv-density-font-size);color:var(--jv-color-foreground);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.jv-list-item-description{font-size:.85rem;color:var(--jv-color-foreground-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.jv-list-item-meta{display:flex;align-items:center;gap:var(--jv-spacing-sm);flex-shrink:0}.jv-list-item-leading{flex-shrink:0;color:var(--jv-color-foreground-muted)}\n"] }]
20386
20386
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], leadingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "leadingIcon", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], activated: [{ type: i0.Output, args: ["activated"] }] } });
20387
20387