@devjuliovilla/jv-ui 1.16.0 → 1.18.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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, computed, Component, inject, signal, Injectable, InjectionToken, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, TemplateRef, Directive, contentChild, output, effect, forwardRef, viewChild, untracked, contentChildren, HostListener, viewChildren, numberAttribute, afterNextRender, Renderer2, ElementRef, DestroyRef } from '@angular/core';
2
+ import { input, computed, Component, inject, Renderer2, ElementRef, HostListener, Directive, signal, Injectable, InjectionToken, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, TemplateRef, contentChild, output, effect, forwardRef, viewChild, untracked, contentChildren, viewChildren, numberAttribute, afterNextRender, DestroyRef } from '@angular/core';
3
3
  import { DOCUMENT, NgTemplateOutlet, CommonModule } from '@angular/common';
4
4
  import * as i1 from '@angular/forms';
5
5
  import { NG_VALUE_ACCESSOR, FormBuilder, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
@@ -18009,6 +18009,193 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
18009
18009
  args: [{ selector: 'jv-button-group', standalone: true, template: `<div class="jv-button-group" role="group"><ng-content></ng-content></div>`, styles: [".jv-button-group{display:inline-flex;flex-wrap:wrap;gap:var(--jv-spacing-sm);align-items:center}\n"] }]
18010
18010
  }] });
18011
18011
 
