codexly-ui 0.14.3 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,6 +10,8 @@ import * as i1 from '@angular/cdk/overlay';
10
10
  import { ScrollStrategyOptions, OverlayModule } from '@angular/cdk/overlay';
11
11
  import { ScrollingModule, CdkScrollable } from '@angular/cdk/scrolling';
12
12
  import { Router, NavigationEnd, RouterLink, RouterLinkActive } from '@angular/router';
13
+ import * as i1$2 from '@angular/cdk/drag-drop';
14
+ import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
13
15
 
14
16
  // ── Parse 'red' | 'red-500' → { color, shade } ───────────────────────────────
15
17
  function parseColorInput(input) {
@@ -2147,6 +2149,9 @@ class ClxProgressBarComponent {
2147
2149
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
2148
2150
  showValue = input(false, ...(ngDevMode ? [{ debugName: "showValue" }] : /* istanbul ignore next */ []));
2149
2151
  valueFormat = input('percent', ...(ngDevMode ? [{ debugName: "valueFormat" }] : /* istanbul ignore next */ []));
2152
+ /** Only read when variant()==='segmented' — ignores value()/max() entirely, each segment's
2153
+ * width is its own value as a fraction of the segments' combined total. */
2154
+ segments = input([], ...(ngDevMode ? [{ debugName: "segments" }] : /* istanbul ignore next */ []));
2150
2155
  _pct = computed(() => {
2151
2156
  const m = this.max();
2152
2157
  if (!m)
@@ -2177,8 +2182,17 @@ class ClxProgressBarComponent {
2177
2182
  cls += ' clx-pb-striped';
2178
2183
  return cls;
2179
2184
  }, ...(ngDevMode ? [{ debugName: "_fillCls" }] : /* istanbul ignore next */ []));
2185
+ _segmentPcts = computed(() => {
2186
+ const segs = this.segments();
2187
+ const total = segs.reduce((sum, s) => sum + s.value, 0) || 1;
2188
+ return segs.map(s => ({
2189
+ pct: (s.value / total) * 100,
2190
+ cls: resolveColor(s.color).bg,
2191
+ label: s.label,
2192
+ }));
2193
+ }, ...(ngDevMode ? [{ debugName: "_segmentPcts" }] : /* istanbul ignore next */ []));
2180
2194
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2181
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxProgressBarComponent, isStandalone: true, selector: "clx-progress-bar", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex flex-col gap-1.5 w-full" }, ngImport: i0, template: `
2195
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxProgressBarComponent, isStandalone: true, selector: "clx-progress-bar", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null }, segments: { classPropertyName: "segments", publicName: "segments", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex flex-col gap-1.5 w-full" }, ngImport: i0, template: `
2182
2196
  @if (label() || showValue()) {
2183
2197
  <div class="flex items-center justify-between gap-2">
2184
2198
  @if (label()) {
@@ -2192,23 +2206,42 @@ class ClxProgressBarComponent {
2192
2206
  </div>
2193
2207
  }
2194
2208
 
2195
- <!-- Track -->
2196
- <div
2197
- class="w-full overflow-hidden rounded-full bg-clx-surface-2"
2198
- [class]="_trackH()"
2199
- role="progressbar"
2200
- [attr.aria-valuenow]="variant() === 'indeterminate' ? null : value()"
2201
- [attr.aria-valuemin]="0"
2202
- [attr.aria-valuemax]="max()"
2203
- [attr.aria-label]="label() || null">
2204
-
2205
- <!-- Fill -->
2209
+ @if (variant() === 'segmented') {
2210
+ <!-- Track: one flex child per segment, own color each, widths normalized to the segments'
2211
+ own value sum — independent of value()/max(), which this variant ignores. -->
2206
2212
  <div
2207
- class="h-full rounded-full transition-[width,background-color] duration-500 ease-out"
2208
- [class]="_fillCls()"
2209
- [style.width]="variant() === 'indeterminate' ? '40%' : _pct() + '%'">
2213
+ class="w-full flex overflow-hidden rounded-full bg-clx-surface-2 gap-0.5"
2214
+ [class]="_trackH()"
2215
+ role="img"
2216
+ [attr.aria-label]="label() || null">
2217
+ @for (seg of _segmentPcts(); track $index) {
2218
+ <div
2219
+ class="h-full first:rounded-l-full last:rounded-r-full"
2220
+ [class]="seg.cls"
2221
+ [style.width.%]="seg.pct"
2222
+ [attr.title]="seg.label">
2223
+ </div>
2224
+ }
2210
2225
  </div>
2211
- </div>
2226
+ } @else {
2227
+ <!-- Track -->
2228
+ <div
2229
+ class="w-full overflow-hidden rounded-full bg-clx-surface-2"
2230
+ [class]="_trackH()"
2231
+ role="progressbar"
2232
+ [attr.aria-valuenow]="variant() === 'indeterminate' ? null : value()"
2233
+ [attr.aria-valuemin]="0"
2234
+ [attr.aria-valuemax]="max()"
2235
+ [attr.aria-label]="label() || null">
2236
+
2237
+ <!-- Fill -->
2238
+ <div
2239
+ class="h-full rounded-full transition-[width,background-color] duration-500 ease-out"
2240
+ [class]="_fillCls()"
2241
+ [style.width]="variant() === 'indeterminate' ? '40%' : _pct() + '%'">
2242
+ </div>
2243
+ </div>
2244
+ }
2212
2245
  `, isInline: true, styles: ["@keyframes clx-pb-indeterminate{0%{transform:translate(-100%) scaleX(.4)}40%{transform:translate(0) scaleX(.4)}to{transform:translate(100%) scaleX(.4)}}.clx-pb-indeterminate{animation:clx-pb-indeterminate 1.4s ease-in-out infinite}@keyframes clx-pb-stripe{0%{background-position:1rem 0}to{background-position:0 0}}.clx-pb-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.18) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.18) 50%,rgba(255,255,255,.18) 75%,transparent 75%,transparent);background-size:1rem 1rem;animation:clx-pb-stripe 1s linear infinite}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2213
2246
  }
2214
2247
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxProgressBarComponent, decorators: [{
@@ -2227,25 +2260,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
2227
2260
  </div>
2228
2261
  }
2229
2262
 
2230
- <!-- Track -->
2231
- <div
2232
- class="w-full overflow-hidden rounded-full bg-clx-surface-2"
2233
- [class]="_trackH()"
2234
- role="progressbar"
2235
- [attr.aria-valuenow]="variant() === 'indeterminate' ? null : value()"
2236
- [attr.aria-valuemin]="0"
2237
- [attr.aria-valuemax]="max()"
2238
- [attr.aria-label]="label() || null">
2239
-
2240
- <!-- Fill -->
2263
+ @if (variant() === 'segmented') {
2264
+ <!-- Track: one flex child per segment, own color each, widths normalized to the segments'
2265
+ own value sum — independent of value()/max(), which this variant ignores. -->
2241
2266
  <div
2242
- class="h-full rounded-full transition-[width,background-color] duration-500 ease-out"
2243
- [class]="_fillCls()"
2244
- [style.width]="variant() === 'indeterminate' ? '40%' : _pct() + '%'">
2267
+ class="w-full flex overflow-hidden rounded-full bg-clx-surface-2 gap-0.5"
2268
+ [class]="_trackH()"
2269
+ role="img"
2270
+ [attr.aria-label]="label() || null">
2271
+ @for (seg of _segmentPcts(); track $index) {
2272
+ <div
2273
+ class="h-full first:rounded-l-full last:rounded-r-full"
2274
+ [class]="seg.cls"
2275
+ [style.width.%]="seg.pct"
2276
+ [attr.title]="seg.label">
2277
+ </div>
2278
+ }
2245
2279
  </div>
2246
- </div>
2280
+ } @else {
2281
+ <!-- Track -->
2282
+ <div
2283
+ class="w-full overflow-hidden rounded-full bg-clx-surface-2"
2284
+ [class]="_trackH()"
2285
+ role="progressbar"
2286
+ [attr.aria-valuenow]="variant() === 'indeterminate' ? null : value()"
2287
+ [attr.aria-valuemin]="0"
2288
+ [attr.aria-valuemax]="max()"
2289
+ [attr.aria-label]="label() || null">
2290
+
2291
+ <!-- Fill -->
2292
+ <div
2293
+ class="h-full rounded-full transition-[width,background-color] duration-500 ease-out"
2294
+ [class]="_fillCls()"
2295
+ [style.width]="variant() === 'indeterminate' ? '40%' : _pct() + '%'">
2296
+ </div>
2297
+ </div>
2298
+ }
2247
2299
  `, styles: ["@keyframes clx-pb-indeterminate{0%{transform:translate(-100%) scaleX(.4)}40%{transform:translate(0) scaleX(.4)}to{transform:translate(100%) scaleX(.4)}}.clx-pb-indeterminate{animation:clx-pb-indeterminate 1.4s ease-in-out infinite}@keyframes clx-pb-stripe{0%{background-position:1rem 0}to{background-position:0 0}}.clx-pb-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.18) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.18) 50%,rgba(255,255,255,.18) 75%,transparent 75%,transparent);background-size:1rem 1rem;animation:clx-pb-stripe 1s linear infinite}\n"] }]
2248
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }] } });
2300
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], segments: [{ type: i0.Input, args: [{ isSignal: true, alias: "segments", required: false }] }] } });
2249
2301
 
