codexly-ui 0.12.45 → 0.12.46

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.
@@ -3,7 +3,7 @@ import { input, computed, ChangeDetectionStrategy, ViewEncapsulation, Component,
3
3
  import { DomSanitizer } from '@angular/platform-browser';
4
4
  import { isPlatformBrowser, NgTemplateOutlet, DOCUMENT, NgStyle, CurrencyPipe, DecimalPipe } from '@angular/common';
5
5
  import * as i1$1 from '@angular/forms';
6
- import { NG_VALUE_ACCESSOR, FormControl, NgControl, ReactiveFormsModule, FormGroup, Validators, FormsModule } from '@angular/forms';
6
+ import { NG_VALUE_ACCESSOR, FormControl, NgControl, TouchedChangeEvent, ReactiveFormsModule, FormGroup, Validators, FormsModule } from '@angular/forms';
7
7
  import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
8
  import { startWith, map, debounceTime, distinctUntilChanged, switchMap, of, catchError, filter } from 'rxjs';
9
9
  import * as i1 from '@angular/cdk/overlay';
@@ -4421,6 +4421,11 @@ class ClxSelectComponent {
4421
4421
  _asyncLoading = signal(false, ...(ngDevMode ? [{ debugName: "_asyncLoading" }] : /* istanbul ignore next */ []));
4422
4422
  _asyncQuery = signal('', ...(ngDevMode ? [{ debugName: "_asyncQuery" }] : /* istanbul ignore next */ []));
4423
4423
  _wasTouched = signal(false, ...(ngDevMode ? [{ debugName: "_wasTouched" }] : /* istanbul ignore next */ []));
4424
+ // Mirrors this._ngControl.control.touched reactively — that flag is plain mutable state on the
4425
+ // AbstractControl, not a signal, so a form.markAllAsTouched() call (e.g. from a failed submit)
4426
+ // would never be noticed by a computed() reading it directly. control.events emits a
4427
+ // TouchedChangeEvent for exactly that call, which is what actually drives this.
4428
+ _controlTouched = signal(false, ...(ngDevMode ? [{ debugName: "_controlTouched" }] : /* istanbul ignore next */ []));
4424
4429
  _selectedVals = signal([], ...(ngDevMode ? [{ debugName: "_selectedVals" }] : /* istanbul ignore next */ []));
4425
4430
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
4426
4431
  _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
@@ -4437,6 +4442,18 @@ class ClxSelectComponent {
4437
4442
  this._searchQuery = toSignal(this._searchControl.valueChanges.pipe(startWith(''), map(v => v ?? '')), { initialValue: '' });
4438
4443
  if (this._ngControl)
4439
4444
  this._ngControl.valueAccessor = this;
4445
+ // See _controlTouched's own doc comment — control.touched itself isn't observable, but
4446
+ // control.events (Angular 18+) emits a TouchedChangeEvent whenever markAsTouched/
4447
+ // markAllAsTouched runs, which is exactly the case (a failed form submit) that needs to make
4448
+ // this field's error visible even though the user never opened this dropdown themselves.
4449
+ const control = this._ngControl?.control;
4450
+ if (control) {
4451
+ this._controlTouched.set(control.touched);
4452
+ control.events.subscribe(event => {
4453
+ if (event instanceof TouchedChangeEvent)
4454
+ this._controlTouched.set(event.touched);
4455
+ });
4456
+ }
4440
4457
  effect(() => {
4441
4458
  const family = this._displayFontFamily();
4442
4459
  if (family)
@@ -4472,7 +4489,12 @@ class ClxSelectComponent {
4472
4489
  _hasValue = computed(() => this._selectedVals().length > 0, ...(ngDevMode ? [{ debugName: "_hasValue" }] : /* istanbul ignore next */ []));
4473
4490
  _isInvalid = computed(() => {
4474
4491
  this._selectedVals(); // re-evaluate control.invalid (imperatively updated by Angular Forms) on each selection change
4475
- return this._wasTouched() && (this._ngControl?.control?.invalid ?? false);
4492
+ // Either signal is enough: the user actually opening/closing this dropdown (_wasTouched), or
4493
+ // Angular Forms marking the control touched programmatically (_controlTouched — set by e.g.
4494
+ // form.markAllAsTouched() on a failed submit, which never fires this component's own
4495
+ // _closePanel(), so _wasTouched alone would stay false forever for a field the user never
4496
+ // interacted with before trying to submit).
4497
+ return (this._wasTouched() || this._controlTouched()) && (this._ngControl?.control?.invalid ?? false);
4476
4498
  }, ...(ngDevMode ? [{ debugName: "_isInvalid" }] : /* istanbul ignore next */ []));
4477
4499
  _selectedOptions = computed(() => {
4478
4500
  const vals = this._selectedVals();