18012
+ let tooltipSequence = 0;
18013
+ class JvTooltipDirective {
18014
+ document = inject(DOCUMENT);
18015
+ renderer = inject(Renderer2);
18016
+ hostRef = inject((ElementRef));
18017
+ tooltipElement = null;
18018
+ showTimeout = null;
18019
+ hideTimeout = null;
18020
+ tooltipId = `jv-tooltip-${++tooltipSequence}`;
18021
+ originalDescribedBy = null;
18022
+ text = input.required({ ...(ngDevMode ? { debugName: "text" } : /* istanbul ignore next */ {}), alias: 'jvTooltip' });
18023
+ position = input('top', { ...(ngDevMode ? { debugName: "position" } : /* istanbul ignore next */ {}), alias: 'jvTooltipPosition' });
18024
+ show() {
18025
+ if (!this.text().trim()) {
18026
+ return;
18027
+ }
18028
+ this.clearHideTimeout();
18029
+ if (this.tooltipElement) {
18030
+ return;
18031
+ }
18032
+ this.showTimeout = setTimeout(() => {
18033
+ this.renderTooltip();
18034
+ this.positionTooltip();
18035
+ this.attachTooltipDescription();
18036
+ }, 120);
18037
+ }
18038
+ hide() {
18039
+ this.clearShowTimeout();
18040
+ if (!this.tooltipElement) {
18041
+ return;
18042
+ }
18043
+ this.hideTimeout = setTimeout(() => this.destroyTooltip(), 80);
18044
+ }
18045
+ handleDocumentKeydown(event) {
18046
+ if (event.key === 'Escape') {
18047
+ this.destroyTooltip();
18048
+ }
18049
+ }
18050
+ handleViewportChange() {
18051
+ if (this.tooltipElement) {
18052
+ this.positionTooltip();
18053
+ }
18054
+ }
18055
+ ngOnDestroy() {
18056
+ this.clearShowTimeout();
18057
+ this.clearHideTimeout();
18058
+ this.destroyTooltip();
18059
+ }
18060
+ renderTooltip() {
18061
+ if (this.tooltipElement) {
18062
+ return;
18063
+ }
18064
+ const tooltip = this.renderer.createElement('div');
18065
+ tooltip.textContent = this.text();
18066
+ this.renderer.setAttribute(tooltip, 'id', this.tooltipId);
18067
+ this.renderer.setAttribute(tooltip, 'role', 'tooltip');
18068
+ this.renderer.setStyle(tooltip, 'position', 'fixed');
18069
+ this.renderer.setStyle(tooltip, 'z-index', '1400');
18070
+ this.renderer.setStyle(tooltip, 'max-width', '18rem');
18071
+ this.renderer.setStyle(tooltip, 'padding', '0.5rem 0.75rem');
18072
+ this.renderer.setStyle(tooltip, 'border-radius', '0.5rem');
18073
+ this.renderer.setStyle(tooltip, 'background', 'rgb(15 23 42)');
18074
+ this.renderer.setStyle(tooltip, 'color', '#ffffff');
18075
+ this.renderer.setStyle(tooltip, 'font-size', '0.8125rem');
18076
+ this.renderer.setStyle(tooltip, 'line-height', '1.4');
18077
+ this.renderer.setStyle(tooltip, 'box-shadow', 'var(--jv-shadow-md)');
18078
+ this.renderer.setStyle(tooltip, 'pointer-events', 'none');
18079
+ this.renderer.setStyle(tooltip, 'opacity', '0');
18080
+ this.renderer.setStyle(tooltip, 'transition', 'opacity 120ms ease');
18081
+ this.renderer.appendChild(this.document.body, tooltip);
18082
+ this.tooltipElement = tooltip;
18083
+ requestAnimationFrame(() => {
18084
+ if (this.tooltipElement) {
18085
+ this.renderer.setStyle(this.tooltipElement, 'opacity', '1');
18086
+ }
18087
+ });
18088
+ }
18089
+ positionTooltip() {
18090
+ const tooltip = this.tooltipElement;
18091
+ if (!tooltip) {
18092
+ return;
18093
+ }
18094
+ const hostRect = this.anchorElement().getBoundingClientRect();
18095
+ const tooltipRect = tooltip.getBoundingClientRect();
18096
+ const gap = 8;
18097
+ let top = hostRect.top - tooltipRect.height - gap;
18098
+ let left = hostRect.left + hostRect.width / 2 - tooltipRect.width / 2;
18099
+ switch (this.position()) {
18100
+ case 'right':
18101
+ top = hostRect.top + hostRect.height / 2 - tooltipRect.height / 2;
18102
+ left = hostRect.right + gap;
18103
+ break;
18104
+ case 'bottom':
18105
+ top = hostRect.bottom + gap;
18106
+ left = hostRect.left + hostRect.width / 2 - tooltipRect.width / 2;
18107
+ break;
18108
+ case 'left':
18109
+ top = hostRect.top + hostRect.height / 2 - tooltipRect.height / 2;
18110
+ left = hostRect.left - tooltipRect.width - gap;
18111
+ break;
18112
+ }
18113
+ this.renderer.setStyle(tooltip, 'top', `${Math.round(top)}px`);
18114
+ this.renderer.setStyle(tooltip, 'left', `${Math.round(left)}px`);
18115
+ }
18116
+ destroyTooltip() {
18117
+ this.clearHideTimeout();
18118
+ this.clearShowTimeout();
18119
+ if (!this.tooltipElement) {
18120
+ return;
18121
+ }
18122
+ this.restoreTooltipDescription();
18123
+ this.renderer.removeChild(this.document.body, this.tooltipElement);
18124
+ this.tooltipElement = null;
18125
+ }
18126
+ attachTooltipDescription() {
18127
+ const anchor = this.anchorElement();
18128
+ this.originalDescribedBy ??= anchor.getAttribute('aria-describedby');
18129
+ const describedBy = [this.originalDescribedBy, this.tooltipId]
18130
+ .filter((value) => !!value)
18131
+ .join(' ')
18132
+ .trim();
18133
+ this.renderer.setAttribute(anchor, 'aria-describedby', describedBy);
18134
+ }
18135
+ restoreTooltipDescription() {
18136
+ const anchor = this.anchorElement();
18137
+ if (this.originalDescribedBy) {
18138
+ this.renderer.setAttribute(anchor, 'aria-describedby', this.originalDescribedBy);
18139
+ }
18140
+ else {
18141
+ this.renderer.removeAttribute(anchor, 'aria-describedby');
18142
+ }
18143
+ this.originalDescribedBy = null;
18144
+ }
18145
+ anchorElement() {
18146
+ const host = this.hostRef.nativeElement;
18147
+ if (isInteractiveElement(host)) {
18148
+ return host;
18149
+ }
18150
+ return host.querySelector('button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])') ?? host;
18151
+ }
18152
+ clearShowTimeout() {
18153
+ if (this.showTimeout) {
18154
+ clearTimeout(this.showTimeout);
18155
+ this.showTimeout = null;
18156
+ }
18157
+ }
18158
+ clearHideTimeout() {
18159
+ if (this.hideTimeout) {
18160
+ clearTimeout(this.hideTimeout);
18161
+ this.hideTimeout = null;
18162
+ }
18163
+ }
18164
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
18165
+ 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 });
18166
+ }
18167
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTooltipDirective, decorators: [{
18168
+ type: Directive,
18169
+ args: [{
18170
+ selector: '[jvTooltip]',
18171
+ standalone: true,
18172
+ }]
18173
+ }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvTooltip", required: true }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvTooltipPosition", required: false }] }], show: [{
18174
+ type: HostListener,
18175
+ args: ['mouseenter']
18176
+ }, {
18177
+ type: HostListener,
18178
+ args: ['focusin']
18179
+ }], hide: [{
18180
+ type: HostListener,
18181
+ args: ['mouseleave']
18182
+ }, {
18183
+ type: HostListener,
18184
+ args: ['blur']
18185
+ }], handleDocumentKeydown: [{
18186
+ type: HostListener,
18187
+ args: ['document:keydown', ['$event']]
18188
+ }], handleViewportChange: [{
18189
+ type: HostListener,
18190
+ args: ['window:scroll']
18191
+ }, {
18192
+ type: HostListener,
18193
+ args: ['window:resize']
18194
+ }] } });
18195
+ function isInteractiveElement(element) {
18196
+ return ['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA'].includes(element.tagName) || element.tabIndex >= 0;
18197
+ }
18198
+
18012
18199
  class JvIconButtonComponent {
18013
18200
  icon = input.required(...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
18014
18201
  ariaLabel = input.required(...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
@@ -18021,25 +18208,31 @@ class JvIconButtonComponent {
18021
18208
  class="jv-icon-button"
18022
18209
  [class]="'jv-icon-button--' + variant()"
18023
18210
  [attr.aria-label]="ariaLabel()"
18211
+ [attr.title]="ariaLabel()"
18212
+ [jvTooltip]="ariaLabel()"
18024
18213
  [disabled]="disabled()"
18025
18214
  >
18026
18215
  <jv-icon [name]="icon()" [size]="18" />
18216
+ <span class="icon-label">{{ ariaLabel() }}</span>
18027
18217
  </button>
18028
- `, isInline: true, styles: [".jv-icon-button{display:inline-grid;place-items:center;width:var(--jv-density-control-height);height:var(--jv-density-control-height);border:1px solid transparent;border-radius:999px;cursor:pointer;transition:background-color .16s ease,color .16s ease,border-color .16s ease}.jv-icon-button:disabled{opacity:.6;cursor:not-allowed}.jv-icon-button--primary{background:var(--jv-color-primary);color:#fff}.jv-icon-button--secondary{background:var(--jv-color-secondary);color:#fff}.jv-icon-button--outline{border-color:var(--jv-color-border);background:var(--jv-color-surface);color:var(--jv-color-foreground)}.jv-icon-button--ghost,.jv-icon-button--link{background:transparent;color:var(--jv-color-foreground)}.jv-icon-button--danger{background:var(--jv-color-danger);color:#fff}.jv-icon-button--success{background:var(--jv-color-success);color:#fff}.jv-icon-button--warning{background:var(--jv-color-warning);color:#111827}.jv-icon-button--outline:hover,.jv-icon-button--ghost:hover,.jv-icon-button--link:hover{background:var(--jv-color-surface-muted)}\n"], dependencies: [{ kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
18218
+ `, isInline: true, styles: [".jv-icon-button{display:inline-grid;place-items:center;width:var(--jv-density-control-height);height:var(--jv-density-control-height);border:1px solid transparent;border-radius:999px;cursor:pointer;transition:background-color .16s ease,color .16s ease,border-color .16s ease}.icon-label{font-size:0}.jv-icon-button:disabled{opacity:.6;cursor:not-allowed}.jv-icon-button--primary{background:var(--jv-color-primary);color:#fff}.jv-icon-button--secondary{background:var(--jv-color-secondary);color:#fff}.jv-icon-button--outline{border-color:var(--jv-color-border);background:var(--jv-color-surface);color:var(--jv-color-foreground)}.jv-icon-button--ghost,.jv-icon-button--link{background:transparent;color:var(--jv-color-foreground)}.jv-icon-button--danger{background:var(--jv-color-danger);color:#fff}.jv-icon-button--success{background:var(--jv-color-success);color:#fff}.jv-icon-button--warning{background:var(--jv-color-warning);color:#111827}.jv-icon-button--outline:hover,.jv-icon-button--ghost:hover,.jv-icon-button--link:hover{background:var(--jv-color-surface-muted)}\n"], dependencies: [{ kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }, { kind: "directive", type: JvTooltipDirective, selector: "[jvTooltip]", inputs: ["jvTooltip", "jvTooltipPosition"] }] });
18029
18219
  }
18030
18220
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvIconButtonComponent, decorators: [{
18031
18221
  type: Component,
18032
- args: [{ selector: 'jv-icon-button', standalone: true, imports: [JvIconComponent], template: `
18222
+ args: [{ selector: 'jv-icon-button', standalone: true, imports: [JvIconComponent, JvTooltipDirective], template: `
18033
18223
  <button
18034
18224
  type="button"
18035
18225
  class="jv-icon-button"
18036
18226
  [class]="'jv-icon-button--' + variant()"
18037
18227
  [attr.aria-label]="ariaLabel()"
18228
+ [attr.title]="ariaLabel()"
18229
+ [jvTooltip]="ariaLabel()"
18038
18230
  [disabled]="disabled()"
18039
18231
  >
18040
18232
  <jv-icon [name]="icon()" [size]="18" />
18233
+ <span class="icon-label">{{ ariaLabel() }}</span>
18041
18234
  </button>
18042
- `, styles: [".jv-icon-button{display:inline-grid;place-items:center;width:var(--jv-density-control-height);height:var(--jv-density-control-height);border:1px solid transparent;border-radius:999px;cursor:pointer;transition:background-color .16s ease,color .16s ease,border-color .16s ease}.jv-icon-button:disabled{opacity:.6;cursor:not-allowed}.jv-icon-button--primary{background:var(--jv-color-primary);color:#fff}.jv-icon-button--secondary{background:var(--jv-color-secondary);color:#fff}.jv-icon-button--outline{border-color:var(--jv-color-border);background:var(--jv-color-surface);color:var(--jv-color-foreground)}.jv-icon-button--ghost,.jv-icon-button--link{background:transparent;color:var(--jv-color-foreground)}.jv-icon-button--danger{background:var(--jv-color-danger);color:#fff}.jv-icon-button--success{background:var(--jv-color-success);color:#fff}.jv-icon-button--warning{background:var(--jv-color-warning);color:#111827}.jv-icon-button--outline:hover,.jv-icon-button--ghost:hover,.jv-icon-button--link:hover{background:var(--jv-color-surface-muted)}\n"] }]
18235
+ `, styles: [".jv-icon-button{display:inline-grid;place-items:center;width:var(--jv-density-control-height);height:var(--jv-density-control-height);border:1px solid transparent;border-radius:999px;cursor:pointer;transition:background-color .16s ease,color .16s ease,border-color .16s ease}.icon-label{font-size:0}.jv-icon-button:disabled{opacity:.6;cursor:not-allowed}.jv-icon-button--primary{background:var(--jv-color-primary);color:#fff}.jv-icon-button--secondary{background:var(--jv-color-secondary);color:#fff}.jv-icon-button--outline{border-color:var(--jv-color-border);background:var(--jv-color-surface);color:var(--jv-color-foreground)}.jv-icon-button--ghost,.jv-icon-button--link{background:transparent;color:var(--jv-color-foreground)}.jv-icon-button--danger{background:var(--jv-color-danger);color:#fff}.jv-icon-button--success{background:var(--jv-color-success);color:#fff}.jv-icon-button--warning{background:var(--jv-color-warning);color:#111827}.jv-icon-button--outline:hover,.jv-icon-button--ghost:hover,.jv-icon-button--link:hover{background:var(--jv-color-surface-muted)}\n"] }]
18043
18236
  }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
18044
18237
 
18045
18238
  const JV_UI_DEFAULT_CONFIG = {
@@ -18850,13 +19043,13 @@ class JvSelectComponent {
18850
19043
  inputId = input(`jv-select-${++selectIdSequence}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
18851
19044
  modelValue = input('', ...(ngDevMode ? [{ debugName: "modelValue" }] : /* istanbul ignore next */ []));
18852
19045
  selectionChange = output();
18853
- value = '';
19046
+ value = signal('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
18854
19047
  disabled = false;
18855
19048
  onChange = () => undefined;
18856
19049
  onTouched = () => undefined;
18857
- currentValue = computed(() => this.modelValue() || this.value, ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
19050
+ currentValue = computed(() => this.modelValue() || this.value(), ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
18858
19051
  writeValue(value) {
18859
- this.value = value ?? '';
19052
+ this.value.set(value ?? '');
18860
19053
  }
18861
19054
  registerOnChange(fn) {
18862
19055
  this.onChange = fn;
@@ -18869,7 +19062,7 @@ class JvSelectComponent {
18869
19062
  }
18870
19063
  handleChange(event) {
18871
19064
  const nextValue = event.target.value;
18872
- this.value = nextValue;
19065
+ this.value.set(nextValue);
18873
19066
  this.onChange(nextValue);
18874
19067
  this.selectionChange.emit(nextValue);
18875
19068
  }
@@ -19719,15 +19912,15 @@ class JvChangePasswordPageComponent {
19719
19912
 
19720
19913
  <form class="auth-form" [formGroup]="form" (ngSubmit)="submit()">
19721
19914
  <jv-form-container label="Current password" inputId="jv-current-password" error="Current password is required" [required]="true" [invalid]="currentInvalid()">
19722
- <jv-input inputId="jv-current-password" type="password" formControlName="currentPassword" [invalid]="currentInvalid()" />
19915
+ <jv-input inputId="jv-current-password" type="password" formControlName="currentPassword" [invalid]="currentInvalid()" [describedBy]="currentInvalid() ? 'jv-current-password-error' : ''" />
19723
19916
  </jv-form-container>
19724
19917
 
19725
19918
  <jv-form-container label="New password" inputId="jv-new-password" error="New password is required" [required]="true" [invalid]="newInvalid()">
19726
- <jv-input inputId="jv-new-password" type="password" formControlName="newPassword" [invalid]="newInvalid()" />
19919
+ <jv-input inputId="jv-new-password" type="password" formControlName="newPassword" [invalid]="newInvalid()" [describedBy]="newInvalid() ? 'jv-new-password-error' : ''" />
19727
19920
  </jv-form-container>
19728
19921
 
19729
19922
  <jv-form-container label="Confirm new password" inputId="jv-confirm-password" error="Passwords must match" [required]="true" [invalid]="confirmInvalid()">
19730
- <jv-input inputId="jv-confirm-password" type="password" formControlName="confirmPassword" [invalid]="confirmInvalid()" />
19923
+ <jv-input inputId="jv-confirm-password" type="password" formControlName="confirmPassword" [invalid]="confirmInvalid()" [describedBy]="confirmInvalid() ? 'jv-confirm-password-error' : ''" />
19731
19924
  </jv-form-container>
19732
19925
 
19733
19926
  <div class="auth-actions">
@@ -19750,15 +19943,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
19750
19943
 
19751
19944
  <form class="auth-form" [formGroup]="form" (ngSubmit)="submit()">
19752
19945
  <jv-form-container label="Current password" inputId="jv-current-password" error="Current password is required" [required]="true" [invalid]="currentInvalid()">
19753
- <jv-input inputId="jv-current-password" type="password" formControlName="currentPassword" [invalid]="currentInvalid()" />
19946
+ <jv-input inputId="jv-current-password" type="password" formControlName="currentPassword" [invalid]="currentInvalid()" [describedBy]="currentInvalid() ? 'jv-current-password-error' : ''" />
19754
19947
  </jv-form-container>
19755
19948
 
19756
19949
  <jv-form-container label="New password" inputId="jv-new-password" error="New password is required" [required]="true" [invalid]="newInvalid()">
19757
- <jv-input inputId="jv-new-password" type="password" formControlName="newPassword" [invalid]="newInvalid()" />
19950
+ <jv-input inputId="jv-new-password" type="password" formControlName="newPassword" [invalid]="newInvalid()" [describedBy]="newInvalid() ? 'jv-new-password-error' : ''" />
19758
19951
  </jv-form-container>
19759
19952
 
19760
19953
  <jv-form-container label="Confirm new password" inputId="jv-confirm-password" error="Passwords must match" [required]="true" [invalid]="confirmInvalid()">
19761
- <jv-input inputId="jv-confirm-password" type="password" formControlName="confirmPassword" [invalid]="confirmInvalid()" />
19954
+ <jv-input inputId="jv-confirm-password" type="password" formControlName="confirmPassword" [invalid]="confirmInvalid()" [describedBy]="confirmInvalid() ? 'jv-confirm-password-error' : ''" />
19762
19955
  </jv-form-container>
19763
19956
 
19764
19957
  <div class="auth-actions">
@@ -19805,7 +19998,7 @@ class JvForgotPasswordPageComponent {
19805
19998
 
19806
19999
  <form class="auth-form" [formGroup]="form" (ngSubmit)="submit()">
19807
20000
  <jv-form-container label="Email" inputId="jv-forgot-email" error="Enter a valid email" [required]="true" [invalid]="emailInvalid()">
19808
- <jv-input inputId="jv-forgot-email" type="email" formControlName="email" [invalid]="emailInvalid()" placeholder="you@example.com" />
20001
+ <jv-input inputId="jv-forgot-email" type="email" formControlName="email" [invalid]="emailInvalid()" [describedBy]="emailInvalid() ? 'jv-forgot-email-error' : ''" placeholder="you@example.com" />
19809
20002
  </jv-form-container>
19810
20003
 
19811
20004
  <div class="auth-actions">
@@ -19828,7 +20021,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
19828
20021
 
19829
20022
  <form class="auth-form" [formGroup]="form" (ngSubmit)="submit()">
19830
20023
  <jv-form-container label="Email" inputId="jv-forgot-email" error="Enter a valid email" [required]="true" [invalid]="emailInvalid()">
19831
- <jv-input inputId="jv-forgot-email" type="email" formControlName="email" [invalid]="emailInvalid()" placeholder="you@example.com" />
20024
+ <jv-input inputId="jv-forgot-email" type="email" formControlName="email" [invalid]="emailInvalid()" [describedBy]="emailInvalid() ? 'jv-forgot-email-error' : ''" placeholder="you@example.com" />
19832
20025
  </jv-form-container>
19833
20026
 
19834
20027
  <div class="auth-actions">
@@ -19957,11 +20150,11 @@ class JvLoginPageComponent {
19957
20150
 
19958
20151
  <form class="auth-form" [formGroup]="form" (ngSubmit)="submit()">
19959
20152
  <jv-form-container label="Email or username" inputId="jv-login-identifier" error="This field is required" [required]="true" [invalid]="identifierInvalid()">
19960
- <jv-input inputId="jv-login-identifier" formControlName="identifier" [invalid]="identifierInvalid()" placeholder="you@example.com" />
20153
+ <jv-input inputId="jv-login-identifier" formControlName="identifier" [invalid]="identifierInvalid()" [describedBy]="identifierInvalid() ? 'jv-login-identifier-error' : ''" placeholder="you@example.com" />
19961
20154
  </jv-form-container>
19962
20155
 
19963
20156
  <jv-form-container label="Password" inputId="jv-login-password" error="Password is required" [required]="true" [invalid]="passwordInvalid()">
19964
- <jv-input inputId="jv-login-password" type="password" formControlName="password" [invalid]="passwordInvalid()" placeholder="Enter your password" />
20157
+ <jv-input inputId="jv-login-password" type="password" formControlName="password" [invalid]="passwordInvalid()" [describedBy]="passwordInvalid() ? 'jv-login-password-error' : ''" placeholder="Enter your password" />
19965
20158
  </jv-form-container>
19966
20159
 
19967
20160
  <div class="auth-inline-row">
@@ -19986,11 +20179,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
19986
20179
 
19987
20180
  <form class="auth-form" [formGroup]="form" (ngSubmit)="submit()">
19988
20181
  <jv-form-container label="Email or username" inputId="jv-login-identifier" error="This field is required" [required]="true" [invalid]="identifierInvalid()">
19989
- <jv-input inputId="jv-login-identifier" formControlName="identifier" [invalid]="identifierInvalid()" placeholder="you@example.com" />
20182
+ <jv-input inputId="jv-login-identifier" formControlName="identifier" [invalid]="identifierInvalid()" [describedBy]="identifierInvalid() ? 'jv-login-identifier-error' : ''" placeholder="you@example.com" />
19990
20183
  </jv-form-container>
19991
20184
 
19992
20185
  <jv-form-container label="Password" inputId="jv-login-password" error="Password is required" [required]="true" [invalid]="passwordInvalid()">
19993
- <jv-input inputId="jv-login-password" type="password" formControlName="password" [invalid]="passwordInvalid()" placeholder="Enter your password" />
20186
+ <jv-input inputId="jv-login-password" type="password" formControlName="password" [invalid]="passwordInvalid()" [describedBy]="passwordInvalid() ? 'jv-login-password-error' : ''" placeholder="Enter your password" />
19994
20187
  </jv-form-container>
19995
20188
 
19996
20189
  <div class="auth-inline-row">
@@ -20627,6 +20820,7 @@ class JvDialogComponent {
20627
20820
  previouslyFocusedElement = null;
20628
20821
  open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
20629
20822
  title = input('Dialog', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
20823
+ description = input('', ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
20630
20824
  closed = output();
20631
20825
  titleId = `jv-dialog-title-${Math.random().toString(36).slice(2, 8)}`;
20632
20826
  descriptionId = `jv-dialog-description-${Math.random().toString(36).slice(2, 8)}`;
@@ -20696,15 +20890,18 @@ class JvDialogComponent {
20696
20890
  return Array.from(panel.querySelectorAll('button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'));
20697
20891
  }
20698
20892
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20699
- 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: `
20893
+ 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 }, description: { classPropertyName: "description", publicName: "description", 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: `
20700
20894
  @if (open()) {
20701
20895
  <div class="jv-dialog-backdrop" tabindex="-1" (click)="backdropClose()" (keydown.escape)="backdropClose()">
20702
- <div class="jv-dialog-panel" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="descriptionId" (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()" #panel>
20896
+ <div class="jv-dialog-panel" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="description() ? descriptionId : null" (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()" #panel>
20703
20897
  <header class="jv-dialog-header">
20704
20898
  <h2 [id]="titleId">{{ title() }}</h2>
20899
+ @if (description()) {
20900
+ <p class="jv-dialog-description" [id]="descriptionId">{{ description() }}</p>
20901
+ }
20705
20902
  </header>
20706
20903
 
20707
- <div class="jv-dialog-body" [id]="descriptionId">
20904
+ <div class="jv-dialog-body">
20708
20905
  <ng-content></ng-content>
20709
20906
  </div>
20710
20907
 
@@ -20721,12 +20918,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
20721
20918
  args: [{ selector: 'jv-dialog', standalone: true, template: `
20722
20919
  @if (open()) {
20723
20920
  <div class="jv-dialog-backdrop" tabindex="-1" (click)="backdropClose()" (keydown.escape)="backdropClose()">
20724
- <div class="jv-dialog-panel" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="descriptionId" (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()" #panel>
20921
+ <div class="jv-dialog-panel" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="description() ? descriptionId : null" (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()" #panel>
20725
20922
  <header class="jv-dialog-header">
20726
20923
  <h2 [id]="titleId">{{ title() }}</h2>
20924
+ @if (description()) {
20925
+ <p class="jv-dialog-description" [id]="descriptionId">{{ description() }}</p>
20926
+ }
20727
20927
  </header>
20728
20928
 
20729
- <div class="jv-dialog-body" [id]="descriptionId">
20929
+ <div class="jv-dialog-body">
20730
20930
  <ng-content></ng-content>
20731
20931
  </div>
20732
20932
 
@@ -20737,7 +20937,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
20737
20937
  </div>
20738
20938
  }
20739
20939
  `, 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"] }]
20740
- }], 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: [{
20940
+ }], 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 }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], handleDocumentKeydown: [{
20741
20941
  type: HostListener,
20742
20942
  args: ['document:keydown', ['$event']]
20743
20943
  }] } });
@@ -20769,6 +20969,9 @@ class JvDatePickerComponent {
20769
20969
  disabled = false;
20770
20970
  resolvedDialogTitle = computed(() => this.dialogTitle() || this.translation.translate('datePicker.dialogTitle'), ...(ngDevMode ? [{ debugName: "resolvedDialogTitle" }] : /* istanbul ignore next */ []));
20771
20971
  currentValue = computed(() => this.valueInput() !== undefined ? this.valueInput() : this.value(), ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
20972
+ resolvedDescribedBy = computed(() => [this.describedBy(), this.invalid() ? (this.error() ? `${this.inputId()}-error` : '') : (this.hint() ? `${this.inputId()}-hint` : '')]
20973
+ .filter(Boolean)
20974
+ .join(' '), ...(ngDevMode ? [{ debugName: "resolvedDescribedBy" }] : /* istanbul ignore next */ []));
20772
20975
  triggerAriaLabel = computed(() => {
20773
20976
  const name = this.label() || this.resolvedDialogTitle();
20774
20977
  return `${name}. ${this.displayValue()}`;
@@ -20974,7 +21177,7 @@ class JvDatePickerComponent {
20974
21177
  [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
20975
21178
  [attr.aria-controls]="dialogId"
20976
21179
  [attr.aria-invalid]="invalid() ? 'true' : null"
20977
- [attr.aria-describedby]="describedBy() || null"
21180
+ [attr.aria-describedby]="resolvedDescribedBy() || null"
20978
21181
  [attr.aria-label]="triggerAriaLabel()"
20979
21182
  [attr.title]="displayValue()"
20980
21183
  (click)="openDialog()"
@@ -21031,7 +21234,7 @@ class JvDatePickerComponent {
21031
21234
  <jv-button variant="outline" (click)="closeDialog()">{{ translation.translate('datePicker.close') }}</jv-button>
21032
21235
  </div>
21033
21236
  </jv-dialog>
21034
- `, 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", "ariaLabel", "inputId", "modelValue"], outputs: ["selectionChange"] }] });
21237
+ `, 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", "description"], 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", "ariaLabel", "inputId", "modelValue"], outputs: ["selectionChange"] }] });
21035
21238
  }
21036
21239
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDatePickerComponent, decorators: [{
21037
21240
  type: Component,
@@ -21061,7 +21264,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
21061
21264
  [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
21062
21265
  [attr.aria-controls]="dialogId"
21063
21266
  [attr.aria-invalid]="invalid() ? 'true' : null"
21064
- [attr.aria-describedby]="describedBy() || null"
21267
+ [attr.aria-describedby]="resolvedDescribedBy() || null"
21065
21268
  [attr.aria-label]="triggerAriaLabel()"
21066
21269
  [attr.title]="displayValue()"
21067
21270
  (click)="openDialog()"
@@ -21163,6 +21366,7 @@ class JvFileInputComponent {
21163
21366
  filesRejected = output();
21164
21367
  dragActive = signal(false, ...(ngDevMode ? [{ debugName: "dragActive" }] : /* istanbul ignore next */ []));
21165
21368
  rejectionMessage = signal('', ...(ngDevMode ? [{ debugName: "rejectionMessage" }] : /* istanbul ignore next */ []));
21369
+ rejectionId = `jv-file-input-rejection-${this.instanceId}`;
21166
21370
  internalFiles = signal([], ...(ngDevMode ? [{ debugName: "internalFiles" }] : /* istanbul ignore next */ []));
21167
21371
  currentFiles = computed(() => {
21168
21372
  const externalFiles = this.filesInput();
@@ -21170,6 +21374,13 @@ class JvFileInputComponent {
21170
21374
  }, ...(ngDevMode ? [{ debugName: "currentFiles" }] : /* istanbul ignore next */ []));
21171
21375
  selectionSummary = computed(() => this.translation.translate('fileInput.selectionSummary', { count: this.currentFiles().length, max: this.maxFiles() }), ...(ngDevMode ? [{ debugName: "selectionSummary" }] : /* istanbul ignore next */ []));
21172
21376
  dropzoneAriaLabel = computed(() => `${this.label() || this.translation.translate('fileInput.dropLabel')}. ${this.selectionSummary()}`, ...(ngDevMode ? [{ debugName: "dropzoneAriaLabel" }] : /* istanbul ignore next */ []));
21377
+ resolvedDescribedBy = computed(() => [
21378
+ this.describedBy(),
21379
+ this.invalid() ? (this.error() ? `${this.inputId()}-error` : '') : (this.hint() ? `${this.inputId()}-hint` : ''),
21380
+ this.rejectionMessage() ? this.rejectionId : '',
21381
+ ]
21382
+ .filter(Boolean)
21383
+ .join(' '), ...(ngDevMode ? [{ debugName: "resolvedDescribedBy" }] : /* istanbul ignore next */ []));
21173
21384
  openFileDialog() {
21174
21385
  if (this.disabled())
21175
21386
  return;
@@ -21301,7 +21512,7 @@ class JvFileInputComponent {
21301
21512
  [attr.accept]="accept() || null"
21302
21513
  [attr.multiple]="multiple() ? '' : null"
21303
21514
  [disabled]="disabled()"
21304
- [attr.aria-describedby]="describedBy() || null"
21515
+ [attr.aria-describedby]="resolvedDescribedBy() || null"
21305
21516
  (change)="onNativeInputChange($any($event.target).files)"
21306
21517
  />
21307
21518
 
@@ -21330,7 +21541,7 @@ class JvFileInputComponent {
21330
21541
  </div>
21331
21542
 
21332
21543
  @if (rejectionMessage()) {
21333
- <p class="jv-file-input-rejection" aria-live="polite">{{ rejectionMessage() }}</p>
21544
+ <p class="jv-file-input-rejection" [id]="rejectionId" aria-live="polite">{{ rejectionMessage() }}</p>
21334
21545
  }
21335
21546
 
21336
21547
  @if (currentFiles().length > 0) {
@@ -21376,7 +21587,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
21376
21587
  [attr.accept]="accept() || null"
21377
21588
  [attr.multiple]="multiple() ? '' : null"
21378
21589
  [disabled]="disabled()"
21379
- [attr.aria-describedby]="describedBy() || null"
21590
+ [attr.aria-describedby]="resolvedDescribedBy() || null"
21380
21591
  (change)="onNativeInputChange($any($event.target).files)"
21381
21592
  />
21382
21593
 
@@ -21405,7 +21616,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
21405
21616
  </div>
21406
21617
 
21407
21618
  @if (rejectionMessage()) {
21408
- <p class="jv-file-input-rejection" aria-live="polite">{{ rejectionMessage() }}</p>
21619
+ <p class="jv-file-input-rejection" [id]="rejectionId" aria-live="polite">{{ rejectionMessage() }}</p>
21409
21620
  }
21410
21621
 
21411
21622
  @if (currentFiles().length > 0) {
@@ -21503,10 +21714,13 @@ class JvModalSelectorComponent {
21503
21714
  listboxId = `jv-modal-selector-listbox-${this.instanceId}`;
21504
21715
  resultsId = `jv-modal-selector-results-${this.instanceId}`;
21505
21716
  searchInputId = `jv-modal-selector-search-${this.instanceId}`;
21506
- value = null;
21717
+ value = signal(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
21507
21718
  disabled = false;
21508
21719
  resolvedDialogTitle = computed(() => this.dialogTitle() || this.label() || this.translation.translate('modalSelector.placeholder'), ...(ngDevMode ? [{ debugName: "resolvedDialogTitle" }] : /* istanbul ignore next */ []));
21509
- currentValue = computed(() => (this.valueInput() !== undefined ? this.valueInput() : this.value), ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
21720
+ currentValue = computed(() => (this.valueInput() !== undefined ? this.valueInput() : this.value()), ...(ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []));
21721
+ resolvedDescribedBy = computed(() => [this.describedBy(), this.invalid() ? (this.error() ? `${this.inputId()}-error` : '') : (this.hint() ? `${this.inputId()}-hint` : '')]
21722
+ .filter(Boolean)
21723
+ .join(' '), ...(ngDevMode ? [{ debugName: "resolvedDescribedBy" }] : /* istanbul ignore next */ []));
21510
21724
  selectedOption = computed(() => this.options().find((option) => Object.is(option.value, this.currentValue())) ?? null, ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
21511
21725
  resolvedPlaceholder = computed(() => this.placeholder() || this.translation.translate('modalSelector.placeholder'), ...(ngDevMode ? [{ debugName: "resolvedPlaceholder" }] : /* istanbul ignore next */ []));
21512
21726
  triggerAriaLabel = computed(() => {
@@ -21528,7 +21742,7 @@ class JvModalSelectorComponent {
21528
21742
  onChange = () => undefined;
21529
21743
  onTouched = () => undefined;
21530
21744
  writeValue(value) {
21531
- this.value = value ?? null;
21745
+ this.value.set(value ?? null);
21532
21746
  }
21533
21747
  registerOnChange(fn) {
21534
21748
  this.onChange = fn;
@@ -21610,7 +21824,7 @@ class JvModalSelectorComponent {
21610
21824
  this.onTouched();
21611
21825
  }
21612
21826
  updateValue(value) {
21613
- this.value = value;
21827
+ this.value.set(value);
21614
21828
  this.onChange(value);
21615
21829
  this.selectionChange.emit(value);
21616
21830
  }
@@ -21655,7 +21869,7 @@ class JvModalSelectorComponent {
21655
21869
  [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
21656
21870
  [attr.aria-controls]="dialogId"
21657
21871
  [attr.aria-invalid]="invalid() ? 'true' : null"
21658
- [attr.aria-describedby]="describedBy() || null"
21872
+ [attr.aria-describedby]="resolvedDescribedBy() || null"
21659
21873
  [attr.aria-label]="triggerAriaLabel()"
21660
21874
  (click)="openDialog()"
21661
21875
  (blur)="handleBlur()"
@@ -21669,12 +21883,8 @@ class JvModalSelectorComponent {
21669
21883
  </button>
21670
21884
  </jv-form-container>
21671
21885
 
21672
- <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
21886
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" [description]="dialogSubtitle()" (closed)="closeDialog()">
21673
21887
  <div [id]="dialogId" class="jv-modal-selector-dialog">
21674
- @if (dialogSubtitle()) {
21675
- <p class="jv-modal-selector-subtitle">{{ dialogSubtitle() }}</p>
21676
- }
21677
-
21678
21888
  @if (showSearch()) {
21679
21889
  <jv-input
21680
21890
  type="search"
@@ -21730,7 +21940,7 @@ class JvModalSelectorComponent {
21730
21940
  <jv-button variant="outline" (click)="closeDialog()">{{ closeLabel() || translation.translate('modalSelector.close') }}</jv-button>
21731
21941
  </div>
21732
21942
  </jv-dialog>
21733
- `, 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: ["listId", "role", "ariaRole", "variant", "ariaLabel"] }] });
21943
+ `, 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", "description"], 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: ["listId", "role", "ariaRole", "variant", "ariaLabel"] }] });
21734
21944
  }
21735
21945
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvModalSelectorComponent, decorators: [{
21736
21946
  type: Component,
@@ -21760,7 +21970,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
21760
21970
  [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
21761
21971
  [attr.aria-controls]="dialogId"
21762
21972
  [attr.aria-invalid]="invalid() ? 'true' : null"
21763
- [attr.aria-describedby]="describedBy() || null"
21973
+ [attr.aria-describedby]="resolvedDescribedBy() || null"
21764
21974
  [attr.aria-label]="triggerAriaLabel()"
21765
21975
  (click)="openDialog()"
21766
21976
  (blur)="handleBlur()"
@@ -21774,12 +21984,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
21774
21984
  </button>
21775
21985
  </jv-form-container>
21776
21986
 
21777
- <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
21987
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" [description]="dialogSubtitle()" (closed)="closeDialog()">
21778
21988
  <div [id]="dialogId" class="jv-modal-selector-dialog">
21779
- @if (dialogSubtitle()) {
21780
- <p class="jv-modal-selector-subtitle">{{ dialogSubtitle() }}</p>
21781
- }
21782
-
21783
21989
  @if (showSearch()) {
21784
21990
  <jv-input
21785
21991
  type="search"
@@ -22280,7 +22486,7 @@ class JvImageGalleryComponent {
22280
22486
  </div>
22281
22487
  </jv-dialog>
22282
22488
  </div>
22283
- `, isInline: true, styles: [".jv-gallery{display:block;width:100%;max-width:100vw}.jv-gallery-strip{display:flex;gap:5px;width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:none;-ms-overflow-style:none;scroll-snap-type:x proximity;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.jv-gallery-strip::-webkit-scrollbar{display:none}.jv-gallery-thumb{flex:0 0 auto;width:calc((100vw - 20px)/4);min-width:120px;height:100%;padding:0;border:2px solid transparent;border-radius:var(--jv-radius-sm);background:transparent;cursor:pointer;overflow:hidden;scroll-snap-align:start;transition:border-color .16s ease,opacity .16s ease}.jv-gallery-thumb:hover{opacity:.85}.jv-gallery-thumb--active{border-color:var(--jv-color-primary)}.jv-gallery-thumb:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-gallery-thumb-image{display:block;width:100%;height:100%;object-fit:cover;pointer-events:none}.jv-gallery-viewer{position:relative;display:flex;align-items:center;justify-content:center;width:100%;height:70vh;min-height:300px;background:#000;border-radius:var(--jv-radius-md);overflow:hidden}.jv-gallery-viewer:focus{outline:none}.jv-gallery-viewer-image{display:block;max-width:100%;max-height:100%;object-fit:contain;-webkit-user-select:none;user-select:none}@keyframes gallery-slide-left{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes gallery-slide-right{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.jv-gallery-viewer-image--slide-left{animation:gallery-slide-left .28s ease-out}.jv-gallery-viewer-image--slide-right{animation:gallery-slide-right .28s ease-out}.jv-gallery-viewer-nav{position:absolute;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:3rem;height:3rem;border:none;border-radius:var(--jv-radius-md);background:#00000080;color:#fff;cursor:pointer;transition:background-color .16s ease;z-index:1}.jv-gallery-viewer-nav:hover{background:#000000bf}.jv-gallery-viewer-nav:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-gallery-viewer-nav--prev{left:var(--jv-spacing-md)}.jv-gallery-viewer-nav--next{right:var(--jv-spacing-md)}.jv-gallery-viewer-counter{position:absolute;bottom:var(--jv-spacing-md);left:50%;transform:translate(-50%);padding:var(--jv-spacing-xs) var(--jv-spacing-sm);border-radius:var(--jv-radius-sm);background:#0009;color:#fff;font-size:.8125rem;font-variant-numeric:tabular-nums;pointer-events:none}@media(max-width:768px){.jv-gallery-thumb{width:calc(100vw - 20px);min-width:200px}.jv-gallery-viewer-nav{width:2.5rem;height:2.5rem}.jv-gallery-viewer-nav--prev{left:var(--jv-spacing-sm)}.jv-gallery-viewer-nav--next{right:var(--jv-spacing-sm)}}@media(prefers-reduced-motion:reduce){.jv-gallery-strip{scroll-behavior:auto}.jv-gallery-viewer-image--slide-left,.jv-gallery-viewer-image--slide-right{animation:none}}\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: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
22489
+ `, isInline: true, styles: [".jv-gallery{display:block;width:100%;max-width:100vw}.jv-gallery-strip{display:flex;gap:5px;width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:none;-ms-overflow-style:none;scroll-snap-type:x proximity;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.jv-gallery-strip::-webkit-scrollbar{display:none}.jv-gallery-thumb{flex:0 0 auto;width:calc((100vw - 20px)/4);min-width:120px;height:100%;padding:0;border:2px solid transparent;border-radius:var(--jv-radius-sm);background:transparent;cursor:pointer;overflow:hidden;scroll-snap-align:start;transition:border-color .16s ease,opacity .16s ease}.jv-gallery-thumb:hover{opacity:.85}.jv-gallery-thumb--active{border-color:var(--jv-color-primary)}.jv-gallery-thumb:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-gallery-thumb-image{display:block;width:100%;height:100%;object-fit:cover;pointer-events:none}.jv-gallery-viewer{position:relative;display:flex;align-items:center;justify-content:center;width:100%;height:70vh;min-height:300px;background:#000;border-radius:var(--jv-radius-md);overflow:hidden}.jv-gallery-viewer:focus{outline:none}.jv-gallery-viewer-image{display:block;max-width:100%;max-height:100%;object-fit:contain;-webkit-user-select:none;user-select:none}@keyframes gallery-slide-left{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes gallery-slide-right{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.jv-gallery-viewer-image--slide-left{animation:gallery-slide-left .28s ease-out}.jv-gallery-viewer-image--slide-right{animation:gallery-slide-right .28s ease-out}.jv-gallery-viewer-nav{position:absolute;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:3rem;height:3rem;border:none;border-radius:var(--jv-radius-md);background:#00000080;color:#fff;cursor:pointer;transition:background-color .16s ease;z-index:1}.jv-gallery-viewer-nav:hover{background:#000000bf}.jv-gallery-viewer-nav:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-gallery-viewer-nav--prev{left:var(--jv-spacing-md)}.jv-gallery-viewer-nav--next{right:var(--jv-spacing-md)}.jv-gallery-viewer-counter{position:absolute;bottom:var(--jv-spacing-md);left:50%;transform:translate(-50%);padding:var(--jv-spacing-xs) var(--jv-spacing-sm);border-radius:var(--jv-radius-sm);background:#0009;color:#fff;font-size:.8125rem;font-variant-numeric:tabular-nums;pointer-events:none}@media(max-width:768px){.jv-gallery-thumb{width:calc(100vw - 20px);min-width:200px}.jv-gallery-viewer-nav{width:2.5rem;height:2.5rem}.jv-gallery-viewer-nav--prev{left:var(--jv-spacing-sm)}.jv-gallery-viewer-nav--next{right:var(--jv-spacing-sm)}}@media(prefers-reduced-motion:reduce){.jv-gallery-strip{scroll-behavior:auto}.jv-gallery-viewer-image--slide-left,.jv-gallery-viewer-image--slide-right{animation:none}}\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", "description"], outputs: ["closed"] }, { kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
22284
22490
  }
22285
22491
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvImageGalleryComponent, decorators: [{
22286
22492
  type: Component,
@@ -22681,16 +22887,15 @@ class JvConfirmDialogComponent {
22681
22887
  <jv-dialog
22682
22888
  [open]="isOpen()"
22683
22889
  [title]="dialogTitle()"
22890
+ [description]="dialogMessage()"
22684
22891
  (closed)="dialogService.resolve(false)"
22685
22892
  >
22686
- <p>{{ dialogMessage() }}</p>
22687
-
22688
22893
  <div dialog-actions class="actions">
22689
22894
  <jv-button [variant]="confirmVariant()" (click)="dialogService.resolve(true)">{{ confirmLabel() }}</jv-button>
22690
22895
  <jv-button variant="ghost" (click)="dialogService.resolve(false)">{{ cancelLabel() }}</jv-button>
22691
22896
  </div>
22692
22897
  </jv-dialog>
22693
- `, 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"] }] });
22898
+ `, 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", "description"], outputs: ["closed"] }] });
22694
22899
  }
22695
22900
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvConfirmDialogComponent, decorators: [{
22696
22901
  type: Component,
@@ -22698,10 +22903,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
22698
22903
  <jv-dialog
22699
22904
  [open]="isOpen()"
22700
22905
  [title]="dialogTitle()"
22906
+ [description]="dialogMessage()"
22701
22907
  (closed)="dialogService.resolve(false)"
22702
22908
  >
22703
- <p>{{ dialogMessage() }}</p>
22704
-
22705
22909
  <div dialog-actions class="actions">
22706
22910
  <jv-button [variant]="confirmVariant()" (click)="dialogService.resolve(true)">{{ confirmLabel() }}</jv-button>
22707
22911
  <jv-button variant="ghost" (click)="dialogService.resolve(false)">{{ cancelLabel() }}</jv-button>
@@ -22716,6 +22920,7 @@ class JvWindowModalComponent {
22716
22920
  open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
22717
22921
  title = input('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
22718
22922
  subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
22923
+ description = input('', ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
22719
22924
  mode = input('floating', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
22720
22925
  size = input('xl', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
22721
22926
  showCloseButton = input(true, ...(ngDevMode ? [{ debugName: "showCloseButton" }] : /* istanbul ignore next */ []));
@@ -22725,6 +22930,7 @@ class JvWindowModalComponent {
22725
22930
  closed = output();
22726
22931
  close = output();
22727
22932
  resolvedMode = computed(() => (this.size() === 'fullscreen' ? 'fullscreen' : this.mode()), ...(ngDevMode ? [{ debugName: "resolvedMode" }] : /* istanbul ignore next */ []));
22933
+ resolvedDescription = computed(() => this.description() || this.subtitle(), ...(ngDevMode ? [{ debugName: "resolvedDescription" }] : /* istanbul ignore next */ []));
22728
22934
  titleId = `jv-window-modal-title-${Math.random().toString(36).slice(2, 8)}`;
22729
22935
  descriptionId = `jv-window-modal-description-${Math.random().toString(36).slice(2, 8)}`;
22730
22936
  previousFocusedElement = null;
@@ -22813,7 +23019,7 @@ class JvWindowModalComponent {
22813
23019
  return Array.from(panel.querySelectorAll('button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'));
22814
23020
  }
22815
23021
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvWindowModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
22816
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvWindowModalComponent, isStandalone: true, selector: "jv-window-modal", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showCloseButton: { classPropertyName: "showCloseButton", publicName: "showCloseButton", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdrop: { classPropertyName: "closeOnBackdrop", publicName: "closeOnBackdrop", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", close: "close" }, host: { listeners: { "document:keydown": "handleDocumentKeydown($event)" } }, viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: `
23022
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvWindowModalComponent, isStandalone: true, selector: "jv-window-modal", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showCloseButton: { classPropertyName: "showCloseButton", publicName: "showCloseButton", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdrop: { classPropertyName: "closeOnBackdrop", publicName: "closeOnBackdrop", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", close: "close" }, host: { listeners: { "document:keydown": "handleDocumentKeydown($event)" } }, viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: `
22817
23023
  @if (open()) {
22818
23024
  <div class="jv-window-modal-backdrop" tabindex="-1" (click)="backdropClose()" (keydown.escape)="backdropClose()">
22819
23025
  <section
@@ -22826,7 +23032,7 @@ class JvWindowModalComponent {
22826
23032
  aria-modal="true"
22827
23033
  tabindex="-1"
22828
23034
  [attr.aria-labelledby]="title() ? titleId : null"
22829
- [attr.aria-describedby]="descriptionId"
23035
+ [attr.aria-describedby]="resolvedDescription() ? descriptionId : null"
22830
23036
  (click)="$event.stopPropagation()"
22831
23037
  (keydown)="$event.stopPropagation()"
22832
23038
  #panel
@@ -22837,8 +23043,10 @@ class JvWindowModalComponent {
22837
23043
  <h2 [id]="titleId">{{ title() }}</h2>
22838
23044
  }
22839
23045
 
22840
- @if (subtitle()) {
22841
- <p class="jv-window-modal-subtitle">{{ subtitle() }}</p>
23046
+ @if (description()) {
23047
+ <p class="jv-window-modal-description" [id]="descriptionId">{{ description() }}</p>
23048
+ } @else if (subtitle()) {
23049
+ <p class="jv-window-modal-subtitle" [id]="descriptionId">{{ subtitle() }}</p>
22842
23050
  }
22843
23051
  </div>
22844
23052
 
@@ -22851,7 +23059,7 @@ class JvWindowModalComponent {
22851
23059
  </div>
22852
23060
  </header>
22853
23061
 
22854
- <div class="jv-window-modal-body" [id]="descriptionId">
23062
+ <div class="jv-window-modal-body">
22855
23063
  <ng-content></ng-content>
22856
23064
  </div>
22857
23065
 
@@ -22878,7 +23086,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
22878
23086
  aria-modal="true"
22879
23087
  tabindex="-1"
22880
23088
  [attr.aria-labelledby]="title() ? titleId : null"
22881
- [attr.aria-describedby]="descriptionId"
23089
+ [attr.aria-describedby]="resolvedDescription() ? descriptionId : null"
22882
23090
  (click)="$event.stopPropagation()"
22883
23091
  (keydown)="$event.stopPropagation()"
22884
23092
  #panel
@@ -22889,8 +23097,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
22889
23097
  <h2 [id]="titleId">{{ title() }}</h2>
22890
23098
  }
22891
23099
 
22892
- @if (subtitle()) {
22893
- <p class="jv-window-modal-subtitle">{{ subtitle() }}</p>
23100
+ @if (description()) {
23101
+ <p class="jv-window-modal-description" [id]="descriptionId">{{ description() }}</p>
23102
+ } @else if (subtitle()) {
23103
+ <p class="jv-window-modal-subtitle" [id]="descriptionId">{{ subtitle() }}</p>
22894
23104
  }
22895
23105
  </div>
22896
23106
 
@@ -22903,7 +23113,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
22903
23113
  </div>
22904
23114
  </header>
22905
23115
 
22906
- <div class="jv-window-modal-body" [id]="descriptionId">
23116
+ <div class="jv-window-modal-body">
22907
23117
  <ng-content></ng-content>
22908
23118
  </div>
22909
23119
 
@@ -22914,7 +23124,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
22914
23124
  </div>
22915
23125
  }
22916
23126
  `, styles: [".jv-window-modal-backdrop{position:fixed;inset:0;z-index:1250;display:flex;align-items:center;justify-content:center;padding:1rem;background:#0f172a94}.jv-window-modal-panel{width:min(100%,72rem);max-width:100%;max-height:calc(100dvh - 2rem);display:grid;grid-template-rows:auto minmax(0,1fr) auto;overflow:hidden;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-xl, 1.25rem);background:var(--jv-color-surface);color:var(--jv-color-foreground);box-shadow:var(--jv-shadow-lg)}.jv-window-modal-panel--lg{width:min(100%,56rem)}.jv-window-modal-panel--xl{width:min(100%,72rem)}.jv-window-modal-panel--fullscreen{width:min(100%,calc(100vw - 1rem));height:calc(100dvh - 1rem);max-height:calc(100dvh - 1rem)}.jv-window-modal-header,.jv-window-modal-footer{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-lg);background:var(--jv-color-surface)}.jv-window-modal-header{border-bottom:1px solid var(--jv-color-border)}.jv-window-modal-footer{border-top:1px solid var(--jv-color-border);justify-content:flex-end}.jv-window-modal-footer:empty{display:none}.jv-window-modal-heading{display:grid;gap:.35rem;min-width:0}.jv-window-modal-heading h2,.jv-window-modal-subtitle{margin:0}.jv-window-modal-subtitle{color:var(--jv-color-foreground-muted)}.jv-window-modal-header-actions{display:inline-flex;align-items:center;gap:var(--jv-spacing-sm);flex-shrink:0}.jv-window-modal-body{min-height:0;overflow:auto;padding:var(--jv-spacing-lg);display:grid;gap:var(--jv-spacing-lg);overscroll-behavior:contain}@media(max-width:720px){.jv-window-modal-backdrop{padding:0}.jv-window-modal-panel,.jv-window-modal-panel--fullscreen{width:100vw;height:100dvh;max-height:100dvh;border-radius:0;border-left:none;border-right:none}.jv-window-modal-header,.jv-window-modal-body,.jv-window-modal-footer{padding:var(--jv-spacing-md)}}\n"] }]
22917
- }], 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 }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], showCloseButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCloseButton", required: false }] }], closeOnBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdrop", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], close: [{ type: i0.Output, args: ["close"] }], handleDocumentKeydown: [{
23127
+ }], 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 }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], showCloseButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCloseButton", required: false }] }], closeOnBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdrop", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], close: [{ type: i0.Output, args: ["close"] }], handleDocumentKeydown: [{
22918
23128
  type: HostListener,
22919
23129
  args: ['document:keydown', ['$event']]
22920
23130
  }] } });
@@ -22969,24 +23179,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
22969
23179
  `, styles: [".jv-breadcrumb ol{display:flex;flex-wrap:wrap;gap:var(--jv-spacing-xs);margin:0;padding:0;list-style:none;color:var(--jv-color-foreground-muted);font-size:.875rem}.jv-breadcrumb li{display:inline-flex;align-items:center;gap:var(--jv-spacing-xs)}.jv-breadcrumb a{color:inherit;text-decoration:none}.jv-breadcrumb a:hover{color:var(--jv-color-foreground);text-decoration:underline}.separator{opacity:.7}\n"] }]
22970
23180
  }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }] } });
22971
23181
 
22972
- class JvSidebarComponent {
22973
- router = inject(Router);
22974
- expandedSections = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedSections" }] : /* istanbul ignore next */ []));
22975
- title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
22976
- subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
22977
- brandIcon = input('monitor', ...(ngDevMode ? [{ debugName: "brandIcon" }] : /* istanbul ignore next */ []));
22978
- items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
22979
- collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
22980
- mobileOpen = input(false, ...(ngDevMode ? [{ debugName: "mobileOpen" }] : /* istanbul ignore next */ []));
22981
- ariaLabel = input('Sidebar navigation', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
22982
- currentTheme = input('light', ...(ngDevMode ? [{ debugName: "currentTheme" }] : /* istanbul ignore next */ []));
22983
- themeOptions = input([], ...(ngDevMode ? [{ debugName: "themeOptions" }] : /* istanbul ignore next */ []));
22984
- topbarActions = input([], ...(ngDevMode ? [{ debugName: "topbarActions" }] : /* istanbul ignore next */ []));
22985
- showThemeSelector = input(true, ...(ngDevMode ? [{ debugName: "showThemeSelector" }] : /* istanbul ignore next */ []));
22986
- collapseToggle = output();
22987
- mobileClose = output();
22988
- themeChange = output();
22989
- actionClick = output();
23182
+ class JvNavigationState {
23183
+ items;
23184
+ router;
23185
+ expandActiveSections;
23186
+ sectionOverrides = signal(new Map(), ...(ngDevMode ? [{ debugName: "sectionOverrides" }] : /* istanbul ignore next */ []));
23187
+ normalizedItems = computed(() => this.items().map((item) => ({
23188
+ ...item,
23189
+ children: item.children ?? [],
23190
+ })), ...(ngDevMode ? [{ debugName: "normalizedItems" }] : /* istanbul ignore next */ []));
22990
23191
  activeChildParentIds = computed(() => {
22991
23192
  const url = this.router.url;
22992
23193
  const ids = new Set();
@@ -22999,22 +23200,33 @@ class JvSidebarComponent {
22999
23200
  }
23000
23201
  return ids;
23001
23202
  }, ...(ngDevMode ? [{ debugName: "activeChildParentIds" }] : /* istanbul ignore next */ []));
23002
- normalizedItems = computed(() => this.items().map((item) => ({
23003
- ...item,
23004
- children: item.children ?? [],
23005
- })), ...(ngDevMode ? [{ debugName: "normalizedItems" }] : /* istanbul ignore next */ []));
23006
- isExpanded(id) {
23007
- return this.expandedSections().has(id) || this.activeChildParentIds().has(id);
23203
+ constructor(items, router, expandActiveSections = true) {
23204
+ this.items = items;
23205
+ this.router = router;
23206
+ this.expandActiveSections = expandActiveSections;
23008
23207
  }
23009
- toggleSection(id) {
23010
- this.expandedSections.update((current) => {
23011
- const next = new Set(current);
23012
- if (next.has(id)) {
23013
- next.delete(id);
23014
- }
23015
- else {
23016
- next.add(id);
23208
+ isExpanded(id) {
23209
+ return this.sectionOverrides().get(id) ?? (this.expandActiveSections && this.activeChildParentIds().has(id));
23210
+ }
23211
+ toggleSection(id, exclusive = false) {
23212
+ this.sectionOverrides.update((current) => {
23213
+ const next = new Map(current);
23214
+ const shouldExpand = !this.isExpanded(id);
23215
+ if (exclusive) {
23216
+ for (const item of this.normalizedItems()) {
23217
+ if (item.children.length > 0) {
23218
+ next.set(item.id, false);
23219
+ }
23220
+ }
23017
23221
  }
23222
+ next.set(id, shouldExpand);
23223
+ return next;
23224
+ });
23225
+ }
23226
+ closeSection(id) {
23227
+ this.sectionOverrides.update((current) => {
23228
+ const next = new Map(current);
23229
+ next.set(id, false);
23018
23230
  return next;
23019
23231
  });
23020
23232
  }
@@ -23028,19 +23240,51 @@ class JvSidebarComponent {
23028
23240
  const target = document.querySelector(href);
23029
23241
  target?.scrollIntoView({ block: 'start', behavior: 'smooth' });
23030
23242
  }
23031
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
23032
- 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: `
23033
- @if (mobileOpen()) {
23034
- <div class="jv-sidebar-backdrop" tabindex="-1" (click)="mobileClose.emit()" (keydown.escape)="mobileClose.emit()"></div>
23035
- }
23243
+ }
23036
23244
 
23037
- <aside
23038
- class="jv-sidebar"
23039
- [class.is-collapsed]="collapsed()"
23040
- [class.is-mobile-open]="mobileOpen()"
23041
- >
23042
- <div class="jv-sidebar-brand">
23043
- <div class="brand-mark">
23245
+ class JvSidebarComponent {
23246
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
23247
+ subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
23248
+ brandIcon = input('monitor', ...(ngDevMode ? [{ debugName: "brandIcon" }] : /* istanbul ignore next */ []));
23249
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
23250
+ collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
23251
+ mobileOpen = input(false, ...(ngDevMode ? [{ debugName: "mobileOpen" }] : /* istanbul ignore next */ []));
23252
+ ariaLabel = input('Sidebar navigation', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
23253
+ currentTheme = input('light', ...(ngDevMode ? [{ debugName: "currentTheme" }] : /* istanbul ignore next */ []));
23254
+ themeOptions = input([], ...(ngDevMode ? [{ debugName: "themeOptions" }] : /* istanbul ignore next */ []));
23255
+ topbarActions = input([], ...(ngDevMode ? [{ debugName: "topbarActions" }] : /* istanbul ignore next */ []));
23256
+ showThemeSelector = input(true, ...(ngDevMode ? [{ debugName: "showThemeSelector" }] : /* istanbul ignore next */ []));
23257
+ navigation = new JvNavigationState(this.items, inject(Router));
23258
+ collapseToggle = output();
23259
+ mobileClose = output();
23260
+ themeChange = output();
23261
+ actionClick = output();
23262
+ normalizedItems = this.navigation.normalizedItems;
23263
+ isExpanded(id) {
23264
+ return this.navigation.isExpanded(id);
23265
+ }
23266
+ toggleSection(id) {
23267
+ this.navigation.toggleSection(id);
23268
+ }
23269
+ isHashLink(href) {
23270
+ return this.navigation.isHashLink(href);
23271
+ }
23272
+ navigateTo(href) {
23273
+ this.navigation.navigateTo(href);
23274
+ }
23275
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
23276
+ 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: `
23277
+ @if (mobileOpen()) {
23278
+ <div class="jv-sidebar-backdrop" tabindex="-1" (click)="mobileClose.emit()" (keydown.escape)="mobileClose.emit()"></div>
23279
+ }
23280
+
23281
+ <aside
23282
+ class="jv-sidebar"
23283
+ [class.is-collapsed]="collapsed()"
23284
+ [class.is-mobile-open]="mobileOpen()"
23285
+ >
23286
+ <div class="jv-sidebar-brand">
23287
+ <div class="brand-mark">
23044
23288
  <jv-icon [name]="brandIcon()" [size]="18" />
23045
23289
  </div>
23046
23290
 
@@ -23176,19 +23420,20 @@ class JvSidebarComponent {
23176
23420
 
23177
23421
  <div class="mobile-actions">
23178
23422
  @for (action of topbarActions(); track action.id) {
23179
- <button type="button" class="icon-button" [attr.aria-label]="action.label" (click)="actionClick.emit(action.id); mobileClose.emit()">
23423
+ <button type="button" class="icon-button" [attr.aria-label]="action.label" [attr.title]="action.label" [jvTooltip]="action.label" (click)="actionClick.emit(action.id); mobileClose.emit()">
23180
23424
  <jv-icon [name]="action.icon" [size]="18" />
23425
+ <span>{{ action.label }}</span>
23181
23426
  </button>
23182
23427
  }
23183
23428
  </div>
23184
23429
  </div>
23185
23430
  </div>
23186
23431
  </aside>
23187
- `, 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"] }] });
23432
+ `, 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-flex;align-items:center;gap:var(--jv-spacing-xs);min-width:var(--jv-density-control-height);height:var(--jv-density-control-height);padding:0 var(--jv-spacing-sm);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"] }, { kind: "directive", type: JvTooltipDirective, selector: "[jvTooltip]", inputs: ["jvTooltip", "jvTooltipPosition"] }] });
23188
23433
  }
23189
23434
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvSidebarComponent, decorators: [{
23190
23435
  type: Component,
23191
- args: [{ selector: 'jv-sidebar', standalone: true, imports: [JvIconComponent, RouterLink], template: `
23436
+ args: [{ selector: 'jv-sidebar', standalone: true, imports: [JvIconComponent, RouterLink, JvTooltipDirective], template: `
23192
23437
  @if (mobileOpen()) {
23193
23438
  <div class="jv-sidebar-backdrop" tabindex="-1" (click)="mobileClose.emit()" (keydown.escape)="mobileClose.emit()"></div>
23194
23439
  }
@@ -23335,15 +23580,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
23335
23580
 
23336
23581
  <div class="mobile-actions">
23337
23582
  @for (action of topbarActions(); track action.id) {
23338
- <button type="button" class="icon-button" [attr.aria-label]="action.label" (click)="actionClick.emit(action.id); mobileClose.emit()">
23583
+ <button type="button" class="icon-button" [attr.aria-label]="action.label" [attr.title]="action.label" [jvTooltip]="action.label" (click)="actionClick.emit(action.id); mobileClose.emit()">
23339
23584
  <jv-icon [name]="action.icon" [size]="18" />
23585
+ <span>{{ action.label }}</span>
23340
23586
  </button>
23341
23587
  }
23342
23588
  </div>
23343
23589
  </div>
23344
23590
  </div>
23345
23591
  </aside>
23346
- `, 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"] }]
23592
+ `, 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-flex;align-items:center;gap:var(--jv-spacing-xs);min-width:var(--jv-density-control-height);height:var(--jv-density-control-height);padding:0 var(--jv-spacing-sm);border:1px solid var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);cursor:pointer}}\n"] }]
23347
23593
  }], 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"] }] } });
23348
23594
 
23349
23595
  class JvTopbarComponent {
@@ -23397,17 +23643,18 @@ class JvTopbarComponent {
23397
23643
  }
23398
23644
 
23399
23645
  @for (action of visibleActions(); track action.id) {
23400
- <button type="button" class="icon-button" [attr.aria-label]="action.label" (click)="actionClick.emit(action.id)">
23646
+ <button type="button" class="icon-button" [attr.aria-label]="action.label" [attr.title]="action.label" [jvTooltip]="action.label" (click)="actionClick.emit(action.id)">
23401
23647
  <jv-icon [name]="action.icon" [size]="18" />
23648
+ <span class="action-label">{{ action.label }}</span>
23402
23649
  </button>
23403
23650
  }
23404
23651
  </div>
23405
23652
  </header>
23406
- `, isInline: true, styles: [".jv-topbar{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-md) var(--jv-spacing-lg);border-bottom:1px solid var(--jv-color-border);background:var(--jv-color-surface)}.jv-topbar-start,.jv-topbar-end,.theme-group{display:flex;align-items:center;gap:var(--jv-spacing-sm)}.jv-topbar-end{flex-wrap:wrap;justify-content:flex-end}.title-block{display:grid;gap:.1rem}.title-block span{color:var(--jv-color-foreground-muted);font-size:.875rem}.icon-button,.theme-pill{border:1px solid var(--jv-color-border);background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.icon-button{display:inline-grid;place-items:center;width:var(--jv-density-control-height);height:var(--jv-density-control-height);border-radius:999px}.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-radius:999px}.theme-pill.is-active{border-color:var(--jv-color-primary);background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface))}@media(max-width:900px){.jv-topbar{align-items:center;flex-direction:row;justify-content:flex-start}.jv-topbar-end{display:none}}\n"], dependencies: [{ kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }] });
23653
+ `, isInline: true, styles: [".jv-topbar{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-md) var(--jv-spacing-lg);border-bottom:1px solid var(--jv-color-border);background:var(--jv-color-surface)}.jv-topbar-start,.jv-topbar-end,.theme-group{display:flex;align-items:center;gap:var(--jv-spacing-sm)}.jv-topbar-end{flex-wrap:wrap;justify-content:flex-end}.title-block{display:grid;gap:.1rem}.title-block span{color:var(--jv-color-foreground-muted);font-size:.875rem}.icon-button,.theme-pill{border:1px solid var(--jv-color-border);background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.icon-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--jv-spacing-xs);min-width:var(--jv-density-control-height);height:var(--jv-density-control-height);padding:0 var(--jv-spacing-sm);border-radius:999px;cursor:pointer}.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-radius:999px}.theme-pill.is-active{border-color:var(--jv-color-primary);background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface))}@media(max-width:900px){.jv-topbar{align-items:center;flex-direction:row;justify-content:flex-start}.jv-topbar-end{display:none}}\n"], dependencies: [{ kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }, { kind: "directive", type: JvTooltipDirective, selector: "[jvTooltip]", inputs: ["jvTooltip", "jvTooltipPosition"] }] });
23407
23654
  }
23408
23655
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTopbarComponent, decorators: [{
23409
23656
  type: Component,
23410
- args: [{ selector: 'jv-topbar', standalone: true, imports: [JvIconComponent], template: `
23657
+ args: [{ selector: 'jv-topbar', standalone: true, imports: [JvIconComponent, JvTooltipDirective], template: `
23411
23658
  <header class="jv-topbar">
23412
23659
  <div class="jv-topbar-start">
23413
23660
  <button type="button" class="icon-button" (click)="menuClick.emit()" aria-label="Toggle navigation menu">
@@ -23446,17 +23693,265 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
23446
23693
  }
23447
23694
 
23448
23695
  @for (action of visibleActions(); track action.id) {
23449
- <button type="button" class="icon-button" [attr.aria-label]="action.label" (click)="actionClick.emit(action.id)">
23696
+ <button type="button" class="icon-button" [attr.aria-label]="action.label" [attr.title]="action.label" [jvTooltip]="action.label" (click)="actionClick.emit(action.id)">
23450
23697
  <jv-icon [name]="action.icon" [size]="18" />
23698
+ <span class="action-label">{{ action.label }}</span>
23451
23699
  </button>
23452
23700
  }
23453
23701
  </div>
23454
23702
  </header>
23455
- `, styles: [".jv-topbar{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-md) var(--jv-spacing-lg);border-bottom:1px solid var(--jv-color-border);background:var(--jv-color-surface)}.jv-topbar-start,.jv-topbar-end,.theme-group{display:flex;align-items:center;gap:var(--jv-spacing-sm)}.jv-topbar-end{flex-wrap:wrap;justify-content:flex-end}.title-block{display:grid;gap:.1rem}.title-block span{color:var(--jv-color-foreground-muted);font-size:.875rem}.icon-button,.theme-pill{border:1px solid var(--jv-color-border);background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.icon-button{display:inline-grid;place-items:center;width:var(--jv-density-control-height);height:var(--jv-density-control-height);border-radius:999px}.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-radius:999px}.theme-pill.is-active{border-color:var(--jv-color-primary);background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface))}@media(max-width:900px){.jv-topbar{align-items:center;flex-direction:row;justify-content:flex-start}.jv-topbar-end{display:none}}\n"] }]
23703
+ `, styles: [".jv-topbar{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-md) var(--jv-spacing-lg);border-bottom:1px solid var(--jv-color-border);background:var(--jv-color-surface)}.jv-topbar-start,.jv-topbar-end,.theme-group{display:flex;align-items:center;gap:var(--jv-spacing-sm)}.jv-topbar-end{flex-wrap:wrap;justify-content:flex-end}.title-block{display:grid;gap:.1rem}.title-block span{color:var(--jv-color-foreground-muted);font-size:.875rem}.icon-button,.theme-pill{border:1px solid var(--jv-color-border);background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.icon-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--jv-spacing-xs);min-width:var(--jv-density-control-height);height:var(--jv-density-control-height);padding:0 var(--jv-spacing-sm);border-radius:999px;cursor:pointer}.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-radius:999px}.theme-pill.is-active{border-color:var(--jv-color-primary);background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface))}@media(max-width:900px){.jv-topbar{align-items:center;flex-direction:row;justify-content:flex-start}.jv-topbar-end{display:none}}\n"] }]
23456
23704
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], currentTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentTheme", required: false }] }], themeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "themeOptions", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], showThemeSelector: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThemeSelector", required: false }] }], menuClick: [{ type: i0.Output, args: ["menuClick"] }], themeChange: [{ type: i0.Output, args: ["themeChange"] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }] } });
23457
23705
 
