@devjuliovilla/jv-ui 1.7.1 → 1.8.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.
@@ -1,8 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, computed, Component, forwardRef, inject, output, signal, Injectable, effect, HostListener, InjectionToken, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, viewChild } from '@angular/core';
2
+ import { input, computed, Component, forwardRef, inject, output, Directive, Injectable, signal, InjectionToken, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, effect, contentChildren, HostListener, viewChild, viewChildren, afterNextRender, Renderer2, ElementRef } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
4
  import { NG_VALUE_ACCESSOR, FormBuilder, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
5
- import { CommonModule, DOCUMENT } from '@angular/common';
5
+ import { CommonModule, DOCUMENT, NgTemplateOutlet } from '@angular/common';
6
6
  import { Router, RouterLink } from '@angular/router';
7
7
 
8
8
  // This file is auto-generated by tools/generate-icons.mjs
@@ -18658,116 +18658,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
18658
18658
  `, styles: [".jv-divider{width:100%;height:1px;background:var(--jv-color-border)}.jv-divider--vertical{width:1px;height:100%;min-height:1.5rem}\n"] }]
18659
18659
  }], propDecorators: { vertical: [{ type: i0.Input, args: [{ isSignal: true, alias: "vertical", required: false }] }] } });
18660
18660
 
18661
- class JvBadgeComponent {
18662
- tone = input('primary', ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
18663
- badgeClass = computed(() => `jv-badge--${this.tone()}`, ...(ngDevMode ? [{ debugName: "badgeClass" }] : /* istanbul ignore next */ []));
18664
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18665
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: JvBadgeComponent, isStandalone: true, selector: "jv-badge", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `<span class="jv-badge" [class]="badgeClass()"><ng-content></ng-content></span>`, isInline: true, styles: [".jv-badge{display:inline-flex;align-items:center;justify-content:center;min-height:1.75rem;padding:0 var(--jv-spacing-sm);border-radius:999px;font-size:.8rem;font-weight:700;line-height:1;white-space:nowrap}.jv-badge--primary{background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface));color:var(--jv-color-primary)}.jv-badge--secondary,.jv-badge--neutral{background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.jv-badge--success{background:color-mix(in srgb,var(--jv-color-success) 16%,var(--jv-color-surface));color:var(--jv-color-success)}.jv-badge--warning{background:color-mix(in srgb,var(--jv-color-warning) 20%,var(--jv-color-surface));color:#8a4b00}.jv-badge--danger{background:color-mix(in srgb,var(--jv-color-danger) 16%,var(--jv-color-surface));color:var(--jv-color-danger)}.jv-badge--info{background:color-mix(in srgb,var(--jv-color-info) 16%,var(--jv-color-surface));color:var(--jv-color-info)}\n"] });
18666
- }
18667
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvBadgeComponent, decorators: [{
18668
- type: Component,
18669
- args: [{ selector: 'jv-badge', standalone: true, template: `<span class="jv-badge" [class]="badgeClass()"><ng-content></ng-content></span>`, styles: [".jv-badge{display:inline-flex;align-items:center;justify-content:center;min-height:1.75rem;padding:0 var(--jv-spacing-sm);border-radius:999px;font-size:.8rem;font-weight:700;line-height:1;white-space:nowrap}.jv-badge--primary{background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface));color:var(--jv-color-primary)}.jv-badge--secondary,.jv-badge--neutral{background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.jv-badge--success{background:color-mix(in srgb,var(--jv-color-success) 16%,var(--jv-color-surface));color:var(--jv-color-success)}.jv-badge--warning{background:color-mix(in srgb,var(--jv-color-warning) 20%,var(--jv-color-surface));color:#8a4b00}.jv-badge--danger{background:color-mix(in srgb,var(--jv-color-danger) 16%,var(--jv-color-surface));color:var(--jv-color-danger)}.jv-badge--info{background:color-mix(in srgb,var(--jv-color-info) 16%,var(--jv-color-surface));color:var(--jv-color-info)}\n"] }]
18670
- }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
18671
-
18672
- class JvLoaderService {
18673
- counter = signal(0, ...(ngDevMode ? [{ debugName: "counter" }] : /* istanbul ignore next */ []));
18674
- visible = signal(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
18675
- message = signal('Loading', ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
18676
- delayMs = 300;
18677
- showTimeout = null;
18678
- activeCount = this.counter.asReadonly();
18679
- isVisible = this.visible.asReadonly();
18680
- liveMessage = this.message.asReadonly();
18681
- show(message = 'Loading') {
18682
- this.message.set(message);
18683
- this.counter.update((count) => count + 1);
18684
- if (this.counter() === 1) {
18685
- this.scheduleVisibility();
18686
- }
18687
- }
18688
- hide() {
18689
- this.counter.update((count) => Math.max(0, count - 1));
18690
- if (this.counter() === 0) {
18691
- this.clearTimer();
18692
- this.visible.set(false);
18693
- }
18694
- }
18695
- scheduleVisibility() {
18696
- this.clearTimer();
18697
- this.showTimeout = setTimeout(() => {
18698
- if (this.counter() > 0) {
18699
- this.visible.set(true);
18700
- }
18701
- this.showTimeout = null;
18702
- }, this.delayMs);
18703
- }
18704
- clearTimer() {
18705
- if (this.showTimeout) {
18706
- clearTimeout(this.showTimeout);
18707
- this.showTimeout = null;
18708
- }
18709
- }
18710
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18711
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderService, providedIn: 'root' });
18712
- }
18713
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderService, decorators: [{
18714
- type: Injectable,
18715
- args: [{ providedIn: 'root' }]
18716
- }] });
18717
-
18718
- class JvLoaderComponent {
18719
- document = inject(DOCUMENT);
18720
- loaderService = inject(JvLoaderService);
18721
- constructor() {
18722
- effect(() => {
18723
- const locked = this.loaderService.isVisible();
18724
- const body = this.document.body;
18725
- const root = this.document.documentElement;
18726
- body.style.overflow = locked ? 'hidden' : '';
18727
- root.style.overflow = locked ? 'hidden' : '';
18728
- });
18729
- }
18730
- handleWheel(event) {
18731
- if (this.loaderService.isVisible()) {
18732
- event.preventDefault();
18733
- }
18734
- }
18735
- handleTouchMove(event) {
18736
- if (this.loaderService.isVisible()) {
18737
- event.preventDefault();
18738
- }
18661
+ class JvAccordionPanelDirective {
18662
+ templateRef;
18663
+ id = input.required({ ...(ngDevMode ? { debugName: "id" } : /* istanbul ignore next */ {}), alias: 'jvAccordionPanel' });
18664
+ constructor(templateRef) {
18665
+ this.templateRef = templateRef;
18739
18666
  }
18740
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18741
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvLoaderComponent, isStandalone: true, selector: "jv-loader", host: { listeners: { "document:wheel": "handleWheel($event)", "document:touchmove": "handleTouchMove($event)" } }, ngImport: i0, template: `
18742
- @if (loaderService.isVisible()) {
18743
- <div class="jv-loader-backdrop" aria-live="polite" aria-atomic="true">
18744
- <div class="jv-loader-panel" role="status">
18745
- <span class="spinner" aria-hidden="true"></span>
18746
- <span>{{ loaderService.liveMessage() }}</span>
18747
- </div>
18748
- </div>
18749
- }
18750
- `, isInline: true, styles: [".jv-loader-backdrop{position:fixed;inset:0;z-index:1050;display:grid;place-items:center;background:#0f172a2e;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.jv-loader-panel{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm);padding:var(--jv-spacing-md) var(--jv-spacing-lg);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-lg);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg);color:var(--jv-color-foreground)}.spinner{width:1rem;height:1rem;border:2px solid currentColor;border-right-color:transparent;border-radius:999px;animation:jv-loader-spin .8s linear infinite}@keyframes jv-loader-spin{to{transform:rotate(360deg)}}\n"] });
18667
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvAccordionPanelDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
18668
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.17", type: JvAccordionPanelDirective, isStandalone: true, selector: "ng-template[jvAccordionPanel]", inputs: { id: { classPropertyName: "id", publicName: "jvAccordionPanel", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
18751
18669
  }
18752
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderComponent, decorators: [{
18753
- type: Component,
18754
- args: [{ selector: 'jv-loader', standalone: true, template: `
18755
- @if (loaderService.isVisible()) {
18756
- <div class="jv-loader-backdrop" aria-live="polite" aria-atomic="true">
18757
- <div class="jv-loader-panel" role="status">
18758
- <span class="spinner" aria-hidden="true"></span>
18759
- <span>{{ loaderService.liveMessage() }}</span>
18760
- </div>
18761
- </div>
18762
- }
18763
- `, styles: [".jv-loader-backdrop{position:fixed;inset:0;z-index:1050;display:grid;place-items:center;background:#0f172a2e;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.jv-loader-panel{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm);padding:var(--jv-spacing-md) var(--jv-spacing-lg);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-lg);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg);color:var(--jv-color-foreground)}.spinner{width:1rem;height:1rem;border:2px solid currentColor;border-right-color:transparent;border-radius:999px;animation:jv-loader-spin .8s linear infinite}@keyframes jv-loader-spin{to{transform:rotate(360deg)}}\n"] }]
18764
- }], ctorParameters: () => [], propDecorators: { handleWheel: [{
18765
- type: HostListener,
18766
- args: ['document:wheel', ['$event']]
18767
- }], handleTouchMove: [{
18768
- type: HostListener,
18769
- args: ['document:touchmove', ['$event']]
18770
- }] } });
18670
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvAccordionPanelDirective, decorators: [{
18671
+ type: Directive,
18672
+ args: [{
18673
+ selector: 'ng-template[jvAccordionPanel]',
18674
+ standalone: true,
18675
+ }]
18676
+ }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvAccordionPanel", required: true }] }] } });
18771
18677
 
18772
18678
  class JvAnnouncementService {
18773
18679
  document = inject(DOCUMENT);
@@ -18885,6 +18791,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
18885
18791
  args: [{ providedIn: 'root' }]
18886
18792
  }] });
18887
18793
 
18794
+ const JV_UI_CONFIG = new InjectionToken('JV_UI_CONFIG');
18795
+ const JV_UI_CONFIG_DEFAULTS = new InjectionToken('JV_UI_CONFIG_DEFAULTS', {
18796
+ providedIn: 'root',
18797
+ factory: () => JV_UI_DEFAULT_CONFIG,
18798
+ });
18799
+ function provideJvUi(config = {}) {
18800
+ const configProvider = {
18801
+ provide: JV_UI_CONFIG,
18802
+ useValue: config,
18803
+ multi: true,
18804
+ };
18805
+ return makeEnvironmentProviders([
18806
+ configProvider,
18807
+ {
18808
+ provide: ENVIRONMENT_INITIALIZER,
18809
+ multi: true,
18810
+ useValue: () => {
18811
+ inject(JvThemeService).initialize();
18812
+ inject(JvTranslationService).initialize();
18813
+ },
18814
+ },
18815
+ ]);
18816
+ }
18817
+
18818
+ class JvUiConfigService {
18819
+ defaults = inject(JV_UI_CONFIG_DEFAULTS);
18820
+ configLayers = inject(JV_UI_CONFIG, { optional: true }) ?? [];
18821
+ get snapshot() {
18822
+ return this.configLayers.reduce((merged, layer) => ({ ...merged, ...layer }), { ...this.defaults });
18823
+ }
18824
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvUiConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18825
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvUiConfigService, providedIn: 'root' });
18826
+ }
18827
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvUiConfigService, decorators: [{
18828
+ type: Injectable,
18829
+ args: [{ providedIn: 'root' }]
18830
+ }] });
18831
+
18888
18832
  const ES = {
18889
18833
  'list.emptyMessage': 'No hay elementos',
18890
18834
  'grid.emptyMessage': 'No hay registros',
@@ -18919,6 +18863,23 @@ const ES = {
18919
18863
  'pagination.nextPage': 'Página siguiente',
18920
18864
  'pagination.lastPage': 'Última página',
18921
18865
  'pagination.itemsPerPage': 'Registros por página',
18866
+ 'datePicker.placeholder': 'Seleccione una fecha',
18867
+ 'datePicker.dialogTitle': 'Seleccionar fecha',
18868
+ 'datePicker.clear': 'Limpiar fecha',
18869
+ 'datePicker.close': 'Cerrar',
18870
+ 'datePicker.prevMonth': 'Mes anterior',
18871
+ 'datePicker.nextMonth': 'Mes siguiente',
18872
+ 'datePicker.selectionCleared': 'Seleccion de fecha limpiada',
18873
+ 'modalSelector.placeholder': 'Seleccione una opcion',
18874
+ 'modalSelector.searchPlaceholder': 'Buscar',
18875
+ 'modalSelector.emptyMessage': 'No se encontraron resultados',
18876
+ 'modalSelector.clear': 'Limpiar seleccion',
18877
+ 'modalSelector.close': 'Cerrar',
18878
+ 'modalSelector.results': '{count} resultados',
18879
+ 'modalSelector.selected': '{label} seleccionado',
18880
+ 'modalSelector.selectionCleared': 'Seleccion limpiada',
18881
+ 'common.expanded': 'Expandido',
18882
+ 'common.collapsed': 'Colapsado',
18922
18883
  };
18923
18884
 
18924
18885
  const EN = {
@@ -18955,6 +18916,23 @@ const EN = {
18955
18916
  'pagination.nextPage': 'Next page',
18956
18917
  'pagination.lastPage': 'Last page',
18957
18918
  'pagination.itemsPerPage': 'Items per page',
18919
+ 'datePicker.placeholder': 'Select a date',
18920
+ 'datePicker.dialogTitle': 'Select date',
18921
+ 'datePicker.clear': 'Clear date',
18922
+ 'datePicker.close': 'Close',
18923
+ 'datePicker.prevMonth': 'Previous month',
18924
+ 'datePicker.nextMonth': 'Next month',
18925
+ 'datePicker.selectionCleared': 'Date selection cleared',
18926
+ 'modalSelector.placeholder': 'Select an option',
18927
+ 'modalSelector.searchPlaceholder': 'Search',
18928
+ 'modalSelector.emptyMessage': 'No results found',
18929
+ 'modalSelector.clear': 'Clear selection',
18930
+ 'modalSelector.close': 'Close',
18931
+ 'modalSelector.results': '{count} results',
18932
+ 'modalSelector.selected': '{label} selected',
18933
+ 'modalSelector.selectionCleared': 'Selection cleared',
18934
+ 'common.expanded': 'Expanded',
18935
+ 'common.collapsed': 'Collapsed',
18958
18936
  };
18959
18937
 
18960
18938
  const JV_LOCALE_DICTIONARIES = {
@@ -18999,94 +18977,413 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
18999
18977
  args: [{ providedIn: 'root' }]
19000
18978
  }] });
19001
18979
 
19002
- const JV_UI_CONFIG = new InjectionToken('JV_UI_CONFIG');
19003
- const JV_UI_CONFIG_DEFAULTS = new InjectionToken('JV_UI_CONFIG_DEFAULTS', {
19004
- providedIn: 'root',
19005
- factory: () => JV_UI_DEFAULT_CONFIG,
19006
- });
19007
- function provideJvUi(config = {}) {
19008
- const configProvider = {
19009
- provide: JV_UI_CONFIG,
19010
- useValue: config,
19011
- multi: true,
19012
- };
19013
- return makeEnvironmentProviders([
19014
- configProvider,
19015
- {
19016
- provide: ENVIRONMENT_INITIALIZER,
19017
- multi: true,
19018
- useValue: () => {
19019
- inject(JvThemeService).initialize();
19020
- inject(JvTranslationService).initialize();
19021
- },
19022
- },
19023
- ]);
19024
- }
19025
-
19026
- class JvUiConfigService {
19027
- defaults = inject(JV_UI_CONFIG_DEFAULTS);
19028
- configLayers = inject(JV_UI_CONFIG, { optional: true }) ?? [];
19029
- get snapshot() {
19030
- return this.configLayers.reduce((merged, layer) => ({ ...merged, ...layer }), { ...this.defaults });
19031
- }
19032
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvUiConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
19033
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvUiConfigService, providedIn: 'root' });
19034
- }
19035
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvUiConfigService, decorators: [{
19036
- type: Injectable,
19037
- args: [{ providedIn: 'root' }]
19038
- }] });
19039
-
19040
- class JvToastService {
18980
+ let collapsiblePanelSequence = 0;
18981
+ class JvCollapsiblePanelComponent {
19041
18982
  announcementService = inject(JvAnnouncementService);
19042
- config = inject(JvUiConfigService);
19043
- items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
19044
- positionOverride = signal(null, ...(ngDevMode ? [{ debugName: "positionOverride" }] : /* istanbul ignore next */ []));
19045
- nextId = 0;
19046
- toasts = computed(() => this.items(), ...(ngDevMode ? [{ debugName: "toasts" }] : /* istanbul ignore next */ []));
19047
- position = computed(() => this.positionOverride() ?? this.config.snapshot.toastPosition, ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
19048
- show(message, tone = 'info', options = {}) {
19049
- const id = ++this.nextId;
19050
- const toast = {
19051
- id,
19052
- title: options.title,
19053
- message,
19054
- tone,
19055
- duration: options.duration ?? 4000,
19056
- };
19057
- this.items.update((current) => [...current, toast]);
19058
- this.announcementService.announce(toast.title ? `${toast.title}. ${toast.message}` : toast.message, toast.tone === 'danger' ? 'assertive' : 'polite');
19059
- if (toast.duration > 0) {
19060
- setTimeout(() => this.dismiss(id), toast.duration);
18983
+ translation = inject(JvTranslationService);
18984
+ internalOpen = signal(false, ...(ngDevMode ? [{ debugName: "internalOpen" }] : /* istanbul ignore next */ []));
18985
+ bodyVisible = signal(false, ...(ngDevMode ? [{ debugName: "bodyVisible" }] : /* istanbul ignore next */ []));
18986
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
18987
+ subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
18988
+ icon = input(null, ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
18989
+ open = input(undefined, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
18990
+ defaultOpen = input(false, ...(ngDevMode ? [{ debugName: "defaultOpen" }] : /* istanbul ignore next */ []));
18991
+ keepMounted = input(true, ...(ngDevMode ? [{ debugName: "keepMounted" }] : /* istanbul ignore next */ []));
18992
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
18993
+ showIndicator = input(true, ...(ngDevMode ? [{ debugName: "showIndicator" }] : /* istanbul ignore next */ []));
18994
+ variant = input('bordered', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
18995
+ openChange = output();
18996
+ triggerId = `jv-collapsible-trigger-${++collapsiblePanelSequence}`;
18997
+ contentId = `jv-collapsible-content-${collapsiblePanelSequence}`;
18998
+ resolvedOpen = computed(() => this.open() ?? this.internalOpen(), ...(ngDevMode ? [{ debugName: "resolvedOpen" }] : /* istanbul ignore next */ []));
18999
+ panelClass = computed(() => `jv-collapsible-panel--${this.variant()}`, ...(ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []));
19000
+ shouldRenderBody = computed(() => this.keepMounted() || this.bodyVisible(), ...(ngDevMode ? [{ debugName: "shouldRenderBody" }] : /* istanbul ignore next */ []));
19001
+ triggerAriaLabel = computed(() => {
19002
+ const parts = [this.title()];
19003
+ if (this.subtitle()) {
19004
+ parts.push(this.subtitle());
19061
19005
  }
19062
- return id;
19006
+ parts.push(this.translation.translate(this.resolvedOpen() ? 'common.expanded' : 'common.collapsed'));
19007
+ return parts.join('. ');
19008
+ }, ...(ngDevMode ? [{ debugName: "triggerAriaLabel" }] : /* istanbul ignore next */ []));
19009
+ constructor() {
19010
+ this.internalOpen.set(this.defaultOpen());
19011
+ this.bodyVisible.set(this.defaultOpen());
19012
+ effect(() => {
19013
+ const open = this.resolvedOpen();
19014
+ const keepMounted = this.keepMounted();
19015
+ if (keepMounted || open) {
19016
+ this.bodyVisible.set(true);
19017
+ }
19018
+ });
19063
19019
  }
19064
- dismiss(id) {
19065
- this.items.update((current) => current.filter((item) => item.id !== id));
19020
+ toggle() {
19021
+ if (this.disabled()) {
19022
+ return;
19023
+ }
19024
+ const nextOpen = !this.resolvedOpen();
19025
+ if (this.open() === undefined) {
19026
+ this.internalOpen.set(nextOpen);
19027
+ }
19028
+ if (nextOpen) {
19029
+ this.bodyVisible.set(true);
19030
+ }
19031
+ this.announcementService.announce(`${this.title()}. ${this.translation.translate(nextOpen ? 'common.expanded' : 'common.collapsed')}`, 'polite');
19032
+ this.openChange.emit(nextOpen);
19066
19033
  }
19067
- setPosition(position) {
19068
- this.positionOverride.set(position);
19034
+ handleBodyTransitionEnd(event) {
19035
+ if (event.propertyName !== 'grid-template-rows' || this.keepMounted() || this.resolvedOpen()) {
19036
+ return;
19037
+ }
19038
+ this.bodyVisible.set(false);
19069
19039
  }
19070
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
19071
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastService, providedIn: 'root' });
19040
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvCollapsiblePanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19041
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvCollapsiblePanelComponent, isStandalone: true, selector: "jv-collapsible-panel", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, defaultOpen: { classPropertyName: "defaultOpen", publicName: "defaultOpen", isSignal: true, isRequired: false, transformFunction: null }, keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showIndicator: { classPropertyName: "showIndicator", publicName: "showIndicator", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openChange: "openChange" }, ngImport: i0, template: `
19042
+ <section class="jv-collapsible-panel" [class]="panelClass()">
19043
+ <button
19044
+ type="button"
19045
+ class="jv-collapsible-trigger"
19046
+ [id]="triggerId"
19047
+ [disabled]="disabled()"
19048
+ [attr.aria-expanded]="resolvedOpen() ? 'true' : 'false'"
19049
+ [attr.aria-controls]="contentId"
19050
+ [attr.aria-label]="triggerAriaLabel()"
19051
+ (click)="toggle()"
19052
+ >
19053
+ <span class="jv-collapsible-main">
19054
+ @if (icon()) {
19055
+ <jv-icon [name]="icon()!" [size]="18" />
19056
+ }
19057
+
19058
+ <span class="jv-collapsible-copy">
19059
+ <span class="jv-collapsible-title">{{ title() }}</span>
19060
+ @if (subtitle()) {
19061
+ <span class="jv-collapsible-subtitle">{{ subtitle() }}</span>
19062
+ }
19063
+ </span>
19064
+ </span>
19065
+
19066
+ @if (showIndicator()) {
19067
+ <jv-icon class="jv-collapsible-indicator" [class.jv-collapsible-indicator--open]="resolvedOpen()" name="chevron-down" [size]="18" />
19068
+ }
19069
+ </button>
19070
+
19071
+ @if (shouldRenderBody()) {
19072
+ <div
19073
+ class="jv-collapsible-body"
19074
+ [class.jv-collapsible-body--open]="resolvedOpen()"
19075
+ [id]="contentId"
19076
+ role="region"
19077
+ [attr.aria-labelledby]="triggerId"
19078
+ [attr.aria-hidden]="resolvedOpen() ? null : 'true'"
19079
+ [attr.inert]="resolvedOpen() ? null : ''"
19080
+ (transitionend)="handleBodyTransitionEnd($event)"
19081
+ >
19082
+ <div class="jv-collapsible-content">
19083
+ <ng-content />
19084
+ </div>
19085
+ </div>
19086
+ }
19087
+ </section>
19088
+ `, isInline: true, styles: [".jv-collapsible-panel{border-radius:var(--jv-radius-lg);background:var(--jv-color-surface)}.jv-collapsible-panel--bordered{border:1px solid var(--jv-color-border)}.jv-collapsible-panel--filled{border:1px solid transparent;background:var(--jv-color-surface-muted)}.jv-collapsible-panel--minimal{border-bottom:1px solid var(--jv-color-border);border-radius:0}.jv-collapsible-trigger{width:100%;display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-md) var(--jv-spacing-lg);border:0;border-radius:inherit;background:transparent;color:var(--jv-color-foreground);text-align:left;font:inherit;cursor:pointer}.jv-collapsible-trigger:disabled{opacity:.6;cursor:not-allowed}.jv-collapsible-trigger:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:-2px}.jv-collapsible-main{min-width:0;display:inline-flex;align-items:flex-start;gap:var(--jv-spacing-sm)}.jv-collapsible-copy{min-width:0;display:grid;gap:.25rem}.jv-collapsible-title{font-weight:600}.jv-collapsible-subtitle{color:var(--jv-color-foreground-muted);font-size:.875rem}.jv-collapsible-indicator{flex-shrink:0;transition:transform .18s ease}.jv-collapsible-indicator--open{transform:rotate(180deg)}.jv-collapsible-body{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .18s ease,opacity .18s ease}.jv-collapsible-body--open{grid-template-rows:1fr;opacity:1}.jv-collapsible-content{min-height:0;overflow:hidden;padding:0 var(--jv-spacing-lg) var(--jv-spacing-lg)}\n"], dependencies: [{ kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
19072
19089
  }
19073
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastService, decorators: [{
19074
- type: Injectable,
19075
- args: [{ providedIn: 'root' }]
19076
- }] });
19090
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvCollapsiblePanelComponent, decorators: [{
19091
+ type: Component,
19092
+ args: [{ selector: 'jv-collapsible-panel', standalone: true, imports: [JvIconComponent], template: `
19093
+ <section class="jv-collapsible-panel" [class]="panelClass()">
19094
+ <button
19095
+ type="button"
19096
+ class="jv-collapsible-trigger"
19097
+ [id]="triggerId"
19098
+ [disabled]="disabled()"
19099
+ [attr.aria-expanded]="resolvedOpen() ? 'true' : 'false'"
19100
+ [attr.aria-controls]="contentId"
19101
+ [attr.aria-label]="triggerAriaLabel()"
19102
+ (click)="toggle()"
19103
+ >
19104
+ <span class="jv-collapsible-main">
19105
+ @if (icon()) {
19106
+ <jv-icon [name]="icon()!" [size]="18" />
19107
+ }
19077
19108
 
19078
- class JvToastComponent {
19079
- toastService = inject(JvToastService);
19080
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19081
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvToastComponent, isStandalone: true, selector: "jv-toast", ngImport: i0, template: `
19082
- @if (toastService.toasts().length > 0) {
19083
- <div class="jv-toast-viewport" [class]="'is-' + toastService.position()" aria-live="polite" aria-atomic="false">
19084
- @for (toast of toastService.toasts(); track toast.id) {
19085
- <article class="jv-toast-item" [class]="'jv-toast--' + toast.tone" [attr.role]="toast.tone === 'danger' ? 'alert' : 'status'" aria-atomic="true">
19086
- <div class="jv-toast-copy">
19087
- @if (toast.title) {
19088
- <strong>{{ toast.title }}</strong>
19089
- }
19109
+ <span class="jv-collapsible-copy">
19110
+ <span class="jv-collapsible-title">{{ title() }}</span>
19111
+ @if (subtitle()) {
19112
+ <span class="jv-collapsible-subtitle">{{ subtitle() }}</span>
19113
+ }
19114
+ </span>
19115
+ </span>
19116
+
19117
+ @if (showIndicator()) {
19118
+ <jv-icon class="jv-collapsible-indicator" [class.jv-collapsible-indicator--open]="resolvedOpen()" name="chevron-down" [size]="18" />
19119
+ }
19120
+ </button>
19121
+
19122
+ @if (shouldRenderBody()) {
19123
+ <div
19124
+ class="jv-collapsible-body"
19125
+ [class.jv-collapsible-body--open]="resolvedOpen()"
19126
+ [id]="contentId"
19127
+ role="region"
19128
+ [attr.aria-labelledby]="triggerId"
19129
+ [attr.aria-hidden]="resolvedOpen() ? null : 'true'"
19130
+ [attr.inert]="resolvedOpen() ? null : ''"
19131
+ (transitionend)="handleBodyTransitionEnd($event)"
19132
+ >
19133
+ <div class="jv-collapsible-content">
19134
+ <ng-content />
19135
+ </div>
19136
+ </div>
19137
+ }
19138
+ </section>
19139
+ `, styles: [".jv-collapsible-panel{border-radius:var(--jv-radius-lg);background:var(--jv-color-surface)}.jv-collapsible-panel--bordered{border:1px solid var(--jv-color-border)}.jv-collapsible-panel--filled{border:1px solid transparent;background:var(--jv-color-surface-muted)}.jv-collapsible-panel--minimal{border-bottom:1px solid var(--jv-color-border);border-radius:0}.jv-collapsible-trigger{width:100%;display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-md) var(--jv-spacing-lg);border:0;border-radius:inherit;background:transparent;color:var(--jv-color-foreground);text-align:left;font:inherit;cursor:pointer}.jv-collapsible-trigger:disabled{opacity:.6;cursor:not-allowed}.jv-collapsible-trigger:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:-2px}.jv-collapsible-main{min-width:0;display:inline-flex;align-items:flex-start;gap:var(--jv-spacing-sm)}.jv-collapsible-copy{min-width:0;display:grid;gap:.25rem}.jv-collapsible-title{font-weight:600}.jv-collapsible-subtitle{color:var(--jv-color-foreground-muted);font-size:.875rem}.jv-collapsible-indicator{flex-shrink:0;transition:transform .18s ease}.jv-collapsible-indicator--open{transform:rotate(180deg)}.jv-collapsible-body{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .18s ease,opacity .18s ease}.jv-collapsible-body--open{grid-template-rows:1fr;opacity:1}.jv-collapsible-content{min-height:0;overflow:hidden;padding:0 var(--jv-spacing-lg) var(--jv-spacing-lg)}\n"] }]
19140
+ }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], defaultOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultOpen", required: false }] }], keepMounted: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepMounted", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIndicator", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], openChange: [{ type: i0.Output, args: ["openChange"] }] } });
19141
+
19142
+ class JvAccordionComponent {
19143
+ panelTemplates = contentChildren(JvAccordionPanelDirective, ...(ngDevMode ? [{ debugName: "panelTemplates" }] : /* istanbul ignore next */ []));
19144
+ internalOpenItemIds = signal([], ...(ngDevMode ? [{ debugName: "internalOpenItemIds" }] : /* istanbul ignore next */ []));
19145
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
19146
+ openItemIds = input(undefined, ...(ngDevMode ? [{ debugName: "openItemIds" }] : /* istanbul ignore next */ []));
19147
+ defaultOpenItemIds = input([], ...(ngDevMode ? [{ debugName: "defaultOpenItemIds" }] : /* istanbul ignore next */ []));
19148
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
19149
+ keepMounted = input(true, ...(ngDevMode ? [{ debugName: "keepMounted" }] : /* istanbul ignore next */ []));
19150
+ showIndicator = input(true, ...(ngDevMode ? [{ debugName: "showIndicator" }] : /* istanbul ignore next */ []));
19151
+ variant = input('bordered', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
19152
+ openItemIdsChange = output();
19153
+ resolvedOpenItemIds = computed(() => {
19154
+ const allItemIds = new Set(this.items().map((item) => item.id));
19155
+ const candidateIds = this.openItemIds() ?? this.internalOpenItemIds();
19156
+ const filteredIds = candidateIds.filter((id) => allItemIds.has(id));
19157
+ return this.multiple() ? filteredIds : filteredIds.slice(0, 1);
19158
+ }, ...(ngDevMode ? [{ debugName: "resolvedOpenItemIds" }] : /* istanbul ignore next */ []));
19159
+ constructor() {
19160
+ this.internalOpenItemIds.set(this.defaultOpenItemIds());
19161
+ }
19162
+ isItemOpen(itemId) {
19163
+ return this.resolvedOpenItemIds().includes(itemId);
19164
+ }
19165
+ handleItemOpenChange(itemId, nextOpen) {
19166
+ const currentIds = this.resolvedOpenItemIds();
19167
+ let nextIds = currentIds;
19168
+ if (nextOpen) {
19169
+ nextIds = this.multiple() ? [...new Set([...currentIds, itemId])] : [itemId];
19170
+ }
19171
+ else {
19172
+ nextIds = currentIds.filter((id) => id !== itemId);
19173
+ }
19174
+ if (this.openItemIds() === undefined) {
19175
+ this.internalOpenItemIds.set(nextIds);
19176
+ }
19177
+ this.openItemIdsChange.emit(nextIds);
19178
+ }
19179
+ panelTemplate(itemId) {
19180
+ return this.panelTemplates().find((panel) => panel.id() === itemId)?.templateRef ?? null;
19181
+ }
19182
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvAccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19183
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvAccordionComponent, isStandalone: true, selector: "jv-accordion", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, openItemIds: { classPropertyName: "openItemIds", publicName: "openItemIds", isSignal: true, isRequired: false, transformFunction: null }, defaultOpenItemIds: { classPropertyName: "defaultOpenItemIds", publicName: "defaultOpenItemIds", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null }, showIndicator: { classPropertyName: "showIndicator", publicName: "showIndicator", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openItemIdsChange: "openItemIdsChange" }, queries: [{ propertyName: "panelTemplates", predicate: JvAccordionPanelDirective, isSignal: true }], ngImport: i0, template: `
19184
+ <div class="jv-accordion">
19185
+ @for (item of items(); track item.id) {
19186
+ <jv-collapsible-panel
19187
+ [title]="item.title"
19188
+ [subtitle]="item.subtitle ?? ''"
19189
+ [icon]="item.icon ?? null"
19190
+ [open]="isItemOpen(item.id)"
19191
+ [keepMounted]="keepMounted()"
19192
+ [disabled]="item.disabled ?? false"
19193
+ [showIndicator]="showIndicator()"
19194
+ [variant]="variant()"
19195
+ (openChange)="handleItemOpenChange(item.id, $event)"
19196
+ >
19197
+ <ng-container [ngTemplateOutlet]="panelTemplate(item.id) ?? null" />
19198
+ </jv-collapsible-panel>
19199
+ }
19200
+ </div>
19201
+ `, isInline: true, styles: [".jv-accordion{display:grid;gap:var(--jv-spacing-sm)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: JvCollapsiblePanelComponent, selector: "jv-collapsible-panel", inputs: ["title", "subtitle", "icon", "open", "defaultOpen", "keepMounted", "disabled", "showIndicator", "variant"], outputs: ["openChange"] }] });
19202
+ }
19203
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvAccordionComponent, decorators: [{
19204
+ type: Component,
19205
+ args: [{ selector: 'jv-accordion', standalone: true, imports: [NgTemplateOutlet, JvCollapsiblePanelComponent], template: `
19206
+ <div class="jv-accordion">
19207
+ @for (item of items(); track item.id) {
19208
+ <jv-collapsible-panel
19209
+ [title]="item.title"
19210
+ [subtitle]="item.subtitle ?? ''"
19211
+ [icon]="item.icon ?? null"
19212
+ [open]="isItemOpen(item.id)"
19213
+ [keepMounted]="keepMounted()"
19214
+ [disabled]="item.disabled ?? false"
19215
+ [showIndicator]="showIndicator()"
19216
+ [variant]="variant()"
19217
+ (openChange)="handleItemOpenChange(item.id, $event)"
19218
+ >
19219
+ <ng-container [ngTemplateOutlet]="panelTemplate(item.id) ?? null" />
19220
+ </jv-collapsible-panel>
19221
+ }
19222
+ </div>
19223
+ `, styles: [".jv-accordion{display:grid;gap:var(--jv-spacing-sm)}\n"] }]
19224
+ }], ctorParameters: () => [], propDecorators: { panelTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => JvAccordionPanelDirective), { isSignal: true }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], openItemIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "openItemIds", required: false }] }], defaultOpenItemIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultOpenItemIds", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], keepMounted: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepMounted", required: false }] }], showIndicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIndicator", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], openItemIdsChange: [{ type: i0.Output, args: ["openItemIdsChange"] }] } });
19225
+
19226
+ class JvBadgeComponent {
19227
+ tone = input('primary', ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
19228
+ badgeClass = computed(() => `jv-badge--${this.tone()}`, ...(ngDevMode ? [{ debugName: "badgeClass" }] : /* istanbul ignore next */ []));
19229
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19230
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: JvBadgeComponent, isStandalone: true, selector: "jv-badge", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `<span class="jv-badge" [class]="badgeClass()"><ng-content></ng-content></span>`, isInline: true, styles: [".jv-badge{display:inline-flex;align-items:center;justify-content:center;min-height:1.75rem;padding:0 var(--jv-spacing-sm);border-radius:999px;font-size:.8rem;font-weight:700;line-height:1;white-space:nowrap}.jv-badge--primary{background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface));color:var(--jv-color-primary)}.jv-badge--secondary,.jv-badge--neutral{background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.jv-badge--success{background:color-mix(in srgb,var(--jv-color-success) 16%,var(--jv-color-surface));color:var(--jv-color-success)}.jv-badge--warning{background:color-mix(in srgb,var(--jv-color-warning) 20%,var(--jv-color-surface));color:#8a4b00}.jv-badge--danger{background:color-mix(in srgb,var(--jv-color-danger) 16%,var(--jv-color-surface));color:var(--jv-color-danger)}.jv-badge--info{background:color-mix(in srgb,var(--jv-color-info) 16%,var(--jv-color-surface));color:var(--jv-color-info)}\n"] });
19231
+ }
19232
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvBadgeComponent, decorators: [{
19233
+ type: Component,
19234
+ args: [{ selector: 'jv-badge', standalone: true, template: `<span class="jv-badge" [class]="badgeClass()"><ng-content></ng-content></span>`, styles: [".jv-badge{display:inline-flex;align-items:center;justify-content:center;min-height:1.75rem;padding:0 var(--jv-spacing-sm);border-radius:999px;font-size:.8rem;font-weight:700;line-height:1;white-space:nowrap}.jv-badge--primary{background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface));color:var(--jv-color-primary)}.jv-badge--secondary,.jv-badge--neutral{background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.jv-badge--success{background:color-mix(in srgb,var(--jv-color-success) 16%,var(--jv-color-surface));color:var(--jv-color-success)}.jv-badge--warning{background:color-mix(in srgb,var(--jv-color-warning) 20%,var(--jv-color-surface));color:#8a4b00}.jv-badge--danger{background:color-mix(in srgb,var(--jv-color-danger) 16%,var(--jv-color-surface));color:var(--jv-color-danger)}.jv-badge--info{background:color-mix(in srgb,var(--jv-color-info) 16%,var(--jv-color-surface));color:var(--jv-color-info)}\n"] }]
19235
+ }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
19236
+
19237
+ class JvLoaderService {
19238
+ counter = signal(0, ...(ngDevMode ? [{ debugName: "counter" }] : /* istanbul ignore next */ []));
19239
+ visible = signal(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
19240
+ message = signal('Loading', ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
19241
+ delayMs = 300;
19242
+ showTimeout = null;
19243
+ activeCount = this.counter.asReadonly();
19244
+ isVisible = this.visible.asReadonly();
19245
+ liveMessage = this.message.asReadonly();
19246
+ show(message = 'Loading') {
19247
+ this.message.set(message);
19248
+ this.counter.update((count) => count + 1);
19249
+ if (this.counter() === 1) {
19250
+ this.scheduleVisibility();
19251
+ }
19252
+ }
19253
+ hide() {
19254
+ this.counter.update((count) => Math.max(0, count - 1));
19255
+ if (this.counter() === 0) {
19256
+ this.clearTimer();
19257
+ this.visible.set(false);
19258
+ }
19259
+ }
19260
+ scheduleVisibility() {
19261
+ this.clearTimer();
19262
+ this.showTimeout = setTimeout(() => {
19263
+ if (this.counter() > 0) {
19264
+ this.visible.set(true);
19265
+ }
19266
+ this.showTimeout = null;
19267
+ }, this.delayMs);
19268
+ }
19269
+ clearTimer() {
19270
+ if (this.showTimeout) {
19271
+ clearTimeout(this.showTimeout);
19272
+ this.showTimeout = null;
19273
+ }
19274
+ }
19275
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
19276
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderService, providedIn: 'root' });
19277
+ }
19278
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderService, decorators: [{
19279
+ type: Injectable,
19280
+ args: [{ providedIn: 'root' }]
19281
+ }] });
19282
+
19283
+ class JvLoaderComponent {
19284
+ document = inject(DOCUMENT);
19285
+ loaderService = inject(JvLoaderService);
19286
+ constructor() {
19287
+ effect(() => {
19288
+ const locked = this.loaderService.isVisible();
19289
+ const body = this.document.body;
19290
+ const root = this.document.documentElement;
19291
+ body.style.overflow = locked ? 'hidden' : '';
19292
+ root.style.overflow = locked ? 'hidden' : '';
19293
+ });
19294
+ }
19295
+ handleWheel(event) {
19296
+ if (this.loaderService.isVisible()) {
19297
+ event.preventDefault();
19298
+ }
19299
+ }
19300
+ handleTouchMove(event) {
19301
+ if (this.loaderService.isVisible()) {
19302
+ event.preventDefault();
19303
+ }
19304
+ }
19305
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19306
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvLoaderComponent, isStandalone: true, selector: "jv-loader", host: { listeners: { "document:wheel": "handleWheel($event)", "document:touchmove": "handleTouchMove($event)" } }, ngImport: i0, template: `
19307
+ @if (loaderService.isVisible()) {
19308
+ <div class="jv-loader-backdrop" aria-live="polite" aria-atomic="true">
19309
+ <div class="jv-loader-panel" role="status">
19310
+ <span class="spinner" aria-hidden="true"></span>
19311
+ <span>{{ loaderService.liveMessage() }}</span>
19312
+ </div>
19313
+ </div>
19314
+ }
19315
+ `, isInline: true, styles: [".jv-loader-backdrop{position:fixed;inset:0;z-index:1050;display:grid;place-items:center;background:#0f172a2e;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.jv-loader-panel{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm);padding:var(--jv-spacing-md) var(--jv-spacing-lg);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-lg);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg);color:var(--jv-color-foreground)}.spinner{width:1rem;height:1rem;border:2px solid currentColor;border-right-color:transparent;border-radius:999px;animation:jv-loader-spin .8s linear infinite}@keyframes jv-loader-spin{to{transform:rotate(360deg)}}\n"] });
19316
+ }
19317
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderComponent, decorators: [{
19318
+ type: Component,
19319
+ args: [{ selector: 'jv-loader', standalone: true, template: `
19320
+ @if (loaderService.isVisible()) {
19321
+ <div class="jv-loader-backdrop" aria-live="polite" aria-atomic="true">
19322
+ <div class="jv-loader-panel" role="status">
19323
+ <span class="spinner" aria-hidden="true"></span>
19324
+ <span>{{ loaderService.liveMessage() }}</span>
19325
+ </div>
19326
+ </div>
19327
+ }
19328
+ `, styles: [".jv-loader-backdrop{position:fixed;inset:0;z-index:1050;display:grid;place-items:center;background:#0f172a2e;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.jv-loader-panel{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm);padding:var(--jv-spacing-md) var(--jv-spacing-lg);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-lg);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg);color:var(--jv-color-foreground)}.spinner{width:1rem;height:1rem;border:2px solid currentColor;border-right-color:transparent;border-radius:999px;animation:jv-loader-spin .8s linear infinite}@keyframes jv-loader-spin{to{transform:rotate(360deg)}}\n"] }]
19329
+ }], ctorParameters: () => [], propDecorators: { handleWheel: [{
19330
+ type: HostListener,
19331
+ args: ['document:wheel', ['$event']]
19332
+ }], handleTouchMove: [{
19333
+ type: HostListener,
19334
+ args: ['document:touchmove', ['$event']]
19335
+ }] } });
19336
+
19337
+ class JvToastService {
19338
+ announcementService = inject(JvAnnouncementService);
19339
+ config = inject(JvUiConfigService);
19340
+ items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
19341
+ positionOverride = signal(null, ...(ngDevMode ? [{ debugName: "positionOverride" }] : /* istanbul ignore next */ []));
19342
+ nextId = 0;
19343
+ toasts = computed(() => this.items(), ...(ngDevMode ? [{ debugName: "toasts" }] : /* istanbul ignore next */ []));
19344
+ position = computed(() => this.positionOverride() ?? this.config.snapshot.toastPosition, ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
19345
+ show(message, tone = 'info', options = {}) {
19346
+ const id = ++this.nextId;
19347
+ const toast = {
19348
+ id,
19349
+ title: options.title,
19350
+ message,
19351
+ tone,
19352
+ duration: options.duration ?? 4000,
19353
+ };
19354
+ this.items.update((current) => [...current, toast]);
19355
+ this.announcementService.announce(toast.title ? `${toast.title}. ${toast.message}` : toast.message, toast.tone === 'danger' ? 'assertive' : 'polite');
19356
+ if (toast.duration > 0) {
19357
+ setTimeout(() => this.dismiss(id), toast.duration);
19358
+ }
19359
+ return id;
19360
+ }
19361
+ dismiss(id) {
19362
+ this.items.update((current) => current.filter((item) => item.id !== id));
19363
+ }
19364
+ setPosition(position) {
19365
+ this.positionOverride.set(position);
19366
+ }
19367
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
19368
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastService, providedIn: 'root' });
19369
+ }
19370
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastService, decorators: [{
19371
+ type: Injectable,
19372
+ args: [{ providedIn: 'root' }]
19373
+ }] });
19374
+
19375
+ class JvToastComponent {
19376
+ toastService = inject(JvToastService);
19377
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19378
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvToastComponent, isStandalone: true, selector: "jv-toast", ngImport: i0, template: `
19379
+ @if (toastService.toasts().length > 0) {
19380
+ <div class="jv-toast-viewport" [class]="'is-' + toastService.position()" aria-live="polite" aria-atomic="false">
19381
+ @for (toast of toastService.toasts(); track toast.id) {
19382
+ <article class="jv-toast-item" [class]="'jv-toast--' + toast.tone" [attr.role]="toast.tone === 'danger' ? 'alert' : 'status'" aria-atomic="true">
19383
+ <div class="jv-toast-copy">
19384
+ @if (toast.title) {
19385
+ <strong>{{ toast.title }}</strong>
19386
+ }
19090
19387
  <p>{{ toast.message }}</p>
19091
19388
  </div>
19092
19389
 
@@ -19099,43 +19396,735 @@ class JvToastComponent {
19099
19396
  }
19100
19397
  `, isInline: true, styles: [".jv-toast-viewport{position:fixed;z-index:1100;display:grid;gap:var(--jv-spacing-sm);width:min(360px,calc(100vw - 2rem))}.is-top-left{top:1rem;left:1rem}.is-top-center{top:1rem;left:50%;transform:translate(-50%)}.is-top-right{top:1rem;right:1rem}.is-bottom-left{bottom:1rem;left:1rem}.is-bottom-center{bottom:1rem;left:50%;transform:translate(-50%)}.is-bottom-right{bottom:1rem;right:1rem}.jv-toast-item{display:grid;grid-template-columns:1fr auto;gap:var(--jv-spacing-sm);padding:var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg)}.jv-toast-copy strong,.jv-toast-copy p{margin:0}.jv-toast-copy{display:grid;gap:.25rem}.jv-toast-copy p{color:var(--jv-color-foreground-muted)}.jv-toast-close{display:inline-grid;place-items:center;width:2rem;height:2rem;border:0;border-radius:999px;background:transparent;color:inherit}.jv-toast--success{border-color:color-mix(in srgb,var(--jv-color-success) 35%,var(--jv-color-border))}.jv-toast--warning{border-color:color-mix(in srgb,var(--jv-color-warning) 35%,var(--jv-color-border))}.jv-toast--danger{border-color:color-mix(in srgb,var(--jv-color-danger) 35%,var(--jv-color-border))}.jv-toast--info,.jv-toast--primary{border-color:color-mix(in srgb,var(--jv-color-primary) 35%,var(--jv-color-border))}\n"], dependencies: [{ kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
19101
19398
  }
19102
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastComponent, decorators: [{
19399
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastComponent, decorators: [{
19400
+ type: Component,
19401
+ args: [{ selector: 'jv-toast', standalone: true, imports: [JvIconComponent], template: `
19402
+ @if (toastService.toasts().length > 0) {
19403
+ <div class="jv-toast-viewport" [class]="'is-' + toastService.position()" aria-live="polite" aria-atomic="false">
19404
+ @for (toast of toastService.toasts(); track toast.id) {
19405
+ <article class="jv-toast-item" [class]="'jv-toast--' + toast.tone" [attr.role]="toast.tone === 'danger' ? 'alert' : 'status'" aria-atomic="true">
19406
+ <div class="jv-toast-copy">
19407
+ @if (toast.title) {
19408
+ <strong>{{ toast.title }}</strong>
19409
+ }
19410
+ <p>{{ toast.message }}</p>
19411
+ </div>
19412
+
19413
+ <button type="button" class="jv-toast-close" (click)="toastService.dismiss(toast.id)" aria-label="Dismiss notification">
19414
+ <jv-icon name="x" [size]="16" />
19415
+ </button>
19416
+ </article>
19417
+ }
19418
+ </div>
19419
+ }
19420
+ `, styles: [".jv-toast-viewport{position:fixed;z-index:1100;display:grid;gap:var(--jv-spacing-sm);width:min(360px,calc(100vw - 2rem))}.is-top-left{top:1rem;left:1rem}.is-top-center{top:1rem;left:50%;transform:translate(-50%)}.is-top-right{top:1rem;right:1rem}.is-bottom-left{bottom:1rem;left:1rem}.is-bottom-center{bottom:1rem;left:50%;transform:translate(-50%)}.is-bottom-right{bottom:1rem;right:1rem}.jv-toast-item{display:grid;grid-template-columns:1fr auto;gap:var(--jv-spacing-sm);padding:var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg)}.jv-toast-copy strong,.jv-toast-copy p{margin:0}.jv-toast-copy{display:grid;gap:.25rem}.jv-toast-copy p{color:var(--jv-color-foreground-muted)}.jv-toast-close{display:inline-grid;place-items:center;width:2rem;height:2rem;border:0;border-radius:999px;background:transparent;color:inherit}.jv-toast--success{border-color:color-mix(in srgb,var(--jv-color-success) 35%,var(--jv-color-border))}.jv-toast--warning{border-color:color-mix(in srgb,var(--jv-color-warning) 35%,var(--jv-color-border))}.jv-toast--danger{border-color:color-mix(in srgb,var(--jv-color-danger) 35%,var(--jv-color-border))}.jv-toast--info,.jv-toast--primary{border-color:color-mix(in srgb,var(--jv-color-primary) 35%,var(--jv-color-border))}\n"] }]
19421
+ }] });
19422
+
19423
+ class JvDialogComponent {
19424
+ document = inject(DOCUMENT);
19425
+ panelRef = viewChild('panel', ...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
19426
+ open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
19427
+ title = input('Dialog', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
19428
+ closed = output();
19429
+ titleId = `jv-dialog-title-${Math.random().toString(36).slice(2, 8)}`;
19430
+ descriptionId = `jv-dialog-description-${Math.random().toString(36).slice(2, 8)}`;
19431
+ constructor() {
19432
+ effect(() => {
19433
+ if (this.open()) {
19434
+ queueMicrotask(() => this.focusInitialElement());
19435
+ }
19436
+ });
19437
+ }
19438
+ handleDocumentKeydown(event) {
19439
+ if (!this.open()) {
19440
+ return;
19441
+ }
19442
+ if (event.key === 'Escape') {
19443
+ event.preventDefault();
19444
+ this.closed.emit();
19445
+ return;
19446
+ }
19447
+ if (event.key === 'Tab') {
19448
+ this.trapFocus(event);
19449
+ }
19450
+ }
19451
+ focusInitialElement() {
19452
+ const panel = this.panelRef()?.nativeElement;
19453
+ if (!panel) {
19454
+ return;
19455
+ }
19456
+ const firstFocusable = this.getFocusableElements()[0];
19457
+ (firstFocusable ?? panel).focus();
19458
+ }
19459
+ backdropClose() {
19460
+ this.closed.emit();
19461
+ }
19462
+ trapFocus(event) {
19463
+ const focusable = this.getFocusableElements();
19464
+ const panel = this.panelRef()?.nativeElement;
19465
+ if (!panel || focusable.length === 0) {
19466
+ panel?.focus();
19467
+ event.preventDefault();
19468
+ return;
19469
+ }
19470
+ const first = focusable[0];
19471
+ const last = focusable[focusable.length - 1];
19472
+ const active = this.document.activeElement;
19473
+ if (event.shiftKey && active === first) {
19474
+ last.focus();
19475
+ event.preventDefault();
19476
+ }
19477
+ else if (!event.shiftKey && active === last) {
19478
+ first.focus();
19479
+ event.preventDefault();
19480
+ }
19481
+ }
19482
+ getFocusableElements() {
19483
+ const panel = this.panelRef()?.nativeElement;
19484
+ if (!panel) {
19485
+ return [];
19486
+ }
19487
+ return Array.from(panel.querySelectorAll('button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'));
19488
+ }
19489
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19490
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvDialogComponent, isStandalone: true, selector: "jv-dialog", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, host: { listeners: { "document:keydown": "handleDocumentKeydown($event)" } }, viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: `
19491
+ @if (open()) {
19492
+ <div class="jv-dialog-backdrop" (click)="backdropClose()">
19493
+ <div class="jv-dialog-panel" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="descriptionId" (click)="$event.stopPropagation()" #panel>
19494
+ <header class="jv-dialog-header">
19495
+ <h2 [id]="titleId">{{ title() }}</h2>
19496
+ </header>
19497
+
19498
+ <div class="jv-dialog-body" [id]="descriptionId">
19499
+ <ng-content></ng-content>
19500
+ </div>
19501
+
19502
+ <footer class="jv-dialog-footer">
19503
+ <ng-content select="[dialog-actions]"></ng-content>
19504
+ </footer>
19505
+ </div>
19506
+ </div>
19507
+ }
19508
+ `, isInline: true, styles: [".jv-dialog-backdrop{position:fixed;inset:0;z-index:1200;display:grid;place-items:center;padding:1rem;background:#0f172a80}.jv-dialog-panel{width:min(32rem,100%);max-height:calc(100vh - 2rem);overflow:auto;padding:var(--jv-spacing-lg);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-lg);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg)}.jv-dialog-header,.jv-dialog-body,.jv-dialog-footer{display:grid;gap:var(--jv-spacing-sm)}.jv-dialog-footer{margin-top:var(--jv-spacing-lg)}h2,p{margin:0}\n"] });
19509
+ }
19510
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDialogComponent, decorators: [{
19511
+ type: Component,
19512
+ args: [{ selector: 'jv-dialog', standalone: true, template: `
19513
+ @if (open()) {
19514
+ <div class="jv-dialog-backdrop" (click)="backdropClose()">
19515
+ <div class="jv-dialog-panel" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="descriptionId" (click)="$event.stopPropagation()" #panel>
19516
+ <header class="jv-dialog-header">
19517
+ <h2 [id]="titleId">{{ title() }}</h2>
19518
+ </header>
19519
+
19520
+ <div class="jv-dialog-body" [id]="descriptionId">
19521
+ <ng-content></ng-content>
19522
+ </div>
19523
+
19524
+ <footer class="jv-dialog-footer">
19525
+ <ng-content select="[dialog-actions]"></ng-content>
19526
+ </footer>
19527
+ </div>
19528
+ </div>
19529
+ }
19530
+ `, styles: [".jv-dialog-backdrop{position:fixed;inset:0;z-index:1200;display:grid;place-items:center;padding:1rem;background:#0f172a80}.jv-dialog-panel{width:min(32rem,100%);max-height:calc(100vh - 2rem);overflow:auto;padding:var(--jv-spacing-lg);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-lg);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg)}.jv-dialog-header,.jv-dialog-body,.jv-dialog-footer{display:grid;gap:var(--jv-spacing-sm)}.jv-dialog-footer{margin-top:var(--jv-spacing-lg)}h2,p{margin:0}\n"] }]
19531
+ }], ctorParameters: () => [], propDecorators: { panelRef: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], handleDocumentKeydown: [{
19532
+ type: HostListener,
19533
+ args: ['document:keydown', ['$event']]
19534
+ }] } });
19535
+
19536
+ let selectIdSequence = 0;
19537
+ class JvSelectComponent {
19538
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
19539
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
19540
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
19541
+ invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
19542
+ describedBy = input('', ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
19543
+ inputId = input(`jv-select-${++selectIdSequence}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
19544
+ modelValue = input('', ...(ngDevMode ? [{ debugName: "modelValue" }] : /* istanbul ignore next */ []));
19545
+ selectionChange = output();
19546
+ value = '';
19547
+ disabled = false;
19548
+ onChange = () => undefined;
19549
+ onTouched = () => undefined;
19550
+ currentValue = computed(() => this.modelValue() || this.value, ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
19551
+ writeValue(value) {
19552
+ this.value = value ?? '';
19553
+ }
19554
+ registerOnChange(fn) {
19555
+ this.onChange = fn;
19556
+ }
19557
+ registerOnTouched(fn) {
19558
+ this.onTouched = fn;
19559
+ }
19560
+ setDisabledState(isDisabled) {
19561
+ this.disabled = isDisabled;
19562
+ }
19563
+ handleChange(event) {
19564
+ const nextValue = event.target.value;
19565
+ this.value = nextValue;
19566
+ this.onChange(nextValue);
19567
+ this.selectionChange.emit(nextValue);
19568
+ }
19569
+ handleBlur() {
19570
+ this.onTouched();
19571
+ }
19572
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19573
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvSelectComponent, isStandalone: true, selector: "jv-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, describedBy: { classPropertyName: "describedBy", publicName: "describedBy", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, modelValue: { classPropertyName: "modelValue", publicName: "modelValue", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, providers: [
19574
+ {
19575
+ provide: NG_VALUE_ACCESSOR,
19576
+ useExisting: forwardRef(() => JvSelectComponent),
19577
+ multi: true,
19578
+ },
19579
+ ], ngImport: i0, template: `
19580
+ <select
19581
+ class="jv-select"
19582
+ [class.is-invalid]="invalid()"
19583
+ [id]="inputId()"
19584
+ [disabled]="disabled"
19585
+ [required]="required()"
19586
+ [attr.aria-invalid]="invalid() ? 'true' : null"
19587
+ [attr.aria-describedby]="describedBy() || null"
19588
+ [value]="currentValue()"
19589
+ (change)="handleChange($event)"
19590
+ (blur)="handleBlur()"
19591
+ >
19592
+ @if (placeholder()) {
19593
+ <option value="" [disabled]="required()">{{ placeholder() }}</option>
19594
+ }
19595
+
19596
+ @for (option of options(); track option.label + ':' + option.value) {
19597
+ <option [value]="option.value" [disabled]="option.disabled ?? false">{{ option.label }}</option>
19598
+ }
19599
+ </select>
19600
+ `, isInline: true, styles: [".jv-select{width:100%;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground)}.jv-select.is-invalid{border-color:var(--jv-color-danger)}\n"] });
19601
+ }
19602
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSelectComponent, decorators: [{
19603
+ type: Component,
19604
+ args: [{ selector: 'jv-select', standalone: true, providers: [
19605
+ {
19606
+ provide: NG_VALUE_ACCESSOR,
19607
+ useExisting: forwardRef(() => JvSelectComponent),
19608
+ multi: true,
19609
+ },
19610
+ ], template: `
19611
+ <select
19612
+ class="jv-select"
19613
+ [class.is-invalid]="invalid()"
19614
+ [id]="inputId()"
19615
+ [disabled]="disabled"
19616
+ [required]="required()"
19617
+ [attr.aria-invalid]="invalid() ? 'true' : null"
19618
+ [attr.aria-describedby]="describedBy() || null"
19619
+ [value]="currentValue()"
19620
+ (change)="handleChange($event)"
19621
+ (blur)="handleBlur()"
19622
+ >
19623
+ @if (placeholder()) {
19624
+ <option value="" [disabled]="required()">{{ placeholder() }}</option>
19625
+ }
19626
+
19627
+ @for (option of options(); track option.label + ':' + option.value) {
19628
+ <option [value]="option.value" [disabled]="option.disabled ?? false">{{ option.label }}</option>
19629
+ }
19630
+ </select>
19631
+ `, styles: [".jv-select{width:100%;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground)}.jv-select.is-invalid{border-color:var(--jv-color-danger)}\n"] }]
19632
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], modelValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "modelValue", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
19633
+
19634
+ let datePickerIdSequence = 0;
19635
+ class JvDatePickerComponent {
19636
+ translation = inject(JvTranslationService);
19637
+ announcementService = inject(JvAnnouncementService);
19638
+ instanceId = ++datePickerIdSequence;
19639
+ calendarRootRef = viewChild('calendarRoot', ...(ngDevMode ? [{ debugName: "calendarRootRef" }] : /* istanbul ignore next */ []));
19640
+ triggerButtonRef = viewChild('triggerButton', ...(ngDevMode ? [{ debugName: "triggerButtonRef" }] : /* istanbul ignore next */ []));
19641
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
19642
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
19643
+ dialogTitle = input('', ...(ngDevMode ? [{ debugName: "dialogTitle" }] : /* istanbul ignore next */ []));
19644
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
19645
+ invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
19646
+ hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
19647
+ error = input('', ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
19648
+ describedBy = input('', ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
19649
+ inputId = input(`jv-date-picker-${this.instanceId}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
19650
+ valueInput = input(undefined, { ...(ngDevMode ? { debugName: "valueInput" } : /* istanbul ignore next */ {}), alias: 'value' });
19651
+ selectionChange = output();
19652
+ dialogOpen = signal(false, ...(ngDevMode ? [{ debugName: "dialogOpen" }] : /* istanbul ignore next */ []));
19653
+ visibleMonth = signal(firstOfMonth(new Date()), ...(ngDevMode ? [{ debugName: "visibleMonth" }] : /* istanbul ignore next */ []));
19654
+ focusedDate = signal(startOfDay(new Date()), ...(ngDevMode ? [{ debugName: "focusedDate" }] : /* istanbul ignore next */ []));
19655
+ dialogId = `jv-date-picker-dialog-${this.instanceId}`;
19656
+ value = null;
19657
+ disabled = false;
19658
+ resolvedDialogTitle = computed(() => this.dialogTitle() || this.translation.translate('datePicker.dialogTitle'), ...(ngDevMode ? [{ debugName: "resolvedDialogTitle" }] : /* istanbul ignore next */ []));
19659
+ currentValue = computed(() => this.valueInput() !== undefined ? this.valueInput() : this.value, ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
19660
+ triggerAriaLabel = computed(() => {
19661
+ const name = this.label() || this.resolvedDialogTitle();
19662
+ return `${name}. ${this.displayValue()}`;
19663
+ }, ...(ngDevMode ? [{ debugName: "triggerAriaLabel" }] : /* istanbul ignore next */ []));
19664
+ displayValue = computed(() => {
19665
+ const value = this.currentValue();
19666
+ if (!value) {
19667
+ return this.placeholder() || this.translation.translate('datePicker.placeholder');
19668
+ }
19669
+ return new Intl.DateTimeFormat(this.translation.locale(), {
19670
+ day: '2-digit',
19671
+ month: '2-digit',
19672
+ year: 'numeric',
19673
+ }).format(value);
19674
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
19675
+ monthOptions = computed(() => {
19676
+ const formatter = new Intl.DateTimeFormat(this.translation.locale(), { month: 'long' });
19677
+ return Array.from({ length: 12 }, (_, month) => ({
19678
+ label: capitalize(formatter.format(new Date(2026, month, 1))),
19679
+ value: String(month),
19680
+ }));
19681
+ }, ...(ngDevMode ? [{ debugName: "monthOptions" }] : /* istanbul ignore next */ []));
19682
+ yearOptions = computed(() => {
19683
+ return Array.from({ length: 201 }, (_, index) => {
19684
+ const year = 1900 + index;
19685
+ return { label: String(year), value: String(year) };
19686
+ });
19687
+ }, ...(ngDevMode ? [{ debugName: "yearOptions" }] : /* istanbul ignore next */ []));
19688
+ weekdayLabels = computed(() => {
19689
+ const startDay = this.firstDayOfWeek();
19690
+ const formatter = new Intl.DateTimeFormat(this.translation.locale(), { weekday: 'short' });
19691
+ return Array.from({ length: 7 }, (_, index) => capitalize(formatter.format(addDays(new Date(2026, 5, 7), startDay + index))));
19692
+ }, ...(ngDevMode ? [{ debugName: "weekdayLabels" }] : /* istanbul ignore next */ []));
19693
+ calendarCells = computed(() => {
19694
+ const month = this.visibleMonth();
19695
+ const firstDay = firstOfMonth(month);
19696
+ const daysInCurrentMonth = daysInMonth(month.getFullYear(), month.getMonth());
19697
+ const startOffset = (firstDay.getDay() - this.firstDayOfWeek() + 7) % 7;
19698
+ const gridStart = addDays(firstDay, -startOffset);
19699
+ return Array.from({ length: 42 }, (_, index) => {
19700
+ const date = addDays(gridStart, index);
19701
+ return {
19702
+ date,
19703
+ currentMonth: date.getMonth() === month.getMonth(),
19704
+ isToday: sameDate(date, new Date()),
19705
+ };
19706
+ }).slice(0, Math.ceil((startOffset + daysInCurrentMonth) / 7) * 7);
19707
+ }, ...(ngDevMode ? [{ debugName: "calendarCells" }] : /* istanbul ignore next */ []));
19708
+ onChange = () => undefined;
19709
+ onTouched = () => undefined;
19710
+ writeValue(value) {
19711
+ this.value = value ? startOfDay(value) : null;
19712
+ }
19713
+ registerOnChange(fn) {
19714
+ this.onChange = fn;
19715
+ }
19716
+ registerOnTouched(fn) {
19717
+ this.onTouched = fn;
19718
+ }
19719
+ setDisabledState(isDisabled) {
19720
+ this.disabled = isDisabled;
19721
+ }
19722
+ openDialog() {
19723
+ if (this.disabled) {
19724
+ return;
19725
+ }
19726
+ const baseDate = this.currentValue() ?? startOfDay(new Date());
19727
+ this.visibleMonth.set(firstOfMonth(baseDate));
19728
+ this.focusedDate.set(baseDate);
19729
+ this.dialogOpen.set(true);
19730
+ queueMicrotask(() => this.focusDateButton(baseDate));
19731
+ }
19732
+ closeDialog() {
19733
+ this.dialogOpen.set(false);
19734
+ this.handleBlur();
19735
+ queueMicrotask(() => this.triggerButtonRef()?.nativeElement.focus());
19736
+ }
19737
+ clearSelection() {
19738
+ this.updateValue(null);
19739
+ this.announcementService.announce(this.translation.translate('datePicker.selectionCleared'), 'polite');
19740
+ this.closeDialog();
19741
+ }
19742
+ shiftMonth(offset) {
19743
+ const month = this.visibleMonth();
19744
+ const nextMonth = firstOfMonth(new Date(month.getFullYear(), month.getMonth() + offset, 1));
19745
+ this.visibleMonth.set(nextMonth);
19746
+ this.focusDateWithinVisibleMonth(this.focusedDate());
19747
+ }
19748
+ handleMonthChange(value) {
19749
+ const month = Number(value);
19750
+ const current = this.visibleMonth();
19751
+ this.visibleMonth.set(firstOfMonth(new Date(current.getFullYear(), month, 1)));
19752
+ this.focusDateWithinVisibleMonth(this.focusedDate());
19753
+ }
19754
+ handleYearChange(value) {
19755
+ const year = Number(value);
19756
+ const current = this.visibleMonth();
19757
+ this.visibleMonth.set(firstOfMonth(new Date(year, current.getMonth(), 1)));
19758
+ this.focusDateWithinVisibleMonth(this.focusedDate());
19759
+ }
19760
+ selectDate(date) {
19761
+ const nextValue = startOfDay(date);
19762
+ this.updateValue(nextValue);
19763
+ this.announcementService.announce(this.displayDateForAnnouncement(nextValue), 'polite');
19764
+ this.closeDialog();
19765
+ }
19766
+ handleDayKeydown(event, date) {
19767
+ let nextDate = null;
19768
+ switch (event.key) {
19769
+ case 'ArrowRight':
19770
+ nextDate = addDays(date, 1);
19771
+ break;
19772
+ case 'ArrowLeft':
19773
+ nextDate = addDays(date, -1);
19774
+ break;
19775
+ case 'ArrowDown':
19776
+ nextDate = addDays(date, 7);
19777
+ break;
19778
+ case 'ArrowUp':
19779
+ nextDate = addDays(date, -7);
19780
+ break;
19781
+ case 'Home':
19782
+ nextDate = addDays(date, -((date.getDay() - this.firstDayOfWeek() + 7) % 7));
19783
+ break;
19784
+ case 'End':
19785
+ nextDate = addDays(date, 6 - ((date.getDay() - this.firstDayOfWeek() + 7) % 7));
19786
+ break;
19787
+ case 'Enter':
19788
+ case ' ':
19789
+ event.preventDefault();
19790
+ this.selectDate(date);
19791
+ return;
19792
+ }
19793
+ if (!nextDate) {
19794
+ return;
19795
+ }
19796
+ event.preventDefault();
19797
+ this.focusedDate.set(nextDate);
19798
+ this.visibleMonth.set(firstOfMonth(nextDate));
19799
+ queueMicrotask(() => this.focusDateButton(nextDate));
19800
+ }
19801
+ isSelected(date) {
19802
+ return sameDate(date, this.currentValue());
19803
+ }
19804
+ isFocusedDate(date) {
19805
+ return sameDate(date, this.focusedDate());
19806
+ }
19807
+ isoDate(date) {
19808
+ const year = date.getFullYear();
19809
+ const month = String(date.getMonth() + 1).padStart(2, '0');
19810
+ const day = String(date.getDate()).padStart(2, '0');
19811
+ return `${year}-${month}-${day}`;
19812
+ }
19813
+ handleBlur() {
19814
+ this.onTouched();
19815
+ }
19816
+ updateValue(value) {
19817
+ this.value = value;
19818
+ this.onChange(value);
19819
+ this.selectionChange.emit(value);
19820
+ }
19821
+ focusDateWithinVisibleMonth(date) {
19822
+ const month = this.visibleMonth();
19823
+ const nextDate = startOfDay(new Date(month.getFullYear(), month.getMonth(), Math.min(date.getDate(), daysInMonth(month.getFullYear(), month.getMonth()))));
19824
+ this.focusedDate.set(nextDate);
19825
+ queueMicrotask(() => this.focusDateButton(nextDate));
19826
+ }
19827
+ focusDateButton(date) {
19828
+ const root = this.calendarRootRef()?.nativeElement;
19829
+ root?.querySelector(`[data-jv-date="${this.isoDate(date)}"]`)?.focus();
19830
+ }
19831
+ firstDayOfWeek() {
19832
+ return this.translation.locale() === 'es' ? 1 : 0;
19833
+ }
19834
+ displayDateForAnnouncement(date) {
19835
+ return new Intl.DateTimeFormat(this.translation.locale(), {
19836
+ weekday: 'long',
19837
+ day: 'numeric',
19838
+ month: 'long',
19839
+ year: 'numeric',
19840
+ }).format(date);
19841
+ }
19842
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19843
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvDatePickerComponent, isStandalone: true, selector: "jv-date-picker", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, dialogTitle: { classPropertyName: "dialogTitle", publicName: "dialogTitle", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, describedBy: { classPropertyName: "describedBy", publicName: "describedBy", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, valueInput: { classPropertyName: "valueInput", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, providers: [
19844
+ {
19845
+ provide: NG_VALUE_ACCESSOR,
19846
+ useExisting: forwardRef(() => JvDatePickerComponent),
19847
+ multi: true,
19848
+ },
19849
+ ], viewQueries: [{ propertyName: "calendarRootRef", first: true, predicate: ["calendarRoot"], descendants: true, isSignal: true }, { propertyName: "triggerButtonRef", first: true, predicate: ["triggerButton"], descendants: true, isSignal: true }], ngImport: i0, template: `
19850
+ <jv-form-container
19851
+ [label]="label()"
19852
+ [inputId]="inputId()"
19853
+ [required]="required()"
19854
+ [invalid]="invalid()"
19855
+ [hint]="hint()"
19856
+ [error]="error()"
19857
+ >
19858
+ <button
19859
+ #triggerButton
19860
+ type="button"
19861
+ class="jv-date-picker-trigger"
19862
+ [class.is-invalid]="invalid()"
19863
+ [id]="inputId()"
19864
+ [disabled]="disabled"
19865
+ [attr.aria-haspopup]="'dialog'"
19866
+ [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
19867
+ [attr.aria-controls]="dialogId"
19868
+ [attr.aria-invalid]="invalid() ? 'true' : null"
19869
+ [attr.aria-describedby]="describedBy() || null"
19870
+ [attr.aria-label]="triggerAriaLabel()"
19871
+ [attr.title]="displayValue()"
19872
+ (click)="openDialog()"
19873
+ (blur)="handleBlur()"
19874
+ >
19875
+ <span [class.jv-date-picker-placeholder]="!currentValue()">{{ displayValue() }}</span>
19876
+ <jv-icon name="calendar-days" [size]="18" />
19877
+ </button>
19878
+ </jv-form-container>
19879
+
19880
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
19881
+ <div #calendarRoot [id]="dialogId" class="jv-date-picker-dialog">
19882
+ <div class="jv-date-picker-toolbar">
19883
+ <jv-icon-button icon="chevron-left" variant="ghost" [ariaLabel]="translation.translate('datePicker.prevMonth')" (click)="shiftMonth(-1)" />
19884
+
19885
+ <div class="jv-date-picker-selects">
19886
+ <jv-select [options]="monthOptions()" [modelValue]="visibleMonth().getMonth().toString()" (selectionChange)="handleMonthChange($event)" />
19887
+ <jv-select [options]="yearOptions()" [modelValue]="visibleMonth().getFullYear().toString()" (selectionChange)="handleYearChange($event)" />
19888
+ </div>
19889
+
19890
+ <jv-icon-button icon="chevron-right" variant="ghost" [ariaLabel]="translation.translate('datePicker.nextMonth')" (click)="shiftMonth(1)" />
19891
+ </div>
19892
+
19893
+ <div class="jv-date-picker-weekdays" aria-hidden="true">
19894
+ @for (weekday of weekdayLabels(); track weekday) {
19895
+ <span>{{ weekday }}</span>
19896
+ }
19897
+ </div>
19898
+
19899
+ <div class="jv-date-picker-grid" role="grid" [attr.aria-label]="resolvedDialogTitle()">
19900
+ @for (cell of calendarCells(); track isoDate(cell.date)) {
19901
+ <button
19902
+ type="button"
19903
+ class="jv-date-picker-day"
19904
+ [class.jv-date-picker-day--outside]="!cell.currentMonth"
19905
+ [class.jv-date-picker-day--today]="cell.isToday"
19906
+ [class.jv-date-picker-day--selected]="isSelected(cell.date)"
19907
+ [attr.data-jv-date]="isoDate(cell.date)"
19908
+ [attr.tabindex]="isFocusedDate(cell.date) ? 0 : -1"
19909
+ [attr.aria-selected]="isSelected(cell.date) ? 'true' : 'false'"
19910
+ (click)="selectDate(cell.date)"
19911
+ (keydown)="handleDayKeydown($event, cell.date)"
19912
+ >
19913
+ {{ cell.date.getDate() }}
19914
+ </button>
19915
+ }
19916
+ </div>
19917
+ </div>
19918
+
19919
+ <div dialog-actions class="jv-date-picker-actions">
19920
+ @if (currentValue()) {
19921
+ <jv-button variant="ghost" (click)="clearSelection()">{{ translation.translate('datePicker.clear') }}</jv-button>
19922
+ }
19923
+ <jv-button variant="outline" (click)="closeDialog()">{{ translation.translate('datePicker.close') }}</jv-button>
19924
+ </div>
19925
+ </jv-dialog>
19926
+ `, isInline: true, styles: [".jv-date-picker-trigger{width:100%;min-height:var(--jv-density-control-height);display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-sm);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);text-align:left;font:inherit;cursor:pointer}.jv-date-picker-trigger.is-invalid{border-color:var(--jv-color-danger)}.jv-date-picker-trigger:disabled{opacity:.7;cursor:not-allowed}.jv-date-picker-trigger:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-date-picker-placeholder{color:var(--jv-color-foreground-muted)}.jv-date-picker-dialog,.jv-date-picker-actions{display:grid;gap:var(--jv-spacing-md)}.jv-date-picker-toolbar,.jv-date-picker-selects,.jv-date-picker-weekdays,.jv-date-picker-grid{display:grid;gap:var(--jv-spacing-sm)}.jv-date-picker-toolbar{grid-template-columns:auto 1fr auto;align-items:center}.jv-date-picker-selects{grid-template-columns:1fr 1fr}.jv-date-picker-weekdays,.jv-date-picker-grid{grid-template-columns:repeat(7,minmax(0,1fr))}.jv-date-picker-weekdays span{text-align:center;font-size:.8125rem;font-weight:600;color:var(--jv-color-foreground-muted)}.jv-date-picker-day{min-height:2.5rem;border:1px solid transparent;border-radius:var(--jv-radius-md);background:transparent;color:var(--jv-color-foreground);font:inherit;cursor:pointer;transition:background-color .16s ease,border-color .16s ease,color .16s ease}.jv-date-picker-day:hover,.jv-date-picker-day:focus-visible{background:var(--jv-color-surface-muted)}.jv-date-picker-day:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-date-picker-day--outside{color:var(--jv-color-foreground-muted)}.jv-date-picker-day--today{border-color:var(--jv-color-primary)}.jv-date-picker-day--selected{background:var(--jv-color-primary);color:#fff}\n"], dependencies: [{ kind: "component", type: JvButtonComponent, selector: "jv-button", inputs: ["type", "variant", "icon", "iconPosition", "loading", "disabled"] }, { kind: "component", type: JvDialogComponent, selector: "jv-dialog", inputs: ["open", "title"], outputs: ["closed"] }, { kind: "component", type: JvFormContainerComponent, selector: "jv-form-container", inputs: ["label", "hint", "error", "inputId", "hintId", "errorId", "required", "invalid"] }, { kind: "component", type: JvIconButtonComponent, selector: "jv-icon-button", inputs: ["icon", "ariaLabel", "variant", "disabled"] }, { kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }, { kind: "component", type: JvSelectComponent, selector: "jv-select", inputs: ["options", "placeholder", "required", "invalid", "describedBy", "inputId", "modelValue"], outputs: ["selectionChange"] }] });
19927
+ }
19928
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDatePickerComponent, decorators: [{
19929
+ type: Component,
19930
+ args: [{ selector: 'jv-date-picker', standalone: true, imports: [JvButtonComponent, JvDialogComponent, JvFormContainerComponent, JvIconButtonComponent, JvIconComponent, JvSelectComponent], providers: [
19931
+ {
19932
+ provide: NG_VALUE_ACCESSOR,
19933
+ useExisting: forwardRef(() => JvDatePickerComponent),
19934
+ multi: true,
19935
+ },
19936
+ ], template: `
19937
+ <jv-form-container
19938
+ [label]="label()"
19939
+ [inputId]="inputId()"
19940
+ [required]="required()"
19941
+ [invalid]="invalid()"
19942
+ [hint]="hint()"
19943
+ [error]="error()"
19944
+ >
19945
+ <button
19946
+ #triggerButton
19947
+ type="button"
19948
+ class="jv-date-picker-trigger"
19949
+ [class.is-invalid]="invalid()"
19950
+ [id]="inputId()"
19951
+ [disabled]="disabled"
19952
+ [attr.aria-haspopup]="'dialog'"
19953
+ [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
19954
+ [attr.aria-controls]="dialogId"
19955
+ [attr.aria-invalid]="invalid() ? 'true' : null"
19956
+ [attr.aria-describedby]="describedBy() || null"
19957
+ [attr.aria-label]="triggerAriaLabel()"
19958
+ [attr.title]="displayValue()"
19959
+ (click)="openDialog()"
19960
+ (blur)="handleBlur()"
19961
+ >
19962
+ <span [class.jv-date-picker-placeholder]="!currentValue()">{{ displayValue() }}</span>
19963
+ <jv-icon name="calendar-days" [size]="18" />
19964
+ </button>
19965
+ </jv-form-container>
19966
+
19967
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
19968
+ <div #calendarRoot [id]="dialogId" class="jv-date-picker-dialog">
19969
+ <div class="jv-date-picker-toolbar">
19970
+ <jv-icon-button icon="chevron-left" variant="ghost" [ariaLabel]="translation.translate('datePicker.prevMonth')" (click)="shiftMonth(-1)" />
19971
+
19972
+ <div class="jv-date-picker-selects">
19973
+ <jv-select [options]="monthOptions()" [modelValue]="visibleMonth().getMonth().toString()" (selectionChange)="handleMonthChange($event)" />
19974
+ <jv-select [options]="yearOptions()" [modelValue]="visibleMonth().getFullYear().toString()" (selectionChange)="handleYearChange($event)" />
19975
+ </div>
19976
+
19977
+ <jv-icon-button icon="chevron-right" variant="ghost" [ariaLabel]="translation.translate('datePicker.nextMonth')" (click)="shiftMonth(1)" />
19978
+ </div>
19979
+
19980
+ <div class="jv-date-picker-weekdays" aria-hidden="true">
19981
+ @for (weekday of weekdayLabels(); track weekday) {
19982
+ <span>{{ weekday }}</span>
19983
+ }
19984
+ </div>
19985
+
19986
+ <div class="jv-date-picker-grid" role="grid" [attr.aria-label]="resolvedDialogTitle()">
19987
+ @for (cell of calendarCells(); track isoDate(cell.date)) {
19988
+ <button
19989
+ type="button"
19990
+ class="jv-date-picker-day"
19991
+ [class.jv-date-picker-day--outside]="!cell.currentMonth"
19992
+ [class.jv-date-picker-day--today]="cell.isToday"
19993
+ [class.jv-date-picker-day--selected]="isSelected(cell.date)"
19994
+ [attr.data-jv-date]="isoDate(cell.date)"
19995
+ [attr.tabindex]="isFocusedDate(cell.date) ? 0 : -1"
19996
+ [attr.aria-selected]="isSelected(cell.date) ? 'true' : 'false'"
19997
+ (click)="selectDate(cell.date)"
19998
+ (keydown)="handleDayKeydown($event, cell.date)"
19999
+ >
20000
+ {{ cell.date.getDate() }}
20001
+ </button>
20002
+ }
20003
+ </div>
20004
+ </div>
20005
+
20006
+ <div dialog-actions class="jv-date-picker-actions">
20007
+ @if (currentValue()) {
20008
+ <jv-button variant="ghost" (click)="clearSelection()">{{ translation.translate('datePicker.clear') }}</jv-button>
20009
+ }
20010
+ <jv-button variant="outline" (click)="closeDialog()">{{ translation.translate('datePicker.close') }}</jv-button>
20011
+ </div>
20012
+ </jv-dialog>
20013
+ `, styles: [".jv-date-picker-trigger{width:100%;min-height:var(--jv-density-control-height);display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-sm);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);text-align:left;font:inherit;cursor:pointer}.jv-date-picker-trigger.is-invalid{border-color:var(--jv-color-danger)}.jv-date-picker-trigger:disabled{opacity:.7;cursor:not-allowed}.jv-date-picker-trigger:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-date-picker-placeholder{color:var(--jv-color-foreground-muted)}.jv-date-picker-dialog,.jv-date-picker-actions{display:grid;gap:var(--jv-spacing-md)}.jv-date-picker-toolbar,.jv-date-picker-selects,.jv-date-picker-weekdays,.jv-date-picker-grid{display:grid;gap:var(--jv-spacing-sm)}.jv-date-picker-toolbar{grid-template-columns:auto 1fr auto;align-items:center}.jv-date-picker-selects{grid-template-columns:1fr 1fr}.jv-date-picker-weekdays,.jv-date-picker-grid{grid-template-columns:repeat(7,minmax(0,1fr))}.jv-date-picker-weekdays span{text-align:center;font-size:.8125rem;font-weight:600;color:var(--jv-color-foreground-muted)}.jv-date-picker-day{min-height:2.5rem;border:1px solid transparent;border-radius:var(--jv-radius-md);background:transparent;color:var(--jv-color-foreground);font:inherit;cursor:pointer;transition:background-color .16s ease,border-color .16s ease,color .16s ease}.jv-date-picker-day:hover,.jv-date-picker-day:focus-visible{background:var(--jv-color-surface-muted)}.jv-date-picker-day:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-date-picker-day--outside{color:var(--jv-color-foreground-muted)}.jv-date-picker-day--today{border-color:var(--jv-color-primary)}.jv-date-picker-day--selected{background:var(--jv-color-primary);color:#fff}\n"] }]
20014
+ }], propDecorators: { calendarRootRef: [{ type: i0.ViewChild, args: ['calendarRoot', { isSignal: true }] }], triggerButtonRef: [{ type: i0.ViewChild, args: ['triggerButton', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], dialogTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "dialogTitle", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], valueInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
20015
+ function daysInMonth(year, month) {
20016
+ return new Date(year, month + 1, 0).getDate();
20017
+ }
20018
+ function firstOfMonth(date) {
20019
+ return new Date(date.getFullYear(), date.getMonth(), 1);
20020
+ }
20021
+ function startOfDay(date) {
20022
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
20023
+ }
20024
+ function addDays(date, amount) {
20025
+ return startOfDay(new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount));
20026
+ }
20027
+ function sameDate(left, right) {
20028
+ if (!left || !right) {
20029
+ return false;
20030
+ }
20031
+ return left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate();
20032
+ }
20033
+ function capitalize(value) {
20034
+ return value.charAt(0).toUpperCase() + value.slice(1);
20035
+ }
20036
+
20037
+ class JvListComponent {
20038
+ role = input('list', ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
20039
+ ariaRole = input(null, ...(ngDevMode ? [{ debugName: "ariaRole" }] : /* istanbul ignore next */ []));
20040
+ variant = input(null, ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
20041
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
20042
+ variantClass = computed(() => {
20043
+ const v = this.variant();
20044
+ return v ? `jv-list--${v}` : '';
20045
+ }, ...(ngDevMode ? [{ debugName: "variantClass" }] : /* istanbul ignore next */ []));
20046
+ resolvedRole = computed(() => this.ariaRole() ?? (this.role() === 'navigation' ? 'list' : null), ...(ngDevMode ? [{ debugName: "resolvedRole" }] : /* istanbul ignore next */ []));
20047
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20048
+ 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 }, ariaRole: { classPropertyName: "ariaRole", publicName: "ariaRole", 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: `
20049
+ <ul
20050
+ class="jv-list"
20051
+ [class]="variantClass()"
20052
+ [attr.role]="resolvedRole()"
20053
+ [attr.aria-label]="ariaLabel()"
20054
+ >
20055
+ <ng-content />
20056
+ </ul>
20057
+ `, 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"] });
20058
+ }
20059
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvListComponent, decorators: [{
19103
20060
  type: Component,
19104
- args: [{ selector: 'jv-toast', standalone: true, imports: [JvIconComponent], template: `
19105
- @if (toastService.toasts().length > 0) {
19106
- <div class="jv-toast-viewport" [class]="'is-' + toastService.position()" aria-live="polite" aria-atomic="false">
19107
- @for (toast of toastService.toasts(); track toast.id) {
19108
- <article class="jv-toast-item" [class]="'jv-toast--' + toast.tone" [attr.role]="toast.tone === 'danger' ? 'alert' : 'status'" aria-atomic="true">
19109
- <div class="jv-toast-copy">
19110
- @if (toast.title) {
19111
- <strong>{{ toast.title }}</strong>
19112
- }
19113
- <p>{{ toast.message }}</p>
19114
- </div>
19115
-
19116
- <button type="button" class="jv-toast-close" (click)="toastService.dismiss(toast.id)" aria-label="Dismiss notification">
19117
- <jv-icon name="x" [size]="16" />
19118
- </button>
19119
- </article>
19120
- }
19121
- </div>
19122
- }
19123
- `, styles: [".jv-toast-viewport{position:fixed;z-index:1100;display:grid;gap:var(--jv-spacing-sm);width:min(360px,calc(100vw - 2rem))}.is-top-left{top:1rem;left:1rem}.is-top-center{top:1rem;left:50%;transform:translate(-50%)}.is-top-right{top:1rem;right:1rem}.is-bottom-left{bottom:1rem;left:1rem}.is-bottom-center{bottom:1rem;left:50%;transform:translate(-50%)}.is-bottom-right{bottom:1rem;right:1rem}.jv-toast-item{display:grid;grid-template-columns:1fr auto;gap:var(--jv-spacing-sm);padding:var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg)}.jv-toast-copy strong,.jv-toast-copy p{margin:0}.jv-toast-copy{display:grid;gap:.25rem}.jv-toast-copy p{color:var(--jv-color-foreground-muted)}.jv-toast-close{display:inline-grid;place-items:center;width:2rem;height:2rem;border:0;border-radius:999px;background:transparent;color:inherit}.jv-toast--success{border-color:color-mix(in srgb,var(--jv-color-success) 35%,var(--jv-color-border))}.jv-toast--warning{border-color:color-mix(in srgb,var(--jv-color-warning) 35%,var(--jv-color-border))}.jv-toast--danger{border-color:color-mix(in srgb,var(--jv-color-danger) 35%,var(--jv-color-border))}.jv-toast--info,.jv-toast--primary{border-color:color-mix(in srgb,var(--jv-color-primary) 35%,var(--jv-color-border))}\n"] }]
19124
- }] });
20061
+ args: [{ selector: 'jv-list', standalone: true, template: `
20062
+ <ul
20063
+ class="jv-list"
20064
+ [class]="variantClass()"
20065
+ [attr.role]="resolvedRole()"
20066
+ [attr.aria-label]="ariaLabel()"
20067
+ >
20068
+ <ng-content />
20069
+ </ul>
20070
+ `, 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"] }]
20071
+ }], propDecorators: { role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], ariaRole: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaRole", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
19125
20072
 
19126
- let radioIdSequence = 0;
19127
- class JvRadioComponent {
19128
- name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
20073
+ let modalSelectorIdSequence = 0;
20074
+ class JvModalSelectorComponent {
20075
+ announcementService = inject(JvAnnouncementService);
20076
+ translation = inject(JvTranslationService);
20077
+ instanceId = ++modalSelectorIdSequence;
20078
+ triggerButtonRef = viewChild('triggerButton', ...(ngDevMode ? [{ debugName: "triggerButtonRef" }] : /* istanbul ignore next */ []));
20079
+ optionElements = viewChildren('optionElement', ...(ngDevMode ? [{ debugName: "optionElements" }] : /* istanbul ignore next */ []));
20080
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
19129
20081
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
19130
- radioValue = input.required(...(ngDevMode ? [{ debugName: "radioValue" }] : /* istanbul ignore next */ []));
20082
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
20083
+ dialogTitle = input('', ...(ngDevMode ? [{ debugName: "dialogTitle" }] : /* istanbul ignore next */ []));
20084
+ dialogSubtitle = input('', ...(ngDevMode ? [{ debugName: "dialogSubtitle" }] : /* istanbul ignore next */ []));
20085
+ searchPlaceholder = input('', ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : /* istanbul ignore next */ []));
20086
+ emptyMessage = input('', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
20087
+ clearLabel = input('', ...(ngDevMode ? [{ debugName: "clearLabel" }] : /* istanbul ignore next */ []));
20088
+ closeLabel = input('', ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
20089
+ showSearch = input(true, ...(ngDevMode ? [{ debugName: "showSearch" }] : /* istanbul ignore next */ []));
20090
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
20091
+ invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
20092
+ hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
20093
+ error = input('', ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
19131
20094
  describedBy = input('', ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
19132
- inputId = input(`jv-radio-${++radioIdSequence}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
19133
- value = '';
20095
+ inputId = input(`jv-modal-selector-${this.instanceId}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
20096
+ valueInput = input(undefined, { ...(ngDevMode ? { debugName: "valueInput" } : /* istanbul ignore next */ {}), alias: 'value' });
20097
+ selectionChange = output();
20098
+ dialogOpen = signal(false, ...(ngDevMode ? [{ debugName: "dialogOpen" }] : /* istanbul ignore next */ []));
20099
+ searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : /* istanbul ignore next */ []));
20100
+ dialogId = `jv-modal-selector-dialog-${this.instanceId}`;
20101
+ searchInputId = `jv-modal-selector-search-${this.instanceId}`;
20102
+ value = null;
19134
20103
  disabled = false;
20104
+ resolvedDialogTitle = computed(() => this.dialogTitle() || this.label() || this.translation.translate('modalSelector.placeholder'), ...(ngDevMode ? [{ debugName: "resolvedDialogTitle" }] : /* istanbul ignore next */ []));
20105
+ currentValue = computed(() => (this.valueInput() !== undefined ? this.valueInput() : this.value), ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
20106
+ selectedOption = computed(() => this.options().find((option) => Object.is(option.value, this.currentValue())) ?? null, ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
20107
+ resolvedPlaceholder = computed(() => this.placeholder() || this.translation.translate('modalSelector.placeholder'), ...(ngDevMode ? [{ debugName: "resolvedPlaceholder" }] : /* istanbul ignore next */ []));
20108
+ triggerAriaLabel = computed(() => {
20109
+ const name = this.label() || this.resolvedDialogTitle();
20110
+ const value = this.selectedOption()?.label ?? this.resolvedPlaceholder();
20111
+ return `${name}. ${value}`;
20112
+ }, ...(ngDevMode ? [{ debugName: "triggerAriaLabel" }] : /* istanbul ignore next */ []));
20113
+ resultsLabel = computed(() => this.translation.translate('modalSelector.results', { count: this.filteredOptions().length }), ...(ngDevMode ? [{ debugName: "resultsLabel" }] : /* istanbul ignore next */ []));
20114
+ filteredOptions = computed(() => {
20115
+ const query = this.searchQuery().trim().toLocaleLowerCase();
20116
+ if (!query) {
20117
+ return this.options();
20118
+ }
20119
+ return this.options().filter((option) => {
20120
+ const haystack = [option.label, option.description ?? '', ...(option.keywords ?? [])].join(' ').toLocaleLowerCase();
20121
+ return haystack.includes(query);
20122
+ });
20123
+ }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
19135
20124
  onChange = () => undefined;
19136
20125
  onTouched = () => undefined;
19137
20126
  writeValue(value) {
19138
- this.value = value ?? '';
20127
+ this.value = value ?? null;
19139
20128
  }
19140
20129
  registerOnChange(fn) {
19141
20130
  this.onChange = fn;
@@ -19146,77 +20135,312 @@ class JvRadioComponent {
19146
20135
  setDisabledState(isDisabled) {
19147
20136
  this.disabled = isDisabled;
19148
20137
  }
19149
- handleChange() {
19150
- const nextValue = this.radioValue();
19151
- this.value = nextValue;
19152
- this.onChange(nextValue);
20138
+ openDialog() {
20139
+ if (this.disabled) {
20140
+ return;
20141
+ }
20142
+ this.searchQuery.set('');
20143
+ this.dialogOpen.set(true);
20144
+ }
20145
+ closeDialog() {
20146
+ this.dialogOpen.set(false);
20147
+ this.handleBlur();
20148
+ queueMicrotask(() => this.triggerButtonRef()?.nativeElement.focus());
20149
+ }
20150
+ clearSelection() {
20151
+ this.updateValue(null);
20152
+ this.announcementService.announce(this.translation.translate('modalSelector.selectionCleared'), 'polite');
20153
+ this.closeDialog();
20154
+ }
20155
+ selectOption(option) {
20156
+ if (option.disabled) {
20157
+ return;
20158
+ }
20159
+ this.updateValue(option.value);
20160
+ this.announcementService.announce(this.translation.translate('modalSelector.selected', { label: option.label }), 'polite');
20161
+ this.closeDialog();
20162
+ }
20163
+ isSelected(option) {
20164
+ return Object.is(option.value, this.currentValue());
20165
+ }
20166
+ getOptionTabIndex(option, index) {
20167
+ if (option.disabled) {
20168
+ return -1;
20169
+ }
20170
+ const selected = this.filteredOptions().find((item) => this.isSelected(item) && !item.disabled);
20171
+ if (selected) {
20172
+ return this.isSelected(option) ? 0 : -1;
20173
+ }
20174
+ const firstEnabledIndex = this.filteredOptions().findIndex((item) => !item.disabled);
20175
+ return index === firstEnabledIndex ? 0 : -1;
20176
+ }
20177
+ optionId(index) {
20178
+ return `jv-modal-selector-option-${this.instanceId}-${index}`;
20179
+ }
20180
+ handleOptionKeydown(event, index, option) {
20181
+ switch (event.key) {
20182
+ case 'ArrowDown':
20183
+ event.preventDefault();
20184
+ this.focusRelativeOption(index, 1);
20185
+ break;
20186
+ case 'ArrowUp':
20187
+ event.preventDefault();
20188
+ this.focusRelativeOption(index, -1);
20189
+ break;
20190
+ case 'Home':
20191
+ event.preventDefault();
20192
+ this.focusOptionByIndex(0);
20193
+ break;
20194
+ case 'End':
20195
+ event.preventDefault();
20196
+ this.focusOptionByIndex(this.filteredOptions().length - 1);
20197
+ break;
20198
+ case 'Enter':
20199
+ case ' ':
20200
+ event.preventDefault();
20201
+ this.selectOption(option);
20202
+ break;
20203
+ }
19153
20204
  }
19154
20205
  handleBlur() {
19155
20206
  this.onTouched();
19156
20207
  }
19157
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvRadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19158
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: JvRadioComponent, isStandalone: true, selector: "jv-radio", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, radioValue: { classPropertyName: "radioValue", publicName: "radioValue", isSignal: true, isRequired: true, transformFunction: null }, describedBy: { classPropertyName: "describedBy", publicName: "describedBy", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
20208
+ updateValue(value) {
20209
+ this.value = value;
20210
+ this.onChange(value);
20211
+ this.selectionChange.emit(value);
20212
+ }
20213
+ focusRelativeOption(startIndex, direction) {
20214
+ const options = this.filteredOptions();
20215
+ for (let offset = 1; offset <= options.length; offset++) {
20216
+ const candidateIndex = (startIndex + direction * offset + options.length) % options.length;
20217
+ if (!options[candidateIndex]?.disabled) {
20218
+ this.focusOptionByIndex(candidateIndex);
20219
+ return;
20220
+ }
20221
+ }
20222
+ }
20223
+ focusOptionByIndex(index) {
20224
+ const optionElements = this.optionElements();
20225
+ optionElements?.[index]?.nativeElement.focus();
20226
+ }
20227
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvModalSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20228
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvModalSelectorComponent, isStandalone: true, selector: "jv-modal-selector", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, dialogTitle: { classPropertyName: "dialogTitle", publicName: "dialogTitle", isSignal: true, isRequired: false, transformFunction: null }, dialogSubtitle: { classPropertyName: "dialogSubtitle", publicName: "dialogSubtitle", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, clearLabel: { classPropertyName: "clearLabel", publicName: "clearLabel", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, describedBy: { classPropertyName: "describedBy", publicName: "describedBy", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, valueInput: { classPropertyName: "valueInput", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, providers: [
19159
20229
  {
19160
20230
  provide: NG_VALUE_ACCESSOR,
19161
- useExisting: forwardRef(() => JvRadioComponent),
20231
+ useExisting: forwardRef(() => JvModalSelectorComponent),
19162
20232
  multi: true,
19163
20233
  },
19164
- ], ngImport: i0, template: `
19165
- <label class="jv-radio" [class.is-disabled]="disabled">
19166
- <input
19167
- type="radio"
19168
- [id]="inputId()"
19169
- [name]="name()"
19170
- [checked]="value === radioValue()"
19171
- [disabled]="disabled"
19172
- [attr.aria-describedby]="describedBy() || null"
19173
- (change)="handleChange()"
19174
- (blur)="handleBlur()"
19175
- />
19176
- <span>{{ label() }}</span>
19177
- </label>
19178
- `, isInline: true, styles: [".jv-radio{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm);min-height:var(--jv-density-control-height);color:var(--jv-color-foreground)}.jv-radio.is-disabled{opacity:.7}\n"] });
20234
+ ], viewQueries: [{ propertyName: "triggerButtonRef", first: true, predicate: ["triggerButton"], descendants: true, isSignal: true }, { propertyName: "optionElements", predicate: ["optionElement"], descendants: true, isSignal: true }], ngImport: i0, template: `
20235
+ <jv-form-container
20236
+ [label]="label()"
20237
+ [inputId]="inputId()"
20238
+ [required]="required()"
20239
+ [invalid]="invalid()"
20240
+ [hint]="hint()"
20241
+ [error]="error()"
20242
+ >
20243
+ <button
20244
+ #triggerButton
20245
+ type="button"
20246
+ class="jv-modal-selector-trigger"
20247
+ [class.is-invalid]="invalid()"
20248
+ [id]="inputId()"
20249
+ [disabled]="disabled"
20250
+ [attr.aria-haspopup]="'dialog'"
20251
+ [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
20252
+ [attr.aria-controls]="dialogId"
20253
+ [attr.aria-invalid]="invalid() ? 'true' : null"
20254
+ [attr.aria-describedby]="describedBy() || null"
20255
+ [attr.aria-label]="triggerAriaLabel()"
20256
+ (click)="openDialog()"
20257
+ (blur)="handleBlur()"
20258
+ >
20259
+ <span class="jv-modal-selector-trigger-copy">
20260
+ <span [class.jv-modal-selector-trigger-placeholder]="!selectedOption()">
20261
+ {{ selectedOption()?.label ?? resolvedPlaceholder() }}
20262
+ </span>
20263
+ </span>
20264
+ <jv-icon name="chevrons-up-down" [size]="18" />
20265
+ </button>
20266
+ </jv-form-container>
20267
+
20268
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
20269
+ <div [id]="dialogId" class="jv-modal-selector-dialog">
20270
+ @if (dialogSubtitle()) {
20271
+ <p class="jv-modal-selector-subtitle">{{ dialogSubtitle() }}</p>
20272
+ }
20273
+
20274
+ @if (showSearch()) {
20275
+ <jv-input
20276
+ type="search"
20277
+ [inputId]="searchInputId"
20278
+ [placeholder]="searchPlaceholder() || translation.translate('modalSelector.searchPlaceholder')"
20279
+ [ngModel]="searchQuery()"
20280
+ (ngModelChange)="searchQuery.set($event)"
20281
+ />
20282
+ }
20283
+
20284
+ <p class="jv-modal-selector-results" aria-live="polite">{{ resultsLabel() }}</p>
20285
+
20286
+ @if (filteredOptions().length > 0) {
20287
+ <jv-list variant="bordered" [ariaRole]="'listbox'" [ariaLabel]="resolvedDialogTitle()">
20288
+ @for (option of filteredOptions(); track option.value; let index = $index) {
20289
+ <li role="presentation">
20290
+ <div
20291
+ #optionElement
20292
+ class="jv-modal-selector-option"
20293
+ [class.jv-modal-selector-option--selected]="isSelected(option)"
20294
+ [class.jv-modal-selector-option--disabled]="option.disabled ?? false"
20295
+ [id]="optionId(index)"
20296
+ role="option"
20297
+ [attr.aria-selected]="isSelected(option) ? 'true' : 'false'"
20298
+ [attr.tabindex]="getOptionTabIndex(option, index)"
20299
+ (click)="selectOption(option)"
20300
+ (keydown)="handleOptionKeydown($event, index, option)"
20301
+ >
20302
+ <div class="jv-modal-selector-option-copy">
20303
+ <span class="jv-modal-selector-option-label">{{ option.label }}</span>
20304
+ @if (option.description) {
20305
+ <span class="jv-modal-selector-option-description">{{ option.description }}</span>
20306
+ }
20307
+ </div>
20308
+ @if (isSelected(option)) {
20309
+ <jv-icon name="check" [size]="16" />
20310
+ }
20311
+ </div>
20312
+ </li>
20313
+ }
20314
+ </jv-list>
20315
+ } @else {
20316
+ <div class="jv-modal-selector-empty" aria-live="polite">{{ emptyMessage() || translation.translate('modalSelector.emptyMessage') }}</div>
20317
+ }
20318
+ </div>
20319
+
20320
+ <div dialog-actions class="jv-modal-selector-actions">
20321
+ @if (selectedOption()) {
20322
+ <jv-button variant="ghost" (click)="clearSelection()">{{ clearLabel() || translation.translate('modalSelector.clear') }}</jv-button>
20323
+ }
20324
+ <jv-button variant="outline" (click)="closeDialog()">{{ closeLabel() || translation.translate('modalSelector.close') }}</jv-button>
20325
+ </div>
20326
+ </jv-dialog>
20327
+ `, isInline: true, styles: [".jv-modal-selector-trigger{width:100%;min-height:var(--jv-density-control-height);display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-sm);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);text-align:left;font:inherit;cursor:pointer}.jv-modal-selector-trigger.is-invalid{border-color:var(--jv-color-danger)}.jv-modal-selector-trigger:disabled{opacity:.7;cursor:not-allowed}.jv-modal-selector-trigger:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-modal-selector-trigger-copy{min-width:0;flex:1}.jv-modal-selector-trigger-placeholder{color:var(--jv-color-foreground-muted)}.jv-modal-selector-dialog,.jv-modal-selector-actions{display:grid;gap:var(--jv-spacing-md)}.jv-modal-selector-subtitle,.jv-modal-selector-results,.jv-modal-selector-option-description,.jv-modal-selector-empty{margin:0;color:var(--jv-color-foreground-muted)}.jv-modal-selector-empty{padding:var(--jv-spacing-md);border:1px dashed var(--jv-color-border);border-radius:var(--jv-radius-md);text-align:center}.jv-modal-selector-option{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-md);background:var(--jv-color-surface);cursor:pointer;outline:none;transition:background-color .16s ease}.jv-modal-selector-option:hover,.jv-modal-selector-option:focus-visible{background:var(--jv-color-surface-muted)}.jv-modal-selector-option:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:-2px}.jv-modal-selector-option--selected{background:color-mix(in srgb,var(--jv-color-primary) 8%,var(--jv-color-surface))}.jv-modal-selector-option--disabled{opacity:.55;cursor:not-allowed;pointer-events:none}.jv-modal-selector-option-copy{min-width:0;display:grid;gap:.25rem}.jv-modal-selector-option-label{font-weight:600;color:var(--jv-color-foreground)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: JvButtonComponent, selector: "jv-button", inputs: ["type", "variant", "icon", "iconPosition", "loading", "disabled"] }, { kind: "component", type: JvDialogComponent, selector: "jv-dialog", inputs: ["open", "title"], outputs: ["closed"] }, { kind: "component", type: JvFormContainerComponent, selector: "jv-form-container", inputs: ["label", "hint", "error", "inputId", "hintId", "errorId", "required", "invalid"] }, { kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }, { kind: "component", type: JvInputComponent, selector: "jv-input", inputs: ["type", "placeholder", "required", "invalid", "describedBy", "inputId"] }, { kind: "component", type: JvListComponent, selector: "jv-list", inputs: ["role", "ariaRole", "variant", "ariaLabel"] }] });
19179
20328
  }
19180
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvRadioComponent, decorators: [{
20329
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvModalSelectorComponent, decorators: [{
19181
20330
  type: Component,
19182
- args: [{ selector: 'jv-radio', standalone: true, providers: [
20331
+ args: [{ selector: 'jv-modal-selector', standalone: true, imports: [FormsModule, JvButtonComponent, JvDialogComponent, JvFormContainerComponent, JvIconComponent, JvInputComponent, JvListComponent], providers: [
19183
20332
  {
19184
20333
  provide: NG_VALUE_ACCESSOR,
19185
- useExisting: forwardRef(() => JvRadioComponent),
20334
+ useExisting: forwardRef(() => JvModalSelectorComponent),
19186
20335
  multi: true,
19187
20336
  },
19188
- ], template: `
19189
- <label class="jv-radio" [class.is-disabled]="disabled">
19190
- <input
19191
- type="radio"
19192
- [id]="inputId()"
19193
- [name]="name()"
19194
- [checked]="value === radioValue()"
19195
- [disabled]="disabled"
19196
- [attr.aria-describedby]="describedBy() || null"
19197
- (change)="handleChange()"
19198
- (blur)="handleBlur()"
19199
- />
19200
- <span>{{ label() }}</span>
19201
- </label>
19202
- `, styles: [".jv-radio{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm);min-height:var(--jv-density-control-height);color:var(--jv-color-foreground)}.jv-radio.is-disabled{opacity:.7}\n"] }]
19203
- }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], radioValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "radioValue", required: true }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }] } });
20337
+ ], template: `
20338
+ <jv-form-container
20339
+ [label]="label()"
20340
+ [inputId]="inputId()"
20341
+ [required]="required()"
20342
+ [invalid]="invalid()"
20343
+ [hint]="hint()"
20344
+ [error]="error()"
20345
+ >
20346
+ <button
20347
+ #triggerButton
20348
+ type="button"
20349
+ class="jv-modal-selector-trigger"
20350
+ [class.is-invalid]="invalid()"
20351
+ [id]="inputId()"
20352
+ [disabled]="disabled"
20353
+ [attr.aria-haspopup]="'dialog'"
20354
+ [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
20355
+ [attr.aria-controls]="dialogId"
20356
+ [attr.aria-invalid]="invalid() ? 'true' : null"
20357
+ [attr.aria-describedby]="describedBy() || null"
20358
+ [attr.aria-label]="triggerAriaLabel()"
20359
+ (click)="openDialog()"
20360
+ (blur)="handleBlur()"
20361
+ >
20362
+ <span class="jv-modal-selector-trigger-copy">
20363
+ <span [class.jv-modal-selector-trigger-placeholder]="!selectedOption()">
20364
+ {{ selectedOption()?.label ?? resolvedPlaceholder() }}
20365
+ </span>
20366
+ </span>
20367
+ <jv-icon name="chevrons-up-down" [size]="18" />
20368
+ </button>
20369
+ </jv-form-container>
19204
20370
 
19205
- let selectIdSequence = 0;
19206
- class JvSelectComponent {
19207
- options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
19208
- placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
19209
- required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
19210
- invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
20371
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
20372
+ <div [id]="dialogId" class="jv-modal-selector-dialog">
20373
+ @if (dialogSubtitle()) {
20374
+ <p class="jv-modal-selector-subtitle">{{ dialogSubtitle() }}</p>
20375
+ }
20376
+
20377
+ @if (showSearch()) {
20378
+ <jv-input
20379
+ type="search"
20380
+ [inputId]="searchInputId"
20381
+ [placeholder]="searchPlaceholder() || translation.translate('modalSelector.searchPlaceholder')"
20382
+ [ngModel]="searchQuery()"
20383
+ (ngModelChange)="searchQuery.set($event)"
20384
+ />
20385
+ }
20386
+
20387
+ <p class="jv-modal-selector-results" aria-live="polite">{{ resultsLabel() }}</p>
20388
+
20389
+ @if (filteredOptions().length > 0) {
20390
+ <jv-list variant="bordered" [ariaRole]="'listbox'" [ariaLabel]="resolvedDialogTitle()">
20391
+ @for (option of filteredOptions(); track option.value; let index = $index) {
20392
+ <li role="presentation">
20393
+ <div
20394
+ #optionElement
20395
+ class="jv-modal-selector-option"
20396
+ [class.jv-modal-selector-option--selected]="isSelected(option)"
20397
+ [class.jv-modal-selector-option--disabled]="option.disabled ?? false"
20398
+ [id]="optionId(index)"
20399
+ role="option"
20400
+ [attr.aria-selected]="isSelected(option) ? 'true' : 'false'"
20401
+ [attr.tabindex]="getOptionTabIndex(option, index)"
20402
+ (click)="selectOption(option)"
20403
+ (keydown)="handleOptionKeydown($event, index, option)"
20404
+ >
20405
+ <div class="jv-modal-selector-option-copy">
20406
+ <span class="jv-modal-selector-option-label">{{ option.label }}</span>
20407
+ @if (option.description) {
20408
+ <span class="jv-modal-selector-option-description">{{ option.description }}</span>
20409
+ }
20410
+ </div>
20411
+ @if (isSelected(option)) {
20412
+ <jv-icon name="check" [size]="16" />
20413
+ }
20414
+ </div>
20415
+ </li>
20416
+ }
20417
+ </jv-list>
20418
+ } @else {
20419
+ <div class="jv-modal-selector-empty" aria-live="polite">{{ emptyMessage() || translation.translate('modalSelector.emptyMessage') }}</div>
20420
+ }
20421
+ </div>
20422
+
20423
+ <div dialog-actions class="jv-modal-selector-actions">
20424
+ @if (selectedOption()) {
20425
+ <jv-button variant="ghost" (click)="clearSelection()">{{ clearLabel() || translation.translate('modalSelector.clear') }}</jv-button>
20426
+ }
20427
+ <jv-button variant="outline" (click)="closeDialog()">{{ closeLabel() || translation.translate('modalSelector.close') }}</jv-button>
20428
+ </div>
20429
+ </jv-dialog>
20430
+ `, styles: [".jv-modal-selector-trigger{width:100%;min-height:var(--jv-density-control-height);display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-sm);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);text-align:left;font:inherit;cursor:pointer}.jv-modal-selector-trigger.is-invalid{border-color:var(--jv-color-danger)}.jv-modal-selector-trigger:disabled{opacity:.7;cursor:not-allowed}.jv-modal-selector-trigger:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-modal-selector-trigger-copy{min-width:0;flex:1}.jv-modal-selector-trigger-placeholder{color:var(--jv-color-foreground-muted)}.jv-modal-selector-dialog,.jv-modal-selector-actions{display:grid;gap:var(--jv-spacing-md)}.jv-modal-selector-subtitle,.jv-modal-selector-results,.jv-modal-selector-option-description,.jv-modal-selector-empty{margin:0;color:var(--jv-color-foreground-muted)}.jv-modal-selector-empty{padding:var(--jv-spacing-md);border:1px dashed var(--jv-color-border);border-radius:var(--jv-radius-md);text-align:center}.jv-modal-selector-option{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-md);background:var(--jv-color-surface);cursor:pointer;outline:none;transition:background-color .16s ease}.jv-modal-selector-option:hover,.jv-modal-selector-option:focus-visible{background:var(--jv-color-surface-muted)}.jv-modal-selector-option:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:-2px}.jv-modal-selector-option--selected{background:color-mix(in srgb,var(--jv-color-primary) 8%,var(--jv-color-surface))}.jv-modal-selector-option--disabled{opacity:.55;cursor:not-allowed;pointer-events:none}.jv-modal-selector-option-copy{min-width:0;display:grid;gap:.25rem}.jv-modal-selector-option-label{font-weight:600;color:var(--jv-color-foreground)}\n"] }]
20431
+ }], propDecorators: { triggerButtonRef: [{ type: i0.ViewChild, args: ['triggerButton', { isSignal: true }] }], optionElements: [{ type: i0.ViewChildren, args: ['optionElement', { isSignal: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], dialogTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "dialogTitle", required: false }] }], dialogSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "dialogSubtitle", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], valueInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
20432
+
20433
+ let radioIdSequence = 0;
20434
+ class JvRadioComponent {
20435
+ name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
20436
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
20437
+ radioValue = input.required(...(ngDevMode ? [{ debugName: "radioValue" }] : /* istanbul ignore next */ []));
19211
20438
  describedBy = input('', ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
19212
- inputId = input(`jv-select-${++selectIdSequence}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
19213
- modelValue = input('', ...(ngDevMode ? [{ debugName: "modelValue" }] : /* istanbul ignore next */ []));
19214
- selectionChange = output();
20439
+ inputId = input(`jv-radio-${++radioIdSequence}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
19215
20440
  value = '';
19216
20441
  disabled = false;
19217
20442
  onChange = () => undefined;
19218
20443
  onTouched = () => undefined;
19219
- currentValue = computed(() => this.modelValue() || this.value, ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
19220
20444
  writeValue(value) {
19221
20445
  this.value = value ?? '';
19222
20446
  }
@@ -19229,76 +20453,61 @@ class JvSelectComponent {
19229
20453
  setDisabledState(isDisabled) {
19230
20454
  this.disabled = isDisabled;
19231
20455
  }
19232
- handleChange(event) {
19233
- const nextValue = event.target.value;
20456
+ handleChange() {
20457
+ const nextValue = this.radioValue();
19234
20458
  this.value = nextValue;
19235
20459
  this.onChange(nextValue);
19236
- this.selectionChange.emit(nextValue);
19237
20460
  }
19238
20461
  handleBlur() {
19239
20462
  this.onTouched();
19240
20463
  }
19241
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19242
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvSelectComponent, isStandalone: true, selector: "jv-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, describedBy: { classPropertyName: "describedBy", publicName: "describedBy", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, modelValue: { classPropertyName: "modelValue", publicName: "modelValue", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, providers: [
20464
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvRadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20465
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: JvRadioComponent, isStandalone: true, selector: "jv-radio", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, radioValue: { classPropertyName: "radioValue", publicName: "radioValue", isSignal: true, isRequired: true, transformFunction: null }, describedBy: { classPropertyName: "describedBy", publicName: "describedBy", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
19243
20466
  {
19244
20467
  provide: NG_VALUE_ACCESSOR,
19245
- useExisting: forwardRef(() => JvSelectComponent),
20468
+ useExisting: forwardRef(() => JvRadioComponent),
19246
20469
  multi: true,
19247
20470
  },
19248
20471
  ], ngImport: i0, template: `
19249
- <select
19250
- class="jv-select"
19251
- [class.is-invalid]="invalid()"
19252
- [id]="inputId()"
19253
- [disabled]="disabled"
19254
- [required]="required()"
19255
- [attr.aria-invalid]="invalid() ? 'true' : null"
19256
- [attr.aria-describedby]="describedBy() || null"
19257
- [value]="currentValue()"
19258
- (change)="handleChange($event)"
19259
- (blur)="handleBlur()"
19260
- >
19261
- @if (placeholder()) {
19262
- <option value="" [disabled]="required()">{{ placeholder() }}</option>
19263
- }
19264
-
19265
- @for (option of options(); track option.label + ':' + option.value) {
19266
- <option [value]="option.value" [disabled]="option.disabled ?? false">{{ option.label }}</option>
19267
- }
19268
- </select>
19269
- `, isInline: true, styles: [".jv-select{width:100%;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground)}.jv-select.is-invalid{border-color:var(--jv-color-danger)}\n"] });
20472
+ <label class="jv-radio" [class.is-disabled]="disabled">
20473
+ <input
20474
+ type="radio"
20475
+ [id]="inputId()"
20476
+ [name]="name()"
20477
+ [checked]="value === radioValue()"
20478
+ [disabled]="disabled"
20479
+ [attr.aria-describedby]="describedBy() || null"
20480
+ (change)="handleChange()"
20481
+ (blur)="handleBlur()"
20482
+ />
20483
+ <span>{{ label() }}</span>
20484
+ </label>
20485
+ `, isInline: true, styles: [".jv-radio{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm);min-height:var(--jv-density-control-height);color:var(--jv-color-foreground)}.jv-radio.is-disabled{opacity:.7}\n"] });
19270
20486
  }
19271
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSelectComponent, decorators: [{
20487
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvRadioComponent, decorators: [{
19272
20488
  type: Component,
19273
- args: [{ selector: 'jv-select', standalone: true, providers: [
20489
+ args: [{ selector: 'jv-radio', standalone: true, providers: [
19274
20490
  {
19275
20491
  provide: NG_VALUE_ACCESSOR,
19276
- useExisting: forwardRef(() => JvSelectComponent),
19277
- multi: true,
19278
- },
19279
- ], template: `
19280
- <select
19281
- class="jv-select"
19282
- [class.is-invalid]="invalid()"
19283
- [id]="inputId()"
19284
- [disabled]="disabled"
19285
- [required]="required()"
19286
- [attr.aria-invalid]="invalid() ? 'true' : null"
19287
- [attr.aria-describedby]="describedBy() || null"
19288
- [value]="currentValue()"
19289
- (change)="handleChange($event)"
19290
- (blur)="handleBlur()"
19291
- >
19292
- @if (placeholder()) {
19293
- <option value="" [disabled]="required()">{{ placeholder() }}</option>
19294
- }
19295
-
19296
- @for (option of options(); track option.label + ':' + option.value) {
19297
- <option [value]="option.value" [disabled]="option.disabled ?? false">{{ option.label }}</option>
19298
- }
19299
- </select>
19300
- `, styles: [".jv-select{width:100%;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground)}.jv-select.is-invalid{border-color:var(--jv-color-danger)}\n"] }]
19301
- }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], modelValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "modelValue", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
20492
+ useExisting: forwardRef(() => JvRadioComponent),
20493
+ multi: true,
20494
+ },
20495
+ ], template: `
20496
+ <label class="jv-radio" [class.is-disabled]="disabled">
20497
+ <input
20498
+ type="radio"
20499
+ [id]="inputId()"
20500
+ [name]="name()"
20501
+ [checked]="value === radioValue()"
20502
+ [disabled]="disabled"
20503
+ [attr.aria-describedby]="describedBy() || null"
20504
+ (change)="handleChange()"
20505
+ (blur)="handleBlur()"
20506
+ />
20507
+ <span>{{ label() }}</span>
20508
+ </label>
20509
+ `, styles: [".jv-radio{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm);min-height:var(--jv-density-control-height);color:var(--jv-color-foreground)}.jv-radio.is-disabled{opacity:.7}\n"] }]
20510
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], radioValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "radioValue", required: true }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }] } });
19302
20511
 
19303
20512
  let switchIdSequence = 0;
19304
20513
  class JvSwitchComponent {
@@ -19515,119 +20724,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
19515
20724
  args: [{ providedIn: 'root' }]
19516
20725
  }] });
19517
20726
 
19518
- class JvDialogComponent {
19519
- document = inject(DOCUMENT);
19520
- panelRef = viewChild('panel', ...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
19521
- open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
19522
- title = input('Dialog', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
19523
- closed = output();
19524
- titleId = `jv-dialog-title-${Math.random().toString(36).slice(2, 8)}`;
19525
- descriptionId = `jv-dialog-description-${Math.random().toString(36).slice(2, 8)}`;
19526
- constructor() {
19527
- effect(() => {
19528
- if (this.open()) {
19529
- queueMicrotask(() => this.focusInitialElement());
19530
- }
19531
- });
19532
- }
19533
- handleDocumentKeydown(event) {
19534
- if (!this.open()) {
19535
- return;
19536
- }
19537
- if (event.key === 'Escape') {
19538
- event.preventDefault();
19539
- this.closed.emit();
19540
- return;
19541
- }
19542
- if (event.key === 'Tab') {
19543
- this.trapFocus(event);
19544
- }
19545
- }
19546
- focusInitialElement() {
19547
- const panel = this.panelRef()?.nativeElement;
19548
- if (!panel) {
19549
- return;
19550
- }
19551
- const firstFocusable = this.getFocusableElements()[0];
19552
- (firstFocusable ?? panel).focus();
19553
- }
19554
- backdropClose() {
19555
- this.closed.emit();
19556
- }
19557
- trapFocus(event) {
19558
- const focusable = this.getFocusableElements();
19559
- const panel = this.panelRef()?.nativeElement;
19560
- if (!panel || focusable.length === 0) {
19561
- panel?.focus();
19562
- event.preventDefault();
19563
- return;
19564
- }
19565
- const first = focusable[0];
19566
- const last = focusable[focusable.length - 1];
19567
- const active = this.document.activeElement;
19568
- if (event.shiftKey && active === first) {
19569
- last.focus();
19570
- event.preventDefault();
19571
- }
19572
- else if (!event.shiftKey && active === last) {
19573
- first.focus();
19574
- event.preventDefault();
19575
- }
19576
- }
19577
- getFocusableElements() {
19578
- const panel = this.panelRef()?.nativeElement;
19579
- if (!panel) {
19580
- return [];
19581
- }
19582
- return Array.from(panel.querySelectorAll('button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'));
19583
- }
19584
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19585
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvDialogComponent, isStandalone: true, selector: "jv-dialog", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, host: { listeners: { "document:keydown": "handleDocumentKeydown($event)" } }, viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: `
19586
- @if (open()) {
19587
- <div class="jv-dialog-backdrop" (click)="backdropClose()">
19588
- <div class="jv-dialog-panel" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="descriptionId" (click)="$event.stopPropagation()" #panel>
19589
- <header class="jv-dialog-header">
19590
- <h2 [id]="titleId">{{ title() }}</h2>
19591
- </header>
19592
-
19593
- <div class="jv-dialog-body" [id]="descriptionId">
19594
- <ng-content></ng-content>
19595
- </div>
19596
-
19597
- <footer class="jv-dialog-footer">
19598
- <ng-content select="[dialog-actions]"></ng-content>
19599
- </footer>
19600
- </div>
19601
- </div>
19602
- }
19603
- `, isInline: true, styles: [".jv-dialog-backdrop{position:fixed;inset:0;z-index:1200;display:grid;place-items:center;padding:1rem;background:#0f172a80}.jv-dialog-panel{width:min(32rem,100%);max-height:calc(100vh - 2rem);overflow:auto;padding:var(--jv-spacing-lg);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-lg);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg)}.jv-dialog-header,.jv-dialog-body,.jv-dialog-footer{display:grid;gap:var(--jv-spacing-sm)}.jv-dialog-footer{margin-top:var(--jv-spacing-lg)}h2,p{margin:0}\n"] });
19604
- }
19605
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDialogComponent, decorators: [{
19606
- type: Component,
19607
- args: [{ selector: 'jv-dialog', standalone: true, template: `
19608
- @if (open()) {
19609
- <div class="jv-dialog-backdrop" (click)="backdropClose()">
19610
- <div class="jv-dialog-panel" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="descriptionId" (click)="$event.stopPropagation()" #panel>
19611
- <header class="jv-dialog-header">
19612
- <h2 [id]="titleId">{{ title() }}</h2>
19613
- </header>
19614
-
19615
- <div class="jv-dialog-body" [id]="descriptionId">
19616
- <ng-content></ng-content>
19617
- </div>
19618
-
19619
- <footer class="jv-dialog-footer">
19620
- <ng-content select="[dialog-actions]"></ng-content>
19621
- </footer>
19622
- </div>
19623
- </div>
19624
- }
19625
- `, styles: [".jv-dialog-backdrop{position:fixed;inset:0;z-index:1200;display:grid;place-items:center;padding:1rem;background:#0f172a80}.jv-dialog-panel{width:min(32rem,100%);max-height:calc(100vh - 2rem);overflow:auto;padding:var(--jv-spacing-lg);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-lg);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg)}.jv-dialog-header,.jv-dialog-body,.jv-dialog-footer{display:grid;gap:var(--jv-spacing-sm)}.jv-dialog-footer{margin-top:var(--jv-spacing-lg)}h2,p{margin:0}\n"] }]
19626
- }], ctorParameters: () => [], propDecorators: { panelRef: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], handleDocumentKeydown: [{
19627
- type: HostListener,
19628
- args: ['document:keydown', ['$event']]
19629
- }] } });
19630
-
19631
20727
  class JvConfirmDialogComponent {
19632
20728
  dialogService = inject(JvDialogService);
19633
20729
  activeDialog = this.dialogService.activeDialog;
@@ -19784,320 +20880,320 @@ class JvSidebarComponent {
19784
20880
  target?.scrollIntoView({ block: 'start', behavior: 'smooth' });
19785
20881
  }
19786
20882
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19787
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvSidebarComponent, isStandalone: true, selector: "jv-sidebar", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, brandIcon: { classPropertyName: "brandIcon", publicName: "brandIcon", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, mobileOpen: { classPropertyName: "mobileOpen", publicName: "mobileOpen", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, currentTheme: { classPropertyName: "currentTheme", publicName: "currentTheme", isSignal: true, isRequired: false, transformFunction: null }, themeOptions: { classPropertyName: "themeOptions", publicName: "themeOptions", isSignal: true, isRequired: false, transformFunction: null }, topbarActions: { classPropertyName: "topbarActions", publicName: "topbarActions", isSignal: true, isRequired: false, transformFunction: null }, showThemeSelector: { classPropertyName: "showThemeSelector", publicName: "showThemeSelector", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapseToggle: "collapseToggle", mobileClose: "mobileClose", themeChange: "themeChange", actionClick: "actionClick" }, ngImport: i0, template: `
19788
- @if (mobileOpen()) {
19789
- <div class="jv-sidebar-backdrop" (click)="mobileClose.emit()"></div>
19790
- }
19791
-
19792
- <aside
19793
- class="jv-sidebar"
19794
- [class.is-collapsed]="collapsed()"
19795
- [class.is-mobile-open]="mobileOpen()"
19796
- >
19797
- <div class="jv-sidebar-brand">
19798
- <div class="brand-mark">
19799
- <jv-icon [name]="brandIcon()" [size]="18" />
19800
- </div>
19801
-
19802
- @if (!collapsed()) {
19803
- <div class="brand-copy">
19804
- <strong>{{ title() }}</strong>
19805
- @if (subtitle()) {
19806
- <span>{{ subtitle() }}</span>
19807
- }
19808
- </div>
19809
- }
19810
- </div>
19811
-
19812
- <nav class="jv-sidebar-nav" [attr.aria-label]="ariaLabel()">
19813
- <ul class="jv-sidebar-list">
19814
- @for (item of normalizedItems(); track item.id) {
19815
- <li class="jv-sidebar-item">
19816
- @if (item.children.length > 0) {
19817
- <button
19818
- type="button"
19819
- class="jv-sidebar-link"
19820
- [class.is-active]="item.active"
19821
- [attr.aria-label]="item.label"
19822
- [attr.aria-expanded]="isExpanded(item.id) ? 'true' : 'false'"
19823
- [attr.aria-controls]="'jv-sidebar-section-' + item.id"
19824
- [attr.title]="item.label"
19825
- (click)="toggleSection(item.id)"
19826
- >
19827
- <span class="jv-sidebar-link-main">
19828
- @if (item.icon) {
19829
- <jv-icon [name]="item.icon" [size]="18" />
19830
- }
19831
-
19832
- @if (!collapsed()) {
19833
- <span>{{ item.label }}</span>
19834
- }
19835
- </span>
19836
-
19837
- @if (!collapsed()) {
19838
- <jv-icon [name]="isExpanded(item.id) ? 'chevron-up' : 'chevron-down'" [size]="16" />
19839
- }
19840
- </button>
19841
- } @else {
19842
- @if (isHashLink(item.href)) {
19843
- <button
19844
- type="button"
19845
- class="jv-sidebar-link"
19846
- [class.is-active]="item.active"
19847
- [attr.aria-label]="item.label"
19848
- [attr.aria-current]="item.active ? 'page' : null"
19849
- [attr.title]="item.label"
19850
- (click)="navigateTo(item.href); mobileClose.emit()"
19851
- >
19852
- <span class="jv-sidebar-link-main">
19853
- @if (item.icon) {
19854
- <jv-icon [name]="item.icon" [size]="18" />
19855
- }
19856
-
19857
- @if (!collapsed()) {
19858
- <span>{{ item.label }}</span>
19859
- }
19860
- </span>
19861
- </button>
19862
- } @else {
19863
- <a
19864
- class="jv-sidebar-link"
19865
- [class.is-active]="item.active"
19866
- [attr.aria-label]="item.label"
19867
- [attr.aria-current]="item.active ? 'page' : null"
19868
- [routerLink]="item.href"
19869
- [attr.title]="item.label"
19870
- (click)="mobileClose.emit()"
19871
- >
19872
- <span class="jv-sidebar-link-main">
19873
- @if (item.icon) {
19874
- <jv-icon [name]="item.icon" [size]="18" />
19875
- }
19876
-
19877
- @if (!collapsed()) {
19878
- <span>{{ item.label }}</span>
19879
- }
19880
- </span>
19881
- </a>
19882
- }
19883
- }
19884
-
19885
- @if (!collapsed() && item.children.length > 0 && isExpanded(item.id)) {
19886
- <div class="jv-sidebar-children" [id]="'jv-sidebar-section-' + item.id">
19887
- @for (child of item.children; track child.id) {
19888
- @if (isHashLink(child.href)) {
19889
- <button type="button" class="jv-sidebar-child-link" [class.is-active]="child.active" [attr.aria-label]="child.label" [attr.aria-current]="child.active ? 'page' : null" [attr.title]="child.label" (click)="navigateTo(child.href)">
19890
- {{ child.label }}
19891
- </button>
19892
- } @else {
19893
- <a class="jv-sidebar-child-link" [class.is-active]="child.active" [attr.aria-label]="child.label" [attr.aria-current]="child.active ? 'page' : null" [routerLink]="child.href" [attr.title]="child.label" (click)="mobileClose.emit()">
19894
- {{ child.label }}
19895
- </a>
19896
- }
19897
- }
19898
- </div>
19899
- }
19900
- </li>
19901
- }
19902
- </ul>
19903
- </nav>
19904
-
19905
- <div class="jv-sidebar-footer">
19906
- <button type="button" class="jv-sidebar-toggle" (click)="collapseToggle.emit()">
19907
- <jv-icon [name]="collapsed() ? 'menu' : 'panel-left-close'" [size]="18" />
19908
- @if (!collapsed()) {
19909
- <span>Collapse</span>
19910
- }
19911
- </button>
19912
-
19913
- <div class="jv-sidebar-mobile-extras">
19914
- @if (showThemeSelector()) {
19915
- <div class="theme-group" aria-label="Theme selector">
19916
- @for (option of themeOptions(); track option.value) {
19917
- <button
19918
- type="button"
19919
- class="theme-pill"
19920
- [class.is-active]="currentTheme() === option.value"
19921
- (click)="themeChange.emit(option.value)"
19922
- >
19923
- @if (option.icon) {
19924
- <jv-icon [name]="option.icon" [size]="16" />
19925
- }
19926
- <span>{{ option.label }}</span>
19927
- </button>
19928
- }
19929
- </div>
19930
- }
19931
-
19932
- <div class="mobile-actions">
19933
- @for (action of topbarActions(); track action.id) {
19934
- <button type="button" class="icon-button" [attr.aria-label]="action.label" (click)="actionClick.emit(action.id); mobileClose.emit()">
19935
- <jv-icon [name]="action.icon" [size]="18" />
19936
- </button>
19937
- }
19938
- </div>
19939
- </div>
19940
- </div>
19941
- </aside>
19942
- `, isInline: true, styles: [".jv-sidebar-backdrop{display:none}.jv-sidebar{display:grid;grid-template-rows:auto 1fr auto;gap:var(--jv-spacing-lg);width:280px;min-width:280px;height:100vh;padding:var(--jv-spacing-lg);overflow-y:auto;border-right:1px solid var(--jv-color-border);background:var(--jv-color-surface);scrollbar-width:none;-ms-overflow-style:none}.jv-sidebar::-webkit-scrollbar{width:0;height:0;display:none}.jv-sidebar.is-collapsed{width:96px;min-width:96px;padding-inline:var(--jv-spacing-md)}.jv-sidebar-footer{display:grid;gap:var(--jv-spacing-md)}.jv-sidebar-mobile-extras{display:none}.jv-sidebar-brand,.jv-sidebar-link,.jv-sidebar-toggle{display:flex;align-items:center;gap:var(--jv-spacing-sm)}.brand-mark{display:inline-grid;place-items:center;width:2.5rem;height:2.5rem;border-radius:var(--jv-radius-md);background:color-mix(in srgb,var(--jv-color-primary) 16%,var(--jv-color-surface));color:var(--jv-color-primary)}.brand-copy{display:grid;gap:.1rem}.brand-copy span{color:var(--jv-color-foreground-muted);font-size:.875rem}.jv-sidebar-item{display:grid;gap:var(--jv-spacing-xs);list-style:none}.jv-sidebar-list{display:grid;gap:var(--jv-spacing-xs);margin:0;padding:0}.jv-sidebar-nav{display:grid;gap:var(--jv-spacing-xs);align-content:start}.jv-sidebar-link,.jv-sidebar-child-link,.jv-sidebar-toggle{width:100%;display:flex;align-items:center;justify-content:space-between;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:1px solid transparent;border-radius:var(--jv-radius-md);background:transparent;color:var(--jv-color-foreground-muted);text-decoration:none;cursor:pointer}.jv-sidebar-link-main{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm)}.jv-sidebar-link:hover,.jv-sidebar-child-link:hover,.jv-sidebar-toggle:hover{background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.jv-sidebar-link.is-active,.jv-sidebar-child-link.is-active{border-color:color-mix(in srgb,var(--jv-color-primary) 25%,var(--jv-color-border));background:color-mix(in srgb,var(--jv-color-primary) 12%,var(--jv-color-surface));color:var(--jv-color-foreground)}.jv-sidebar-children{display:grid;gap:var(--jv-spacing-xs);margin-left:var(--jv-spacing-xl);padding-left:var(--jv-spacing-sm);border-left:1px solid var(--jv-color-border)}.jv-sidebar-child-link{display:flex;align-items:center;min-height:2.25rem;font-size:.9rem}.jv-sidebar-toggle{width:100%;background:var(--jv-color-surface-muted);cursor:pointer}@media(max-width:900px){.jv-sidebar-backdrop{display:block;position:fixed;inset:0;z-index:99;background:#0006}.jv-sidebar{position:fixed;top:0;left:0;z-index:100;width:280px;min-width:280px;height:100vh;border-right:1px solid var(--jv-color-border);border-bottom:none;box-shadow:var(--jv-shadow-lg);transform:translate(-100%);visibility:hidden;transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}.jv-sidebar.is-mobile-open{transform:translate(0);visibility:visible}.jv-sidebar.is-collapsed{width:280px;min-width:280px}.jv-sidebar-mobile-extras{display:flex;flex-direction:column;gap:var(--jv-spacing-md);padding-top:var(--jv-spacing-md);border-top:1px solid var(--jv-color-border)}.jv-sidebar-mobile-extras .theme-group{display:flex;flex-wrap:wrap;gap:var(--jv-spacing-xs)}.jv-sidebar-mobile-extras .theme-pill{display:inline-flex;align-items:center;gap:var(--jv-spacing-xs);min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);cursor:pointer;font:inherit}.jv-sidebar-mobile-extras .theme-pill.is-active{border-color:var(--jv-color-primary);background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface))}.jv-sidebar-mobile-extras .mobile-actions{display:flex;gap:var(--jv-spacing-sm)}.jv-sidebar-mobile-extras .icon-button{display:inline-grid;place-items:center;width:var(--jv-density-control-height);height:var(--jv-density-control-height);border:1px solid var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);cursor:pointer}}\n"], dependencies: [{ kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] });
19943
- }
19944
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSidebarComponent, decorators: [{
19945
- type: Component,
19946
- args: [{ selector: 'jv-sidebar', standalone: true, imports: [JvIconComponent, RouterLink], template: `
19947
- @if (mobileOpen()) {
19948
- <div class="jv-sidebar-backdrop" (click)="mobileClose.emit()"></div>
19949
- }
19950
-
19951
- <aside
19952
- class="jv-sidebar"
19953
- [class.is-collapsed]="collapsed()"
19954
- [class.is-mobile-open]="mobileOpen()"
19955
- >
19956
- <div class="jv-sidebar-brand">
19957
- <div class="brand-mark">
19958
- <jv-icon [name]="brandIcon()" [size]="18" />
19959
- </div>
19960
-
19961
- @if (!collapsed()) {
19962
- <div class="brand-copy">
19963
- <strong>{{ title() }}</strong>
19964
- @if (subtitle()) {
19965
- <span>{{ subtitle() }}</span>
19966
- }
19967
- </div>
19968
- }
19969
- </div>
19970
-
19971
- <nav class="jv-sidebar-nav" [attr.aria-label]="ariaLabel()">
19972
- <ul class="jv-sidebar-list">
19973
- @for (item of normalizedItems(); track item.id) {
19974
- <li class="jv-sidebar-item">
19975
- @if (item.children.length > 0) {
19976
- <button
19977
- type="button"
19978
- class="jv-sidebar-link"
19979
- [class.is-active]="item.active"
19980
- [attr.aria-label]="item.label"
19981
- [attr.aria-expanded]="isExpanded(item.id) ? 'true' : 'false'"
19982
- [attr.aria-controls]="'jv-sidebar-section-' + item.id"
19983
- [attr.title]="item.label"
19984
- (click)="toggleSection(item.id)"
19985
- >
19986
- <span class="jv-sidebar-link-main">
19987
- @if (item.icon) {
19988
- <jv-icon [name]="item.icon" [size]="18" />
19989
- }
19990
-
19991
- @if (!collapsed()) {
19992
- <span>{{ item.label }}</span>
19993
- }
19994
- </span>
19995
-
19996
- @if (!collapsed()) {
19997
- <jv-icon [name]="isExpanded(item.id) ? 'chevron-up' : 'chevron-down'" [size]="16" />
19998
- }
19999
- </button>
20000
- } @else {
20001
- @if (isHashLink(item.href)) {
20002
- <button
20003
- type="button"
20004
- class="jv-sidebar-link"
20005
- [class.is-active]="item.active"
20006
- [attr.aria-label]="item.label"
20007
- [attr.aria-current]="item.active ? 'page' : null"
20008
- [attr.title]="item.label"
20009
- (click)="navigateTo(item.href); mobileClose.emit()"
20010
- >
20011
- <span class="jv-sidebar-link-main">
20012
- @if (item.icon) {
20013
- <jv-icon [name]="item.icon" [size]="18" />
20014
- }
20015
-
20016
- @if (!collapsed()) {
20017
- <span>{{ item.label }}</span>
20018
- }
20019
- </span>
20020
- </button>
20021
- } @else {
20022
- <a
20023
- class="jv-sidebar-link"
20024
- [class.is-active]="item.active"
20025
- [attr.aria-label]="item.label"
20026
- [attr.aria-current]="item.active ? 'page' : null"
20027
- [routerLink]="item.href"
20028
- [attr.title]="item.label"
20029
- (click)="mobileClose.emit()"
20030
- >
20031
- <span class="jv-sidebar-link-main">
20032
- @if (item.icon) {
20033
- <jv-icon [name]="item.icon" [size]="18" />
20034
- }
20035
-
20036
- @if (!collapsed()) {
20037
- <span>{{ item.label }}</span>
20038
- }
20039
- </span>
20040
- </a>
20041
- }
20042
- }
20043
-
20044
- @if (!collapsed() && item.children.length > 0 && isExpanded(item.id)) {
20045
- <div class="jv-sidebar-children" [id]="'jv-sidebar-section-' + item.id">
20046
- @for (child of item.children; track child.id) {
20047
- @if (isHashLink(child.href)) {
20048
- <button type="button" class="jv-sidebar-child-link" [class.is-active]="child.active" [attr.aria-label]="child.label" [attr.aria-current]="child.active ? 'page' : null" [attr.title]="child.label" (click)="navigateTo(child.href)">
20049
- {{ child.label }}
20050
- </button>
20051
- } @else {
20052
- <a class="jv-sidebar-child-link" [class.is-active]="child.active" [attr.aria-label]="child.label" [attr.aria-current]="child.active ? 'page' : null" [routerLink]="child.href" [attr.title]="child.label" (click)="mobileClose.emit()">
20053
- {{ child.label }}
20054
- </a>
20055
- }
20056
- }
20057
- </div>
20058
- }
20059
- </li>
20060
- }
20061
- </ul>
20062
- </nav>
20063
-
20064
- <div class="jv-sidebar-footer">
20065
- <button type="button" class="jv-sidebar-toggle" (click)="collapseToggle.emit()">
20066
- <jv-icon [name]="collapsed() ? 'menu' : 'panel-left-close'" [size]="18" />
20067
- @if (!collapsed()) {
20068
- <span>Collapse</span>
20069
- }
20070
- </button>
20071
-
20072
- <div class="jv-sidebar-mobile-extras">
20073
- @if (showThemeSelector()) {
20074
- <div class="theme-group" aria-label="Theme selector">
20075
- @for (option of themeOptions(); track option.value) {
20076
- <button
20077
- type="button"
20078
- class="theme-pill"
20079
- [class.is-active]="currentTheme() === option.value"
20080
- (click)="themeChange.emit(option.value)"
20081
- >
20082
- @if (option.icon) {
20083
- <jv-icon [name]="option.icon" [size]="16" />
20084
- }
20085
- <span>{{ option.label }}</span>
20086
- </button>
20087
- }
20088
- </div>
20089
- }
20090
-
20091
- <div class="mobile-actions">
20092
- @for (action of topbarActions(); track action.id) {
20093
- <button type="button" class="icon-button" [attr.aria-label]="action.label" (click)="actionClick.emit(action.id); mobileClose.emit()">
20094
- <jv-icon [name]="action.icon" [size]="18" />
20095
- </button>
20096
- }
20097
- </div>
20098
- </div>
20099
- </div>
20100
- </aside>
20883
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvSidebarComponent, isStandalone: true, selector: "jv-sidebar", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, brandIcon: { classPropertyName: "brandIcon", publicName: "brandIcon", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, mobileOpen: { classPropertyName: "mobileOpen", publicName: "mobileOpen", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, currentTheme: { classPropertyName: "currentTheme", publicName: "currentTheme", isSignal: true, isRequired: false, transformFunction: null }, themeOptions: { classPropertyName: "themeOptions", publicName: "themeOptions", isSignal: true, isRequired: false, transformFunction: null }, topbarActions: { classPropertyName: "topbarActions", publicName: "topbarActions", isSignal: true, isRequired: false, transformFunction: null }, showThemeSelector: { classPropertyName: "showThemeSelector", publicName: "showThemeSelector", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapseToggle: "collapseToggle", mobileClose: "mobileClose", themeChange: "themeChange", actionClick: "actionClick" }, ngImport: i0, template: `
20884
+ @if (mobileOpen()) {
20885
+ <div class="jv-sidebar-backdrop" (click)="mobileClose.emit()"></div>
20886
+ }
20887
+
20888
+ <aside
20889
+ class="jv-sidebar"
20890
+ [class.is-collapsed]="collapsed()"
20891
+ [class.is-mobile-open]="mobileOpen()"
20892
+ >
20893
+ <div class="jv-sidebar-brand">
20894
+ <div class="brand-mark">
20895
+ <jv-icon [name]="brandIcon()" [size]="18" />
20896
+ </div>
20897
+
20898
+ @if (!collapsed()) {
20899
+ <div class="brand-copy">
20900
+ <strong>{{ title() }}</strong>
20901
+ @if (subtitle()) {
20902
+ <span>{{ subtitle() }}</span>
20903
+ }
20904
+ </div>
20905
+ }
20906
+ </div>
20907
+
20908
+ <nav class="jv-sidebar-nav" [attr.aria-label]="ariaLabel()">
20909
+ <ul class="jv-sidebar-list">
20910
+ @for (item of normalizedItems(); track item.id) {
20911
+ <li class="jv-sidebar-item">
20912
+ @if (item.children.length > 0) {
20913
+ <button
20914
+ type="button"
20915
+ class="jv-sidebar-link"
20916
+ [class.is-active]="item.active"
20917
+ [attr.aria-label]="item.label"
20918
+ [attr.aria-expanded]="isExpanded(item.id) ? 'true' : 'false'"
20919
+ [attr.aria-controls]="'jv-sidebar-section-' + item.id"
20920
+ [attr.title]="item.label"
20921
+ (click)="toggleSection(item.id)"
20922
+ >
20923
+ <span class="jv-sidebar-link-main">
20924
+ @if (item.icon) {
20925
+ <jv-icon [name]="item.icon" [size]="18" />
20926
+ }
20927
+
20928
+ @if (!collapsed()) {
20929
+ <span>{{ item.label }}</span>
20930
+ }
20931
+ </span>
20932
+
20933
+ @if (!collapsed()) {
20934
+ <jv-icon [name]="isExpanded(item.id) ? 'chevron-up' : 'chevron-down'" [size]="16" />
20935
+ }
20936
+ </button>
20937
+ } @else {
20938
+ @if (isHashLink(item.href)) {
20939
+ <button
20940
+ type="button"
20941
+ class="jv-sidebar-link"
20942
+ [class.is-active]="item.active"
20943
+ [attr.aria-label]="item.label"
20944
+ [attr.aria-current]="item.active ? 'page' : null"
20945
+ [attr.title]="item.label"
20946
+ (click)="navigateTo(item.href); mobileClose.emit()"
20947
+ >
20948
+ <span class="jv-sidebar-link-main">
20949
+ @if (item.icon) {
20950
+ <jv-icon [name]="item.icon" [size]="18" />
20951
+ }
20952
+
20953
+ @if (!collapsed()) {
20954
+ <span>{{ item.label }}</span>
20955
+ }
20956
+ </span>
20957
+ </button>
20958
+ } @else {
20959
+ <a
20960
+ class="jv-sidebar-link"
20961
+ [class.is-active]="item.active"
20962
+ [attr.aria-label]="item.label"
20963
+ [attr.aria-current]="item.active ? 'page' : null"
20964
+ [routerLink]="item.href"
20965
+ [attr.title]="item.label"
20966
+ (click)="mobileClose.emit()"
20967
+ >
20968
+ <span class="jv-sidebar-link-main">
20969
+ @if (item.icon) {
20970
+ <jv-icon [name]="item.icon" [size]="18" />
20971
+ }
20972
+
20973
+ @if (!collapsed()) {
20974
+ <span>{{ item.label }}</span>
20975
+ }
20976
+ </span>
20977
+ </a>
20978
+ }
20979
+ }
20980
+
20981
+ @if (!collapsed() && item.children.length > 0 && isExpanded(item.id)) {
20982
+ <div class="jv-sidebar-children" [id]="'jv-sidebar-section-' + item.id">
20983
+ @for (child of item.children; track child.id) {
20984
+ @if (isHashLink(child.href)) {
20985
+ <button type="button" class="jv-sidebar-child-link" [class.is-active]="child.active" [attr.aria-label]="child.label" [attr.aria-current]="child.active ? 'page' : null" [attr.title]="child.label" (click)="navigateTo(child.href)">
20986
+ {{ child.label }}
20987
+ </button>
20988
+ } @else {
20989
+ <a class="jv-sidebar-child-link" [class.is-active]="child.active" [attr.aria-label]="child.label" [attr.aria-current]="child.active ? 'page' : null" [routerLink]="child.href" [attr.title]="child.label" (click)="mobileClose.emit()">
20990
+ {{ child.label }}
20991
+ </a>
20992
+ }
20993
+ }
20994
+ </div>
20995
+ }
20996
+ </li>
20997
+ }
20998
+ </ul>
20999
+ </nav>
21000
+
21001
+ <div class="jv-sidebar-footer">
21002
+ <button type="button" class="jv-sidebar-toggle" (click)="collapseToggle.emit()">
21003
+ <jv-icon [name]="collapsed() ? 'menu' : 'panel-left-close'" [size]="18" />
21004
+ @if (!collapsed()) {
21005
+ <span>Collapse</span>
21006
+ }
21007
+ </button>
21008
+
21009
+ <div class="jv-sidebar-mobile-extras">
21010
+ @if (showThemeSelector()) {
21011
+ <div class="theme-group" aria-label="Theme selector">
21012
+ @for (option of themeOptions(); track option.value) {
21013
+ <button
21014
+ type="button"
21015
+ class="theme-pill"
21016
+ [class.is-active]="currentTheme() === option.value"
21017
+ (click)="themeChange.emit(option.value)"
21018
+ >
21019
+ @if (option.icon) {
21020
+ <jv-icon [name]="option.icon" [size]="16" />
21021
+ }
21022
+ <span>{{ option.label }}</span>
21023
+ </button>
21024
+ }
21025
+ </div>
21026
+ }
21027
+
21028
+ <div class="mobile-actions">
21029
+ @for (action of topbarActions(); track action.id) {
21030
+ <button type="button" class="icon-button" [attr.aria-label]="action.label" (click)="actionClick.emit(action.id); mobileClose.emit()">
21031
+ <jv-icon [name]="action.icon" [size]="18" />
21032
+ </button>
21033
+ }
21034
+ </div>
21035
+ </div>
21036
+ </div>
21037
+ </aside>
21038
+ `, isInline: true, styles: [".jv-sidebar-backdrop{display:none}.jv-sidebar{display:grid;grid-template-rows:auto 1fr auto;gap:var(--jv-spacing-lg);width:280px;min-width:280px;height:100vh;padding:var(--jv-spacing-lg);overflow-y:auto;border-right:1px solid var(--jv-color-border);background:var(--jv-color-surface);scrollbar-width:none;-ms-overflow-style:none}.jv-sidebar::-webkit-scrollbar{width:0;height:0;display:none}.jv-sidebar.is-collapsed{width:96px;min-width:96px;padding-inline:var(--jv-spacing-md)}.jv-sidebar-footer{display:grid;gap:var(--jv-spacing-md)}.jv-sidebar-mobile-extras{display:none}.jv-sidebar-brand,.jv-sidebar-link,.jv-sidebar-toggle{display:flex;align-items:center;gap:var(--jv-spacing-sm)}.brand-mark{display:inline-grid;place-items:center;width:2.5rem;height:2.5rem;border-radius:var(--jv-radius-md);background:color-mix(in srgb,var(--jv-color-primary) 16%,var(--jv-color-surface));color:var(--jv-color-primary)}.brand-copy{display:grid;gap:.1rem}.brand-copy span{color:var(--jv-color-foreground-muted);font-size:.875rem}.jv-sidebar-item{display:grid;gap:var(--jv-spacing-xs);list-style:none}.jv-sidebar-list{display:grid;gap:var(--jv-spacing-xs);margin:0;padding:0}.jv-sidebar-nav{display:grid;gap:var(--jv-spacing-xs);align-content:start}.jv-sidebar-link,.jv-sidebar-child-link,.jv-sidebar-toggle{width:100%;display:flex;align-items:center;justify-content:space-between;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:1px solid transparent;border-radius:var(--jv-radius-md);background:transparent;color:var(--jv-color-foreground-muted);text-decoration:none;cursor:pointer}.jv-sidebar-link-main{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm)}.jv-sidebar-link:hover,.jv-sidebar-child-link:hover,.jv-sidebar-toggle:hover{background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.jv-sidebar-link.is-active,.jv-sidebar-child-link.is-active{border-color:color-mix(in srgb,var(--jv-color-primary) 25%,var(--jv-color-border));background:color-mix(in srgb,var(--jv-color-primary) 12%,var(--jv-color-surface));color:var(--jv-color-foreground)}.jv-sidebar-children{display:grid;gap:var(--jv-spacing-xs);margin-left:var(--jv-spacing-xl);padding-left:var(--jv-spacing-sm);border-left:1px solid var(--jv-color-border)}.jv-sidebar-child-link{display:flex;align-items:center;min-height:2.25rem;font-size:.9rem}.jv-sidebar-toggle{width:100%;background:var(--jv-color-surface-muted);cursor:pointer}@media(max-width:900px){.jv-sidebar-backdrop{display:block;position:fixed;inset:0;z-index:99;background:#0006}.jv-sidebar{position:fixed;top:0;left:0;z-index:100;width:280px;min-width:280px;height:100vh;border-right:1px solid var(--jv-color-border);border-bottom:none;box-shadow:var(--jv-shadow-lg);transform:translate(-100%);visibility:hidden;transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}.jv-sidebar.is-mobile-open{transform:translate(0);visibility:visible}.jv-sidebar.is-collapsed{width:280px;min-width:280px}.jv-sidebar-mobile-extras{display:flex;flex-direction:column;gap:var(--jv-spacing-md);padding-top:var(--jv-spacing-md);border-top:1px solid var(--jv-color-border)}.jv-sidebar-mobile-extras .theme-group{display:flex;flex-wrap:wrap;gap:var(--jv-spacing-xs)}.jv-sidebar-mobile-extras .theme-pill{display:inline-flex;align-items:center;gap:var(--jv-spacing-xs);min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);cursor:pointer;font:inherit}.jv-sidebar-mobile-extras .theme-pill.is-active{border-color:var(--jv-color-primary);background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface))}.jv-sidebar-mobile-extras .mobile-actions{display:flex;gap:var(--jv-spacing-sm)}.jv-sidebar-mobile-extras .icon-button{display:inline-grid;place-items:center;width:var(--jv-density-control-height);height:var(--jv-density-control-height);border:1px solid var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);cursor:pointer}}\n"], dependencies: [{ kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] });
21039
+ }
21040
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSidebarComponent, decorators: [{
21041
+ type: Component,
21042
+ args: [{ selector: 'jv-sidebar', standalone: true, imports: [JvIconComponent, RouterLink], template: `
21043
+ @if (mobileOpen()) {
21044
+ <div class="jv-sidebar-backdrop" (click)="mobileClose.emit()"></div>
21045
+ }
21046
+
21047
+ <aside
21048
+ class="jv-sidebar"
21049
+ [class.is-collapsed]="collapsed()"
21050
+ [class.is-mobile-open]="mobileOpen()"
21051
+ >
21052
+ <div class="jv-sidebar-brand">
21053
+ <div class="brand-mark">
21054
+ <jv-icon [name]="brandIcon()" [size]="18" />
21055
+ </div>
21056
+
21057
+ @if (!collapsed()) {
21058
+ <div class="brand-copy">
21059
+ <strong>{{ title() }}</strong>
21060
+ @if (subtitle()) {
21061
+ <span>{{ subtitle() }}</span>
21062
+ }
21063
+ </div>
21064
+ }
21065
+ </div>
21066
+
21067
+ <nav class="jv-sidebar-nav" [attr.aria-label]="ariaLabel()">
21068
+ <ul class="jv-sidebar-list">
21069
+ @for (item of normalizedItems(); track item.id) {
21070
+ <li class="jv-sidebar-item">
21071
+ @if (item.children.length > 0) {
21072
+ <button
21073
+ type="button"
21074
+ class="jv-sidebar-link"
21075
+ [class.is-active]="item.active"
21076
+ [attr.aria-label]="item.label"
21077
+ [attr.aria-expanded]="isExpanded(item.id) ? 'true' : 'false'"
21078
+ [attr.aria-controls]="'jv-sidebar-section-' + item.id"
21079
+ [attr.title]="item.label"
21080
+ (click)="toggleSection(item.id)"
21081
+ >
21082
+ <span class="jv-sidebar-link-main">
21083
+ @if (item.icon) {
21084
+ <jv-icon [name]="item.icon" [size]="18" />
21085
+ }
21086
+
21087
+ @if (!collapsed()) {
21088
+ <span>{{ item.label }}</span>
21089
+ }
21090
+ </span>
21091
+
21092
+ @if (!collapsed()) {
21093
+ <jv-icon [name]="isExpanded(item.id) ? 'chevron-up' : 'chevron-down'" [size]="16" />
21094
+ }
21095
+ </button>
21096
+ } @else {
21097
+ @if (isHashLink(item.href)) {
21098
+ <button
21099
+ type="button"
21100
+ class="jv-sidebar-link"
21101
+ [class.is-active]="item.active"
21102
+ [attr.aria-label]="item.label"
21103
+ [attr.aria-current]="item.active ? 'page' : null"
21104
+ [attr.title]="item.label"
21105
+ (click)="navigateTo(item.href); mobileClose.emit()"
21106
+ >
21107
+ <span class="jv-sidebar-link-main">
21108
+ @if (item.icon) {
21109
+ <jv-icon [name]="item.icon" [size]="18" />
21110
+ }
21111
+
21112
+ @if (!collapsed()) {
21113
+ <span>{{ item.label }}</span>
21114
+ }
21115
+ </span>
21116
+ </button>
21117
+ } @else {
21118
+ <a
21119
+ class="jv-sidebar-link"
21120
+ [class.is-active]="item.active"
21121
+ [attr.aria-label]="item.label"
21122
+ [attr.aria-current]="item.active ? 'page' : null"
21123
+ [routerLink]="item.href"
21124
+ [attr.title]="item.label"
21125
+ (click)="mobileClose.emit()"
21126
+ >
21127
+ <span class="jv-sidebar-link-main">
21128
+ @if (item.icon) {
21129
+ <jv-icon [name]="item.icon" [size]="18" />
21130
+ }
21131
+
21132
+ @if (!collapsed()) {
21133
+ <span>{{ item.label }}</span>
21134
+ }
21135
+ </span>
21136
+ </a>
21137
+ }
21138
+ }
21139
+
21140
+ @if (!collapsed() && item.children.length > 0 && isExpanded(item.id)) {
21141
+ <div class="jv-sidebar-children" [id]="'jv-sidebar-section-' + item.id">
21142
+ @for (child of item.children; track child.id) {
21143
+ @if (isHashLink(child.href)) {
21144
+ <button type="button" class="jv-sidebar-child-link" [class.is-active]="child.active" [attr.aria-label]="child.label" [attr.aria-current]="child.active ? 'page' : null" [attr.title]="child.label" (click)="navigateTo(child.href)">
21145
+ {{ child.label }}
21146
+ </button>
21147
+ } @else {
21148
+ <a class="jv-sidebar-child-link" [class.is-active]="child.active" [attr.aria-label]="child.label" [attr.aria-current]="child.active ? 'page' : null" [routerLink]="child.href" [attr.title]="child.label" (click)="mobileClose.emit()">
21149
+ {{ child.label }}
21150
+ </a>
21151
+ }
21152
+ }
21153
+ </div>
21154
+ }
21155
+ </li>
21156
+ }
21157
+ </ul>
21158
+ </nav>
21159
+
21160
+ <div class="jv-sidebar-footer">
21161
+ <button type="button" class="jv-sidebar-toggle" (click)="collapseToggle.emit()">
21162
+ <jv-icon [name]="collapsed() ? 'menu' : 'panel-left-close'" [size]="18" />
21163
+ @if (!collapsed()) {
21164
+ <span>Collapse</span>
21165
+ }
21166
+ </button>
21167
+
21168
+ <div class="jv-sidebar-mobile-extras">
21169
+ @if (showThemeSelector()) {
21170
+ <div class="theme-group" aria-label="Theme selector">
21171
+ @for (option of themeOptions(); track option.value) {
21172
+ <button
21173
+ type="button"
21174
+ class="theme-pill"
21175
+ [class.is-active]="currentTheme() === option.value"
21176
+ (click)="themeChange.emit(option.value)"
21177
+ >
21178
+ @if (option.icon) {
21179
+ <jv-icon [name]="option.icon" [size]="16" />
21180
+ }
21181
+ <span>{{ option.label }}</span>
21182
+ </button>
21183
+ }
21184
+ </div>
21185
+ }
21186
+
21187
+ <div class="mobile-actions">
21188
+ @for (action of topbarActions(); track action.id) {
21189
+ <button type="button" class="icon-button" [attr.aria-label]="action.label" (click)="actionClick.emit(action.id); mobileClose.emit()">
21190
+ <jv-icon [name]="action.icon" [size]="18" />
21191
+ </button>
21192
+ }
21193
+ </div>
21194
+ </div>
21195
+ </div>
21196
+ </aside>
20101
21197
  `, styles: [".jv-sidebar-backdrop{display:none}.jv-sidebar{display:grid;grid-template-rows:auto 1fr auto;gap:var(--jv-spacing-lg);width:280px;min-width:280px;height:100vh;padding:var(--jv-spacing-lg);overflow-y:auto;border-right:1px solid var(--jv-color-border);background:var(--jv-color-surface);scrollbar-width:none;-ms-overflow-style:none}.jv-sidebar::-webkit-scrollbar{width:0;height:0;display:none}.jv-sidebar.is-collapsed{width:96px;min-width:96px;padding-inline:var(--jv-spacing-md)}.jv-sidebar-footer{display:grid;gap:var(--jv-spacing-md)}.jv-sidebar-mobile-extras{display:none}.jv-sidebar-brand,.jv-sidebar-link,.jv-sidebar-toggle{display:flex;align-items:center;gap:var(--jv-spacing-sm)}.brand-mark{display:inline-grid;place-items:center;width:2.5rem;height:2.5rem;border-radius:var(--jv-radius-md);background:color-mix(in srgb,var(--jv-color-primary) 16%,var(--jv-color-surface));color:var(--jv-color-primary)}.brand-copy{display:grid;gap:.1rem}.brand-copy span{color:var(--jv-color-foreground-muted);font-size:.875rem}.jv-sidebar-item{display:grid;gap:var(--jv-spacing-xs);list-style:none}.jv-sidebar-list{display:grid;gap:var(--jv-spacing-xs);margin:0;padding:0}.jv-sidebar-nav{display:grid;gap:var(--jv-spacing-xs);align-content:start}.jv-sidebar-link,.jv-sidebar-child-link,.jv-sidebar-toggle{width:100%;display:flex;align-items:center;justify-content:space-between;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:1px solid transparent;border-radius:var(--jv-radius-md);background:transparent;color:var(--jv-color-foreground-muted);text-decoration:none;cursor:pointer}.jv-sidebar-link-main{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm)}.jv-sidebar-link:hover,.jv-sidebar-child-link:hover,.jv-sidebar-toggle:hover{background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.jv-sidebar-link.is-active,.jv-sidebar-child-link.is-active{border-color:color-mix(in srgb,var(--jv-color-primary) 25%,var(--jv-color-border));background:color-mix(in srgb,var(--jv-color-primary) 12%,var(--jv-color-surface));color:var(--jv-color-foreground)}.jv-sidebar-children{display:grid;gap:var(--jv-spacing-xs);margin-left:var(--jv-spacing-xl);padding-left:var(--jv-spacing-sm);border-left:1px solid var(--jv-color-border)}.jv-sidebar-child-link{display:flex;align-items:center;min-height:2.25rem;font-size:.9rem}.jv-sidebar-toggle{width:100%;background:var(--jv-color-surface-muted);cursor:pointer}@media(max-width:900px){.jv-sidebar-backdrop{display:block;position:fixed;inset:0;z-index:99;background:#0006}.jv-sidebar{position:fixed;top:0;left:0;z-index:100;width:280px;min-width:280px;height:100vh;border-right:1px solid var(--jv-color-border);border-bottom:none;box-shadow:var(--jv-shadow-lg);transform:translate(-100%);visibility:hidden;transition:transform .24s cubic-bezier(.4,0,.2,1),visibility .24s}.jv-sidebar.is-mobile-open{transform:translate(0);visibility:visible}.jv-sidebar.is-collapsed{width:280px;min-width:280px}.jv-sidebar-mobile-extras{display:flex;flex-direction:column;gap:var(--jv-spacing-md);padding-top:var(--jv-spacing-md);border-top:1px solid var(--jv-color-border)}.jv-sidebar-mobile-extras .theme-group{display:flex;flex-wrap:wrap;gap:var(--jv-spacing-xs)}.jv-sidebar-mobile-extras .theme-pill{display:inline-flex;align-items:center;gap:var(--jv-spacing-xs);min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);cursor:pointer;font:inherit}.jv-sidebar-mobile-extras .theme-pill.is-active{border-color:var(--jv-color-primary);background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface))}.jv-sidebar-mobile-extras .mobile-actions{display:flex;gap:var(--jv-spacing-sm)}.jv-sidebar-mobile-extras .icon-button{display:inline-grid;place-items:center;width:var(--jv-density-control-height);height:var(--jv-density-control-height);border:1px solid var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);cursor:pointer}}\n"] }]
20102
21198
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], brandIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandIcon", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], mobileOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileOpen", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], currentTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentTheme", required: false }] }], themeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "themeOptions", required: false }] }], topbarActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "topbarActions", required: false }] }], showThemeSelector: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThemeSelector", required: false }] }], collapseToggle: [{ type: i0.Output, args: ["collapseToggle"] }], mobileClose: [{ type: i0.Output, args: ["mobileClose"] }], themeChange: [{ type: i0.Output, args: ["themeChange"] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }] } });
20103
21199
 
@@ -20229,88 +21325,88 @@ class JvDashboardShellComponent {
20229
21325
  themeChange = output();
20230
21326
  topbarActionClick = output();
20231
21327
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDashboardShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20232
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: JvDashboardShellComponent, isStandalone: true, selector: "jv-dashboard-shell", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, sidebarSubtitle: { classPropertyName: "sidebarSubtitle", publicName: "sidebarSubtitle", isSignal: true, isRequired: false, transformFunction: null }, brandIcon: { classPropertyName: "brandIcon", publicName: "brandIcon", isSignal: true, isRequired: false, transformFunction: null }, navItems: { classPropertyName: "navItems", publicName: "navItems", isSignal: true, isRequired: false, transformFunction: null }, sidebarCollapsed: { classPropertyName: "sidebarCollapsed", publicName: "sidebarCollapsed", isSignal: true, isRequired: false, transformFunction: null }, mobileSidebarOpen: { classPropertyName: "mobileSidebarOpen", publicName: "mobileSidebarOpen", isSignal: true, isRequired: false, transformFunction: null }, topbarTitle: { classPropertyName: "topbarTitle", publicName: "topbarTitle", isSignal: true, isRequired: false, transformFunction: null }, topbarSubtitle: { classPropertyName: "topbarSubtitle", publicName: "topbarSubtitle", isSignal: true, isRequired: false, transformFunction: null }, currentTheme: { classPropertyName: "currentTheme", publicName: "currentTheme", isSignal: true, isRequired: false, transformFunction: null }, themeOptions: { classPropertyName: "themeOptions", publicName: "themeOptions", isSignal: true, isRequired: false, transformFunction: null }, topbarActions: { classPropertyName: "topbarActions", publicName: "topbarActions", isSignal: true, isRequired: false, transformFunction: null }, showThemeSelector: { classPropertyName: "showThemeSelector", publicName: "showThemeSelector", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuClick: "menuClick", mobileClose: "mobileClose", sidebarCollapseToggle: "sidebarCollapseToggle", themeChange: "themeChange", topbarActionClick: "topbarActionClick" }, ngImport: i0, template: `
20233
- <div class="jv-dashboard-shell" [class.is-sidebar-collapsed]="sidebarCollapsed()">
20234
- <jv-sidebar
20235
- [title]="title()"
20236
- [subtitle]="sidebarSubtitle()"
20237
- [brandIcon]="brandIcon()"
20238
- [items]="navItems()"
20239
- [collapsed]="sidebarCollapsed()"
20240
- [mobileOpen]="mobileSidebarOpen()"
20241
- [currentTheme]="currentTheme()"
20242
- [themeOptions]="themeOptions()"
20243
- [topbarActions]="topbarActions()"
20244
- [showThemeSelector]="showThemeSelector()"
20245
- (collapseToggle)="sidebarCollapseToggle.emit()"
20246
- (mobileClose)="mobileClose.emit()"
20247
- (themeChange)="themeChange.emit($event)"
20248
- (actionClick)="topbarActionClick.emit($event)"
20249
- />
20250
-
20251
- <div class="jv-dashboard-main">
20252
- <jv-topbar
20253
- [title]="topbarTitle()"
20254
- [subtitle]="topbarSubtitle()"
20255
- [currentTheme]="currentTheme()"
20256
- [themeOptions]="themeOptions()"
20257
- [actions]="topbarActions()"
20258
- [showThemeSelector]="showThemeSelector()"
20259
- (menuClick)="menuClick.emit()"
20260
- (themeChange)="themeChange.emit($event)"
20261
- (actionClick)="topbarActionClick.emit($event)"
20262
- >
20263
- <ng-content select="[topbar-actions]" topbar-actions></ng-content>
20264
- </jv-topbar>
20265
-
20266
- <main class="jv-dashboard-content">
20267
- <ng-content></ng-content>
20268
- </main>
20269
- </div>
20270
- </div>
21328
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: JvDashboardShellComponent, isStandalone: true, selector: "jv-dashboard-shell", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, sidebarSubtitle: { classPropertyName: "sidebarSubtitle", publicName: "sidebarSubtitle", isSignal: true, isRequired: false, transformFunction: null }, brandIcon: { classPropertyName: "brandIcon", publicName: "brandIcon", isSignal: true, isRequired: false, transformFunction: null }, navItems: { classPropertyName: "navItems", publicName: "navItems", isSignal: true, isRequired: false, transformFunction: null }, sidebarCollapsed: { classPropertyName: "sidebarCollapsed", publicName: "sidebarCollapsed", isSignal: true, isRequired: false, transformFunction: null }, mobileSidebarOpen: { classPropertyName: "mobileSidebarOpen", publicName: "mobileSidebarOpen", isSignal: true, isRequired: false, transformFunction: null }, topbarTitle: { classPropertyName: "topbarTitle", publicName: "topbarTitle", isSignal: true, isRequired: false, transformFunction: null }, topbarSubtitle: { classPropertyName: "topbarSubtitle", publicName: "topbarSubtitle", isSignal: true, isRequired: false, transformFunction: null }, currentTheme: { classPropertyName: "currentTheme", publicName: "currentTheme", isSignal: true, isRequired: false, transformFunction: null }, themeOptions: { classPropertyName: "themeOptions", publicName: "themeOptions", isSignal: true, isRequired: false, transformFunction: null }, topbarActions: { classPropertyName: "topbarActions", publicName: "topbarActions", isSignal: true, isRequired: false, transformFunction: null }, showThemeSelector: { classPropertyName: "showThemeSelector", publicName: "showThemeSelector", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuClick: "menuClick", mobileClose: "mobileClose", sidebarCollapseToggle: "sidebarCollapseToggle", themeChange: "themeChange", topbarActionClick: "topbarActionClick" }, ngImport: i0, template: `
21329
+ <div class="jv-dashboard-shell" [class.is-sidebar-collapsed]="sidebarCollapsed()">
21330
+ <jv-sidebar
21331
+ [title]="title()"
21332
+ [subtitle]="sidebarSubtitle()"
21333
+ [brandIcon]="brandIcon()"
21334
+ [items]="navItems()"
21335
+ [collapsed]="sidebarCollapsed()"
21336
+ [mobileOpen]="mobileSidebarOpen()"
21337
+ [currentTheme]="currentTheme()"
21338
+ [themeOptions]="themeOptions()"
21339
+ [topbarActions]="topbarActions()"
21340
+ [showThemeSelector]="showThemeSelector()"
21341
+ (collapseToggle)="sidebarCollapseToggle.emit()"
21342
+ (mobileClose)="mobileClose.emit()"
21343
+ (themeChange)="themeChange.emit($event)"
21344
+ (actionClick)="topbarActionClick.emit($event)"
21345
+ />
21346
+
21347
+ <div class="jv-dashboard-main">
21348
+ <jv-topbar
21349
+ [title]="topbarTitle()"
21350
+ [subtitle]="topbarSubtitle()"
21351
+ [currentTheme]="currentTheme()"
21352
+ [themeOptions]="themeOptions()"
21353
+ [actions]="topbarActions()"
21354
+ [showThemeSelector]="showThemeSelector()"
21355
+ (menuClick)="menuClick.emit()"
21356
+ (themeChange)="themeChange.emit($event)"
21357
+ (actionClick)="topbarActionClick.emit($event)"
21358
+ >
21359
+ <ng-content select="[topbar-actions]" topbar-actions></ng-content>
21360
+ </jv-topbar>
21361
+
21362
+ <main class="jv-dashboard-content">
21363
+ <ng-content></ng-content>
21364
+ </main>
21365
+ </div>
21366
+ </div>
20271
21367
  `, isInline: true, styles: [":host{display:block;height:100dvh;overflow:hidden}.jv-dashboard-shell{display:grid;height:100%;grid-template-columns:auto 1fr;background:var(--jv-color-background);overflow:hidden}.jv-dashboard-main{display:grid;grid-template-rows:auto 1fr;min-width:0;min-height:0;overflow:hidden}.jv-dashboard-content{overflow:auto;min-width:0;min-height:0;padding:var(--jv-spacing-xl);scrollbar-width:none;-ms-overflow-style:none}.jv-dashboard-content::-webkit-scrollbar{width:0;height:0;display:none}@media(max-width:900px){.jv-dashboard-shell{grid-template-columns:1fr}.jv-dashboard-content{padding:var(--jv-spacing-lg)}}\n"], dependencies: [{ kind: "component", type: JvSidebarComponent, selector: "jv-sidebar", inputs: ["title", "subtitle", "brandIcon", "items", "collapsed", "mobileOpen", "ariaLabel", "currentTheme", "themeOptions", "topbarActions", "showThemeSelector"], outputs: ["collapseToggle", "mobileClose", "themeChange", "actionClick"] }, { kind: "component", type: JvTopbarComponent, selector: "jv-topbar", inputs: ["title", "subtitle", "currentTheme", "themeOptions", "actions", "showThemeSelector"], outputs: ["menuClick", "themeChange", "actionClick"] }] });
20272
21368
  }
20273
21369
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDashboardShellComponent, decorators: [{
20274
21370
  type: Component,
20275
- args: [{ selector: 'jv-dashboard-shell', standalone: true, imports: [JvSidebarComponent, JvTopbarComponent], template: `
20276
- <div class="jv-dashboard-shell" [class.is-sidebar-collapsed]="sidebarCollapsed()">
20277
- <jv-sidebar
20278
- [title]="title()"
20279
- [subtitle]="sidebarSubtitle()"
20280
- [brandIcon]="brandIcon()"
20281
- [items]="navItems()"
20282
- [collapsed]="sidebarCollapsed()"
20283
- [mobileOpen]="mobileSidebarOpen()"
20284
- [currentTheme]="currentTheme()"
20285
- [themeOptions]="themeOptions()"
20286
- [topbarActions]="topbarActions()"
20287
- [showThemeSelector]="showThemeSelector()"
20288
- (collapseToggle)="sidebarCollapseToggle.emit()"
20289
- (mobileClose)="mobileClose.emit()"
20290
- (themeChange)="themeChange.emit($event)"
20291
- (actionClick)="topbarActionClick.emit($event)"
20292
- />
20293
-
20294
- <div class="jv-dashboard-main">
20295
- <jv-topbar
20296
- [title]="topbarTitle()"
20297
- [subtitle]="topbarSubtitle()"
20298
- [currentTheme]="currentTheme()"
20299
- [themeOptions]="themeOptions()"
20300
- [actions]="topbarActions()"
20301
- [showThemeSelector]="showThemeSelector()"
20302
- (menuClick)="menuClick.emit()"
20303
- (themeChange)="themeChange.emit($event)"
20304
- (actionClick)="topbarActionClick.emit($event)"
20305
- >
20306
- <ng-content select="[topbar-actions]" topbar-actions></ng-content>
20307
- </jv-topbar>
20308
-
20309
- <main class="jv-dashboard-content">
20310
- <ng-content></ng-content>
20311
- </main>
20312
- </div>
20313
- </div>
21371
+ args: [{ selector: 'jv-dashboard-shell', standalone: true, imports: [JvSidebarComponent, JvTopbarComponent], template: `
21372
+ <div class="jv-dashboard-shell" [class.is-sidebar-collapsed]="sidebarCollapsed()">
21373
+ <jv-sidebar
21374
+ [title]="title()"
21375
+ [subtitle]="sidebarSubtitle()"
21376
+ [brandIcon]="brandIcon()"
21377
+ [items]="navItems()"
21378
+ [collapsed]="sidebarCollapsed()"
21379
+ [mobileOpen]="mobileSidebarOpen()"
21380
+ [currentTheme]="currentTheme()"
21381
+ [themeOptions]="themeOptions()"
21382
+ [topbarActions]="topbarActions()"
21383
+ [showThemeSelector]="showThemeSelector()"
21384
+ (collapseToggle)="sidebarCollapseToggle.emit()"
21385
+ (mobileClose)="mobileClose.emit()"
21386
+ (themeChange)="themeChange.emit($event)"
21387
+ (actionClick)="topbarActionClick.emit($event)"
21388
+ />
21389
+
21390
+ <div class="jv-dashboard-main">
21391
+ <jv-topbar
21392
+ [title]="topbarTitle()"
21393
+ [subtitle]="topbarSubtitle()"
21394
+ [currentTheme]="currentTheme()"
21395
+ [themeOptions]="themeOptions()"
21396
+ [actions]="topbarActions()"
21397
+ [showThemeSelector]="showThemeSelector()"
21398
+ (menuClick)="menuClick.emit()"
21399
+ (themeChange)="themeChange.emit($event)"
21400
+ (actionClick)="topbarActionClick.emit($event)"
21401
+ >
21402
+ <ng-content select="[topbar-actions]" topbar-actions></ng-content>
21403
+ </jv-topbar>
21404
+
21405
+ <main class="jv-dashboard-content">
21406
+ <ng-content></ng-content>
21407
+ </main>
21408
+ </div>
21409
+ </div>
20314
21410
  `, styles: [":host{display:block;height:100dvh;overflow:hidden}.jv-dashboard-shell{display:grid;height:100%;grid-template-columns:auto 1fr;background:var(--jv-color-background);overflow:hidden}.jv-dashboard-main{display:grid;grid-template-rows:auto 1fr;min-width:0;min-height:0;overflow:hidden}.jv-dashboard-content{overflow:auto;min-width:0;min-height:0;padding:var(--jv-spacing-xl);scrollbar-width:none;-ms-overflow-style:none}.jv-dashboard-content::-webkit-scrollbar{width:0;height:0;display:none}@media(max-width:900px){.jv-dashboard-shell{grid-template-columns:1fr}.jv-dashboard-content{padding:var(--jv-spacing-lg)}}\n"] }]
20315
21411
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], sidebarSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarSubtitle", required: false }] }], brandIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandIcon", required: false }] }], navItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navItems", required: false }] }], sidebarCollapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarCollapsed", required: false }] }], mobileSidebarOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileSidebarOpen", required: false }] }], topbarTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "topbarTitle", required: false }] }], topbarSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "topbarSubtitle", required: false }] }], currentTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentTheme", required: false }] }], themeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "themeOptions", required: false }] }], topbarActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "topbarActions", required: false }] }], showThemeSelector: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThemeSelector", required: false }] }], menuClick: [{ type: i0.Output, args: ["menuClick"] }], mobileClose: [{ type: i0.Output, args: ["mobileClose"] }], sidebarCollapseToggle: [{ type: i0.Output, args: ["sidebarCollapseToggle"] }], themeChange: [{ type: i0.Output, args: ["themeChange"] }], topbarActionClick: [{ type: i0.Output, args: ["topbarActionClick"] }] } });
20316
21412
 
