codexly-ui 0.1.21 → 0.1.23

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.
@@ -7,7 +7,7 @@ import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
7
  import { startWith, map, debounceTime, distinctUntilChanged, switchMap, of, catchError } from 'rxjs';
8
8
  import * as i1$1 from '@angular/cdk/overlay';
9
9
  import { ScrollStrategyOptions, OverlayModule } from '@angular/cdk/overlay';
10
- import { ScrollingModule } from '@angular/cdk/scrolling';
10
+ import { ScrollingModule, CdkScrollable } from '@angular/cdk/scrolling';
11
11
  import { Chart, LineController, BarController, PieController, DoughnutController, RadarController, PolarAreaController, CategoryScale, LinearScale, RadialLinearScale, BarElement, LineElement, PointElement, ArcElement, Filler, Tooltip, Legend } from 'chart.js';
12
12
  import { RouterLink, RouterLinkActive } from '@angular/router';
13
13
 
@@ -4761,145 +4761,145 @@ class ClxSelectComponent {
4761
4761
  this._cvaDisabled.set(disabled);
4762
4762
  }
4763
4763
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4764
- 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: `
4765
- @if (label()) {
4766
- <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
4767
- }
4768
-
4769
- <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
4770
- <div
4771
- #origin="cdkOverlayOrigin"
4772
- cdkOverlayOrigin
4773
- [class]="_triggerClass()"
4774
- [attr.tabindex]="_disabled() ? null : '0'"
4775
- role="combobox"
4776
- [attr.aria-labelledby]="label() ? _labelId : null"
4777
- [attr.aria-expanded]="_isOpen()"
4778
- aria-haspopup="listbox"
4779
- (click)="!_disabled() && _togglePanel()"
4780
- (keydown)="_onTriggerKeydown($event)">
4781
-
4782
- <!-- Multi mode: tag chips -->
4783
- @if (multiple()) {
4784
- <div [class]="_tagContainerClass()">
4785
- @for (opt of _selectedOptions(); track opt.value) {
4786
- <span
4787
- clx-tag
4788
- variant="soft"
4789
- [color]="_activeColor()"
4790
- [size]="_sizeConfig().tagSize"
4791
- (onRemove)="_deselect(opt.value)">
4792
- {{ opt.label }}
4793
- </span>
4794
- }
4795
- @if (!_selectedOptions().length) {
4796
- <span [class]="_placeholderClass()">{{ placeholder() }}</span>
4797
- }
4798
- </div>
4799
-
4800
- <!-- Single mode: display value -->
4801
- } @else {
4802
- <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
4803
- }
4804
-
4805
- <!-- Right controls: clear + chevron -->
4806
- <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
4807
- @if (_hasValue() && !_disabled() && multiple()) {
4808
- <button
4809
- clx-button
4810
- variant="ghost"
4811
- [color]="_activeColor()"
4812
- shape="circle"
4813
- [size]="_sizeConfig().btnSize"
4814
- icon="close"
4815
- [iconOnly]="true"
4816
- (mousedown)="$event.preventDefault()"
4817
- (click)="$event.stopPropagation(); _clearAll()">
4818
- </button>
4819
- }
4820
- <button
4821
- clx-button
4822
- variant="ghost"
4823
- [color]="_activeColor()"
4824
- shape="circle"
4825
- [size]="_sizeConfig().btnSize"
4826
- icon="keyboard_arrow_down"
4827
- [iconOnly]="true"
4828
- [class]="_chevronBtnClass()"
4829
- (mousedown)="$event.preventDefault()">
4830
- </button>
4831
- </div>
4832
- </div>
4833
-
4834
- <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
4835
- <ng-template
4836
- cdkConnectedOverlay
4837
- [cdkConnectedOverlayOrigin]="origin"
4838
- [cdkConnectedOverlayOpen]="_isOpen()"
4839
- [cdkConnectedOverlayPositions]="_positions"
4840
- [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
4841
- [cdkConnectedOverlayWidth]="_overlayWidth()"
4842
- [cdkConnectedOverlayPush]="true"
4843
- (overlayOutsideClick)="_closePanel()"
4844
- (overlayKeydown)="_onOverlayKeydown($event)">
4845
-
4846
- <div [class]="_panelClass()" role="listbox">
4847
-
4848
- <!-- Search / Autocomplete input -->
4849
- @if (searchable() || async()) {
4850
- <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
4851
- <clx-input
4852
- [formControl]="_searchControl"
4853
- [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
4854
- [color]="_activeColor()"
4855
- [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
4856
- size="sm">
4857
- </clx-input>
4858
- </div>
4859
- }
4860
-
4861
- <!-- Options list -->
4862
- <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
4863
- @if (async() && _asyncLoading()) {
4864
- <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
4865
- <clx-spinner size="sm" [color]="_activeColor()" />
4866
- Buscando...
4867
- </div>
4868
- } @else if (_showAsyncPlaceholder()) {
4869
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
4870
- Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
4871
- </div>
4872
- } @else if (_filteredOptions().length === 0) {
4873
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
4874
- } @else {
4875
- @for (opt of _filteredOptions(); track opt.value) {
4876
- <button
4877
- type="button"
4878
- [class]="_optionClass(opt)"
4879
- [disabled]="opt.disabled ?? false"
4880
- (mousedown)="$event.preventDefault()"
4881
- (click)="_selectOption(opt)">
4882
- <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
4883
- @if (_isSelected(opt.value)) {
4884
- <span clx-icon name="check" size="xs" class="shrink-0"></span>
4885
- }
4886
- </button>
4887
- }
4888
- }
4889
- </div>
4890
-
4891
- </div>
4892
- </ng-template>
4893
-
4894
- <!-- ── Bottom message ─────────────────────────────────────────────────── -->
4895
- @if (_isInvalid()) {
4896
- <p class="text-xs text-red-500 flex items-center gap-1">
4897
- <span clx-icon name="error" size="xs"></span>
4898
- {{ errorMessage() }}
4899
- </p>
4900
- } @else if (hint()) {
4901
- <p [class]="_hintClass()">{{ hint() }}</p>
4902
- }
4764
+ 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: `
4765
+ @if (label()) {
4766
+ <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
4767
+ }
4768
+
4769
+ <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
4770
+ <div
4771
+ #origin="cdkOverlayOrigin"
4772
+ cdkOverlayOrigin
4773
+ [class]="_triggerClass()"
4774
+ [attr.tabindex]="_disabled() ? null : '0'"
4775
+ role="combobox"
4776
+ [attr.aria-labelledby]="label() ? _labelId : null"
4777
+ [attr.aria-expanded]="_isOpen()"
4778
+ aria-haspopup="listbox"
4779
+ (click)="!_disabled() && _togglePanel()"
4780
+ (keydown)="_onTriggerKeydown($event)">
4781
+
4782
+ <!-- Multi mode: tag chips -->
4783
+ @if (multiple()) {
4784
+ <div [class]="_tagContainerClass()">
4785
+ @for (opt of _selectedOptions(); track opt.value) {
4786
+ <span
4787
+ clx-tag
4788
+ variant="soft"
4789
+ [color]="_activeColor()"
4790
+ [size]="_sizeConfig().tagSize"
4791
+ (onRemove)="_deselect(opt.value)">
4792
+ {{ opt.label }}
4793
+ </span>
4794
+ }
4795
+ @if (!_selectedOptions().length) {
4796
+ <span [class]="_placeholderClass()">{{ placeholder() }}</span>
4797
+ }
4798
+ </div>
4799
+
4800
+ <!-- Single mode: display value -->
4801
+ } @else {
4802
+ <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
4803
+ }
4804
+
4805
+ <!-- Right controls: clear + chevron -->
4806
+ <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
4807
+ @if (_hasValue() && !_disabled() && multiple()) {
4808
+ <button
4809
+ clx-button
4810
+ variant="ghost"
4811
+ [color]="_activeColor()"
4812
+ shape="circle"
4813
+ [size]="_sizeConfig().btnSize"
4814
+ icon="close"
4815
+ [iconOnly]="true"
4816
+ (mousedown)="$event.preventDefault()"
4817
+ (click)="$event.stopPropagation(); _clearAll()">
4818
+ </button>
4819
+ }
4820
+ <button
4821
+ clx-button
4822
+ variant="ghost"
4823
+ [color]="_activeColor()"
4824
+ shape="circle"
4825
+ [size]="_sizeConfig().btnSize"
4826
+ icon="keyboard_arrow_down"
4827
+ [iconOnly]="true"
4828
+ [class]="_chevronBtnClass()"
4829
+ (mousedown)="$event.preventDefault()">
4830
+ </button>
4831
+ </div>
4832
+ </div>
4833
+
4834
+ <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
4835
+ <ng-template
4836
+ cdkConnectedOverlay
4837
+ [cdkConnectedOverlayOrigin]="origin"
4838
+ [cdkConnectedOverlayOpen]="_isOpen()"
4839
+ [cdkConnectedOverlayPositions]="_positions"
4840
+ [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
4841
+ [cdkConnectedOverlayWidth]="_overlayWidth()"
4842
+ [cdkConnectedOverlayPush]="true"
4843
+ (overlayOutsideClick)="_closePanel()"
4844
+ (overlayKeydown)="_onOverlayKeydown($event)">
4845
+
4846
+ <div [class]="_panelClass()" role="listbox">
4847
+
4848
+ <!-- Search / Autocomplete input -->
4849
+ @if (searchable() || async()) {
4850
+ <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
4851
+ <clx-input
4852
+ [formControl]="_searchControl"
4853
+ [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
4854
+ [color]="_activeColor()"
4855
+ [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
4856
+ size="sm">
4857
+ </clx-input>
4858
+ </div>
4859
+ }
4860
+
4861
+ <!-- Options list -->
4862
+ <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
4863
+ @if (async() && _asyncLoading()) {
4864
+ <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
4865
+ <clx-spinner size="sm" [color]="_activeColor()" />
4866
+ Buscando...
4867
+ </div>
4868
+ } @else if (_showAsyncPlaceholder()) {
4869
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
4870
+ Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
4871
+ </div>
4872
+ } @else if (_filteredOptions().length === 0) {
4873
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
4874
+ } @else {
4875
+ @for (opt of _filteredOptions(); track opt.value) {
4876
+ <button
4877
+ type="button"
4878
+ [class]="_optionClass(opt)"
4879
+ [disabled]="opt.disabled ?? false"
4880
+ (mousedown)="$event.preventDefault()"
4881
+ (click)="_selectOption(opt)">
4882
+ <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
4883
+ @if (_isSelected(opt.value)) {
4884
+ <span clx-icon name="check" size="xs" class="shrink-0"></span>
4885
+ }
4886
+ </button>
4887
+ }
4888
+ }
4889
+ </div>
4890
+
4891
+ </div>
4892
+ </ng-template>
4893
+
4894
+ <!-- ── Bottom message ─────────────────────────────────────────────────── -->
4895
+ @if (_isInvalid()) {
4896
+ <p class="text-xs text-red-500 flex items-center gap-1">
4897
+ <span clx-icon name="error" size="xs"></span>
4898
+ {{ errorMessage() }}
4899
+ </p>
4900
+ } @else if (hint()) {
4901
+ <p [class]="_hintClass()">{{ hint() }}</p>
4902
+ }
4903
4903
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$1.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$1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.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 });
4904
4904
  }
