@devjuliovilla/jv-ui 1.9.0 → 1.10.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.
@@ -18717,9 +18717,135 @@ const JV_UI_DEFAULT_CONFIG = {
18717
18717
  density: 'normal',
18718
18718
  language: 'es-MX',
18719
18719
  locale: 'es',
18720
+ messagesLocale: 'es',
18721
+ formatLocale: 'es-MX',
18722
+ formats: {
18723
+ date: { day: '2-digit', month: '2-digit', year: 'numeric' },
18724
+ time: { hour: '2-digit', minute: '2-digit' },
18725
+ datetime: { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' },
18726
+ integer: { maximumFractionDigits: 0 },
18727
+ number: { minimumFractionDigits: 0, maximumFractionDigits: 2 },
18728
+ currency: { style: 'currency', currency: 'MXN', minimumFractionDigits: 2, maximumFractionDigits: 2 },
18729
+ percent: { style: 'percent', minimumFractionDigits: 0, maximumFractionDigits: 2 },
18730
+ },
18720
18731
  toastPosition: 'bottom-right',
18721
18732
  };
18722
18733
 
18734
+ class JvFormatService {
18735
+ config = inject(JvUiConfigService);
18736
+ formatLocale = signal(this.config.snapshot.formatLocale || this.config.snapshot.language || 'es-MX', ...(ngDevMode ? [{ debugName: "formatLocale" }] : /* istanbul ignore next */ []));
18737
+ formats = signal(this.resolveFormats(this.config.snapshot.formats), ...(ngDevMode ? [{ debugName: "formats" }] : /* istanbul ignore next */ []));
18738
+ firstDayOfWeek = computed(() => this.resolveFirstDayOfWeek(this.formatLocale()), ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : /* istanbul ignore next */ []));
18739
+ initialize() {
18740
+ this.formatLocale.set(this.config.snapshot.formatLocale || this.config.snapshot.language || 'es-MX');
18741
+ this.formats.set(this.resolveFormats(this.config.snapshot.formats));
18742
+ }
18743
+ setFormatLocale(locale) {
18744
+ this.formatLocale.set(locale);
18745
+ }
18746
+ setFormats(formats) {
18747
+ this.formats.update((current) => this.resolveFormats({ ...current, ...formats }));
18748
+ }
18749
+ format(kind, value, overrides) {
18750
+ if (value == null || value === '')
18751
+ return '';
18752
+ if (kind === 'date' || kind === 'time' || kind === 'datetime') {
18753
+ const date = this.toDate(value);
18754
+ if (!date)
18755
+ return String(value);
18756
+ return new Intl.DateTimeFormat(this.formatLocale(), this.resolveDateOptions(kind, overrides)).format(date);
18757
+ }
18758
+ const number = this.toNumber(value);
18759
+ if (number === null)
18760
+ return String(value);
18761
+ return new Intl.NumberFormat(this.formatLocale(), this.resolveNumberOptions(kind, overrides)).format(number);
18762
+ }
18763
+ toComparable(kind, value) {
18764
+ if (value == null || value === '')
18765
+ return null;
18766
+ if (kind === 'date' || kind === 'time' || kind === 'datetime') {
18767
+ const date = this.toDate(value);
18768
+ return date ? date.getTime() : String(value);
18769
+ }
18770
+ if (kind === 'integer' || kind === 'number' || kind === 'currency' || kind === 'percent') {
18771
+ const number = this.toNumber(value);
18772
+ return number ?? String(value);
18773
+ }
18774
+ return String(value);
18775
+ }
18776
+ toDate(value) {
18777
+ if (value instanceof Date) {
18778
+ return Number.isNaN(value.getTime()) ? null : value;
18779
+ }
18780
+ if (typeof value === 'number' || typeof value === 'string') {
18781
+ const date = new Date(value);
18782
+ return Number.isNaN(date.getTime()) ? null : date;
18783
+ }
18784
+ return null;
18785
+ }
18786
+ toNumber(value) {
18787
+ if (typeof value === 'number') {
18788
+ return Number.isFinite(value) ? value : null;
18789
+ }
18790
+ if (typeof value === 'string') {
18791
+ const normalized = value.trim().replace(/[%,$\s]/g, '').replace(/,/g, '');
18792
+ if (!normalized)
18793
+ return null;
18794
+ const parsed = Number(normalized);
18795
+ return Number.isFinite(parsed) ? parsed : null;
18796
+ }
18797
+ return null;
18798
+ }
18799
+ toSearchValue(kind, value) {
18800
+ if (value == null)
18801
+ return '';
18802
+ if (kind === 'date' || kind === 'time' || kind === 'datetime') {
18803
+ const date = this.toDate(value);
18804
+ if (!date)
18805
+ return String(value).toLowerCase();
18806
+ return date.toISOString().toLowerCase();
18807
+ }
18808
+ return String(value).toLowerCase();
18809
+ }
18810
+ resolveFormats(formats) {
18811
+ const defaults = this.config.snapshot.formats;
18812
+ return {
18813
+ date: { ...defaults.date, ...formats?.date },
18814
+ time: { ...defaults.time, ...formats?.time },
18815
+ datetime: { ...defaults.datetime, ...formats?.datetime },
18816
+ integer: { ...defaults.integer, ...formats?.integer },
18817
+ number: { ...defaults.number, ...formats?.number },
18818
+ currency: { ...defaults.currency, ...formats?.currency },
18819
+ percent: { ...defaults.percent, ...formats?.percent },
18820
+ };
18821
+ }
18822
+ resolveDateOptions(kind, overrides) {
18823
+ return { ...this.formats()[kind], ...overrides };
18824
+ }
18825
+ resolveNumberOptions(kind, overrides) {
18826
+ return { ...this.formats()[kind], ...overrides };
18827
+ }
18828
+ resolveFirstDayOfWeek(locale) {
18829
+ try {
18830
+ const intlLocale = new Intl.Locale(locale);
18831
+ const firstDay = intlLocale.weekInfo?.firstDay;
18832
+ if (typeof firstDay === 'number') {
18833
+ return firstDay % 7;
18834
+ }
18835
+ }
18836
+ catch {
18837
+ // Ignore unsupported Intl.Locale implementations.
18838
+ }
18839
+ return 0;
18840
+ }
18841
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvFormatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18842
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvFormatService, providedIn: 'root' });
18843
+ }
18844
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvFormatService, decorators: [{
18845
+ type: Injectable,
18846
+ args: [{ providedIn: 'root' }]
18847
+ }] });
18848
+
18723
18849
  class JvThemeService {
18724
18850
  document = inject(DOCUMENT);
18725
18851
  config = inject(JvUiConfigService);
@@ -18810,6 +18936,7 @@ function provideJvUi(config = {}) {
18810
18936
  useValue: () => {
18811
18937
  inject(JvThemeService).initialize();
18812
18938
  inject(JvTranslationService).initialize();
18939
+ inject(JvFormatService).initialize();
18813
18940
  },
18814
18941
  },
18815
18942
  ]);