@@ -20379,40 +21475,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
20379
21475
  `, styles: [".jv-page{display:grid;gap:var(--jv-spacing-lg);min-width:0}.jv-page-header{display:grid;gap:var(--jv-spacing-sm)}.jv-page-content{min-width:0}.eyebrow{margin:0;color:var(--jv-color-primary);font-size:.75rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1,.description{margin:0}.description{color:var(--jv-color-foreground-muted);max-width:72ch}\n"] }]
20380
21476
  }], propDecorators: { eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], breadcrumbs: [{ type: i0.Input, args: [{ isSignal: true, alias: "breadcrumbs", required: false }] }] } });
20381
21477
 
20382
- class JvListComponent {
20383
- role = input('list', ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
20384
- variant = input(null, ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
20385
- ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
20386
- variantClass = computed(() => {
20387
- const v = this.variant();
20388
- return v ? `jv-list--${v}` : '';
20389
- }, ...(ngDevMode ? [{ debugName: "variantClass" }] : /* istanbul ignore next */ []));
20390
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20391
- 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: `
20392
- <ul
20393
- class="jv-list"
20394
- [class]="variantClass()"
20395
- [attr.role]="role() === 'navigation' ? 'list' : null"
20396
- [attr.aria-label]="ariaLabel()"
20397
- >
20398
- <ng-content />
20399
- </ul>
20400
- `, 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"] });
20401
- }
20402
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvListComponent, decorators: [{
20403
- type: Component,
20404
- args: [{ selector: 'jv-list', standalone: true, template: `
20405
- <ul
20406
- class="jv-list"
20407
- [class]="variantClass()"
20408
- [attr.role]="role() === 'navigation' ? 'list' : null"
20409
- [attr.aria-label]="ariaLabel()"
20410
- >
20411
- <ng-content />
20412
- </ul>
20413
- `, 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"] }]
20414
- }], 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 }] }] } });
20415
-
20416
21478
  class JvListItemComponent {
20417
21479
  title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
20418
21480
  description = input('', ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
@@ -20481,6 +21543,470 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
20481
21543
  `, 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"] }]
