@devjuliovilla/jv-ui 1.7.1 → 1.9.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,26 @@ 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
+ 'dialog.confirmTitle': 'Confirmar',
18882
+ 'dialog.confirm': 'Aceptar',
18883
+ 'dialog.cancel': 'Cancelar',
18884
+ 'common.expanded': 'Expandido',
18885
+ 'common.collapsed': 'Colapsado',
18922
18886
  };
18923
18887
 
18924
18888
  const EN = {
@@ -18955,6 +18919,26 @@ const EN = {
18955
18919
  'pagination.nextPage': 'Next page',
18956
18920
  'pagination.lastPage': 'Last page',
18957
18921
  'pagination.itemsPerPage': 'Items per page',
18922
+ 'datePicker.placeholder': 'Select a date',
18923
+ 'datePicker.dialogTitle': 'Select date',
18924
+ 'datePicker.clear': 'Clear date',
18925
+ 'datePicker.close': 'Close',
18926
+ 'datePicker.prevMonth': 'Previous month',
18927
+ 'datePicker.nextMonth': 'Next month',
18928
+ 'datePicker.selectionCleared': 'Date selection cleared',
18929
+ 'modalSelector.placeholder': 'Select an option',
18930
+ 'modalSelector.searchPlaceholder': 'Search',
18931
+ 'modalSelector.emptyMessage': 'No results found',
18932
+ 'modalSelector.clear': 'Clear selection',
18933
+ 'modalSelector.close': 'Close',
18934
+ 'modalSelector.results': '{count} results',
18935
+ 'modalSelector.selected': '{label} selected',
18936
+ 'modalSelector.selectionCleared': 'Selection cleared',
18937
+ 'dialog.confirmTitle': 'Confirm',
18938
+ 'dialog.confirm': 'Confirm',
18939
+ 'dialog.cancel': 'Cancel',
18940
+ 'common.expanded': 'Expanded',
18941
+ 'common.collapsed': 'Collapsed',
18958
18942
  };
18959
18943
 
18960
18944
  const JV_LOCALE_DICTIONARIES = {
@@ -18999,96 +18983,415 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
18999
18983
  args: [{ providedIn: 'root' }]
19000
18984
  }] });