2250
2302
  class ClxRatingComponent {
2251
2303
  _themeSvc = inject(ClxThemeService);
@@ -4505,6 +4557,12 @@ class ClxNumberComponent {
4505
4557
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4506
4558
  // ── Internal state ───────────────────────────────────────────────────────
4507
4559
  _value = signal(0, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
4560
+ /** What the native input actually shows while the user is typing — kept separate from _value so
4561
+ * a trailing "." or "," (valid mid-edit, not yet a complete number) isn't stomped by re-rendering
4562
+ * the parsed numeric value on every keystroke; see _handleInput's own comment. Resynced from
4563
+ * _value only on programmatic changes (writeValue, the value() input, +/-, blur), never while the
4564
+ * user is actively typing a valid partial number. */
4565
+ _displayText = signal('0', ...(ngDevMode ? [{ debugName: "_displayText" }] : /* istanbul ignore next */ []));
4508
4566
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
4509
4567
  _cardDisabled = inject(CLX_CARD_DISABLED_CONTEXT, { optional: true });
4510
4568
  _cvaConnected = false;
@@ -4512,7 +4570,7 @@ class ClxNumberComponent {
4512
4570
  _valueEffect = effect(() => {
4513
4571
  const v = this.value();
4514
4572
  if (!this._cvaConnected)
4515
- untracked(() => this._value.set(v));
4573
+ untracked(() => { this._value.set(v); this._displayText.set(String(v)); });
4516
4574
  }, ...(ngDevMode ? [{ debugName: "_valueEffect" }] : /* istanbul ignore next */ []));
4517
4575
  _onChange = () => { };
4518
4576
  _onTouched = () => { };
@@ -4557,6 +4615,7 @@ class ClxNumberComponent {
4557
4615
  writeValue(val) {
4558
4616
  this._cvaConnected = true;
4559
4617
  this._value.set(val ?? 0);
4618
+ this._displayText.set(String(val ?? 0));
4560
4619
  }
4561
4620
  registerOnChange(fn) {
4562
4621
  this._onChange = fn;
@@ -4568,9 +4627,44 @@ class ClxNumberComponent {
4568
4627
  this._cvaDisabled.set(disabled);
4569
4628
  }
4570
4629
  // ── Handlers ─────────────────────────────────────────────────────────────
4630
+ /** Allows only digits, a single decimal separator (. or ,) and a leading minus — anything else
4631
+ * typed (letters, a second dot, a stray minus mid-string) is dropped. This is the type="text"
4632
+ * replacement for the browser's own type="number" filtering, needed because the native one keys
4633
+ * off the BROWSER's locale for which decimal separator it accepts and silently rejects the other,
4634
+ * blocking that key outright for users whose input doesn't match. */
4635
+ _sanitizeTyped(raw) {
4636
+ let out = '';
4637
+ let seenDot = false;
4638
+ for (let i = 0; i < raw.length; i++) {
4639
+ const ch = raw[i];
4640
+ if (ch === '-' && i === 0) {
4641
+ out += ch;
4642
+ continue;
4643
+ }
4644
+ if ((ch === '.' || ch === ',') && !seenDot) {
4645
+ seenDot = true;
4646
+ out += '.';
4647
+ continue;
4648
+ }
4649
+ if (ch >= '0' && ch <= '9') {
4650
+ out += ch;
4651
+ }
4652
+ }
4653
+ return out;
4654
+ }
4571
4655
  _handleInput(event) {
4572
- const raw = event.target.value;
4573
- const num = raw === '' ? 0 : parseFloat(raw);
4656
+ const input = event.target;
4657
+ const sanitized = this._sanitizeTyped(input.value);
4658
+ if (sanitized !== input.value)
4659
+ input.value = sanitized;
4660
+ this._displayText.set(sanitized);
4661
+ // A bare "-", "." or a trailing "." (e.g. "2.") is a valid mid-edit state but not yet a
4662
+ // complete number — don't push it through onChange/clamping until it parses, and don't touch
4663
+ // _value (which would re-render _displayText via nothing, since nothing reads _value for the
4664
+ // input anymore, but would desync the two if left stale).
4665
+ if (sanitized === '' || sanitized === '-' || sanitized.endsWith('.'))
4666
+ return;
4667
+ const num = parseFloat(sanitized);
4574
4668
  if (isNaN(num))
4575
4669
  return;
4576
4670
  let clamped = num;
@@ -4580,20 +4674,23 @@ class ClxNumberComponent {
4580
4674
  clamped = Math.min(clamped, this.max());
4581
4675
  this._value.set(clamped);
4582
4676
  this._onChange(clamped);
4583
- // Reflect the clamp back into the native input immediately — otherwise the DOM would keep
4584
- // showing the raw out-of-range digits the user typed until blur re-renders the value.
4677
+ // Reflect the clamp back into both the native input and _displayText immediately — otherwise
4678
+ // the DOM would keep showing the raw out-of-range digits the user typed until blur.
4585
4679
  if (clamped !== num) {
4586
- event.target.value = String(clamped);
4680
+ input.value = String(clamped);
4681
+ this._displayText.set(String(clamped));
4587
4682
  }
4588
4683
  }
4589
4684
  _handleBlur() {
4590
- // Clamp to min/max on blur
4685
+ // Clamp to min/max on blur, and resolve any incomplete typed state (trailing ".", bare "-")
4686
+ // back to the last valid numeric value.
4591
4687
  let v = this._value();
4592
4688
  if (this.min() !== null)
4593
4689
  v = Math.max(v, this.min());
4594
4690
  if (this.max() !== null)
4595
4691
  v = Math.min(v, this.max());
4596
4692
  this._value.set(v);
4693
+ this._displayText.set(String(v));
4597
4694
  this._onChange(v);
4598
4695
  this._onTouched();
4599
4696
  }
@@ -4621,6 +4718,7 @@ class ClxNumberComponent {
4621
4718
  }
4622
4719
  _applyValue(v) {
4623
4720
  this._value.set(v);
4721
+ this._displayText.set(String(v));
4624
4722
  this._onChange(v);
4625
4723
  }
4626
4724
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNumberComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -4650,16 +4748,17 @@ class ClxNumberComponent {
4650
4748
  </div>
4651
4749
  }
4652
4750
 
4653
- <!-- Native number input -->
4751
+ <!-- type="text" + inputmode="decimal" instead of type="number": a native number input
4752
+ rejects a comma decimal separator outright (event.target.value silently becomes '' while
4753
+ the typed text is invalid), which stomps whatever the user typed on every keystroke and
4754
+ resets the caret to 0 — see _handleInput's own comment for the parse side of this fix. -->
4654
4755
  <input
4655
- type="number"
4756
+ type="text"
4757
+ inputmode="decimal"
4656
4758
  [id]="_inputId"
4657
4759
  [name]="_inputId"
4658
4760
  [class]="_inputClass()"
4659
- [value]="_value()"
4660
- [min]="min() ?? ''"
4661
- [max]="max() ?? ''"
4662
- [step]="step()"
4761
+ [value]="_displayText()"
4663
4762
  [disabled]="_disabled()"
4664
4763
  [placeholder]="placeholder()"
4665
4764
  (input)="_handleInput($event)"
@@ -4767,16 +4866,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
4767
4866
  </div>
4768
4867
  }
4769
4868
 
4770
- <!-- Native number input -->
4869
+ <!-- type="text" + inputmode="decimal" instead of type="number": a native number input
4870
+ rejects a comma decimal separator outright (event.target.value silently becomes '' while
4871
+ the typed text is invalid), which stomps whatever the user typed on every keystroke and
4872
+ resets the caret to 0 — see _handleInput's own comment for the parse side of this fix. -->
4771
4873
  <input
4772
- type="number"
4874
+ type="text"
4875
+ inputmode="decimal"
4773
4876
  [id]="_inputId"
4774
4877
  [name]="_inputId"
4775
4878
  [class]="_inputClass()"
4776
- [value]="_value()"
4777
- [min]="min() ?? ''"
4778
- [max]="max() ?? ''"
4779
- [step]="step()"
4879
+ [value]="_displayText()"
4780
4880
  [disabled]="_disabled()"
4781
4881
  [placeholder]="placeholder()"
4782
4882
  (input)="_handleInput($event)"
@@ -10501,6 +10601,9 @@ class ClxWizardComponent {
10501
10601
  prevLabel = input('Anterior', ...(ngDevMode ? [{ debugName: "prevLabel" }] : /* istanbul ignore next */ []));
10502
10602
  nextLabel = input('Siguiente', ...(ngDevMode ? [{ debugName: "nextLabel" }] : /* istanbul ignore next */ []));
10503
10603
  finishLabel = input('Finalizar', ...(ngDevMode ? [{ debugName: "finishLabel" }] : /* istanbul ignore next */ []));
10604
+ /** Shows a spinner on the finish button and disables it — for an async (finish) handler that
10605
+ * takes a moment (an HTTP call), so the consumer doesn't need its own separate loading label. */
10606
+ finishLoading = input(false, ...(ngDevMode ? [{ debugName: "finishLoading" }] : /* istanbul ignore next */ []));
10504
10607
  // ── Outputs ────────────────────────────────────────────────────────────────
10505
10608
  stepChange = output();
10506
10609
  finish = output();
@@ -10609,7 +10712,7 @@ class ClxWizardComponent {
10609
10712
  return `${base} border-2 border-clx-border text-clx-text-subtle bg-clx-surface ${ptr}`;
10610
10713
  }
10611
10714
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxWizardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10612
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxWizardComponent, isStandalone: true, selector: "clx-wizard", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, activeTextColor: { classPropertyName: "activeTextColor", publicName: "activeTextColor", isSignal: true, isRequired: false, transformFunction: null }, underlineColor: { classPropertyName: "underlineColor", publicName: "underlineColor", isSignal: true, isRequired: false, transformFunction: null }, stepColor: { classPropertyName: "stepColor", publicName: "stepColor", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null }, prevLabel: { classPropertyName: "prevLabel", publicName: "prevLabel", isSignal: true, isRequired: false, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: false, transformFunction: null }, finishLabel: { classPropertyName: "finishLabel", publicName: "finishLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { stepChange: "stepChange", finish: "finish" }, queries: [{ propertyName: "_stepQuery", predicate: ClxStepComponent }], ngImport: i0, template: `
10715
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxWizardComponent, isStandalone: true, selector: "clx-wizard", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, activeTextColor: { classPropertyName: "activeTextColor", publicName: "activeTextColor", isSignal: true, isRequired: false, transformFunction: null }, underlineColor: { classPropertyName: "underlineColor", publicName: "underlineColor", isSignal: true, isRequired: false, transformFunction: null }, stepColor: { classPropertyName: "stepColor", publicName: "stepColor", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null }, prevLabel: { classPropertyName: "prevLabel", publicName: "prevLabel", isSignal: true, isRequired: false, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: false, transformFunction: null }, finishLabel: { classPropertyName: "finishLabel", publicName: "finishLabel", isSignal: true, isRequired: false, transformFunction: null }, finishLoading: { classPropertyName: "finishLoading", publicName: "finishLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { stepChange: "stepChange", finish: "finish" }, queries: [{ propertyName: "_stepQuery", predicate: ClxStepComponent }], ngImport: i0, template: `
10613
10716
  <div [class]="_rootClass()">
10614
10717
 
10615
10718
  <!-- ══ HORIZONTAL: clx-tabs indicator ═══════════════════════════════ -->
@@ -10707,7 +10810,8 @@ class ClxWizardComponent {
10707
10810
  size="sm"
10708
10811
  icon="check_circle"
10709
10812
  iconPosition="right"
10710
- [disabled]="!_currentStepValid()"
10813
+ [disabled]="!_currentStepValid() || finishLoading()"
10814
+ [loading]="finishLoading()"
10711
10815
  (click)="_onFinish()"
10712
10816
  >{{ finishLabel() }}</button>
10713
10817
  }
@@ -10816,7 +10920,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
10816
10920
  size="sm"
10817
10921
  icon="check_circle"
10818
10922
  iconPosition="right"
10819
- [disabled]="!_currentStepValid()"
10923
+ [disabled]="!_currentStepValid() || finishLoading()"
10924
+ [loading]="finishLoading()"
10820
10925
  (click)="_onFinish()"
10821
10926
  >{{ finishLabel() }}</button>
10822
10927
  }
@@ -10824,7 +10929,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
10824
10929
  </div>
10825
10930
  </div>
10826
10931
  `, styles: ["@keyframes clx-wizard-fade-slide{0%{opacity:0;transform:translate(10px)}to{opacity:1;transform:translate(0)}}.clx-wizard-step-enter{animation:clx-wizard-fade-slide .22s cubic-bezier(.4,0,.2,1) both}@keyframes clx-wizard-fill{0%{width:0%}to{width:100%}}.clx-wizard-connector-fill{animation:clx-wizard-fill .3s ease both}clx-step[data-active=false]{display:none}\n"] }]
10827
- }], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], activeTextColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTextColor", required: false }] }], underlineColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "underlineColor", required: false }] }], stepColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepColor", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], linear: [{ type: i0.Input, args: [{ isSignal: true, alias: "linear", required: false }] }], prevLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], finishLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "finishLabel", required: false }] }], stepChange: [{ type: i0.Output, args: ["stepChange"] }], finish: [{ type: i0.Output, args: ["finish"] }], _stepQuery: [{
10932
+ }], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], activeTextColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTextColor", required: false }] }], underlineColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "underlineColor", required: false }] }], stepColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepColor", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], linear: [{ type: i0.Input, args: [{ isSignal: true, alias: "linear", required: false }] }], prevLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], finishLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "finishLabel", required: false }] }], finishLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "finishLoading", required: false }] }], stepChange: [{ type: i0.Output, args: ["stepChange"] }], finish: [{ type: i0.Output, args: ["finish"] }], _stepQuery: [{
10828
10933
  type: ContentChildren,
10829
10934
  args: [ClxStepComponent]
10830
10935
  }] } });
@@ -15844,13 +15949,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15844
15949
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], retryLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "retryLabel", required: false }] }], backLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "backLabel", required: false }] }], errorId: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorId", required: false }] }], retry: [{ type: i0.Output, args: ["retry"] }], back: [{ type: i0.Output, args: ["back"] }] } });
15845
15950
 
15846
15951
  class ClxStatCardComponent {
15952
+ _themeSvc = inject(ClxThemeService);
15847
15953
  metric = input.required(...(ngDevMode ? [{ debugName: "metric" }] : /* istanbul ignore next */ []));
15848
15954
  chartData = input([30, 45, 28, 60, 40, 70, 55, 80, 50, 90], ...(ngDevMode ? [{ debugName: "chartData" }] : /* istanbul ignore next */ []));
15849
15955
  color = input('indigo', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
15850
15956
  layout = input('horizontal', ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
15851
15957
  _hostClass = computed(() => {
15852
15958
  const border = resolveColor(this.color()).borderLight;
15853
- const base = `bg-white rounded-xl shadow-md border ${border} p-5 w-full overflow-hidden min-w-[260px]`;
15959
+ const radius = resolveContainerRadius(this._themeSvc.config().borderRadius);
15960
+ const base = `bg-clx-surface ${radius} shadow-md border ${border} p-5 w-full overflow-hidden min-w-[260px]`;
15854
15961
  return `flex items-center gap-4 ${base}`;
15855
15962
  }, ...(ngDevMode ? [{ debugName: "_hostClass" }] : /* istanbul ignore next */ []));
15856
15963
  _barHeights = computed(() => {
@@ -15878,7 +15985,7 @@ class ClxStatCardComponent {
15878
15985
  }, ...(ngDevMode ? [{ debugName: "_trendArrowIcon" }] : /* istanbul ignore next */ []));
15879
15986
  _trendTextClass = computed(() => {
15880
15987
  const d = this.metric().trendDirection;
15881
- return d === 'up' ? 'text-green-500' : d === 'down' ? 'text-red-500' : 'text-slate-400';
15988
+ return d === 'up' ? 'text-green-500' : d === 'down' ? 'text-red-500' : 'text-clx-text-subtle';
15882
15989
  }, ...(ngDevMode ? [{ debugName: "_trendTextClass" }] : /* istanbul ignore next */ []));
15883
15990
  _trendIcon = computed(() => {
15884
15991
  const d = this.metric().trendDirection;
@@ -15941,8 +16048,8 @@ class ClxStatCardComponent {
15941
16048
 
15942
16049
  <!-- Text block -->
15943
16050
  <div class="flex flex-col gap-1 min-w-0 flex-1">
15944
- <span class="text-sm text-slate-500 truncate">{{ metric().title }}</span>
15945
- <span class="text-2xl font-semibold text-slate-900 leading-tight">
16051
+ <span class="text-sm text-clx-text-muted truncate">{{ metric().title }}</span>
16052
+ <span class="text-2xl font-semibold text-clx-text leading-tight">
15946
16053
  {{ _formattedValue() }}
15947
16054
  </span>
15948
16055
  <span clx-badge
@@ -15978,8 +16085,8 @@ class ClxStatCardComponent {
15978
16085
  <div class="w-11 h-11 rounded-full flex items-center justify-center mb-2" [class]="_iconBgSubtle()">
15979
16086
  <span clx-icon [name]="metric().icon" size="md" [class]="_iconTextBase()"></span>
15980
16087
  </div>
15981
- <span class="text-sm text-slate-500 truncate">{{ metric().title }}</span>
15982
- <span class="text-2xl font-semibold text-slate-900 leading-tight">{{ _formattedValue() }}</span>
16088
+ <span class="text-sm text-clx-text-muted truncate">{{ metric().title }}</span>
16089
+ <span class="text-2xl font-semibold text-clx-text leading-tight">{{ _formattedValue() }}</span>
15983
16090
  </div>
15984
16091
 
15985
16092
  <!-- Col derecha: barras + tendencia -->
@@ -15994,7 +16101,7 @@ class ClxStatCardComponent {
15994
16101
  }
15995
16102
  </div>
15996
16103
  <!-- Tendencia -->
15997
- <div class="flex items-center gap-1 text-xs text-slate-400 whitespace-nowrap">
16104
+ <div class="flex items-center gap-1 text-xs text-clx-text-subtle whitespace-nowrap">
15998
16105
  {{ metric().trendDirection === 'up' ? 'Increased By' : 'Decreased By' }}
15999
16106
  <span class="font-semibold" [class]="_trendTextClass()">{{ metric().trend }}</span>
16000
16107
  <span clx-icon [name]="_trendArrowIcon()" size="sm" [class]="_trendTextClass()"></span>
@@ -16013,8 +16120,8 @@ class ClxStatCardComponent {
16013
16120
  <span clx-icon [name]="_trendIcon()" size="xxs"></span>
16014
16121
  {{ metric().trend }}
16015
16122
  </span>
16016
- <span class="text-2xl font-semibold text-slate-900 leading-tight">{{ _formattedValue() }}</span>
16017
- <span class="text-xs font-semibold tracking-widest text-slate-400 uppercase">{{ metric().title }}</span>
16123
+ <span class="text-2xl font-semibold text-clx-text leading-tight">{{ _formattedValue() }}</span>
16124
+ <span class="text-xs font-semibold tracking-widest text-clx-text-subtle uppercase">{{ metric().title }}</span>
16018
16125
  </div>
16019
16126
 
16020
16127
  <!-- Col derecha: solo icono -->
@@ -16044,8 +16151,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
16044
16151
 
16045
16152
  <!-- Text block -->
16046
16153
  <div class="flex flex-col gap-1 min-w-0 flex-1">
16047
- <span class="text-sm text-slate-500 truncate">{{ metric().title }}</span>
16048
- <span class="text-2xl font-semibold text-slate-900 leading-tight">
16154
+ <span class="text-sm text-clx-text-muted truncate">{{ metric().title }}</span>
16155
+ <span class="text-2xl font-semibold text-clx-text leading-tight">
16049
16156
  {{ _formattedValue() }}
16050
16157
  </span>
16051
16158
  <span clx-badge
@@ -16081,8 +16188,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
16081
16188
  <div class="w-11 h-11 rounded-full flex items-center justify-center mb-2" [class]="_iconBgSubtle()">
16082
16189
  <span clx-icon [name]="metric().icon" size="md" [class]="_iconTextBase()"></span>
16083
16190
  </div>
16084
- <span class="text-sm text-slate-500 truncate">{{ metric().title }}</span>
16085
- <span class="text-2xl font-semibold text-slate-900 leading-tight">{{ _formattedValue() }}</span>
16191
+ <span class="text-sm text-clx-text-muted truncate">{{ metric().title }}</span>
16192
+ <span class="text-2xl font-semibold text-clx-text leading-tight">{{ _formattedValue() }}</span>
16086
16193
  </div>
16087
16194
 
16088
16195
  <!-- Col derecha: barras + tendencia -->
@@ -16097,7 +16204,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
16097
16204
  }
16098
16205
  </div>
16099
16206
  <!-- Tendencia -->
16100
- <div class="flex items-center gap-1 text-xs text-slate-400 whitespace-nowrap">
16207
+ <div class="flex items-center gap-1 text-xs text-clx-text-subtle whitespace-nowrap">
16101
16208
  {{ metric().trendDirection === 'up' ? 'Increased By' : 'Decreased By' }}
16102
16209
  <span class="font-semibold" [class]="_trendTextClass()">{{ metric().trend }}</span>
16103
16210
  <span clx-icon [name]="_trendArrowIcon()" size="sm" [class]="_trendTextClass()"></span>
@@ -16116,8 +16223,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
16116
16223
  <span clx-icon [name]="_trendIcon()" size="xxs"></span>
16117
16224
  {{ metric().trend }}
16118
16225
  </span>
16119
- <span class="text-2xl font-semibold text-slate-900 leading-tight">{{ _formattedValue() }}</span>
16120
- <span class="text-xs font-semibold tracking-widest text-slate-400 uppercase">{{ metric().title }}</span>
16226
+ <span class="text-2xl font-semibold text-clx-text leading-tight">{{ _formattedValue() }}</span>
16227
+ <span class="text-xs font-semibold tracking-widest text-clx-text-subtle uppercase">{{ metric().title }}</span>
16121
16228
  </div>
16122
16229
 
16123
16230
  <!-- Col derecha: solo icono -->
@@ -16129,6 +16236,469 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
16129
16236
  }]
16130
16237
  }], ctorParameters: () => [], propDecorators: { metric: [{ type: i0.Input, args: [{ isSignal: true, alias: "metric", required: true }] }], chartData: [{ type: i0.Input, args: [{ isSignal: true, alias: "chartData", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }] } });
16131
16238
 
16239
+ /** Standard chrome for any dashboard widget (stat card, chart, table, list...) — header with
16240
+ * title + an optional drag handle + an overlay menu for per-widget actions (configure, resize,
16241
+ * remove), plus shared loading/empty/error states so individual widgets never re-implement them.
16242
+ * Widget content itself is projected via the default <ng-content>; callers keep full control of
16243
+ * what renders inside once state() is 'ready'. */
16244
+ class ClxWidgetCardComponent {
16245
+ _themeSvc = inject(ClxThemeService);
16246
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
16247
+ state = input('ready', ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
16248
+ draggable = input(false, ...(ngDevMode ? [{ debugName: "draggable" }] : /* istanbul ignore next */ []));
16249
+ menuActions = input([], ...(ngDevMode ? [{ debugName: "menuActions" }] : /* istanbul ignore next */ []));
16250
+ menuLabel = input('Opciones del widget', ...(ngDevMode ? [{ debugName: "menuLabel" }] : /* istanbul ignore next */ []));
16251
+ emptyMessage = input('Sin datos en el rango seleccionado', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
16252
+ errorMessage = input('No se pudo cargar este widget', ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
16253
+ retryLabel = input('Reintentar', ...(ngDevMode ? [{ debugName: "retryLabel" }] : /* istanbul ignore next */ []));
16254
+ menuAction = output();
16255
+ retry = output();
16256
+ _sso = inject(ScrollStrategyOptions);
16257
+ _scrollStrategy = this._sso.reposition();
16258
+ _positions = [
16259
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
16260
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
16261
+ ];
16262
+ _menuOpen = signal(false, ...(ngDevMode ? [{ debugName: "_menuOpen" }] : /* istanbul ignore next */ []));
16263
+ _menuPanelClass = computed(() => {
16264
+ const t = resolveColor(this._themeSvc.cardBorderColor());
16265
+ return `w-48 p-1.5 bg-clx-surface rounded-xl shadow-xl border ${t.borderLight} overflow-hidden mt-1`;
16266
+ }, ...(ngDevMode ? [{ debugName: "_menuPanelClass" }] : /* istanbul ignore next */ []));
16267
+ _hostClass = computed(() => {
16268
+ const t = resolveColor(this._themeSvc.cardBorderColor());
16269
+ const radius = resolveContainerRadius(this._themeSvc.config().borderRadius);
16270
+ return `flex flex-col bg-clx-surface ${radius} border ${t.borderLight} shadow-sm overflow-hidden h-full`;
16271
+ }, ...(ngDevMode ? [{ debugName: "_hostClass" }] : /* istanbul ignore next */ []));
16272
+ _toggleMenu() { this._menuOpen.update(v => !v); }
16273
+ _closeMenu() { this._menuOpen.set(false); }
16274
+ _onMenuKeydown(event) {
16275
+ if (event.key === 'Escape')
16276
+ this._closeMenu();
16277
+ }
16278
+ _onMenuAction(action) {
16279
+ this.menuAction.emit(action);
16280
+ this._closeMenu();
16281
+ }
16282
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxWidgetCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
16283
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxWidgetCardComponent, isStandalone: true, selector: "clx-widget-card", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: true, isRequired: false, transformFunction: null }, menuActions: { classPropertyName: "menuActions", publicName: "menuActions", isSignal: true, isRequired: false, transformFunction: null }, menuLabel: { classPropertyName: "menuLabel", publicName: "menuLabel", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, retryLabel: { classPropertyName: "retryLabel", publicName: "retryLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuAction: "menuAction", retry: "retry" }, host: { properties: { "class": "_hostClass()" } }, ngImport: i0, template: `
16284
+ <div class="flex items-center justify-between gap-2 px-4 py-3 border-b border-clx-border-soft shrink-0">
16285
+ <div class="flex items-center gap-2 min-w-0">
16286
+ @if (draggable()) {
16287
+ <span clx-icon name="drag_indicator" size="sm" class="text-clx-text-subtle cursor-grab shrink-0"></span>
16288
+ }
16289
+ <p class="text-sm font-semibold text-clx-text-label truncate">{{ title() }}</p>
16290
+ </div>
16291
+
16292
+ @if (menuActions().length) {
16293
+ <button
16294
+ #origin="cdkOverlayOrigin"
16295
+ cdkOverlayOrigin
16296
+ type="button"
16297
+ class="shrink-0 w-7 h-7 rounded-lg flex items-center justify-center text-clx-text-subtle hover:bg-clx-surface-2 cursor-pointer"
16298
+ [attr.aria-label]="menuLabel()"
16299
+ (click)="_toggleMenu()">
16300
+ <span clx-icon name="more_vert" size="sm"></span>
16301
+ </button>
16302
+
16303
+ <ng-template
16304
+ cdkConnectedOverlay
16305
+ [cdkConnectedOverlayOrigin]="origin"
16306
+ [cdkConnectedOverlayOpen]="_menuOpen()"
16307
+ [cdkConnectedOverlayPositions]="_positions"
16308
+ [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
16309
+ [cdkConnectedOverlayPush]="true"
16310
+ (overlayOutsideClick)="_closeMenu()"
16311
+ (overlayKeydown)="_onMenuKeydown($event)">
16312
+ <div [class]="_menuPanelClass()">
16313
+ @for (item of menuActions(); track item.action) {
16314
+ <button
16315
+ type="button"
16316
+ class="w-full text-left px-3 py-2 rounded-lg text-sm flex items-center gap-2 hover:bg-clx-surface-2 cursor-pointer"
16317
+ [class]="item.danger ? 'text-red-600' : 'text-clx-text-label'"
16318
+ (click)="_onMenuAction(item.action)">
16319
+ <span clx-icon [name]="item.icon" size="sm" [class]="item.danger ? 'text-red-600' : 'text-clx-text-subtle'"></span>
16320
+ {{ item.label }}
16321
+ </button>
16322
+ }
16323
+ </div>
16324
+ </ng-template>
16325
+ }
16326
+ </div>
16327
+
16328
+ <div class="flex-1 min-h-0 overflow-auto">
16329
+ @switch (state()) {
16330
+ @case ('loading') {
16331
+ <div class="h-full flex flex-col gap-3 justify-center p-4">
16332
+ <clx-skeleton variant="text" width="100%" height="12px" />
16333
+ <clx-skeleton variant="text" width="85%" height="12px" />
16334
+ <clx-skeleton variant="text" width="92%" height="12px" />
16335
+ </div>
16336
+ }
16337
+ @case ('empty') {
16338
+ <div class="h-full flex flex-col items-center justify-center gap-2 p-6 text-center">
16339
+ <div class="w-11 h-11 rounded-xl bg-clx-surface-2 flex items-center justify-center">
16340
+ <span clx-icon name="inbox" size="md" class="text-clx-text-subtle"></span>
16341
+ </div>
16342
+ <p class="text-xs text-clx-text-subtle">{{ emptyMessage() }}</p>
16343
+ </div>
16344
+ }
16345
+ @case ('error') {
16346
+ <div class="h-full flex flex-col items-center justify-center gap-3 p-6 text-center">
16347
+ <div class="w-11 h-11 rounded-xl bg-red-50 flex items-center justify-center">
16348
+ <span clx-icon name="error" size="md" class="text-red-600"></span>
16349
+ </div>
16350
+ <p class="text-xs text-clx-text-subtle">{{ errorMessage() }}</p>
16351
+ <button
16352
+ type="button"
16353
+ class="text-xs font-semibold px-3 py-1.5 rounded-lg border border-clx-border text-clx-text-label hover:bg-clx-surface-2 cursor-pointer"
16354
+ (click)="retry.emit()">
16355
+ {{ retryLabel() }}
16356
+ </button>
16357
+ </div>
16358
+ }
16359
+ @default {
16360
+ <ng-content />
16361
+ }
16362
+ }
16363
+ </div>
16364
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
16365
+ }
16366
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxWidgetCardComponent, decorators: [{
16367
+ type: Component,
16368
+ args: [{
16369
+ selector: 'clx-widget-card',
16370
+ standalone: true,
16371
+ imports: [OverlayModule, ClxIconComponent, ClxSkeletonComponent],
16372
+ encapsulation: ViewEncapsulation.None,
16373
+ changeDetection: ChangeDetectionStrategy.OnPush,
16374
+ host: { '[class]': '_hostClass()' },
16375
+ template: `
16376
+ <div class="flex items-center justify-between gap-2 px-4 py-3 border-b border-clx-border-soft shrink-0">
16377
+ <div class="flex items-center gap-2 min-w-0">
16378
+ @if (draggable()) {
16379
+ <span clx-icon name="drag_indicator" size="sm" class="text-clx-text-subtle cursor-grab shrink-0"></span>
16380
+ }
16381
+ <p class="text-sm font-semibold text-clx-text-label truncate">{{ title() }}</p>
16382
+ </div>
16383
+
16384
+ @if (menuActions().length) {
16385
+ <button
16386
+ #origin="cdkOverlayOrigin"
16387
+ cdkOverlayOrigin
16388
+ type="button"
16389
+ class="shrink-0 w-7 h-7 rounded-lg flex items-center justify-center text-clx-text-subtle hover:bg-clx-surface-2 cursor-pointer"
16390
+ [attr.aria-label]="menuLabel()"
16391
+ (click)="_toggleMenu()">
16392
+ <span clx-icon name="more_vert" size="sm"></span>
16393
+ </button>
16394
+
16395
+ <ng-template
16396
+ cdkConnectedOverlay
16397
+ [cdkConnectedOverlayOrigin]="origin"
16398
+ [cdkConnectedOverlayOpen]="_menuOpen()"
16399
+ [cdkConnectedOverlayPositions]="_positions"
16400
+ [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
16401
+ [cdkConnectedOverlayPush]="true"
16402
+ (overlayOutsideClick)="_closeMenu()"
16403
+ (overlayKeydown)="_onMenuKeydown($event)">
16404
+ <div [class]="_menuPanelClass()">
16405
+ @for (item of menuActions(); track item.action) {
16406
+ <button
16407
+ type="button"
16408
+ class="w-full text-left px-3 py-2 rounded-lg text-sm flex items-center gap-2 hover:bg-clx-surface-2 cursor-pointer"
16409
+ [class]="item.danger ? 'text-red-600' : 'text-clx-text-label'"
16410
+ (click)="_onMenuAction(item.action)">
16411
+ <span clx-icon [name]="item.icon" size="sm" [class]="item.danger ? 'text-red-600' : 'text-clx-text-subtle'"></span>
16412
+ {{ item.label }}
16413
+ </button>
16414
+ }
16415
+ </div>
16416
+ </ng-template>
16417
+ }
16418
+ </div>
16419
+
16420
+ <div class="flex-1 min-h-0 overflow-auto">
16421
+ @switch (state()) {
16422
+ @case ('loading') {
16423
+ <div class="h-full flex flex-col gap-3 justify-center p-4">
16424
+ <clx-skeleton variant="text" width="100%" height="12px" />
16425
+ <clx-skeleton variant="text" width="85%" height="12px" />
16426
+ <clx-skeleton variant="text" width="92%" height="12px" />
16427
+ </div>
16428
+ }
16429
+ @case ('empty') {
16430
+ <div class="h-full flex flex-col items-center justify-center gap-2 p-6 text-center">
16431
+ <div class="w-11 h-11 rounded-xl bg-clx-surface-2 flex items-center justify-center">
16432
+ <span clx-icon name="inbox" size="md" class="text-clx-text-subtle"></span>
16433
+ </div>
16434
+ <p class="text-xs text-clx-text-subtle">{{ emptyMessage() }}</p>
16435
+ </div>
16436
+ }
16437
+ @case ('error') {
16438
+ <div class="h-full flex flex-col items-center justify-center gap-3 p-6 text-center">
16439
+ <div class="w-11 h-11 rounded-xl bg-red-50 flex items-center justify-center">
16440
+ <span clx-icon name="error" size="md" class="text-red-600"></span>
16441
+ </div>
16442
+ <p class="text-xs text-clx-text-subtle">{{ errorMessage() }}</p>
16443
+ <button
16444
+ type="button"
16445
+ class="text-xs font-semibold px-3 py-1.5 rounded-lg border border-clx-border text-clx-text-label hover:bg-clx-surface-2 cursor-pointer"
16446
+ (click)="retry.emit()">
16447
+ {{ retryLabel() }}
16448
+ </button>
16449
+ </div>
16450
+ }
16451
+ @default {
16452
+ <ng-content />
16453
+ }
16454
+ }
16455
+ </div>
16456
+ `,
16457
+ }]
16458
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], draggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "draggable", required: false }] }], menuActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuActions", required: false }] }], menuLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuLabel", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], retryLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "retryLabel", required: false }] }], menuAction: [{ type: i0.Output, args: ["menuAction"] }], retry: [{ type: i0.Output, args: ["retry"] }] } });
16459
+
16460
+ /** Drop-in cell for a `clxCell` table column: label + inline progress bar + trailing value —
16461
+ * e.g. "Canadá [████████░░] 6,192" rows in a "sales by country/branch" dashboard widget. Mount it
16462
+ * inside `<ng-template clxCell let-row>` instead of hand-building the flex row each time. */
16463
+ class ClxTableProgressCellComponent {
16464
+ label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
16465
+ value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
16466
+ max = input(100, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
16467
+ color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
16468
+ /** Optional leading glyph (e.g. a flag emoji) shown before the label. */
16469
+ leadingIcon = input(undefined, ...(ngDevMode ? [{ debugName: "leadingIcon" }] : /* istanbul ignore next */ []));
16470
+ /** Overrides the trailing number's text (e.g. a formatted "6,192") — falls back to the raw value(). */
16471
+ displayValue = input(undefined, ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
16472
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTableProgressCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
16473
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxTableProgressCellComponent, isStandalone: true, selector: "clx-table-progress-cell", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, leadingIcon: { classPropertyName: "leadingIcon", publicName: "leadingIcon", isSignal: true, isRequired: false, transformFunction: null }, displayValue: { classPropertyName: "displayValue", publicName: "displayValue", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex items-center gap-2.5 w-full" }, ngImport: i0, template: `
16474
+ @if (leadingIcon()) {
16475
+ <span class="text-base shrink-0">{{ leadingIcon() }}</span>
16476
+ }
16477
+ <span class="text-sm text-clx-text-label shrink-0 w-24 truncate">{{ label() }}</span>
16478
+ <div class="flex-1 min-w-0">
16479
+ <clx-progress-bar [value]="value()" [max]="max()" [color]="color()" size="xs" />
16480
+ </div>
16481
+ <span class="text-sm font-semibold text-clx-text tabular-nums shrink-0">{{ displayValue() ?? value() }}</span>
16482
+ `, isInline: true, dependencies: [{ kind: "component", type: ClxProgressBarComponent, selector: "clx-progress-bar", inputs: ["value", "max", "color", "size", "variant", "label", "showValue", "valueFormat", "segments"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
16483
+ }
16484
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTableProgressCellComponent, decorators: [{
16485
+ type: Component,
16486
+ args: [{
16487
+ selector: 'clx-table-progress-cell',
16488
+ standalone: true,
16489
+ imports: [ClxProgressBarComponent],
16490
+ encapsulation: ViewEncapsulation.None,
16491
+ changeDetection: ChangeDetectionStrategy.OnPush,
16492
+ host: { class: 'flex items-center gap-2.5 w-full' },
16493
+ template: `
16494
+ @if (leadingIcon()) {
16495
+ <span class="text-base shrink-0">{{ leadingIcon() }}</span>
16496
+ }
16497
+ <span class="text-sm text-clx-text-label shrink-0 w-24 truncate">{{ label() }}</span>
16498
+ <div class="flex-1 min-w-0">
16499
+ <clx-progress-bar [value]="value()" [max]="max()" [color]="color()" size="xs" />
16500
+ </div>
16501
+ <span class="text-sm font-semibold text-clx-text tabular-nums shrink-0">{{ displayValue() ?? value() }}</span>
16502
+ `,
16503
+ }]
16504
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], leadingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "leadingIcon", required: false }] }], displayValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayValue", required: false }] }] } });
16505
+
16506
+ /** Drop-in cell for a `clxCell` table column: avatar (or initials) + label on the left, a status
16507
+ * pill on the right — e.g. a "recent orders" dashboard widget row like "Orden #4582 ⋯ Entregado".
16508
+ * Mount it inside `<ng-template clxCell let-row>` instead of hand-building the flex row each time. */
16509
+ class ClxTableStatusCellComponent {
16510
+ label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
16511
+ avatarSrc = input(null, ...(ngDevMode ? [{ debugName: "avatarSrc" }] : /* istanbul ignore next */ []));
16512
+ avatarInitials = input(null, ...(ngDevMode ? [{ debugName: "avatarInitials" }] : /* istanbul ignore next */ []));
16513
+ avatarColor = input(undefined, ...(ngDevMode ? [{ debugName: "avatarColor" }] : /* istanbul ignore next */ []));
16514
+ statusLabel = input.required(...(ngDevMode ? [{ debugName: "statusLabel" }] : /* istanbul ignore next */ []));
16515
+ statusColor = input('slate', ...(ngDevMode ? [{ debugName: "statusColor" }] : /* istanbul ignore next */ []));
16516
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTableStatusCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
16517
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.15", type: ClxTableStatusCellComponent, isStandalone: true, selector: "clx-table-status-cell", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, avatarSrc: { classPropertyName: "avatarSrc", publicName: "avatarSrc", isSignal: true, isRequired: false, transformFunction: null }, avatarInitials: { classPropertyName: "avatarInitials", publicName: "avatarInitials", isSignal: true, isRequired: false, transformFunction: null }, avatarColor: { classPropertyName: "avatarColor", publicName: "avatarColor", isSignal: true, isRequired: false, transformFunction: null }, statusLabel: { classPropertyName: "statusLabel", publicName: "statusLabel", isSignal: true, isRequired: true, transformFunction: null }, statusColor: { classPropertyName: "statusColor", publicName: "statusColor", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex items-center justify-between gap-3 w-full" }, ngImport: i0, template: `
16518
+ <div class="flex items-center gap-2.5 min-w-0">
16519
+ <clx-avatar [src]="avatarSrc()" [initials]="avatarInitials()" [color]="avatarColor()" size="xs" />
16520
+ <span class="text-sm text-clx-text-label truncate">{{ label() }}</span>
16521
+ </div>
16522
+ <span clx-badge [color]="statusColor()" variant="light" shape="pill" size="xs" class="shrink-0">
16523
+ {{ statusLabel() }}
16524
+ </span>
16525
+ `, isInline: true, dependencies: [{ kind: "component", type: ClxAvatarComponent, selector: "clx-avatar", inputs: ["src", "initials", "iconName", "color", "size", "shape", "alt"] }, { kind: "component", type: ClxBadgeComponent, selector: "span[clx-badge]", inputs: ["variant", "color", "size", "shape", "positioned"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
16526
+ }
16527
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTableStatusCellComponent, decorators: [{
16528
+ type: Component,
16529
+ args: [{
16530
+ selector: 'clx-table-status-cell',
16531
+ standalone: true,
16532
+ imports: [ClxAvatarComponent, ClxBadgeComponent],
16533
+ encapsulation: ViewEncapsulation.None,
16534
+ changeDetection: ChangeDetectionStrategy.OnPush,
16535
+ host: { class: 'flex items-center justify-between gap-3 w-full' },
16536
+ template: `
16537
+ <div class="flex items-center gap-2.5 min-w-0">
16538
+ <clx-avatar [src]="avatarSrc()" [initials]="avatarInitials()" [color]="avatarColor()" size="xs" />
16539
+ <span class="text-sm text-clx-text-label truncate">{{ label() }}</span>
16540
+ </div>
16541
+ <span clx-badge [color]="statusColor()" variant="light" shape="pill" size="xs" class="shrink-0">
16542
+ {{ statusLabel() }}
16543
+ </span>
16544
+ `,
16545
+ }]
16546
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], avatarSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarSrc", required: false }] }], avatarInitials: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarInitials", required: false }] }], avatarColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarColor", required: false }] }], statusLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "statusLabel", required: true }] }], statusColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "statusColor", required: false }] }] } });
16547
+
16548
+ /** Content template for one grid cell — `<ng-template clxGridItem let-item>...</ng-template>`,
16549
+ * mirroring the `clxCell`/`clxColumn` convention ClxTableComponent already uses for projected
16550
+ * per-row templates. Typically a <clx-widget-card> wrapping whatever the widget actually renders. */
16551
+ class ClxGridItemDirective {
16552
+ templateRef;
16553
+ constructor(templateRef) {
16554
+ this.templateRef = templateRef;
16555
+ }
16556
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxGridItemDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
16557
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.15", type: ClxGridItemDirective, isStandalone: true, selector: "ng-template[clxGridItem]", ngImport: i0 });
16558
+ }
16559
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxGridItemDirective, decorators: [{
16560
+ type: Directive,
16561
+ args: [{ selector: 'ng-template[clxGridItem]', standalone: true }]
16562
+ }], ctorParameters: () => [{ type: i0.TemplateRef }] });
16563
+ /** Content-agnostic configurable dashboard grid: positions/sizes projected widgets on a CSS grid
16564
+ * using column/row units, supports drag-to-reorder (via Angular CDK's CdkDropList/CdkDrag — no
16565
+ * new dependency, @angular/cdk is already pulled in by ClxProfileComponent/ClxWidgetCardComponent's
16566
+ * overlay usage) and resize-by-dragging a corner handle. Persistence is the caller's job: listen
16567
+ * to (layoutChange) and save the resulting ClxGridItem[] (e.g. to rp_dashboard_layouts.layout). */
16568
+ class ClxDashboardGridComponent {
16569
+ items = input.required(...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
16570
+ columns = input(4, ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
16571
+ rowHeight = input(92, ...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
16572
+ gap = input(16, ...(ngDevMode ? [{ debugName: "gap" }] : /* istanbul ignore next */ []));
16573
+ editable = input(false, ...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
16574
+ layoutChange = output();
16575
+ itemResize = output();
16576
+ _itemTemplate = contentChild(ClxGridItemDirective, ...(ngDevMode ? [{ debugName: "_itemTemplate" }] : /* istanbul ignore next */ []));
16577
+ _gridCols = computed(() => `repeat(${this.columns()}, minmax(0, 1fr))`, ...(ngDevMode ? [{ debugName: "_gridCols" }] : /* istanbul ignore next */ []));
16578
+ _resizing = null;
16579
+ _onDrop(event) {
16580
+ const next = [...this.items()];
16581
+ moveItemInArray(next, event.previousIndex, event.currentIndex);
16582
+ this.layoutChange.emit(next);
16583
+ }
16584
+ _onDragMoved(_event) {
16585
+ // Reordering is handled by cdkDropListDropped above; this hook exists for future free-form
16586
+ // (x/y pixel) positioning if a consumer needs it — not required for a reorderable column grid.
16587
+ }
16588
+ _startResize(event, item) {
16589
+ event.preventDefault();
16590
+ event.stopPropagation();
16591
+ this._resizing = { item, startX: event.clientX, startY: event.clientY, startW: item.w, startH: item.h };
16592
+ const cellW = event.target.closest('[style*="grid-column"]')?.clientWidth
16593
+ ?? (this.rowHeight() * 2);
16594
+ const onMove = (e) => {
16595
+ if (!this._resizing)
16596
+ return;
16597
+ const dx = e.clientX - this._resizing.startX;
16598
+ const dy = e.clientY - this._resizing.startY;
16599
+ const deltaCols = Math.round(dx / (cellW / this._resizing.startW || 1));
16600
+ const deltaRows = Math.round(dy / this.rowHeight());
16601
+ const nextW = Math.max(item.minW ?? 1, this._resizing.startW + deltaCols);
16602
+ const nextH = Math.max(item.minH ?? 1, this._resizing.startH + deltaRows);
16603
+ if (nextW !== item.w || nextH !== item.h) {
16604
+ item.w = nextW;
16605
+ item.h = nextH;
16606
+ this.itemResize.emit({ id: item.id, w: nextW, h: nextH });
16607
+ }
16608
+ };
16609
+ const onUp = () => {
16610
+ this._resizing = null;
16611
+ this.layoutChange.emit([...this.items()]);
16612
+ window.removeEventListener('pointermove', onMove);
16613
+ window.removeEventListener('pointerup', onUp);
16614
+ };
16615
+ window.addEventListener('pointermove', onMove);
16616
+ window.addEventListener('pointerup', onUp);
16617
+ }
16618
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxDashboardGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
16619
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxDashboardGridComponent, isStandalone: true, selector: "clx-dashboard-grid", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { layoutChange: "layoutChange", itemResize: "itemResize" }, host: { classAttribute: "block w-full" }, queries: [{ propertyName: "_itemTemplate", first: true, predicate: ClxGridItemDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
16620
+ <div
16621
+ cdkDropList
16622
+ [cdkDropListDisabled]="!editable()"
16623
+ (cdkDropListDropped)="_onDrop($event)"
16624
+ class="grid w-full"
16625
+ [style.grid-template-columns]="_gridCols()"
16626
+ [style.grid-auto-rows.px]="rowHeight()"
16627
+ [style.gap.px]="gap()">
16628
+ @for (item of items(); track item.id) {
16629
+ <div
16630
+ cdkDrag
16631
+ [cdkDragDisabled]="!editable()"
16632
+ [cdkDragData]="item"
16633
+ (cdkDragMoved)="_onDragMoved($event)"
16634
+ class="relative min-w-0"
16635
+ [class.cursor-grab]="editable()"
16636
+ [style.grid-column]="'span ' + item.w"
16637
+ [style.grid-row]="'span ' + item.h">
16638
+
16639
+ <ng-container
16640
+ [ngTemplateOutlet]="_itemTemplate()?.templateRef ?? null"
16641
+ [ngTemplateOutletContext]="{ $implicit: item }">
16642
+ </ng-container>
16643
+
16644
+ @if (editable()) {
16645
+ <div
16646
+ class="absolute -right-1 -bottom-1 w-3.5 h-3.5 rounded bg-clx-primary border-2 border-clx-surface shadow cursor-nwse-resize"
16647
+ (pointerdown)="_startResize($event, item)">
16648
+ </div>
16649
+ }
16650
+ </div>
16651
+ }
16652
+ </div>
16653
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1$2.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1$2.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
16654
+ }
16655
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxDashboardGridComponent, decorators: [{
16656
+ type: Component,
16657
+ args: [{
16658
+ selector: 'clx-dashboard-grid',
16659
+ standalone: true,
16660
+ imports: [NgTemplateOutlet, DragDropModule],
16661
+ encapsulation: ViewEncapsulation.None,
16662
+ changeDetection: ChangeDetectionStrategy.OnPush,
16663
+ host: { class: 'block w-full' },
16664
+ template: `
16665
+ <div
16666
+ cdkDropList
16667
+ [cdkDropListDisabled]="!editable()"
16668
+ (cdkDropListDropped)="_onDrop($event)"
16669
+ class="grid w-full"
16670
+ [style.grid-template-columns]="_gridCols()"
16671
+ [style.grid-auto-rows.px]="rowHeight()"
16672
+ [style.gap.px]="gap()">
16673
+ @for (item of items(); track item.id) {
16674
+ <div
16675
+ cdkDrag
16676
+ [cdkDragDisabled]="!editable()"
16677
+ [cdkDragData]="item"
16678
+ (cdkDragMoved)="_onDragMoved($event)"
16679
+ class="relative min-w-0"
16680
+ [class.cursor-grab]="editable()"
16681
+ [style.grid-column]="'span ' + item.w"
16682
+ [style.grid-row]="'span ' + item.h">
16683
+
16684
+ <ng-container
16685
+ [ngTemplateOutlet]="_itemTemplate()?.templateRef ?? null"
16686
+ [ngTemplateOutletContext]="{ $implicit: item }">
16687
+ </ng-container>
16688
+
16689
+ @if (editable()) {
16690
+ <div
16691
+ class="absolute -right-1 -bottom-1 w-3.5 h-3.5 rounded bg-clx-primary border-2 border-clx-surface shadow cursor-nwse-resize"
16692
+ (pointerdown)="_startResize($event, item)">
16693
+ </div>
16694
+ }
16695
+ </div>
16696
+ }
16697
+ </div>
16698
+ `,
16699
+ }]
16700
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], layoutChange: [{ type: i0.Output, args: ["layoutChange"] }], itemResize: [{ type: i0.Output, args: ["itemResize"] }], _itemTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ClxGridItemDirective), { isSignal: true }] }] } });
16701
+
16132
16702
  const PRODUCT_TAG_LABEL = {
16133
16703
  'new': 'Nuevo',
16134
16704
  'sale': 'Oferta',
@@ -18732,5 +19302,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
18732
19302
  * Generated bundle index. Do not edit.
18733
19303
  */
18734
19304
 
18735
- export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_FONT_CATALOG, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardExpiryInputComponent, ClxCardFooterDirective, ClxCardHeaderActionsDirective, ClxCardHeaderDirective, ClxCardNumberInputComponent, ClxCardValidators, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxCheckboxComponent, ClxCollapseComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorImageModalComponent, ClxEditorLinkModalComponent, ClxFabComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxMenuItemTrailingDirective, ClxModalComponent, ClxModalService, ClxNativeOverlayService, ClxNavGroupComponent, ClxNotificationComponent, ClxNumberComponent, ClxOtpComponent, ClxPageEmptyComponent, ClxPageHeaderComponent, ClxPageHeaderTitleDirective, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProductQuickViewComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSearchComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSocialIconComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableActionsComponent, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, detectClxCardBrand, isClxCardExpired, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
19305
+ export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_FONT_CATALOG, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardExpiryInputComponent, ClxCardFooterDirective, ClxCardHeaderActionsDirective, ClxCardHeaderDirective, ClxCardNumberInputComponent, ClxCardValidators, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxCheckboxComponent, ClxCollapseComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDashboardGridComponent, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorImageModalComponent, ClxEditorLinkModalComponent, ClxFabComponent, ClxFilterPanelComponent, ClxGridItemDirective, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxMenuItemTrailingDirective, ClxModalComponent, ClxModalService, ClxNativeOverlayService, ClxNavGroupComponent, ClxNotificationComponent, ClxNumberComponent, ClxOtpComponent, ClxPageEmptyComponent, ClxPageHeaderComponent, ClxPageHeaderTitleDirective, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProductQuickViewComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSearchComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSocialIconComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableActionsComponent, ClxTableComponent, ClxTableProgressCellComponent, ClxTableStatusCellComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWidgetCardComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, detectClxCardBrand, isClxCardExpired, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
18736
19306
  //# sourceMappingURL=codexly-ui.mjs.map