codexly-ui 0.5.6 → 0.5.7

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, ChangeDetectionStrategy, ViewEncapsulation, Component, InjectionToken, inject, PLATFORM_ID, signal, effect, Injectable, ElementRef, HostAttributeToken, output, Directive, contentChild, forwardRef, Renderer2, ApplicationRef, EnvironmentInjector, DestroyRef, createComponent, untracked, HostListener, viewChild, ViewChild, Injector, ChangeDetectorRef, NgZone, model, contentChildren, ContentChildren, ViewChildren, Input, numberAttribute, booleanAttribute } from '@angular/core';
2
+ import { input, computed, ChangeDetectionStrategy, ViewEncapsulation, Component, InjectionToken, inject, PLATFORM_ID, signal, effect, Injectable, ElementRef, HostAttributeToken, output, Directive, forwardRef, contentChild, Renderer2, ApplicationRef, EnvironmentInjector, DestroyRef, createComponent, untracked, HostListener, viewChild, ViewChild, Injector, ChangeDetectorRef, NgZone, model, contentChildren, ContentChildren, ViewChildren, Input, numberAttribute, booleanAttribute } from '@angular/core';
3
3
  import { isPlatformBrowser, NgTemplateOutlet, DOCUMENT, NgStyle, CurrencyPipe, DecimalPipe } from '@angular/common';
4
4
  import * as i1$1 from '@angular/forms';
5
5
  import { NG_VALUE_ACCESSOR, FormControl, NgControl, ReactiveFormsModule, FormGroup, Validators, FormsModule } from '@angular/forms';
@@ -655,6 +655,24 @@ const BADGE_SHAPE_FIXED = {
655
655
  circle: 'rounded-full',
656
656
  };
657
657
 
658
+ // ── Context ───────────────────────────────────────────────────────────────────
659
+ /** Provided by clx-card so nested form controls/buttons can inherit its disabled state. */
660
+ const CLX_CARD_DISABLED_CONTEXT = new InjectionToken('CLX_CARD_DISABLED_CONTEXT');
661
+ const CARD_PADDING_MAP = {
662
+ none: '',
663
+ sm: 'p-2',
664
+ md: 'p-3',
665
+ lg: 'p-5',
666
+ };
667
+ const CARD_SHADOW_MAP = {
668
+ none: 'shadow-none',
669
+ sm: 'shadow-sm',
670
+ md: 'shadow-md',
671
+ lg: 'shadow-lg',
672
+ xl: 'shadow-xl',
673
+ '2xl': 'shadow-2xl',
674
+ };
675
+
658
676
  const SPINNER_SIZE_MAP = {
659
677
  xxs: 'w-3 h-3 border-2',
660
678
  xs: 'w-4 h-4 border-2',
@@ -788,7 +806,8 @@ class ClxButtonComponent {
788
806
  * necessarily painted yet when the constructor runs — otherwise any consumer-supplied
789
807
  * classes (e.g. positioning utilities like `absolute`/`top-1`) would be silently lost. */
790
808
  _consumerClass = inject(new HostAttributeToken('class'), { optional: true }) ?? '';
791
- _inactive = computed(() => this.loading() || this.disabled(), ...(ngDevMode ? [{ debugName: "_inactive" }] : /* istanbul ignore next */ []));
809
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
810
+ _inactive = computed(() => this.loading() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_inactive" }] : /* istanbul ignore next */ []));
792
811
  _badgeClass = computed(() => {
793
812
  const t = resolveColor(this.badgeColor());
794
813
  const size = BADGE_CIRCLE_SIZE_MAP['xs'];
@@ -1208,21 +1227,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
1208
1227
  }]
1209
1228
  }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], onRemove: [{ type: i0.Output, args: ["onRemove"] }] } });
1210
1229
 
1211
- const CARD_PADDING_MAP = {
1212
- none: '',
1213
- sm: 'p-2',
1214
- md: 'p-3',
1215
- lg: 'p-5',
1216
- };
1217
- const CARD_SHADOW_MAP = {
1218
- none: 'shadow-none',
1219
- sm: 'shadow-sm',
1220
- md: 'shadow-md',
1221
- lg: 'shadow-lg',
1222
- xl: 'shadow-xl',
1223
- '2xl': 'shadow-2xl',
1224
- };
1225
-
1226
1230
  class ClxCardHeaderDirective {
1227
1231
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxCardHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1228
1232
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.15", type: ClxCardHeaderDirective, isStandalone: true, selector: "[clxCardHeader]", ngImport: i0 });
@@ -1234,20 +1238,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
1234
1238
 
1235
1239
  class ClxCardBodyDirective {
1236
1240
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxCardBodyDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1237
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.15", type: ClxCardBodyDirective, isStandalone: true, selector: "[clxCardBody]", ngImport: i0 });
1241
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.15", type: ClxCardBodyDirective, isStandalone: true, selector: "[clxCardBody]", providers: [
1242
+ {
1243
+ provide: CLX_CARD_DISABLED_CONTEXT,
1244
+ useFactory: () => inject(forwardRef(() => ClxCardComponent)).disabled,
1245
+ },
1246
+ ], ngImport: i0 });
1238
1247
  }
1239
1248
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxCardBodyDirective, decorators: [{
1240
1249
  type: Directive,
1241
- args: [{ selector: '[clxCardBody]', standalone: true }]
1250
+ args: [{
1251
+ selector: '[clxCardBody]',
1252
+ standalone: true,
1253
+ providers: [
1254
+ {
1255
+ provide: CLX_CARD_DISABLED_CONTEXT,
1256
+ useFactory: () => inject(forwardRef(() => ClxCardComponent)).disabled,
1257
+ },
1258
+ ],
1259
+ }]
1242
1260
  }] });
1243
1261
 