19001
18985
 
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 {
18986
+ let collapsiblePanelSequence = 0;
18987
+ class JvCollapsiblePanelComponent {
19041
18988
  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);
18989
+ translation = inject(JvTranslationService);
18990
+ internalOpen = signal(false, ...(ngDevMode ? [{ debugName: "internalOpen" }] : /* istanbul ignore next */ []));
18991
+ bodyVisible = signal(false, ...(ngDevMode ? [{ debugName: "bodyVisible" }] : /* istanbul ignore next */ []));
18992
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
18993
+ subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
18994
+ icon = input(null, ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
18995
+ open = input(undefined, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
18996
+ defaultOpen = input(false, ...(ngDevMode ? [{ debugName: "defaultOpen" }] : /* istanbul ignore next */ []));
18997
+ keepMounted = input(true, ...(ngDevMode ? [{ debugName: "keepMounted" }] : /* istanbul ignore next */ []));
18998
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
18999
+ showIndicator = input(true, ...(ngDevMode ? [{ debugName: "showIndicator" }] : /* istanbul ignore next */ []));
19000
+ variant = input('bordered', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
19001
+ openChange = output();
19002
+ triggerId = `jv-collapsible-trigger-${++collapsiblePanelSequence}`;
19003
+ contentId = `jv-collapsible-content-${collapsiblePanelSequence}`;
19004
+ resolvedOpen = computed(() => this.open() ?? this.internalOpen(), ...(ngDevMode ? [{ debugName: "resolvedOpen" }] : /* istanbul ignore next */ []));
19005
+ panelClass = computed(() => `jv-collapsible-panel--${this.variant()}`, ...(ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []));
19006
+ shouldRenderBody = computed(() => this.keepMounted() || this.bodyVisible(), ...(ngDevMode ? [{ debugName: "shouldRenderBody" }] : /* istanbul ignore next */ []));
19007
+ triggerAriaLabel = computed(() => {
19008
+ const parts = [this.title()];
19009
+ if (this.subtitle()) {
19010
+ parts.push(this.subtitle());
19061
19011
  }
19062
- return id;
19012
+ parts.push(this.translation.translate(this.resolvedOpen() ? 'common.expanded' : 'common.collapsed'));
19013
+ return parts.join('. ');
19014
+ }, ...(ngDevMode ? [{ debugName: "triggerAriaLabel" }] : /* istanbul ignore next */ []));
19015
+ constructor() {
19016
+ this.internalOpen.set(this.defaultOpen());
19017
+ this.bodyVisible.set(this.defaultOpen());
19018
+ effect(() => {
19019
+ const open = this.resolvedOpen();
19020
+ const keepMounted = this.keepMounted();
19021
+ if (keepMounted || open) {
19022
+ this.bodyVisible.set(true);
19023
+ }
19024
+ });
19063
19025
  }
19064
- dismiss(id) {
19065
- this.items.update((current) => current.filter((item) => item.id !== id));
19026
+ toggle() {
19027
+ if (this.disabled()) {
19028
+ return;
19029
+ }
19030
+ const nextOpen = !this.resolvedOpen();
19031
+ if (this.open() === undefined) {
19032
+ this.internalOpen.set(nextOpen);
19033
+ }
19034
+ if (nextOpen) {
19035
+ this.bodyVisible.set(true);
19036
+ }
19037
+ this.announcementService.announce(`${this.title()}. ${this.translation.translate(nextOpen ? 'common.expanded' : 'common.collapsed')}`, 'polite');
19038
+ this.openChange.emit(nextOpen);
19066
19039
  }
19067
- setPosition(position) {
19068
- this.positionOverride.set(position);
19040
+ handleBodyTransitionEnd(event) {
19041
+ if (event.propertyName !== 'grid-template-rows' || this.keepMounted() || this.resolvedOpen()) {
19042
+ return;
19043
+ }
19044
+ this.bodyVisible.set(false);
19069
19045
  }
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' });
19046
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvCollapsiblePanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19047
+ 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: `
19048
+ <section class="jv-collapsible-panel" [class]="panelClass()">
19049
+ <button
19050
+ type="button"
19051
+ class="jv-collapsible-trigger"
19052
+ [id]="triggerId"
19053
+ [disabled]="disabled()"
19054
+ [attr.aria-expanded]="resolvedOpen() ? 'true' : 'false'"
19055
+ [attr.aria-controls]="contentId"
19056
+ [attr.aria-label]="triggerAriaLabel()"
19057
+ (click)="toggle()"
19058
+ >
19059
+ <span class="jv-collapsible-main">
19060
+ @if (icon()) {
19061
+ <jv-icon [name]="icon()!" [size]="18" />
19062
+ }
19063
+
19064
+ <span class="jv-collapsible-copy">
19065
+ <span class="jv-collapsible-title">{{ title() }}</span>
19066
+ @if (subtitle()) {
19067
+ <span class="jv-collapsible-subtitle">{{ subtitle() }}</span>
19068
+ }
19069
+ </span>
19070
+ </span>
19071
+
19072
+ @if (showIndicator()) {
19073
+ <jv-icon class="jv-collapsible-indicator" [class.jv-collapsible-indicator--open]="resolvedOpen()" name="chevron-down" [size]="18" />
19074
+ }
19075
+ </button>
19076
+
19077
+ @if (shouldRenderBody()) {
19078
+ <div
19079
+ class="jv-collapsible-body"
19080
+ [class.jv-collapsible-body--open]="resolvedOpen()"
19081
+ [id]="contentId"
19082
+ role="region"
19083
+ [attr.aria-labelledby]="triggerId"
19084
+ [attr.aria-hidden]="resolvedOpen() ? null : 'true'"
19085
+ [attr.inert]="resolvedOpen() ? null : ''"
19086
+ (transitionend)="handleBodyTransitionEnd($event)"
19087
+ >
19088
+ <div class="jv-collapsible-content">
19089
+ <ng-content />
19090
+ </div>
19091
+ </div>
19092
+ }
19093
+ </section>
19094
+ `, 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
19095
  }
19073
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastService, decorators: [{
19074
- type: Injectable,
19075
- args: [{ providedIn: 'root' }]
19076
- }] });
19096
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvCollapsiblePanelComponent, decorators: [{
19097
+ type: Component,
19098
+ args: [{ selector: 'jv-collapsible-panel', standalone: true, imports: [JvIconComponent], template: `
19099
+ <section class="jv-collapsible-panel" [class]="panelClass()">
19100
+ <button
19101
+ type="button"
19102
+ class="jv-collapsible-trigger"
19103
+ [id]="triggerId"
19104
+ [disabled]="disabled()"
19105
+ [attr.aria-expanded]="resolvedOpen() ? 'true' : 'false'"
19106
+ [attr.aria-controls]="contentId"
19107
+ [attr.aria-label]="triggerAriaLabel()"
19108
+ (click)="toggle()"
19109
+ >
19110
+ <span class="jv-collapsible-main">
19111
+ @if (icon()) {
19112
+ <jv-icon [name]="icon()!" [size]="18" />
19113
+ }
19077
19114
 
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
- }
19090
- <p>{{ toast.message }}</p>
19091
- </div>
19115
+ <span class="jv-collapsible-copy">
19116
+ <span class="jv-collapsible-title">{{ title() }}</span>
19117
+ @if (subtitle()) {
19118
+ <span class="jv-collapsible-subtitle">{{ subtitle() }}</span>
19119
+ }
19120
+ </span>
19121
+ </span>
19122
+
19123
+ @if (showIndicator()) {
19124
+ <jv-icon class="jv-collapsible-indicator" [class.jv-collapsible-indicator--open]="resolvedOpen()" name="chevron-down" [size]="18" />
19125
+ }
19126
+ </button>
19127
+
19128
+ @if (shouldRenderBody()) {
19129
+ <div
19130
+ class="jv-collapsible-body"
19131
+ [class.jv-collapsible-body--open]="resolvedOpen()"
19132
+ [id]="contentId"
19133
+ role="region"
19134
+ [attr.aria-labelledby]="triggerId"
19135
+ [attr.aria-hidden]="resolvedOpen() ? null : 'true'"
19136
+ [attr.inert]="resolvedOpen() ? null : ''"
19137
+ (transitionend)="handleBodyTransitionEnd($event)"
19138
+ >
19139
+ <div class="jv-collapsible-content">
19140
+ <ng-content />
19141
+ </div>
19142
+ </div>
19143
+ }
19144
+ </section>
19145
+ `, 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"] }]
19146
+ }], 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"] }] } });
19147
+
19148
+ class JvAccordionComponent {
19149
+ panelTemplates = contentChildren(JvAccordionPanelDirective, ...(ngDevMode ? [{ debugName: "panelTemplates" }] : /* istanbul ignore next */ []));
19150
+ internalOpenItemIds = signal([], ...(ngDevMode ? [{ debugName: "internalOpenItemIds" }] : /* istanbul ignore next */ []));
19151
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
19152
+ openItemIds = input(undefined, ...(ngDevMode ? [{ debugName: "openItemIds" }] : /* istanbul ignore next */ []));
19153
+ defaultOpenItemIds = input([], ...(ngDevMode ? [{ debugName: "defaultOpenItemIds" }] : /* istanbul ignore next */ []));
19154
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
19155
+ keepMounted = input(true, ...(ngDevMode ? [{ debugName: "keepMounted" }] : /* istanbul ignore next */ []));
19156
+ showIndicator = input(true, ...(ngDevMode ? [{ debugName: "showIndicator" }] : /* istanbul ignore next */ []));
19157
+ variant = input('bordered', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
19158
+ openItemIdsChange = output();
19159
+ resolvedOpenItemIds = computed(() => {
19160
+ const allItemIds = new Set(this.items().map((item) => item.id));
19161
+ const candidateIds = this.openItemIds() ?? this.internalOpenItemIds();
19162
+ const filteredIds = candidateIds.filter((id) => allItemIds.has(id));
19163
+ return this.multiple() ? filteredIds : filteredIds.slice(0, 1);
19164
+ }, ...(ngDevMode ? [{ debugName: "resolvedOpenItemIds" }] : /* istanbul ignore next */ []));
19165
+ constructor() {
19166
+ this.internalOpenItemIds.set(this.defaultOpenItemIds());
19167
+ }
19168
+ isItemOpen(itemId) {
19169
+ return this.resolvedOpenItemIds().includes(itemId);
19170
+ }
19171
+ handleItemOpenChange(itemId, nextOpen) {
19172
+ const currentIds = this.resolvedOpenItemIds();
19173
+ let nextIds = currentIds;
19174
+ if (nextOpen) {
19175
+ nextIds = this.multiple() ? [...new Set([...currentIds, itemId])] : [itemId];
19176
+ }
19177
+ else {
19178
+ nextIds = currentIds.filter((id) => id !== itemId);
19179
+ }
19180
+ if (this.openItemIds() === undefined) {
19181
+ this.internalOpenItemIds.set(nextIds);
19182
+ }
19183
+ this.openItemIdsChange.emit(nextIds);
19184
+ }
19185
+ panelTemplate(itemId) {
19186
+ return this.panelTemplates().find((panel) => panel.id() === itemId)?.templateRef ?? null;
19187
+ }
19188
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvAccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19189
+ 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: `
19190
+ <div class="jv-accordion">
19191
+ @for (item of items(); track item.id) {
19192
+ <jv-collapsible-panel
19193
+ [title]="item.title"
19194
+ [subtitle]="item.subtitle ?? ''"
19195
+ [icon]="item.icon ?? null"
19196
+ [open]="isItemOpen(item.id)"
19197
+ [keepMounted]="keepMounted()"
19198
+ [disabled]="item.disabled ?? false"
19199
+ [showIndicator]="showIndicator()"
19200
+ [variant]="variant()"
19201
+ (openChange)="handleItemOpenChange(item.id, $event)"
19202
+ >
19203
+ <ng-container [ngTemplateOutlet]="panelTemplate(item.id) ?? null" />
19204
+ </jv-collapsible-panel>
19205
+ }
19206
+ </div>
19207
+ `, 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"] }] });
19208
+ }
19209
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvAccordionComponent, decorators: [{
19210
+ type: Component,
19211
+ args: [{ selector: 'jv-accordion', standalone: true, imports: [NgTemplateOutlet, JvCollapsiblePanelComponent], template: `
19212
+ <div class="jv-accordion">
19213
+ @for (item of items(); track item.id) {
19214
+ <jv-collapsible-panel
19215
+ [title]="item.title"
19216
+ [subtitle]="item.subtitle ?? ''"
19217
+ [icon]="item.icon ?? null"
19218
+ [open]="isItemOpen(item.id)"
19219
+ [keepMounted]="keepMounted()"
19220
+ [disabled]="item.disabled ?? false"
19221
+ [showIndicator]="showIndicator()"
19222
+ [variant]="variant()"
19223
+ (openChange)="handleItemOpenChange(item.id, $event)"
19224
+ >
19225
+ <ng-container [ngTemplateOutlet]="panelTemplate(item.id) ?? null" />
19226
+ </jv-collapsible-panel>
19227
+ }
19228
+ </div>
19229
+ `, styles: [".jv-accordion{display:grid;gap:var(--jv-spacing-sm)}\n"] }]
19230
+ }], 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"] }] } });
19231
+
19232
+ class JvBadgeComponent {
19233
+ tone = input('primary', ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
19234
+ badgeClass = computed(() => `jv-badge--${this.tone()}`, ...(ngDevMode ? [{ debugName: "badgeClass" }] : /* istanbul ignore next */ []));
19235
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19236
+ 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"] });
19237
+ }
19238
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvBadgeComponent, decorators: [{
19239
+ type: Component,
19240
+ 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"] }]
19241
+ }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
19242
+
19243
+ class JvLoaderService {
19244
+ counter = signal(0, ...(ngDevMode ? [{ debugName: "counter" }] : /* istanbul ignore next */ []));
19245
+ visible = signal(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
19246
+ message = signal('Loading', ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
19247
+ delayMs = 300;
19248
+ showTimeout = null;
19249
+ activeCount = this.counter.asReadonly();
19250
+ isVisible = this.visible.asReadonly();
19251
+ liveMessage = this.message.asReadonly();
19252
+ show(message = 'Loading') {
19253
+ this.message.set(message);
19254
+ this.counter.update((count) => count + 1);
19255
+ if (this.counter() === 1) {
19256
+ this.scheduleVisibility();
19257
+ }
19258
+ }
19259
+ hide() {
19260
+ this.counter.update((count) => Math.max(0, count - 1));
19261
+ if (this.counter() === 0) {
19262
+ this.clearTimer();
19263
+ this.visible.set(false);
19264
+ }
19265
+ }
19266
+ scheduleVisibility() {
19267
+ this.clearTimer();
19268
+ this.showTimeout = setTimeout(() => {
19269
+ if (this.counter() > 0) {
19270
+ this.visible.set(true);
19271
+ }
19272
+ this.showTimeout = null;
19273
+ }, this.delayMs);
19274
+ }
19275
+ clearTimer() {
19276
+ if (this.showTimeout) {
19277
+ clearTimeout(this.showTimeout);
19278
+ this.showTimeout = null;
19279
+ }
19280
+ }
19281
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
19282
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderService, providedIn: 'root' });
19283
+ }
19284
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderService, decorators: [{
19285
+ type: Injectable,
19286
+ args: [{ providedIn: 'root' }]
19287
+ }] });
19288
+
19289
+ class JvLoaderComponent {
19290
+ document = inject(DOCUMENT);
19291
+ loaderService = inject(JvLoaderService);
19292
+ constructor() {
19293
+ effect(() => {
19294
+ const locked = this.loaderService.isVisible();
19295
+ const body = this.document.body;
19296
+ const root = this.document.documentElement;
19297
+ body.style.overflow = locked ? 'hidden' : '';
19298
+ root.style.overflow = locked ? 'hidden' : '';
19299
+ });
19300
+ }
19301
+ handleWheel(event) {
19302
+ if (this.loaderService.isVisible()) {
19303
+ event.preventDefault();
19304
+ }
19305
+ }
19306
+ handleTouchMove(event) {
19307
+ if (this.loaderService.isVisible()) {
19308
+ event.preventDefault();
19309
+ }
19310
+ }
19311
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19312
+ 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: `
19313
+ @if (loaderService.isVisible()) {
19314
+ <div class="jv-loader-backdrop" aria-live="polite" aria-atomic="true">
19315
+ <div class="jv-loader-panel" role="status">
19316
+ <span class="spinner" aria-hidden="true"></span>
19317
+ <span>{{ loaderService.liveMessage() }}</span>
19318
+ </div>
19319
+ </div>
19320
+ }
19321
+ `, 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);width:min(35vw,calc(100vw - 2rem));height:min(35vh,calc(100vh - 2rem));max-width:calc(100vw - 2rem);max-height:calc(100vh - 2rem);justify-content:center;border:1px solid #d6dce5;border-radius:var(--jv-radius-lg);background:#f8fafc;box-shadow:var(--jv-shadow-lg);color:#111827;text-align:center;forced-color-adjust:none}.spinner{width:1.25rem;height:1.25rem;border:2px solid currentColor;border-right-color:transparent;border-radius:999px;animation:jv-loader-spin .8s linear infinite}@media(forced-colors:active){.jv-loader-panel{border-color:#d6dce5;background:#f8fafc;color:#111827}}@keyframes jv-loader-spin{to{transform:rotate(360deg)}}\n"] });
19322
+ }
19323
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvLoaderComponent, decorators: [{
19324
+ type: Component,
19325
+ args: [{ selector: 'jv-loader', standalone: true, template: `
19326
+ @if (loaderService.isVisible()) {
19327
+ <div class="jv-loader-backdrop" aria-live="polite" aria-atomic="true">
19328
+ <div class="jv-loader-panel" role="status">
19329
+ <span class="spinner" aria-hidden="true"></span>
19330
+ <span>{{ loaderService.liveMessage() }}</span>
19331
+ </div>
19332
+ </div>
19333
+ }
19334
+ `, 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);width:min(35vw,calc(100vw - 2rem));height:min(35vh,calc(100vh - 2rem));max-width:calc(100vw - 2rem);max-height:calc(100vh - 2rem);justify-content:center;border:1px solid #d6dce5;border-radius:var(--jv-radius-lg);background:#f8fafc;box-shadow:var(--jv-shadow-lg);color:#111827;text-align:center;forced-color-adjust:none}.spinner{width:1.25rem;height:1.25rem;border:2px solid currentColor;border-right-color:transparent;border-radius:999px;animation:jv-loader-spin .8s linear infinite}@media(forced-colors:active){.jv-loader-panel{border-color:#d6dce5;background:#f8fafc;color:#111827}}@keyframes jv-loader-spin{to{transform:rotate(360deg)}}\n"] }]
19335
+ }], ctorParameters: () => [], propDecorators: { handleWheel: [{
19336
+ type: HostListener,
19337
+ args: ['document:wheel', ['$event']]
19338
+ }], handleTouchMove: [{
19339
+ type: HostListener,
19340
+ args: ['document:touchmove', ['$event']]
19341
+ }] } });
19342
+
19343
+ class JvToastService {
19344
+ announcementService = inject(JvAnnouncementService);
19345
+ config = inject(JvUiConfigService);
19346
+ items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
19347
+ positionOverride = signal(null, ...(ngDevMode ? [{ debugName: "positionOverride" }] : /* istanbul ignore next */ []));
19348
+ nextId = 0;
19349
+ toasts = computed(() => this.items(), ...(ngDevMode ? [{ debugName: "toasts" }] : /* istanbul ignore next */ []));
19350
+ position = computed(() => this.positionOverride() ?? this.config.snapshot.toastPosition, ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
19351
+ show(message, tone = 'info', options = {}) {
19352
+ const id = ++this.nextId;
19353
+ const toast = {
19354
+ id,
19355
+ title: options.title,
19356
+ message,
19357
+ tone,
19358
+ duration: options.duration ?? 4000,
19359
+ };
19360
+ this.items.update((current) => [...current, toast]);
19361
+ this.announcementService.announce(toast.title ? `${toast.title}. ${toast.message}` : toast.message, toast.tone === 'danger' ? 'assertive' : 'polite');
19362
+ if (toast.duration > 0) {
19363
+ setTimeout(() => this.dismiss(id), toast.duration);
19364
+ }
19365
+ return id;
19366
+ }
19367
+ dismiss(id) {
19368
+ this.items.update((current) => current.filter((item) => item.id !== id));
19369
+ }
19370
+ setPosition(position) {
19371
+ this.positionOverride.set(position);
19372
+ }
19373
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
19374
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastService, providedIn: 'root' });
19375
+ }
19376
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastService, decorators: [{
19377
+ type: Injectable,
19378
+ args: [{ providedIn: 'root' }]
19379
+ }] });
19380
+
19381
+ class JvToastComponent {
19382
+ toastService = inject(JvToastService);
19383
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19384
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvToastComponent, isStandalone: true, selector: "jv-toast", ngImport: i0, template: `
19385
+ @if (toastService.toasts().length > 0) {
19386
+ <div class="jv-toast-viewport" [class]="'is-' + toastService.position()" aria-live="polite" aria-atomic="false">
19387
+ @for (toast of toastService.toasts(); track toast.id) {
19388
+ <article class="jv-toast-item" [class]="'jv-toast--' + toast.tone" [attr.role]="toast.tone === 'danger' ? 'alert' : 'status'" aria-atomic="true">
19389
+ <div class="jv-toast-copy">
19390
+ @if (toast.title) {
19391
+ <strong>{{ toast.title }}</strong>
19392
+ }
19393
+ <p>{{ toast.message }}</p>
19394
+ </div>
19092
19395
 
19093
19396
  <button type="button" class="jv-toast-close" (click)="toastService.dismiss(toast.id)" aria-label="Dismiss notification">
19094
19397
  <jv-icon name="x" [size]="16" />
@@ -19099,43 +19402,844 @@ class JvToastComponent {
19099
19402
  }
19100
19403
  `, 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
19404
  }
19102
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastComponent, decorators: [{
19405
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvToastComponent, decorators: [{
19406
+ type: Component,
19407
+ args: [{ selector: 'jv-toast', standalone: true, imports: [JvIconComponent], template: `
19408
+ @if (toastService.toasts().length > 0) {
19409
+ <div class="jv-toast-viewport" [class]="'is-' + toastService.position()" aria-live="polite" aria-atomic="false">
19410
+ @for (toast of toastService.toasts(); track toast.id) {
19411
+ <article class="jv-toast-item" [class]="'jv-toast--' + toast.tone" [attr.role]="toast.tone === 'danger' ? 'alert' : 'status'" aria-atomic="true">
19412
+ <div class="jv-toast-copy">
19413
+ @if (toast.title) {
19414
+ <strong>{{ toast.title }}</strong>
19415
+ }
19416
+ <p>{{ toast.message }}</p>
19417
+ </div>
19418
+
19419
+ <button type="button" class="jv-toast-close" (click)="toastService.dismiss(toast.id)" aria-label="Dismiss notification">
19420
+ <jv-icon name="x" [size]="16" />
19421
+ </button>
19422
+ </article>
19423
+ }
19424
+ </div>
19425
+ }
19426
+ `, 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"] }]
19427
+ }] });
19428
+
19429
+ class JvCodeComponent {
19430
+ code = input.required(...(ngDevMode ? [{ debugName: "code" }] : /* istanbul ignore next */ []));
19431
+ language = input('', ...(ngDevMode ? [{ debugName: "language" }] : /* istanbul ignore next */ []));
19432
+ title = input('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
19433
+ font = input('jetbrains-mono', ...(ngDevMode ? [{ debugName: "font" }] : /* istanbul ignore next */ []));
19434
+ editable = input(false, ...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
19435
+ width = input('100%', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
19436
+ height = input('auto', ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
19437
+ codeChange = output();
19438
+ fontFamily = computed(() => {
19439
+ switch (this.font()) {
19440
+ case 'fira-code':
19441
+ return `'Fira Code', 'JetBrains Mono', 'Cascadia Code', 'Consolas', monospace`;
19442
+ case 'cascadia':
19443
+ return `'Cascadia Code', 'JetBrains Mono', 'Fira Code', 'Consolas', monospace`;
19444
+ case 'consolas':
19445
+ return `'Consolas', 'Cascadia Code', 'JetBrains Mono', 'Fira Code', monospace`;
19446
+ case 'system':
19447
+ return `ui-monospace, 'SFMono-Regular', 'Cascadia Code', 'JetBrains Mono', 'Fira Code', 'Consolas', monospace`;
19448
+ default:
19449
+ return `'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace`;
19450
+ }
19451
+ }, ...(ngDevMode ? [{ debugName: "fontFamily" }] : /* istanbul ignore next */ []));
19452
+ languageLabel() {
19453
+ return this.language().trim();
19454
+ }
19455
+ handleInput(event) {
19456
+ this.codeChange.emit(event.target.value);
19457
+ }
19458
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvCodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19459
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvCodeComponent, isStandalone: true, selector: "jv-code", inputs: { code: { classPropertyName: "code", publicName: "code", isSignal: true, isRequired: true, transformFunction: null }, language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, font: { classPropertyName: "font", publicName: "font", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { codeChange: "codeChange" }, ngImport: i0, template: `
19460
+ <section
19461
+ class="jv-code"
19462
+ [style.--jv-code-font-family]="fontFamily()"
19463
+ [style.width]="width()"
19464
+ role="region"
19465
+ [attr.aria-label]="title() || languageLabel() || 'Code block'"
19466
+ >
19467
+ @if (title() || languageLabel()) {
19468
+ <header class="jv-code-header">
19469
+ @if (title()) {
19470
+ <span class="jv-code-title">{{ title() }}</span>
19471
+ }
19472
+
19473
+ @if (languageLabel()) {
19474
+ <span class="jv-code-language">{{ languageLabel() }}</span>
19475
+ }
19476
+ </header>
19477
+ }
19478
+
19479
+ @if (editable()) {
19480
+ <div class="jv-code-body">
19481
+ <textarea
19482
+ class="jv-code-editor"
19483
+ [value]="code()"
19484
+ [style.height]="height()"
19485
+ spellcheck="false"
19486
+ (input)="handleInput($event)"
19487
+ ></textarea>
19488
+ </div>
19489
+ } @else {
19490
+ <div class="jv-code-scroll">
19491
+ <pre class="jv-code-pre" [style.min-height]="height() === 'auto' ? null : height()"><code>{{ code() }}</code></pre>
19492
+ </div>
19493
+ }
19494
+ </section>
19495
+ `, isInline: true, styles: [".jv-code{display:grid;gap:0;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-lg);background:color-mix(in srgb,var(--jv-color-surface-muted) 60%,#0f172a 40%);color:#e5eefb;overflow:hidden}.jv-code-body{min-width:0}.jv-code-header{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-sm);padding:var(--jv-spacing-sm) var(--jv-spacing-md);border-bottom:1px solid rgba(148,163,184,.22);background:#0f172ae0}.jv-code-title{font-size:.875rem;font-weight:600;color:#f8fafc}.jv-code-language{display:inline-flex;align-items:center;min-height:1.75rem;padding:0 .65rem;border:1px solid rgba(125,211,252,.28);border-radius:999px;background:#0ea5e924;color:#c4f1ff;font-size:.75rem;text-transform:uppercase;letter-spacing:.06em}.jv-code-scroll{overflow-x:auto;overflow-y:hidden;scrollbar-width:thin;scrollbar-color:transparent transparent}.jv-code-scroll:hover,.jv-code-scroll:focus-within{scrollbar-color:rgba(148,163,184,.7) transparent}.jv-code-scroll::-webkit-scrollbar{height:.45rem}.jv-code-scroll::-webkit-scrollbar-track{background:transparent}.jv-code-scroll::-webkit-scrollbar-thumb{border-radius:999px;background:transparent}.jv-code-scroll:hover::-webkit-scrollbar-thumb,.jv-code-scroll:focus-within::-webkit-scrollbar-thumb{background:#94a3b8b8}.jv-code-pre{margin:0;min-width:max-content;padding:var(--jv-spacing-md);font-family:var(--jv-code-font-family, \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", \"Consolas\", monospace);font-size:.875rem;line-height:1.6;white-space:pre}.jv-code-editor{display:block;width:100%;min-width:100%;min-height:12rem;padding:var(--jv-spacing-md);border:0;background:transparent;color:inherit;font-family:var(--jv-code-font-family, \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", \"Consolas\", monospace);font-size:.875rem;line-height:1.6;white-space:pre;overflow:auto;resize:both;box-sizing:border-box}.jv-code-editor:focus{outline:2px solid color-mix(in srgb,#7dd3fc 70%,transparent);outline-offset:-2px}code{font:inherit}\n"] });
19496
+ }
19497
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvCodeComponent, decorators: [{
19498
+ type: Component,
19499
+ args: [{ selector: 'jv-code', standalone: true, template: `
19500
+ <section
19501
+ class="jv-code"
19502
+ [style.--jv-code-font-family]="fontFamily()"
19503
+ [style.width]="width()"
19504
+ role="region"
19505
+ [attr.aria-label]="title() || languageLabel() || 'Code block'"
19506
+ >
19507
+ @if (title() || languageLabel()) {
19508
+ <header class="jv-code-header">
19509
+ @if (title()) {
19510
+ <span class="jv-code-title">{{ title() }}</span>
19511
+ }
19512
+
19513
+ @if (languageLabel()) {
19514
+ <span class="jv-code-language">{{ languageLabel() }}</span>
19515
+ }
19516
+ </header>
19517
+ }
19518
+
19519
+ @if (editable()) {
19520
+ <div class="jv-code-body">
19521
+ <textarea
19522
+ class="jv-code-editor"
19523
+ [value]="code()"
19524
+ [style.height]="height()"
19525
+ spellcheck="false"
19526
+ (input)="handleInput($event)"
19527
+ ></textarea>
19528
+ </div>
19529
+ } @else {
19530
+ <div class="jv-code-scroll">
19531
+ <pre class="jv-code-pre" [style.min-height]="height() === 'auto' ? null : height()"><code>{{ code() }}</code></pre>
19532
+ </div>
19533
+ }
19534
+ </section>
19535
+ `, styles: [".jv-code{display:grid;gap:0;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-lg);background:color-mix(in srgb,var(--jv-color-surface-muted) 60%,#0f172a 40%);color:#e5eefb;overflow:hidden}.jv-code-body{min-width:0}.jv-code-header{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-sm);padding:var(--jv-spacing-sm) var(--jv-spacing-md);border-bottom:1px solid rgba(148,163,184,.22);background:#0f172ae0}.jv-code-title{font-size:.875rem;font-weight:600;color:#f8fafc}.jv-code-language{display:inline-flex;align-items:center;min-height:1.75rem;padding:0 .65rem;border:1px solid rgba(125,211,252,.28);border-radius:999px;background:#0ea5e924;color:#c4f1ff;font-size:.75rem;text-transform:uppercase;letter-spacing:.06em}.jv-code-scroll{overflow-x:auto;overflow-y:hidden;scrollbar-width:thin;scrollbar-color:transparent transparent}.jv-code-scroll:hover,.jv-code-scroll:focus-within{scrollbar-color:rgba(148,163,184,.7) transparent}.jv-code-scroll::-webkit-scrollbar{height:.45rem}.jv-code-scroll::-webkit-scrollbar-track{background:transparent}.jv-code-scroll::-webkit-scrollbar-thumb{border-radius:999px;background:transparent}.jv-code-scroll:hover::-webkit-scrollbar-thumb,.jv-code-scroll:focus-within::-webkit-scrollbar-thumb{background:#94a3b8b8}.jv-code-pre{margin:0;min-width:max-content;padding:var(--jv-spacing-md);font-family:var(--jv-code-font-family, \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", \"Consolas\", monospace);font-size:.875rem;line-height:1.6;white-space:pre}.jv-code-editor{display:block;width:100%;min-width:100%;min-height:12rem;padding:var(--jv-spacing-md);border:0;background:transparent;color:inherit;font-family:var(--jv-code-font-family, \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", \"Consolas\", monospace);font-size:.875rem;line-height:1.6;white-space:pre;overflow:auto;resize:both;box-sizing:border-box}.jv-code-editor:focus{outline:2px solid color-mix(in srgb,#7dd3fc 70%,transparent);outline-offset:-2px}code{font:inherit}\n"] }]
19536
+ }], propDecorators: { code: [{ type: i0.Input, args: [{ isSignal: true, alias: "code", required: true }] }], language: [{ type: i0.Input, args: [{ isSignal: true, alias: "language", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], font: [{ type: i0.Input, args: [{ isSignal: true, alias: "font", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], codeChange: [{ type: i0.Output, args: ["codeChange"] }] } });
19537
+
19538
+ class JvDialogComponent {
19539
+ document = inject(DOCUMENT);
19540
+ panelRef = viewChild('panel', ...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
19541
+ open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
19542
+ title = input('Dialog', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
19543
+ closed = output();
19544
+ titleId = `jv-dialog-title-${Math.random().toString(36).slice(2, 8)}`;
19545
+ descriptionId = `jv-dialog-description-${Math.random().toString(36).slice(2, 8)}`;
19546
+ constructor() {
19547
+ effect(() => {
19548
+ if (this.open()) {
19549
+ queueMicrotask(() => this.focusInitialElement());
19550
+ }
19551
+ });
19552
+ }
19553
+ handleDocumentKeydown(event) {
19554
+ if (!this.open()) {
19555
+ return;
19556
+ }
19557
+ if (event.key === 'Escape') {
19558
+ event.preventDefault();
19559
+ this.closed.emit();
19560
+ return;
19561
+ }
19562
+ if (event.key === 'Tab') {
19563
+ this.trapFocus(event);
19564
+ }
19565
+ }
19566
+ focusInitialElement() {
19567
+ const panel = this.panelRef()?.nativeElement;
19568
+ if (!panel) {
19569
+ return;
19570
+ }
19571
+ const firstFocusable = this.getFocusableElements()[0];
19572
+ (firstFocusable ?? panel).focus();
19573
+ }
19574
+ backdropClose() {
19575
+ this.closed.emit();
19576
+ }
19577
+ trapFocus(event) {
19578
+ const focusable = this.getFocusableElements();
19579
+ const panel = this.panelRef()?.nativeElement;
19580
+ if (!panel || focusable.length === 0) {
19581
+ panel?.focus();
19582
+ event.preventDefault();
19583
+ return;
19584
+ }
19585
+ const first = focusable[0];
19586
+ const last = focusable[focusable.length - 1];
19587
+ const active = this.document.activeElement;
19588
+ if (event.shiftKey && active === first) {
19589
+ last.focus();
19590
+ event.preventDefault();
19591
+ }
19592
+ else if (!event.shiftKey && active === last) {
19593
+ first.focus();
19594
+ event.preventDefault();
19595
+ }
19596
+ }
19597
+ getFocusableElements() {
19598
+ const panel = this.panelRef()?.nativeElement;
19599
+ if (!panel) {
19600
+ return [];
19601
+ }
19602
+ return Array.from(panel.querySelectorAll('button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'));
19603
+ }
19604
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19605
+ 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: `
19606
+ @if (open()) {
19607
+ <div class="jv-dialog-backdrop" (click)="backdropClose()">
19608
+ <div class="jv-dialog-panel" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="descriptionId" (click)="$event.stopPropagation()" #panel>
19609
+ <header class="jv-dialog-header">
19610
+ <h2 [id]="titleId">{{ title() }}</h2>
19611
+ </header>
19612
+
19613
+ <div class="jv-dialog-body" [id]="descriptionId">
19614
+ <ng-content></ng-content>
19615
+ </div>
19616
+
19617
+ <footer class="jv-dialog-footer">
19618
+ <ng-content select="[dialog-actions]"></ng-content>
19619
+ </footer>
19620
+ </div>
19621
+ </div>
19622
+ }
19623
+ `, 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"] });
19624
+ }
19625
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDialogComponent, decorators: [{
19626
+ type: Component,
19627
+ args: [{ selector: 'jv-dialog', standalone: true, template: `
19628
+ @if (open()) {
19629
+ <div class="jv-dialog-backdrop" (click)="backdropClose()">
19630
+ <div class="jv-dialog-panel" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="descriptionId" (click)="$event.stopPropagation()" #panel>
19631
+ <header class="jv-dialog-header">
19632
+ <h2 [id]="titleId">{{ title() }}</h2>
19633
+ </header>
19634
+
19635
+ <div class="jv-dialog-body" [id]="descriptionId">
19636
+ <ng-content></ng-content>
19637
+ </div>
19638
+
19639
+ <footer class="jv-dialog-footer">
19640
+ <ng-content select="[dialog-actions]"></ng-content>
19641
+ </footer>
19642
+ </div>
19643
+ </div>
19644
+ }
19645
+ `, 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"] }]
19646
+ }], 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: [{
19647
+ type: HostListener,
19648
+ args: ['document:keydown', ['$event']]
19649
+ }] } });
19650
+
19651
+ let selectIdSequence = 0;
19652
+ class JvSelectComponent {
19653
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
19654
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
19655
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
19656
+ invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
19657
+ describedBy = input('', ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
19658
+ inputId = input(`jv-select-${++selectIdSequence}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
19659
+ modelValue = input('', ...(ngDevMode ? [{ debugName: "modelValue" }] : /* istanbul ignore next */ []));
19660
+ selectionChange = output();
19661
+ value = '';
19662
+ disabled = false;
19663
+ onChange = () => undefined;
19664
+ onTouched = () => undefined;
19665
+ currentValue = computed(() => this.modelValue() || this.value, ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
19666
+ writeValue(value) {
19667
+ this.value = value ?? '';
19668
+ }
19669
+ registerOnChange(fn) {
19670
+ this.onChange = fn;
19671
+ }
19672
+ registerOnTouched(fn) {
19673
+ this.onTouched = fn;
19674
+ }
19675
+ setDisabledState(isDisabled) {
19676
+ this.disabled = isDisabled;
19677
+ }
19678
+ handleChange(event) {
19679
+ const nextValue = event.target.value;
19680
+ this.value = nextValue;
19681
+ this.onChange(nextValue);
19682
+ this.selectionChange.emit(nextValue);
19683
+ }
19684
+ handleBlur() {
19685
+ this.onTouched();
19686
+ }
19687
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19688
+ 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: [
19689
+ {
19690
+ provide: NG_VALUE_ACCESSOR,
19691
+ useExisting: forwardRef(() => JvSelectComponent),
19692
+ multi: true,
19693
+ },
19694
+ ], ngImport: i0, template: `
19695
+ <select
19696
+ class="jv-select"
19697
+ [class.is-invalid]="invalid()"
19698
+ [id]="inputId()"
19699
+ [disabled]="disabled"
19700
+ [required]="required()"
19701
+ [attr.aria-invalid]="invalid() ? 'true' : null"
19702
+ [attr.aria-describedby]="describedBy() || null"
19703
+ [value]="currentValue()"
19704
+ (change)="handleChange($event)"
19705
+ (blur)="handleBlur()"
19706
+ >
19707
+ @if (placeholder()) {
19708
+ <option value="" [disabled]="required()">{{ placeholder() }}</option>
19709
+ }
19710
+
19711
+ @for (option of options(); track option.label + ':' + option.value) {
19712
+ <option [value]="option.value" [disabled]="option.disabled ?? false">{{ option.label }}</option>
19713
+ }
19714
+ </select>
19715
+ `, 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"] });
19716
+ }
19717
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSelectComponent, decorators: [{
19718
+ type: Component,
19719
+ args: [{ selector: 'jv-select', standalone: true, providers: [
19720
+ {
19721
+ provide: NG_VALUE_ACCESSOR,
19722
+ useExisting: forwardRef(() => JvSelectComponent),
19723
+ multi: true,
19724
+ },
19725
+ ], template: `
19726
+ <select
19727
+ class="jv-select"
19728
+ [class.is-invalid]="invalid()"
19729
+ [id]="inputId()"
19730
+ [disabled]="disabled"
19731
+ [required]="required()"
19732
+ [attr.aria-invalid]="invalid() ? 'true' : null"
19733
+ [attr.aria-describedby]="describedBy() || null"
19734
+ [value]="currentValue()"
19735
+ (change)="handleChange($event)"
19736
+ (blur)="handleBlur()"
19737
+ >
19738
+ @if (placeholder()) {
19739
+ <option value="" [disabled]="required()">{{ placeholder() }}</option>
19740
+ }
19741
+
19742
+ @for (option of options(); track option.label + ':' + option.value) {
19743
+ <option [value]="option.value" [disabled]="option.disabled ?? false">{{ option.label }}</option>
19744
+ }
19745
+ </select>
19746
+ `, 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"] }]
19747
+ }], 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"] }] } });
19748
+
19749
+ let datePickerIdSequence = 0;
19750
+ class JvDatePickerComponent {
19751
+ translation = inject(JvTranslationService);
19752
+ announcementService = inject(JvAnnouncementService);
19753
+ instanceId = ++datePickerIdSequence;
19754
+ calendarRootRef = viewChild('calendarRoot', ...(ngDevMode ? [{ debugName: "calendarRootRef" }] : /* istanbul ignore next */ []));
19755
+ triggerButtonRef = viewChild('triggerButton', ...(ngDevMode ? [{ debugName: "triggerButtonRef" }] : /* istanbul ignore next */ []));
19756
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
19757
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
19758
+ dialogTitle = input('', ...(ngDevMode ? [{ debugName: "dialogTitle" }] : /* istanbul ignore next */ []));
19759
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
19760
+ invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
19761
+ hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
19762
+ error = input('', ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
19763
+ describedBy = input('', ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
19764
+ inputId = input(`jv-date-picker-${this.instanceId}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
19765
+ valueInput = input(undefined, { ...(ngDevMode ? { debugName: "valueInput" } : /* istanbul ignore next */ {}), alias: 'value' });
19766
+ selectionChange = output();
19767
+ dialogOpen = signal(false, ...(ngDevMode ? [{ debugName: "dialogOpen" }] : /* istanbul ignore next */ []));
19768
+ visibleMonth = signal(firstOfMonth(new Date()), ...(ngDevMode ? [{ debugName: "visibleMonth" }] : /* istanbul ignore next */ []));
19769
+ focusedDate = signal(startOfDay(new Date()), ...(ngDevMode ? [{ debugName: "focusedDate" }] : /* istanbul ignore next */ []));
19770
+ dialogId = `jv-date-picker-dialog-${this.instanceId}`;
19771
+ value = null;
19772
+ disabled = false;
19773
+ resolvedDialogTitle = computed(() => this.dialogTitle() || this.translation.translate('datePicker.dialogTitle'), ...(ngDevMode ? [{ debugName: "resolvedDialogTitle" }] : /* istanbul ignore next */ []));
19774
+ currentValue = computed(() => this.valueInput() !== undefined ? this.valueInput() : this.value, ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
19775
+ triggerAriaLabel = computed(() => {
19776
+ const name = this.label() || this.resolvedDialogTitle();
19777
+ return `${name}. ${this.displayValue()}`;
19778
+ }, ...(ngDevMode ? [{ debugName: "triggerAriaLabel" }] : /* istanbul ignore next */ []));
19779
+ displayValue = computed(() => {
19780
+ const value = this.currentValue();
19781
+ if (!value) {
19782
+ return this.placeholder() || this.translation.translate('datePicker.placeholder');
19783
+ }
19784
+ return new Intl.DateTimeFormat(this.translation.locale(), {
19785
+ day: '2-digit',
19786
+ month: '2-digit',
19787
+ year: 'numeric',
19788
+ }).format(value);
19789
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
19790
+ monthOptions = computed(() => {
19791
+ const formatter = new Intl.DateTimeFormat(this.translation.locale(), { month: 'long' });
19792
+ return Array.from({ length: 12 }, (_, month) => ({
19793
+ label: capitalize(formatter.format(new Date(2026, month, 1))),
19794
+ value: String(month),
19795
+ }));
19796
+ }, ...(ngDevMode ? [{ debugName: "monthOptions" }] : /* istanbul ignore next */ []));
19797
+ yearOptions = computed(() => {
19798
+ return Array.from({ length: 201 }, (_, index) => {
19799
+ const year = 1900 + index;
19800
+ return { label: String(year), value: String(year) };
19801
+ });
19802
+ }, ...(ngDevMode ? [{ debugName: "yearOptions" }] : /* istanbul ignore next */ []));
19803
+ weekdayLabels = computed(() => {
19804
+ const startDay = this.firstDayOfWeek();
19805
+ const formatter = new Intl.DateTimeFormat(this.translation.locale(), { weekday: 'short' });
19806
+ return Array.from({ length: 7 }, (_, index) => capitalize(formatter.format(addDays(new Date(2026, 5, 7), startDay + index))));
19807
+ }, ...(ngDevMode ? [{ debugName: "weekdayLabels" }] : /* istanbul ignore next */ []));
19808
+ calendarCells = computed(() => {
19809
+ const month = this.visibleMonth();
19810
+ const firstDay = firstOfMonth(month);
19811
+ const daysInCurrentMonth = daysInMonth(month.getFullYear(), month.getMonth());
19812
+ const startOffset = (firstDay.getDay() - this.firstDayOfWeek() + 7) % 7;
19813
+ const gridStart = addDays(firstDay, -startOffset);
19814
+ return Array.from({ length: 42 }, (_, index) => {
19815
+ const date = addDays(gridStart, index);
19816
+ return {
19817
+ date,
19818
+ currentMonth: date.getMonth() === month.getMonth(),
19819
+ isToday: sameDate(date, new Date()),
19820
+ };
19821
+ }).slice(0, Math.ceil((startOffset + daysInCurrentMonth) / 7) * 7);
19822
+ }, ...(ngDevMode ? [{ debugName: "calendarCells" }] : /* istanbul ignore next */ []));
19823
+ onChange = () => undefined;
19824
+ onTouched = () => undefined;
19825
+ writeValue(value) {
19826
+ this.value = value ? startOfDay(value) : null;
19827
+ }
19828
+ registerOnChange(fn) {
19829
+ this.onChange = fn;
19830
+ }
19831
+ registerOnTouched(fn) {
19832
+ this.onTouched = fn;
19833
+ }
19834
+ setDisabledState(isDisabled) {
19835
+ this.disabled = isDisabled;
19836
+ }
19837
+ openDialog() {
19838
+ if (this.disabled) {
19839
+ return;
19840
+ }
19841
+ const baseDate = this.currentValue() ?? startOfDay(new Date());
19842
+ this.visibleMonth.set(firstOfMonth(baseDate));
19843
+ this.focusedDate.set(baseDate);
19844
+ this.dialogOpen.set(true);
19845
+ queueMicrotask(() => this.focusDateButton(baseDate));
19846
+ }
19847
+ closeDialog() {
19848
+ this.dialogOpen.set(false);
19849
+ this.handleBlur();
19850
+ queueMicrotask(() => this.triggerButtonRef()?.nativeElement.focus());
19851
+ }
19852
+ clearSelection() {
19853
+ this.updateValue(null);
19854
+ this.announcementService.announce(this.translation.translate('datePicker.selectionCleared'), 'polite');
19855
+ this.closeDialog();
19856
+ }
19857
+ shiftMonth(offset) {
19858
+ const month = this.visibleMonth();
19859
+ const nextMonth = firstOfMonth(new Date(month.getFullYear(), month.getMonth() + offset, 1));
19860
+ this.visibleMonth.set(nextMonth);
19861
+ this.focusDateWithinVisibleMonth(this.focusedDate());
19862
+ }
19863
+ handleMonthChange(value) {
19864
+ const month = Number(value);
19865
+ const current = this.visibleMonth();
19866
+ this.visibleMonth.set(firstOfMonth(new Date(current.getFullYear(), month, 1)));
19867
+ this.focusDateWithinVisibleMonth(this.focusedDate());
19868
+ }
19869
+ handleYearChange(value) {
19870
+ const year = Number(value);
19871
+ const current = this.visibleMonth();
19872
+ this.visibleMonth.set(firstOfMonth(new Date(year, current.getMonth(), 1)));
19873
+ this.focusDateWithinVisibleMonth(this.focusedDate());
19874
+ }
19875
+ selectDate(date) {
19876
+ const nextValue = startOfDay(date);
19877
+ this.updateValue(nextValue);
19878
+ this.announcementService.announce(this.displayDateForAnnouncement(nextValue), 'polite');
19879
+ this.closeDialog();
19880
+ }
19881
+ handleDayKeydown(event, date) {
19882
+ let nextDate = null;
19883
+ switch (event.key) {
19884
+ case 'ArrowRight':
19885
+ nextDate = addDays(date, 1);
19886
+ break;
19887
+ case 'ArrowLeft':
19888
+ nextDate = addDays(date, -1);
19889
+ break;
19890
+ case 'ArrowDown':
19891
+ nextDate = addDays(date, 7);
19892
+ break;
19893
+ case 'ArrowUp':
19894
+ nextDate = addDays(date, -7);
19895
+ break;
19896
+ case 'Home':
19897
+ nextDate = addDays(date, -((date.getDay() - this.firstDayOfWeek() + 7) % 7));
19898
+ break;
19899
+ case 'End':
19900
+ nextDate = addDays(date, 6 - ((date.getDay() - this.firstDayOfWeek() + 7) % 7));
19901
+ break;
19902
+ case 'Enter':
19903
+ case ' ':
19904
+ event.preventDefault();
19905
+ this.selectDate(date);
19906
+ return;
19907
+ }
19908
+ if (!nextDate) {
19909
+ return;
19910
+ }
19911
+ event.preventDefault();
19912
+ this.focusedDate.set(nextDate);
19913
+ this.visibleMonth.set(firstOfMonth(nextDate));
19914
+ queueMicrotask(() => this.focusDateButton(nextDate));
19915
+ }
19916
+ isSelected(date) {
19917
+ return sameDate(date, this.currentValue());
19918
+ }
19919
+ isFocusedDate(date) {
19920
+ return sameDate(date, this.focusedDate());
19921
+ }
19922
+ isoDate(date) {
19923
+ const year = date.getFullYear();
19924
+ const month = String(date.getMonth() + 1).padStart(2, '0');
19925
+ const day = String(date.getDate()).padStart(2, '0');
19926
+ return `${year}-${month}-${day}`;
19927
+ }
19928
+ handleBlur() {
19929
+ this.onTouched();
19930
+ }
19931
+ updateValue(value) {
19932
+ this.value = value;
19933
+ this.onChange(value);
19934
+ this.selectionChange.emit(value);
19935
+ }
19936
+ focusDateWithinVisibleMonth(date) {
19937
+ const month = this.visibleMonth();
19938
+ const nextDate = startOfDay(new Date(month.getFullYear(), month.getMonth(), Math.min(date.getDate(), daysInMonth(month.getFullYear(), month.getMonth()))));
19939
+ this.focusedDate.set(nextDate);
19940
+ queueMicrotask(() => this.focusDateButton(nextDate));
19941
+ }
19942
+ focusDateButton(date) {
19943
+ const root = this.calendarRootRef()?.nativeElement;
19944
+ root?.querySelector(`[data-jv-date="${this.isoDate(date)}"]`)?.focus();
19945
+ }
19946
+ firstDayOfWeek() {
19947
+ return this.translation.locale() === 'es' ? 1 : 0;
19948
+ }
19949
+ displayDateForAnnouncement(date) {
19950
+ return new Intl.DateTimeFormat(this.translation.locale(), {
19951
+ weekday: 'long',
19952
+ day: 'numeric',
19953
+ month: 'long',
19954
+ year: 'numeric',
19955
+ }).format(date);
19956
+ }
19957
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19958
+ 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: [
19959
+ {
19960
+ provide: NG_VALUE_ACCESSOR,
19961
+ useExisting: forwardRef(() => JvDatePickerComponent),
19962
+ multi: true,
19963
+ },
19964
+ ], viewQueries: [{ propertyName: "calendarRootRef", first: true, predicate: ["calendarRoot"], descendants: true, isSignal: true }, { propertyName: "triggerButtonRef", first: true, predicate: ["triggerButton"], descendants: true, isSignal: true }], ngImport: i0, template: `
19965
+ <jv-form-container
19966
+ [label]="label()"
19967
+ [inputId]="inputId()"
19968
+ [required]="required()"
19969
+ [invalid]="invalid()"
19970
+ [hint]="hint()"
19971
+ [error]="error()"
19972
+ >
19973
+ <button
19974
+ #triggerButton
19975
+ type="button"
19976
+ class="jv-date-picker-trigger"
19977
+ [class.is-invalid]="invalid()"
19978
+ [id]="inputId()"
19979
+ [disabled]="disabled"
19980
+ [attr.aria-haspopup]="'dialog'"
19981
+ [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
19982
+ [attr.aria-controls]="dialogId"
19983
+ [attr.aria-invalid]="invalid() ? 'true' : null"
19984
+ [attr.aria-describedby]="describedBy() || null"
19985
+ [attr.aria-label]="triggerAriaLabel()"
19986
+ [attr.title]="displayValue()"
19987
+ (click)="openDialog()"
19988
+ (blur)="handleBlur()"
19989
+ >
19990
+ <span [class.jv-date-picker-placeholder]="!currentValue()">{{ displayValue() }}</span>
19991
+ <jv-icon name="calendar-days" [size]="18" />
19992
+ </button>
19993
+ </jv-form-container>
19994
+
19995
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
19996
+ <div #calendarRoot [id]="dialogId" class="jv-date-picker-dialog">
19997
+ <div class="jv-date-picker-toolbar">
19998
+ <jv-icon-button icon="chevron-left" variant="ghost" [ariaLabel]="translation.translate('datePicker.prevMonth')" (click)="shiftMonth(-1)" />
19999
+
20000
+ <div class="jv-date-picker-selects">
20001
+ <jv-select [options]="monthOptions()" [modelValue]="visibleMonth().getMonth().toString()" (selectionChange)="handleMonthChange($event)" />
20002
+ <jv-select [options]="yearOptions()" [modelValue]="visibleMonth().getFullYear().toString()" (selectionChange)="handleYearChange($event)" />
20003
+ </div>
20004
+
20005
+ <jv-icon-button icon="chevron-right" variant="ghost" [ariaLabel]="translation.translate('datePicker.nextMonth')" (click)="shiftMonth(1)" />
20006
+ </div>
20007
+
20008
+ <div class="jv-date-picker-weekdays" aria-hidden="true">
20009
+ @for (weekday of weekdayLabels(); track weekday) {
20010
+ <span>{{ weekday }}</span>
20011
+ }
20012
+ </div>
20013
+
20014
+ <div class="jv-date-picker-grid" role="grid" [attr.aria-label]="resolvedDialogTitle()">
20015
+ @for (cell of calendarCells(); track isoDate(cell.date)) {
20016
+ <button
20017
+ type="button"
20018
+ class="jv-date-picker-day"
20019
+ [class.jv-date-picker-day--outside]="!cell.currentMonth"
20020
+ [class.jv-date-picker-day--today]="cell.isToday"
20021
+ [class.jv-date-picker-day--selected]="isSelected(cell.date)"
20022
+ [attr.data-jv-date]="isoDate(cell.date)"
20023
+ [attr.tabindex]="isFocusedDate(cell.date) ? 0 : -1"
20024
+ [attr.aria-selected]="isSelected(cell.date) ? 'true' : 'false'"
20025
+ (click)="selectDate(cell.date)"
20026
+ (keydown)="handleDayKeydown($event, cell.date)"
20027
+ >
20028
+ {{ cell.date.getDate() }}
20029
+ </button>
20030
+ }
20031
+ </div>
20032
+ </div>
20033
+
20034
+ <div dialog-actions class="jv-date-picker-actions">
20035
+ @if (currentValue()) {
20036
+ <jv-button variant="ghost" (click)="clearSelection()">{{ translation.translate('datePicker.clear') }}</jv-button>
20037
+ }
20038
+ <jv-button variant="outline" (click)="closeDialog()">{{ translation.translate('datePicker.close') }}</jv-button>
20039
+ </div>
20040
+ </jv-dialog>
20041
+ `, 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"] }] });
20042
+ }
20043
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDatePickerComponent, decorators: [{
20044
+ type: Component,
20045
+ args: [{ selector: 'jv-date-picker', standalone: true, imports: [JvButtonComponent, JvDialogComponent, JvFormContainerComponent, JvIconButtonComponent, JvIconComponent, JvSelectComponent], providers: [
20046
+ {
20047
+ provide: NG_VALUE_ACCESSOR,
20048
+ useExisting: forwardRef(() => JvDatePickerComponent),
20049
+ multi: true,
20050
+ },
20051
+ ], template: `
20052
+ <jv-form-container
20053
+ [label]="label()"
20054
+ [inputId]="inputId()"
20055
+ [required]="required()"
20056
+ [invalid]="invalid()"
20057
+ [hint]="hint()"
20058
+ [error]="error()"
20059
+ >
20060
+ <button
20061
+ #triggerButton
20062
+ type="button"
20063
+ class="jv-date-picker-trigger"
20064
+ [class.is-invalid]="invalid()"
20065
+ [id]="inputId()"
20066
+ [disabled]="disabled"
20067
+ [attr.aria-haspopup]="'dialog'"
20068
+ [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
20069
+ [attr.aria-controls]="dialogId"
20070
+ [attr.aria-invalid]="invalid() ? 'true' : null"
20071
+ [attr.aria-describedby]="describedBy() || null"
20072
+ [attr.aria-label]="triggerAriaLabel()"
20073
+ [attr.title]="displayValue()"
20074
+ (click)="openDialog()"
20075
+ (blur)="handleBlur()"
20076
+ >
20077
+ <span [class.jv-date-picker-placeholder]="!currentValue()">{{ displayValue() }}</span>
20078
+ <jv-icon name="calendar-days" [size]="18" />
20079
+ </button>
20080
+ </jv-form-container>
20081
+
20082
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
20083
+ <div #calendarRoot [id]="dialogId" class="jv-date-picker-dialog">
20084
+ <div class="jv-date-picker-toolbar">
20085
+ <jv-icon-button icon="chevron-left" variant="ghost" [ariaLabel]="translation.translate('datePicker.prevMonth')" (click)="shiftMonth(-1)" />
20086
+
20087
+ <div class="jv-date-picker-selects">
20088
+ <jv-select [options]="monthOptions()" [modelValue]="visibleMonth().getMonth().toString()" (selectionChange)="handleMonthChange($event)" />
20089
+ <jv-select [options]="yearOptions()" [modelValue]="visibleMonth().getFullYear().toString()" (selectionChange)="handleYearChange($event)" />
20090
+ </div>
20091
+
20092
+ <jv-icon-button icon="chevron-right" variant="ghost" [ariaLabel]="translation.translate('datePicker.nextMonth')" (click)="shiftMonth(1)" />
20093
+ </div>
20094
+
20095
+ <div class="jv-date-picker-weekdays" aria-hidden="true">
20096
+ @for (weekday of weekdayLabels(); track weekday) {
20097
+ <span>{{ weekday }}</span>
20098
+ }
20099
+ </div>
20100
+
20101
+ <div class="jv-date-picker-grid" role="grid" [attr.aria-label]="resolvedDialogTitle()">
20102
+ @for (cell of calendarCells(); track isoDate(cell.date)) {
20103
+ <button
20104
+ type="button"
20105
+ class="jv-date-picker-day"
20106
+ [class.jv-date-picker-day--outside]="!cell.currentMonth"
20107
+ [class.jv-date-picker-day--today]="cell.isToday"
20108
+ [class.jv-date-picker-day--selected]="isSelected(cell.date)"
20109
+ [attr.data-jv-date]="isoDate(cell.date)"
20110
+ [attr.tabindex]="isFocusedDate(cell.date) ? 0 : -1"
20111
+ [attr.aria-selected]="isSelected(cell.date) ? 'true' : 'false'"
20112
+ (click)="selectDate(cell.date)"
20113
+ (keydown)="handleDayKeydown($event, cell.date)"
20114
+ >
20115
+ {{ cell.date.getDate() }}
20116
+ </button>
20117
+ }
20118
+ </div>
20119
+ </div>
20120
+
20121
+ <div dialog-actions class="jv-date-picker-actions">
20122
+ @if (currentValue()) {
20123
+ <jv-button variant="ghost" (click)="clearSelection()">{{ translation.translate('datePicker.clear') }}</jv-button>
20124
+ }
20125
+ <jv-button variant="outline" (click)="closeDialog()">{{ translation.translate('datePicker.close') }}</jv-button>
20126
+ </div>
20127
+ </jv-dialog>
20128
+ `, 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"] }]
20129
+ }], 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"] }] } });
20130
+ function daysInMonth(year, month) {
20131
+ return new Date(year, month + 1, 0).getDate();
20132
+ }
20133
+ function firstOfMonth(date) {
20134
+ return new Date(date.getFullYear(), date.getMonth(), 1);
20135
+ }
20136
+ function startOfDay(date) {
20137
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
20138
+ }
20139
+ function addDays(date, amount) {
20140
+ return startOfDay(new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount));
20141
+ }
20142
+ function sameDate(left, right) {
20143
+ if (!left || !right) {
20144
+ return false;
20145
+ }
20146
+ return left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate();
20147
+ }
20148
+ function capitalize(value) {
20149
+ return value.charAt(0).toUpperCase() + value.slice(1);
20150
+ }
20151
+
20152
+ class JvListComponent {
20153
+ role = input('list', ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
20154
+ ariaRole = input(null, ...(ngDevMode ? [{ debugName: "ariaRole" }] : /* istanbul ignore next */ []));
20155
+ variant = input(null, ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
20156
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
20157
+ variantClass = computed(() => {
20158
+ const v = this.variant();
20159
+ return v ? `jv-list--${v}` : '';
20160
+ }, ...(ngDevMode ? [{ debugName: "variantClass" }] : /* istanbul ignore next */ []));
20161
+ resolvedRole = computed(() => this.ariaRole() ?? (this.role() === 'navigation' ? 'list' : null), ...(ngDevMode ? [{ debugName: "resolvedRole" }] : /* istanbul ignore next */ []));
20162
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20163
+ 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: `
20164
+ <ul
20165
+ class="jv-list"
20166
+ [class]="variantClass()"
20167
+ [attr.role]="resolvedRole()"
20168
+ [attr.aria-label]="ariaLabel()"
20169
+ >
20170
+ <ng-content />
20171
+ </ul>
20172
+ `, 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"] });
20173
+ }
20174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvListComponent, decorators: [{
19103
20175
  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
- }] });
20176
+ args: [{ selector: 'jv-list', standalone: true, template: `
20177
+ <ul
20178
+ class="jv-list"
20179
+ [class]="variantClass()"
20180
+ [attr.role]="resolvedRole()"
20181
+ [attr.aria-label]="ariaLabel()"
20182
+ >
20183
+ <ng-content />
20184
+ </ul>
20185
+ `, 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"] }]
20186
+ }], 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
20187
 
19126
- let radioIdSequence = 0;
19127
- class JvRadioComponent {
19128
- name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
20188
+ let modalSelectorIdSequence = 0;
20189
+ class JvModalSelectorComponent {
20190
+ announcementService = inject(JvAnnouncementService);
20191
+ translation = inject(JvTranslationService);
20192
+ instanceId = ++modalSelectorIdSequence;
20193
+ triggerButtonRef = viewChild('triggerButton', ...(ngDevMode ? [{ debugName: "triggerButtonRef" }] : /* istanbul ignore next */ []));
20194
+ optionElements = viewChildren('optionElement', ...(ngDevMode ? [{ debugName: "optionElements" }] : /* istanbul ignore next */ []));
20195
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
19129
20196
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
19130
- radioValue = input.required(...(ngDevMode ? [{ debugName: "radioValue" }] : /* istanbul ignore next */ []));
20197
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
20198
+ dialogTitle = input('', ...(ngDevMode ? [{ debugName: "dialogTitle" }] : /* istanbul ignore next */ []));
20199
+ dialogSubtitle = input('', ...(ngDevMode ? [{ debugName: "dialogSubtitle" }] : /* istanbul ignore next */ []));
20200
+ searchPlaceholder = input('', ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : /* istanbul ignore next */ []));
20201
+ emptyMessage = input('', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
20202
+ clearLabel = input('', ...(ngDevMode ? [{ debugName: "clearLabel" }] : /* istanbul ignore next */ []));
20203
+ closeLabel = input('', ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
20204
+ showSearch = input(true, ...(ngDevMode ? [{ debugName: "showSearch" }] : /* istanbul ignore next */ []));
20205
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
20206
+ invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
20207
+ hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
20208
+ error = input('', ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
19131
20209
  describedBy = input('', ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
19132
- inputId = input(`jv-radio-${++radioIdSequence}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
19133
- value = '';
20210
+ inputId = input(`jv-modal-selector-${this.instanceId}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
20211
+ valueInput = input(undefined, { ...(ngDevMode ? { debugName: "valueInput" } : /* istanbul ignore next */ {}), alias: 'value' });
20212
+ selectionChange = output();
20213
+ dialogOpen = signal(false, ...(ngDevMode ? [{ debugName: "dialogOpen" }] : /* istanbul ignore next */ []));
20214
+ searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : /* istanbul ignore next */ []));
20215
+ dialogId = `jv-modal-selector-dialog-${this.instanceId}`;
20216
+ searchInputId = `jv-modal-selector-search-${this.instanceId}`;
20217
+ value = null;
19134
20218
  disabled = false;
20219
+ resolvedDialogTitle = computed(() => this.dialogTitle() || this.label() || this.translation.translate('modalSelector.placeholder'), ...(ngDevMode ? [{ debugName: "resolvedDialogTitle" }] : /* istanbul ignore next */ []));
20220
+ currentValue = computed(() => (this.valueInput() !== undefined ? this.valueInput() : this.value), ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
20221
+ selectedOption = computed(() => this.options().find((option) => Object.is(option.value, this.currentValue())) ?? null, ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
20222
+ resolvedPlaceholder = computed(() => this.placeholder() || this.translation.translate('modalSelector.placeholder'), ...(ngDevMode ? [{ debugName: "resolvedPlaceholder" }] : /* istanbul ignore next */ []));
20223
+ triggerAriaLabel = computed(() => {
20224
+ const name = this.label() || this.resolvedDialogTitle();
20225
+ const value = this.selectedOption()?.label ?? this.resolvedPlaceholder();
20226
+ return `${name}. ${value}`;
20227
+ }, ...(ngDevMode ? [{ debugName: "triggerAriaLabel" }] : /* istanbul ignore next */ []));
20228
+ resultsLabel = computed(() => this.translation.translate('modalSelector.results', { count: this.filteredOptions().length }), ...(ngDevMode ? [{ debugName: "resultsLabel" }] : /* istanbul ignore next */ []));
20229
+ filteredOptions = computed(() => {
20230
+ const query = this.searchQuery().trim().toLocaleLowerCase();
20231
+ if (!query) {
20232
+ return this.options();
20233
+ }
20234
+ return this.options().filter((option) => {
20235
+ const haystack = [option.label, option.description ?? '', ...(option.keywords ?? [])].join(' ').toLocaleLowerCase();
20236
+ return haystack.includes(query);
20237
+ });
20238
+ }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
19135
20239
  onChange = () => undefined;
19136
20240
  onTouched = () => undefined;
19137
20241
  writeValue(value) {
19138
- this.value = value ?? '';
20242
+ this.value = value ?? null;
19139
20243
  }
19140
20244
  registerOnChange(fn) {
19141
20245
  this.onChange = fn;
@@ -19146,77 +20250,312 @@ class JvRadioComponent {
19146
20250
  setDisabledState(isDisabled) {
19147
20251
  this.disabled = isDisabled;
19148
20252
  }
19149
- handleChange() {
19150
- const nextValue = this.radioValue();
19151
- this.value = nextValue;
19152
- this.onChange(nextValue);
20253
+ openDialog() {
20254
+ if (this.disabled) {
20255
+ return;
20256
+ }
20257
+ this.searchQuery.set('');
20258
+ this.dialogOpen.set(true);
20259
+ }
20260
+ closeDialog() {
20261
+ this.dialogOpen.set(false);
20262
+ this.handleBlur();
20263
+ queueMicrotask(() => this.triggerButtonRef()?.nativeElement.focus());
20264
+ }
20265
+ clearSelection() {
20266
+ this.updateValue(null);
20267
+ this.announcementService.announce(this.translation.translate('modalSelector.selectionCleared'), 'polite');
20268
+ this.closeDialog();
20269
+ }
20270
+ selectOption(option) {
20271
+ if (option.disabled) {
20272
+ return;
20273
+ }
20274
+ this.updateValue(option.value);
20275
+ this.announcementService.announce(this.translation.translate('modalSelector.selected', { label: option.label }), 'polite');
20276
+ this.closeDialog();
20277
+ }
20278
+ isSelected(option) {
20279
+ return Object.is(option.value, this.currentValue());
20280
+ }
20281
+ getOptionTabIndex(option, index) {
20282
+ if (option.disabled) {
20283
+ return -1;
20284
+ }
20285
+ const selected = this.filteredOptions().find((item) => this.isSelected(item) && !item.disabled);
20286
+ if (selected) {
20287
+ return this.isSelected(option) ? 0 : -1;
20288
+ }
20289
+ const firstEnabledIndex = this.filteredOptions().findIndex((item) => !item.disabled);
20290
+ return index === firstEnabledIndex ? 0 : -1;
20291
+ }
20292
+ optionId(index) {
20293
+ return `jv-modal-selector-option-${this.instanceId}-${index}`;
20294
+ }
20295
+ handleOptionKeydown(event, index, option) {
20296
+ switch (event.key) {
20297
+ case 'ArrowDown':
20298
+ event.preventDefault();
20299
+ this.focusRelativeOption(index, 1);
20300
+ break;
20301
+ case 'ArrowUp':
20302
+ event.preventDefault();
20303
+ this.focusRelativeOption(index, -1);
20304
+ break;
20305
+ case 'Home':
20306
+ event.preventDefault();
20307
+ this.focusOptionByIndex(0);
20308
+ break;
20309
+ case 'End':
20310
+ event.preventDefault();
20311
+ this.focusOptionByIndex(this.filteredOptions().length - 1);
20312
+ break;
20313
+ case 'Enter':
20314
+ case ' ':
20315
+ event.preventDefault();
20316
+ this.selectOption(option);
20317
+ break;
20318
+ }
19153
20319
  }
19154
20320
  handleBlur() {
19155
20321
  this.onTouched();
19156
20322
  }
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: [
20323
+ updateValue(value) {
20324
+ this.value = value;
20325
+ this.onChange(value);
20326
+ this.selectionChange.emit(value);
20327
+ }
20328
+ focusRelativeOption(startIndex, direction) {
20329
+ const options = this.filteredOptions();
20330
+ for (let offset = 1; offset <= options.length; offset++) {
20331
+ const candidateIndex = (startIndex + direction * offset + options.length) % options.length;
20332
+ if (!options[candidateIndex]?.disabled) {
20333
+ this.focusOptionByIndex(candidateIndex);
20334
+ return;
20335
+ }
20336
+ }
20337
+ }
20338
+ focusOptionByIndex(index) {
20339
+ const optionElements = this.optionElements();
20340
+ optionElements?.[index]?.nativeElement.focus();
20341
+ }
20342
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvModalSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20343
+ 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
20344
  {
19160
20345
  provide: NG_VALUE_ACCESSOR,
19161
- useExisting: forwardRef(() => JvRadioComponent),
20346
+ useExisting: forwardRef(() => JvModalSelectorComponent),
19162
20347
  multi: true,
19163
20348
  },
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"] });
20349
+ ], viewQueries: [{ propertyName: "triggerButtonRef", first: true, predicate: ["triggerButton"], descendants: true, isSignal: true }, { propertyName: "optionElements", predicate: ["optionElement"], descendants: true, isSignal: true }], ngImport: i0, template: `
20350
+ <jv-form-container
20351
+ [label]="label()"
20352
+ [inputId]="inputId()"
20353
+ [required]="required()"
20354
+ [invalid]="invalid()"
20355
+ [hint]="hint()"
20356
+ [error]="error()"
20357
+ >
20358
+ <button
20359
+ #triggerButton
20360
+ type="button"
20361
+ class="jv-modal-selector-trigger"
20362
+ [class.is-invalid]="invalid()"
20363
+ [id]="inputId()"
20364
+ [disabled]="disabled"
20365
+ [attr.aria-haspopup]="'dialog'"
20366
+ [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
20367
+ [attr.aria-controls]="dialogId"
20368
+ [attr.aria-invalid]="invalid() ? 'true' : null"
20369
+ [attr.aria-describedby]="describedBy() || null"
20370
+ [attr.aria-label]="triggerAriaLabel()"
20371
+ (click)="openDialog()"
20372
+ (blur)="handleBlur()"
20373
+ >
20374
+ <span class="jv-modal-selector-trigger-copy">
20375
+ <span [class.jv-modal-selector-trigger-placeholder]="!selectedOption()">
20376
+ {{ selectedOption()?.label ?? resolvedPlaceholder() }}
20377
+ </span>
20378
+ </span>
20379
+ <jv-icon name="chevrons-up-down" [size]="18" />
20380
+ </button>
20381
+ </jv-form-container>
20382
+
20383
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
20384
+ <div [id]="dialogId" class="jv-modal-selector-dialog">
20385
+ @if (dialogSubtitle()) {
20386
+ <p class="jv-modal-selector-subtitle">{{ dialogSubtitle() }}</p>
20387
+ }
20388
+
20389
+ @if (showSearch()) {
20390
+ <jv-input
20391
+ type="search"
20392
+ [inputId]="searchInputId"
20393
+ [placeholder]="searchPlaceholder() || translation.translate('modalSelector.searchPlaceholder')"
20394
+ [ngModel]="searchQuery()"
20395
+ (ngModelChange)="searchQuery.set($event)"
20396
+ />
20397
+ }
20398
+
20399
+ <p class="jv-modal-selector-results" aria-live="polite">{{ resultsLabel() }}</p>
20400
+
20401
+ @if (filteredOptions().length > 0) {
20402
+ <jv-list variant="bordered" [ariaRole]="'listbox'" [ariaLabel]="resolvedDialogTitle()">
20403
+ @for (option of filteredOptions(); track option.value; let index = $index) {
20404
+ <li role="presentation">
20405
+ <div
20406
+ #optionElement
20407
+ class="jv-modal-selector-option"
20408
+ [class.jv-modal-selector-option--selected]="isSelected(option)"
20409
+ [class.jv-modal-selector-option--disabled]="option.disabled ?? false"
20410
+ [id]="optionId(index)"
20411
+ role="option"
20412
+ [attr.aria-selected]="isSelected(option) ? 'true' : 'false'"
20413
+ [attr.tabindex]="getOptionTabIndex(option, index)"
20414
+ (click)="selectOption(option)"
20415
+ (keydown)="handleOptionKeydown($event, index, option)"
20416
+ >
20417
+ <div class="jv-modal-selector-option-copy">
20418
+ <span class="jv-modal-selector-option-label">{{ option.label }}</span>
20419
+ @if (option.description) {
20420
+ <span class="jv-modal-selector-option-description">{{ option.description }}</span>
20421
+ }
20422
+ </div>
20423
+ @if (isSelected(option)) {
20424
+ <jv-icon name="check" [size]="16" />
20425
+ }
20426
+ </div>
20427
+ </li>
20428
+ }
20429
+ </jv-list>
20430
+ } @else {
20431
+ <div class="jv-modal-selector-empty" aria-live="polite">{{ emptyMessage() || translation.translate('modalSelector.emptyMessage') }}</div>
20432
+ }
20433
+ </div>
20434
+
20435
+ <div dialog-actions class="jv-modal-selector-actions">
20436
+ @if (selectedOption()) {
20437
+ <jv-button variant="ghost" (click)="clearSelection()">{{ clearLabel() || translation.translate('modalSelector.clear') }}</jv-button>
20438
+ }
20439
+ <jv-button variant="outline" (click)="closeDialog()">{{ closeLabel() || translation.translate('modalSelector.close') }}</jv-button>
20440
+ </div>
20441
+ </jv-dialog>
20442
+ `, 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
20443
  }
19180
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvRadioComponent, decorators: [{
20444
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvModalSelectorComponent, decorators: [{
19181
20445
  type: Component,
19182
- args: [{ selector: 'jv-radio', standalone: true, providers: [
20446
+ args: [{ selector: 'jv-modal-selector', standalone: true, imports: [FormsModule, JvButtonComponent, JvDialogComponent, JvFormContainerComponent, JvIconComponent, JvInputComponent, JvListComponent], providers: [
19183
20447
  {
19184
20448
  provide: NG_VALUE_ACCESSOR,
19185
- useExisting: forwardRef(() => JvRadioComponent),
20449
+ useExisting: forwardRef(() => JvModalSelectorComponent),
19186
20450
  multi: true,
19187
20451
  },
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 }] }] } });
20452
+ ], template: `
20453
+ <jv-form-container
20454
+ [label]="label()"
20455
+ [inputId]="inputId()"
20456
+ [required]="required()"
20457
+ [invalid]="invalid()"
20458
+ [hint]="hint()"
20459
+ [error]="error()"
20460
+ >
20461
+ <button
20462
+ #triggerButton
20463
+ type="button"
20464
+ class="jv-modal-selector-trigger"
20465
+ [class.is-invalid]="invalid()"
20466
+ [id]="inputId()"
20467
+ [disabled]="disabled"
20468
+ [attr.aria-haspopup]="'dialog'"
20469
+ [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
20470
+ [attr.aria-controls]="dialogId"
20471
+ [attr.aria-invalid]="invalid() ? 'true' : null"
20472
+ [attr.aria-describedby]="describedBy() || null"
20473
+ [attr.aria-label]="triggerAriaLabel()"
20474
+ (click)="openDialog()"
20475
+ (blur)="handleBlur()"
20476
+ >
20477
+ <span class="jv-modal-selector-trigger-copy">
20478
+ <span [class.jv-modal-selector-trigger-placeholder]="!selectedOption()">
20479
+ {{ selectedOption()?.label ?? resolvedPlaceholder() }}
20480
+ </span>
20481
+ </span>
20482
+ <jv-icon name="chevrons-up-down" [size]="18" />
20483
+ </button>
20484
+ </jv-form-container>
19204
20485
 
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 */ []));
20486
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
20487
+ <div [id]="dialogId" class="jv-modal-selector-dialog">
20488
+ @if (dialogSubtitle()) {
20489
+ <p class="jv-modal-selector-subtitle">{{ dialogSubtitle() }}</p>
20490
+ }
20491
+
20492
+ @if (showSearch()) {
20493
+ <jv-input
20494
+ type="search"
20495
+ [inputId]="searchInputId"
20496
+ [placeholder]="searchPlaceholder() || translation.translate('modalSelector.searchPlaceholder')"
20497
+ [ngModel]="searchQuery()"
20498
+ (ngModelChange)="searchQuery.set($event)"
20499
+ />
20500
+ }
20501
+
20502
+ <p class="jv-modal-selector-results" aria-live="polite">{{ resultsLabel() }}</p>
20503
+
20504
+ @if (filteredOptions().length > 0) {
20505
+ <jv-list variant="bordered" [ariaRole]="'listbox'" [ariaLabel]="resolvedDialogTitle()">
20506
+ @for (option of filteredOptions(); track option.value; let index = $index) {
20507
+ <li role="presentation">
20508
+ <div
20509
+ #optionElement
20510
+ class="jv-modal-selector-option"
20511
+ [class.jv-modal-selector-option--selected]="isSelected(option)"
20512
+ [class.jv-modal-selector-option--disabled]="option.disabled ?? false"
20513
+ [id]="optionId(index)"
20514
+ role="option"
20515
+ [attr.aria-selected]="isSelected(option) ? 'true' : 'false'"
20516
+ [attr.tabindex]="getOptionTabIndex(option, index)"
20517
+ (click)="selectOption(option)"
20518
+ (keydown)="handleOptionKeydown($event, index, option)"
20519
+ >
20520
+ <div class="jv-modal-selector-option-copy">
20521
+ <span class="jv-modal-selector-option-label">{{ option.label }}</span>
20522
+ @if (option.description) {
20523
+ <span class="jv-modal-selector-option-description">{{ option.description }}</span>
20524
+ }
20525
+ </div>
20526
+ @if (isSelected(option)) {
20527
+ <jv-icon name="check" [size]="16" />
20528
+ }
20529
+ </div>
20530
+ </li>
20531
+ }
20532
+ </jv-list>
20533
+ } @else {
20534
+ <div class="jv-modal-selector-empty" aria-live="polite">{{ emptyMessage() || translation.translate('modalSelector.emptyMessage') }}</div>
20535
+ }
20536
+ </div>
20537
+
20538
+ <div dialog-actions class="jv-modal-selector-actions">
20539
+ @if (selectedOption()) {
20540
+ <jv-button variant="ghost" (click)="clearSelection()">{{ clearLabel() || translation.translate('modalSelector.clear') }}</jv-button>
20541
+ }
20542
+ <jv-button variant="outline" (click)="closeDialog()">{{ closeLabel() || translation.translate('modalSelector.close') }}</jv-button>
20543
+ </div>
20544
+ </jv-dialog>
20545
+ `, 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"] }]
20546
+ }], 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"] }] } });
20547
+
20548
+ let radioIdSequence = 0;
20549
+ class JvRadioComponent {
20550
+ name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
20551
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
20552
+ radioValue = input.required(...(ngDevMode ? [{ debugName: "radioValue" }] : /* istanbul ignore next */ []));
19211
20553
  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();
20554
+ inputId = input(`jv-radio-${++radioIdSequence}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
19215
20555
  value = '';
19216
20556
  disabled = false;
19217
20557
  onChange = () => undefined;
19218
20558
  onTouched = () => undefined;
19219
- currentValue = computed(() => this.modelValue() || this.value, ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
19220
20559
  writeValue(value) {
19221
20560
  this.value = value ?? '';
19222
20561
  }
@@ -19229,76 +20568,61 @@ class JvSelectComponent {
19229
20568
  setDisabledState(isDisabled) {
19230
20569
  this.disabled = isDisabled;
19231
20570
  }
19232
- handleChange(event) {
19233
- const nextValue = event.target.value;
20571
+ handleChange() {
20572
+ const nextValue = this.radioValue();
19234
20573
  this.value = nextValue;
19235
20574
  this.onChange(nextValue);
19236
- this.selectionChange.emit(nextValue);
19237
20575
  }
19238
20576
  handleBlur() {
19239
20577
  this.onTouched();
19240
20578
  }
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: [
20579
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvRadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20580
+ 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
20581
  {
19244
20582
  provide: NG_VALUE_ACCESSOR,
19245
- useExisting: forwardRef(() => JvSelectComponent),
20583
+ useExisting: forwardRef(() => JvRadioComponent),
19246
20584
  multi: true,
19247
20585
  },
19248
20586
  ], 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"] });
20587
+ <label class="jv-radio" [class.is-disabled]="disabled">
20588
+ <input
20589
+ type="radio"
20590
+ [id]="inputId()"
20591
+ [name]="name()"
20592
+ [checked]="value === radioValue()"
20593
+ [disabled]="disabled"
20594
+ [attr.aria-describedby]="describedBy() || null"
20595
+ (change)="handleChange()"
20596
+ (blur)="handleBlur()"
20597
+ />
20598
+ <span>{{ label() }}</span>
20599
+ </label>
20600
+ `, 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
20601
  }
19271
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSelectComponent, decorators: [{
20602
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvRadioComponent, decorators: [{
19272
20603
  type: Component,
19273
- args: [{ selector: 'jv-select', standalone: true, providers: [
20604
+ args: [{ selector: 'jv-radio', standalone: true, providers: [
19274
20605
  {
19275
20606
  provide: NG_VALUE_ACCESSOR,
19276
- useExisting: forwardRef(() => JvSelectComponent),
20607
+ useExisting: forwardRef(() => JvRadioComponent),
19277
20608
  multi: true,
19278
20609
  },
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"] }] } });
20610
+ ], template: `
20611
+ <label class="jv-radio" [class.is-disabled]="disabled">
20612
+ <input
20613
+ type="radio"
20614
+ [id]="inputId()"
20615
+ [name]="name()"
20616
+ [checked]="value === radioValue()"
20617
+ [disabled]="disabled"
20618
+ [attr.aria-describedby]="describedBy() || null"
20619
+ (change)="handleChange()"
20620
+ (blur)="handleBlur()"
20621
+ />
20622
+ <span>{{ label() }}</span>
20623
+ </label>
20624
+ `, 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"] }]
20625
+ }], 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
20626
 
19303
20627
  let switchIdSequence = 0;
19304
20628
  class JvSwitchComponent {
@@ -19478,6 +20802,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
19478
20802
 
19479
20803
  class JvDialogService {
19480
20804
  announcementService = inject(JvAnnouncementService);
20805
+ translation = inject(JvTranslationService);
19481
20806
  document = inject(DOCUMENT);
19482
20807
  activeDialogSignal = signal(null, ...(ngDevMode ? [{ debugName: "activeDialogSignal" }] : /* istanbul ignore next */ []));
19483
20808
  nextId = 0;
@@ -19487,15 +20812,16 @@ class JvDialogService {
19487
20812
  confirm(options) {
19488
20813
  const id = ++this.nextId;
19489
20814
  this.restoreTarget = this.document.activeElement instanceof HTMLElement ? this.document.activeElement : null;
20815
+ const title = options.title ?? this.translation.translate('dialog.confirmTitle');
19490
20816
  this.activeDialogSignal.set({
19491
20817
  id,
19492
- title: options.title,
20818
+ title,
19493
20819
  message: options.message,
19494
- confirmLabel: options.confirmLabel ?? 'Confirm',
19495
- cancelLabel: options.cancelLabel ?? 'Cancel',
20820
+ confirmLabel: options.confirmLabel ?? this.translation.translate('dialog.confirm'),
20821
+ cancelLabel: options.cancelLabel ?? this.translation.translate('dialog.cancel'),
19496
20822
  tone: options.tone ?? 'primary',
19497
20823
  });
19498
- this.announcementService.announce(`${options.title}. ${options.message}`, 'assertive');
20824
+ this.announcementService.announce(`${title}. ${options.message}`, 'assertive');
19499
20825
  return new Promise((resolve) => {
19500
20826
  this.resolver = resolve;
19501
20827
  });
@@ -19515,127 +20841,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
19515
20841
  args: [{ providedIn: 'root' }]
19516
20842
  }] });
19517
20843
 
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
20844
  class JvConfirmDialogComponent {
19632
20845
  dialogService = inject(JvDialogService);
20846
+ translation = inject(JvTranslationService);
19633
20847
  activeDialog = this.dialogService.activeDialog;
19634
20848
  isOpen = computed(() => this.activeDialog() !== null, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
19635
- dialogTitle = computed(() => this.activeDialog()?.title ?? 'Confirm', ...(ngDevMode ? [{ debugName: "dialogTitle" }] : /* istanbul ignore next */ []));
20849
+ dialogTitle = computed(() => this.activeDialog()?.title ?? this.translation.translate('dialog.confirmTitle'), ...(ngDevMode ? [{ debugName: "dialogTitle" }] : /* istanbul ignore next */ []));
19636
20850
  dialogMessage = computed(() => this.activeDialog()?.message ?? '', ...(ngDevMode ? [{ debugName: "dialogMessage" }] : /* istanbul ignore next */ []));
19637
- cancelLabel = computed(() => this.activeDialog()?.cancelLabel ?? 'Cancel', ...(ngDevMode ? [{ debugName: "cancelLabel" }] : /* istanbul ignore next */ []));
19638
- confirmLabel = computed(() => this.activeDialog()?.confirmLabel ?? 'Confirm', ...(ngDevMode ? [{ debugName: "confirmLabel" }] : /* istanbul ignore next */ []));
20851
+ cancelLabel = computed(() => this.activeDialog()?.cancelLabel ?? this.translation.translate('dialog.cancel'), ...(ngDevMode ? [{ debugName: "cancelLabel" }] : /* istanbul ignore next */ []));
20852
+ confirmLabel = computed(() => this.activeDialog()?.confirmLabel ?? this.translation.translate('dialog.confirm'), ...(ngDevMode ? [{ debugName: "confirmLabel" }] : /* istanbul ignore next */ []));
19639
20853
  confirmVariant = computed(() => {
19640
20854
  const tone = this.activeDialog()?.tone ?? 'primary';
19641
20855
  return tone === 'info' ? 'primary' : tone;
@@ -19650,8 +20864,8 @@ class JvConfirmDialogComponent {
19650
20864
  <p>{{ dialogMessage() }}</p>
19651
20865
 
19652
20866
  <div dialog-actions class="actions">
19653
- <jv-button variant="ghost" (click)="dialogService.resolve(false)">{{ cancelLabel() }}</jv-button>
19654
20867
  <jv-button [variant]="confirmVariant()" (click)="dialogService.resolve(true)">{{ confirmLabel() }}</jv-button>
20868
+ <jv-button variant="ghost" (click)="dialogService.resolve(false)">{{ cancelLabel() }}</jv-button>
19655
20869
  </div>
19656
20870
  </jv-dialog>
19657
20871
  `, isInline: true, styles: [".actions{display:flex;justify-content:flex-end;gap:var(--jv-spacing-sm);flex-wrap:wrap}p{margin:0;color:var(--jv-color-foreground-muted)}\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"] }] });
@@ -19667,8 +20881,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
19667
20881
  <p>{{ dialogMessage() }}</p>
19668
20882
 
19669
20883
  <div dialog-actions class="actions">
19670
- <jv-button variant="ghost" (click)="dialogService.resolve(false)">{{ cancelLabel() }}</jv-button>
19671
20884
  <jv-button [variant]="confirmVariant()" (click)="dialogService.resolve(true)">{{ confirmLabel() }}</jv-button>
20885
+ <jv-button variant="ghost" (click)="dialogService.resolve(false)">{{ cancelLabel() }}</jv-button>
19672
20886
  </div>
19673
20887
  </jv-dialog>
19674
20888
  `, styles: [".actions{display:flex;justify-content:flex-end;gap:var(--jv-spacing-sm);flex-wrap:wrap}p{margin:0;color:var(--jv-color-foreground-muted)}\n"] }]
@@ -19784,321 +20998,321 @@ class JvSidebarComponent {
19784
20998
  target?.scrollIntoView({ block: 'start', behavior: 'smooth' });
19785
20999
  }
19786
21000
  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>
20101
- `, 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"] }]
21001
+ 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: `
21002
+ @if (mobileOpen()) {
21003
+ <div class="jv-sidebar-backdrop" (click)="mobileClose.emit()"></div>
21004
+ }
21005
+
21006
+ <aside
21007
+ class="jv-sidebar"
21008
+ [class.is-collapsed]="collapsed()"
21009
+ [class.is-mobile-open]="mobileOpen()"
21010
+ >
21011
+ <div class="jv-sidebar-brand">
21012
+ <div class="brand-mark">
21013
+ <jv-icon [name]="brandIcon()" [size]="18" />
21014
+ </div>
21015
+
21016
+ @if (!collapsed()) {
21017
+ <div class="brand-copy">
21018
+ <strong>{{ title() }}</strong>
21019
+ @if (subtitle()) {
21020
+ <span>{{ subtitle() }}</span>
21021
+ }
21022
+ </div>
21023
+ }
21024
+ </div>
21025
+
21026
+ <nav class="jv-sidebar-nav" [attr.aria-label]="ariaLabel()">
21027
+ <ul class="jv-sidebar-list">
21028
+ @for (item of normalizedItems(); track item.id) {
21029
+ <li class="jv-sidebar-item">
21030
+ @if (item.children.length > 0) {
21031
+ <button
21032
+ type="button"
21033
+ class="jv-sidebar-link"
21034
+ [class.is-active]="item.active"
21035
+ [attr.aria-label]="item.label"
21036
+ [attr.aria-expanded]="isExpanded(item.id) ? 'true' : 'false'"
21037
+ [attr.aria-controls]="'jv-sidebar-section-' + item.id"
21038
+ [attr.title]="item.label"
21039
+ (click)="toggleSection(item.id)"
21040
+ >
21041
+ <span class="jv-sidebar-link-main">
21042
+ @if (item.icon) {
21043
+ <jv-icon [name]="item.icon" [size]="18" />
21044
+ }
21045
+
21046
+ @if (!collapsed()) {
21047
+ <span>{{ item.label }}</span>
21048
+ }
21049
+ </span>
21050
+
21051
+ @if (!collapsed()) {
21052
+ <jv-icon [name]="isExpanded(item.id) ? 'chevron-up' : 'chevron-down'" [size]="16" />
21053
+ }
21054
+ </button>
21055
+ } @else {
21056
+ @if (isHashLink(item.href)) {
21057
+ <button
21058
+ type="button"
21059
+ class="jv-sidebar-link"
21060
+ [class.is-active]="item.active"
21061
+ [attr.aria-label]="item.label"
21062
+ [attr.aria-current]="item.active ? 'page' : null"
21063
+ [attr.title]="item.label"
21064
+ (click)="navigateTo(item.href); mobileClose.emit()"
21065
+ >
21066
+ <span class="jv-sidebar-link-main">
21067
+ @if (item.icon) {
21068
+ <jv-icon [name]="item.icon" [size]="18" />
21069
+ }
21070
+
21071
+ @if (!collapsed()) {
21072
+ <span>{{ item.label }}</span>
21073
+ }
21074
+ </span>
21075
+ </button>
21076
+ } @else {
21077
+ <a
21078
+ class="jv-sidebar-link"
21079
+ [class.is-active]="item.active"
21080
+ [attr.aria-label]="item.label"
21081
+ [attr.aria-current]="item.active ? 'page' : null"
21082
+ [routerLink]="item.href"
21083
+ [attr.title]="item.label"
21084
+ (click)="mobileClose.emit()"
21085
+ >
21086
+ <span class="jv-sidebar-link-main">
21087
+ @if (item.icon) {
21088
+ <jv-icon [name]="item.icon" [size]="18" />
21089
+ }
21090
+
21091
+ @if (!collapsed()) {
21092
+ <span>{{ item.label }}</span>
21093
+ }
21094
+ </span>
21095
+ </a>
21096
+ }
21097
+ }
21098
+
21099
+ @if (!collapsed() && item.children.length > 0 && isExpanded(item.id)) {
21100
+ <div class="jv-sidebar-children" [id]="'jv-sidebar-section-' + item.id">
21101
+ @for (child of item.children; track child.id) {
21102
+ @if (isHashLink(child.href)) {
21103
+ <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)">
21104
+ {{ child.label }}
21105
+ </button>
21106
+ } @else {
21107
+ <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()">
21108
+ {{ child.label }}
21109
+ </a>
21110
+ }
21111
+ }
21112
+ </div>
21113
+ }
21114
+ </li>
21115
+ }
21116
+ </ul>
21117
+ </nav>
21118
+
21119
+ <div class="jv-sidebar-footer">
21120
+ <button type="button" class="jv-sidebar-toggle" (click)="collapseToggle.emit()">
21121
+ <jv-icon [name]="collapsed() ? 'menu' : 'panel-left-close'" [size]="18" />
21122
+ @if (!collapsed()) {
21123
+ <span>Collapse</span>
21124
+ }
21125
+ </button>
21126
+
21127
+ <div class="jv-sidebar-mobile-extras">
21128
+ @if (showThemeSelector()) {
21129
+ <div class="theme-group" aria-label="Theme selector">
21130
+ @for (option of themeOptions(); track option.value) {
21131
+ <button
21132
+ type="button"
21133
+ class="theme-pill"
21134
+ [class.is-active]="currentTheme() === option.value"
21135
+ (click)="themeChange.emit(option.value)"
21136
+ >
21137
+ @if (option.icon) {
21138
+ <jv-icon [name]="option.icon" [size]="16" />
21139
+ }
21140
+ <span>{{ option.label }}</span>
21141
+ </button>
21142
+ }
21143
+ </div>
21144
+ }
21145
+
21146
+ <div class="mobile-actions">
21147
+ @for (action of topbarActions(); track action.id) {
21148
+ <button type="button" class="icon-button" [attr.aria-label]="action.label" (click)="actionClick.emit(action.id); mobileClose.emit()">
21149
+ <jv-icon [name]="action.icon" [size]="18" />
21150
+ </button>
21151
+ }
21152
+ </div>
21153
+ </div>
21154
+ </div>
21155
+ </aside>
21156
+ `, 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:var(--jv-density-control-height);height:var(--jv-density-control-height);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);flex:0 0 auto}.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:calc(var(--jv-density-control-height) - .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"] }] });
21157
+ }
21158
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSidebarComponent, decorators: [{
21159
+ type: Component,
21160
+ args: [{ selector: 'jv-sidebar', standalone: true, imports: [JvIconComponent, RouterLink], template: `
21161
+ @if (mobileOpen()) {
21162
+ <div class="jv-sidebar-backdrop" (click)="mobileClose.emit()"></div>
21163
+ }
21164
+
21165
+ <aside
21166
+ class="jv-sidebar"
21167
+ [class.is-collapsed]="collapsed()"
21168
+ [class.is-mobile-open]="mobileOpen()"
21169
+ >
21170
+ <div class="jv-sidebar-brand">
21171
+ <div class="brand-mark">
21172
+ <jv-icon [name]="brandIcon()" [size]="18" />
21173
+ </div>
21174
+
21175
+ @if (!collapsed()) {
21176
+ <div class="brand-copy">
21177
+ <strong>{{ title() }}</strong>
21178
+ @if (subtitle()) {
21179
+ <span>{{ subtitle() }}</span>
21180
+ }
21181
+ </div>
21182
+ }
21183
+ </div>
21184
+
21185
+ <nav class="jv-sidebar-nav" [attr.aria-label]="ariaLabel()">
21186
+ <ul class="jv-sidebar-list">
21187
+ @for (item of normalizedItems(); track item.id) {
21188
+ <li class="jv-sidebar-item">
21189
+ @if (item.children.length > 0) {
21190
+ <button
21191
+ type="button"
21192
+ class="jv-sidebar-link"
21193
+ [class.is-active]="item.active"
21194
+ [attr.aria-label]="item.label"
21195
+ [attr.aria-expanded]="isExpanded(item.id) ? 'true' : 'false'"
21196
+ [attr.aria-controls]="'jv-sidebar-section-' + item.id"
21197
+ [attr.title]="item.label"
21198
+ (click)="toggleSection(item.id)"
21199
+ >
21200
+ <span class="jv-sidebar-link-main">
21201
+ @if (item.icon) {
21202
+ <jv-icon [name]="item.icon" [size]="18" />
21203
+ }
21204
+
21205
+ @if (!collapsed()) {
21206
+ <span>{{ item.label }}</span>
21207
+ }
21208
+ </span>
21209
+
21210
+ @if (!collapsed()) {
21211
+ <jv-icon [name]="isExpanded(item.id) ? 'chevron-up' : 'chevron-down'" [size]="16" />
21212
+ }
21213
+ </button>
21214
+ } @else {
21215
+ @if (isHashLink(item.href)) {
21216
+ <button
21217
+ type="button"
21218
+ class="jv-sidebar-link"
21219
+ [class.is-active]="item.active"
21220
+ [attr.aria-label]="item.label"
21221
+ [attr.aria-current]="item.active ? 'page' : null"
21222
+ [attr.title]="item.label"
21223
+ (click)="navigateTo(item.href); mobileClose.emit()"
21224
+ >
21225
+ <span class="jv-sidebar-link-main">
21226
+ @if (item.icon) {
21227
+ <jv-icon [name]="item.icon" [size]="18" />
21228
+ }
21229
+
21230
+ @if (!collapsed()) {
21231
+ <span>{{ item.label }}</span>
21232
+ }
21233
+ </span>
21234
+ </button>
21235
+ } @else {
21236
+ <a
21237
+ class="jv-sidebar-link"
21238
+ [class.is-active]="item.active"
21239
+ [attr.aria-label]="item.label"
21240
+ [attr.aria-current]="item.active ? 'page' : null"
21241
+ [routerLink]="item.href"
21242
+ [attr.title]="item.label"
21243
+ (click)="mobileClose.emit()"
21244
+ >
21245
+ <span class="jv-sidebar-link-main">
21246
+ @if (item.icon) {
21247
+ <jv-icon [name]="item.icon" [size]="18" />
21248
+ }
21249
+
21250
+ @if (!collapsed()) {
21251
+ <span>{{ item.label }}</span>
21252
+ }
21253
+ </span>
21254
+ </a>
21255
+ }
21256
+ }
21257
+
21258
+ @if (!collapsed() && item.children.length > 0 && isExpanded(item.id)) {
21259
+ <div class="jv-sidebar-children" [id]="'jv-sidebar-section-' + item.id">
21260
+ @for (child of item.children; track child.id) {
21261
+ @if (isHashLink(child.href)) {
21262
+ <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)">
21263
+ {{ child.label }}
21264
+ </button>
21265
+ } @else {
21266
+ <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()">
21267
+ {{ child.label }}
21268
+ </a>
21269
+ }
21270
+ }
21271
+ </div>
21272
+ }
21273
+ </li>
21274
+ }
21275
+ </ul>
21276
+ </nav>
21277
+
21278
+ <div class="jv-sidebar-footer">
21279
+ <button type="button" class="jv-sidebar-toggle" (click)="collapseToggle.emit()">
21280
+ <jv-icon [name]="collapsed() ? 'menu' : 'panel-left-close'" [size]="18" />
21281
+ @if (!collapsed()) {
21282
+ <span>Collapse</span>
21283
+ }
21284
+ </button>
21285
+
21286
+ <div class="jv-sidebar-mobile-extras">
21287
+ @if (showThemeSelector()) {
21288
+ <div class="theme-group" aria-label="Theme selector">
21289
+ @for (option of themeOptions(); track option.value) {
21290
+ <button
21291
+ type="button"
21292
+ class="theme-pill"
21293
+ [class.is-active]="currentTheme() === option.value"
21294
+ (click)="themeChange.emit(option.value)"
21295
+ >
21296
+ @if (option.icon) {
21297
+ <jv-icon [name]="option.icon" [size]="16" />
21298
+ }
21299
+ <span>{{ option.label }}</span>
21300
+ </button>
21301
+ }
21302
+ </div>
21303
+ }
21304
+
21305
+ <div class="mobile-actions">
21306
+ @for (action of topbarActions(); track action.id) {
21307
+ <button type="button" class="icon-button" [attr.aria-label]="action.label" (click)="actionClick.emit(action.id); mobileClose.emit()">
21308
+ <jv-icon [name]="action.icon" [size]="18" />
21309
+ </button>
21310
+ }
21311
+ </div>
21312
+ </div>
21313
+ </div>
21314
+ </aside>
21315
+ `, 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:var(--jv-density-control-height);height:var(--jv-density-control-height);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);flex:0 0 auto}.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:calc(var(--jv-density-control-height) - .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
21316
  }], 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
21317
 
20104
21318
  class JvTopbarComponent {
@@ -20229,88 +21443,88 @@ class JvDashboardShellComponent {
20229
21443
  themeChange = output();
20230
21444
  topbarActionClick = output();
20231
21445
  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>
21446
+ 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: `
21447
+ <div class="jv-dashboard-shell" [class.is-sidebar-collapsed]="sidebarCollapsed()">
21448
+ <jv-sidebar
21449
+ [title]="title()"
21450
+ [subtitle]="sidebarSubtitle()"
21451
+ [brandIcon]="brandIcon()"
21452
+ [items]="navItems()"
21453
+ [collapsed]="sidebarCollapsed()"
21454
+ [mobileOpen]="mobileSidebarOpen()"
21455
+ [currentTheme]="currentTheme()"
21456
+ [themeOptions]="themeOptions()"
21457
+ [topbarActions]="topbarActions()"
21458
+ [showThemeSelector]="showThemeSelector()"
21459
+ (collapseToggle)="sidebarCollapseToggle.emit()"
21460
+ (mobileClose)="mobileClose.emit()"
21461
+ (themeChange)="themeChange.emit($event)"
21462
+ (actionClick)="topbarActionClick.emit($event)"
21463
+ />
21464
+
21465
+ <div class="jv-dashboard-main">
21466
+ <jv-topbar
21467
+ [title]="topbarTitle()"
21468
+ [subtitle]="topbarSubtitle()"
21469
+ [currentTheme]="currentTheme()"
21470
+ [themeOptions]="themeOptions()"
21471
+ [actions]="topbarActions()"
21472
+ [showThemeSelector]="showThemeSelector()"
21473
+ (menuClick)="menuClick.emit()"
21474
+ (themeChange)="themeChange.emit($event)"
21475
+ (actionClick)="topbarActionClick.emit($event)"
21476
+ >
21477
+ <ng-content select="[topbar-actions]" topbar-actions></ng-content>
21478
+ </jv-topbar>
21479
+
21480
+ <main class="jv-dashboard-content">
21481
+ <ng-content></ng-content>
21482
+ </main>
21483
+ </div>
21484
+ </div>
20271
21485
  `, 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
21486
  }
20273
21487
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDashboardShellComponent, decorators: [{
20274
21488
  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>
21489
+ args: [{ selector: 'jv-dashboard-shell', standalone: true, imports: [JvSidebarComponent, JvTopbarComponent], template: `
21490
+ <div class="jv-dashboard-shell" [class.is-sidebar-collapsed]="sidebarCollapsed()">
21491
+ <jv-sidebar
21492
+ [title]="title()"
21493
+ [subtitle]="sidebarSubtitle()"
21494
+ [brandIcon]="brandIcon()"
21495
+ [items]="navItems()"
21496
+ [collapsed]="sidebarCollapsed()"
21497
+ [mobileOpen]="mobileSidebarOpen()"
21498
+ [currentTheme]="currentTheme()"
21499
+ [themeOptions]="themeOptions()"
21500
+ [topbarActions]="topbarActions()"
21501
+ [showThemeSelector]="showThemeSelector()"
21502
+ (collapseToggle)="sidebarCollapseToggle.emit()"
21503
+ (mobileClose)="mobileClose.emit()"
21504
+ (themeChange)="themeChange.emit($event)"
21505
+ (actionClick)="topbarActionClick.emit($event)"
21506
+ />
21507
+
21508
+ <div class="jv-dashboard-main">
21509
+ <jv-topbar
21510
+ [title]="topbarTitle()"
21511
+ [subtitle]="topbarSubtitle()"
21512
+ [currentTheme]="currentTheme()"
21513
+ [themeOptions]="themeOptions()"
21514
+ [actions]="topbarActions()"
21515
+ [showThemeSelector]="showThemeSelector()"
21516
+ (menuClick)="menuClick.emit()"
21517
+ (themeChange)="themeChange.emit($event)"
21518
+ (actionClick)="topbarActionClick.emit($event)"
21519
+ >
21520
+ <ng-content select="[topbar-actions]" topbar-actions></ng-content>
21521
+ </jv-topbar>
21522
+
21523
+ <main class="jv-dashboard-content">
21524
+ <ng-content></ng-content>
21525
+ </main>
21526
+ </div>
21527
+ </div>
20314
21528
  `, 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
21529
  }], 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
21530
 
@@ -20379,40 +21593,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
20379
21593
  `, 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
21594
  }], 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
21595
 
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
21596
  class JvListItemComponent {
20417
21597
  title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
20418
21598
  description = input('', ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
@@ -20481,6 +21661,470 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
20481
21661
  `, 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
21662
  }], 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
21663
 
21664
+ class JvTabPanelDirective {
21665
+ templateRef;
21666
+ id = input.required({ ...(ngDevMode ? { debugName: "id" } : /* istanbul ignore next */ {}), alias: 'jvTabPanel' });
21667
+ constructor(templateRef) {
21668
+ this.templateRef = templateRef;
21669
+ }
21670
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTabPanelDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
21671
+ 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 });
21672
+ }
21673
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTabPanelDirective, decorators: [{
21674
+ type: Directive,
21675
+ args: [{
21676
+ selector: 'ng-template[jvTabPanel]',
21677
+ standalone: true,
21678
+ }]
21679
+ }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvTabPanel", required: true }] }] } });
21680
+
21681
+ let tabsSequence = 0;
21682
+ class JvTabsComponent {
21683
+ instanceId = ++tabsSequence;
21684
+ panelTemplates = contentChildren(JvTabPanelDirective, ...(ngDevMode ? [{ debugName: "panelTemplates" }] : /* istanbul ignore next */ []));
21685
+ tabListRef = viewChild('tabList', ...(ngDevMode ? [{ debugName: "tabListRef" }] : /* istanbul ignore next */ []));
21686
+ tabs = input([], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
21687
+ activeTabId = input(null, ...(ngDevMode ? [{ debugName: "activeTabId" }] : /* istanbul ignore next */ []));
21688
+ keepMounted = input(true, ...(ngDevMode ? [{ debugName: "keepMounted" }] : /* istanbul ignore next */ []));
21689
+ activeTabChange = output();
21690
+ showScrollLeft = signal(false, ...(ngDevMode ? [{ debugName: "showScrollLeft" }] : /* istanbul ignore next */ []));
21691
+ showScrollRight = signal(false, ...(ngDevMode ? [{ debugName: "showScrollRight" }] : /* istanbul ignore next */ []));
21692
+ resolvedActiveTabId = computed(() => {
21693
+ const enabledTabs = this.tabs().filter((tab) => !tab.disabled);
21694
+ const requestedId = this.activeTabId();
21695
+ if (requestedId && enabledTabs.some((tab) => tab.id === requestedId)) {
21696
+ return requestedId;
21697
+ }
21698
+ return enabledTabs[0]?.id ?? null;
21699
+ }, ...(ngDevMode ? [{ debugName: "resolvedActiveTabId" }] : /* istanbul ignore next */ []));
21700
+ constructor() {
21701
+ afterNextRender(() => this.updateScrollControls());
21702
+ effect(() => {
21703
+ this.tabs();
21704
+ this.panelTemplates();
21705
+ this.activeTabId();
21706
+ queueMicrotask(() => {
21707
+ this.ensureActiveTabVisible();
21708
+ this.updateScrollControls();
21709
+ });
21710
+ });
21711
+ }
21712
+ ngAfterViewInit() {
21713
+ this.updateScrollControls();
21714
+ }
21715
+ handleWindowResize() {
21716
+ this.updateScrollControls();
21717
+ }
21718
+ isActive(tabId) {
21719
+ return this.resolvedActiveTabId() === tabId;
21720
+ }
21721
+ getTabIndex(tabId, disabled) {
21722
+ if (disabled) {
21723
+ return -1;
21724
+ }
21725
+ return this.isActive(tabId) ? 0 : -1;
21726
+ }
21727
+ activateTab(tabId) {
21728
+ const tab = this.tabs().find((item) => item.id === tabId);
21729
+ if (!tab || tab.disabled || this.resolvedActiveTabId() === tabId) {
21730
+ return;
21731
+ }
21732
+ this.activeTabChange.emit(tabId);
21733
+ queueMicrotask(() => this.focusTabButton(tabId));
21734
+ }
21735
+ handleKeydown(event, currentTabId) {
21736
+ const enabledTabs = this.tabs().filter((tab) => !tab.disabled);
21737
+ if (enabledTabs.length === 0) {
21738
+ return;
21739
+ }
21740
+ const currentIndex = Math.max(0, enabledTabs.findIndex((tab) => tab.id === currentTabId));
21741
+ switch (event.key) {
21742
+ case 'ArrowRight':
21743
+ event.preventDefault();
21744
+ this.activateAndFocus(enabledTabs[(currentIndex + 1) % enabledTabs.length].id);
21745
+ break;
21746
+ case 'ArrowLeft':
21747
+ event.preventDefault();
21748
+ this.activateAndFocus(enabledTabs[(currentIndex - 1 + enabledTabs.length) % enabledTabs.length].id);
21749
+ break;
21750
+ case 'Home':
21751
+ event.preventDefault();
21752
+ this.activateAndFocus(enabledTabs[0].id);
21753
+ break;
21754
+ case 'End':
21755
+ event.preventDefault();
21756
+ this.activateAndFocus(enabledTabs[enabledTabs.length - 1].id);
21757
+ break;
21758
+ }
21759
+ }
21760
+ scrollTabs(direction) {
21761
+ const tabList = this.tabListRef()?.nativeElement;
21762
+ if (!tabList) {
21763
+ return;
21764
+ }
21765
+ tabList.scrollBy({
21766
+ left: direction * Math.max(tabList.clientWidth * 0.75, 180),
21767
+ behavior: 'smooth',
21768
+ });
21769
+ }
21770
+ updateScrollControls() {
21771
+ const tabList = this.tabListRef()?.nativeElement;
21772
+ if (!tabList) {
21773
+ return;
21774
+ }
21775
+ const maxScrollLeft = Math.max(tabList.scrollWidth - tabList.clientWidth, 0);
21776
+ this.showScrollLeft.set(tabList.scrollLeft > 1);
21777
+ this.showScrollRight.set(tabList.scrollLeft < maxScrollLeft - 1);
21778
+ }
21779
+ panelTemplate(tabId) {
21780
+ return this.panelTemplates().find((panel) => panel.id() === tabId)?.templateRef ?? null;
21781
+ }
21782
+ tabButtonId(tabId) {
21783
+ return `jv-tab-${this.instanceId}-${tabId}`;
21784
+ }
21785
+ panelId(tabId) {
21786
+ return `jv-tab-panel-${this.instanceId}-${tabId}`;
21787
+ }
21788
+ activateAndFocus(tabId) {
21789
+ if (this.resolvedActiveTabId() !== tabId) {
21790
+ this.activeTabChange.emit(tabId);
21791
+ }
21792
+ queueMicrotask(() => this.focusTabButton(tabId));
21793
+ }
21794
+ ensureActiveTabVisible() {
21795
+ const activeTabId = this.resolvedActiveTabId();
21796
+ if (!activeTabId) {
21797
+ return;
21798
+ }
21799
+ const button = this.getTabButton(activeTabId);
21800
+ if (button && typeof button.scrollIntoView === 'function') {
21801
+ button.scrollIntoView({ inline: 'nearest', block: 'nearest' });
21802
+ }
21803
+ }
21804
+ focusTabButton(tabId) {
21805
+ this.getTabButton(tabId)?.focus();
21806
+ }
21807
+ getTabButton(tabId) {
21808
+ const tabList = this.tabListRef()?.nativeElement;
21809
+ if (!tabList) {
21810
+ return null;
21811
+ }
21812
+ return (Array.from(tabList.querySelectorAll('[data-jv-tab-id]')).find((button) => button.dataset['jvTabId'] === tabId) ?? null);
21813
+ }
21814
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
21815
+ 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: `
21816
+ <div class="jv-tabs">
21817
+ <div class="jv-tabs-header">
21818
+ <div class="jv-tabs-scroll" #tabList role="tablist" aria-orientation="horizontal" (scroll)="updateScrollControls()">
21819
+ @for (tab of tabs(); track tab.id) {
21820
+ <button
21821
+ type="button"
21822
+ class="jv-tab"
21823
+ [class.jv-tab--active]="isActive(tab.id)"
21824
+ [class.jv-tab--disabled]="tab.disabled ?? false"
21825
+ [id]="tabButtonId(tab.id)"
21826
+ [attr.data-jv-tab-id]="tab.id"
21827
+ role="tab"
21828
+ [attr.aria-selected]="isActive(tab.id) ? 'true' : 'false'"
21829
+ [attr.aria-controls]="panelId(tab.id)"
21830
+ [attr.tabindex]="getTabIndex(tab.id, tab.disabled ?? false)"
21831
+ [disabled]="tab.disabled ?? false"
21832
+ (click)="activateTab(tab.id)"
21833
+ (keydown)="handleKeydown($event, tab.id)"
21834
+ >
21835
+ @if (tab.icon) {
21836
+ <jv-icon [name]="tab.icon" [size]="16" />
21837
+ }
21838
+ <span>{{ tab.label }}</span>
21839
+ </button>
21840
+ }
21841
+ </div>
21842
+
21843
+ @if (showScrollLeft() || showScrollRight()) {
21844
+ <div class="jv-tabs-controls" aria-hidden="true">
21845
+ <button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollLeft()" (click)="scrollTabs(-1)">
21846
+ <jv-icon name="chevron-left" [size]="16" />
21847
+ </button>
21848
+ <button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollRight()" (click)="scrollTabs(1)">
21849
+ <jv-icon name="chevron-right" [size]="16" />
21850
+ </button>
21851
+ </div>
21852
+ }
21853
+ </div>
21854
+
21855
+ <div class="jv-tabs-panels">
21856
+ @if (keepMounted()) {
21857
+ @for (tab of tabs(); track tab.id) {
21858
+ <div
21859
+ class="jv-tab-panel"
21860
+ [class.jv-tab-panel--hidden]="!isActive(tab.id)"
21861
+ [id]="panelId(tab.id)"
21862
+ role="tabpanel"
21863
+ [attr.aria-labelledby]="tabButtonId(tab.id)"
21864
+ [attr.tabindex]="isActive(tab.id) ? 0 : null"
21865
+ [hidden]="!isActive(tab.id)"
21866
+ >
21867
+ <ng-container [ngTemplateOutlet]="panelTemplate(tab.id) ?? null" />
21868
+ </div>
21869
+ }
21870
+ } @else if (resolvedActiveTabId(); as activeId) {
21871
+ <div
21872
+ class="jv-tab-panel"
21873
+ [id]="panelId(activeId)"
21874
+ role="tabpanel"
21875
+ [attr.aria-labelledby]="tabButtonId(activeId)"
21876
+ tabindex="0"
21877
+ >
21878
+ <ng-container [ngTemplateOutlet]="panelTemplate(activeId) ?? null" />
21879
+ </div>
21880
+ }
21881
+ </div>
21882
+ </div>
21883
+ `, 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"] }] });
21884
+ }
21885
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTabsComponent, decorators: [{
21886
+ type: Component,
21887
+ args: [{ selector: 'jv-tabs', standalone: true, imports: [NgTemplateOutlet, JvIconComponent], template: `
21888
+ <div class="jv-tabs">
21889
+ <div class="jv-tabs-header">
21890
+ <div class="jv-tabs-scroll" #tabList role="tablist" aria-orientation="horizontal" (scroll)="updateScrollControls()">
21891
+ @for (tab of tabs(); track tab.id) {
21892
+ <button
21893
+ type="button"
21894
+ class="jv-tab"
21895
+ [class.jv-tab--active]="isActive(tab.id)"
21896
+ [class.jv-tab--disabled]="tab.disabled ?? false"
21897
+ [id]="tabButtonId(tab.id)"
21898
+ [attr.data-jv-tab-id]="tab.id"
21899
+ role="tab"
21900
+ [attr.aria-selected]="isActive(tab.id) ? 'true' : 'false'"
21901
+ [attr.aria-controls]="panelId(tab.id)"
21902
+ [attr.tabindex]="getTabIndex(tab.id, tab.disabled ?? false)"
21903
+ [disabled]="tab.disabled ?? false"
21904
+ (click)="activateTab(tab.id)"
21905
+ (keydown)="handleKeydown($event, tab.id)"
21906
+ >
21907
+ @if (tab.icon) {
21908
+ <jv-icon [name]="tab.icon" [size]="16" />
21909
+ }
21910
+ <span>{{ tab.label }}</span>
21911
+ </button>
21912
+ }
21913
+ </div>
21914
+
21915
+ @if (showScrollLeft() || showScrollRight()) {
21916
+ <div class="jv-tabs-controls" aria-hidden="true">
21917
+ <button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollLeft()" (click)="scrollTabs(-1)">
21918
+ <jv-icon name="chevron-left" [size]="16" />
21919
+ </button>
21920
+ <button type="button" class="jv-tabs-scroll-button" [disabled]="!showScrollRight()" (click)="scrollTabs(1)">
21921
+ <jv-icon name="chevron-right" [size]="16" />
21922
+ </button>
21923
+ </div>
21924
+ }
21925
+ </div>
21926
+
21927
+ <div class="jv-tabs-panels">
21928
+ @if (keepMounted()) {
21929
+ @for (tab of tabs(); track tab.id) {
21930
+ <div
21931
+ class="jv-tab-panel"
21932
+ [class.jv-tab-panel--hidden]="!isActive(tab.id)"
21933
+ [id]="panelId(tab.id)"
21934
+ role="tabpanel"
21935
+ [attr.aria-labelledby]="tabButtonId(tab.id)"
21936
+ [attr.tabindex]="isActive(tab.id) ? 0 : null"
21937
+ [hidden]="!isActive(tab.id)"
21938
+ >
21939
+ <ng-container [ngTemplateOutlet]="panelTemplate(tab.id) ?? null" />
21940
+ </div>
21941
+ }
21942
+ } @else if (resolvedActiveTabId(); as activeId) {
21943
+ <div
21944
+ class="jv-tab-panel"
21945
+ [id]="panelId(activeId)"
21946
+ role="tabpanel"
21947
+ [attr.aria-labelledby]="tabButtonId(activeId)"
21948
+ tabindex="0"
21949
+ >
21950
+ <ng-container [ngTemplateOutlet]="panelTemplate(activeId) ?? null" />
21951
+ </div>
21952
+ }
21953
+ </div>
21954
+ </div>
21955
+ `, 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"] }]
21956
+ }], 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: [{
21957
+ type: HostListener,
21958
+ args: ['window:resize']
21959
+ }] } });
21960
+
21961
+ let tooltipSequence = 0;
21962
+ class JvTooltipDirective {
21963
+ document = inject(DOCUMENT);
21964
+ renderer = inject(Renderer2);
21965
+ hostRef = inject((ElementRef));
21966
+ tooltipElement = null;
21967
+ showTimeout = null;
21968
+ hideTimeout = null;
21969
+ tooltipId = `jv-tooltip-${++tooltipSequence}`;
21970
+ text = input.required({ ...(ngDevMode ? { debugName: "text" } : /* istanbul ignore next */ {}), alias: 'jvTooltip' });
21971
+ position = input('top', { ...(ngDevMode ? { debugName: "position" } : /* istanbul ignore next */ {}), alias: 'jvTooltipPosition' });
21972
+ show() {
21973
+ if (!this.text().trim()) {
21974
+ return;
21975
+ }
21976
+ this.clearHideTimeout();
21977
+ if (this.tooltipElement) {
21978
+ return;
21979
+ }
21980
+ this.showTimeout = setTimeout(() => {
21981
+ this.renderTooltip();
21982
+ this.positionTooltip();
21983
+ this.renderer.setAttribute(this.anchorElement(), 'aria-describedby', this.tooltipId);
21984
+ }, 120);
21985
+ }
21986
+ hide() {
21987
+ this.clearShowTimeout();
21988
+ if (!this.tooltipElement) {
21989
+ return;
21990
+ }
21991
+ this.hideTimeout = setTimeout(() => this.destroyTooltip(), 80);
21992
+ }
21993
+ handleDocumentKeydown(event) {
21994
+ if (event.key === 'Escape') {
21995
+ this.destroyTooltip();
21996
+ }
21997
+ }
21998
+ handleViewportChange() {
21999
+ if (this.tooltipElement) {
22000
+ this.positionTooltip();
22001
+ }
22002
+ }
22003
+ ngOnDestroy() {
22004
+ this.clearShowTimeout();
22005
+ this.clearHideTimeout();
22006
+ this.destroyTooltip();
22007
+ }
22008
+ renderTooltip() {
22009
+ if (this.tooltipElement) {
22010
+ return;
22011
+ }
22012
+ const tooltip = this.renderer.createElement('div');
22013
+ tooltip.textContent = this.text();
22014
+ this.renderer.setAttribute(tooltip, 'id', this.tooltipId);
22015
+ this.renderer.setAttribute(tooltip, 'role', 'tooltip');
22016
+ this.renderer.setStyle(tooltip, 'position', 'fixed');
22017
+ this.renderer.setStyle(tooltip, 'z-index', '1400');
22018
+ this.renderer.setStyle(tooltip, 'max-width', '18rem');
22019
+ this.renderer.setStyle(tooltip, 'padding', '0.5rem 0.75rem');
22020
+ this.renderer.setStyle(tooltip, 'border-radius', '0.5rem');
22021
+ this.renderer.setStyle(tooltip, 'background', 'rgb(15 23 42)');
22022
+ this.renderer.setStyle(tooltip, 'color', '#ffffff');
22023
+ this.renderer.setStyle(tooltip, 'font-size', '0.8125rem');
22024
+ this.renderer.setStyle(tooltip, 'line-height', '1.4');
22025
+ this.renderer.setStyle(tooltip, 'box-shadow', 'var(--jv-shadow-md)');
22026
+ this.renderer.setStyle(tooltip, 'pointer-events', 'none');
22027
+ this.renderer.setStyle(tooltip, 'opacity', '0');
22028
+ this.renderer.setStyle(tooltip, 'transition', 'opacity 120ms ease');
22029
+ this.renderer.appendChild(this.document.body, tooltip);
22030
+ this.tooltipElement = tooltip;
22031
+ requestAnimationFrame(() => {
22032
+ if (this.tooltipElement) {
22033
+ this.renderer.setStyle(this.tooltipElement, 'opacity', '1');
22034
+ }
22035
+ });
22036
+ }
22037
+ positionTooltip() {
22038
+ const tooltip = this.tooltipElement;
22039
+ if (!tooltip) {
22040
+ return;
22041
+ }
22042
+ const hostRect = this.anchorElement().getBoundingClientRect();
22043
+ const tooltipRect = tooltip.getBoundingClientRect();
22044
+ const gap = 8;
22045
+ let top = hostRect.top - tooltipRect.height - gap;
22046
+ let left = hostRect.left + hostRect.width / 2 - tooltipRect.width / 2;
22047
+ switch (this.position()) {
22048
+ case 'right':
22049
+ top = hostRect.top + hostRect.height / 2 - tooltipRect.height / 2;
22050
+ left = hostRect.right + gap;
22051
+ break;
22052
+ case 'bottom':
22053
+ top = hostRect.bottom + gap;
22054
+ left = hostRect.left + hostRect.width / 2 - tooltipRect.width / 2;
22055
+ break;
22056
+ case 'left':
22057
+ top = hostRect.top + hostRect.height / 2 - tooltipRect.height / 2;
22058
+ left = hostRect.left - tooltipRect.width - gap;
22059
+ break;
22060
+ }
22061
+ this.renderer.setStyle(tooltip, 'top', `${Math.round(top)}px`);
22062
+ this.renderer.setStyle(tooltip, 'left', `${Math.round(left)}px`);
22063
+ }
22064
+ destroyTooltip() {
22065
+ this.clearHideTimeout();
22066
+ this.clearShowTimeout();
22067
+ if (!this.tooltipElement) {
22068
+ return;
22069
+ }
22070
+ this.renderer.removeAttribute(this.anchorElement(), 'aria-describedby');
22071
+ this.renderer.removeChild(this.document.body, this.tooltipElement);
22072
+ this.tooltipElement = null;
22073
+ }
22074
+ anchorElement() {
22075
+ const host = this.hostRef.nativeElement;
22076
+ if (isInteractiveElement(host)) {
22077
+ return host;
22078
+ }
22079
+ return host.querySelector('button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])') ?? host;
22080
+ }
22081
+ clearShowTimeout() {
22082
+ if (this.showTimeout) {
22083
+ clearTimeout(this.showTimeout);
22084
+ this.showTimeout = null;
22085
+ }
22086
+ }
22087
+ clearHideTimeout() {
22088
+ if (this.hideTimeout) {
22089
+ clearTimeout(this.hideTimeout);
22090
+ this.hideTimeout = null;
22091
+ }
22092
+ }
22093
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
22094
+ 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 });
22095
+ }
22096
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTooltipDirective, decorators: [{
22097
+ type: Directive,
22098
+ args: [{
22099
+ selector: '[jvTooltip]',
22100
+ standalone: true,
22101
+ }]
22102
+ }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvTooltip", required: true }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvTooltipPosition", required: false }] }], show: [{
22103
+ type: HostListener,
22104
+ args: ['mouseenter']
22105
+ }, {
22106
+ type: HostListener,
22107
+ args: ['focusin']
22108
+ }], hide: [{
22109
+ type: HostListener,
22110
+ args: ['mouseleave']
22111
+ }, {
22112
+ type: HostListener,
22113
+ args: ['blur']
22114
+ }], handleDocumentKeydown: [{
22115
+ type: HostListener,
22116
+ args: ['document:keydown', ['$event']]
22117
+ }], handleViewportChange: [{
22118
+ type: HostListener,
22119
+ args: ['window:scroll']
22120
+ }, {
22121
+ type: HostListener,
22122
+ args: ['window:resize']
22123
+ }] } });
22124
+ function isInteractiveElement(element) {
22125
+ return ['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA'].includes(element.tagName) || element.tabIndex >= 0;
22126
+ }
22127
+
20484
22128
  const JV_GRID_DEFAULT_OPTIONS = {
20485
22129
  searchable: true,
20486
22130
  sortable: true,
@@ -21880,5 +23524,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
21880
23524
  * Generated bundle index. Do not edit.
21881
23525
  */
21882
23526
 
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 };
23527
+ 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, JvCodeComponent, 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
23528
  //# sourceMappingURL=devjuliovilla-jv-ui.mjs.map