4905
4905
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxSelectComponent, decorators: [{
@@ -4908,145 +4908,145 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
4908
4908
  selector: 'clx-select',
4909
4909
  standalone: true,
4910
4910
  imports: [OverlayModule, ReactiveFormsModule, ClxTagComponent, ClxIconComponent, ClxButtonComponent, ClxInputComponent, ClxSpinnerComponent],
4911
- template: `
4912
- @if (label()) {
4913
- <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
4914
- }
4915
-
4916
- <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
4917
- <div
4918
- #origin="cdkOverlayOrigin"
4919
- cdkOverlayOrigin
4920
- [class]="_triggerClass()"
4921
- [attr.tabindex]="_disabled() ? null : '0'"
4922
- role="combobox"
4923
- [attr.aria-labelledby]="label() ? _labelId : null"
4924
- [attr.aria-expanded]="_isOpen()"
4925
- aria-haspopup="listbox"
4926
- (click)="!_disabled() && _togglePanel()"
4927
- (keydown)="_onTriggerKeydown($event)">
4928
-
4929
- <!-- Multi mode: tag chips -->
4930
- @if (multiple()) {
4931
- <div [class]="_tagContainerClass()">
4932
- @for (opt of _selectedOptions(); track opt.value) {
4933
- <span
4934
- clx-tag
4935
- variant="soft"
4936
- [color]="_activeColor()"
4937
- [size]="_sizeConfig().tagSize"
4938
- (onRemove)="_deselect(opt.value)">
4939
- {{ opt.label }}
4940
- </span>
4941
- }
4942
- @if (!_selectedOptions().length) {
4943
- <span [class]="_placeholderClass()">{{ placeholder() }}</span>
4944
- }
4945
- </div>
4946
-
4947
- <!-- Single mode: display value -->
4948
- } @else {
4949
- <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
4950
- }
4951
-
4952
- <!-- Right controls: clear + chevron -->
4953
- <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
4954
- @if (_hasValue() && !_disabled() && multiple()) {
4955
- <button
4956
- clx-button
4957
- variant="ghost"
4958
- [color]="_activeColor()"
4959
- shape="circle"
4960
- [size]="_sizeConfig().btnSize"
4961
- icon="close"
4962
- [iconOnly]="true"
4963
- (mousedown)="$event.preventDefault()"
4964
- (click)="$event.stopPropagation(); _clearAll()">
4965
- </button>
4966
- }
4967
- <button
4968
- clx-button
4969
- variant="ghost"
4970
- [color]="_activeColor()"
4971
- shape="circle"
4972
- [size]="_sizeConfig().btnSize"
4973
- icon="keyboard_arrow_down"
4974
- [iconOnly]="true"
4975
- [class]="_chevronBtnClass()"
4976
- (mousedown)="$event.preventDefault()">
4977
- </button>
4978
- </div>
4979
- </div>
4980
-
4981
- <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
4982
- <ng-template
4983
- cdkConnectedOverlay
4984
- [cdkConnectedOverlayOrigin]="origin"
4985
- [cdkConnectedOverlayOpen]="_isOpen()"
4986
- [cdkConnectedOverlayPositions]="_positions"
4987
- [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
4988
- [cdkConnectedOverlayWidth]="_overlayWidth()"
4989
- [cdkConnectedOverlayPush]="true"
4990
- (overlayOutsideClick)="_closePanel()"
4991
- (overlayKeydown)="_onOverlayKeydown($event)">
4992
-
4993
- <div [class]="_panelClass()" role="listbox">
4994
-
4995
- <!-- Search / Autocomplete input -->
4996
- @if (searchable() || async()) {
4997
- <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
4998
- <clx-input
4999
- [formControl]="_searchControl"
5000
- [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
5001
- [color]="_activeColor()"
5002
- [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
5003
- size="sm">
5004
- </clx-input>
5005
- </div>
5006
- }
5007
-
5008
- <!-- Options list -->
5009
- <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
5010
- @if (async() && _asyncLoading()) {
5011
- <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
5012
- <clx-spinner size="sm" [color]="_activeColor()" />
5013
- Buscando...
5014
- </div>
5015
- } @else if (_showAsyncPlaceholder()) {
5016
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
5017
- Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
5018
- </div>
5019
- } @else if (_filteredOptions().length === 0) {
5020
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
5021
- } @else {
5022
- @for (opt of _filteredOptions(); track opt.value) {
5023
- <button
5024
- type="button"
5025
- [class]="_optionClass(opt)"
5026
- [disabled]="opt.disabled ?? false"
5027
- (mousedown)="$event.preventDefault()"
5028
- (click)="_selectOption(opt)">
5029
- <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
5030
- @if (_isSelected(opt.value)) {
5031
- <span clx-icon name="check" size="xs" class="shrink-0"></span>
5032
- }
5033
- </button>
5034
- }
5035
- }
5036
- </div>
5037
-
5038
- </div>
5039
- </ng-template>
5040
-
5041
- <!-- ── Bottom message ─────────────────────────────────────────────────── -->
5042
- @if (_isInvalid()) {
5043
- <p class="text-xs text-red-500 flex items-center gap-1">
5044
- <span clx-icon name="error" size="xs"></span>
5045
- {{ errorMessage() }}
5046
- </p>
5047
- } @else if (hint()) {
5048
- <p [class]="_hintClass()">{{ hint() }}</p>
5049
- }
4911
+ template: `
4912
+ @if (label()) {
4913
+ <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
4914
+ }
4915
+
4916
+ <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
4917
+ <div
4918
+ #origin="cdkOverlayOrigin"
4919
+ cdkOverlayOrigin
4920
+ [class]="_triggerClass()"
4921
+ [attr.tabindex]="_disabled() ? null : '0'"
4922
+ role="combobox"
4923
+ [attr.aria-labelledby]="label() ? _labelId : null"
4924
+ [attr.aria-expanded]="_isOpen()"
4925
+ aria-haspopup="listbox"
4926
+ (click)="!_disabled() && _togglePanel()"
4927
+ (keydown)="_onTriggerKeydown($event)">
4928
+
4929
+ <!-- Multi mode: tag chips -->
4930
+ @if (multiple()) {
4931
+ <div [class]="_tagContainerClass()">
4932
+ @for (opt of _selectedOptions(); track opt.value) {
4933
+ <span
4934
+ clx-tag
4935
+ variant="soft"
4936
+ [color]="_activeColor()"
4937
+ [size]="_sizeConfig().tagSize"
4938
+ (onRemove)="_deselect(opt.value)">
4939
+ {{ opt.label }}
4940
+ </span>
4941
+ }
4942
+ @if (!_selectedOptions().length) {
4943
+ <span [class]="_placeholderClass()">{{ placeholder() }}</span>
4944
+ }
4945
+ </div>
4946
+
4947
+ <!-- Single mode: display value -->
4948
+ } @else {
4949
+ <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
4950
+ }
4951
+
4952
+ <!-- Right controls: clear + chevron -->
4953
+ <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
4954
+ @if (_hasValue() && !_disabled() && multiple()) {
4955
+ <button
4956
+ clx-button
4957
+ variant="ghost"
4958
+ [color]="_activeColor()"
4959
+ shape="circle"
4960
+ [size]="_sizeConfig().btnSize"
4961
+ icon="close"
4962
+ [iconOnly]="true"
4963
+ (mousedown)="$event.preventDefault()"
4964
+ (click)="$event.stopPropagation(); _clearAll()">
4965
+ </button>
4966
+ }
4967
+ <button
4968
+ clx-button
4969
+ variant="ghost"
4970
+ [color]="_activeColor()"
4971
+ shape="circle"
4972
+ [size]="_sizeConfig().btnSize"
4973
+ icon="keyboard_arrow_down"
4974
+ [iconOnly]="true"
4975
+ [class]="_chevronBtnClass()"
4976
+ (mousedown)="$event.preventDefault()">
4977
+ </button>
4978
+ </div>
4979
+ </div>
4980
+
4981
+ <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
4982
+ <ng-template
4983
+ cdkConnectedOverlay
4984
+ [cdkConnectedOverlayOrigin]="origin"
4985
+ [cdkConnectedOverlayOpen]="_isOpen()"
4986
+ [cdkConnectedOverlayPositions]="_positions"
4987
+ [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
4988
+ [cdkConnectedOverlayWidth]="_overlayWidth()"
4989
+ [cdkConnectedOverlayPush]="true"
4990
+ (overlayOutsideClick)="_closePanel()"
4991
+ (overlayKeydown)="_onOverlayKeydown($event)">
4992
+
4993
+ <div [class]="_panelClass()" role="listbox">
4994
+
4995
+ <!-- Search / Autocomplete input -->
4996
+ @if (searchable() || async()) {
4997
+ <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
4998
+ <clx-input
4999
+ [formControl]="_searchControl"
5000
+ [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
5001
+ [color]="_activeColor()"
5002
+ [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
5003
+ size="sm">
5004
+ </clx-input>
5005
+ </div>
5006
+ }
5007
+
5008
+ <!-- Options list -->
5009
+ <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
5010
+ @if (async() && _asyncLoading()) {
5011
+ <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
5012
+ <clx-spinner size="sm" [color]="_activeColor()" />
5013
+ Buscando...
5014
+ </div>
5015
+ } @else if (_showAsyncPlaceholder()) {
5016
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
5017
+ Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
5018
+ </div>
5019
+ } @else if (_filteredOptions().length === 0) {
5020
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
5021
+ } @else {
5022
+ @for (opt of _filteredOptions(); track opt.value) {
5023
+ <button
5024
+ type="button"
5025
+ [class]="_optionClass(opt)"
5026
+ [disabled]="opt.disabled ?? false"
5027
+ (mousedown)="$event.preventDefault()"
5028
+ (click)="_selectOption(opt)">
5029
+ <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
5030
+ @if (_isSelected(opt.value)) {
5031
+ <span clx-icon name="check" size="xs" class="shrink-0"></span>
5032
+ }
5033
+ </button>
5034
+ }
5035
+ }
5036
+ </div>
5037
+
5038
+ </div>
5039
+ </ng-template>
5040
+
5041
+ <!-- ── Bottom message ─────────────────────────────────────────────────── -->
5042
+ @if (_isInvalid()) {
5043
+ <p class="text-xs text-red-500 flex items-center gap-1">
5044
+ <span clx-icon name="error" size="xs"></span>
5045
+ {{ errorMessage() }}
5046
+ </p>
5047
+ } @else if (hint()) {
5048
+ <p [class]="_hintClass()">{{ hint() }}</p>
5049
+ }
5050
5050
  `,
5051
5051
  encapsulation: ViewEncapsulation.None,
5052
5052
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -5575,142 +5575,142 @@ class ClxAlertComponent {
5575
5575
  }
5576
5576
  }
5577
5577
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5578
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxAlertComponent, isStandalone: true, selector: "clx-alert", ngImport: i0, template: `
5579
- <!-- Dialog card -->
5580
- <div
5581
- #card
5582
- class="clx-alert-card relative w-full max-w-sm mx-auto bg-clx-surface rounded-3xl shadow-2xl overflow-hidden flex flex-col items-center text-center px-8 pt-8 pb-6 gap-4"
5583
- >
5584
- <!-- Icon circle -->
5585
- <div class="w-20 h-20 rounded-full flex items-center justify-center shrink-0"
5586
- [class]="_iconBg()">
5587
- <span clx-icon [name]="_icon()" size="xl" [class]="_iconColor()"></span>
5588
- </div>
5589
-
5590
- <!-- Title -->
5591
- @if (_title()) {
5592
- <h2
5593
- class="text-lg font-bold text-clx-text-label leading-snug"
5594
- [innerHTML]="_title()">
5595
- </h2>
5596
- }
5597
-
5598
- <!-- Message -->
5599
- @if (_message()) {
5600
- <p
5601
- class="text-sm text-clx-text-subtle leading-relaxed"
5602
- [innerHTML]="_message()">
5603
- </p>
5604
- }
5605
-
5606
- <!-- Buttons -->
5607
- <div class="flex items-center justify-center gap-3 w-full mt-1">
5608
-
5609
- @if (_showCancel()) {
5610
- <button
5611
- clx-button
5612
- [color]="_cancelColor()"
5613
- [variant]="_cancelVariant()"
5614
- size="md"
5615
- (click)="dismiss()"
5616
- >
5617
- {{ _cancelText() }}
5618
- </button>
5619
- }
5620
-
5621
- <button
5622
- clx-button
5623
- [color]="_confirmColor()"
5624
- variant="solid"
5625
- size="md"
5626
- (click)="confirm()"
5627
- >
5628
- {{ _confirmText() }}
5629
- </button>
5630
-
5631
- </div>
5632
-
5633
- <!-- Timer progress bar -->
5634
- @if (_timerTotal() > 0) {
5635
- <div class="absolute bottom-0 left-0 right-0 h-1 bg-clx-surface-2 rounded-b-3xl overflow-hidden">
5636
- <div
5637
- class="h-full transition-none rounded-b-3xl"
5638
- [class]="_progressColor()"
5639
- [ngStyle]="{ width: _progressPct() + '%' }">
5640
- </div>
5641
- </div>
5642
- }
5643
- </div>
5578
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxAlertComponent, isStandalone: true, selector: "clx-alert", ngImport: i0, template: `
5579
+ <!-- Dialog card -->
5580
+ <div
5581
+ #card
5582
+ class="clx-alert-card relative w-full max-w-sm mx-auto bg-clx-surface rounded-3xl shadow-2xl overflow-hidden flex flex-col items-center text-center px-8 pt-8 pb-6 gap-4"
5583
+ >
5584
+ <!-- Icon circle -->
5585
+ <div class="w-20 h-20 rounded-full flex items-center justify-center shrink-0"
5586
+ [class]="_iconBg()">
5587
+ <span clx-icon [name]="_icon()" size="xl" [class]="_iconColor()"></span>
5588
+ </div>
5589
+
5590
+ <!-- Title -->
5591
+ @if (_title()) {
5592
+ <h2
5593
+ class="text-lg font-bold text-clx-text-label leading-snug"
5594
+ [innerHTML]="_title()">
5595
+ </h2>
5596
+ }
5597
+
5598
+ <!-- Message -->
5599
+ @if (_message()) {
5600
+ <p
5601
+ class="text-sm text-clx-text-subtle leading-relaxed"
5602
+ [innerHTML]="_message()">
5603
+ </p>
5604
+ }
5605
+
5606
+ <!-- Buttons -->
5607
+ <div class="flex items-center justify-center gap-3 w-full mt-1">
5608
+
5609
+ @if (_showCancel()) {
5610
+ <button
5611
+ clx-button
5612
+ [color]="_cancelColor()"
5613
+ [variant]="_cancelVariant()"
5614
+ size="md"
5615
+ (click)="dismiss()"
5616
+ >
5617
+ {{ _cancelText() }}
5618
+ </button>
5619
+ }
5620
+
5621
+ <button
5622
+ clx-button
5623
+ [color]="_confirmColor()"
5624
+ variant="solid"
5625
+ size="md"
5626
+ (click)="confirm()"
5627
+ >
5628
+ {{ _confirmText() }}
5629
+ </button>
5630
+
5631
+ </div>
5632
+
5633
+ <!-- Timer progress bar -->
5634
+ @if (_timerTotal() > 0) {
5635
+ <div class="absolute bottom-0 left-0 right-0 h-1 bg-clx-surface-2 rounded-b-3xl overflow-hidden">
5636
+ <div
5637
+ class="h-full transition-none rounded-b-3xl"
5638
+ [class]="_progressColor()"
5639
+ [ngStyle]="{ width: _progressPct() + '%' }">
5640
+ </div>
5641
+ </div>
5642
+ }
5643
+ </div>
5644
5644
  `, isInline: true, styles: ["clx-alert{display:flex;align-items:center;justify-content:center;width:100%}.clx-alert-card{will-change:transform,opacity}\n"], dependencies: [{ 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: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5645
5645
  }
5646
5646
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAlertComponent, decorators: [{
5647
5647
  type: Component,
5648
- args: [{ selector: 'clx-alert', standalone: true, imports: [ClxIconComponent, ClxButtonComponent, NgStyle], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: `
5649
- <!-- Dialog card -->
5650
- <div
5651
- #card
5652
- class="clx-alert-card relative w-full max-w-sm mx-auto bg-clx-surface rounded-3xl shadow-2xl overflow-hidden flex flex-col items-center text-center px-8 pt-8 pb-6 gap-4"
5653
- >
5654
- <!-- Icon circle -->
5655
- <div class="w-20 h-20 rounded-full flex items-center justify-center shrink-0"
5656
- [class]="_iconBg()">
5657
- <span clx-icon [name]="_icon()" size="xl" [class]="_iconColor()"></span>
5658
- </div>
5659
-
5660
- <!-- Title -->
5661
- @if (_title()) {
5662
- <h2
5663
- class="text-lg font-bold text-clx-text-label leading-snug"
5664
- [innerHTML]="_title()">
5665
- </h2>
5666
- }
5667
-
5668
- <!-- Message -->
5669
- @if (_message()) {
5670
- <p
5671
- class="text-sm text-clx-text-subtle leading-relaxed"
5672
- [innerHTML]="_message()">
5673
- </p>
5674
- }
5675
-
5676
- <!-- Buttons -->
5677
- <div class="flex items-center justify-center gap-3 w-full mt-1">
5678
-
5679
- @if (_showCancel()) {
5680
- <button
5681
- clx-button
5682
- [color]="_cancelColor()"
5683
- [variant]="_cancelVariant()"
5684
- size="md"
5685
- (click)="dismiss()"
5686
- >
5687
- {{ _cancelText() }}
5688
- </button>
5689
- }
5690
-
5691
- <button
5692
- clx-button
5693
- [color]="_confirmColor()"
5694
- variant="solid"
5695
- size="md"
5696
- (click)="confirm()"
5697
- >
5698
- {{ _confirmText() }}
5699
- </button>
5700
-
5701
- </div>
5702
-
5703
- <!-- Timer progress bar -->
5704
- @if (_timerTotal() > 0) {
5705
- <div class="absolute bottom-0 left-0 right-0 h-1 bg-clx-surface-2 rounded-b-3xl overflow-hidden">
5706
- <div
5707
- class="h-full transition-none rounded-b-3xl"
5708
- [class]="_progressColor()"
5709
- [ngStyle]="{ width: _progressPct() + '%' }">
5710
- </div>
5711
- </div>
5712
- }
5713
- </div>
5648
+ args: [{ selector: 'clx-alert', standalone: true, imports: [ClxIconComponent, ClxButtonComponent, NgStyle], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: `
5649
+ <!-- Dialog card -->
5650
+ <div
5651
+ #card
5652
+ class="clx-alert-card relative w-full max-w-sm mx-auto bg-clx-surface rounded-3xl shadow-2xl overflow-hidden flex flex-col items-center text-center px-8 pt-8 pb-6 gap-4"
5653
+ >
5654
+ <!-- Icon circle -->
5655
+ <div class="w-20 h-20 rounded-full flex items-center justify-center shrink-0"
5656
+ [class]="_iconBg()">
5657
+ <span clx-icon [name]="_icon()" size="xl" [class]="_iconColor()"></span>
5658
+ </div>
5659
+
5660
+ <!-- Title -->
5661
+ @if (_title()) {
5662
+ <h2
5663
+ class="text-lg font-bold text-clx-text-label leading-snug"
5664
+ [innerHTML]="_title()">
5665
+ </h2>
5666
+ }
5667
+
5668
+ <!-- Message -->
5669
+ @if (_message()) {
5670
+ <p
5671
+ class="text-sm text-clx-text-subtle leading-relaxed"
5672
+ [innerHTML]="_message()">
5673
+ </p>
5674
+ }
5675
+
5676
+ <!-- Buttons -->
5677
+ <div class="flex items-center justify-center gap-3 w-full mt-1">
5678
+
5679
+ @if (_showCancel()) {
5680
+ <button
5681
+ clx-button
5682
+ [color]="_cancelColor()"
5683
+ [variant]="_cancelVariant()"
5684
+ size="md"
5685
+ (click)="dismiss()"
5686
+ >
5687
+ {{ _cancelText() }}
5688
+ </button>
5689
+ }
5690
+
5691
+ <button
5692
+ clx-button
5693
+ [color]="_confirmColor()"
5694
+ variant="solid"
5695
+ size="md"
5696
+ (click)="confirm()"
5697
+ >
5698
+ {{ _confirmText() }}
5699
+ </button>
5700
+
5701
+ </div>
5702
+
5703
+ <!-- Timer progress bar -->
5704
+ @if (_timerTotal() > 0) {
5705
+ <div class="absolute bottom-0 left-0 right-0 h-1 bg-clx-surface-2 rounded-b-3xl overflow-hidden">
5706
+ <div
5707
+ class="h-full transition-none rounded-b-3xl"
5708
+ [class]="_progressColor()"
5709
+ [ngStyle]="{ width: _progressPct() + '%' }">
5710
+ </div>
5711
+ </div>
5712
+ }
5713
+ </div>
5714
5714
  `, styles: ["clx-alert{display:flex;align-items:center;justify-content:center;width:100%}.clx-alert-card{will-change:transform,opacity}\n"] }]
5715
5715
  }], ctorParameters: () => [] });
5716
5716
 
@@ -5771,57 +5771,57 @@ class ClxDrawerComponent {
5771
5771
  this._modalRef?.close();
5772
5772
  }
5773
5773
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5774
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxDrawerComponent, isStandalone: true, selector: "clx-drawer", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, confirmButton: { classPropertyName: "confirmButton", publicName: "confirmButton", isSignal: true, isRequired: false, transformFunction: null }, cancelButton: { classPropertyName: "cancelButton", publicName: "cancelButton", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmClick: "confirmClick", cancelClick: "cancelClick" }, host: { properties: { "class": "_hostClass()" } }, ngImport: i0, template: `
5775
- <!-- Header -->
5776
- <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5777
- <div class="flex-1 min-w-0">
5778
- <ng-content select="[clx-drawer-title]" />
5779
- </div>
5780
- @if (showClose()) {
5781
- <button
5782
- clx-button
5783
- variant="ghost"
5784
- color="zinc"
5785
- shape="circle"
5786
- size="sm"
5787
- icon="close"
5788
- [iconOnly]="true"
5789
- class="shrink-0 -mr-1"
5790
- (click)="_close()">
5791
- </button>
5792
- }
5793
- </div>
5794
-
5795
- <!-- Body -->
5796
- <div class="flex-1 overflow-y-auto px-6 py-5">
5797
- <ng-content />
5798
- </div>
5799
-
5800
- <!-- Footer -->
5801
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5802
- @if (_hasBuiltInButtons()) {
5803
- @if (showCancelButton()) {
5804
- <button
5805
- clx-button
5806
- [color]="_cancelColor()"
5807
- [variant]="_cancelVariant()"
5808
- size="md"
5809
- (click)="cancelClick.emit()">
5810
- {{ _cancelText() }}
5811
- </button>
5812
- }
5813
- <button
5814
- clx-button
5815
- [color]="_confirmColor()"
5816
- [variant]="_confirmVariant()"
5817
- size="md"
5818
- (click)="confirmClick.emit()">
5819
- {{ _confirmText() }}
5820
- </button>
5821
- } @else {
5822
- <ng-content select="[clx-drawer-footer]" />
5823
- }
5824
- </div>
5774
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxDrawerComponent, isStandalone: true, selector: "clx-drawer", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, confirmButton: { classPropertyName: "confirmButton", publicName: "confirmButton", isSignal: true, isRequired: false, transformFunction: null }, cancelButton: { classPropertyName: "cancelButton", publicName: "cancelButton", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmClick: "confirmClick", cancelClick: "cancelClick" }, host: { properties: { "class": "_hostClass()" } }, ngImport: i0, template: `
5775
+ <!-- Header -->
5776
+ <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5777
+ <div class="flex-1 min-w-0">
5778
+ <ng-content select="[clx-drawer-title]" />
5779
+ </div>
5780
+ @if (showClose()) {
5781
+ <button
5782
+ clx-button
5783
+ variant="ghost"
5784
+ color="zinc"
5785
+ shape="circle"
5786
+ size="sm"
5787
+ icon="close"
5788
+ [iconOnly]="true"
5789
+ class="shrink-0 -mr-1"
5790
+ (click)="_close()">
5791
+ </button>
5792
+ }
5793
+ </div>
5794
+
5795
+ <!-- Body -->
5796
+ <div class="flex-1 overflow-y-auto px-6 py-5">
5797
+ <ng-content />
5798
+ </div>
5799
+
5800
+ <!-- Footer -->
5801
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5802
+ @if (_hasBuiltInButtons()) {
5803
+ @if (showCancelButton()) {
5804
+ <button
5805
+ clx-button
5806
+ [color]="_cancelColor()"
5807
+ [variant]="_cancelVariant()"
5808
+ size="md"
5809
+ (click)="cancelClick.emit()">
5810
+ {{ _cancelText() }}
5811
+ </button>
5812
+ }
5813
+ <button
5814
+ clx-button
5815
+ [color]="_confirmColor()"
5816
+ [variant]="_confirmVariant()"
5817
+ size="md"
5818
+ (click)="confirmClick.emit()">
5819
+ {{ _confirmText() }}
5820
+ </button>
5821
+ } @else {
5822
+ <ng-content select="[clx-drawer-footer]" />
5823
+ }
5824
+ </div>
5825
5825
  `, isInline: true, dependencies: [{ kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5826
5826
  }
5827
5827
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxDrawerComponent, decorators: [{
@@ -5830,57 +5830,57 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5830
5830
  selector: 'clx-drawer',
5831
5831
  standalone: true,
5832
5832
  imports: [ClxButtonComponent],
5833
- template: `
5834
- <!-- Header -->
5835
- <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5836
- <div class="flex-1 min-w-0">
5837
- <ng-content select="[clx-drawer-title]" />
5838
- </div>
5839
- @if (showClose()) {
5840
- <button
5841
- clx-button
5842
- variant="ghost"
5843
- color="zinc"
5844
- shape="circle"
5845
- size="sm"
5846
- icon="close"
5847
- [iconOnly]="true"
5848
- class="shrink-0 -mr-1"
5849
- (click)="_close()">
5850
- </button>
5851
- }
5852
- </div>
5853
-
5854
- <!-- Body -->
5855
- <div class="flex-1 overflow-y-auto px-6 py-5">
5856
- <ng-content />
5857
- </div>
5858
-
5859
- <!-- Footer -->
5860
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5861
- @if (_hasBuiltInButtons()) {
5862
- @if (showCancelButton()) {
5863
- <button
5864
- clx-button
5865
- [color]="_cancelColor()"
5866
- [variant]="_cancelVariant()"
5867
- size="md"
5868
- (click)="cancelClick.emit()">
5869
- {{ _cancelText() }}
5870
- </button>
5871
- }
5872
- <button
5873
- clx-button
5874
- [color]="_confirmColor()"
5875
- [variant]="_confirmVariant()"
5876
- size="md"
5877
- (click)="confirmClick.emit()">
5878
- {{ _confirmText() }}
5879
- </button>
5880
- } @else {
5881
- <ng-content select="[clx-drawer-footer]" />
5882
- }
5883
- </div>
5833
+ template: `
5834
+ <!-- Header -->
5835
+ <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5836
+ <div class="flex-1 min-w-0">
5837
+ <ng-content select="[clx-drawer-title]" />
5838
+ </div>
5839
+ @if (showClose()) {
5840
+ <button
5841
+ clx-button
5842
+ variant="ghost"
5843
+ color="zinc"
5844
+ shape="circle"
5845
+ size="sm"
5846
+ icon="close"
5847
+ [iconOnly]="true"
5848
+ class="shrink-0 -mr-1"
5849
+ (click)="_close()">
5850
+ </button>
5851
+ }
5852
+ </div>
5853
+
5854
+ <!-- Body -->
5855
+ <div class="flex-1 overflow-y-auto px-6 py-5">
5856
+ <ng-content />
5857
+ </div>
5858
+
5859
+ <!-- Footer -->
5860
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5861
+ @if (_hasBuiltInButtons()) {
5862
+ @if (showCancelButton()) {
5863
+ <button
5864
+ clx-button
5865
+ [color]="_cancelColor()"
5866
+ [variant]="_cancelVariant()"
5867
+ size="md"
5868
+ (click)="cancelClick.emit()">
5869
+ {{ _cancelText() }}
5870
+ </button>
5871
+ }
5872
+ <button
5873
+ clx-button
5874
+ [color]="_confirmColor()"
5875
+ [variant]="_confirmVariant()"
5876
+ size="md"
5877
+ (click)="confirmClick.emit()">
5878
+ {{ _confirmText() }}
5879
+ </button>
5880
+ } @else {
5881
+ <ng-content select="[clx-drawer-footer]" />
5882
+ }
5883
+ </div>
5884
5884
  `,
5885
5885
  encapsulation: ViewEncapsulation.None,
5886
5886
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -5903,66 +5903,66 @@ class ClxModalComponent {
5903
5903
  _hostClass = computed(() => `${MODAL_CONTAINER_CLASS} ${MODAL_SIZE_MAP[this._resolvedSize()]} max-h-[90vh]`, ...(ngDevMode ? [{ debugName: "_hostClass" }] : /* istanbul ignore next */ []));
5904
5904
  _hasBuiltInButtons = computed(() => !!this.confirmButton(), ...(ngDevMode ? [{ debugName: "_hasBuiltInButtons" }] : /* istanbul ignore next */ []));
5905
5905
  _confirmText = computed(() => this.confirmButton()?.text ?? 'Aceptar', ...(ngDevMode ? [{ debugName: "_confirmText" }] : /* istanbul ignore next */ []));
5906
- _confirmColor = computed(() => this.confirmButton()?.color ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_confirmColor" }] : /* istanbul ignore next */ []));
5907
- _confirmVariant = computed(() => this.confirmButton()?.variant ?? 'solid', ...(ngDevMode ? [{ debugName: "_confirmVariant" }] : /* istanbul ignore next */ []));
5908
- _cancelText = computed(() => this.cancelButton()?.text ?? 'Cancelar', ...(ngDevMode ? [{ debugName: "_cancelText" }] : /* istanbul ignore next */ []));
5909
- _cancelColor = computed(() => this.cancelButton()?.color ?? 'slate', ...(ngDevMode ? [{ debugName: "_cancelColor" }] : /* istanbul ignore next */ []));
5910
- _cancelVariant = computed(() => this.cancelButton()?.variant ?? 'ghost', ...(ngDevMode ? [{ debugName: "_cancelVariant" }] : /* istanbul ignore next */ []));
5911
- _close() {
5912
- this._modalRef?.close();
5913
- }
5914
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5915
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxModalComponent, isStandalone: true, selector: "clx-modal", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, confirmButton: { classPropertyName: "confirmButton", publicName: "confirmButton", isSignal: true, isRequired: false, transformFunction: null }, cancelButton: { classPropertyName: "cancelButton", publicName: "cancelButton", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmClick: "confirmClick", cancelClick: "cancelClick" }, host: { properties: { "class": "_hostClass()" } }, ngImport: i0, template: `
5916
- <!-- Header -->
5917
- <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5918
- <div class="flex-1 min-w-0 text-clx-text-label">
5919
- <ng-content select="[clx-modal-title]" />
5920
- </div>
5921
- @if (showClose()) {
5922
- <button
5923
- clx-button
5924
- variant="ghost"
5925
- color="zinc"
5926
- shape="circle"
5927
- size="md"
5928
- icon="close"
5929
- [iconOnly]="true"
5930
- class="shrink-0 -mr-1"
5931
- (click)="_close()">
5932
- </button>
5933
- }
5934
- </div>
5935
-
5936
- <!-- Body -->
5937
- <div cdkScrollable class="flex-1 min-h-0 overflow-y-auto px-6 py-5">
5938
- <ng-content />
5939
- </div>
5940
-
5941
- <!-- Footer -->
5942
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5943
- @if (_hasBuiltInButtons()) {
5944
- @if (showCancelButton()) {
5945
- <button
5946
- clx-button
5947
- [color]="_cancelColor()"
5948
- [variant]="_cancelVariant()"
5949
- size="md"
5950
- (click)="cancelClick.emit()">
5951
- {{ _cancelText() }}
5952
- </button>
5953
- }
5954
- <button
5955
- clx-button
5956
- [color]="_confirmColor()"
5957
- [variant]="_confirmVariant()"
5958
- size="md"
5959
- (click)="confirmClick.emit()">
5960
- {{ _confirmText() }}
5961
- </button>
5962
- } @else {
5963
- <ng-content select="[clx-modal-footer]" />
5964
- }
5965
- </div>
5906
+ _confirmColor = computed(() => this.confirmButton()?.color ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_confirmColor" }] : /* istanbul ignore next */ []));
5907
+ _confirmVariant = computed(() => this.confirmButton()?.variant ?? 'solid', ...(ngDevMode ? [{ debugName: "_confirmVariant" }] : /* istanbul ignore next */ []));
5908
+ _cancelText = computed(() => this.cancelButton()?.text ?? 'Cancelar', ...(ngDevMode ? [{ debugName: "_cancelText" }] : /* istanbul ignore next */ []));
5909
+ _cancelColor = computed(() => this.cancelButton()?.color ?? 'slate', ...(ngDevMode ? [{ debugName: "_cancelColor" }] : /* istanbul ignore next */ []));
5910
+ _cancelVariant = computed(() => this.cancelButton()?.variant ?? 'ghost', ...(ngDevMode ? [{ debugName: "_cancelVariant" }] : /* istanbul ignore next */ []));
5911
+ _close() {
5912
+ this._modalRef?.close();
5913
+ }
5914
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5915
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxModalComponent, isStandalone: true, selector: "clx-modal", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, confirmButton: { classPropertyName: "confirmButton", publicName: "confirmButton", isSignal: true, isRequired: false, transformFunction: null }, cancelButton: { classPropertyName: "cancelButton", publicName: "cancelButton", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmClick: "confirmClick", cancelClick: "cancelClick" }, host: { properties: { "class": "_hostClass()" } }, ngImport: i0, template: `
5916
+ <!-- Header -->
5917
+ <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5918
+ <div class="flex-1 min-w-0 text-clx-text-label">
5919
+ <ng-content select="[clx-modal-title]" />
5920
+ </div>
5921
+ @if (showClose()) {
5922
+ <button
5923
+ clx-button
5924
+ variant="ghost"
5925
+ color="zinc"
5926
+ shape="circle"
5927
+ size="md"
5928
+ icon="close"
5929
+ [iconOnly]="true"
5930
+ class="shrink-0 -mr-1"
5931
+ (click)="_close()">
5932
+ </button>
5933
+ }
5934
+ </div>
5935
+
5936
+ <!-- Body -->
5937
+ <div cdkScrollable class="flex-1 min-h-0 overflow-y-auto px-6 py-5">
5938
+ <ng-content />
5939
+ </div>
5940
+
5941
+ <!-- Footer -->
5942
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5943
+ @if (_hasBuiltInButtons()) {
5944
+ @if (showCancelButton()) {
5945
+ <button
5946
+ clx-button
5947
+ [color]="_cancelColor()"
5948
+ [variant]="_cancelVariant()"
5949
+ size="md"
5950
+ (click)="cancelClick.emit()">
5951
+ {{ _cancelText() }}
5952
+ </button>
5953
+ }
5954
+ <button
5955
+ clx-button
5956
+ [color]="_confirmColor()"
5957
+ [variant]="_confirmVariant()"
5958
+ size="md"
5959
+ (click)="confirmClick.emit()">
5960
+ {{ _confirmText() }}
5961
+ </button>
5962
+ } @else {
5963
+ <ng-content select="[clx-modal-footer]" />
5964
+ }
5965
+ </div>
5966
5966
  `, isInline: true, dependencies: [{ 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: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1$1.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5967
5967
  }
5968
5968
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalComponent, decorators: [{
@@ -5971,57 +5971,57 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5971
5971
  selector: 'clx-modal',
5972
5972
  standalone: true,
5973
5973
  imports: [ClxButtonComponent, ScrollingModule],
5974
- template: `
5975
- <!-- Header -->
5976
- <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5977
- <div class="flex-1 min-w-0 text-clx-text-label">
5978
- <ng-content select="[clx-modal-title]" />
5979
- </div>
5980
- @if (showClose()) {
5981
- <button
5982
- clx-button
5983
- variant="ghost"
5984
- color="zinc"
5985
- shape="circle"
5986
- size="md"
5987
- icon="close"
5988
- [iconOnly]="true"
5989
- class="shrink-0 -mr-1"
5990
- (click)="_close()">
5991
- </button>
5992
- }
5993
- </div>
5994
-
5995
- <!-- Body -->
5996
- <div cdkScrollable class="flex-1 min-h-0 overflow-y-auto px-6 py-5">
5997
- <ng-content />
5998
- </div>
5999
-
6000
- <!-- Footer -->
6001
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
6002
- @if (_hasBuiltInButtons()) {
6003
- @if (showCancelButton()) {
6004
- <button
6005
- clx-button
6006
- [color]="_cancelColor()"
6007
- [variant]="_cancelVariant()"
6008
- size="md"
6009
- (click)="cancelClick.emit()">
6010
- {{ _cancelText() }}
6011
- </button>
6012
- }
6013
- <button
6014
- clx-button
6015
- [color]="_confirmColor()"
6016
- [variant]="_confirmVariant()"
6017
- size="md"
6018
- (click)="confirmClick.emit()">
6019
- {{ _confirmText() }}
6020
- </button>
6021
- } @else {
6022
- <ng-content select="[clx-modal-footer]" />
6023
- }
6024
- </div>
5974
+ template: `
5975
+ <!-- Header -->
5976
+ <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5977
+ <div class="flex-1 min-w-0 text-clx-text-label">
5978
+ <ng-content select="[clx-modal-title]" />
5979
+ </div>
5980
+ @if (showClose()) {
5981
+ <button
5982
+ clx-button
5983
+ variant="ghost"
5984
+ color="zinc"
5985
+ shape="circle"
5986
+ size="md"
5987
+ icon="close"
5988
+ [iconOnly]="true"
5989
+ class="shrink-0 -mr-1"
5990
+ (click)="_close()">
5991
+ </button>
5992
+ }
5993
+ </div>
5994
+
5995
+ <!-- Body -->
5996
+ <div cdkScrollable class="flex-1 min-h-0 overflow-y-auto px-6 py-5">
5997
+ <ng-content />
5998
+ </div>
5999
+
6000
+ <!-- Footer -->
6001
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
6002
+ @if (_hasBuiltInButtons()) {
6003
+ @if (showCancelButton()) {
6004
+ <button
6005
+ clx-button
6006
+ [color]="_cancelColor()"
6007
+ [variant]="_cancelVariant()"
6008
+ size="md"
6009
+ (click)="cancelClick.emit()">
6010
+ {{ _cancelText() }}
6011
+ </button>
6012
+ }
6013
+ <button
6014
+ clx-button
6015
+ [color]="_confirmColor()"
6016
+ [variant]="_confirmVariant()"
6017
+ size="md"
6018
+ (click)="confirmClick.emit()">
6019
+ {{ _confirmText() }}
6020
+ </button>
6021
+ } @else {
6022
+ <ng-content select="[clx-modal-footer]" />
6023
+ }
6024
+ </div>
6025
6025
  `,
6026
6026
  encapsulation: ViewEncapsulation.None,
6027
6027
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -11778,9 +11778,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
11778
11778
  }] } });
11779
11779
 
11780
11780
  class ClxAppLayoutComponent {
11781
- _zone = inject(NgZone);
11782
- _document = inject(DOCUMENT);
11783
- _el = inject(ElementRef);
11781
+ _zone;
11782
+ _document;
11784
11783
  activeColor = input(undefined, ...(ngDevMode ? [{ debugName: "activeColor" }] : /* istanbul ignore next */ []));
11785
11784
  _sidebarOpen = signal(false, ...(ngDevMode ? [{ debugName: "_sidebarOpen" }] : /* istanbul ignore next */ []));
11786
11785
  collapsed = signal(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
@@ -11788,41 +11787,42 @@ class ClxAppLayoutComponent {
11788
11787
  _isMobile = signal(true, ...(ngDevMode ? [{ debugName: "_isMobile" }] : /* istanbul ignore next */ []));
11789
11788
  collapsedChange = output();
11790
11789
  expandedChange = output();
11790
+ constructor() {
11791
+ const zone = inject(NgZone, { optional: true });
11792
+ this._zone = zone ?? { run: (fn) => fn(), runOutsideAngular: (fn) => fn() };
11793
+ this._document = inject(DOCUMENT);
11794
+ }
11791
11795
  _showBackdrop = computed(() => this._sidebarOpen() && this._isMobile(), ...(ngDevMode ? [{ debugName: "_showBackdrop" }] : /* istanbul ignore next */ []));
11792
11796
  _isExpanded = computed(() => !this.collapsed() || this._hovered(), ...(ngDevMode ? [{ debugName: "_isExpanded" }] : /* istanbul ignore next */ []));
11793
11797
  _sidebarCls = computed(() => {
11794
- const mobile = this._isMobile();
11795
- const open = this._sidebarOpen();
11796
- const collapsed = this.collapsed();
11798
+ const isDesktopCollapsed = this.collapsed() && !this._isMobile();
11797
11799
  const hovered = this._hovered();
11798
- const base = 'flex flex-col bg-clx-surface border-r border-clx-border overflow-hidden shrink-0';
11799
- if (mobile) {
11800
- // Mobile: fixed overlay, slides in/out — no width transition needed
11801
- const visible = open ? 'translate-x-0' : '-translate-x-full';
11802
- return `fixed inset-y-0 left-0 z-30 w-72 ${base} transition-transform duration-300 ease-in-out ${visible}`;
11803
- }
11804
- // Desktop: in-flow (relative), width-only transition — no fixed, no transform
11805
- const width = collapsed && !hovered ? 'w-20' : 'w-72';
11806
- const shadow = collapsed && hovered ? 'shadow-2xl' : '';
11807
- return `relative z-30 ${width} ${base} transition-[width] duration-300 ease-in-out ${shadow}`;
11800
+ let width;
11801
+ if (isDesktopCollapsed && hovered) {
11802
+ width = 'w-72';
11803
+ }
11804
+ else if (isDesktopCollapsed) {
11805
+ width = 'w-20';
11806
+ }
11807
+ else {
11808
+ width = 'w-72';
11809
+ }
11810
+ const isOverlay = isDesktopCollapsed && hovered;
11811
+ return [
11812
+ `fixed inset-y-0 left-0 z-30 ${width} flex flex-col`,
11813
+ 'bg-clx-surface border-r border-clx-border overflow-hidden shrink-0',
11814
+ 'transition-[width,transform] duration-300 ease-in-out',
11815
+ isOverlay ? 'shadow-2xl' : '',
11816
+ 'lg:transition-[width] lg:relative lg:inset-auto lg:z-30 lg:translate-x-0',
11817
+ (!this._isMobile() || this._sidebarOpen()) ? 'translate-x-0' : '-translate-x-full',
11818
+ ].join(' ');
11808
11819
  }, ...(ngDevMode ? [{ debugName: "_sidebarCls" }] : /* istanbul ignore next */ []));
11809
- _nativeSetTimeout;
11810
- _nativeClearTimeout;
11811
11820
  _mql;
11812
11821
  _mqlListener;
11813
- _resizeTimer = null;
11814
- _resizeListener;
11815
- _mouseEnterFn;
11816
- _mouseLeaveFn;
11817
- _sidebarEl = null;
11818
11822
  ngOnInit() {
11819
11823
  const win = this._document.defaultView;
11820
11824
  if (!win)
11821
11825
  return;
11822
- // Use native (non-Zone-patched) timers so the resize debounce
11823
- // does not trigger Angular change detection cycles
11824
- this._nativeSetTimeout = win.__zone_symbol__setTimeout ?? win.setTimeout.bind(win);
11825
- this._nativeClearTimeout = win.__zone_symbol__clearTimeout ?? win.clearTimeout.bind(win);
11826
11826
  this._mql = win.matchMedia('(min-width: 1024px)');
11827
11827
  this._isMobile.set(!this._mql.matches);
11828
11828
  if (this._mql.matches)
@@ -11841,58 +11841,9 @@ class ClxAppLayoutComponent {
11841
11841
  });
11842
11842
  };
11843
11843
  this._mql.addEventListener('change', this._mqlListener);
11844
- this._zone.runOutsideAngular(() => {
11845
- // Suppress all transitions during resize using native (non-Zone-patched) timer
11846
- this._resizeListener = () => {
11847
- this._document.documentElement.classList.add('clx-resizing');
11848
- if (this._resizeTimer !== null)
11849
- this._nativeClearTimeout(this._resizeTimer);
11850
- this._resizeTimer = this._nativeSetTimeout(() => {
11851
- this._document.documentElement.classList.remove('clx-resizing');
11852
- this._resizeTimer = null;
11853
- }, 150);
11854
- };
11855
- win.addEventListener('resize', this._resizeListener, { passive: true });
11856
- // Sidebar hover outside NgZone — only enters zone when state actually changes
11857
- this._mouseEnterFn = () => {
11858
- if (!this.collapsed() || this._isMobile())
11859
- return;
11860
- if (!this._hovered()) {
11861
- this._zone.run(() => {
11862
- this._hovered.set(true);
11863
- this.expandedChange.emit(true);
11864
- });
11865
- }
11866
- };
11867
- this._mouseLeaveFn = () => {
11868
- if (this._hovered()) {
11869
- this._zone.run(() => {
11870
- this._hovered.set(false);
11871
- this.expandedChange.emit(false);
11872
- });
11873
- }
11874
- };
11875
- // Attach after a tick so the aside is rendered (native timer — no CD)
11876
- this._nativeSetTimeout(() => {
11877
- const host = this._el.nativeElement;
11878
- this._sidebarEl = host.querySelector('aside');
11879
- if (this._sidebarEl) {
11880
- this._sidebarEl.addEventListener('mouseenter', this._mouseEnterFn, { passive: true });
11881
- this._sidebarEl.addEventListener('mouseleave', this._mouseLeaveFn, { passive: true });
11882
- }
11883
- }, 0);
11884
- });
11885
11844
  }
11886
11845
  ngOnDestroy() {
11887
11846
  this._mql?.removeEventListener('change', this._mqlListener);
11888
- const win = this._document.defaultView;
11889
- win?.removeEventListener('resize', this._resizeListener);
11890
- if (this._resizeTimer !== null)
11891
- this._nativeClearTimeout?.(this._resizeTimer);
11892
- if (this._sidebarEl) {
11893
- this._sidebarEl.removeEventListener('mouseenter', this._mouseEnterFn);
11894
- this._sidebarEl.removeEventListener('mouseleave', this._mouseLeaveFn);
11895
- }
11896
11847
  }
11897
11848
  _toggleCollapsed() {
11898
11849
  const next = !this.collapsed();
@@ -11900,122 +11851,144 @@ class ClxAppLayoutComponent {
11900
11851
  this._hovered.set(false);
11901
11852
  this.collapsedChange.emit(next);
11902
11853
  }
11854
+ _onSidebarEnter() {
11855
+ if (this.collapsed() && !this._isMobile()) {
11856
+ this._hovered.set(true);
11857
+ this.expandedChange.emit(true);
11858
+ }
11859
+ }
11860
+ _onSidebarLeave() {
11861
+ if (this._hovered()) {
11862
+ this._hovered.set(false);
11863
+ this.expandedChange.emit(false);
11864
+ }
11865
+ }
11903
11866
  toggleSidebar() { this._sidebarOpen.update(v => !v); }
11904
11867
  openSidebar() { this._sidebarOpen.set(true); }
11905
11868
  closeSidebar() { this._sidebarOpen.set(false); }
11906
11869
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAppLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11907
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxAppLayoutComponent, isStandalone: true, selector: "clx-app-layout", inputs: { activeColor: { classPropertyName: "activeColor", publicName: "activeColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsedChange: "collapsedChange", expandedChange: "expandedChange" }, host: { classAttribute: "flex h-screen overflow-hidden" }, ngImport: i0, template: `
11908
- <!-- Mobile backdrop -->
11909
- @if (_showBackdrop()) {
11910
- <div
11911
- class="fixed inset-0 z-20 bg-slate-900/60"
11912
- aria-hidden="true"
11913
- (click)="closeSidebar()">
11914
- </div>
11915
- }
11916
-
11917
- <!-- Sidebar -->
11918
- <aside
11919
- [class]="_sidebarCls()"
11920
- aria-label="Navegación principal">
11921
-
11922
- <div class="border-b border-slate-700/50 shrink-0 overflow-hidden">
11923
- <ng-content select="clx-brand"></ng-content>
11924
- </div>
11925
-
11926
- <div class="flex flex-col flex-1 min-h-0 overflow-y-auto">
11927
- <ng-content select="[clx-sidebar]"></ng-content>
11928
- </div>
11929
- </aside>
11930
-
11931
- <!-- Right panel -->
11932
- <div class="flex-1 flex flex-col overflow-hidden min-w-0" style="contain:layout paint">
11933
-
11934
- <header class="h-16 shrink-0 bg-clx-surface border-b border-clx-border flex items-center z-10 px-4 gap-3">
11935
- <button
11936
- type="button"
11937
- class="hidden lg:flex items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11938
- (click)="_toggleCollapsed()">
11939
- <span clx-icon [name]="collapsed() ? 'menu_open' : 'menu'" size="sm"></span>
11940
- </button>
11941
- <button
11942
- type="button"
11943
- class="flex lg:hidden items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11944
- (click)="toggleSidebar()">
11945
- <span clx-icon name="menu" size="sm"></span>
11946
- </button>
11947
-
11948
- <ng-content select="[clx-header]"></ng-content>
11949
- </header>
11950
-
11951
- <main class="flex-1 overflow-y-auto bg-clx-bg" style="contain:layout paint">
11952
- <ng-content></ng-content>
11953
- </main>
11954
-
11955
- </div>
11956
- `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
11870
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxAppLayoutComponent, isStandalone: true, selector: "clx-app-layout", inputs: { activeColor: { classPropertyName: "activeColor", publicName: "activeColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsedChange: "collapsedChange", expandedChange: "expandedChange" }, host: { classAttribute: "flex h-screen overflow-hidden" }, ngImport: i0, template: `
11871
+ <!-- Mobile backdrop -->
11872
+ @if (_showBackdrop()) {
11873
+ <div
11874
+ class="fixed inset-0 z-20 bg-slate-900/60"
11875
+ aria-hidden="true"
11876
+ (click)="closeSidebar()">
11877
+ </div>
11878
+ }
11879
+
11880
+ <!-- Sidebar -->
11881
+ <aside
11882
+ [class]="_sidebarCls()"
11883
+ (mouseenter)="_onSidebarEnter()"
11884
+ (mouseleave)="_onSidebarLeave()"
11885
+ aria-label="Navegación principal">
11886
+
11887
+ <!-- Brand slot -->
11888
+ <div class="border-b border-slate-700/50 shrink-0 overflow-hidden">
11889
+ <ng-content select="clx-brand"></ng-content>
11890
+ </div>
11891
+
11892
+ <div class="flex flex-col flex-1 min-h-0">
11893
+ <ng-content select="[clx-sidebar]"></ng-content>
11894
+ </div>
11895
+ </aside>
11896
+
11897
+ <!-- Right panel -->
11898
+ <div class="flex-1 flex flex-col overflow-hidden min-w-0">
11899
+
11900
+ <header class="h-16 shrink-0 bg-clx-surface border-b border-clx-border flex items-center z-10 px-4 gap-3">
11901
+ <!-- Toggle button desktop -->
11902
+ <button
11903
+ type="button"
11904
+ class="hidden lg:flex items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11905
+ (click)="_toggleCollapsed()">
11906
+ <span clx-icon [name]="collapsed() ? 'menu_open' : 'menu'" size="sm"></span>
11907
+ </button>
11908
+ <!-- Toggle button mobile -->
11909
+ <button
11910
+ type="button"
11911
+ class="flex lg:hidden items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11912
+ (click)="toggleSidebar()">
11913
+ <span clx-icon name="menu" size="sm"></span>
11914
+ </button>
11915
+
11916
+ <ng-content select="[clx-header]"></ng-content>
11917
+ </header>
11918
+
11919
+ <main cdkScrollable class="flex-1 overflow-y-auto bg-clx-bg">
11920
+ <ng-content></ng-content>
11921
+ </main>
11922
+
11923
+ </div>
11924
+ `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "directive", type: CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
11957
11925
  }
11958
11926
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAppLayoutComponent, decorators: [{
11959
11927
  type: Component,
11960
11928
  args: [{
11961
11929
  selector: 'clx-app-layout',
11962
11930
  standalone: true,
11963
- imports: [ClxIconComponent],
11964
- template: `
11965
- <!-- Mobile backdrop -->
11966
- @if (_showBackdrop()) {
11967
- <div
11968
- class="fixed inset-0 z-20 bg-slate-900/60"
11969
- aria-hidden="true"
11970
- (click)="closeSidebar()">
11971
- </div>
11972
- }
11973
-
11974
- <!-- Sidebar -->
11975
- <aside
11976
- [class]="_sidebarCls()"
11977
- aria-label="Navegación principal">
11978
-
11979
- <div class="border-b border-slate-700/50 shrink-0 overflow-hidden">
11980
- <ng-content select="clx-brand"></ng-content>
11981
- </div>
11982
-
11983
- <div class="flex flex-col flex-1 min-h-0 overflow-y-auto">
11984
- <ng-content select="[clx-sidebar]"></ng-content>
11985
- </div>
11986
- </aside>
11987
-
11988
- <!-- Right panel -->
11989
- <div class="flex-1 flex flex-col overflow-hidden min-w-0" style="contain:layout paint">
11990
-
11991
- <header class="h-16 shrink-0 bg-clx-surface border-b border-clx-border flex items-center z-10 px-4 gap-3">
11992
- <button
11993
- type="button"
11994
- class="hidden lg:flex items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11995
- (click)="_toggleCollapsed()">
11996
- <span clx-icon [name]="collapsed() ? 'menu_open' : 'menu'" size="sm"></span>
11997
- </button>
11998
- <button
11999
- type="button"
12000
- class="flex lg:hidden items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
12001
- (click)="toggleSidebar()">
12002
- <span clx-icon name="menu" size="sm"></span>
12003
- </button>
12004
-
12005
- <ng-content select="[clx-header]"></ng-content>
12006
- </header>
12007
-
12008
- <main class="flex-1 overflow-y-auto bg-clx-bg" style="contain:layout paint">
12009
- <ng-content></ng-content>
12010
- </main>
12011
-
12012
- </div>
11931
+ imports: [ClxIconComponent, CdkScrollable],
11932
+ template: `
11933
+ <!-- Mobile backdrop -->
11934
+ @if (_showBackdrop()) {
11935
+ <div
11936
+ class="fixed inset-0 z-20 bg-slate-900/60"
11937
+ aria-hidden="true"
11938
+ (click)="closeSidebar()">
11939
+ </div>
11940
+ }
11941
+
11942
+ <!-- Sidebar -->
11943
+ <aside
11944
+ [class]="_sidebarCls()"
11945
+ (mouseenter)="_onSidebarEnter()"
11946
+ (mouseleave)="_onSidebarLeave()"
11947
+ aria-label="Navegación principal">
11948
+
11949
+ <!-- Brand slot -->
11950
+ <div class="border-b border-slate-700/50 shrink-0 overflow-hidden">
11951
+ <ng-content select="clx-brand"></ng-content>
11952
+ </div>
11953
+
11954
+ <div class="flex flex-col flex-1 min-h-0">
11955
+ <ng-content select="[clx-sidebar]"></ng-content>
11956
+ </div>
11957
+ </aside>
11958
+
11959
+ <!-- Right panel -->
11960
+ <div class="flex-1 flex flex-col overflow-hidden min-w-0">
11961
+
11962
+ <header class="h-16 shrink-0 bg-clx-surface border-b border-clx-border flex items-center z-10 px-4 gap-3">
11963
+ <!-- Toggle button desktop -->
11964
+ <button
11965
+ type="button"
11966
+ class="hidden lg:flex items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11967
+ (click)="_toggleCollapsed()">
11968
+ <span clx-icon [name]="collapsed() ? 'menu_open' : 'menu'" size="sm"></span>
11969
+ </button>
11970
+ <!-- Toggle button mobile -->
11971
+ <button
11972
+ type="button"
11973
+ class="flex lg:hidden items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11974
+ (click)="toggleSidebar()">
11975
+ <span clx-icon name="menu" size="sm"></span>
11976
+ </button>
11977
+
11978
+ <ng-content select="[clx-header]"></ng-content>
11979
+ </header>
11980
+
11981
+ <main cdkScrollable class="flex-1 overflow-y-auto bg-clx-bg">
11982
+ <ng-content></ng-content>
11983
+ </main>
11984
+
11985
+ </div>
12013
11986
  `,
12014
11987
  encapsulation: ViewEncapsulation.None,
12015
11988
  changeDetection: ChangeDetectionStrategy.OnPush,
12016
11989
  host: { class: 'flex h-screen overflow-hidden' },
12017
11990
  }]
12018
- }], propDecorators: { activeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeColor", required: false }] }], collapsedChange: [{ type: i0.Output, args: ["collapsedChange"] }], expandedChange: [{ type: i0.Output, args: ["expandedChange"] }] } });
11991
+ }], ctorParameters: () => [], propDecorators: { activeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeColor", required: false }] }], collapsedChange: [{ type: i0.Output, args: ["collapsedChange"] }], expandedChange: [{ type: i0.Output, args: ["expandedChange"] }] } });
12019
11992
 
12020
11993
  class ClxBrandComponent {
12021
11994
  logo = input.required(...(ngDevMode ? [{ debugName: "logo" }] : /* istanbul ignore next */ []));
@@ -12101,36 +12074,36 @@ class ClxMenuItemComponent {
12101
12074
  return `${NAV_ITEM_BASE} ${this._geom()} ${t.textSubtle} font-semibold border-l-2 ${t.borderLight} !pl-[22px] ${t.hoverBgMuted}`;
12102
12075
  }, ...(ngDevMode ? [{ debugName: "_activeCls" }] : /* istanbul ignore next */ []));
12103
12076
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxMenuItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12104
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxMenuItemComponent, isStandalone: true, selector: "clx-menu-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, nested: { classPropertyName: "nested", publicName: "nested", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, host: { classAttribute: "block" }, ngImport: i0, template: `
12105
- @if (routerLink()) {
12106
- <a
12107
- [routerLink]="routerLink()"
12108
- routerLinkActive
12109
- #rla="routerLinkActive"
12110
- [routerLinkActiveOptions]="routerLinkActiveOptions()"
12111
- [class]="rla.isActive ? _activeCls() : _idleCls()"
12112
- [title]="collapsed() ? label() : ''">
12113
- @if (icon()) {
12114
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12115
- }
12116
- @if (!collapsed()) {
12117
- <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12118
- }
12119
- </a>
12120
- } @else {
12121
- <button
12122
- type="button"
12123
- [class]="active() ? _activeCls() : _idleCls()"
12124
- [title]="collapsed() ? label() : ''"
12125
- (click)="itemClick.emit()">
12126
- @if (icon()) {
12127
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12128
- }
12129
- @if (!collapsed()) {
12130
- <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12131
- }
12132
- </button>
12133
- }
12077
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxMenuItemComponent, isStandalone: true, selector: "clx-menu-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, nested: { classPropertyName: "nested", publicName: "nested", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, host: { classAttribute: "block" }, ngImport: i0, template: `
12078
+ @if (routerLink()) {
12079
+ <a
12080
+ [routerLink]="routerLink()"
12081
+ routerLinkActive
12082
+ #rla="routerLinkActive"
12083
+ [routerLinkActiveOptions]="routerLinkActiveOptions()"
12084
+ [class]="rla.isActive ? _activeCls() : _idleCls()"
12085
+ [title]="collapsed() ? label() : ''">
12086
+ @if (icon()) {
12087
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12088
+ }
12089
+ @if (!collapsed()) {
12090
+ <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12091
+ }
12092
+ </a>
12093
+ } @else {
12094
+ <button
12095
+ type="button"
12096
+ [class]="active() ? _activeCls() : _idleCls()"
12097
+ [title]="collapsed() ? label() : ''"
12098
+ (click)="itemClick.emit()">
12099
+ @if (icon()) {
12100
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12101
+ }
12102
+ @if (!collapsed()) {
12103
+ <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12104
+ }
12105
+ </button>
12106
+ }
12134
12107
  `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
12135
12108
  }
12136
12109
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxMenuItemComponent, decorators: [{
@@ -12139,36 +12112,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12139
12112
  selector: 'clx-menu-item',
12140
12113
  standalone: true,
12141
12114
  imports: [ClxIconComponent, RouterLink, RouterLinkActive],
12142
- template: `
12143
- @if (routerLink()) {
12144
- <a
12145
- [routerLink]="routerLink()"
12146
- routerLinkActive
12147
- #rla="routerLinkActive"
12148
- [routerLinkActiveOptions]="routerLinkActiveOptions()"
12149
- [class]="rla.isActive ? _activeCls() : _idleCls()"
12150
- [title]="collapsed() ? label() : ''">
12151
- @if (icon()) {
12152
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12153
- }
12154
- @if (!collapsed()) {
12155
- <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12156
- }
12157
- </a>
12158
- } @else {
12159
- <button
12160
- type="button"
12161
- [class]="active() ? _activeCls() : _idleCls()"
12162
- [title]="collapsed() ? label() : ''"
12163
- (click)="itemClick.emit()">
12164
- @if (icon()) {
12165
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12166
- }
12167
- @if (!collapsed()) {
12168
- <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12169
- }
12170
- </button>
12171
- }
12115
+ template: `
12116
+ @if (routerLink()) {
12117
+ <a
12118
+ [routerLink]="routerLink()"
12119
+ routerLinkActive
12120
+ #rla="routerLinkActive"
12121
+ [routerLinkActiveOptions]="routerLinkActiveOptions()"
12122
+ [class]="rla.isActive ? _activeCls() : _idleCls()"
12123
+ [title]="collapsed() ? label() : ''">
12124
+ @if (icon()) {
12125
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12126
+ }
12127
+ @if (!collapsed()) {
12128
+ <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12129
+ }
12130
+ </a>
12131
+ } @else {
12132
+ <button
12133
+ type="button"
12134
+ [class]="active() ? _activeCls() : _idleCls()"
12135
+ [title]="collapsed() ? label() : ''"
12136
+ (click)="itemClick.emit()">
12137
+ @if (icon()) {
12138
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12139
+ }
12140
+ @if (!collapsed()) {
12141
+ <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12142
+ }
12143
+ </button>
12144
+ }
12172
12145
  `,
12173
12146
  encapsulation: ViewEncapsulation.None,
12174
12147
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -12224,61 +12197,61 @@ class ClxMenuComponent {
12224
12197
  this._isExpanded.update(v => !v);
12225
12198
  }
12226
12199
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12227
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxMenuComponent, isStandalone: true, selector: "clx-menu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, viewQueries: [{ propertyName: "_rla", first: true, predicate: RouterLinkActive, descendants: true }], ngImport: i0, template: `
12228
- @if (routerLink()) {
12229
- <a
12230
- [routerLink]="routerLink()"
12231
- routerLinkActive
12232
- #rla="routerLinkActive"
12233
- [routerLinkActiveOptions]="routerLinkActiveOptions()"
12234
- [class]="_buttonCls(rla.isActive)"
12235
- [title]="collapsed() ? label() : ''"
12236
- (click)="$event.preventDefault(); !collapsed() && _toggle()">
12237
- @if (icon()) {
12238
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12239
- }
12240
- @if (!collapsed()) {
12241
- <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12242
- <span
12243
- clx-icon
12244
- name="expand_more"
12245
- size="sm"
12246
- class="shrink-0 transition-transform duration-300"
12247
- [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12248
- </span>
12249
- }
12250
- </a>
12251
- } @else {
12252
- <button
12253
- type="button"
12254
- [class]="_buttonCls(active())"
12255
- [title]="collapsed() ? label() : ''"
12256
- (click)="!collapsed() && _toggle()">
12257
- @if (icon()) {
12258
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12259
- }
12260
- @if (!collapsed()) {
12261
- <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12262
- <span
12263
- clx-icon
12264
- name="expand_more"
12265
- size="sm"
12266
- class="shrink-0 transition-transform duration-300"
12267
- [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12268
- </span>
12269
- }
12270
- </button>
12271
- }
12272
-
12273
- <div
12274
- [class]="NAV_CHILDREN_WRAPPER"
12275
- [style.grid-template-rows]="(!collapsed() && _isExpanded()) ? '1fr' : '0fr'">
12276
- <div [class]="NAV_CHILDREN_INNER">
12277
- <div [class]="NAV_CHILDREN_LIST">
12278
- <ng-content></ng-content>
12279
- </div>
12280
- </div>
12281
- </div>
12200
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxMenuComponent, isStandalone: true, selector: "clx-menu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, viewQueries: [{ propertyName: "_rla", first: true, predicate: RouterLinkActive, descendants: true }], ngImport: i0, template: `
12201
+ @if (routerLink()) {
12202
+ <a
12203
+ [routerLink]="routerLink()"
12204
+ routerLinkActive
12205
+ #rla="routerLinkActive"
12206
+ [routerLinkActiveOptions]="routerLinkActiveOptions()"
12207
+ [class]="_buttonCls(rla.isActive)"
12208
+ [title]="collapsed() ? label() : ''"
12209
+ (click)="$event.preventDefault(); !collapsed() && _toggle()">
12210
+ @if (icon()) {
12211
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12212
+ }
12213
+ @if (!collapsed()) {
12214
+ <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12215
+ <span
12216
+ clx-icon
12217
+ name="expand_more"
12218
+ size="sm"
12219
+ class="shrink-0 transition-transform duration-300"
12220
+ [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12221
+ </span>
12222
+ }
12223
+ </a>
12224
+ } @else {
12225
+ <button
12226
+ type="button"
12227
+ [class]="_buttonCls(active())"
12228
+ [title]="collapsed() ? label() : ''"
12229
+ (click)="!collapsed() && _toggle()">
12230
+ @if (icon()) {
12231
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12232
+ }
12233
+ @if (!collapsed()) {
12234
+ <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12235
+ <span
12236
+ clx-icon
12237
+ name="expand_more"
12238
+ size="sm"
12239
+ class="shrink-0 transition-transform duration-300"
12240
+ [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12241
+ </span>
12242
+ }
12243
+ </button>
12244
+ }
12245
+
12246
+ <div
12247
+ [class]="NAV_CHILDREN_WRAPPER"
12248
+ [style.grid-template-rows]="(!collapsed() && _isExpanded()) ? '1fr' : '0fr'">
12249
+ <div [class]="NAV_CHILDREN_INNER">
12250
+ <div [class]="NAV_CHILDREN_LIST">
12251
+ <ng-content></ng-content>
12252
+ </div>
12253
+ </div>
12254
+ </div>
12282
12255
  `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
12283
12256
  }
12284
12257
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxMenuComponent, decorators: [{
@@ -12287,61 +12260,61 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12287
12260
  selector: 'clx-menu',
12288
12261
  standalone: true,
12289
12262
  imports: [ClxIconComponent, RouterLink, RouterLinkActive],
12290
- template: `
12291
- @if (routerLink()) {
12292
- <a
12293
- [routerLink]="routerLink()"
12294
- routerLinkActive
12295
- #rla="routerLinkActive"
12296
- [routerLinkActiveOptions]="routerLinkActiveOptions()"
12297
- [class]="_buttonCls(rla.isActive)"
12298
- [title]="collapsed() ? label() : ''"
12299
- (click)="$event.preventDefault(); !collapsed() && _toggle()">
12300
- @if (icon()) {
12301
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12302
- }
12303
- @if (!collapsed()) {
12304
- <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12305
- <span
12306
- clx-icon
12307
- name="expand_more"
12308
- size="sm"
12309
- class="shrink-0 transition-transform duration-300"
12310
- [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12311
- </span>
12312
- }
12313
- </a>
12314
- } @else {
12315
- <button
12316
- type="button"
12317
- [class]="_buttonCls(active())"
12318
- [title]="collapsed() ? label() : ''"
12319
- (click)="!collapsed() && _toggle()">
12320
- @if (icon()) {
12321
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12322
- }
12323
- @if (!collapsed()) {
12324
- <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12325
- <span
12326
- clx-icon
12327
- name="expand_more"
12328
- size="sm"
12329
- class="shrink-0 transition-transform duration-300"
12330
- [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12331
- </span>
12332
- }
12333
- </button>
12334
- }
12335
-
12336
- <div
12337
- [class]="NAV_CHILDREN_WRAPPER"
12338
- [style.grid-template-rows]="(!collapsed() && _isExpanded()) ? '1fr' : '0fr'">
12339
- <div [class]="NAV_CHILDREN_INNER">
12340
- <div [class]="NAV_CHILDREN_LIST">
12341
- <ng-content></ng-content>
12342
- </div>
12343
- </div>
12344
- </div>
12263
+ template: `
12264
+ @if (routerLink()) {
12265
+ <a
12266
+ [routerLink]="routerLink()"
12267
+ routerLinkActive
12268
+ #rla="routerLinkActive"
12269
+ [routerLinkActiveOptions]="routerLinkActiveOptions()"
12270
+ [class]="_buttonCls(rla.isActive)"
12271
+ [title]="collapsed() ? label() : ''"
12272
+ (click)="$event.preventDefault(); !collapsed() && _toggle()">
12273
+ @if (icon()) {
12274
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12275
+ }
12276
+ @if (!collapsed()) {
12277
+ <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12278
+ <span
12279
+ clx-icon
12280
+ name="expand_more"
12281
+ size="sm"
12282
+ class="shrink-0 transition-transform duration-300"
12283
+ [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12284
+ </span>
12285
+ }
12286
+ </a>
12287
+ } @else {
12288
+ <button
12289
+ type="button"
12290
+ [class]="_buttonCls(active())"
12291
+ [title]="collapsed() ? label() : ''"
12292
+ (click)="!collapsed() && _toggle()">
12293
+ @if (icon()) {
12294
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12295
+ }
12296
+ @if (!collapsed()) {
12297
+ <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12298
+ <span
12299
+ clx-icon
12300
+ name="expand_more"
12301
+ size="sm"
12302
+ class="shrink-0 transition-transform duration-300"
12303
+ [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12304
+ </span>
12305
+ }
12306
+ </button>
12307
+ }
12308
+
12309
+ <div
12310
+ [class]="NAV_CHILDREN_WRAPPER"
12311
+ [style.grid-template-rows]="(!collapsed() && _isExpanded()) ? '1fr' : '0fr'">
12312
+ <div [class]="NAV_CHILDREN_INNER">
12313
+ <div [class]="NAV_CHILDREN_LIST">
12314
+ <ng-content></ng-content>
12315
+ </div>
12316
+ </div>
12317
+ </div>
12345
12318
  `,
12346
12319
  encapsulation: ViewEncapsulation.None,
12347
12320
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -12361,14 +12334,14 @@ class ClxNavGroupComponent {
12361
12334
  return c ? resolveColor(c).textSubtle : 'text-clx-text-subtle';
12362
12335
  }, ...(ngDevMode ? [{ debugName: "labelClass" }] : /* istanbul ignore next */ []));
12363
12336
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNavGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12364
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxNavGroupComponent, isStandalone: true, selector: "clx-nav-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: `
12365
- @if (!collapsed()) {
12366
- <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest select-none" [class]="labelClass()">{{ label() }}</span>
12367
- } @else {
12368
- <span class="flex items-center justify-center w-full py-3">
12369
- <span class="w-1.5 h-1.5 rounded-full bg-clx-border"></span>
12370
- </span>
12371
- }
12337
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxNavGroupComponent, isStandalone: true, selector: "clx-nav-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: `
12338
+ @if (!collapsed()) {
12339
+ <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest select-none" [class]="labelClass()">{{ label() }}</span>
12340
+ } @else {
12341
+ <span class="flex items-center justify-center w-full py-3">
12342
+ <span class="w-1.5 h-1.5 rounded-full bg-clx-border"></span>
12343
+ </span>
12344
+ }
12372
12345
  `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
12373
12346
  }
12374
12347
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNavGroupComponent, decorators: [{
@@ -12376,14 +12349,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12376
12349
  args: [{
12377
12350
  selector: 'clx-nav-group',
12378
12351
  standalone: true,
12379
- template: `
12380
- @if (!collapsed()) {
12381
- <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest select-none" [class]="labelClass()">{{ label() }}</span>
12382
- } @else {
12383
- <span class="flex items-center justify-center w-full py-3">
12384
- <span class="w-1.5 h-1.5 rounded-full bg-clx-border"></span>
12385
- </span>
12386
- }
12352
+ template: `
12353
+ @if (!collapsed()) {
12354
+ <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest select-none" [class]="labelClass()">{{ label() }}</span>
12355
+ } @else {
12356
+ <span class="flex items-center justify-center w-full py-3">
12357
+ <span class="w-1.5 h-1.5 rounded-full bg-clx-border"></span>
12358
+ </span>
12359
+ }
12387
12360
  `,
12388
12361
  encapsulation: ViewEncapsulation.None,
12389
12362
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -12870,25 +12843,20 @@ class ClxTableComponent {
12870
12843
  _isSelected(item) {
12871
12844
  return this.selectedItems().some(s => s === item);
12872
12845
  }
12873
- _headerCellClassMap = computed(() => {
12874
- const map = new Map();
12875
- for (const col of this.columns()) {
12876
- const align = TABLE_ALIGN_MAP[col.align || 'left'];
12877
- const cursor = col.sortable ? 'cursor-pointer select-none' : '';
12878
- const extra = col.headerClass || '';
12879
- map.set(col.key, `${TABLE_HEADER_CELL_CLASS} ${align} ${cursor} ${extra}`.trim());
12880
- }
12881
- return map;
12882
- }, ...(ngDevMode ? [{ debugName: "_headerCellClassMap" }] : /* istanbul ignore next */ []));
12883
- _dataCellClassMap = computed(() => {
12884
- const map = new Map();
12885
- for (const col of this.columns()) {
12886
- const align = TABLE_ALIGN_MAP[col.align || 'left'];
12887
- const extra = col.cellClass || '';
12888
- map.set(col.key, `${TABLE_CELL_CLASS} ${align} ${extra}`.trim());
12889
- }
12890
- return map;
12891
- }, ...(ngDevMode ? [{ debugName: "_dataCellClassMap" }] : /* istanbul ignore next */ []));
12846
+ _getAlignClass(align) {
12847
+ return TABLE_ALIGN_MAP[align];
12848
+ }
12849
+ _getHeaderCellClass(col) {
12850
+ const align = this._getAlignClass(col.align || 'left');
12851
+ const cursor = col.sortable ? 'cursor-pointer select-none' : '';
12852
+ const extra = col.headerClass || '';
12853
+ return `${TABLE_HEADER_CELL_CLASS} ${align} ${cursor} ${extra}`.trim();
12854
+ }
12855
+ _getDataCellClass(col) {
12856
+ const align = this._getAlignClass(col.align || 'left');
12857
+ const extra = col.cellClass || '';
12858
+ return `${TABLE_CELL_CLASS} ${align} ${extra}`.trim();
12859
+ }
12892
12860
  _getCellValue(item, key) {
12893
12861
  return item[key];
12894
12862
  }
@@ -12930,143 +12898,143 @@ class ClxTableComponent {
12930
12898
  this.selectionChange.emit({ selected: this.selectedItems(), allSelected: false });
12931
12899
  }
12932
12900
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12933
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxTableComponent, isStandalone: true, selector: "clx-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, singleSelect: { classPropertyName: "singleSelect", publicName: "singleSelect", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, emptyIcon: { classPropertyName: "emptyIcon", publicName: "emptyIcon", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, emptyDescription: { classPropertyName: "emptyDescription", publicName: "emptyDescription", isSignal: true, isRequired: false, transformFunction: null }, sortKey: { classPropertyName: "sortKey", publicName: "sortKey", isSignal: true, isRequired: false, transformFunction: null }, sortDir: { classPropertyName: "sortDir", publicName: "sortDir", isSignal: true, isRequired: false, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, _currentPage: { classPropertyName: "_currentPage", publicName: "_currentPage", isSignal: true, isRequired: false, transformFunction: null }, searchQuery: { classPropertyName: "searchQuery", publicName: "searchQuery", isSignal: true, isRequired: false, transformFunction: null }, selectedItems: { classPropertyName: "selectedItems", publicName: "selectedItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortKey: "sortKeyChange", sortDir: "sortDirChange", pageIndex: "pageIndexChange", pageSize: "pageSizeChange", _currentPage: "_currentPageChange", searchQuery: "searchQueryChange", selectedItems: "selectedItemsChange", sortChange: "sortChange", pageChange: "pageChange", selectionChange: "selectionChange" }, host: { classAttribute: "block" }, queries: [{ propertyName: "_columnDefs", predicate: ClxColumnDefDirective, isSignal: true }], ngImport: i0, template: `
12934
- <!-- ── Toolbar: búsqueda ──────────────────────────────────────────────── -->
12935
- @if (searchable() && !loading() && _filteredData().length > 0) {
12936
- <div class="mb-3 max-w-80 min-w-40">
12937
- <clx-input
12938
- placeholder="Buscar..."
12939
- size="sm"
12940
- suffixIcon="search"
12941
- [color]="_color()"
12942
- [(ngModel)]="searchQuery">
12943
- </clx-input>
12944
- </div>
12945
- }
12946
-
12947
- @if (loading()) {
12948
- <!-- ── Loading State ───────────────────────────────────────────────── -->
12949
- <div class="space-y-3">
12950
- @for (row of [1, 2, 3, 4, 5]; track row) {
12951
- <div class="flex items-center gap-4">
12952
- @if (selectable()) {
12953
- <clx-skeleton variant="circular" size="xs"></clx-skeleton>
12954
- }
12955
- @for (col of columns(); track col.key) {
12956
- <clx-skeleton variant="text" size="md" [style.flex]="'1'"></clx-skeleton>
12957
- }
12958
- </div>
12959
- }
12960
- </div>
12961
- } @else if (_filteredData().length === 0) {
12962
- <!-- ── Empty State ─────────────────────────────────────────────────── -->
12963
- <div class="py-10">
12964
- <clx-page-empty
12965
- size="sm"
12966
- [icon]="emptyIcon()"
12967
- [title]="_emptyTitle()"
12968
- [description]="_emptyDescription()" />
12969
- </div>
12970
- } @else {
12971
- <!-- ── Tabla ───────────────────────────────────────────────────────── -->
12972
- <div class="overflow-x-auto">
12973
- <table [class]="_tableClass()">
12974
- <thead>
12975
- <tr [class]="_headerClass()">
12976
- @if (selectable()) {
12977
- <th [class]="_headerCellClass()"
12978
- [class.rounded-tl-lg]="true"
12979
- [class.rounded-bl-lg]="true"
12980
- class="w-10 text-center">
12981
- @if (!singleSelect()) {
12982
- <clx-checkbox
12983
- [color]="_color()"
12984
- [ngModel]="_allSelected()"
12985
- (ngModelChange)="_handleSelectAll($event)">
12986
- </clx-checkbox>
12987
- }
12988
- </th>
12989
- }
12990
-
12991
- @for (col of columns(); track col.key; let first = $first; let last = $last) {
12992
- <th
12993
- [class]="_headerCellClassMap().get(col.key)"
12994
- [class.rounded-tl-lg]="first && !selectable()"
12995
- [class.rounded-bl-lg]="first && !selectable()"
12996
- [class.rounded-tr-lg]="last"
12997
- [class.rounded-br-lg]="last"
12998
- [style.width]="col.width"
12999
- (click)="col.sortable && _handleSort(col.key)">
13000
- <div class="flex items-center gap-1.5"
13001
- [class.justify-end]="col.align === 'right'"
13002
- [class.justify-center]="col.align === 'center'">
13003
- @if (_getColumnDef(col.key)?.headerCell()?.templateRef; as tpl) {
13004
- <ng-container [ngTemplateOutlet]="tpl"></ng-container>
13005
- } @else {
13006
- <span>{{ col.header || col.key }}</span>
13007
- }
13008
- @if (col.sortable) {
13009
- <span class="opacity-50">
13010
- @if (sortKey() === col.key && sortDir() === 'asc') {
13011
- <span clx-icon name="arrow_upward" size="xs"></span>
13012
- } @else if (sortKey() === col.key && sortDir() === 'desc') {
13013
- <span clx-icon name="arrow_downward" size="xs"></span>
13014
- } @else {
13015
- <span clx-icon name="swap_vert" size="xs"></span>
13016
- }
13017
- </span>
13018
- }
13019
- </div>
13020
- </th>
13021
- }
13022
- </tr>
13023
- </thead>
13024
-
13025
- <tbody>
13026
- @for (item of _paginatedData(); track i; let i = $index) {
13027
- <tr [class]="_rowClass()">
13028
- @if (selectable()) {
13029
- <td [class]="_cellClass()" class="text-center">
13030
- <clx-checkbox
13031
- [color]="_color()"
13032
- [ngModel]="_isSelected(item)"
13033
- (ngModelChange)="_handleSelectItem(item, $event)">
13034
- </clx-checkbox>
13035
- </td>
13036
- }
13037
-
13038
- @for (col of columns(); track col.key) {
13039
- <td [class]="_dataCellClassMap().get(col.key)">
13040
- @if (_getColumnDef(col.key)?.cell()?.templateRef; as tpl) {
13041
- <ng-container
13042
- [ngTemplateOutlet]="tpl"
13043
- [ngTemplateOutletContext]="{ $implicit: item, index: i }">
13044
- </ng-container>
13045
- } @else {
13046
- {{ _getCellValue(item, col.key) }}
13047
- }
13048
- </td>
13049
- }
13050
- </tr>
13051
- }
13052
- </tbody>
13053
- </table>
13054
- </div>
13055
-
13056
- <!-- ── Footer: clx-pagination ────────────────────────────────────────── -->
13057
- @if (pagination()) {
13058
- <div class="border-t border-clx-border-soft pt-3">
13059
- <clx-pagination
13060
- size="sm"
13061
- [totalItems]="_totalItems()"
13062
- [color]="_color()"
13063
- [(currentPage)]="_currentPage"
13064
- [(pageSize)]="pageSize"
13065
- (pageChange)="_onPaginationPageChange($event.page)">
13066
- </clx-pagination>
13067
- </div>
13068
- }
13069
- }
12901
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxTableComponent, isStandalone: true, selector: "clx-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, singleSelect: { classPropertyName: "singleSelect", publicName: "singleSelect", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, emptyIcon: { classPropertyName: "emptyIcon", publicName: "emptyIcon", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, emptyDescription: { classPropertyName: "emptyDescription", publicName: "emptyDescription", isSignal: true, isRequired: false, transformFunction: null }, sortKey: { classPropertyName: "sortKey", publicName: "sortKey", isSignal: true, isRequired: false, transformFunction: null }, sortDir: { classPropertyName: "sortDir", publicName: "sortDir", isSignal: true, isRequired: false, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, _currentPage: { classPropertyName: "_currentPage", publicName: "_currentPage", isSignal: true, isRequired: false, transformFunction: null }, searchQuery: { classPropertyName: "searchQuery", publicName: "searchQuery", isSignal: true, isRequired: false, transformFunction: null }, selectedItems: { classPropertyName: "selectedItems", publicName: "selectedItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortKey: "sortKeyChange", sortDir: "sortDirChange", pageIndex: "pageIndexChange", pageSize: "pageSizeChange", _currentPage: "_currentPageChange", searchQuery: "searchQueryChange", selectedItems: "selectedItemsChange", sortChange: "sortChange", pageChange: "pageChange", selectionChange: "selectionChange" }, host: { classAttribute: "block" }, queries: [{ propertyName: "_columnDefs", predicate: ClxColumnDefDirective, isSignal: true }], ngImport: i0, template: `
12902
+ <!-- ── Toolbar: búsqueda ──────────────────────────────────────────────── -->
12903
+ @if (searchable() && !loading() && _filteredData().length > 0) {
12904
+ <div class="mb-3 max-w-80 min-w-40">
12905
+ <clx-input
12906
+ placeholder="Buscar..."
12907
+ size="sm"
12908
+ suffixIcon="search"
12909
+ [color]="_color()"
12910
+ [(ngModel)]="searchQuery">
12911
+ </clx-input>
12912
+ </div>
12913
+ }
12914
+
12915
+ @if (loading()) {
12916
+ <!-- ── Loading State ───────────────────────────────────────────────── -->
12917
+ <div class="space-y-3">
12918
+ @for (row of [1, 2, 3, 4, 5]; track row) {
12919
+ <div class="flex items-center gap-4">
12920
+ @if (selectable()) {
12921
+ <clx-skeleton variant="circular" size="xs"></clx-skeleton>
12922
+ }
12923
+ @for (col of columns(); track col.key) {
12924
+ <clx-skeleton variant="text" size="md" [style.flex]="'1'"></clx-skeleton>
12925
+ }
12926
+ </div>
12927
+ }
12928
+ </div>
12929
+ } @else if (_filteredData().length === 0) {
12930
+ <!-- ── Empty State ─────────────────────────────────────────────────── -->
12931
+ <div class="py-10">
12932
+ <clx-page-empty
12933
+ size="sm"
12934
+ [icon]="emptyIcon()"
12935
+ [title]="_emptyTitle()"
12936
+ [description]="_emptyDescription()" />
12937
+ </div>
12938
+ } @else {
12939
+ <!-- ── Tabla ───────────────────────────────────────────────────────── -->
12940
+ <div class="overflow-x-auto">
12941
+ <table [class]="_tableClass()">
12942
+ <thead>
12943
+ <tr [class]="_headerClass()">
12944
+ @if (selectable()) {
12945
+ <th [class]="_headerCellClass()"
12946
+ [class.rounded-tl-lg]="true"
12947
+ [class.rounded-bl-lg]="true"
12948
+ class="w-10 text-center">
12949
+ @if (!singleSelect()) {
12950
+ <clx-checkbox
12951
+ [color]="_color()"
12952
+ [ngModel]="_allSelected()"
12953
+ (ngModelChange)="_handleSelectAll($event)">
12954
+ </clx-checkbox>
12955
+ }
12956
+ </th>
12957
+ }
12958
+
12959
+ @for (col of columns(); track col.key; let first = $first; let last = $last) {
12960
+ <th
12961
+ [class]="_getHeaderCellClass(col)"
12962
+ [class.rounded-tl-lg]="first && !selectable()"
12963
+ [class.rounded-bl-lg]="first && !selectable()"
12964
+ [class.rounded-tr-lg]="last"
12965
+ [class.rounded-br-lg]="last"
12966
+ [style.width]="col.width"
12967
+ (click)="col.sortable && _handleSort(col.key)">
12968
+ <div class="flex items-center gap-1.5"
12969
+ [class.justify-end]="col.align === 'right'"
12970
+ [class.justify-center]="col.align === 'center'">
12971
+ @if (_getColumnDef(col.key)?.headerCell()?.templateRef; as tpl) {
12972
+ <ng-container [ngTemplateOutlet]="tpl"></ng-container>
12973
+ } @else {
12974
+ <span>{{ col.header || col.key }}</span>
12975
+ }
12976
+ @if (col.sortable) {
12977
+ <span class="opacity-50">
12978
+ @if (sortKey() === col.key && sortDir() === 'asc') {
12979
+ <span clx-icon name="arrow_upward" size="xs"></span>
12980
+ } @else if (sortKey() === col.key && sortDir() === 'desc') {
12981
+ <span clx-icon name="arrow_downward" size="xs"></span>
12982
+ } @else {
12983
+ <span clx-icon name="swap_vert" size="xs"></span>
12984
+ }
12985
+ </span>
12986
+ }
12987
+ </div>
12988
+ </th>
12989
+ }
12990
+ </tr>
12991
+ </thead>
12992
+
12993
+ <tbody>
12994
+ @for (item of _paginatedData(); track i; let i = $index) {
12995
+ <tr [class]="_rowClass()">
12996
+ @if (selectable()) {
12997
+ <td [class]="_cellClass()" class="text-center">
12998
+ <clx-checkbox
12999
+ [color]="_color()"
13000
+ [ngModel]="_isSelected(item)"
13001
+ (ngModelChange)="_handleSelectItem(item, $event)">
13002
+ </clx-checkbox>
13003
+ </td>
13004
+ }
13005
+
13006
+ @for (col of columns(); track col.key) {
13007
+ <td [class]="_getDataCellClass(col)">
13008
+ @if (_getColumnDef(col.key)?.cell()?.templateRef; as tpl) {
13009
+ <ng-container
13010
+ [ngTemplateOutlet]="tpl"
13011
+ [ngTemplateOutletContext]="{ $implicit: item, index: i }">
13012
+ </ng-container>
13013
+ } @else {
13014
+ {{ _getCellValue(item, col.key) }}
13015
+ }
13016
+ </td>
13017
+ }
13018
+ </tr>
13019
+ }
13020
+ </tbody>
13021
+ </table>
13022
+ </div>
13023
+
13024
+ <!-- ── Footer: clx-pagination ────────────────────────────────────────── -->
13025
+ @if (pagination()) {
13026
+ <div class="border-t border-clx-border-soft pt-3">
13027
+ <clx-pagination
13028
+ size="sm"
13029
+ [totalItems]="_totalItems()"
13030
+ [color]="_color()"
13031
+ [(currentPage)]="_currentPage"
13032
+ [(pageSize)]="pageSize"
13033
+ (pageChange)="_onPaginationPageChange($event.page)">
13034
+ </clx-pagination>
13035
+ </div>
13036
+ }
13037
+ }
13070
13038
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ClxCheckboxComponent, selector: "clx-checkbox", inputs: ["color", "variant", "size", "label", "value", "disabled"] }, { 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: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxPaginationComponent, selector: "clx-pagination", inputs: ["totalItems", "color", "size", "showPageSize", "pageSizeOptions", "currentPage", "pageSize"], outputs: ["currentPageChange", "pageSizeChange", "pageChange", "sizeChange"] }, { kind: "component", type: ClxPageEmptyComponent, selector: "clx-page-empty", inputs: ["icon", "title", "description", "ctaLabel", "ctaIcon", "ctaColor", "size"], outputs: ["cta"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
13071
13039
  }
13072
13040
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTableComponent, decorators: [{
@@ -13084,143 +13052,143 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
13084
13052
  ClxPaginationComponent,
13085
13053
  ClxPageEmptyComponent,
13086
13054
  ],
13087
- template: `
13088
- <!-- ── Toolbar: búsqueda ──────────────────────────────────────────────── -->
13089
- @if (searchable() && !loading() && _filteredData().length > 0) {
13090
- <div class="mb-3 max-w-80 min-w-40">
13091
- <clx-input
13092
- placeholder="Buscar..."
13093
- size="sm"
13094
- suffixIcon="search"
13095
- [color]="_color()"
13096
- [(ngModel)]="searchQuery">
13097
- </clx-input>
13098
- </div>
13099
- }
13100
-
13101
- @if (loading()) {
13102
- <!-- ── Loading State ───────────────────────────────────────────────── -->
13103
- <div class="space-y-3">
13104
- @for (row of [1, 2, 3, 4, 5]; track row) {
13105
- <div class="flex items-center gap-4">
13106
- @if (selectable()) {
13107
- <clx-skeleton variant="circular" size="xs"></clx-skeleton>
13108
- }
13109
- @for (col of columns(); track col.key) {
13110
- <clx-skeleton variant="text" size="md" [style.flex]="'1'"></clx-skeleton>
13111
- }
13112
- </div>
13113
- }
13114
- </div>
13115
- } @else if (_filteredData().length === 0) {
13116
- <!-- ── Empty State ─────────────────────────────────────────────────── -->
13117
- <div class="py-10">
13118
- <clx-page-empty
13119
- size="sm"
13120
- [icon]="emptyIcon()"
13121
- [title]="_emptyTitle()"
13122
- [description]="_emptyDescription()" />
13123
- </div>
13124
- } @else {
13125
- <!-- ── Tabla ───────────────────────────────────────────────────────── -->
13126
- <div class="overflow-x-auto">
13127
- <table [class]="_tableClass()">
13128
- <thead>
13129
- <tr [class]="_headerClass()">
13130
- @if (selectable()) {
13131
- <th [class]="_headerCellClass()"
13132
- [class.rounded-tl-lg]="true"
13133
- [class.rounded-bl-lg]="true"
13134
- class="w-10 text-center">
13135
- @if (!singleSelect()) {
13136
- <clx-checkbox
13137
- [color]="_color()"
13138
- [ngModel]="_allSelected()"
13139
- (ngModelChange)="_handleSelectAll($event)">
13140
- </clx-checkbox>
13141
- }
13142
- </th>
13143
- }
13144
-
13145
- @for (col of columns(); track col.key; let first = $first; let last = $last) {
13146
- <th
13147
- [class]="_headerCellClassMap().get(col.key)"
13148
- [class.rounded-tl-lg]="first && !selectable()"
13149
- [class.rounded-bl-lg]="first && !selectable()"
13150
- [class.rounded-tr-lg]="last"
13151
- [class.rounded-br-lg]="last"
13152
- [style.width]="col.width"
13153
- (click)="col.sortable && _handleSort(col.key)">
13154
- <div class="flex items-center gap-1.5"
13155
- [class.justify-end]="col.align === 'right'"
13156
- [class.justify-center]="col.align === 'center'">
13157
- @if (_getColumnDef(col.key)?.headerCell()?.templateRef; as tpl) {
13158
- <ng-container [ngTemplateOutlet]="tpl"></ng-container>
13159
- } @else {
13160
- <span>{{ col.header || col.key }}</span>
13161
- }
13162
- @if (col.sortable) {
13163
- <span class="opacity-50">
13164
- @if (sortKey() === col.key && sortDir() === 'asc') {
13165
- <span clx-icon name="arrow_upward" size="xs"></span>
13166
- } @else if (sortKey() === col.key && sortDir() === 'desc') {
13167
- <span clx-icon name="arrow_downward" size="xs"></span>
13168
- } @else {
13169
- <span clx-icon name="swap_vert" size="xs"></span>
13170
- }
13171
- </span>
13172
- }
13173
- </div>
13174
- </th>
13175
- }
13176
- </tr>
13177
- </thead>
13178
-
13179
- <tbody>
13180
- @for (item of _paginatedData(); track i; let i = $index) {
13181
- <tr [class]="_rowClass()">
13182
- @if (selectable()) {
13183
- <td [class]="_cellClass()" class="text-center">
13184
- <clx-checkbox
13185
- [color]="_color()"
13186
- [ngModel]="_isSelected(item)"
13187
- (ngModelChange)="_handleSelectItem(item, $event)">
13188
- </clx-checkbox>
13189
- </td>
13190
- }
13191
-
13192
- @for (col of columns(); track col.key) {
13193
- <td [class]="_dataCellClassMap().get(col.key)">
13194
- @if (_getColumnDef(col.key)?.cell()?.templateRef; as tpl) {
13195
- <ng-container
13196
- [ngTemplateOutlet]="tpl"
13197
- [ngTemplateOutletContext]="{ $implicit: item, index: i }">
13198
- </ng-container>
13199
- } @else {
13200
- {{ _getCellValue(item, col.key) }}
13201
- }
13202
- </td>
13203
- }
13204
- </tr>
13205
- }
13206
- </tbody>
13207
- </table>
13208
- </div>
13209
-
13210
- <!-- ── Footer: clx-pagination ────────────────────────────────────────── -->
13211
- @if (pagination()) {
13212
- <div class="border-t border-clx-border-soft pt-3">
13213
- <clx-pagination
13214
- size="sm"
13215
- [totalItems]="_totalItems()"
13216
- [color]="_color()"
13217
- [(currentPage)]="_currentPage"
13218
- [(pageSize)]="pageSize"
13219
- (pageChange)="_onPaginationPageChange($event.page)">
13220
- </clx-pagination>
13221
- </div>
13222
- }
13223
- }
13055
+ template: `
13056
+ <!-- ── Toolbar: búsqueda ──────────────────────────────────────────────── -->
13057
+ @if (searchable() && !loading() && _filteredData().length > 0) {
13058
+ <div class="mb-3 max-w-80 min-w-40">
13059
+ <clx-input
13060
+ placeholder="Buscar..."
13061
+ size="sm"
13062
+ suffixIcon="search"
13063
+ [color]="_color()"
13064
+ [(ngModel)]="searchQuery">
13065
+ </clx-input>
13066
+ </div>
13067
+ }
13068
+
13069
+ @if (loading()) {
13070
+ <!-- ── Loading State ───────────────────────────────────────────────── -->
13071
+ <div class="space-y-3">
13072
+ @for (row of [1, 2, 3, 4, 5]; track row) {
13073
+ <div class="flex items-center gap-4">
13074
+ @if (selectable()) {
13075
+ <clx-skeleton variant="circular" size="xs"></clx-skeleton>
13076
+ }
13077
+ @for (col of columns(); track col.key) {
13078
+ <clx-skeleton variant="text" size="md" [style.flex]="'1'"></clx-skeleton>
13079
+ }
13080
+ </div>
13081
+ }
13082
+ </div>
13083
+ } @else if (_filteredData().length === 0) {
13084
+ <!-- ── Empty State ─────────────────────────────────────────────────── -->
13085
+ <div class="py-10">
13086
+ <clx-page-empty
13087
+ size="sm"
13088
+ [icon]="emptyIcon()"
13089
+ [title]="_emptyTitle()"
13090
+ [description]="_emptyDescription()" />
13091
+ </div>
13092
+ } @else {
13093
+ <!-- ── Tabla ───────────────────────────────────────────────────────── -->
13094
+ <div class="overflow-x-auto">
13095
+ <table [class]="_tableClass()">
13096
+ <thead>
13097
+ <tr [class]="_headerClass()">
13098
+ @if (selectable()) {
13099
+ <th [class]="_headerCellClass()"
13100
+ [class.rounded-tl-lg]="true"
13101
+ [class.rounded-bl-lg]="true"
13102
+ class="w-10 text-center">
13103
+ @if (!singleSelect()) {
13104
+ <clx-checkbox
13105
+ [color]="_color()"
13106
+ [ngModel]="_allSelected()"
13107
+ (ngModelChange)="_handleSelectAll($event)">
13108
+ </clx-checkbox>
13109
+ }
13110
+ </th>
13111
+ }
13112
+
13113
+ @for (col of columns(); track col.key; let first = $first; let last = $last) {
13114
+ <th
13115
+ [class]="_getHeaderCellClass(col)"
13116
+ [class.rounded-tl-lg]="first && !selectable()"
13117
+ [class.rounded-bl-lg]="first && !selectable()"
13118
+ [class.rounded-tr-lg]="last"
13119
+ [class.rounded-br-lg]="last"
13120
+ [style.width]="col.width"
13121
+ (click)="col.sortable && _handleSort(col.key)">
13122
+ <div class="flex items-center gap-1.5"
13123
+ [class.justify-end]="col.align === 'right'"
13124
+ [class.justify-center]="col.align === 'center'">
13125
+ @if (_getColumnDef(col.key)?.headerCell()?.templateRef; as tpl) {
13126
+ <ng-container [ngTemplateOutlet]="tpl"></ng-container>
13127
+ } @else {
13128
+ <span>{{ col.header || col.key }}</span>
13129
+ }
13130
+ @if (col.sortable) {
13131
+ <span class="opacity-50">
13132
+ @if (sortKey() === col.key && sortDir() === 'asc') {
13133
+ <span clx-icon name="arrow_upward" size="xs"></span>
13134
+ } @else if (sortKey() === col.key && sortDir() === 'desc') {
13135
+ <span clx-icon name="arrow_downward" size="xs"></span>
13136
+ } @else {
13137
+ <span clx-icon name="swap_vert" size="xs"></span>
13138
+ }
13139
+ </span>
13140
+ }
13141
+ </div>
13142
+ </th>
13143
+ }
13144
+ </tr>
13145
+ </thead>
13146
+
13147
+ <tbody>
13148
+ @for (item of _paginatedData(); track i; let i = $index) {
13149
+ <tr [class]="_rowClass()">
13150
+ @if (selectable()) {
13151
+ <td [class]="_cellClass()" class="text-center">
13152
+ <clx-checkbox
13153
+ [color]="_color()"
13154
+ [ngModel]="_isSelected(item)"
13155
+ (ngModelChange)="_handleSelectItem(item, $event)">
13156
+ </clx-checkbox>
13157
+ </td>
13158
+ }
13159
+
13160
+ @for (col of columns(); track col.key) {
13161
+ <td [class]="_getDataCellClass(col)">
13162
+ @if (_getColumnDef(col.key)?.cell()?.templateRef; as tpl) {
13163
+ <ng-container
13164
+ [ngTemplateOutlet]="tpl"
13165
+ [ngTemplateOutletContext]="{ $implicit: item, index: i }">
13166
+ </ng-container>
13167
+ } @else {
13168
+ {{ _getCellValue(item, col.key) }}
13169
+ }
13170
+ </td>
13171
+ }
13172
+ </tr>
13173
+ }
13174
+ </tbody>
13175
+ </table>
13176
+ </div>
13177
+
13178
+ <!-- ── Footer: clx-pagination ────────────────────────────────────────── -->
13179
+ @if (pagination()) {
13180
+ <div class="border-t border-clx-border-soft pt-3">
13181
+ <clx-pagination
13182
+ size="sm"
13183
+ [totalItems]="_totalItems()"
13184
+ [color]="_color()"
13185
+ [(currentPage)]="_currentPage"
13186
+ [(pageSize)]="pageSize"
13187
+ (pageChange)="_onPaginationPageChange($event.page)">
13188
+ </clx-pagination>
13189
+ </div>
13190
+ }
13191
+ }
13224
13192
  `,
13225
13193
  encapsulation: ViewEncapsulation.None,
13226
13194
  changeDetection: ChangeDetectionStrategy.OnPush,