20482
21544
  }], 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"] }] } });
20483
21545
 
21546
+ class JvTabPanelDirective {
21547
+ templateRef;
21548
+ id = input.required({ ...(ngDevMode ? { debugName: "id" } : /* istanbul ignore next */ {}), alias: 'jvTabPanel' });
21549
+ constructor(templateRef) {
21550
+ this.templateRef = templateRef;
21551
+ }
21552
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTabPanelDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
21553
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.17", type: JvTabPanelDirective, isStandalone: true, selector: "ng-template[jvTabPanel]", inputs: { id: { classPropertyName: "id", publicName: "jvTabPanel", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
21554
+ }
21555
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTabPanelDirective, decorators: [{
21556
+ type: Directive,
21557
+ args: [{
21558
+ selector: 'ng-template[jvTabPanel]',
21559
+ standalone: true,
21560
+ }]
21561
+ }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvTabPanel", required: true }] }] } });
21562
+
21563
+ let tabsSequence = 0;
21564
+ class JvTabsComponent {
21565
+ instanceId = ++tabsSequence;
21566
+ panelTemplates = contentChildren(JvTabPanelDirective, ...(ngDevMode ? [{ debugName: "panelTemplates" }] : /* istanbul ignore next */ []));
21567
+ tabListRef = viewChild('tabList', ...(ngDevMode ? [{ debugName: "tabListRef" }] : /* istanbul ignore next */ []));
21568
+ tabs = input([], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
21569
+ activeTabId = input(null, ...(ngDevMode ? [{ debugName: "activeTabId" }] : /* istanbul ignore next */ []));
21570
+ keepMounted = input(true, ...(ngDevMode ? [{ debugName: "keepMounted" }] : /* istanbul ignore next */ []));
21571
+ activeTabChange = output();
21572
+ showScrollLeft = signal(false, ...(ngDevMode ? [{ debugName: "showScrollLeft" }] : /* istanbul ignore next */ []));
21573
+ showScrollRight = signal(false, ...(ngDevMode ? [{ debugName: "showScrollRight" }] : /* istanbul ignore next */ []));
21574
+ resolvedActiveTabId = computed(() => {
21575
+ const enabledTabs = this.tabs().filter((tab) => !tab.disabled);
21576
+ const requestedId = this.activeTabId();
21577
+ if (requestedId && enabledTabs.some((tab) => tab.id === requestedId)) {
21578
+ return requestedId;
21579
+ }
21580
+ return enabledTabs[0]?.id ?? null;
21581
+ }, ...(ngDevMode ? [{ debugName: "resolvedActiveTabId" }] : /* istanbul ignore next */ []));
21582
+ constructor() {
21583
+ afterNextRender(() => this.updateScrollControls());
21584
+ effect(() => {
21585
+ this.tabs();
21586
+ this.panelTemplates();
21587
+ this.activeTabId();
21588
+ queueMicrotask(() => {
21589
+ this.ensureActiveTabVisible();
21590
+ this.updateScrollControls();
21591
+ });
21592
+ });
21593
+ }
21594
+ ngAfterViewInit() {
21595
+ this.updateScrollControls();
21596
+ }
21597
+ handleWindowResize() {
21598
+ this.updateScrollControls();
21599
+ }
21600
+ isActive(tabId) {
21601
+ return this.resolvedActiveTabId() === tabId;
21602
+ }
21603
+ getTabIndex(tabId, disabled) {
21604
+ if (disabled) {
21605
+ return -1;
21606
+ }
21607
+ return this.isActive(tabId) ? 0 : -1;
21608
+ }
21609
+ activateTab(tabId) {
21610
+ const tab = this.tabs().find((item) => item.id === tabId);
21611
+ if (!tab || tab.disabled || this.resolvedActiveTabId() === tabId) {
21612
+ return;
21613
+ }
21614
+ this.activeTabChange.emit(tabId);
21615
+ queueMicrotask(() => this.focusTabButton(tabId));
21616
+ }
21617
+ handleKeydown(event, currentTabId) {
21618
+ const enabledTabs = this.tabs().filter((tab) => !tab.disabled);
21619
+ if (enabledTabs.length === 0) {
21620
+ return;
21621
+ }
21622
+ const currentIndex = Math.max(0, enabledTabs.findIndex((tab) => tab.id === currentTabId));
21623
+ switch (event.key) {
21624
+ case 'ArrowRight':
21625
+ event.preventDefault();
21626
+ this.activateAndFocus(enabledTabs[(currentIndex + 1) % enabledTabs.length].id);
21627
+ break;
21628
+ case 'ArrowLeft':
21629
+ event.preventDefault();
21630
+ this.activateAndFocus(enabledTabs[(currentIndex - 1 + enabledTabs.length) % enabledTabs.length].id);
21631
+ break;
21632
+ case 'Home':
21633
+ event.preventDefault();
21634
+ this.activateAndFocus(enabledTabs[0].id);
21635
+ break;
21636
+ case 'End':
21637
+ event.preventDefault();
21638
+ this.activateAndFocus(enabledTabs[enabledTabs.length - 1].id);
21639
+ break;
21640
+ }
21641
+ }
21642
+ scrollTabs(direction) {
21643
+ const tabList = this.tabListRef()?.nativeElement;
21644
+ if (!tabList) {
21645
+ return;
21646
+ }
21647
+ tabList.scrollBy({
21648
+ left: direction * Math.max(tabList.clientWidth * 0.75, 180),
21649
+ behavior: 'smooth',
21650
+ });
21651
+ }
21652
+ updateScrollControls() {
21653
+ const tabList = this.tabListRef()?.nativeElement;
21654
+ if (!tabList) {
21655
+ return;
21656
+ }
21657
+ const maxScrollLeft = Math.max(tabList.scrollWidth - tabList.clientWidth, 0);
21658
+ this.showScrollLeft.set(tabList.scrollLeft > 1);
21659
+ this.showScrollRight.set(tabList.scrollLeft < maxScrollLeft - 1);
21660
+ }
21661
+ panelTemplate(tabId) {
21662
+ return this.panelTemplates().find((panel) => panel.id() === tabId)?.templateRef ?? null;
21663
+ }
21664
+ tabButtonId(tabId) {
21665
+ return `jv-tab-${this.instanceId}-${tabId}`;
21666
+ }
21667
+ panelId(tabId) {
21668
+ return `jv-tab-panel-${this.instanceId}-${tabId}`;
21669
+ }
21670
+ activateAndFocus(tabId) {
21671
+ if (this.resolvedActiveTabId() !== tabId) {
21672
+ this.activeTabChange.emit(tabId);
21673
+ }
21674
+ queueMicrotask(() => this.focusTabButton(tabId));
21675
+ }
21676
+ ensureActiveTabVisible() {
21677
+ const activeTabId = this.resolvedActiveTabId();
21678
+ if (!activeTabId) {
21679
+ return;
21680
+ }
21681
+ const button = this.getTabButton(activeTabId);
21682
+ if (button && typeof button.scrollIntoView === 'function') {
21683
+ button.scrollIntoView({ inline: 'nearest', block: 'nearest' });
21684
+ }
21685
+ }
21686
+ focusTabButton(tabId) {
21687
+ this.getTabButton(tabId)?.focus();
21688
+ }
21689
+ getTabButton(tabId) {
21690
+ const tabList = this.tabListRef()?.nativeElement;
21691
+ if (!tabList) {
21692
+ return null;
21693
+ }
21694
+ return (Array.from(tabList.querySelectorAll('[data-jv-tab-id]')).find((button) => button.dataset['jvTabId'] === tabId) ?? null);
21695
+ }
21696
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
21697
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvTabsComponent, isStandalone: true, selector: "jv-tabs", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null }, activeTabId: { classPropertyName: "activeTabId", publicName: "activeTabId", isSignal: true, isRequired: false, transformFunction: null }, keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeTabChange: "activeTabChange" }, host: { listeners: { "window:resize": "handleWindowResize()" } }, queries: [{ propertyName: "panelTemplates", predicate: JvTabPanelDirective, isSignal: true }], viewQueries: [{ propertyName: "tabListRef", first: true, predicate: ["tabList"], descendants: true, isSignal: true }], ngImport: i0, template: `
21698
+ <div class="jv-tabs">
21699
+ <div class="jv-tabs-header">
21700
+ <div class="jv-tabs-scroll" #tabList role="tablist" aria-orientation="horizontal" (scroll)="updateScrollControls()">
21701
+ @for (tab of tabs(); track tab.id) {
21702
+ <button
21703
+ type="button"
21704
+ class="jv-tab"
21705
+ [class.jv-tab--active]="isActive(tab.id)"
21706
+ [class.jv-tab--disabled]="tab.disabled ?? false"
21707
+ [id]="tabButtonId(tab.id)"
21708
+ [attr.data-jv-tab-id]="tab.id"
21709
+ role="tab"
21710
+ [attr.aria-selected]="isActive(tab.id) ? 'true' : 'false'"
21711
+ [attr.aria-controls]="panelId(tab.id)"
21712
+ [attr.tabindex]="getTabIndex(tab.id, tab.disabled ?? false)"
21713
+ [disabled]="tab.disabled ?? false"
21714
+ (click)="activateTab(tab.id)"
21715
+ (keydown)="handleKeydown($event, tab.id)"
21716
+ >
21717
+ @if (tab.icon) {
21718
+ <jv-icon [name]="tab.icon" [size]="16" />
21719
+ }
21720
+ <span>{{ tab.label }}</span>
21721
+ </button>
21722
+ }
21723
+ </div>
21724
+
21725
+ @if (showScrollLeft() || showScrollRight()) {
21726
+ <div class="jv-tabs-controls" aria-hidden="true">
21727
+ <button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollLeft()" (click)="scrollTabs(-1)">
21728
+ <jv-icon name="chevron-left" [size]="16" />
21729
+ </button>
21730
+ <button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollRight()" (click)="scrollTabs(1)">
21731
+ <jv-icon name="chevron-right" [size]="16" />
21732
+ </button>
21733
+ </div>
21734
+ }
21735
+ </div>
21736
+
21737
+ <div class="jv-tabs-panels">
21738
+ @if (keepMounted()) {
21739
+ @for (tab of tabs(); track tab.id) {
21740
+ <div
21741
+ class="jv-tab-panel"
21742
+ [class.jv-tab-panel--hidden]="!isActive(tab.id)"
21743
+ [id]="panelId(tab.id)"
21744
+ role="tabpanel"
21745
+ [attr.aria-labelledby]="tabButtonId(tab.id)"
21746
+ [attr.tabindex]="isActive(tab.id) ? 0 : null"
21747
+ [hidden]="!isActive(tab.id)"
21748
+ >
21749
+ <ng-container [ngTemplateOutlet]="panelTemplate(tab.id) ?? null" />
21750
+ </div>
21751
+ }
21752
+ } @else if (resolvedActiveTabId(); as activeId) {
21753
+ <div
21754
+ class="jv-tab-panel"
21755
+ [id]="panelId(activeId)"
21756
+ role="tabpanel"
21757
+ [attr.aria-labelledby]="tabButtonId(activeId)"
21758
+ tabindex="0"
21759
+ >
21760
+ <ng-container [ngTemplateOutlet]="panelTemplate(activeId) ?? null" />
21761
+ </div>
21762
+ }
21763
+ </div>
21764
+ </div>
21765
+ `, isInline: true, styles: [".jv-tabs{display:grid;gap:var(--jv-spacing-md)}.jv-tabs-header{display:flex;align-items:center;gap:var(--jv-spacing-sm);min-width:0}.jv-tabs-scroll{flex:1;min-width:0;display:flex;align-items:stretch;gap:var(--jv-spacing-xs);overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none;border-bottom:1px solid var(--jv-color-border);scroll-behavior:smooth}.jv-tabs-scroll::-webkit-scrollbar{display:none}.jv-tab{position:relative;display:inline-flex;align-items:center;gap:var(--jv-spacing-xs);flex:0 0 auto;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:0;border-bottom:2px solid transparent;background:transparent;color:var(--jv-color-foreground-muted);font:inherit;font-weight:600;white-space:nowrap;cursor:pointer;transition:color .16s ease,border-color .16s ease,background-color .16s ease}.jv-tab:hover:not(:disabled){color:var(--jv-color-foreground);background:color-mix(in srgb,var(--jv-color-primary) 6%,transparent)}.jv-tab:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:-2px;border-radius:var(--jv-radius-sm) var(--jv-radius-sm) 0 0}.jv-tab--active{color:var(--jv-color-primary);border-bottom-color:var(--jv-color-primary)}.jv-tab--disabled{opacity:.5;cursor:not-allowed}.jv-tabs-controls{display:inline-flex;align-items:center;gap:var(--jv-spacing-xs)}.jv-tabs-scroll-button{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);cursor:pointer}.jv-tabs-scroll-button:disabled{opacity:.4;cursor:default}.jv-tab-panel{outline:none}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
21766
+ }
21767
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTabsComponent, decorators: [{
21768
+ type: Component,
21769
+ args: [{ selector: 'jv-tabs', standalone: true, imports: [NgTemplateOutlet, JvIconComponent], template: `
21770
+ <div class="jv-tabs">
21771
+ <div class="jv-tabs-header">
21772
+ <div class="jv-tabs-scroll" #tabList role="tablist" aria-orientation="horizontal" (scroll)="updateScrollControls()">
21773
+ @for (tab of tabs(); track tab.id) {
21774
+ <button
21775
+ type="button"
21776
+ class="jv-tab"
21777
+ [class.jv-tab--active]="isActive(tab.id)"
21778
+ [class.jv-tab--disabled]="tab.disabled ?? false"
21779
+ [id]="tabButtonId(tab.id)"
21780
+ [attr.data-jv-tab-id]="tab.id"
21781
+ role="tab"
21782
+ [attr.aria-selected]="isActive(tab.id) ? 'true' : 'false'"
21783
+ [attr.aria-controls]="panelId(tab.id)"
21784
+ [attr.tabindex]="getTabIndex(tab.id, tab.disabled ?? false)"
21785
+ [disabled]="tab.disabled ?? false"
21786
+ (click)="activateTab(tab.id)"
21787
+ (keydown)="handleKeydown($event, tab.id)"
21788
+ >
21789
+ @if (tab.icon) {
21790
+ <jv-icon [name]="tab.icon" [size]="16" />
21791
+ }
21792
+ <span>{{ tab.label }}</span>
21793
+ </button>
21794
+ }
21795
+ </div>
21796
+
21797
+ @if (showScrollLeft() || showScrollRight()) {
21798
+ <div class="jv-tabs-controls" aria-hidden="true">
21799
+ <button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollLeft()" (click)="scrollTabs(-1)">
21800
+ <jv-icon name="chevron-left" [size]="16" />
21801
+ </button>
21802
+ <button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollRight()" (click)="scrollTabs(1)">
21803
+ <jv-icon name="chevron-right" [size]="16" />
21804
+ </button>
21805
+ </div>
21806
+ }
21807
+ </div>
21808
+
21809
+ <div class="jv-tabs-panels">
21810
+ @if (keepMounted()) {
21811
+ @for (tab of tabs(); track tab.id) {
21812
+ <div
21813
+ class="jv-tab-panel"
21814
+ [class.jv-tab-panel--hidden]="!isActive(tab.id)"
21815
+ [id]="panelId(tab.id)"
21816
+ role="tabpanel"
21817
+ [attr.aria-labelledby]="tabButtonId(tab.id)"
21818
+ [attr.tabindex]="isActive(tab.id) ? 0 : null"
21819
+ [hidden]="!isActive(tab.id)"
21820
+ >
21821
+ <ng-container [ngTemplateOutlet]="panelTemplate(tab.id) ?? null" />
21822
+ </div>
21823
+ }
21824
+ } @else if (resolvedActiveTabId(); as activeId) {
21825
+ <div
21826
+ class="jv-tab-panel"
21827
+ [id]="panelId(activeId)"
21828
+ role="tabpanel"
21829
+ [attr.aria-labelledby]="tabButtonId(activeId)"
21830
+ tabindex="0"
21831
+ >
21832
+ <ng-container [ngTemplateOutlet]="panelTemplate(activeId) ?? null" />
21833
+ </div>
21834
+ }
21835
+ </div>
21836
+ </div>
21837
+ `, styles: [".jv-tabs{display:grid;gap:var(--jv-spacing-md)}.jv-tabs-header{display:flex;align-items:center;gap:var(--jv-spacing-sm);min-width:0}.jv-tabs-scroll{flex:1;min-width:0;display:flex;align-items:stretch;gap:var(--jv-spacing-xs);overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none;border-bottom:1px solid var(--jv-color-border);scroll-behavior:smooth}.jv-tabs-scroll::-webkit-scrollbar{display:none}.jv-tab{position:relative;display:inline-flex;align-items:center;gap:var(--jv-spacing-xs);flex:0 0 auto;min-height:var(--jv-density-control-height);padding:0 var(--jv-spacing-md);border:0;border-bottom:2px solid transparent;background:transparent;color:var(--jv-color-foreground-muted);font:inherit;font-weight:600;white-space:nowrap;cursor:pointer;transition:color .16s ease,border-color .16s ease,background-color .16s ease}.jv-tab:hover:not(:disabled){color:var(--jv-color-foreground);background:color-mix(in srgb,var(--jv-color-primary) 6%,transparent)}.jv-tab:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:-2px;border-radius:var(--jv-radius-sm) var(--jv-radius-sm) 0 0}.jv-tab--active{color:var(--jv-color-primary);border-bottom-color:var(--jv-color-primary)}.jv-tab--disabled{opacity:.5;cursor:not-allowed}.jv-tabs-controls{display:inline-flex;align-items:center;gap:var(--jv-spacing-xs)}.jv-tabs-scroll-button{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);cursor:pointer}.jv-tabs-scroll-button:disabled{opacity:.4;cursor:default}.jv-tab-panel{outline:none}\n"] }]
21838
+ }], ctorParameters: () => [], propDecorators: { panelTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => JvTabPanelDirective), { isSignal: true }] }], tabListRef: [{ type: i0.ViewChild, args: ['tabList', { isSignal: true }] }], tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: false }] }], activeTabId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTabId", required: false }] }], keepMounted: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepMounted", required: false }] }], activeTabChange: [{ type: i0.Output, args: ["activeTabChange"] }], handleWindowResize: [{
21839
+ type: HostListener,
21840
+ args: ['window:resize']
21841
+ }] } });
21842
+
21843
+ let tooltipSequence = 0;
21844
+ class JvTooltipDirective {
21845
+ document = inject(DOCUMENT);
21846
+ renderer = inject(Renderer2);
21847
+ hostRef = inject((ElementRef));
21848
+ tooltipElement = null;
21849
+ showTimeout = null;
21850
+ hideTimeout = null;
21851
+ tooltipId = `jv-tooltip-${++tooltipSequence}`;
21852
+ text = input.required({ ...(ngDevMode ? { debugName: "text" } : /* istanbul ignore next */ {}), alias: 'jvTooltip' });
21853
+ position = input('top', { ...(ngDevMode ? { debugName: "position" } : /* istanbul ignore next */ {}), alias: 'jvTooltipPosition' });
21854
+ show() {
21855
+ if (!this.text().trim()) {
21856
+ return;
21857
+ }
21858
+ this.clearHideTimeout();
21859
+ if (this.tooltipElement) {
21860
+ return;
21861
+ }
21862
+ this.showTimeout = setTimeout(() => {
21863
+ this.renderTooltip();
21864
+ this.positionTooltip();
21865
+ this.renderer.setAttribute(this.anchorElement(), 'aria-describedby', this.tooltipId);
21866
+ }, 120);
21867
+ }
21868
+ hide() {
21869
+ this.clearShowTimeout();
21870
+ if (!this.tooltipElement) {
21871
+ return;
21872
+ }
21873
+ this.hideTimeout = setTimeout(() => this.destroyTooltip(), 80);
21874
+ }
21875
+ handleDocumentKeydown(event) {
21876
+ if (event.key === 'Escape') {
21877
+ this.destroyTooltip();
21878
+ }
21879
+ }
21880
+ handleViewportChange() {
21881
+ if (this.tooltipElement) {
21882
+ this.positionTooltip();
21883
+ }
21884
+ }
21885
+ ngOnDestroy() {
21886
+ this.clearShowTimeout();
21887
+ this.clearHideTimeout();
21888
+ this.destroyTooltip();
21889
+ }
21890
+ renderTooltip() {
21891
+ if (this.tooltipElement) {
21892
+ return;
21893
+ }
21894
+ const tooltip = this.renderer.createElement('div');
21895
+ tooltip.textContent = this.text();
21896
+ this.renderer.setAttribute(tooltip, 'id', this.tooltipId);
21897
+ this.renderer.setAttribute(tooltip, 'role', 'tooltip');
21898
+ this.renderer.setStyle(tooltip, 'position', 'fixed');
21899
+ this.renderer.setStyle(tooltip, 'z-index', '1400');
21900
+ this.renderer.setStyle(tooltip, 'max-width', '18rem');
21901
+ this.renderer.setStyle(tooltip, 'padding', '0.5rem 0.75rem');
21902
+ this.renderer.setStyle(tooltip, 'border-radius', '0.5rem');
21903
+ this.renderer.setStyle(tooltip, 'background', 'rgb(15 23 42)');
21904
+ this.renderer.setStyle(tooltip, 'color', '#ffffff');
21905
+ this.renderer.setStyle(tooltip, 'font-size', '0.8125rem');
21906
+ this.renderer.setStyle(tooltip, 'line-height', '1.4');
21907
+ this.renderer.setStyle(tooltip, 'box-shadow', 'var(--jv-shadow-md)');
21908
+ this.renderer.setStyle(tooltip, 'pointer-events', 'none');
21909
+ this.renderer.setStyle(tooltip, 'opacity', '0');
21910
+ this.renderer.setStyle(tooltip, 'transition', 'opacity 120ms ease');
21911
+ this.renderer.appendChild(this.document.body, tooltip);
21912
+ this.tooltipElement = tooltip;
21913
+ requestAnimationFrame(() => {
21914
+ if (this.tooltipElement) {
21915
+ this.renderer.setStyle(this.tooltipElement, 'opacity', '1');
21916
+ }
21917
+ });
21918
+ }
21919
+ positionTooltip() {
21920
+ const tooltip = this.tooltipElement;
21921
+ if (!tooltip) {
21922
+ return;
21923
+ }
21924
+ const hostRect = this.anchorElement().getBoundingClientRect();
21925
+ const tooltipRect = tooltip.getBoundingClientRect();
21926
+ const gap = 8;
21927
+ let top = hostRect.top - tooltipRect.height - gap;
21928
+ let left = hostRect.left + hostRect.width / 2 - tooltipRect.width / 2;
21929
+ switch (this.position()) {
21930
+ case 'right':
21931
+ top = hostRect.top + hostRect.height / 2 - tooltipRect.height / 2;
21932
+ left = hostRect.right + gap;
21933
+ break;
21934
+ case 'bottom':
21935
+ top = hostRect.bottom + gap;
21936
+ left = hostRect.left + hostRect.width / 2 - tooltipRect.width / 2;
21937
+ break;
21938
+ case 'left':
21939
+ top = hostRect.top + hostRect.height / 2 - tooltipRect.height / 2;
21940
+ left = hostRect.left - tooltipRect.width - gap;
21941
+ break;
21942
+ }
21943
+ this.renderer.setStyle(tooltip, 'top', `${Math.round(top)}px`);
21944
+ this.renderer.setStyle(tooltip, 'left', `${Math.round(left)}px`);
21945
+ }
21946
+ destroyTooltip() {
21947
+ this.clearHideTimeout();
21948
+ this.clearShowTimeout();
21949
+ if (!this.tooltipElement) {
21950
+ return;
21951
+ }
21952
+ this.renderer.removeAttribute(this.anchorElement(), 'aria-describedby');
21953
+ this.renderer.removeChild(this.document.body, this.tooltipElement);
21954
+ this.tooltipElement = null;
21955
+ }
21956
+ anchorElement() {
21957
+ const host = this.hostRef.nativeElement;
21958
+ if (isInteractiveElement(host)) {
21959
+ return host;
21960
+ }
21961
+ return host.querySelector('button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])') ?? host;
21962
+ }
21963
+ clearShowTimeout() {
21964
+ if (this.showTimeout) {
21965
+ clearTimeout(this.showTimeout);
21966
+ this.showTimeout = null;
21967
+ }
21968
+ }
21969
+ clearHideTimeout() {
21970
+ if (this.hideTimeout) {
21971
+ clearTimeout(this.hideTimeout);
21972
+ this.hideTimeout = null;
21973
+ }
21974
+ }
21975
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
21976
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.17", type: JvTooltipDirective, isStandalone: true, selector: "[jvTooltip]", inputs: { text: { classPropertyName: "text", publicName: "jvTooltip", isSignal: true, isRequired: true, transformFunction: null }, position: { classPropertyName: "position", publicName: "jvTooltipPosition", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "show()", "focusin": "show()", "mouseleave": "hide()", "blur": "hide()", "document:keydown": "handleDocumentKeydown($event)", "window:scroll": "handleViewportChange()", "window:resize": "handleViewportChange()" } }, ngImport: i0 });
21977
+ }
21978
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTooltipDirective, decorators: [{
21979
+ type: Directive,
21980
+ args: [{
21981
+ selector: '[jvTooltip]',
21982
+ standalone: true,
21983
+ }]
21984
+ }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvTooltip", required: true }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvTooltipPosition", required: false }] }], show: [{
21985
+ type: HostListener,
21986
+ args: ['mouseenter']
21987
+ }, {
21988
+ type: HostListener,
21989
+ args: ['focusin']
21990
+ }], hide: [{
21991
+ type: HostListener,
21992
+ args: ['mouseleave']
21993
+ }, {
21994
+ type: HostListener,
21995
+ args: ['blur']
21996
+ }], handleDocumentKeydown: [{
21997
+ type: HostListener,
21998
+ args: ['document:keydown', ['$event']]
21999
+ }], handleViewportChange: [{
22000
+ type: HostListener,
22001
+ args: ['window:scroll']
22002
+ }, {
22003
+ type: HostListener,
22004
+ args: ['window:resize']
22005
+ }] } });
22006
+ function isInteractiveElement(element) {
22007
+ return ['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA'].includes(element.tagName) || element.tabIndex >= 0;
22008
+ }
22009
+
20484
22010
  const JV_GRID_DEFAULT_OPTIONS = {
20485
22011
  searchable: true,
20486
22012
  sortable: true,
@@ -21880,5 +23406,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
21880
23406
  * Generated bundle index. Do not edit.
21881
23407
  */
21882
23408
 
21883
- export { EN, ES, JV_DEFAULT_LOCALE, JV_FALLBACK_ICON_NAME, JV_GRID_DEFAULT_OPTIONS, JV_LOCALE_DICTIONARIES, JV_LUCIDE_ICON_REGISTRY, JV_UI_CONFIG, JV_UI_CONFIG_DEFAULTS, JV_UI_DEFAULT_CONFIG, JvAlertComponent, JvAnnouncementService, JvBadgeComponent, JvBreadcrumbComponent, JvButtonComponent, JvButtonGroupComponent, JvCardComponent, JvChangePasswordPageComponent, JvCheckboxComponent, JvConfirmDialogComponent, JvDashboardShellComponent, JvDialogComponent, JvDialogService, JvDividerComponent, JvForgotPasswordPageComponent, JvFormContainerComponent, JvGridComponent, JvIconButtonComponent, JvIconComponent, JvInputComponent, JvListComponent, JvListItemComponent, JvLoaderComponent, JvLoaderService, JvLoginPageComponent, JvPageComponent, JvPaginationComponent, JvRadioComponent, JvSectionComponent, JvSelectComponent, JvSidebarComponent, JvSwitchComponent, JvTextareaComponent, JvThemeService, JvToastComponent, JvToastService, JvTopbarComponent, JvTranslationService, provideJvUi };
23409
+ export { EN, ES, JV_DEFAULT_LOCALE, JV_FALLBACK_ICON_NAME, JV_GRID_DEFAULT_OPTIONS, JV_LOCALE_DICTIONARIES, JV_LUCIDE_ICON_REGISTRY, JV_UI_CONFIG, JV_UI_CONFIG_DEFAULTS, JV_UI_DEFAULT_CONFIG, JvAccordionComponent, JvAccordionPanelDirective, JvAlertComponent, JvAnnouncementService, JvBadgeComponent, JvBreadcrumbComponent, JvButtonComponent, JvButtonGroupComponent, JvCardComponent, JvChangePasswordPageComponent, JvCheckboxComponent, JvCollapsiblePanelComponent, JvConfirmDialogComponent, JvDashboardShellComponent, JvDatePickerComponent, JvDialogComponent, JvDialogService, JvDividerComponent, JvForgotPasswordPageComponent, JvFormContainerComponent, JvGridComponent, JvIconButtonComponent, JvIconComponent, JvInputComponent, JvListComponent, JvListItemComponent, JvLoaderComponent, JvLoaderService, JvLoginPageComponent, JvModalSelectorComponent, JvPageComponent, JvPaginationComponent, JvRadioComponent, JvSectionComponent, JvSelectComponent, JvSidebarComponent, JvSwitchComponent, JvTabPanelDirective, JvTabsComponent, JvTextareaComponent, JvThemeService, JvToastComponent, JvToastService, JvTooltipDirective, JvTopbarComponent, JvTranslationService, provideJvUi };
21884
23410
  //# sourceMappingURL=devjuliovilla-jv-ui.mjs.map