1244
1262
  class ClxCardFooterDirective {
1245
1263
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxCardFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1246
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.15", type: ClxCardFooterDirective, isStandalone: true, selector: "[clxCardFooter]", ngImport: i0 });
1264
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.15", type: ClxCardFooterDirective, isStandalone: true, selector: "[clxCardFooter]", providers: [
1265
+ {
1266
+ provide: CLX_CARD_DISABLED_CONTEXT,
1267
+ useFactory: () => inject(forwardRef(() => ClxCardComponent)).disabled,
1268
+ },
1269
+ ], ngImport: i0 });
1247
1270
  }
1248
1271
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxCardFooterDirective, decorators: [{
1249
1272
  type: Directive,
1250
- args: [{ selector: '[clxCardFooter]', standalone: true }]
1273
+ args: [{
1274
+ selector: '[clxCardFooter]',
1275
+ standalone: true,
1276
+ providers: [
1277
+ {
1278
+ provide: CLX_CARD_DISABLED_CONTEXT,
1279
+ useFactory: () => inject(forwardRef(() => ClxCardComponent)).disabled,
1280
+ },
1281
+ ],
1282
+ }]
1251
1283
  }] });
1252
1284
 
1253
1285
  class ClxCardComponent {
@@ -1257,6 +1289,7 @@ class ClxCardComponent {
1257
1289
  padding = input('md', ...(ngDevMode ? [{ debugName: "padding" }] : /* istanbul ignore next */ []));
1258
1290
  shadow = input(undefined, ...(ngDevMode ? [{ debugName: "shadow" }] : /* istanbul ignore next */ []));
1259
1291
  hover = input(false, ...(ngDevMode ? [{ debugName: "hover" }] : /* istanbul ignore next */ []));
1292
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1260
1293
  _color = computed(() => this.color() ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_color" }] : /* istanbul ignore next */ []));
1261
1294
  _headerSlot = contentChild(ClxCardHeaderDirective, ...(ngDevMode ? [{ debugName: "_headerSlot" }] : /* istanbul ignore next */ []));
1262
1295
  _bodySlot = contentChild(ClxCardBodyDirective, ...(ngDevMode ? [{ debugName: "_bodySlot" }] : /* istanbul ignore next */ []));
@@ -1286,10 +1319,11 @@ class ClxCardComponent {
1286
1319
  const shadowClass = shadowInput !== undefined ? CARD_SHADOW_MAP[shadowInput] : '';
1287
1320
  variantClass = `bg-clx-surface-2 border border-transparent ${shadowClass}`;
1288
1321
  }
1289
- return `flex flex-col ${resolveContainerRadius(this._themeSvc.config().borderRadius)} overflow-hidden w-full ${variantClass} ${hover}`.trim();
1322
+ const disabledCls = this.disabled() ? 'opacity-50 pointer-events-none' : '';
1323
+ return `flex flex-col ${resolveContainerRadius(this._themeSvc.config().borderRadius)} overflow-hidden w-full ${variantClass} ${hover} ${disabledCls}`.trim();
1290
1324
  }, ...(ngDevMode ? [{ debugName: "_hostClass" }] : /* istanbul ignore next */ []));
1291
1325
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1292
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxCardComponent, isStandalone: true, selector: "clx-card", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, shadow: { classPropertyName: "shadow", publicName: "shadow", isSignal: true, isRequired: false, transformFunction: null }, hover: { classPropertyName: "hover", publicName: "hover", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "_hostClass()" } }, queries: [{ propertyName: "_headerSlot", first: true, predicate: ClxCardHeaderDirective, descendants: true, isSignal: true }, { propertyName: "_bodySlot", first: true, predicate: ClxCardBodyDirective, descendants: true, isSignal: true }, { propertyName: "_footerSlot", first: true, predicate: ClxCardFooterDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
1326
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxCardComponent, isStandalone: true, selector: "clx-card", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, shadow: { classPropertyName: "shadow", publicName: "shadow", isSignal: true, isRequired: false, transformFunction: null }, hover: { classPropertyName: "hover", publicName: "hover", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "_hostClass()", "attr.aria-disabled": "disabled() || null" } }, queries: [{ propertyName: "_headerSlot", first: true, predicate: ClxCardHeaderDirective, descendants: true, isSignal: true }, { propertyName: "_bodySlot", first: true, predicate: ClxCardBodyDirective, descendants: true, isSignal: true }, { propertyName: "_footerSlot", first: true, predicate: ClxCardFooterDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
1293
1327
  @if (_hasHeader()) {
1294
1328
  <div class="clx-card-header flex items-center justify-between gap-3 px-4 py-3 border-b border-clx-border-soft">
1295
1329
  <ng-content select="[clxCardHeader]" />
@@ -1318,7 +1352,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
1318
1352
  imports: [],
1319
1353
  encapsulation: ViewEncapsulation.None,
1320
1354
  changeDetection: ChangeDetectionStrategy.OnPush,
1321
- host: { '[class]': '_hostClass()' },
1355
+ host: {
1356
+ '[class]': '_hostClass()',
1357
+ '[attr.aria-disabled]': 'disabled() || null',
1358
+ },
1322
1359
  template: `
1323
1360
  @if (_hasHeader()) {
1324
1361
  <div class="clx-card-header flex items-center justify-between gap-3 px-4 py-3 border-b border-clx-border-soft">
@@ -1340,7 +1377,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
1340
1377
  }
1341
1378
  `,
1342
1379
  }]
1343
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], shadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "shadow", required: false }] }], hover: [{ type: i0.Input, args: [{ isSignal: true, alias: "hover", required: false }] }], _headerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ClxCardHeaderDirective), { isSignal: true }] }], _bodySlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ClxCardBodyDirective), { isSignal: true }] }], _footerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ClxCardFooterDirective), { isSignal: true }] }] } });
1380
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], shadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "shadow", required: false }] }], hover: [{ type: i0.Input, args: [{ isSignal: true, alias: "hover", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], _headerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ClxCardHeaderDirective), { isSignal: true }] }], _bodySlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ClxCardBodyDirective), { isSignal: true }] }], _footerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ClxCardFooterDirective), { isSignal: true }] }] } });
1344
1381
 