23706
+ class JvTopbarMenuComponent {
23707
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
23708
+ subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
23709
+ brandIcon = input('monitor', ...(ngDevMode ? [{ debugName: "brandIcon" }] : /* istanbul ignore next */ []));
23710
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
23711
+ mobileOpen = input(false, ...(ngDevMode ? [{ debugName: "mobileOpen" }] : /* istanbul ignore next */ []));
23712
+ ariaLabel = input('Topbar navigation', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
23713
+ currentTheme = input('light', ...(ngDevMode ? [{ debugName: "currentTheme" }] : /* istanbul ignore next */ []));
23714
+ themeOptions = input([], ...(ngDevMode ? [{ debugName: "themeOptions" }] : /* istanbul ignore next */ []));
23715
+ actions = input([], ...(ngDevMode ? [{ debugName: "actions" }] : /* istanbul ignore next */ []));
23716
+ showThemeSelector = input(true, ...(ngDevMode ? [{ debugName: "showThemeSelector" }] : /* istanbul ignore next */ []));
23717
+ menuClick = output();
23718
+ mobileClose = output();
23719
+ themeChange = output();
23720
+ actionClick = output();
23721
+ navigation = new JvNavigationState(this.items, inject(Router), false);
23722
+ normalizedItems = this.navigation.normalizedItems;
23723
+ isExpanded(id) {
23724
+ return this.navigation.isExpanded(id);
23725
+ }
23726
+ toggleSection(id) {
23727
+ this.navigation.toggleSection(id, true);
23728
+ }
23729
+ isHashLink(href) {
23730
+ return this.navigation.isHashLink(href);
23731
+ }
23732
+ navigateTo(href, parentId) {
23733
+ this.navigation.navigateTo(href);
23734
+ if (parentId) {
23735
+ this.navigation.closeSection(parentId);
23736
+ }
23737
+ this.mobileClose.emit();
23738
+ }
23739
+ closeSection(id) {
23740
+ this.navigation.closeSection(id);
23741
+ }
23742
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTopbarMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
23743
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvTopbarMenuComponent, isStandalone: true, selector: "jv-topbar-menu", 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 }, 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 }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, showThemeSelector: { classPropertyName: "showThemeSelector", publicName: "showThemeSelector", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuClick: "menuClick", mobileClose: "mobileClose", themeChange: "themeChange", actionClick: "actionClick" }, ngImport: i0, template: `
23744
+ @if (mobileOpen()) {
23745
+ <div class="jv-topbar-menu-backdrop" tabindex="-1" (click)="mobileClose.emit()" (keydown.escape)="mobileClose.emit()"></div>
23746
+ }
23747
+
23748
+ <header class="jv-topbar-menu" [class.is-mobile-open]="mobileOpen()">
23749
+ <div class="jv-topbar-menu-brand">
23750
+ <div class="brand-mark">
23751
+ <jv-icon [name]="brandIcon()" [size]="18" />
23752
+ </div>
23753
+ <div class="brand-copy">
23754
+ <strong>{{ title() }}</strong>
23755
+ @if (subtitle()) {
23756
+ <span>{{ subtitle() }}</span>
23757
+ }
23758
+ </div>
23759
+ </div>
23760
+
23761
+ <button type="button" class="mobile-menu-button" aria-label="Toggle navigation menu" (click)="menuClick.emit()">
23762
+ <jv-icon name="menu" [size]="18" />
23763
+ </button>
23764
+
23765
+ <nav class="jv-topbar-menu-nav" [attr.aria-label]="ariaLabel()">
23766
+ <ul class="jv-topbar-menu-list">
23767
+ @for (item of normalizedItems(); track item.id) {
23768
+ <li class="jv-topbar-menu-item">
23769
+ @if (item.children.length > 0) {
23770
+ <button
23771
+ type="button"
23772
+ class="jv-topbar-menu-link"
23773
+ [class.is-active]="item.active"
23774
+ [attr.aria-expanded]="isExpanded(item.id) ? 'true' : 'false'"
23775
+ [attr.aria-controls]="'jv-topbar-menu-section-' + item.id"
23776
+ (click)="toggleSection(item.id)"
23777
+ >
23778
+ @if (item.icon) {
23779
+ <jv-icon [name]="item.icon" [size]="17" />
23780
+ }
23781
+ <span>{{ item.label }}</span>
23782
+ <jv-icon [name]="isExpanded(item.id) ? 'chevron-up' : 'chevron-down'" [size]="15" />
23783
+ </button>
23784
+
23785
+ @if (isExpanded(item.id)) {
23786
+ <div class="jv-topbar-menu-dropdown" [id]="'jv-topbar-menu-section-' + item.id">
23787
+ @for (child of item.children; track child.id) {
23788
+ @if (isHashLink(child.href)) {
23789
+ <button type="button" class="jv-topbar-menu-child" [class.is-active]="child.active" (click)="navigateTo(child.href, item.id)">
23790
+ {{ child.label }}
23791
+ </button>
23792
+ } @else {
23793
+ <a class="jv-topbar-menu-child" [class.is-active]="child.active" [routerLink]="child.href" [attr.aria-current]="child.active ? 'page' : null" (click)="closeSection(item.id); mobileClose.emit()">
23794
+ {{ child.label }}
23795
+ </a>
23796
+ }
23797
+ }
23798
+ </div>
23799
+ }
23800
+ } @else {
23801
+ @if (isHashLink(item.href)) {
23802
+ <button type="button" class="jv-topbar-menu-link" [class.is-active]="item.active" (click)="navigateTo(item.href)">
23803
+ @if (item.icon) {
23804
+ <jv-icon [name]="item.icon" [size]="17" />
23805
+ }
23806
+ <span>{{ item.label }}</span>
23807
+ </button>
23808
+ } @else {
23809
+ <a class="jv-topbar-menu-link" [class.is-active]="item.active" [routerLink]="item.href" [attr.aria-current]="item.active ? 'page' : null" (click)="mobileClose.emit()">
23810
+ @if (item.icon) {
23811
+ <jv-icon [name]="item.icon" [size]="17" />
23812
+ }
23813
+ <span>{{ item.label }}</span>
23814
+ </a>
23815
+ }
23816
+ }
23817
+ </li>
23818
+ }
23819
+ </ul>
23820
+ </nav>
23821
+
23822
+ <div class="jv-topbar-menu-actions">
23823
+ @if (showThemeSelector()) {
23824
+ <div class="theme-group" aria-label="Theme selector">
23825
+ @for (option of themeOptions(); track option.value) {
23826
+ <button type="button" class="theme-pill" [class.is-active]="currentTheme() === option.value" (click)="themeChange.emit(option.value)">
23827
+ @if (option.icon) {
23828
+ <jv-icon [name]="option.icon" [size]="16" />
23829
+ }
23830
+ <span>{{ option.label }}</span>
23831
+ </button>
23832
+ }
23833
+ </div>
23834
+ }
23835
+
23836
+ @for (action of actions(); track action.id) {
23837
+ <button type="button" class="icon-button" [attr.aria-label]="action.label" [attr.title]="action.label" [jvTooltip]="action.label" (click)="actionClick.emit(action.id)">
23838
+ <jv-icon [name]="action.icon" [size]="18" />
23839
+ <span class="action-label">{{ action.label }}</span>
23840
+ </button>
23841
+ }
23842
+ </div>
23843
+ </header>
23844
+ `, isInline: true, styles: [":host{display:block;position:relative;z-index:20}.jv-topbar-menu,.jv-topbar-menu-brand,.jv-topbar-menu-list,.jv-topbar-menu-actions,.theme-group{display:flex;align-items:center}.jv-topbar-menu{gap:var(--jv-spacing-lg);min-height:4.5rem;padding:var(--jv-spacing-md) var(--jv-spacing-xl);min-width:0;border-bottom:1px solid var(--jv-color-border);background:var(--jv-color-surface)}.jv-topbar-menu-brand{gap:var(--jv-spacing-sm);flex:0 0 auto}.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)}.brand-copy{display:grid;gap:.1rem}.brand-copy span{color:var(--jv-color-foreground-muted);font-size:.8125rem}.jv-topbar-menu-nav{min-width:0;flex:1 1 0}.jv-topbar-menu-list{gap:var(--jv-spacing-xs);margin:0;padding:0;list-style:none;min-width:0;flex-wrap:wrap}.jv-topbar-menu-item{position:relative;min-width:0;flex:0 1 auto}.jv-topbar-menu-link,.jv-topbar-menu-child,.icon-button,.theme-pill,.mobile-menu-button{border:1px solid transparent;background:transparent;color:var(--jv-color-foreground-muted);cursor:pointer;font:inherit;text-decoration:none}.jv-topbar-menu-link{display:inline-flex;align-items:center;gap:var(--jv-spacing-xs);min-height:var(--jv-density-control-height);min-width:0;width:100%;max-width:15rem;padding:0 var(--jv-spacing-sm);border-radius:var(--jv-radius-md);white-space:nowrap}.jv-topbar-menu-link span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.jv-topbar-menu-link:hover,.jv-topbar-menu-child:hover,.jv-topbar-menu-link.is-active,.jv-topbar-menu-child.is-active{background:color-mix(in srgb,var(--jv-color-primary) 12%,var(--jv-color-surface));color:var(--jv-color-foreground)}.jv-topbar-menu-dropdown{position:absolute;top:calc(100% + var(--jv-spacing-xs));left:0;display:grid;min-width:14rem;max-height:min(70vh,32rem);gap:var(--jv-spacing-xs);padding:var(--jv-spacing-sm);overflow-y:auto;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg);z-index:30}.jv-topbar-menu-child{display:block;padding:var(--jv-spacing-sm) var(--jv-spacing-md);border-radius:var(--jv-radius-sm);text-align:left;white-space:nowrap}.jv-topbar-menu-actions{justify-content:flex-end;gap:var(--jv-spacing-sm);flex:0 0 auto}.theme-group{gap:var(--jv-spacing-xs)}.icon-button,.mobile-menu-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--jv-spacing-xs);min-width:var(--jv-density-control-height);height:var(--jv-density-control-height);padding:0 var(--jv-spacing-sm);border-color:var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.icon-button{cursor:pointer}.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-sm);border-color:var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);font-size:.8125rem}.theme-pill.is-active{border-color:var(--jv-color-primary);background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface))}.mobile-menu-button,.jv-topbar-menu-backdrop{display:none}@media(max-width:1100px){.jv-topbar-menu{gap:var(--jv-spacing-md);padding-inline:var(--jv-spacing-lg)}.jv-topbar-menu-link{max-width:12rem;padding-inline:var(--jv-spacing-xs)}}@media(max-width:1400px){.jv-topbar-menu-actions .theme-group{display:none}}@media(max-width:900px){.jv-topbar-menu-backdrop{display:block;position:fixed;inset:0;z-index:19;background:#0006}.jv-topbar-menu{position:relative;justify-content:space-between;min-height:4rem;padding:var(--jv-spacing-sm) var(--jv-spacing-md)}.mobile-menu-button{display:inline-grid;order:3}.jv-topbar-menu-nav{position:fixed;top:4rem;right:0;bottom:0;z-index:21;width:min(22rem,88vw);padding:var(--jv-spacing-lg);overflow-y:auto;border-left:1px solid var(--jv-color-border);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg);transform:translate(100%);visibility:hidden;transition:transform .24s ease,visibility .24s}.jv-topbar-menu.is-mobile-open .jv-topbar-menu-nav{transform:translate(0);visibility:visible}.jv-topbar-menu-list{display:grid;align-items:stretch}.jv-topbar-menu-link{width:100%;justify-content:flex-start;padding-inline:var(--jv-spacing-md)}.jv-topbar-menu-dropdown{position:static;min-width:0;margin-left:var(--jv-spacing-md);box-shadow:none}.jv-topbar-menu-actions{margin-left:auto}}\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"] }, { kind: "directive", type: JvTooltipDirective, selector: "[jvTooltip]", inputs: ["jvTooltip", "jvTooltipPosition"] }] });
23845
+ }
23846
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTopbarMenuComponent, decorators: [{
23847
+ type: Component,
23848
+ args: [{ selector: 'jv-topbar-menu', standalone: true, imports: [JvIconComponent, RouterLink, JvTooltipDirective], template: `
23849
+ @if (mobileOpen()) {
23850
+ <div class="jv-topbar-menu-backdrop" tabindex="-1" (click)="mobileClose.emit()" (keydown.escape)="mobileClose.emit()"></div>
23851
+ }
23852
+
23853
+ <header class="jv-topbar-menu" [class.is-mobile-open]="mobileOpen()">
23854
+ <div class="jv-topbar-menu-brand">
23855
+ <div class="brand-mark">
23856
+ <jv-icon [name]="brandIcon()" [size]="18" />
23857
+ </div>
23858
+ <div class="brand-copy">
23859
+ <strong>{{ title() }}</strong>
23860
+ @if (subtitle()) {
23861
+ <span>{{ subtitle() }}</span>
23862
+ }
23863
+ </div>
23864
+ </div>
23865
+
23866
+ <button type="button" class="mobile-menu-button" aria-label="Toggle navigation menu" (click)="menuClick.emit()">
23867
+ <jv-icon name="menu" [size]="18" />
23868
+ </button>
23869
+
23870
+ <nav class="jv-topbar-menu-nav" [attr.aria-label]="ariaLabel()">
23871
+ <ul class="jv-topbar-menu-list">
23872
+ @for (item of normalizedItems(); track item.id) {
23873
+ <li class="jv-topbar-menu-item">
23874
+ @if (item.children.length > 0) {
23875
+ <button
23876
+ type="button"
23877
+ class="jv-topbar-menu-link"
23878
+ [class.is-active]="item.active"
23879
+ [attr.aria-expanded]="isExpanded(item.id) ? 'true' : 'false'"
23880
+ [attr.aria-controls]="'jv-topbar-menu-section-' + item.id"
23881
+ (click)="toggleSection(item.id)"
23882
+ >
23883
+ @if (item.icon) {
23884
+ <jv-icon [name]="item.icon" [size]="17" />
23885
+ }
23886
+ <span>{{ item.label }}</span>
23887
+ <jv-icon [name]="isExpanded(item.id) ? 'chevron-up' : 'chevron-down'" [size]="15" />
23888
+ </button>
23889
+
23890
+ @if (isExpanded(item.id)) {
23891
+ <div class="jv-topbar-menu-dropdown" [id]="'jv-topbar-menu-section-' + item.id">
23892
+ @for (child of item.children; track child.id) {
23893
+ @if (isHashLink(child.href)) {
23894
+ <button type="button" class="jv-topbar-menu-child" [class.is-active]="child.active" (click)="navigateTo(child.href, item.id)">
23895
+ {{ child.label }}
23896
+ </button>
23897
+ } @else {
23898
+ <a class="jv-topbar-menu-child" [class.is-active]="child.active" [routerLink]="child.href" [attr.aria-current]="child.active ? 'page' : null" (click)="closeSection(item.id); mobileClose.emit()">
23899
+ {{ child.label }}
23900
+ </a>
23901
+ }
23902
+ }
23903
+ </div>
23904
+ }
23905
+ } @else {
23906
+ @if (isHashLink(item.href)) {
23907
+ <button type="button" class="jv-topbar-menu-link" [class.is-active]="item.active" (click)="navigateTo(item.href)">
23908
+ @if (item.icon) {
23909
+ <jv-icon [name]="item.icon" [size]="17" />
23910
+ }
23911
+ <span>{{ item.label }}</span>
23912
+ </button>
23913
+ } @else {
23914
+ <a class="jv-topbar-menu-link" [class.is-active]="item.active" [routerLink]="item.href" [attr.aria-current]="item.active ? 'page' : null" (click)="mobileClose.emit()">
23915
+ @if (item.icon) {
23916
+ <jv-icon [name]="item.icon" [size]="17" />
23917
+ }
23918
+ <span>{{ item.label }}</span>
23919
+ </a>
23920
+ }
23921
+ }
23922
+ </li>
23923
+ }
23924
+ </ul>
23925
+ </nav>
23926
+
23927
+ <div class="jv-topbar-menu-actions">
23928
+ @if (showThemeSelector()) {
23929
+ <div class="theme-group" aria-label="Theme selector">
23930
+ @for (option of themeOptions(); track option.value) {
23931
+ <button type="button" class="theme-pill" [class.is-active]="currentTheme() === option.value" (click)="themeChange.emit(option.value)">
23932
+ @if (option.icon) {
23933
+ <jv-icon [name]="option.icon" [size]="16" />
23934
+ }
23935
+ <span>{{ option.label }}</span>
23936
+ </button>
23937
+ }
23938
+ </div>
23939
+ }
23940
+
23941
+ @for (action of actions(); track action.id) {
23942
+ <button type="button" class="icon-button" [attr.aria-label]="action.label" [attr.title]="action.label" [jvTooltip]="action.label" (click)="actionClick.emit(action.id)">
23943
+ <jv-icon [name]="action.icon" [size]="18" />
23944
+ <span class="action-label">{{ action.label }}</span>
23945
+ </button>
23946
+ }
23947
+ </div>
23948
+ </header>
23949
+ `, styles: [":host{display:block;position:relative;z-index:20}.jv-topbar-menu,.jv-topbar-menu-brand,.jv-topbar-menu-list,.jv-topbar-menu-actions,.theme-group{display:flex;align-items:center}.jv-topbar-menu{gap:var(--jv-spacing-lg);min-height:4.5rem;padding:var(--jv-spacing-md) var(--jv-spacing-xl);min-width:0;border-bottom:1px solid var(--jv-color-border);background:var(--jv-color-surface)}.jv-topbar-menu-brand{gap:var(--jv-spacing-sm);flex:0 0 auto}.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)}.brand-copy{display:grid;gap:.1rem}.brand-copy span{color:var(--jv-color-foreground-muted);font-size:.8125rem}.jv-topbar-menu-nav{min-width:0;flex:1 1 0}.jv-topbar-menu-list{gap:var(--jv-spacing-xs);margin:0;padding:0;list-style:none;min-width:0;flex-wrap:wrap}.jv-topbar-menu-item{position:relative;min-width:0;flex:0 1 auto}.jv-topbar-menu-link,.jv-topbar-menu-child,.icon-button,.theme-pill,.mobile-menu-button{border:1px solid transparent;background:transparent;color:var(--jv-color-foreground-muted);cursor:pointer;font:inherit;text-decoration:none}.jv-topbar-menu-link{display:inline-flex;align-items:center;gap:var(--jv-spacing-xs);min-height:var(--jv-density-control-height);min-width:0;width:100%;max-width:15rem;padding:0 var(--jv-spacing-sm);border-radius:var(--jv-radius-md);white-space:nowrap}.jv-topbar-menu-link span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.jv-topbar-menu-link:hover,.jv-topbar-menu-child:hover,.jv-topbar-menu-link.is-active,.jv-topbar-menu-child.is-active{background:color-mix(in srgb,var(--jv-color-primary) 12%,var(--jv-color-surface));color:var(--jv-color-foreground)}.jv-topbar-menu-dropdown{position:absolute;top:calc(100% + var(--jv-spacing-xs));left:0;display:grid;min-width:14rem;max-height:min(70vh,32rem);gap:var(--jv-spacing-xs);padding:var(--jv-spacing-sm);overflow-y:auto;border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg);z-index:30}.jv-topbar-menu-child{display:block;padding:var(--jv-spacing-sm) var(--jv-spacing-md);border-radius:var(--jv-radius-sm);text-align:left;white-space:nowrap}.jv-topbar-menu-actions{justify-content:flex-end;gap:var(--jv-spacing-sm);flex:0 0 auto}.theme-group{gap:var(--jv-spacing-xs)}.icon-button,.mobile-menu-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--jv-spacing-xs);min-width:var(--jv-density-control-height);height:var(--jv-density-control-height);padding:0 var(--jv-spacing-sm);border-color:var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground)}.icon-button{cursor:pointer}.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-sm);border-color:var(--jv-color-border);border-radius:999px;background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);font-size:.8125rem}.theme-pill.is-active{border-color:var(--jv-color-primary);background:color-mix(in srgb,var(--jv-color-primary) 14%,var(--jv-color-surface))}.mobile-menu-button,.jv-topbar-menu-backdrop{display:none}@media(max-width:1100px){.jv-topbar-menu{gap:var(--jv-spacing-md);padding-inline:var(--jv-spacing-lg)}.jv-topbar-menu-link{max-width:12rem;padding-inline:var(--jv-spacing-xs)}}@media(max-width:1400px){.jv-topbar-menu-actions .theme-group{display:none}}@media(max-width:900px){.jv-topbar-menu-backdrop{display:block;position:fixed;inset:0;z-index:19;background:#0006}.jv-topbar-menu{position:relative;justify-content:space-between;min-height:4rem;padding:var(--jv-spacing-sm) var(--jv-spacing-md)}.mobile-menu-button{display:inline-grid;order:3}.jv-topbar-menu-nav{position:fixed;top:4rem;right:0;bottom:0;z-index:21;width:min(22rem,88vw);padding:var(--jv-spacing-lg);overflow-y:auto;border-left:1px solid var(--jv-color-border);background:var(--jv-color-surface);box-shadow:var(--jv-shadow-lg);transform:translate(100%);visibility:hidden;transition:transform .24s ease,visibility .24s}.jv-topbar-menu.is-mobile-open .jv-topbar-menu-nav{transform:translate(0);visibility:visible}.jv-topbar-menu-list{display:grid;align-items:stretch}.jv-topbar-menu-link{width:100%;justify-content:flex-start;padding-inline:var(--jv-spacing-md)}.jv-topbar-menu-dropdown{position:static;min-width:0;margin-left:var(--jv-spacing-md);box-shadow:none}.jv-topbar-menu-actions{margin-left:auto}}\n"] }]
23950
+ }], 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 }] }], 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 }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", 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"] }], themeChange: [{ type: i0.Output, args: ["themeChange"] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }] } });
23951
+
23458
23952
  class JvDashboardShellComponent {
23459
23953
  title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
23954
+ navigationMode = input('sidebar', ...(ngDevMode ? [{ debugName: "navigationMode" }] : /* istanbul ignore next */ []));
23460
23955
  sidebarSubtitle = input('', ...(ngDevMode ? [{ debugName: "sidebarSubtitle" }] : /* istanbul ignore next */ []));
23461
23956
  brandIcon = input('monitor', ...(ngDevMode ? [{ debugName: "brandIcon" }] : /* istanbul ignore next */ []));
23462
23957
  navItems = input([], ...(ngDevMode ? [{ debugName: "navItems" }] : /* istanbul ignore next */ []));
@@ -23474,90 +23969,130 @@ class JvDashboardShellComponent {
23474
23969
  themeChange = output();
23475
23970
  topbarActionClick = output();
23476
23971
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDashboardShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
23477
- 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: `
23478
- <div class="jv-dashboard-shell" [class.is-sidebar-collapsed]="sidebarCollapsed()">
23479
- <jv-sidebar
23480
- [title]="title()"
23481
- [subtitle]="sidebarSubtitle()"
23482
- [brandIcon]="brandIcon()"
23483
- [items]="navItems()"
23484
- [collapsed]="sidebarCollapsed()"
23485
- [mobileOpen]="mobileSidebarOpen()"
23486
- [currentTheme]="currentTheme()"
23487
- [themeOptions]="themeOptions()"
23488
- [topbarActions]="topbarActions()"
23489
- [showThemeSelector]="showThemeSelector()"
23490
- (collapseToggle)="sidebarCollapseToggle.emit()"
23491
- (mobileClose)="mobileClose.emit()"
23492
- (themeChange)="themeChange.emit($event)"
23493
- (actionClick)="topbarActionClick.emit($event)"
23494
- />
23495
-
23496
- <div class="jv-dashboard-main">
23497
- <jv-topbar
23498
- [title]="topbarTitle()"
23499
- [subtitle]="topbarSubtitle()"
23972
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.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 }, navigationMode: { classPropertyName: "navigationMode", publicName: "navigationMode", isSignal: true, isRequired: false, 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: `
23973
+ <div class="jv-dashboard-shell" [class.is-sidebar-collapsed]="sidebarCollapsed()" [class.is-topbar-navigation]="navigationMode() === 'topbar'">
23974
+ @if (navigationMode() === 'sidebar') {
23975
+ <jv-sidebar
23976
+ [title]="title()"
23977
+ [subtitle]="sidebarSubtitle()"
23978
+ [brandIcon]="brandIcon()"
23979
+ [items]="navItems()"
23980
+ [collapsed]="sidebarCollapsed()"
23981
+ [mobileOpen]="mobileSidebarOpen()"
23500
23982
  [currentTheme]="currentTheme()"
23501
23983
  [themeOptions]="themeOptions()"
23502
- [actions]="topbarActions()"
23984
+ [topbarActions]="topbarActions()"
23503
23985
  [showThemeSelector]="showThemeSelector()"
23504
- (menuClick)="menuClick.emit()"
23986
+ (collapseToggle)="sidebarCollapseToggle.emit()"
23987
+ (mobileClose)="mobileClose.emit()"
23505
23988
  (themeChange)="themeChange.emit($event)"
23506
23989
  (actionClick)="topbarActionClick.emit($event)"
23507
- >
23508
- <ng-content select="[topbar-actions]" topbar-actions></ng-content>
23509
- </jv-topbar>
23990
+ />
23991
+ }
23992
+
23993
+ <div class="jv-dashboard-main">
23994
+ @if (navigationMode() === 'topbar') {
23995
+ <jv-topbar-menu
23996
+ [title]="title()"
23997
+ [subtitle]="sidebarSubtitle()"
23998
+ [brandIcon]="brandIcon()"
23999
+ [items]="navItems()"
24000
+ [mobileOpen]="mobileSidebarOpen()"
24001
+ [currentTheme]="currentTheme()"
24002
+ [themeOptions]="themeOptions()"
24003
+ [actions]="topbarActions()"
24004
+ [showThemeSelector]="showThemeSelector()"
24005
+ (menuClick)="menuClick.emit()"
24006
+ (mobileClose)="mobileClose.emit()"
24007
+ (themeChange)="themeChange.emit($event)"
24008
+ (actionClick)="topbarActionClick.emit($event)"
24009
+ />
24010
+ } @else {
24011
+ <jv-topbar
24012
+ [title]="topbarTitle()"
24013
+ [subtitle]="topbarSubtitle()"
24014
+ [currentTheme]="currentTheme()"
24015
+ [themeOptions]="themeOptions()"
24016
+ [actions]="topbarActions()"
24017
+ [showThemeSelector]="showThemeSelector()"
24018
+ (menuClick)="menuClick.emit()"
24019
+ (themeChange)="themeChange.emit($event)"
24020
+ (actionClick)="topbarActionClick.emit($event)"
24021
+ >
24022
+ <ng-content select="[topbar-actions]" topbar-actions></ng-content>
24023
+ </jv-topbar>
24024
+ }
23510
24025
 
23511
24026
  <main class="jv-dashboard-content">
23512
24027
  <ng-content></ng-content>
23513
24028
  </main>
23514
24029
  </div>
23515
24030
  </div>
23516
- `, 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"] }] });
24031
+ `, 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-shell.is-topbar-navigation{grid-template-columns:1fr}.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"] }, { kind: "component", type: JvTopbarMenuComponent, selector: "jv-topbar-menu", inputs: ["title", "subtitle", "brandIcon", "items", "mobileOpen", "ariaLabel", "currentTheme", "themeOptions", "actions", "showThemeSelector"], outputs: ["menuClick", "mobileClose", "themeChange", "actionClick"] }] });
23517
24032
  }
23518
24033
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDashboardShellComponent, decorators: [{
23519
24034
  type: Component,
23520
- args: [{ selector: 'jv-dashboard-shell', standalone: true, imports: [JvSidebarComponent, JvTopbarComponent], template: `
23521
- <div class="jv-dashboard-shell" [class.is-sidebar-collapsed]="sidebarCollapsed()">
23522
- <jv-sidebar
23523
- [title]="title()"
23524
- [subtitle]="sidebarSubtitle()"
23525
- [brandIcon]="brandIcon()"
23526
- [items]="navItems()"
23527
- [collapsed]="sidebarCollapsed()"
23528
- [mobileOpen]="mobileSidebarOpen()"
23529
- [currentTheme]="currentTheme()"
23530
- [themeOptions]="themeOptions()"
23531
- [topbarActions]="topbarActions()"
23532
- [showThemeSelector]="showThemeSelector()"
23533
- (collapseToggle)="sidebarCollapseToggle.emit()"
23534
- (mobileClose)="mobileClose.emit()"
23535
- (themeChange)="themeChange.emit($event)"
23536
- (actionClick)="topbarActionClick.emit($event)"
23537
- />
23538
-
23539
- <div class="jv-dashboard-main">
23540
- <jv-topbar
23541
- [title]="topbarTitle()"
23542
- [subtitle]="topbarSubtitle()"
24035
+ args: [{ selector: 'jv-dashboard-shell', standalone: true, imports: [JvSidebarComponent, JvTopbarComponent, JvTopbarMenuComponent], template: `
24036
+ <div class="jv-dashboard-shell" [class.is-sidebar-collapsed]="sidebarCollapsed()" [class.is-topbar-navigation]="navigationMode() === 'topbar'">
24037
+ @if (navigationMode() === 'sidebar') {
24038
+ <jv-sidebar
24039
+ [title]="title()"
24040
+ [subtitle]="sidebarSubtitle()"
24041
+ [brandIcon]="brandIcon()"
24042
+ [items]="navItems()"
24043
+ [collapsed]="sidebarCollapsed()"
24044
+ [mobileOpen]="mobileSidebarOpen()"
23543
24045
  [currentTheme]="currentTheme()"
23544
24046
  [themeOptions]="themeOptions()"
23545
- [actions]="topbarActions()"
24047
+ [topbarActions]="topbarActions()"
23546
24048
  [showThemeSelector]="showThemeSelector()"
23547
- (menuClick)="menuClick.emit()"
24049
+ (collapseToggle)="sidebarCollapseToggle.emit()"
24050
+ (mobileClose)="mobileClose.emit()"
23548
24051
  (themeChange)="themeChange.emit($event)"
23549
24052
  (actionClick)="topbarActionClick.emit($event)"
23550
- >
23551
- <ng-content select="[topbar-actions]" topbar-actions></ng-content>
23552
- </jv-topbar>
24053
+ />
24054
+ }
24055
+
24056
+ <div class="jv-dashboard-main">
24057
+ @if (navigationMode() === 'topbar') {
24058
+ <jv-topbar-menu
24059
+ [title]="title()"
24060
+ [subtitle]="sidebarSubtitle()"
24061
+ [brandIcon]="brandIcon()"
24062
+ [items]="navItems()"
24063
+ [mobileOpen]="mobileSidebarOpen()"
24064
+ [currentTheme]="currentTheme()"
24065
+ [themeOptions]="themeOptions()"
24066
+ [actions]="topbarActions()"
24067
+ [showThemeSelector]="showThemeSelector()"
24068
+ (menuClick)="menuClick.emit()"
24069
+ (mobileClose)="mobileClose.emit()"
24070
+ (themeChange)="themeChange.emit($event)"
24071
+ (actionClick)="topbarActionClick.emit($event)"
24072
+ />
24073
+ } @else {
24074
+ <jv-topbar
24075
+ [title]="topbarTitle()"
24076
+ [subtitle]="topbarSubtitle()"
24077
+ [currentTheme]="currentTheme()"
24078
+ [themeOptions]="themeOptions()"
24079
+ [actions]="topbarActions()"
24080
+ [showThemeSelector]="showThemeSelector()"
24081
+ (menuClick)="menuClick.emit()"
24082
+ (themeChange)="themeChange.emit($event)"
24083
+ (actionClick)="topbarActionClick.emit($event)"
24084
+ >
24085
+ <ng-content select="[topbar-actions]" topbar-actions></ng-content>
24086
+ </jv-topbar>
24087
+ }
23553
24088
 
23554
24089
  <main class="jv-dashboard-content">
23555
24090
  <ng-content></ng-content>
23556
24091
  </main>
23557
24092
  </div>
23558
24093
  </div>
23559
- `, 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"] }]
23560
- }], 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"] }] } });
24094
+ `, 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-shell.is-topbar-navigation{grid-template-columns:1fr}.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"] }]
24095
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], navigationMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "navigationMode", required: false }] }], 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"] }] } });
23561
24096
 