@@ -18817,9 +18944,19 @@ function provideJvUi(config = {}) {
18817
18944
 
18818
18945
  class JvUiConfigService {
18819
18946
  defaults = inject(JV_UI_CONFIG_DEFAULTS);
18820
- configLayers = inject(JV_UI_CONFIG, { optional: true }) ?? [];
18947
+ configLayers = (inject(JV_UI_CONFIG, { optional: true }) ?? []);
18821
18948
  get snapshot() {
18822
- return this.configLayers.reduce((merged, layer) => ({ ...merged, ...layer }), { ...this.defaults });
18949
+ return this.configLayers.reduce((merged, layer) => ({
18950
+ ...merged,
18951
+ ...layer,
18952
+ formats: {
18953
+ ...merged.formats,
18954
+ ...(layer.formats ?? {}),
18955
+ },
18956
+ }), {
18957
+ ...this.defaults,
18958
+ formats: { ...this.defaults.formats },
18959
+ });
18823
18960
  }
18824
18961
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvUiConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18825
18962
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvUiConfigService, providedIn: 'root' });
@@ -18878,6 +19015,12 @@ const ES = {
18878
19015
  'modalSelector.results': '{count} resultados',
18879
19016
  'modalSelector.selected': '{label} seleccionado',
18880
19017
  'modalSelector.selectionCleared': 'Seleccion limpiada',
19018
+ 'fileInput.dropLabel': 'Arrastre archivos aqui o haga clic para seleccionarlos',
19019
+ 'fileInput.helperText': 'Compatible con seleccion multiple y arrastrar y soltar',
19020
+ 'fileInput.selectionSummary': '{count} de {max} archivos seleccionados',
19021
+ 'fileInput.invalidFiles': '{count} archivos no coinciden con el tipo permitido',
19022
+ 'fileInput.tooManyFiles': 'Solo se permiten hasta {max} archivos',
19023
+ 'fileInput.removeFile': 'Quitar archivo {name}',
18881
19024
  'dialog.confirmTitle': 'Confirmar',
18882
19025
  'dialog.confirm': 'Aceptar',
18883
19026
  'dialog.cancel': 'Cancelar',
@@ -18934,6 +19077,12 @@ const EN = {
18934
19077
  'modalSelector.results': '{count} results',
18935
19078
  'modalSelector.selected': '{label} selected',
18936
19079
  'modalSelector.selectionCleared': 'Selection cleared',
19080
+ 'fileInput.dropLabel': 'Drag files here or click to choose them',
19081
+ 'fileInput.helperText': 'Supports multiple selection and drag and drop',
19082
+ 'fileInput.selectionSummary': '{count} of {max} files selected',
19083
+ 'fileInput.invalidFiles': '{count} files do not match the allowed type',
19084
+ 'fileInput.tooManyFiles': 'Only up to {max} files are allowed',
19085
+ 'fileInput.removeFile': 'Remove file {name}',
18937
19086
  'dialog.confirmTitle': 'Confirm',
18938
19087
  'dialog.confirm': 'Confirm',
18939
19088
  'dialog.cancel': 'Cancel',
@@ -18949,14 +19098,17 @@ const JV_DEFAULT_LOCALE = 'es';
18949
19098
 
18950
19099
  class JvTranslationService {
18951
19100
  config = inject(JvUiConfigService);
18952
- locale = signal(this.config.snapshot.locale, ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
19101
+ locale = signal(this.resolveLocale(this.config.snapshot.messagesLocale ?? this.config.snapshot.locale), ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
18953
19102
  initialize() {
18954
- const locale = this.resolveLocale(this.config.snapshot.locale);
19103
+ const locale = this.resolveLocale(this.config.snapshot.messagesLocale ?? this.config.snapshot.locale);
18955
19104
  this.locale.set(locale);
18956
19105
  }
18957
19106
  setLocale(locale) {
18958
19107
  this.locale.set(locale);
18959
19108
  }
19109
+ setMessagesLocale(locale) {
19110
+ this.setLocale(locale);
19111
+ }
18960
19112
  translate(key, params) {
18961
19113
  const dict = JV_LOCALE_DICTIONARIES[this.locale()] ?? JV_LOCALE_DICTIONARIES[JV_DEFAULT_LOCALE];
18962
19114
  let value = dict[key];
@@ -18971,6 +19123,8 @@ class JvTranslationService {
18971
19123
  return value;
18972
19124
  }
18973
19125
  resolveLocale(locale) {
19126
+ if (!locale)
19127
+ return JV_DEFAULT_LOCALE;
18974
19128
  if (locale === 'es' || locale === 'en')
18975
19129
  return locale;
18976
19130
  return locale.startsWith('es') ? 'es' : 'en';
@@ -19749,6 +19903,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
19749
19903
  let datePickerIdSequence = 0;
19750
19904
  class JvDatePickerComponent {
19751
19905
  translation = inject(JvTranslationService);
19906
+ formatService = inject(JvFormatService);
19752
19907
  announcementService = inject(JvAnnouncementService);
19753
19908
  instanceId = ++datePickerIdSequence;
19754
19909
  calendarRootRef = viewChild('calendarRoot', ...(ngDevMode ? [{ debugName: "calendarRootRef" }] : /* istanbul ignore next */ []));
@@ -19781,14 +19936,10 @@ class JvDatePickerComponent {
19781
19936
  if (!value) {
19782
19937
  return this.placeholder() || this.translation.translate('datePicker.placeholder');
19783
19938
  }
19784
- return new Intl.DateTimeFormat(this.translation.locale(), {
19785
- day: '2-digit',
19786
- month: '2-digit',
19787
- year: 'numeric',
19788
- }).format(value);
19939
+ return this.formatService.format('date', value);
19789
19940
  }, ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
19790
19941
  monthOptions = computed(() => {
19791
- const formatter = new Intl.DateTimeFormat(this.translation.locale(), { month: 'long' });
19942
+ const formatter = new Intl.DateTimeFormat(this.formatService.formatLocale(), { month: 'long' });
19792
19943
  return Array.from({ length: 12 }, (_, month) => ({
19793
19944
  label: capitalize(formatter.format(new Date(2026, month, 1))),
19794
19945
  value: String(month),
@@ -19802,7 +19953,7 @@ class JvDatePickerComponent {
19802
19953
  }, ...(ngDevMode ? [{ debugName: "yearOptions" }] : /* istanbul ignore next */ []));
19803
19954
  weekdayLabels = computed(() => {
19804
19955
  const startDay = this.firstDayOfWeek();
19805
- const formatter = new Intl.DateTimeFormat(this.translation.locale(), { weekday: 'short' });
19956
+ const formatter = new Intl.DateTimeFormat(this.formatService.formatLocale(), { weekday: 'short' });
19806
19957
  return Array.from({ length: 7 }, (_, index) => capitalize(formatter.format(addDays(new Date(2026, 5, 7), startDay + index))));
19807
19958
  }, ...(ngDevMode ? [{ debugName: "weekdayLabels" }] : /* istanbul ignore next */ []));
19808
19959
  calendarCells = computed(() => {
@@ -19944,10 +20095,10 @@ class JvDatePickerComponent {
19944
20095
  root?.querySelector(`[data-jv-date="${this.isoDate(date)}"]`)?.focus();
19945
20096
  }
19946
20097
  firstDayOfWeek() {
19947
- return this.translation.locale() === 'es' ? 1 : 0;
20098
+ return this.formatService.firstDayOfWeek();
19948
20099
  }
19949
20100
  displayDateForAnnouncement(date) {
19950
- return new Intl.DateTimeFormat(this.translation.locale(), {
20101
+ return new Intl.DateTimeFormat(this.formatService.formatLocale(), {
19951
20102
  weekday: 'long',
19952
20103
  day: 'numeric',
19953
20104
  month: 'long',
@@ -19961,83 +20112,83 @@ class JvDatePickerComponent {
19961
20112
  useExisting: forwardRef(() => JvDatePickerComponent),
19962
20113
  multi: true,
19963
20114
  },
19964
- ], viewQueries: [{ propertyName: "calendarRootRef", first: true, predicate: ["calendarRoot"], descendants: true, isSignal: true }, { propertyName: "triggerButtonRef", first: true, predicate: ["triggerButton"], descendants: true, isSignal: true }], ngImport: i0, template: `
19965
- <jv-form-container
19966
- [label]="label()"
19967
- [inputId]="inputId()"
19968
- [required]="required()"
19969
- [invalid]="invalid()"
19970
- [hint]="hint()"
19971
- [error]="error()"
19972
- >
19973
- <button
19974
- #triggerButton
19975
- type="button"
19976
- class="jv-date-picker-trigger"
19977
- [class.is-invalid]="invalid()"
19978
- [id]="inputId()"
19979
- [disabled]="disabled"
19980
- [attr.aria-haspopup]="'dialog'"
19981
- [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
19982
- [attr.aria-controls]="dialogId"
19983
- [attr.aria-invalid]="invalid() ? 'true' : null"
19984
- [attr.aria-describedby]="describedBy() || null"
19985
- [attr.aria-label]="triggerAriaLabel()"
19986
- [attr.title]="displayValue()"
19987
- (click)="openDialog()"
19988
- (blur)="handleBlur()"
19989
- >
19990
- <span [class.jv-date-picker-placeholder]="!currentValue()">{{ displayValue() }}</span>
19991
- <jv-icon name="calendar-days" [size]="18" />
19992
- </button>
19993
- </jv-form-container>
19994
-
19995
- <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
19996
- <div #calendarRoot [id]="dialogId" class="jv-date-picker-dialog">
19997
- <div class="jv-date-picker-toolbar">
19998
- <jv-icon-button icon="chevron-left" variant="ghost" [ariaLabel]="translation.translate('datePicker.prevMonth')" (click)="shiftMonth(-1)" />
19999
-
20000
- <div class="jv-date-picker-selects">
20001
- <jv-select [options]="monthOptions()" [modelValue]="visibleMonth().getMonth().toString()" (selectionChange)="handleMonthChange($event)" />
20002
- <jv-select [options]="yearOptions()" [modelValue]="visibleMonth().getFullYear().toString()" (selectionChange)="handleYearChange($event)" />
20003
- </div>
20004
-
20005
- <jv-icon-button icon="chevron-right" variant="ghost" [ariaLabel]="translation.translate('datePicker.nextMonth')" (click)="shiftMonth(1)" />
20006
- </div>
20007
-
20008
- <div class="jv-date-picker-weekdays" aria-hidden="true">
20009
- @for (weekday of weekdayLabels(); track weekday) {
20010
- <span>{{ weekday }}</span>
20011
- }
20012
- </div>
20013
-
20014
- <div class="jv-date-picker-grid" role="grid" [attr.aria-label]="resolvedDialogTitle()">
20015
- @for (cell of calendarCells(); track isoDate(cell.date)) {
20016
- <button
20017
- type="button"
20018
- class="jv-date-picker-day"
20019
- [class.jv-date-picker-day--outside]="!cell.currentMonth"
20020
- [class.jv-date-picker-day--today]="cell.isToday"
20021
- [class.jv-date-picker-day--selected]="isSelected(cell.date)"
20022
- [attr.data-jv-date]="isoDate(cell.date)"
20023
- [attr.tabindex]="isFocusedDate(cell.date) ? 0 : -1"
20024
- [attr.aria-selected]="isSelected(cell.date) ? 'true' : 'false'"
20025
- (click)="selectDate(cell.date)"
20026
- (keydown)="handleDayKeydown($event, cell.date)"
20027
- >
20028
- {{ cell.date.getDate() }}
20029
- </button>
20030
- }
20031
- </div>
20032
- </div>
20033
-
20034
- <div dialog-actions class="jv-date-picker-actions">
20035
- @if (currentValue()) {
20036
- <jv-button variant="ghost" (click)="clearSelection()">{{ translation.translate('datePicker.clear') }}</jv-button>
20037
- }
20038
- <jv-button variant="outline" (click)="closeDialog()">{{ translation.translate('datePicker.close') }}</jv-button>
20039
- </div>
20040
- </jv-dialog>
20115
+ ], viewQueries: [{ propertyName: "calendarRootRef", first: true, predicate: ["calendarRoot"], descendants: true, isSignal: true }, { propertyName: "triggerButtonRef", first: true, predicate: ["triggerButton"], descendants: true, isSignal: true }], ngImport: i0, template: `
20116
+ <jv-form-container
20117
+ [label]="label()"
20118
+ [inputId]="inputId()"
20119
+ [required]="required()"
20120
+ [invalid]="invalid()"
20121
+ [hint]="hint()"
20122
+ [error]="error()"
20123
+ >
20124
+ <button
20125
+ #triggerButton
20126
+ type="button"
20127
+ class="jv-date-picker-trigger"
20128
+ [class.is-invalid]="invalid()"
20129
+ [id]="inputId()"
20130
+ [disabled]="disabled"
20131
+ [attr.aria-haspopup]="'dialog'"
20132
+ [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
20133
+ [attr.aria-controls]="dialogId"
20134
+ [attr.aria-invalid]="invalid() ? 'true' : null"
20135
+ [attr.aria-describedby]="describedBy() || null"
20136
+ [attr.aria-label]="triggerAriaLabel()"
20137
+ [attr.title]="displayValue()"
20138
+ (click)="openDialog()"
20139
+ (blur)="handleBlur()"
20140
+ >
20141
+ <span [class.jv-date-picker-placeholder]="!currentValue()">{{ displayValue() }}</span>
20142
+ <jv-icon name="calendar-days" [size]="18" />
20143
+ </button>
20144
+ </jv-form-container>
20145
+
20146
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
20147
+ <div #calendarRoot [id]="dialogId" class="jv-date-picker-dialog">
20148
+ <div class="jv-date-picker-toolbar">
20149
+ <jv-icon-button icon="chevron-left" variant="ghost" [ariaLabel]="translation.translate('datePicker.prevMonth')" (click)="shiftMonth(-1)" />
20150
+
20151
+ <div class="jv-date-picker-selects">
20152
+ <jv-select [options]="monthOptions()" [modelValue]="visibleMonth().getMonth().toString()" (selectionChange)="handleMonthChange($event)" />
20153
+ <jv-select [options]="yearOptions()" [modelValue]="visibleMonth().getFullYear().toString()" (selectionChange)="handleYearChange($event)" />
20154
+ </div>
20155
+
20156
+ <jv-icon-button icon="chevron-right" variant="ghost" [ariaLabel]="translation.translate('datePicker.nextMonth')" (click)="shiftMonth(1)" />
20157
+ </div>
20158
+
20159
+ <div class="jv-date-picker-weekdays" aria-hidden="true">
20160
+ @for (weekday of weekdayLabels(); track weekday) {
20161
+ <span>{{ weekday }}</span>
20162
+ }
20163
+ </div>
20164
+
20165
+ <div class="jv-date-picker-grid" role="grid" [attr.aria-label]="resolvedDialogTitle()">
20166
+ @for (cell of calendarCells(); track isoDate(cell.date)) {
20167
+ <button
20168
+ type="button"
20169
+ class="jv-date-picker-day"
20170
+ [class.jv-date-picker-day--outside]="!cell.currentMonth"
20171
+ [class.jv-date-picker-day--today]="cell.isToday"
20172
+ [class.jv-date-picker-day--selected]="isSelected(cell.date)"
20173
+ [attr.data-jv-date]="isoDate(cell.date)"
20174
+ [attr.tabindex]="isFocusedDate(cell.date) ? 0 : -1"
20175
+ [attr.aria-selected]="isSelected(cell.date) ? 'true' : 'false'"
20176
+ (click)="selectDate(cell.date)"
20177
+ (keydown)="handleDayKeydown($event, cell.date)"
20178
+ >
20179
+ {{ cell.date.getDate() }}
20180
+ </button>
20181
+ }
20182
+ </div>
20183
+ </div>
20184
+
20185
+ <div dialog-actions class="jv-date-picker-actions">
20186
+ @if (currentValue()) {
20187
+ <jv-button variant="ghost" (click)="clearSelection()">{{ translation.translate('datePicker.clear') }}</jv-button>
20188
+ }
20189
+ <jv-button variant="outline" (click)="closeDialog()">{{ translation.translate('datePicker.close') }}</jv-button>
20190
+ </div>
20191
+ </jv-dialog>
20041
20192
  `, isInline: true, styles: [".jv-date-picker-trigger{width:100%;min-height:var(--jv-density-control-height);display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-sm);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);text-align:left;font:inherit;cursor:pointer}.jv-date-picker-trigger.is-invalid{border-color:var(--jv-color-danger)}.jv-date-picker-trigger:disabled{opacity:.7;cursor:not-allowed}.jv-date-picker-trigger:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-date-picker-placeholder{color:var(--jv-color-foreground-muted)}.jv-date-picker-dialog,.jv-date-picker-actions{display:grid;gap:var(--jv-spacing-md)}.jv-date-picker-toolbar,.jv-date-picker-selects,.jv-date-picker-weekdays,.jv-date-picker-grid{display:grid;gap:var(--jv-spacing-sm)}.jv-date-picker-toolbar{grid-template-columns:auto 1fr auto;align-items:center}.jv-date-picker-selects{grid-template-columns:1fr 1fr}.jv-date-picker-weekdays,.jv-date-picker-grid{grid-template-columns:repeat(7,minmax(0,1fr))}.jv-date-picker-weekdays span{text-align:center;font-size:.8125rem;font-weight:600;color:var(--jv-color-foreground-muted)}.jv-date-picker-day{min-height:2.5rem;border:1px solid transparent;border-radius:var(--jv-radius-md);background:transparent;color:var(--jv-color-foreground);font:inherit;cursor:pointer;transition:background-color .16s ease,border-color .16s ease,color .16s ease}.jv-date-picker-day:hover,.jv-date-picker-day:focus-visible{background:var(--jv-color-surface-muted)}.jv-date-picker-day:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-date-picker-day--outside{color:var(--jv-color-foreground-muted)}.jv-date-picker-day--today{border-color:var(--jv-color-primary)}.jv-date-picker-day--selected{background:var(--jv-color-primary);color:#fff}\n"], dependencies: [{ kind: "component", type: JvButtonComponent, selector: "jv-button", inputs: ["type", "variant", "icon", "iconPosition", "loading", "disabled"] }, { kind: "component", type: JvDialogComponent, selector: "jv-dialog", inputs: ["open", "title"], outputs: ["closed"] }, { kind: "component", type: JvFormContainerComponent, selector: "jv-form-container", inputs: ["label", "hint", "error", "inputId", "hintId", "errorId", "required", "invalid"] }, { kind: "component", type: JvIconButtonComponent, selector: "jv-icon-button", inputs: ["icon", "ariaLabel", "variant", "disabled"] }, { kind: "component", type: JvIconComponent, selector: "jv-icon", inputs: ["name", "size", "strokeWidth", "decorative", "ariaLabel"] }, { kind: "component", type: JvSelectComponent, selector: "jv-select", inputs: ["options", "placeholder", "required", "invalid", "describedBy", "inputId", "modelValue"], outputs: ["selectionChange"] }] });
20042
20193
  }
20043
20194
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvDatePickerComponent, decorators: [{
@@ -20048,83 +20199,83 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
20048
20199
  useExisting: forwardRef(() => JvDatePickerComponent),
20049
20200
  multi: true,
20050
20201
  },
20051
- ], template: `
20052
- <jv-form-container
20053
- [label]="label()"
20054
- [inputId]="inputId()"
20055
- [required]="required()"
20056
- [invalid]="invalid()"
20057
- [hint]="hint()"
20058
- [error]="error()"
20059
- >
20060
- <button
20061
- #triggerButton
20062
- type="button"
20063
- class="jv-date-picker-trigger"
20064
- [class.is-invalid]="invalid()"
20065
- [id]="inputId()"
20066
- [disabled]="disabled"
20067
- [attr.aria-haspopup]="'dialog'"
20068
- [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
20069
- [attr.aria-controls]="dialogId"
20070
- [attr.aria-invalid]="invalid() ? 'true' : null"
20071
- [attr.aria-describedby]="describedBy() || null"
20072
- [attr.aria-label]="triggerAriaLabel()"
20073
- [attr.title]="displayValue()"
20074
- (click)="openDialog()"
20075
- (blur)="handleBlur()"
20076
- >
20077
- <span [class.jv-date-picker-placeholder]="!currentValue()">{{ displayValue() }}</span>
20078
- <jv-icon name="calendar-days" [size]="18" />
20079
- </button>
20080
- </jv-form-container>
20081
-
20082
- <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
20083
- <div #calendarRoot [id]="dialogId" class="jv-date-picker-dialog">
20084
- <div class="jv-date-picker-toolbar">
20085
- <jv-icon-button icon="chevron-left" variant="ghost" [ariaLabel]="translation.translate('datePicker.prevMonth')" (click)="shiftMonth(-1)" />
20086
-
20087
- <div class="jv-date-picker-selects">
20088
- <jv-select [options]="monthOptions()" [modelValue]="visibleMonth().getMonth().toString()" (selectionChange)="handleMonthChange($event)" />
20089
- <jv-select [options]="yearOptions()" [modelValue]="visibleMonth().getFullYear().toString()" (selectionChange)="handleYearChange($event)" />
20090
- </div>
20091
-
20092
- <jv-icon-button icon="chevron-right" variant="ghost" [ariaLabel]="translation.translate('datePicker.nextMonth')" (click)="shiftMonth(1)" />
20093
- </div>
20094
-
20095
- <div class="jv-date-picker-weekdays" aria-hidden="true">
20096
- @for (weekday of weekdayLabels(); track weekday) {
20097
- <span>{{ weekday }}</span>
20098
- }
20099
- </div>
20100
-
20101
- <div class="jv-date-picker-grid" role="grid" [attr.aria-label]="resolvedDialogTitle()">
20102
- @for (cell of calendarCells(); track isoDate(cell.date)) {
20103
- <button
20104
- type="button"
20105
- class="jv-date-picker-day"
20106
- [class.jv-date-picker-day--outside]="!cell.currentMonth"
20107
- [class.jv-date-picker-day--today]="cell.isToday"
20108
- [class.jv-date-picker-day--selected]="isSelected(cell.date)"
20109
- [attr.data-jv-date]="isoDate(cell.date)"
20110
- [attr.tabindex]="isFocusedDate(cell.date) ? 0 : -1"
20111
- [attr.aria-selected]="isSelected(cell.date) ? 'true' : 'false'"
20112
- (click)="selectDate(cell.date)"
20113
- (keydown)="handleDayKeydown($event, cell.date)"
20114
- >
20115
- {{ cell.date.getDate() }}
20116
- </button>
20117
- }
20118
- </div>
20119
- </div>
20120
-
20121
- <div dialog-actions class="jv-date-picker-actions">
20122
- @if (currentValue()) {
20123
- <jv-button variant="ghost" (click)="clearSelection()">{{ translation.translate('datePicker.clear') }}</jv-button>
20124
- }
20125
- <jv-button variant="outline" (click)="closeDialog()">{{ translation.translate('datePicker.close') }}</jv-button>
20126
- </div>
20127
- </jv-dialog>
20202
+ ], template: `
20203
+ <jv-form-container
20204
+ [label]="label()"
20205
+ [inputId]="inputId()"
20206
+ [required]="required()"
20207
+ [invalid]="invalid()"
20208
+ [hint]="hint()"
20209
+ [error]="error()"
20210
+ >
20211
+ <button
20212
+ #triggerButton
20213
+ type="button"
20214
+ class="jv-date-picker-trigger"
20215
+ [class.is-invalid]="invalid()"
20216
+ [id]="inputId()"
20217
+ [disabled]="disabled"
20218
+ [attr.aria-haspopup]="'dialog'"
20219
+ [attr.aria-expanded]="dialogOpen() ? 'true' : 'false'"
20220
+ [attr.aria-controls]="dialogId"
20221
+ [attr.aria-invalid]="invalid() ? 'true' : null"
20222
+ [attr.aria-describedby]="describedBy() || null"
20223
+ [attr.aria-label]="triggerAriaLabel()"
20224
+ [attr.title]="displayValue()"
20225
+ (click)="openDialog()"
20226
+ (blur)="handleBlur()"
20227
+ >
20228
+ <span [class.jv-date-picker-placeholder]="!currentValue()">{{ displayValue() }}</span>
20229
+ <jv-icon name="calendar-days" [size]="18" />
20230
+ </button>
20231
+ </jv-form-container>
20232
+
20233
+ <jv-dialog [open]="dialogOpen()" [title]="resolvedDialogTitle()" (closed)="closeDialog()">
20234
+ <div #calendarRoot [id]="dialogId" class="jv-date-picker-dialog">
20235
+ <div class="jv-date-picker-toolbar">
20236
+ <jv-icon-button icon="chevron-left" variant="ghost" [ariaLabel]="translation.translate('datePicker.prevMonth')" (click)="shiftMonth(-1)" />
20237
+
20238
+ <div class="jv-date-picker-selects">
20239
+ <jv-select [options]="monthOptions()" [modelValue]="visibleMonth().getMonth().toString()" (selectionChange)="handleMonthChange($event)" />
20240
+ <jv-select [options]="yearOptions()" [modelValue]="visibleMonth().getFullYear().toString()" (selectionChange)="handleYearChange($event)" />
20241
+ </div>
20242
+
20243
+ <jv-icon-button icon="chevron-right" variant="ghost" [ariaLabel]="translation.translate('datePicker.nextMonth')" (click)="shiftMonth(1)" />
20244
+ </div>
20245
+
20246
+ <div class="jv-date-picker-weekdays" aria-hidden="true">
20247
+ @for (weekday of weekdayLabels(); track weekday) {
20248
+ <span>{{ weekday }}</span>
20249
+ }
20250
+ </div>
20251
+
20252
+ <div class="jv-date-picker-grid" role="grid" [attr.aria-label]="resolvedDialogTitle()">
20253
+ @for (cell of calendarCells(); track isoDate(cell.date)) {
20254
+ <button
20255
+ type="button"
20256
+ class="jv-date-picker-day"
20257
+ [class.jv-date-picker-day--outside]="!cell.currentMonth"
20258
+ [class.jv-date-picker-day--today]="cell.isToday"
20259
+ [class.jv-date-picker-day--selected]="isSelected(cell.date)"
20260
+ [attr.data-jv-date]="isoDate(cell.date)"
20261
+ [attr.tabindex]="isFocusedDate(cell.date) ? 0 : -1"
20262
+ [attr.aria-selected]="isSelected(cell.date) ? 'true' : 'false'"
20263
+ (click)="selectDate(cell.date)"
20264
+ (keydown)="handleDayKeydown($event, cell.date)"
20265
+ >
20266
+ {{ cell.date.getDate() }}
20267
+ </button>
20268
+ }
20269
+ </div>
20270
+ </div>
20271
+
20272
+ <div dialog-actions class="jv-date-picker-actions">
20273
+ @if (currentValue()) {
20274
+ <jv-button variant="ghost" (click)="clearSelection()">{{ translation.translate('datePicker.clear') }}</jv-button>
20275
+ }
20276
+ <jv-button variant="outline" (click)="closeDialog()">{{ translation.translate('datePicker.close') }}</jv-button>
20277
+ </div>
20278
+ </jv-dialog>
20128
20279
  `, styles: [".jv-date-picker-trigger{width:100%;min-height:var(--jv-density-control-height);display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-sm);padding:0 var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);text-align:left;font:inherit;cursor:pointer}.jv-date-picker-trigger.is-invalid{border-color:var(--jv-color-danger)}.jv-date-picker-trigger:disabled{opacity:.7;cursor:not-allowed}.jv-date-picker-trigger:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-date-picker-placeholder{color:var(--jv-color-foreground-muted)}.jv-date-picker-dialog,.jv-date-picker-actions{display:grid;gap:var(--jv-spacing-md)}.jv-date-picker-toolbar,.jv-date-picker-selects,.jv-date-picker-weekdays,.jv-date-picker-grid{display:grid;gap:var(--jv-spacing-sm)}.jv-date-picker-toolbar{grid-template-columns:auto 1fr auto;align-items:center}.jv-date-picker-selects{grid-template-columns:1fr 1fr}.jv-date-picker-weekdays,.jv-date-picker-grid{grid-template-columns:repeat(7,minmax(0,1fr))}.jv-date-picker-weekdays span{text-align:center;font-size:.8125rem;font-weight:600;color:var(--jv-color-foreground-muted)}.jv-date-picker-day{min-height:2.5rem;border:1px solid transparent;border-radius:var(--jv-radius-md);background:transparent;color:var(--jv-color-foreground);font:inherit;cursor:pointer;transition:background-color .16s ease,border-color .16s ease,color .16s ease}.jv-date-picker-day:hover,.jv-date-picker-day:focus-visible{background:var(--jv-color-surface-muted)}.jv-date-picker-day:focus-visible{outline:2px solid var(--jv-color-primary);outline-offset:1px}.jv-date-picker-day--outside{color:var(--jv-color-foreground-muted)}.jv-date-picker-day--today{border-color:var(--jv-color-primary)}.jv-date-picker-day--selected{background:var(--jv-color-primary);color:#fff}\n"] }]
20129
20280
  }], propDecorators: { calendarRootRef: [{ type: i0.ViewChild, args: ['calendarRoot', { isSignal: true }] }], triggerButtonRef: [{ type: i0.ViewChild, args: ['triggerButton', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], dialogTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "dialogTitle", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], valueInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
20130
20281
  function daysInMonth(year, month) {
@@ -20149,6 +20300,297 @@ function capitalize(value) {
20149
20300
  return value.charAt(0).toUpperCase() + value.slice(1);
20150
20301
  }
20151
20302
 
20303
+ let fileInputIdSequence = 0;
20304
+ class JvFileInputComponent {
20305
+ translation = inject(JvTranslationService);
20306
+ instanceId = ++fileInputIdSequence;
20307
+ nativeInputRef = viewChild('nativeInput', ...(ngDevMode ? [{ debugName: "nativeInputRef" }] : /* istanbul ignore next */ []));
20308
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
20309
+ hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
20310
+ error = input('', ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
20311
+ describedBy = input('', ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
20312
+ inputId = input(`jv-file-input-${this.instanceId}`, ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
20313
+ accept = input('', ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
20314
+ multiple = input(true, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
20315
+ maxFiles = input(10, ...(ngDevMode ? [{ debugName: "maxFiles" }] : /* istanbul ignore next */ []));
20316
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
20317
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
20318
+ invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
20319
+ filesInput = input(undefined, { ...(ngDevMode ? { debugName: "filesInput" } : /* istanbul ignore next */ {}), alias: 'files' });
20320
+ filesChange = output();
20321
+ filesRejected = output();
20322
+ dragActive = signal(false, ...(ngDevMode ? [{ debugName: "dragActive" }] : /* istanbul ignore next */ []));
20323
+ rejectionMessage = signal('', ...(ngDevMode ? [{ debugName: "rejectionMessage" }] : /* istanbul ignore next */ []));
20324
+ internalFiles = signal([], ...(ngDevMode ? [{ debugName: "internalFiles" }] : /* istanbul ignore next */ []));
20325
+ currentFiles = computed(() => {
20326
+ const externalFiles = this.filesInput();
20327
+ return externalFiles !== undefined ? [...(externalFiles ?? [])] : this.internalFiles();
20328
+ }, ...(ngDevMode ? [{ debugName: "currentFiles" }] : /* istanbul ignore next */ []));
20329
+ selectionSummary = computed(() => this.translation.translate('fileInput.selectionSummary', { count: this.currentFiles().length, max: this.maxFiles() }), ...(ngDevMode ? [{ debugName: "selectionSummary" }] : /* istanbul ignore next */ []));
20330
+ dropzoneAriaLabel = computed(() => `${this.label() || this.translation.translate('fileInput.dropLabel')}. ${this.selectionSummary()}`, ...(ngDevMode ? [{ debugName: "dropzoneAriaLabel" }] : /* istanbul ignore next */ []));
20331
+ openFileDialog() {
20332
+ if (this.disabled())
20333
+ return;
20334
+ this.nativeInputRef()?.nativeElement.click();
20335
+ }
20336
+ handleDropzoneKeydown(event) {
20337
+ if (event.key === 'Enter' || event.key === ' ') {
20338
+ event.preventDefault();
20339
+ this.openFileDialog();
20340
+ }
20341
+ }
20342
+ onNativeInputChange(fileList) {
20343
+ this.consumeFiles(fileList ? Array.from(fileList) : []);
20344
+ const input = this.nativeInputRef()?.nativeElement;
20345
+ if (input) {
20346
+ input.value = '';
20347
+ }
20348
+ }
20349
+ onDragEnter(event) {
20350
+ if (this.disabled())
20351
+ return;
20352
+ event.preventDefault();
20353
+ this.dragActive.set(true);
20354
+ }
20355
+ onDragOver(event) {
20356
+ if (this.disabled())
20357
+ return;
20358
+ event.preventDefault();
20359
+ this.dragActive.set(true);
20360
+ }
20361
+ onDragLeave(event) {
20362
+ if (event.currentTarget?.contains(event.relatedTarget)) {
20363
+ return;
20364
+ }
20365
+ this.dragActive.set(false);
20366
+ }
20367
+ onDrop(event) {
20368
+ if (this.disabled())
20369
+ return;
20370
+ event.preventDefault();
20371
+ this.dragActive.set(false);
20372
+ this.consumeFiles(Array.from(event.dataTransfer?.files ?? []));
20373
+ }
20374
+ removeFile(index) {
20375
+ if (this.disabled())
20376
+ return;
20377
+ const nextFiles = [...this.currentFiles()];
20378
+ nextFiles.splice(index, 1);
20379
+ this.updateFiles(nextFiles);
20380
+ }
20381
+ trackFile(index, file) {
20382
+ return `${file.name}-${file.size}-${file.lastModified}-${index}`;
20383
+ }
20384
+ formatBytes(size) {
20385
+ if (size < 1024)
20386
+ return `${size} B`;
20387
+ if (size < 1024 * 1024)
20388
+ return `${(size / 1024).toFixed(1)} KB`;
20389
+ return `${(size / (1024 * 1024)).toFixed(1)} MB`;
20390
+ }
20391
+ consumeFiles(files) {
20392
+ if (files.length === 0)
20393
+ return;
20394
+ const accepted = [];
20395
+ const rejectedByAccept = [];
20396
+ const currentFiles = this.currentFiles();
20397
+ const remainingSlots = this.multiple() ? Math.max(this.maxFiles() - currentFiles.length, 0) : 1;
20398
+ for (const file of files) {
20399
+ if (!this.matchesAccept(file)) {
20400
+ rejectedByAccept.push(file);
20401
+ continue;
20402
+ }
20403
+ accepted.push(file);
20404
+ }
20405
+ const nextAccepted = this.multiple() ? accepted.slice(0, remainingSlots) : accepted.slice(0, 1);
20406
+ const rejectedByCount = this.multiple() ? accepted.slice(remainingSlots) : accepted.slice(1);
20407
+ const nextFiles = this.multiple() ? [...currentFiles, ...nextAccepted] : nextAccepted;
20408
+ this.rejectionMessage.set('');
20409
+ if (rejectedByAccept.length > 0) {
20410
+ this.rejectionMessage.set(this.translation.translate('fileInput.invalidFiles', { count: rejectedByAccept.length }));
20411
+ this.filesRejected.emit({ files: rejectedByAccept, reason: 'accept' });
20412
+ }
20413
+ if (rejectedByCount.length > 0) {
20414
+ this.rejectionMessage.set(this.translation.translate('fileInput.tooManyFiles', { max: this.maxFiles() }));
20415
+ this.filesRejected.emit({ files: rejectedByCount, reason: 'maxFiles' });
20416
+ }
20417
+ this.updateFiles(nextFiles);
20418
+ }
20419
+ updateFiles(files) {
20420
+ if (this.filesInput() === undefined) {
20421
+ this.internalFiles.set(files);
20422
+ }
20423
+ this.filesChange.emit(files);
20424
+ }
20425
+ matchesAccept(file) {
20426
+ const accept = this.accept().trim();
20427
+ if (!accept)
20428
+ return true;
20429
+ const type = file.type.toLowerCase();
20430
+ const name = file.name.toLowerCase();
20431
+ return accept
20432
+ .split(',')
20433
+ .map((entry) => entry.trim().toLowerCase())
20434
+ .some((rule) => {
20435
+ if (!rule)
20436
+ return false;
20437
+ if (rule.startsWith('.'))
20438
+ return name.endsWith(rule);
20439
+ if (rule.endsWith('/*'))
20440
+ return type.startsWith(`${rule.slice(0, -1)}`);
20441
+ return type === rule;
20442
+ });
20443
+ }
20444
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20445
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JvFileInputComponent, isStandalone: true, selector: "jv-file-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, describedBy: { classPropertyName: "describedBy", publicName: "describedBy", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxFiles: { classPropertyName: "maxFiles", publicName: "maxFiles", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, filesInput: { classPropertyName: "filesInput", publicName: "files", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filesChange: "filesChange", filesRejected: "filesRejected" }, viewQueries: [{ propertyName: "nativeInputRef", first: true, predicate: ["nativeInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
20446
+ <jv-form-container
20447
+ [label]="label()"
20448
+ [inputId]="inputId()"
20449
+ [required]="required()"
20450
+ [invalid]="invalid()"
20451
+ [hint]="hint()"
20452
+ [error]="error()"
20453
+ >
20454
+ <input
20455
+ #nativeInput
20456
+ class="jv-file-input-native"
20457
+ type="file"
20458
+ [id]="inputId()"
20459
+ [attr.accept]="accept() || null"
20460
+ [attr.multiple]="multiple() ? '' : null"
20461
+ [disabled]="disabled()"
20462
+ [attr.aria-describedby]="describedBy() || null"
20463
+ (change)="onNativeInputChange($any($event.target).files)"
20464
+ />
20465
+
20466
+ <div
20467
+ class="jv-file-input-dropzone"
20468
+ [class.jv-file-input-dropzone--disabled]="disabled()"
20469
+ [class.jv-file-input-dropzone--dragover]="dragActive()"
20470
+ [class.jv-file-input-dropzone--invalid]="invalid()"
20471
+ role="button"
20472
+ tabindex="0"
20473
+ [attr.aria-disabled]="disabled() ? 'true' : 'false'"
20474
+ [attr.aria-label]="dropzoneAriaLabel()"
20475
+ (click)="openFileDialog()"
20476
+ (keydown)="handleDropzoneKeydown($event)"
20477
+ (dragenter)="onDragEnter($event)"
20478
+ (dragover)="onDragOver($event)"
20479
+ (dragleave)="onDragLeave($event)"
20480
+ (drop)="onDrop($event)"
20481
+ >
20482
+ <jv-icon name="upload-cloud" [size]="24" />
20483
+ <div class="jv-file-input-copy">
20484
+ <strong>{{ translation.translate('fileInput.dropLabel') }}</strong>
20485
+ <span>{{ translation.translate('fileInput.helperText') }}</span>
20486
+ <span class="jv-file-input-meta">{{ selectionSummary() }}</span>
20487
+ </div>
20488
+ </div>
20489
+
20490
+ @if (rejectionMessage()) {
20491
+ <p class="jv-file-input-rejection" aria-live="polite">{{ rejectionMessage() }}</p>
20492
+ }
20493
+
20494
+ @if (currentFiles().length > 0) {
20495
+ <ul class="jv-file-input-list">
20496
+ @for (file of currentFiles(); track trackFile($index, file); let index = $index) {
20497
+ <li class="jv-file-input-item">
20498
+ <div class="jv-file-input-item-copy">
20499
+ <span class="jv-file-input-item-name">{{ file.name }}</span>
20500
+ <span class="jv-file-input-item-meta">{{ formatBytes(file.size) }}</span>
20501
+ </div>
20502
+ <button
20503
+ type="button"
20504
+ class="jv-file-input-remove"
20505
+ [disabled]="disabled()"
20506
+ [attr.aria-label]="translation.translate('fileInput.removeFile', { name: file.name })"
20507
+ (click)="removeFile(index)"
20508
+ >
20509
+ <jv-icon name="x" [size]="16" />
20510
+ </button>
20511
+ </li>
20512
+ }
20513
+ </ul>
20514
+ }
20515
+ </jv-form-container>
20516
+ `, isInline: true, styles: [".jv-file-input-native{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.jv-file-input-dropzone{display:grid;grid-template-columns:auto 1fr;gap:var(--jv-spacing-md);align-items:center;padding:var(--jv-spacing-lg);border:1px dashed var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);cursor:pointer;transition:border-color .16s ease,background-color .16s ease,box-shadow .16s ease;outline:none}.jv-file-input-dropzone:hover,.jv-file-input-dropzone:focus-visible,.jv-file-input-dropzone--dragover{border-color:var(--jv-color-primary);background:var(--jv-color-surface-muted);box-shadow:inset 0 0 0 1px var(--jv-color-primary)}.jv-file-input-dropzone--invalid{border-color:var(--jv-color-danger)}.jv-file-input-dropzone--disabled{opacity:.65;cursor:not-allowed}.jv-file-input-copy{display:grid;gap:.25rem;min-width:0}.jv-file-input-copy span,.jv-file-input-meta,.jv-file-input-item-meta,.jv-file-input-rejection{color:var(--jv-color-foreground-muted)}.jv-file-input-rejection{margin:var(--jv-spacing-sm) 0 0;font-size:.875rem}.jv-file-input-list{list-style:none;padding:0;margin:var(--jv-spacing-md) 0 0;display:grid;gap:var(--jv-spacing-sm)}.jv-file-input-item{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-sm) var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface)}.jv-file-input-item-copy{min-width:0;display:grid;gap:.125rem}.jv-file-input-item-name{font-weight:600;color:var(--jv-color-foreground);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.jv-file-input-remove{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:none;border-radius:999px;background:transparent;color:var(--jv-color-foreground-muted);cursor:pointer}.jv-file-input-remove:hover,.jv-file-input-remove:focus-visible{background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);outline:none}.jv-file-input-remove:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ 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"] }] });
20517
+ }
20518
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JvFileInputComponent, decorators: [{
20519
+ type: Component,
20520
+ args: [{ selector: 'jv-file-input', standalone: true, imports: [JvFormContainerComponent, JvIconComponent], template: `
20521
+ <jv-form-container
20522
+ [label]="label()"
20523
+ [inputId]="inputId()"
20524
+ [required]="required()"
20525
+ [invalid]="invalid()"
20526
+ [hint]="hint()"
20527
+ [error]="error()"
20528
+ >
20529
+ <input
20530
+ #nativeInput
20531
+ class="jv-file-input-native"
20532
+ type="file"
20533
+ [id]="inputId()"
20534
+ [attr.accept]="accept() || null"
20535
+ [attr.multiple]="multiple() ? '' : null"
20536
+ [disabled]="disabled()"
20537
+ [attr.aria-describedby]="describedBy() || null"
20538
+ (change)="onNativeInputChange($any($event.target).files)"
20539
+ />
20540
+
20541
+ <div
20542
+ class="jv-file-input-dropzone"
20543
+ [class.jv-file-input-dropzone--disabled]="disabled()"
20544
+ [class.jv-file-input-dropzone--dragover]="dragActive()"
20545
+ [class.jv-file-input-dropzone--invalid]="invalid()"
20546
+ role="button"
20547
+ tabindex="0"
20548
+ [attr.aria-disabled]="disabled() ? 'true' : 'false'"
20549
+ [attr.aria-label]="dropzoneAriaLabel()"
20550
+ (click)="openFileDialog()"
20551
+ (keydown)="handleDropzoneKeydown($event)"
20552
+ (dragenter)="onDragEnter($event)"
20553
+ (dragover)="onDragOver($event)"
20554
+ (dragleave)="onDragLeave($event)"
20555
+ (drop)="onDrop($event)"
20556
+ >
20557
+ <jv-icon name="upload-cloud" [size]="24" />
20558
+ <div class="jv-file-input-copy">
20559
+ <strong>{{ translation.translate('fileInput.dropLabel') }}</strong>
20560
+ <span>{{ translation.translate('fileInput.helperText') }}</span>
20561
+ <span class="jv-file-input-meta">{{ selectionSummary() }}</span>
20562
+ </div>
20563
+ </div>
20564
+
20565
+ @if (rejectionMessage()) {
20566
+ <p class="jv-file-input-rejection" aria-live="polite">{{ rejectionMessage() }}</p>
20567
+ }
20568
+
20569
+ @if (currentFiles().length > 0) {
20570
+ <ul class="jv-file-input-list">
20571
+ @for (file of currentFiles(); track trackFile($index, file); let index = $index) {
20572
+ <li class="jv-file-input-item">
20573
+ <div class="jv-file-input-item-copy">
20574
+ <span class="jv-file-input-item-name">{{ file.name }}</span>
20575
+ <span class="jv-file-input-item-meta">{{ formatBytes(file.size) }}</span>
20576
+ </div>
20577
+ <button
20578
+ type="button"
20579
+ class="jv-file-input-remove"
20580
+ [disabled]="disabled()"
20581
+ [attr.aria-label]="translation.translate('fileInput.removeFile', { name: file.name })"
20582
+ (click)="removeFile(index)"
20583
+ >
20584
+ <jv-icon name="x" [size]="16" />
20585
+ </button>
20586
+ </li>
20587
+ }
20588
+ </ul>
20589
+ }
20590
+ </jv-form-container>
20591
+ `, styles: [".jv-file-input-native{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.jv-file-input-dropzone{display:grid;grid-template-columns:auto 1fr;gap:var(--jv-spacing-md);align-items:center;padding:var(--jv-spacing-lg);border:1px dashed var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface);color:var(--jv-color-foreground);cursor:pointer;transition:border-color .16s ease,background-color .16s ease,box-shadow .16s ease;outline:none}.jv-file-input-dropzone:hover,.jv-file-input-dropzone:focus-visible,.jv-file-input-dropzone--dragover{border-color:var(--jv-color-primary);background:var(--jv-color-surface-muted);box-shadow:inset 0 0 0 1px var(--jv-color-primary)}.jv-file-input-dropzone--invalid{border-color:var(--jv-color-danger)}.jv-file-input-dropzone--disabled{opacity:.65;cursor:not-allowed}.jv-file-input-copy{display:grid;gap:.25rem;min-width:0}.jv-file-input-copy span,.jv-file-input-meta,.jv-file-input-item-meta,.jv-file-input-rejection{color:var(--jv-color-foreground-muted)}.jv-file-input-rejection{margin:var(--jv-spacing-sm) 0 0;font-size:.875rem}.jv-file-input-list{list-style:none;padding:0;margin:var(--jv-spacing-md) 0 0;display:grid;gap:var(--jv-spacing-sm)}.jv-file-input-item{display:flex;align-items:center;justify-content:space-between;gap:var(--jv-spacing-md);padding:var(--jv-spacing-sm) var(--jv-spacing-md);border:1px solid var(--jv-color-border);border-radius:var(--jv-radius-md);background:var(--jv-color-surface)}.jv-file-input-item-copy{min-width:0;display:grid;gap:.125rem}.jv-file-input-item-name{font-weight:600;color:var(--jv-color-foreground);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.jv-file-input-remove{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:none;border-radius:999px;background:transparent;color:var(--jv-color-foreground-muted);cursor:pointer}.jv-file-input-remove:hover,.jv-file-input-remove:focus-visible{background:var(--jv-color-surface-muted);color:var(--jv-color-foreground);outline:none}.jv-file-input-remove:disabled{opacity:.5;cursor:not-allowed}\n"] }]
20592
+ }], propDecorators: { nativeInputRef: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFiles", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], filesInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }], filesChange: [{ type: i0.Output, args: ["filesChange"] }], filesRejected: [{ type: i0.Output, args: ["filesRejected"] }] } });
20593
+
20152
20594
  class JvListComponent {
20153
20595
  role = input('list', ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
20154
20596
  ariaRole = input(null, ...(ngDevMode ? [{ debugName: "ariaRole" }] : /* istanbul ignore next */ []));
@@ -22154,6 +22596,7 @@ const JV_GRID_DEFAULT_OPTIONS = {
22154
22596
  let gridIdSequence = 0;
22155
22597
  class JvGridComponent {
22156
22598
  gridId = `jv-grid-${++gridIdSequence}`;
22599
+ formatService = inject(JvFormatService);
22157
22600
  translationService = inject(JvTranslationService);
22158
22601
  data = input([], ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
22159
22602
  columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
@@ -22222,7 +22665,7 @@ class JvGridComponent {
22222
22665
  const value = this.getRawCellValue(row, col);
22223
22666
  if (value == null)
22224
22667
  return false;
22225
- return String(value).toLowerCase().includes(term);
22668
+ return this.getSearchValue(value, col).includes(term);
22226
22669
  });
22227
22670
  });
22228
22671
  }
@@ -22236,7 +22679,7 @@ class JvGridComponent {
22236
22679
  const col = this.columns().find(c => this.colKey(c) === key);
22237
22680
  if (!col || col.filterable === false)
22238
22681
  return true;
22239
- const value = String(this.getRawCellValue(row, col) ?? '').toLowerCase();
22682
+ const value = this.getSearchValue(this.getRawCellValue(row, col), col);
22240
22683
  const fv = filter.value.toLowerCase();
22241
22684
  switch (filter.operator) {
22242
22685
  case 'contains': return value.includes(fv);
@@ -22258,8 +22701,9 @@ class JvGridComponent {
22258
22701
  if (!columnKey || !direction)
22259
22702
  return items;
22260
22703
  return [...items].sort((a, b) => {
22261
- const va = this.getRawCellValue(a, this.findColumn(columnKey));
22262
- const vb = this.getRawCellValue(b, this.findColumn(columnKey));
22704
+ const col = this.findColumn(columnKey);
22705
+ const va = this.getComparableValue(this.getRawCellValue(a, col), col);
22706
+ const vb = this.getComparableValue(this.getRawCellValue(b, col), col);
22263
22707
  if (va == null)
22264
22708
  return 1;
22265
22709
  if (vb == null)
@@ -22363,14 +22807,56 @@ class JvGridComponent {
22363
22807
  const val = this.getCellValue(row, col);
22364
22808
  return val == null ? '' : String(val);
22365
22809
  }
22366
- formatValue(value, col) {
22810
+ formatValue(value, col, _row) {
22367
22811
  if (value == null)
22368
22812
  return '';
22369
- if (col.type === 'date' && value instanceof Date) {
22370
- return value.toLocaleDateString();
22813
+ const type = this.resolveColumnType(col);
22814
+ if (type === 'text' || type === 'boolean') {
22815
+ return String(value);
22816
+ }
22817
+ return this.formatService.format(type, value, this.getFormatOverrides(col));
22818
+ }
22819
+ getComparableValue(value, col) {
22820
+ const type = this.resolveColumnType(col);
22821
+ if (value == null || value === '')
22822
+ return null;
22823
+ if (type === 'text') {
22824
+ return String(value).toLowerCase();
22825
+ }
22826
+ if (type === 'boolean') {
22827
+ return Boolean(value);
22828
+ }
22829
+ return this.formatService.toComparable(type, value);
22830
+ }
22831
+ getSearchValue(value, col) {
22832
+ const type = this.resolveColumnType(col);
22833
+ if (type === 'text' || type === 'boolean') {
22834
+ return String(value ?? '').toLowerCase();
22835
+ }
22836
+ return this.formatService.toSearchValue(type, value);
22837
+ }
22838
+ getExportValue(value, col) {
22839
+ if (value == null)
22840
+ return '';
22841
+ const type = this.resolveColumnType(col);
22842
+ if (type === 'date' || type === 'time' || type === 'datetime') {
22843
+ const date = this.formatService.toDate(value);
22844
+ if (!date)
22845
+ return String(value);
22846
+ if (type === 'date')
22847
+ return date.toISOString().slice(0, 10);
22848
+ if (type === 'time')
22849
+ return date.toISOString().slice(11, 19);
22850
+ return date.toISOString();
22371
22851
  }
22372
22852
  return String(value);
22373
22853
  }
22854
+ resolveColumnType(col) {
22855
+ return col.type ?? 'text';
22856
+ }
22857
+ getFormatOverrides(col) {
22858
+ return col.formatOptions;
22859
+ }
22374
22860
  getAriaSort(col) {
22375
22861
  if (col.sortable === false || !this.resolvedOptions().sortable)
22376
22862
  return null;
@@ -22618,14 +23104,14 @@ class JvGridComponent {
22618
23104
  }
22619
23105
  // Export
22620
23106
  exportCsv() {
22621
- const cols = this.visibleColumns().map(c => ({ key: this.colKey(c), header: c.header }));
23107
+ const cols = this.visibleColumns().map(c => ({ key: this.colKey(c), header: c.header, column: c }));
22622
23108
  const allData = this.effectiveOptions().serverSide ? this.data() : this.sortedData();
22623
23109
  const headerRow = cols.map(c => `"${c.header}"`).join(',');
22624
23110
  const dataRows = allData.map(row => cols.map(col => {
22625
23111
  const val = row[col.key];
22626
23112
  if (val == null)
22627
23113
  return '';
22628
- return `"${String(val).replace(/"/g, '""')}"`;
23114
+ return `"${this.getExportValue(val, col.column).replace(/"/g, '""')}"`;
22629
23115
  }).join(','));
22630
23116
  const csv = [headerRow, ...dataRows].join('\r\n');
22631
23117
  const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
@@ -22637,13 +23123,13 @@ class JvGridComponent {
22637
23123
  URL.revokeObjectURL(url);
22638
23124
  }
22639
23125
  exportExcel() {
22640
- const cols = this.visibleColumns().map(c => ({ key: this.colKey(c), header: c.header }));
23126
+ const cols = this.visibleColumns().map(c => ({ key: this.colKey(c), header: c.header, column: c }));
22641
23127
  const allData = this.effectiveOptions().serverSide ? this.data() : this.sortedData();
22642
23128
  let html = '<table>';
22643
23129
  html += '<thead><tr>' + cols.map(c => `<th>${c.header}</th>`).join('') + '</tr></thead>';
22644
23130
  html += '<tbody>';
22645
23131
  for (const row of allData) {
22646
- html += '<tr>' + cols.map(col => `<td>${row[col.key] ?? ''}</td>`).join('') + '</tr>';
23132
+ html += '<tr>' + cols.map(col => `<td>${this.getExportValue(row[col.key], col.column)}</td>`).join('') + '</tr>';
22647
23133
  }
22648
23134
  html += '</tbody></table>';
22649
23135
  const blob = new Blob([html], { type: 'application/vnd.ms-excel' });
@@ -22924,7 +23410,7 @@ class JvGridComponent {
22924
23410
  } @else if (col.format) {
22925
23411
  {{ col.format(value, row) }}
22926
23412
  } @else {
22927
- {{ formatValue(value, col) }}
23413
+ {{ formatValue(value, col, row) }}
22928
23414
  }
22929
23415
  }
22930
23416
  </td>
@@ -23275,7 +23761,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
23275
23761
  } @else if (col.format) {
23276
23762
  {{ col.format(value, row) }}
23277
23763
  } @else {
23278
- {{ formatValue(value, col) }}
23764
+ {{ formatValue(value, col, row) }}
23279
23765
  }
23280
23766
  }
23281
23767
  </td>
@@ -23524,5 +24010,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
23524
24010
  * Generated bundle index. Do not edit.
23525
24011
  */
23526
24012
 
23527
- export { EN, ES, JV_DEFAULT_LOCALE, JV_FALLBACK_ICON_NAME, JV_GRID_DEFAULT_OPTIONS, JV_LOCALE_DICTIONARIES, JV_LUCIDE_ICON_REGISTRY, JV_UI_CONFIG, JV_UI_CONFIG_DEFAULTS, JV_UI_DEFAULT_CONFIG, JvAccordionComponent, JvAccordionPanelDirective, JvAlertComponent, JvAnnouncementService, JvBadgeComponent, JvBreadcrumbComponent, JvButtonComponent, JvButtonGroupComponent, JvCardComponent, JvChangePasswordPageComponent, JvCheckboxComponent, JvCodeComponent, JvCollapsiblePanelComponent, JvConfirmDialogComponent, JvDashboardShellComponent, JvDatePickerComponent, JvDialogComponent, JvDialogService, JvDividerComponent, JvForgotPasswordPageComponent, JvFormContainerComponent, JvGridComponent, JvIconButtonComponent, JvIconComponent, JvInputComponent, JvListComponent, JvListItemComponent, JvLoaderComponent, JvLoaderService, JvLoginPageComponent, JvModalSelectorComponent, JvPageComponent, JvPaginationComponent, JvRadioComponent, JvSectionComponent, JvSelectComponent, JvSidebarComponent, JvSwitchComponent, JvTabPanelDirective, JvTabsComponent, JvTextareaComponent, JvThemeService, JvToastComponent, JvToastService, JvTooltipDirective, JvTopbarComponent, JvTranslationService, provideJvUi };
24013
+ export { EN, ES, JV_DEFAULT_LOCALE, JV_FALLBACK_ICON_NAME, JV_GRID_DEFAULT_OPTIONS, JV_LOCALE_DICTIONARIES, JV_LUCIDE_ICON_REGISTRY, JV_UI_CONFIG, JV_UI_CONFIG_DEFAULTS, JV_UI_DEFAULT_CONFIG, JvAccordionComponent, JvAccordionPanelDirective, JvAlertComponent, JvAnnouncementService, JvBadgeComponent, JvBreadcrumbComponent, JvButtonComponent, JvButtonGroupComponent, JvCardComponent, JvChangePasswordPageComponent, JvCheckboxComponent, JvCodeComponent, JvCollapsiblePanelComponent, JvConfirmDialogComponent, JvDashboardShellComponent, JvDatePickerComponent, JvDialogComponent, JvDialogService, JvDividerComponent, JvFileInputComponent, JvForgotPasswordPageComponent, JvFormContainerComponent, JvFormatService, JvGridComponent, JvIconButtonComponent, JvIconComponent, JvInputComponent, JvListComponent, JvListItemComponent, JvLoaderComponent, JvLoaderService, JvLoginPageComponent, JvModalSelectorComponent, JvPageComponent, JvPaginationComponent, JvRadioComponent, JvSectionComponent, JvSelectComponent, JvSidebarComponent, JvSwitchComponent, JvTabPanelDirective, JvTabsComponent, JvTextareaComponent, JvThemeService, JvToastComponent, JvToastService, JvTooltipDirective, JvTopbarComponent, JvTranslationService, provideJvUi };
23528
24014
  //# sourceMappingURL=devjuliovilla-jv-ui.mjs.map