1345
1382
  const CLX_LIST_CONTEXT = new InjectionToken('CLX_LIST_CONTEXT');
1346
1383
  // ── Variant maps ──────────────────────────────────────────────────────────────
@@ -2623,7 +2660,8 @@ class ClxInputComponent {
2623
2660
  _value = signal('', ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
2624
2661
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
2625
2662
  _cvaConnected = false;
2626
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
2663
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
2664
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
2627
2665
  _valueEffect = effect(() => {
2628
2666
  const v = this.value();
2629
2667
  if (!this._cvaConnected)
@@ -2972,8 +3010,9 @@ class ClxTextareaComponent {
2972
3010
  _value = signal('', ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
2973
3011
  _themeSvc = inject(ClxThemeService);
2974
3012
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
3013
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
2975
3014
  _cvaConnected = false;
2976
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
3015
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
2977
3016
  _valueEffect = effect(() => {
2978
3017
  const v = this.value();
2979
3018
  if (!this._cvaConnected)
@@ -3239,8 +3278,9 @@ class ClxNumberComponent {
3239
3278
  // ── Internal state ───────────────────────────────────────────────────────
3240
3279
  _value = signal(0, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
3241
3280
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
3281
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
3242
3282
  _cvaConnected = false;
3243
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
3283
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
3244
3284
  _valueEffect = effect(() => {
3245
3285
  const v = this.value();
3246
3286
  if (!this._cvaConnected)
@@ -3611,6 +3651,7 @@ class ClxSelectComponent {
3611
3651
  _wasTouched = signal(false, ...(ngDevMode ? [{ debugName: "_wasTouched" }] : /* istanbul ignore next */ []));
3612
3652
  _selectedVals = signal([], ...(ngDevMode ? [{ debugName: "_selectedVals" }] : /* istanbul ignore next */ []));
3613
3653
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
3654
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
3614
3655
  // ── DI ─────────────────────────────────────────────────────────────────────
3615
3656
  _themeSvc = inject(ClxThemeService);
3616
3657
  _ngControl = inject(NgControl, { optional: true, self: true });
@@ -3654,7 +3695,7 @@ class ClxSelectComponent {
3654
3695
  { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
3655
3696
  ];
3656
3697
  // ── Computed ───────────────────────────────────────────────────────────────
3657
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
3698
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
3658
3699
  _sizeConfig = computed(() => SELECT_SIZE_MAP[this._size()], ...(ngDevMode ? [{ debugName: "_sizeConfig" }] : /* istanbul ignore next */ []));
3659
3700
  _hasValue = computed(() => this._selectedVals().length > 0, ...(ngDevMode ? [{ debugName: "_hasValue" }] : /* istanbul ignore next */ []));
3660
3701
  _isInvalid = computed(() => this._wasTouched() && (this._ngControl?.control?.invalid ?? false), ...(ngDevMode ? [{ debugName: "_isInvalid" }] : /* istanbul ignore next */ []));
@@ -3821,147 +3862,147 @@ class ClxSelectComponent {
3821
3862
  this._cvaDisabled.set(disabled);
3822
3863
  }
3823
3864
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3824
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxSelectComponent, isStandalone: true, selector: "clx-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, activeColor: { classPropertyName: "activeColor", publicName: "activeColor", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, async: { classPropertyName: "async", publicName: "async", isSignal: true, isRequired: false, transformFunction: null }, loadOptions: { classPropertyName: "loadOptions", publicName: "loadOptions", isSignal: true, isRequired: false, transformFunction: null }, debounceMs: { classPropertyName: "debounceMs", publicName: "debounceMs", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown.escape": "onEscape()" }, classAttribute: "flex flex-col gap-1.5 w-full" }, ngImport: i0, template: `
3825
- @if (label()) {
3826
- <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
3827
- }
3828
-
3829
- <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
3830
- <div
3831
- #origin="cdkOverlayOrigin"
3832
- cdkOverlayOrigin
3833
- [class]="_triggerClass()"
3834
- [attr.tabindex]="_disabled() ? null : '0'"
3835
- role="combobox"
3836
- [attr.aria-labelledby]="label() ? _labelId : null"
3837
- [attr.aria-expanded]="_isOpen()"
3838
- aria-haspopup="listbox"
3839
- (click)="!_disabled() && _togglePanel()"
3840
- (keydown)="_onTriggerKeydown($event)">
3841
-
3842
- <!-- Multi mode: tag chips -->
3843
- @if (multiple()) {
3844
- <div [class]="_tagContainerClass()">
3845
- @for (opt of _selectedOptions(); track opt.value) {
3846
- <span
3847
- clx-tag
3848
- variant="soft"
3849
- [color]="_activeColor()"
3850
- [size]="_sizeConfig().tagSize"
3851
- (onRemove)="_deselect(opt.value)">
3852
- {{ opt.label }}
3853
- </span>
3854
- }
3855
- @if (!_selectedOptions().length) {
3856
- <span [class]="_placeholderClass()">{{ placeholder() }}</span>
3857
- }
3858
- </div>
3859
-
3860
- <!-- Single mode: display value -->
3861
- } @else {
3862
- <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
3863
- }
3864
-
3865
- <!-- Right controls: clear + chevron -->
3866
- <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
3867
- @if (_hasValue() && !_disabled() && multiple()) {
3868
- <button
3869
- clx-button
3870
- type="button"
3871
- variant="ghost"
3872
- [color]="_activeColor()"
3873
- shape="circle"
3874
- [size]="_sizeConfig().btnSize"
3875
- icon="close"
3876
- [iconOnly]="true"
3877
- (mousedown)="$event.preventDefault()"
3878
- (click)="$event.stopPropagation(); _clearAll()">
3879
- </button>
3880
- }
3881
- <button
3882
- clx-button
3883
- type="button"
3884
- variant="ghost"
3885
- [color]="_activeColor()"
3886
- shape="circle"
3887
- [size]="_sizeConfig().btnSize"
3888
- icon="keyboard_arrow_down"
3889
- [iconOnly]="true"
3890
- [class]="_chevronBtnClass()"
3891
- (mousedown)="$event.preventDefault()">
3892
- </button>
3893
- </div>
3894
- </div>
3895
-
3896
- <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
3897
- <ng-template
3898
- cdkConnectedOverlay
3899
- [cdkConnectedOverlayOrigin]="origin"
3900
- [cdkConnectedOverlayOpen]="_isOpen()"
3901
- [cdkConnectedOverlayPositions]="_positions"
3902
- [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
3903
- [cdkConnectedOverlayWidth]="_overlayWidth()"
3904
- [cdkConnectedOverlayPush]="true"
3905
- (overlayOutsideClick)="_closePanel()"
3906
- (overlayKeydown)="_onOverlayKeydown($event)">
3907
-
3908
- <div [class]="_panelClass()" role="listbox">
3909
-
3910
- <!-- Search / Autocomplete input -->
3911
- @if (searchable() || async()) {
3912
- <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
3913
- <clx-input
3914
- [formControl]="_searchControl"
3915
- [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
3916
- [color]="_activeColor()"
3917
- [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
3918
- size="sm">
3919
- </clx-input>
3920
- </div>
3921
- }
3922
-
3923
- <!-- Options list -->
3924
- <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
3925
- @if (async() && _asyncLoading()) {
3926
- <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
3927
- <clx-spinner size="sm" [color]="_activeColor()" />
3928
- Buscando...
3929
- </div>
3930
- } @else if (_showAsyncPlaceholder()) {
3931
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
3932
- Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
3933
- </div>
3934
- } @else if (_filteredOptions().length === 0) {
3935
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
3936
- } @else {
3937
- @for (opt of _filteredOptions(); track opt.value) {
3938
- <button
3939
- type="button"
3940
- [class]="_optionClass(opt)"
3941
- [disabled]="opt.disabled ?? false"
3942
- (mousedown)="$event.preventDefault()"
3943
- (click)="_selectOption(opt)">
3944
- <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
3945
- @if (_isSelected(opt.value)) {
3946
- <span clx-icon name="check" size="xs" class="shrink-0"></span>
3947
- }
3948
- </button>
3949
- }
3950
- }
3951
- </div>
3952
-
3953
- </div>
3954
- </ng-template>
3955
-
3956
- <!-- ── Bottom message ─────────────────────────────────────────────────── -->
3957
- @if (_isInvalid()) {
3958
- <p class="text-xs text-red-500 flex items-center gap-1">
3959
- <span clx-icon name="error" size="xs"></span>
3960
- {{ errorMessage() }}
3961
- </p>
3962
- } @else if (hint()) {
3963
- <p [class]="_hintClass()">{{ hint() }}</p>
3964
- }
3865
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxSelectComponent, isStandalone: true, selector: "clx-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, activeColor: { classPropertyName: "activeColor", publicName: "activeColor", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, async: { classPropertyName: "async", publicName: "async", isSignal: true, isRequired: false, transformFunction: null }, loadOptions: { classPropertyName: "loadOptions", publicName: "loadOptions", isSignal: true, isRequired: false, transformFunction: null }, debounceMs: { classPropertyName: "debounceMs", publicName: "debounceMs", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown.escape": "onEscape()" }, classAttribute: "flex flex-col gap-1.5 w-full" }, ngImport: i0, template: `
3866
+ @if (label()) {
3867
+ <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
3868
+ }
3869
+
3870
+ <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
3871
+ <div
3872
+ #origin="cdkOverlayOrigin"
3873
+ cdkOverlayOrigin
3874
+ [class]="_triggerClass()"
3875
+ [attr.tabindex]="_disabled() ? null : '0'"
3876
+ role="combobox"
3877
+ [attr.aria-labelledby]="label() ? _labelId : null"
3878
+ [attr.aria-expanded]="_isOpen()"
3879
+ aria-haspopup="listbox"
3880
+ (click)="!_disabled() && _togglePanel()"
3881
+ (keydown)="_onTriggerKeydown($event)">
3882
+
3883
+ <!-- Multi mode: tag chips -->
3884
+ @if (multiple()) {
3885
+ <div [class]="_tagContainerClass()">
3886
+ @for (opt of _selectedOptions(); track opt.value) {
3887
+ <span
3888
+ clx-tag
3889
+ variant="soft"
3890
+ [color]="_activeColor()"
3891
+ [size]="_sizeConfig().tagSize"
3892
+ (onRemove)="_deselect(opt.value)">
3893
+ {{ opt.label }}
3894
+ </span>
3895
+ }
3896
+ @if (!_selectedOptions().length) {
3897
+ <span [class]="_placeholderClass()">{{ placeholder() }}</span>
3898
+ }
3899
+ </div>
3900
+
3901
+ <!-- Single mode: display value -->
3902
+ } @else {
3903
+ <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
3904
+ }
3905
+
3906
+ <!-- Right controls: clear + chevron -->
3907
+ <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
3908
+ @if (_hasValue() && !_disabled() && multiple()) {
3909
+ <button
3910
+ clx-button
3911
+ type="button"
3912
+ variant="ghost"
3913
+ [color]="_activeColor()"
3914
+ shape="circle"
3915
+ [size]="_sizeConfig().btnSize"
3916
+ icon="close"
3917
+ [iconOnly]="true"
3918
+ (mousedown)="$event.preventDefault()"
3919
+ (click)="$event.stopPropagation(); _clearAll()">
3920
+ </button>
3921
+ }
3922
+ <button
3923
+ clx-button
3924
+ type="button"
3925
+ variant="ghost"
3926
+ [color]="_activeColor()"
3927
+ shape="circle"
3928
+ [size]="_sizeConfig().btnSize"
3929
+ icon="keyboard_arrow_down"
3930
+ [iconOnly]="true"
3931
+ [class]="_chevronBtnClass()"
3932
+ (mousedown)="$event.preventDefault()">
3933
+ </button>
3934
+ </div>
3935
+ </div>
3936
+
3937
+ <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
3938
+ <ng-template
3939
+ cdkConnectedOverlay
3940
+ [cdkConnectedOverlayOrigin]="origin"
3941
+ [cdkConnectedOverlayOpen]="_isOpen()"
3942
+ [cdkConnectedOverlayPositions]="_positions"
3943
+ [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
3944
+ [cdkConnectedOverlayWidth]="_overlayWidth()"
3945
+ [cdkConnectedOverlayPush]="true"
3946
+ (overlayOutsideClick)="_closePanel()"
3947
+ (overlayKeydown)="_onOverlayKeydown($event)">
3948
+
3949
+ <div [class]="_panelClass()" role="listbox">
3950
+
3951
+ <!-- Search / Autocomplete input -->
3952
+ @if (searchable() || async()) {
3953
+ <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
3954
+ <clx-input
3955
+ [formControl]="_searchControl"
3956
+ [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
3957
+ [color]="_activeColor()"
3958
+ [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
3959
+ size="sm">
3960
+ </clx-input>
3961
+ </div>
3962
+ }
3963
+
3964
+ <!-- Options list -->
3965
+ <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
3966
+ @if (async() && _asyncLoading()) {
3967
+ <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
3968
+ <clx-spinner size="sm" [color]="_activeColor()" />
3969
+ Buscando...
3970
+ </div>
3971
+ } @else if (_showAsyncPlaceholder()) {
3972
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
3973
+ Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
3974
+ </div>
3975
+ } @else if (_filteredOptions().length === 0) {
3976
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
3977
+ } @else {
3978
+ @for (opt of _filteredOptions(); track opt.value) {
3979
+ <button
3980
+ type="button"
3981
+ [class]="_optionClass(opt)"
3982
+ [disabled]="opt.disabled ?? false"
3983
+ (mousedown)="$event.preventDefault()"
3984
+ (click)="_selectOption(opt)">
3985
+ <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
3986
+ @if (_isSelected(opt.value)) {
3987
+ <span clx-icon name="check" size="xs" class="shrink-0"></span>
3988
+ }
3989
+ </button>
3990
+ }
3991
+ }
3992
+ </div>
3993
+
3994
+ </div>
3995
+ </ng-template>
3996
+
3997
+ <!-- ── Bottom message ─────────────────────────────────────────────────── -->
3998
+ @if (_isInvalid()) {
3999
+ <p class="text-xs text-red-500 flex items-center gap-1">
4000
+ <span clx-icon name="error" size="xs"></span>
4001
+ {{ errorMessage() }}
4002
+ </p>
4003
+ } @else if (hint()) {
4004
+ <p [class]="_hintClass()">{{ hint() }}</p>
4005
+ }
3965
4006
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: ClxTagComponent, selector: "span[clx-tag]", inputs: ["variant", "color", "size", "shape"], outputs: ["onRemove"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "component", type: ClxInputComponent, selector: "clx-input", inputs: ["label", "placeholder", "type", "color", "hint", "prefixIcon", "suffixIcon", "prefixText", "suffixText", "status", "statusMessage", "size", "value", "disabled", "autocomplete"] }, { kind: "component", type: ClxSpinnerComponent, selector: "clx-spinner", inputs: ["size", "color", "variant"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3966
4007
  }
3967
4008
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxSelectComponent, decorators: [{
@@ -3970,147 +4011,147 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
3970
4011
  selector: 'clx-select',
3971
4012
  standalone: true,
3972
4013
  imports: [OverlayModule, ReactiveFormsModule, ClxTagComponent, ClxIconComponent, ClxButtonComponent, ClxInputComponent, ClxSpinnerComponent],
3973
- template: `
3974
- @if (label()) {
3975
- <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
3976
- }
3977
-
3978
- <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
3979
- <div
3980
- #origin="cdkOverlayOrigin"
3981
- cdkOverlayOrigin
3982
- [class]="_triggerClass()"
3983
- [attr.tabindex]="_disabled() ? null : '0'"
3984
- role="combobox"
3985
- [attr.aria-labelledby]="label() ? _labelId : null"
3986
- [attr.aria-expanded]="_isOpen()"
3987
- aria-haspopup="listbox"
3988
- (click)="!_disabled() && _togglePanel()"
3989
- (keydown)="_onTriggerKeydown($event)">
3990
-
3991
- <!-- Multi mode: tag chips -->
3992
- @if (multiple()) {
3993
- <div [class]="_tagContainerClass()">
3994
- @for (opt of _selectedOptions(); track opt.value) {
3995
- <span
3996
- clx-tag
3997
- variant="soft"
3998
- [color]="_activeColor()"
3999
- [size]="_sizeConfig().tagSize"
4000
- (onRemove)="_deselect(opt.value)">
4001
- {{ opt.label }}
4002
- </span>
4003
- }
4004
- @if (!_selectedOptions().length) {
4005
- <span [class]="_placeholderClass()">{{ placeholder() }}</span>
4006
- }
4007
- </div>
4008
-
4009
- <!-- Single mode: display value -->
4010
- } @else {
4011
- <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
4012
- }
4013
-
4014
- <!-- Right controls: clear + chevron -->
4015
- <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
4016
- @if (_hasValue() && !_disabled() && multiple()) {
4017
- <button
4018
- clx-button
4019
- type="button"
4020
- variant="ghost"
4021
- [color]="_activeColor()"
4022
- shape="circle"
4023
- [size]="_sizeConfig().btnSize"
4024
- icon="close"
4025
- [iconOnly]="true"
4026
- (mousedown)="$event.preventDefault()"
4027
- (click)="$event.stopPropagation(); _clearAll()">
4028
- </button>
4029
- }
4030
- <button
4031
- clx-button
4032
- type="button"
4033
- variant="ghost"
4034
- [color]="_activeColor()"
4035
- shape="circle"
4036
- [size]="_sizeConfig().btnSize"
4037
- icon="keyboard_arrow_down"
4038
- [iconOnly]="true"
4039
- [class]="_chevronBtnClass()"
4040
- (mousedown)="$event.preventDefault()">
4041
- </button>
4042
- </div>
4043
- </div>
4044
-
4045
- <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
4046
- <ng-template
4047
- cdkConnectedOverlay
4048
- [cdkConnectedOverlayOrigin]="origin"
4049
- [cdkConnectedOverlayOpen]="_isOpen()"
4050
- [cdkConnectedOverlayPositions]="_positions"
4051
- [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
4052
- [cdkConnectedOverlayWidth]="_overlayWidth()"
4053
- [cdkConnectedOverlayPush]="true"
4054
- (overlayOutsideClick)="_closePanel()"
4055
- (overlayKeydown)="_onOverlayKeydown($event)">
4056
-
4057
- <div [class]="_panelClass()" role="listbox">
4058
-
4059
- <!-- Search / Autocomplete input -->
4060
- @if (searchable() || async()) {
4061
- <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
4062
- <clx-input
4063
- [formControl]="_searchControl"
4064
- [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
4065
- [color]="_activeColor()"
4066
- [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
4067
- size="sm">
4068
- </clx-input>
4069
- </div>
4070
- }
4071
-
4072
- <!-- Options list -->
4073
- <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
4074
- @if (async() && _asyncLoading()) {
4075
- <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
4076
- <clx-spinner size="sm" [color]="_activeColor()" />
4077
- Buscando...
4078
- </div>
4079
- } @else if (_showAsyncPlaceholder()) {
4080
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
4081
- Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
4082
- </div>
4083
- } @else if (_filteredOptions().length === 0) {
4084
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
4085
- } @else {
4086
- @for (opt of _filteredOptions(); track opt.value) {
4087
- <button
4088
- type="button"
4089
- [class]="_optionClass(opt)"
4090
- [disabled]="opt.disabled ?? false"
4091
- (mousedown)="$event.preventDefault()"
4092
- (click)="_selectOption(opt)">
4093
- <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
4094
- @if (_isSelected(opt.value)) {
4095
- <span clx-icon name="check" size="xs" class="shrink-0"></span>
4096
- }
4097
- </button>
4098
- }
4099
- }
4100
- </div>
4101
-
4102
- </div>
4103
- </ng-template>
4104
-
4105
- <!-- ── Bottom message ─────────────────────────────────────────────────── -->
4106
- @if (_isInvalid()) {
4107
- <p class="text-xs text-red-500 flex items-center gap-1">
4108
- <span clx-icon name="error" size="xs"></span>
4109
- {{ errorMessage() }}
4110
- </p>
4111
- } @else if (hint()) {
4112
- <p [class]="_hintClass()">{{ hint() }}</p>
4113
- }
4014
+ template: `
4015
+ @if (label()) {
4016
+ <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
4017
+ }
4018
+
4019
+ <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
4020
+ <div
4021
+ #origin="cdkOverlayOrigin"
4022
+ cdkOverlayOrigin
4023
+ [class]="_triggerClass()"
4024
+ [attr.tabindex]="_disabled() ? null : '0'"
4025
+ role="combobox"
4026
+ [attr.aria-labelledby]="label() ? _labelId : null"
4027
+ [attr.aria-expanded]="_isOpen()"
4028
+ aria-haspopup="listbox"
4029
+ (click)="!_disabled() && _togglePanel()"
4030
+ (keydown)="_onTriggerKeydown($event)">
4031
+
4032
+ <!-- Multi mode: tag chips -->
4033
+ @if (multiple()) {
4034
+ <div [class]="_tagContainerClass()">
4035
+ @for (opt of _selectedOptions(); track opt.value) {
4036
+ <span
4037
+ clx-tag
4038
+ variant="soft"
4039
+ [color]="_activeColor()"
4040
+ [size]="_sizeConfig().tagSize"
4041
+ (onRemove)="_deselect(opt.value)">
4042
+ {{ opt.label }}
4043
+ </span>
4044
+ }
4045
+ @if (!_selectedOptions().length) {
4046
+ <span [class]="_placeholderClass()">{{ placeholder() }}</span>
4047
+ }
4048
+ </div>
4049
+
4050
+ <!-- Single mode: display value -->
4051
+ } @else {
4052
+ <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
4053
+ }
4054
+
4055
+ <!-- Right controls: clear + chevron -->
4056
+ <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
4057
+ @if (_hasValue() && !_disabled() && multiple()) {
4058
+ <button
4059
+ clx-button
4060
+ type="button"
4061
+ variant="ghost"
4062
+ [color]="_activeColor()"
4063
+ shape="circle"
4064
+ [size]="_sizeConfig().btnSize"
4065
+ icon="close"
4066
+ [iconOnly]="true"
4067
+ (mousedown)="$event.preventDefault()"
4068
+ (click)="$event.stopPropagation(); _clearAll()">
4069
+ </button>
4070
+ }
4071
+ <button
4072
+ clx-button
4073
+ type="button"
4074
+ variant="ghost"
4075
+ [color]="_activeColor()"
4076
+ shape="circle"
4077
+ [size]="_sizeConfig().btnSize"
4078
+ icon="keyboard_arrow_down"
4079
+ [iconOnly]="true"
4080
+ [class]="_chevronBtnClass()"
4081
+ (mousedown)="$event.preventDefault()">
4082
+ </button>
4083
+ </div>
4084
+ </div>
4085
+
4086
+ <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
4087
+ <ng-template
4088
+ cdkConnectedOverlay
4089
+ [cdkConnectedOverlayOrigin]="origin"
4090
+ [cdkConnectedOverlayOpen]="_isOpen()"
4091
+ [cdkConnectedOverlayPositions]="_positions"
4092
+ [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
4093
+ [cdkConnectedOverlayWidth]="_overlayWidth()"
4094
+ [cdkConnectedOverlayPush]="true"
4095
+ (overlayOutsideClick)="_closePanel()"
4096
+ (overlayKeydown)="_onOverlayKeydown($event)">
4097
+
4098
+ <div [class]="_panelClass()" role="listbox">
4099
+
4100
+ <!-- Search / Autocomplete input -->
4101
+ @if (searchable() || async()) {
4102
+ <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
4103
+ <clx-input
4104
+ [formControl]="_searchControl"
4105
+ [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
4106
+ [color]="_activeColor()"
4107
+ [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
4108
+ size="sm">
4109
+ </clx-input>
4110
+ </div>
4111
+ }
4112
+
4113
+ <!-- Options list -->
4114
+ <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
4115
+ @if (async() && _asyncLoading()) {
4116
+ <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
4117
+ <clx-spinner size="sm" [color]="_activeColor()" />
4118
+ Buscando...
4119
+ </div>
4120
+ } @else if (_showAsyncPlaceholder()) {
4121
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
4122
+ Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
4123
+ </div>
4124
+ } @else if (_filteredOptions().length === 0) {
4125
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
4126
+ } @else {
4127
+ @for (opt of _filteredOptions(); track opt.value) {
4128
+ <button
4129
+ type="button"
4130
+ [class]="_optionClass(opt)"
4131
+ [disabled]="opt.disabled ?? false"
4132
+ (mousedown)="$event.preventDefault()"
4133
+ (click)="_selectOption(opt)">
4134
+ <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
4135
+ @if (_isSelected(opt.value)) {
4136
+ <span clx-icon name="check" size="xs" class="shrink-0"></span>
4137
+ }
4138
+ </button>
4139
+ }
4140
+ }
4141
+ </div>
4142
+
4143
+ </div>
4144
+ </ng-template>
4145
+
4146
+ <!-- ── Bottom message ─────────────────────────────────────────────────── -->
4147
+ @if (_isInvalid()) {
4148
+ <p class="text-xs text-red-500 flex items-center gap-1">
4149
+ <span clx-icon name="error" size="xs"></span>
4150
+ {{ errorMessage() }}
4151
+ </p>
4152
+ } @else if (hint()) {
4153
+ <p [class]="_hintClass()">{{ hint() }}</p>
4154
+ }
4114
4155
  `,
4115
4156
  encapsulation: ViewEncapsulation.None,
4116
4157
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -4141,8 +4182,9 @@ class ClxCheckboxComponent {
4141
4182
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4142
4183
  _checked = signal(false, ...(ngDevMode ? [{ debugName: "_checked" }] : /* istanbul ignore next */ []));
4143
4184
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
4185
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
4144
4186
  _cvaConnected = false;
4145
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
4187
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
4146
4188
  _valueEffect = effect(() => {
4147
4189
  const v = this.value();
4148
4190
  if (!this._cvaConnected)
@@ -4288,8 +4330,9 @@ class ClxRadioGroupComponent {
4288
4330
  /** Current selected value — readable by child clx-radio via injection */
4289
4331
  _value = signal(null, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
4290
4332
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
4333
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
4291
4334
  /** Merged disabled state (input + CVA) — readable by child clx-radio */
4292
- isDisabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
4335
+ isDisabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
4293
4336
  _onChange = () => { };
4294
4337
  _onTouched = () => { };
4295
4338
  select(value) {
@@ -4476,8 +4519,9 @@ class ClxSwitchComponent {
4476
4519
  changed = output();
4477
4520
  _checked = signal(false, ...(ngDevMode ? [{ debugName: "_checked" }] : /* istanbul ignore next */ []));
4478
4521
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
4522
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
4479
4523
  _cvaConnected = false;
4480
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
4524
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
4481
4525
  _valueEffect = effect(() => {
4482
4526
  const v = this.value();
4483
4527
  if (!this._cvaConnected)
@@ -4624,11 +4668,12 @@ class ClxSliderComponent {
4624
4668
  // ── Internal state ───────────────────────────────────────────────────────────
4625
4669
  _currentValue = signal(0, ...(ngDevMode ? [{ debugName: "_currentValue" }] : /* istanbul ignore next */ []));
4626
4670
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
4671
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
4627
4672
  _isFocused = signal(false, ...(ngDevMode ? [{ debugName: "_isFocused" }] : /* istanbul ignore next */ []));
4628
4673
  _isActive = signal(false, ...(ngDevMode ? [{ debugName: "_isActive" }] : /* istanbul ignore next */ []));
4629
4674
  _wasTouched = signal(false, ...(ngDevMode ? [{ debugName: "_wasTouched" }] : /* istanbul ignore next */ []));
4630
4675
  // ── Computed ─────────────────────────────────────────────────────────────────
4631
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
4676
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
4632
4677
  _sizeConfig = computed(() => SLIDER_SIZE_MAP[this._size()], ...(ngDevMode ? [{ debugName: "_sizeConfig" }] : /* istanbul ignore next */ []));
4633
4678
  _colorTokens = computed(() => resolveColor(this._isInvalid() ? 'red' : this._activeColor()), ...(ngDevMode ? [{ debugName: "_colorTokens" }] : /* istanbul ignore next */ []));
4634
4679
  _pct = computed(() => {
@@ -4982,6 +5027,7 @@ class ClxUploadComponent {
4982
5027
  _ngControl = inject(NgControl, { optional: true, self: true });
4983
5028
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
4984
5029
  _cvaConnected = false;
5030
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
4985
5031
  _onChange = () => { };
4986
5032
  _onTouched = () => { };
4987
5033
  // ── Template refs ──────────────────────────────────────────────────────────
@@ -4991,7 +5037,7 @@ class ClxUploadComponent {
4991
5037
  this._ngControl.valueAccessor = this;
4992
5038
  }
4993
5039
  // ── Computed ───────────────────────────────────────────────────────────────
4994
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
5040
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
4995
5041
  _sizeConfig = computed(() => UPLOAD_SIZE_MAP[this._size()], ...(ngDevMode ? [{ debugName: "_sizeConfig" }] : /* istanbul ignore next */ []));
4996
5042
  _isError = computed(() => this._errorKey() !== null, ...(ngDevMode ? [{ debugName: "_isError" }] : /* istanbul ignore next */ []));
4997
5043
  _thumbClass = computed(() => `relative group flex flex-col ${resolveRadius(this._themeSvc.config().borderRadius)} border border-clx-border bg-clx-surface-2`, ...(ngDevMode ? [{ debugName: "_thumbClass" }] : /* istanbul ignore next */ []));
@@ -5650,6 +5696,7 @@ class ClxColorPickerComponent {
5650
5696
  _sso = inject(ScrollStrategyOptions);
5651
5697
  _wasTouched = signal(false, ...(ngDevMode ? [{ debugName: "_wasTouched" }] : /* istanbul ignore next */ []));
5652
5698
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
5699
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
5653
5700
  _scrollStrategy = this._sso.reposition();
5654
5701
  _onChange = () => { };
5655
5702
  _onTouched = () => { };
@@ -5673,7 +5720,7 @@ class ClxColorPickerComponent {
5673
5720
  { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
5674
5721
  ];
5675
5722
  // ── Computed ───────────────────────────────────────────────────────────────
5676
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
5723
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
5677
5724
  _sizeConfig = computed(() => COLOR_PICKER_SIZE_MAP[this._size()], ...(ngDevMode ? [{ debugName: "_sizeConfig" }] : /* istanbul ignore next */ []));
5678
5725
  _hasValue = computed(() => this._hasColor(), ...(ngDevMode ? [{ debugName: "_hasValue" }] : /* istanbul ignore next */ []));
5679
5726
  _isInvalid = computed(() => this._wasTouched() && (this._ngControl?.control?.invalid ?? false), ...(ngDevMode ? [{ debugName: "_isInvalid" }] : /* istanbul ignore next */ []));
@@ -6255,8 +6302,9 @@ class ClxDatepickerComponent {
6255
6302
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
6256
6303
  _value = signal(null, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
6257
6304
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
6305
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
6258
6306
  _cvaConnected = false;
6259
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
6307
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
6260
6308
  _cursorYear = signal(new Date().getFullYear(), ...(ngDevMode ? [{ debugName: "_cursorYear" }] : /* istanbul ignore next */ []));
6261
6309
  _cursorMonth = signal(new Date().getMonth(), ...(ngDevMode ? [{ debugName: "_cursorMonth" }] : /* istanbul ignore next */ []));
6262
6310
  _isOpen = signal(false, ...(ngDevMode ? [{ debugName: "_isOpen" }] : /* istanbul ignore next */ []));
@@ -6681,13 +6729,14 @@ class ClxTimepickerComponent {
6681
6729
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
6682
6730
  _value = signal(null, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
6683
6731
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
6732
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
6684
6733
  _cvaConnected = false;
6685
6734
  _valueEffect = effect(() => {
6686
6735
  const v = this.value();
6687
6736
  if (!this._cvaConnected)
6688
6737
  untracked(() => { this._value.set(v); });
6689
6738
  }, ...(ngDevMode ? [{ debugName: "_valueEffect" }] : /* istanbul ignore next */ []));
6690
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
6739
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
6691
6740
  _isOpen = signal(false, ...(ngDevMode ? [{ debugName: "_isOpen" }] : /* istanbul ignore next */ []));
6692
6741
  _hours = HOURS;
6693
6742
  _minutes = MINUTES;
@@ -7033,7 +7082,8 @@ class ClxDateRangePickerComponent {
7033
7082
  _presets = DATE_PRESETS;
7034
7083
  _dayLabels = DAY_LABELS;
7035
7084
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
7036
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
7085
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
7086
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
7037
7087
  _isOpen = signal(false, ...(ngDevMode ? [{ debugName: "_isOpen" }] : /* istanbul ignore next */ []));
7038
7088
  // Cursor: left calendar month
7039
7089
  _cursorYear = signal(new Date().getFullYear(), ...(ngDevMode ? [{ debugName: "_cursorYear" }] : /* istanbul ignore next */ []));
@@ -8076,7 +8126,8 @@ class ClxEditorComponent {
8076
8126
  _headingCtrl = new FormControl('p');
8077
8127
  // ── State ─────────────────────────────────────────────────────────────────
8078
8128
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
8079
- _disabled = computed(() => this._cvaDisabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
8129
+ _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
8130
+ _disabled = computed(() => this._cvaDisabled() || this.disabled() || !!this._cardDisabled?.(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
8080
8131
  _tick = signal(0, ...(ngDevMode ? [{ debugName: "_tick" }] : /* istanbul ignore next */ []));
8081
8132
  _sourceMode = signal(false, ...(ngDevMode ? [{ debugName: "_sourceMode" }] : /* istanbul ignore next */ []));
8082
8133
  _sourceValue = signal('', ...(ngDevMode ? [{ debugName: "_sourceValue" }] : /* istanbul ignore next */ []));