23562
24097
  class JvPageComponent {
23563
24098
  eyebrow = input('', ...(ngDevMode ? [{ debugName: "eyebrow" }] : /* istanbul ignore next */ []));
@@ -24093,193 +24628,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
24093
24628
  args: ['window:resize']
24094
24629
  }] } });
24095
24630
 
24096
- let tooltipSequence = 0;
24097
- class JvTooltipDirective {
24098
- document = inject(DOCUMENT);
24099
- renderer = inject(Renderer2);
24100
- hostRef = inject((ElementRef));
24101
- tooltipElement = null;
24102
- showTimeout = null;
24103
- hideTimeout = null;
24104
- tooltipId = `jv-tooltip-${++tooltipSequence}`;
24105
- originalDescribedBy = null;
24106
- text = input.required({ ...(ngDevMode ? { debugName: "text" } : /* istanbul ignore next */ {}), alias: 'jvTooltip' });
24107
- position = input('top', { ...(ngDevMode ? { debugName: "position" } : /* istanbul ignore next */ {}), alias: 'jvTooltipPosition' });
24108
- show() {
24109
- if (!this.text().trim()) {
24110
- return;
24111
- }
24112
- this.clearHideTimeout();
24113
- if (this.tooltipElement) {
24114
- return;
24115
- }
24116
- this.showTimeout = setTimeout(() => {
24117
- this.renderTooltip();
24118
- this.positionTooltip();
24119
- this.attachTooltipDescription();
24120
- }, 120);
24121
- }
24122
- hide() {
24123
- this.clearShowTimeout();
24124
- if (!this.tooltipElement) {
24125
- return;
24126
- }
24127
- this.hideTimeout = setTimeout(() => this.destroyTooltip(), 80);
24128
- }
24129
- handleDocumentKeydown(event) {
24130
- if (event.key === 'Escape') {
24131
- this.destroyTooltip();
24132
- }
24133
- }
24134
- handleViewportChange() {
24135
- if (this.tooltipElement) {
24136
- this.positionTooltip();
24137
- }
24138
- }
24139
- ngOnDestroy() {
24140
- this.clearShowTimeout();
24141
- this.clearHideTimeout();
24142
- this.destroyTooltip();
24143
- }
24144
- renderTooltip() {
24145
- if (this.tooltipElement) {
24146
- return;
24147
- }
24148
- const tooltip = this.renderer.createElement('div');
24149
- tooltip.textContent = this.text();
24150
- this.renderer.setAttribute(tooltip, 'id', this.tooltipId);
24151
- this.renderer.setAttribute(tooltip, 'role', 'tooltip');
24152
- this.renderer.setStyle(tooltip, 'position', 'fixed');
24153
- this.renderer.setStyle(tooltip, 'z-index', '1400');
24154
- this.renderer.setStyle(tooltip, 'max-width', '18rem');
24155
- this.renderer.setStyle(tooltip, 'padding', '0.5rem 0.75rem');
24156
- this.renderer.setStyle(tooltip, 'border-radius', '0.5rem');
24157
- this.renderer.setStyle(tooltip, 'background', 'rgb(15 23 42)');
24158
- this.renderer.setStyle(tooltip, 'color', '#ffffff');
24159
- this.renderer.setStyle(tooltip, 'font-size', '0.8125rem');
24160
- this.renderer.setStyle(tooltip, 'line-height', '1.4');
24161
- this.renderer.setStyle(tooltip, 'box-shadow', 'var(--jv-shadow-md)');
24162
- this.renderer.setStyle(tooltip, 'pointer-events', 'none');
24163
- this.renderer.setStyle(tooltip, 'opacity', '0');
24164
- this.renderer.setStyle(tooltip, 'transition', 'opacity 120ms ease');
24165
- this.renderer.appendChild(this.document.body, tooltip);
24166
- this.tooltipElement = tooltip;
24167
- requestAnimationFrame(() => {
24168
- if (this.tooltipElement) {
24169
- this.renderer.setStyle(this.tooltipElement, 'opacity', '1');
24170
- }
24171
- });
24172
- }
24173
- positionTooltip() {
24174
- const tooltip = this.tooltipElement;
24175
- if (!tooltip) {
24176
- return;
24177
- }
24178
- const hostRect = this.anchorElement().getBoundingClientRect();
24179
- const tooltipRect = tooltip.getBoundingClientRect();
24180
- const gap = 8;
24181
- let top = hostRect.top - tooltipRect.height - gap;
24182
- let left = hostRect.left + hostRect.width / 2 - tooltipRect.width / 2;
24183
- switch (this.position()) {
24184
- case 'right':
24185
- top = hostRect.top + hostRect.height / 2 - tooltipRect.height / 2;
24186
- left = hostRect.right + gap;
24187
- break;
24188
- case 'bottom':
24189
- top = hostRect.bottom + gap;
24190
- left = hostRect.left + hostRect.width / 2 - tooltipRect.width / 2;
24191
- break;
24192
- case 'left':
24193
- top = hostRect.top + hostRect.height / 2 - tooltipRect.height / 2;
24194
- left = hostRect.left - tooltipRect.width - gap;
24195
- break;
24196
- }
24197
- this.renderer.setStyle(tooltip, 'top', `${Math.round(top)}px`);
24198
- this.renderer.setStyle(tooltip, 'left', `${Math.round(left)}px`);
24199
- }
24200
- destroyTooltip() {
24201
- this.clearHideTimeout();
24202
- this.clearShowTimeout();
24203
- if (!this.tooltipElement) {
24204
- return;
24205
- }
24206
- this.restoreTooltipDescription();
24207
- this.renderer.removeChild(this.document.body, this.tooltipElement);
24208
- this.tooltipElement = null;
24209
- }
24210
- attachTooltipDescription() {
24211
- const anchor = this.anchorElement();
24212
- this.originalDescribedBy ??= anchor.getAttribute('aria-describedby');
24213
- const describedBy = [this.originalDescribedBy, this.tooltipId]
24214
- .filter((value) => !!value)
24215
- .join(' ')
24216
- .trim();
24217
- this.renderer.setAttribute(anchor, 'aria-describedby', describedBy);
24218
- }
24219
- restoreTooltipDescription() {
24220
- const anchor = this.anchorElement();
24221
- if (this.originalDescribedBy) {
24222
- this.renderer.setAttribute(anchor, 'aria-describedby', this.originalDescribedBy);
24223
- }
24224
- else {
24225
- this.renderer.removeAttribute(anchor, 'aria-describedby');
24226
- }
24227
- this.originalDescribedBy = null;
24228
- }
24229
- anchorElement() {
24230
- const host = this.hostRef.nativeElement;
24231
- if (isInteractiveElement(host)) {
24232
- return host;
24233
- }
24234
- return host.querySelector('button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])') ?? host;
24235
- }
24236
- clearShowTimeout() {
24237
- if (this.showTimeout) {
24238
- clearTimeout(this.showTimeout);
24239
- this.showTimeout = null;
24240
- }
24241
- }
24242
- clearHideTimeout() {
24243
- if (this.hideTimeout) {
24244
- clearTimeout(this.hideTimeout);
24245
- this.hideTimeout = null;
24246
- }
24247
- }
24248
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
24249
- 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 });
24250
- }
24251
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvTooltipDirective, decorators: [{
24252
- type: Directive,
24253
- args: [{
24254
- selector: '[jvTooltip]',
24255
- standalone: true,
24256
- }]
24257
- }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvTooltip", required: true }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "jvTooltipPosition", required: false }] }], show: [{
24258
- type: HostListener,
24259
- args: ['mouseenter']
24260
- }, {
24261
- type: HostListener,
24262
- args: ['focusin']
24263
- }], hide: [{
24264
- type: HostListener,
24265
- args: ['mouseleave']
24266
- }, {
24267
- type: HostListener,
24268
- args: ['blur']
24269
- }], handleDocumentKeydown: [{
24270
- type: HostListener,
24271
- args: ['document:keydown', ['$event']]
24272
- }], handleViewportChange: [{
24273
- type: HostListener,
24274
- args: ['window:scroll']
24275
- }, {
24276
- type: HostListener,
24277
- args: ['window:resize']
24278
- }] } });
24279
- function isInteractiveElement(element) {
24280
- return ['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA'].includes(element.tagName) || element.tabIndex >= 0;
24281
- }
24282
-
24283
24631
  const JV_GRID_DEFAULT_OPTIONS = {
24284
24632
  searchable: true,
24285
24633
  sortable: true,
@@ -25625,5 +25973,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
25625
25973
  * Generated bundle index. Do not edit.
25626
25974
  */
25627
25975
 
25628
- 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, JvDayPlannerComponent, JvDayPlannerDetailDirective, JvDayPlannerItemDirective, JvDialogComponent, JvDialogService, JvDividerComponent, JvFileInputComponent, JvForgotPasswordPageComponent, JvFormContainerComponent, JvFormatService, JvGridComponent, JvIconButtonComponent, JvIconComponent, JvImageGalleryComponent, JvImageSliderComponent, JvInputComponent, JvListComponent, JvListItemComponent, JvLoaderComponent, JvLoaderService, JvLoginPageComponent, JvModalSelectorComponent, JvMonthCalendarComponent, JvMonthCalendarDayDirective, JvPageComponent, JvPaginationComponent, JvRadioComponent, JvSectionComponent, JvSelectComponent, JvSidebarComponent, JvSwitchComponent, JvTabPanelDirective, JvTabsComponent, JvTextareaComponent, JvThemeService, JvToastComponent, JvToastService, JvTooltipDirective, JvTopbarComponent, JvTranslationService, JvWindowModalComponent, provideJvUi };
25976
+ 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, JvDayPlannerComponent, JvDayPlannerDetailDirective, JvDayPlannerItemDirective, JvDialogComponent, JvDialogService, JvDividerComponent, JvFileInputComponent, JvForgotPasswordPageComponent, JvFormContainerComponent, JvFormatService, JvGridComponent, JvIconButtonComponent, JvIconComponent, JvImageGalleryComponent, JvImageSliderComponent, JvInputComponent, JvListComponent, JvListItemComponent, JvLoaderComponent, JvLoaderService, JvLoginPageComponent, JvModalSelectorComponent, JvMonthCalendarComponent, JvMonthCalendarDayDirective, JvPageComponent, JvPaginationComponent, JvRadioComponent, JvSectionComponent, JvSelectComponent, JvSidebarComponent, JvSwitchComponent, JvTabPanelDirective, JvTabsComponent, JvTextareaComponent, JvThemeService, JvToastComponent, JvToastService, JvTooltipDirective, JvTopbarComponent, JvTopbarMenuComponent, JvTranslationService, JvWindowModalComponent, provideJvUi };
25629
25977
  //# sourceMappingURL=devjuliovilla-jv-ui.mjs.map