@ajuarezso/sui-design-system 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, input, computed, ChangeDetectionStrategy, Component, model, signal, viewChild, effect, untracked, output, inject, ElementRef, forwardRef, HostListener, TemplateRef, Directive, ViewContainerRef, DestroyRef, ContentChild, ViewChild, ViewEncapsulation, Renderer2, DOCUMENT, viewChildren, afterNextRender, ContentChildren, ViewChildren, Injectable } from '@angular/core';
2
+ import { InjectionToken, input, computed, ChangeDetectionStrategy, Component, model, signal, viewChild, effect, untracked, output, inject, ElementRef, forwardRef, TemplateRef, Directive, ViewContainerRef, DestroyRef, ContentChild, ViewChild, ViewEncapsulation, Renderer2, DOCUMENT, HostListener, viewChildren, afterNextRender, ContentChildren, ViewChildren, Injectable } from '@angular/core';
3
3
  import { NgTemplateOutlet, DOCUMENT as DOCUMENT$1 } from '@angular/common';
4
4
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
5
5
  import { Subscription } from 'rxjs';
@@ -3143,6 +3143,218 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
3143
3143
  }, template: "<button\n type=\"button\"\n class=\"sui-color-dot__inner\"\n role=\"radio\"\n [disabled]=\"state() === 'disabled' || null\"\n [attr.aria-checked]=\"state() === 'selected' ? 'true' : 'false'\"\n [attr.aria-label]=\"ariaLabel() || defaultAriaLabel()\"\n (click)=\"onClick()\"\n (touchstart)=\"$event.currentTarget\"\n (touchend)=\"$any($event.currentTarget).blur()\"\n></button>\n", styles: ["@charset \"UTF-8\";:host{display:inline-block;width:24px;height:24px;vertical-align:middle;--c: var(--sui-neutral-500)}:host([data-sui-color=gray]){--c: var(--sui-neutral-500)}:host([data-sui-color=blue]){--c: var(--sui-blue-500)}:host([data-sui-color=orange]){--c: var(--sui-orange-500)}:host([data-sui-color=red]){--c: var(--sui-red-500)}:host([data-sui-color=green]){--c: var(--sui-green-500)}:host([data-sui-color=yellow]){--c: var(--sui-yellow-500)}:host([data-sui-color=purple]){--c: var(--sui-purple-500)}:host([data-sui-color=sky]){--c: var(--sui-sky-500)}:host([data-sui-color=pink]){--c: var(--sui-pink-500)}:host([data-sui-color=teal]){--c: var(--sui-teal-500)}:host([data-sui-state=disabled]){--c: var(--sui-bg-soft-200)}.sui-color-dot__inner{position:relative;display:block;width:24px;height:24px;padding:0;margin:0;border:none;background:transparent;cursor:pointer;appearance:none;outline:none;border-radius:50%;transition:transform var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}.sui-color-dot__inner:after{content:\"\";position:absolute;inset:4px;border-radius:50%;background:var(--c);transition:inset var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}:host([data-sui-state=selected]) .sui-color-dot__inner:after{inset:6px}.sui-color-dot__inner:before{content:\"\";position:absolute;inset:3px;border-radius:50%;border:2px solid transparent;transition:border-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}:host([data-sui-state=selected]) .sui-color-dot__inner:before{border-color:var(--c)}.sui-color-dot__inner:active{transform:none}:host([data-sui-state=default]) .sui-color-dot__inner:hover:after,:host([data-sui-state=hover]) .sui-color-dot__inner:after{inset:5px}.sui-color-dot__inner:focus-visible{outline:2px solid var(--c);outline-offset:2px}:host([data-sui-state=disabled]) .sui-color-dot__inner{cursor:not-allowed;pointer-events:none}\n"] }]
3144
3144
  }], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], customHex: [{ type: i0.Input, args: [{ isSignal: true, alias: "customHex", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }] } });
3145
3145
 
3146
+ /**
3147
+ * Structural directive — marca un `<ng-template>` como panel del
3148
+ * sui-dropdown-menu. El componente padre captura este TemplateRef
3149
+ * via `@ContentChild(SuiDropdownPanel)` y lo monta en el CDK Overlay
3150
+ * cuando se abre el menu.
3151
+ *
3152
+ * Usa el patrón canon Angular (similar a `*matMenuContent` de Material):
3153
+ * un directive que se aplica al `<ng-template>` SE convierte en una
3154
+ * marca semántica, evitando que Angular renderee el contenido inline
3155
+ * cuando no hay `<ng-content>` matching para el template.
3156
+ *
3157
+ * Uso:
3158
+ * <sui-dropdown-menu>
3159
+ * <sui-compact-select trigger ... />
3160
+ * <ng-template suiDropdownPanel>
3161
+ * <sui-dropdown-item ...></sui-dropdown-item>
3162
+ * </ng-template>
3163
+ * </sui-dropdown-menu>
3164
+ */
3165
+ class SuiDropdownPanel {
3166
+ template = inject(TemplateRef);
3167
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SuiDropdownPanel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3168
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.17", type: SuiDropdownPanel, isStandalone: true, selector: "ng-template[suiDropdownPanel]", ngImport: i0 });
3169
+ }
3170
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SuiDropdownPanel, decorators: [{
3171
+ type: Directive,
3172
+ args: [{
3173
+ selector: 'ng-template[suiDropdownPanel]',
3174
+ standalone: true,
3175
+ }]
3176
+ }] });
3177
+
3178
+ /* Posiciones con fallback HORIZONTAL: la primera (start-aligned, abre a la
3179
+ derecha) es la default; si el trigger está pegado al borde derecho y no entra,
3180
+ CDK cae a la end-aligned (abre hacia la izquierda) y recién después flipea
3181
+ vertical. Sin esto, un trigger en la esquina derecha (ej. kebab ⋮ de una card)
3182
+ recorta el panel fuera del viewport. */
3183
+ const POSITIONS_BELOW = [
3184
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
3185
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
3186
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
3187
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
3188
+ ];
3189
+ const POSITIONS_ABOVE = [
3190
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
3191
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
3192
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
3193
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
3194
+ ];
3195
+ class SuiDropdownMenu {
3196
+ /** Si true, el panel matchea el ancho del trigger. Default: auto width. */
3197
+ matchWidth = input(false, ...(ngDevMode ? [{ debugName: "matchWidth" }] : /* istanbul ignore next */ []));
3198
+ /** Min-width fijo del panel (px). Si matchWidth + minWidth: `max(triggerWidth, minWidth)`. */
3199
+ minWidth = input(null, ...(ngDevMode ? [{ debugName: "minWidth" }] : /* istanbul ignore next */ []));
3200
+ /** Position primary. Default: auto = below con fallback above. */
3201
+ position = input('auto', ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
3202
+ /** Margen 8px del viewport edge (canon SUI). */
3203
+ viewportMargin = input(8, ...(ngDevMode ? [{ debugName: "viewportMargin" }] : /* istanbul ignore next */ []));
3204
+ /** Panel custom class para overrides de bg/padding/etc desde el consumer. */
3205
+ panelClass = input('', ...(ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []));
3206
+ /** Disabled — no abre el menu al clickear el trigger. */
3207
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3208
+ /**
3209
+ * Rol del popup para `aria-haspopup` del trigger. Default 'menu' (los items
3210
+ * proyectados llevan role="menuitem"). Los consumers de tipo listbox
3211
+ * (sui-select / compact-selects, items con role="option") DEBEN pasar
3212
+ * `popupRole="listbox"`. Ver SuiDropdownMenuPopupRole.
3213
+ */
3214
+ popupRole = input('menu', ...(ngDevMode ? [{ debugName: "popupRole" }] : /* istanbul ignore next */ []));
3215
+ /**
3216
+ * Origin externo. Si presente, el menu se ancla a ese elemento y NO renderea
3217
+ * el trigger wrapper interno (el parent maneja el click). Caso de uso:
3218
+ * compact-selects donde el host element es el trigger visual.
3219
+ */
3220
+ origin = input(null, ...(ngDevMode ? [{ debugName: "origin" }] : /* istanbul ignore next */ []));
3221
+ openedChange = output();
3222
+ /** Signal pública — wire al `[open]` del trigger para visual state. */
3223
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
3224
+ /** Wrapper del trigger projection — captura el click. NO existe si `[origin]` se pasa. */
3225
+ _triggerWrap;
3226
+ /** Panel directive proyectado — capturado via `[suiDropdownPanel]` en un ng-template. */
3227
+ _panel;
3228
+ _getTriggerEl() {
3229
+ const o = this.origin();
3230
+ if (o)
3231
+ return o instanceof ElementRef ? o.nativeElement : o;
3232
+ return this._triggerWrap?.nativeElement ?? null;
3233
+ }
3234
+ _vcr = inject(ViewContainerRef);
3235
+ _destroyRef = inject(DestroyRef);
3236
+ _overlay = inject(Overlay);
3237
+ _overlayRef = null;
3238
+ _overlaySubs = null;
3239
+ _resizeObserver = null;
3240
+ constructor() {
3241
+ this._destroyRef.onDestroy(() => this.close());
3242
+ }
3243
+ onTriggerClick(event) {
3244
+ if (this.disabled())
3245
+ return;
3246
+ event.stopPropagation();
3247
+ this.toggle();
3248
+ }
3249
+ onTriggerKeydown(event) {
3250
+ if (this.disabled())
3251
+ return;
3252
+ if (event.key === 'Enter' || event.key === ' ') {
3253
+ event.preventDefault();
3254
+ this.toggle();
3255
+ }
3256
+ else if (event.key === 'ArrowDown') {
3257
+ event.preventDefault();
3258
+ this.open();
3259
+ }
3260
+ }
3261
+ toggle() {
3262
+ if (this.isOpen())
3263
+ this.close();
3264
+ else
3265
+ this.open();
3266
+ }
3267
+ open() {
3268
+ if (this.disabled() || this._overlayRef || !this._panel)
3269
+ return;
3270
+ const triggerEl = this._getTriggerEl();
3271
+ if (!triggerEl)
3272
+ return;
3273
+ const positions = this.position() === 'above' ? POSITIONS_ABOVE : POSITIONS_BELOW;
3274
+ const positionStrategy = this._overlay
3275
+ .position()
3276
+ .flexibleConnectedTo(triggerEl)
3277
+ .withPositions(positions)
3278
+ .withPush(false)
3279
+ .withFlexibleDimensions(true)
3280
+ .withViewportMargin(this.viewportMargin());
3281
+ const triggerWidth = triggerEl.getBoundingClientRect().width;
3282
+ const minW = this.minWidth();
3283
+ const effectiveMinWidth = this.matchWidth() && minW !== null
3284
+ ? Math.max(triggerWidth, minW)
3285
+ : minW;
3286
+ const config = {
3287
+ positionStrategy,
3288
+ scrollStrategy: this._overlay.scrollStrategies.reposition(),
3289
+ hasBackdrop: true,
3290
+ backdropClass: 'cdk-overlay-transparent-backdrop',
3291
+ panelClass: ['sui-dropdown-menu__overlay-pane', this.panelClass()].filter(Boolean),
3292
+ disposeOnNavigation: true,
3293
+ };
3294
+ if (this.matchWidth() && minW === null) {
3295
+ config.width = `${triggerWidth}px`;
3296
+ }
3297
+ if (effectiveMinWidth !== null) {
3298
+ config.minWidth = effectiveMinWidth;
3299
+ }
3300
+ this._overlayRef = this._overlay.create(config);
3301
+ const portal = new TemplatePortal(this._panel.template, this._vcr);
3302
+ this._overlayRef.attach(portal);
3303
+ /* Width sync mientras el viewport cambia (canon sui-select). Solo aplica
3304
+ cuando matchWidth fija width o minWidth — para que el panel siga el trigger. */
3305
+ if (this.matchWidth() && typeof ResizeObserver !== 'undefined') {
3306
+ this._resizeObserver = new ResizeObserver(() => {
3307
+ if (!this._overlayRef)
3308
+ return;
3309
+ const w = triggerEl.getBoundingClientRect().width;
3310
+ if (minW === null) {
3311
+ this._overlayRef.updateSize({ width: `${w}px` });
3312
+ }
3313
+ else {
3314
+ this._overlayRef.updateSize({ minWidth: Math.max(w, minW) });
3315
+ }
3316
+ this._overlayRef.updatePosition();
3317
+ });
3318
+ this._resizeObserver.observe(triggerEl);
3319
+ }
3320
+ this._overlaySubs = new Subscription();
3321
+ this._overlaySubs.add(this._overlayRef.backdropClick().subscribe(() => this.close()));
3322
+ this._overlaySubs.add(this._overlayRef.keydownEvents().subscribe((e) => {
3323
+ if (e.key === 'Escape')
3324
+ this.close();
3325
+ }));
3326
+ this._overlaySubs.add(this._overlayRef.detachments().subscribe(() => {
3327
+ this.isOpen.set(false);
3328
+ }));
3329
+ this.isOpen.set(true);
3330
+ this.openedChange.emit(true);
3331
+ }
3332
+ close() {
3333
+ if (!this._overlayRef)
3334
+ return;
3335
+ this._overlaySubs?.unsubscribe();
3336
+ this._overlaySubs = null;
3337
+ this._resizeObserver?.disconnect();
3338
+ this._resizeObserver = null;
3339
+ this._overlayRef.dispose();
3340
+ this._overlayRef = null;
3341
+ this.isOpen.set(false);
3342
+ this.openedChange.emit(false);
3343
+ }
3344
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SuiDropdownMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
3345
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SuiDropdownMenu, isStandalone: true, selector: "sui-dropdown-menu", inputs: { matchWidth: { classPropertyName: "matchWidth", publicName: "matchWidth", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, viewportMargin: { classPropertyName: "viewportMargin", publicName: "viewportMargin", isSignal: true, isRequired: false, transformFunction: null }, panelClass: { classPropertyName: "panelClass", publicName: "panelClass", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, popupRole: { classPropertyName: "popupRole", publicName: "popupRole", isSignal: true, isRequired: false, transformFunction: null }, origin: { classPropertyName: "origin", publicName: "origin", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openedChange: "openedChange" }, queries: [{ propertyName: "_panel", first: true, predicate: SuiDropdownPanel, descendants: true }], viewQueries: [{ propertyName: "_triggerWrap", first: true, predicate: ["triggerWrap"], descendants: true }], exportAs: ["suiDropdownMenu"], ngImport: i0, template: "<!-- Trigger wrapper \u2014 captura click y keydown del trigger child. Solo se\n renderea cuando NO se pasa [origin] (modo content-projection canon).\n Cuando se pasa [origin], el parent maneja el trigger y llama\n menu.toggle() desde su propio click handler. -->\n@if (!origin()) {\n <span\n #triggerWrap\n class=\"sui-dropdown-menu__trigger\"\n [attr.aria-haspopup]=\"popupRole()\"\n [attr.aria-expanded]=\"isOpen()\"\n (click)=\"onTriggerClick($event)\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n <ng-content select=\"[trigger]\" />\n </span>\n}\n", styles: ["@charset \"UTF-8\";:host{display:contents}.sui-dropdown-menu__trigger{display:inline-block;cursor:pointer;outline:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3346
+ }
3347
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SuiDropdownMenu, decorators: [{
3348
+ type: Component,
3349
+ args: [{ selector: 'sui-dropdown-menu', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'suiDropdownMenu', template: "<!-- Trigger wrapper \u2014 captura click y keydown del trigger child. Solo se\n renderea cuando NO se pasa [origin] (modo content-projection canon).\n Cuando se pasa [origin], el parent maneja el trigger y llama\n menu.toggle() desde su propio click handler. -->\n@if (!origin()) {\n <span\n #triggerWrap\n class=\"sui-dropdown-menu__trigger\"\n [attr.aria-haspopup]=\"popupRole()\"\n [attr.aria-expanded]=\"isOpen()\"\n (click)=\"onTriggerClick($event)\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n <ng-content select=\"[trigger]\" />\n </span>\n}\n", styles: ["@charset \"UTF-8\";:host{display:contents}.sui-dropdown-menu__trigger{display:inline-block;cursor:pointer;outline:none}\n"] }]
3350
+ }], ctorParameters: () => [], propDecorators: { matchWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "matchWidth", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], viewportMargin: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportMargin", required: false }] }], panelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelClass", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], popupRole: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupRole", required: false }] }], origin: [{ type: i0.Input, args: [{ isSignal: true, alias: "origin", required: false }] }], openedChange: [{ type: i0.Output, args: ["openedChange"] }], _triggerWrap: [{
3351
+ type: ViewChild,
3352
+ args: ['triggerWrap', { static: false }]
3353
+ }], _panel: [{
3354
+ type: ContentChild,
3355
+ args: [SuiDropdownPanel]
3356
+ }] } });
3357
+
3146
3358
  /* ════════════════════════════════════════════════════════════════════════
3147
3359
  * HSV ↔ RGB ↔ HEX conversion helpers
3148
3360
  *
@@ -3391,6 +3603,7 @@ class SuiColorPicker {
3391
3603
  /** Hex input visible (separate del value model para validación en blur). */
3392
3604
  hexInput = signal('', ...(ngDevMode ? [{ debugName: "hexInput" }] : /* istanbul ignore next */ []));
3393
3605
  /** Refs del spectrum y sliders para cálculos de bounds */
3606
+ _menu = viewChild(SuiDropdownMenu, ...(ngDevMode ? [{ debugName: "_menu" }] : /* istanbul ignore next */ []));
3394
3607
  canvasRef = viewChild('canvas', ...(ngDevMode ? [{ debugName: "canvasRef" }] : /* istanbul ignore next */ []));
3395
3608
  hueRef = viewChild('hueSlider', ...(ngDevMode ? [{ debugName: "hueRef" }] : /* istanbul ignore next */ []));
3396
3609
  opacityRef = viewChild('opacitySlider', ...(ngDevMode ? [{ debugName: "opacityRef" }] : /* istanbul ignore next */ []));
@@ -3577,17 +3790,21 @@ class SuiColorPicker {
3577
3790
  toggle() {
3578
3791
  if (this.disabled())
3579
3792
  return;
3580
- this.open.update(v => !v);
3581
- // Al abrir, sync hex input con value actual
3582
- if (this.open()) {
3793
+ this._menu()?.toggle();
3794
+ }
3795
+ close() {
3796
+ this._menu()?.close();
3797
+ }
3798
+ /** El sui-dropdown-menu (CDK overlay) es el dueño del open/close: acá solo se
3799
+ sincroniza el signal `open` (aria-expanded, chevron, data-sui-open) y el
3800
+ hex input al abrir. Backdrop + Esc + detachments los maneja el menu. */
3801
+ onMenuOpenedChange(opened) {
3802
+ this.open.set(opened);
3803
+ if (opened) {
3583
3804
  const parsed = parseHexWithAlpha(this.value());
3584
3805
  this.hexInput.set(parsed?.hex ?? '');
3585
3806
  }
3586
3807
  }
3587
- close() {
3588
- if (this.open())
3589
- this.open.set(false);
3590
- }
3591
3808
  /* ─────────── Recommended dot click ─────────── */
3592
3809
  onPresetClick(preset) {
3593
3810
  // setColor atómico → garantiza que value, HSV y hexInput queden
@@ -3761,34 +3978,17 @@ class SuiColorPicker {
3761
3978
  // user canceló (Esc) — no-op.
3762
3979
  }
3763
3980
  }
3764
- /* ─────────── Outside click + Esc ─────────── */
3765
- onDocumentClick(target) {
3766
- if (!this.open())
3767
- return;
3768
- if (target instanceof Node && !this.hostRef.nativeElement.contains(target)) {
3769
- this.close();
3770
- }
3771
- }
3772
- onEscape() {
3773
- this.close();
3774
- }
3775
3981
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SuiColorPicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
3776
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SuiColorPicker, isStandalone: true, selector: "sui-color-picker", inputs: { colors: { classPropertyName: "colors", publicName: "colors", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, recommendedLabel: { classPropertyName: "recommendedLabel", publicName: "recommendedLabel", isSignal: true, isRequired: false, transformFunction: null }, presetsOnly: { classPropertyName: "presetsOnly", publicName: "presetsOnly", isSignal: true, isRequired: false, transformFunction: null }, alpha: { classPropertyName: "alpha", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null }, eyedropper: { classPropertyName: "eyedropper", publicName: "eyedropper", isSignal: true, isRequired: false, transformFunction: null }, showFormatLabel: { classPropertyName: "showFormatLabel", publicName: "showFormatLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", change: "change" }, host: { listeners: { "document:click": "onDocumentClick($event.target)", "document:keydown.escape": "onEscape()" }, properties: { "attr.data-sui-open": "open() ? \"\" : null", "attr.data-sui-disabled": "disabled() ? \"\" : null", "attr.data-sui-presets-only": "presetsOnly() ? \"\" : null" } }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true, isSignal: true }, { propertyName: "hueRef", first: true, predicate: ["hueSlider"], descendants: true, isSignal: true }, { propertyName: "opacityRef", first: true, predicate: ["opacitySlider"], descendants: true, isSignal: true }], ngImport: i0, template: "<button\n type=\"button\"\n class=\"sui-color-picker__trigger\"\n [disabled]=\"disabled() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open() ? 'true' : 'false'\"\n (click)=\"toggle()\"\n (touchstart)=\"$event.currentTarget\"\n (touchend)=\"$any($event.currentTarget).blur()\"\n>\n @if (value()) {\n @if (currentPreset()) {\n <sui-color-dot [color]=\"currentPreset()!\" state=\"selected\" class=\"sui-color-picker__preview\" />\n } @else {\n <sui-color-dot [customHex]=\"previewColor()\" state=\"selected\" class=\"sui-color-picker__preview\" />\n }\n } @else {\n <span class=\"sui-color-picker__placeholder-dot\" aria-hidden=\"true\"></span>\n }\n <span class=\"sui-color-picker__label\">{{ currentLabel() }}</span>\n <sui-icon name=\"arrow-down-s-line\" [size]=\"16\" class=\"sui-color-picker__chevron\" />\n</button>\n\n@if (open()) {\n <div\n class=\"sui-color-picker__panel\"\n role=\"dialog\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n @if (!presetsOnly()) {\n <!-- Secci\u00F3n \"Colors\" \u2014 Figma 4456:115716 -->\n <div class=\"sui-color-picker__section sui-color-picker__section--colors\">\n <!-- HSV Color Spectrum \u2014 Figma 4456:115425 -->\n <div\n #canvas\n class=\"sui-color-picker__canvas\"\n role=\"application\"\n aria-label=\"Saturaci\u00F3n y brillo\"\n [style.--hue-color]=\"hueColor()\"\n (pointerdown)=\"onCanvasPointerDown($event)\"\n (pointermove)=\"onCanvasPointerMove($event)\"\n (pointerup)=\"onCanvasPointerUp($event)\"\n (pointercancel)=\"onCanvasPointerUp($event)\"\n >\n <div\n class=\"sui-color-picker__canvas-thumb\"\n [style.left]=\"canvasThumbX()\"\n [style.top]=\"canvasThumbY()\"\n [style.background-color]=\"currentHex()\"\n ></div>\n </div>\n\n <!-- Hue slider \u2014 Figma 4415:53669 -->\n <div\n #hueSlider\n class=\"sui-color-picker__slider sui-color-picker__hue\"\n role=\"slider\"\n aria-label=\"Matiz\"\n aria-valuemin=\"0\"\n aria-valuemax=\"360\"\n [attr.aria-valuenow]=\"hueRounded()\"\n (pointerdown)=\"onHuePointerDown($event)\"\n (pointermove)=\"onHuePointerMove($event)\"\n (pointerup)=\"onHuePointerUp($event)\"\n (pointercancel)=\"onHuePointerUp($event)\"\n >\n <div class=\"sui-color-picker__slider-track\"></div>\n <div\n class=\"sui-color-picker__slider-thumb\"\n [style.left]=\"hueThumbX()\"\n [style.background-color]=\"hueColor()\"\n ></div>\n </div>\n\n <!-- Opacity slider \u2014 Figma 4415:53670. Visible por default (alpha on);\n ocultable con [alpha]=false. -->\n @if (alpha()) {\n <div\n #opacitySlider\n class=\"sui-color-picker__slider sui-color-picker__opacity\"\n role=\"slider\"\n aria-label=\"Opacidad\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"alphaPercent()\"\n [style.--opacity-color]=\"opacityColor()\"\n (pointerdown)=\"onOpacityPointerDown($event)\"\n (pointermove)=\"onOpacityPointerMove($event)\"\n (pointerup)=\"onOpacityPointerUp($event)\"\n (pointercancel)=\"onOpacityPointerUp($event)\"\n >\n <div class=\"sui-color-picker__slider-track\"></div>\n <div\n class=\"sui-color-picker__slider-thumb\"\n [style.left]=\"opacityThumbX()\"\n [style.--thumb-color]=\"opacityThumbColor()\"\n ></div>\n </div>\n }\n\n <!-- Color Input \u2014 Figma 4456:115720 -->\n <div class=\"sui-color-picker__color-input\">\n <!-- Format label \"Hex\" + chevron \u2014 Figma 4456:115721.\n Hex-only por ahora (ver PENDING VERIFY en el .ts). -->\n @if (showFormatLabel()) {\n <div class=\"sui-color-picker__format\" aria-hidden=\"true\">\n <span class=\"sui-color-picker__format-label\">Hex</span>\n <sui-icon name=\"arrow-down-s-line\" [size]=\"20\" class=\"sui-color-picker__format-chevron\" />\n </div>\n }\n\n <!-- Input row \u2014 Figma 4456:115724: sip | hex | opacity -->\n <div class=\"sui-color-picker__input-row\">\n <!-- Sip (eyedropper) \u2014 Figma 4456:115796. Solo con EyeDropper API. -->\n @if (showEyedropper()) {\n <button\n type=\"button\"\n class=\"sui-color-picker__sip\"\n aria-label=\"Cuentagotas\"\n (click)=\"onEyedropper()\"\n (touchstart)=\"$event.currentTarget\"\n (touchend)=\"$any($event.currentTarget).blur()\"\n >\n <sui-icon name=\"sip-line\" [size]=\"20\" />\n </button>\n }\n\n <!-- Hex field \u2014 Figma 4456:115725 / 4456:115726: el texto va SIN\n prefijo '#' (Figma muestra \"335CFF\" pelado dentro de la celda). -->\n <div class=\"sui-color-picker__hex-cell\">\n <input\n class=\"sui-color-picker__hex-field\"\n type=\"text\"\n inputmode=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n maxlength=\"8\"\n aria-label=\"Valor hexadecimal\"\n [value]=\"hexDisplayValue()\"\n (input)=\"onHexInputChange($event)\"\n (keydown)=\"onHexKeyDown($event)\"\n (blur)=\"onHexCommit()\"\n />\n </div>\n\n <!-- Opacity field \"%\" \u2014 Figma 4456:115727. Visible por default\n (alpha on); ocultable con [alpha]=false. -->\n @if (alpha()) {\n <div class=\"sui-color-picker__opacity-cell\">\n <input\n class=\"sui-color-picker__opacity-field\"\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n maxlength=\"3\"\n aria-label=\"Opacidad\"\n [value]=\"alphaPercent()\"\n (change)=\"onOpacityFieldCommit($event)\"\n (keydown)=\"onHexKeyDown($event)\"\n />\n <span class=\"sui-color-picker__opacity-suffix\" aria-hidden=\"true\">%</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Secci\u00F3n \"Recommended Colors\" \u2014 Figma 4456:115765 -->\n <div class=\"sui-color-picker__section sui-color-picker__section--recommended\">\n <p class=\"sui-color-picker__panel-label\">{{ recommendedLabel() }}</p>\n <div class=\"sui-color-picker__grid\" role=\"radiogroup\">\n @for (c of colors(); track c) {\n <sui-color-dot\n [color]=\"c\"\n [state]=\"currentPreset() === c ? 'selected' : 'default'\"\n (clicked)=\"onPresetClick(c)\"\n />\n }\n </div>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{position:relative;display:inline-block;font-family:var(--sui-font-sans);font-feature-settings:\"ss11\" 1,\"calt\" 0,\"liga\" 0}.sui-color-picker__trigger{display:inline-flex;align-items:center;gap:8px;padding:6px 10px 6px 6px;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-8);cursor:pointer;appearance:none;font:inherit;color:var(--sui-text-strong-950);font-size:var(--sui-type-label-sm-size);font-weight:var(--sui-type-label-sm-weight);line-height:var(--sui-type-label-sm-line);letter-spacing:var(--sui-type-label-sm-spacing);transition:background-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out),border-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}.sui-color-picker__trigger:hover{background:var(--sui-bg-weak-50)}:host([data-sui-open]) .sui-color-picker__trigger{border-color:var(--sui-stroke-strong-950, var(--sui-text-strong-950))}.sui-color-picker__trigger:focus-visible{outline:2px solid var(--sui-stroke-strong-950, var(--sui-text-strong-950));outline-offset:2px}.sui-color-picker__trigger:active{transform:none}:host([data-sui-disabled]) .sui-color-picker__trigger{background:var(--sui-bg-weak-50);color:var(--sui-text-disabled-300);cursor:not-allowed;pointer-events:none}.sui-color-picker__placeholder-dot{display:inline-block;width:24px;height:24px;border-radius:50%;background:var(--sui-bg-soft-200)}.sui-color-picker__label{flex:1 0 auto;white-space:nowrap}.sui-color-picker__chevron{flex-shrink:0;color:var(--sui-icon-sub-600);transition:transform var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}:host([data-sui-open]) .sui-color-picker__chevron{transform:rotate(180deg)}.sui-color-picker__panel{position:absolute;top:calc(100% + 6px);left:0;z-index:100;width:272px;box-sizing:border-box;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-16);box-shadow:var(--sui-shadow-medium);overflow:hidden;display:flex;flex-direction:column;animation:sui-color-picker__fade var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}@keyframes sui-color-picker__fade{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.sui-color-picker__section{box-sizing:border-box;display:flex;flex-direction:column;gap:12px;padding:16px;width:100%}.sui-color-picker__section--colors{border-bottom:1px solid var(--sui-stroke-soft-200)}.sui-color-picker__section--recommended{gap:8px}:host([data-sui-presets-only]) .sui-color-picker__section--recommended{border-bottom:none}.sui-color-picker__canvas{position:relative;width:100%;height:232px;border-radius:var(--sui-radius-8);cursor:crosshair;-webkit-user-select:none;user-select:none;touch-action:none;overflow:hidden;background-color:var(--hue-color, #335cff);background-image:linear-gradient(to top,#000,#0000),linear-gradient(to right,#fff,#fff0)}.sui-color-picker__canvas-thumb{position:absolute;width:16px;height:16px;border-radius:50%;border:2px solid var(--sui-static-white, #fff);box-shadow:0 0 0 1px #00000026,0 1px 2px #0003;transform:translate(-50%,-50%);pointer-events:none}.sui-color-picker__slider{position:relative;width:100%;height:16px;cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:none}.sui-color-picker__slider-track{position:absolute;left:0;right:0;top:4px;height:8px;border-radius:var(--sui-radius-full);overflow:hidden}.sui-color-picker__hue .sui-color-picker__slider-track{background-image:linear-gradient(to right,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.sui-color-picker__opacity .sui-color-picker__slider-track{background-color:#fff;background-image:linear-gradient(to right,transparent,var(--opacity-color, #335cff)),conic-gradient(#ebebeb 0 25%,transparent 0 50%,#ebebeb 0 75%,transparent 0);background-size:auto,10.88px 10.88px}.sui-color-picker__slider-thumb{position:absolute;top:50%;width:16px;height:16px;border-radius:50%;border:2px solid var(--sui-static-white, #fff);box-shadow:0 0 0 1px #00000026,0 1px 2px #0003;transform:translate(-50%,-50%);pointer-events:none}.sui-color-picker__opacity .sui-color-picker__slider-thumb{background-image:linear-gradient(var(--thumb-color, #335cff),var(--thumb-color, #335cff)),conic-gradient(#ebebeb 0 25%,#fff 0 50%,#ebebeb 0 75%,#fff 0);background-size:auto,6px 6px}.sui-color-picker__color-input{display:flex;flex-direction:column;gap:4px;width:100%}.sui-color-picker__format{display:flex;align-items:center;gap:2px;appearance:none;border:none;background:transparent;padding:0;cursor:default;font:inherit;color:var(--sui-text-strong-950)}.sui-color-picker__format-label{font-size:var(--sui-type-label-sm-size);font-weight:var(--sui-type-label-sm-weight);line-height:var(--sui-type-label-sm-line);letter-spacing:var(--sui-type-label-sm-spacing);white-space:nowrap}.sui-color-picker__format-chevron{flex-shrink:0;color:var(--sui-icon-sub-600)}.sui-color-picker__input-row{box-sizing:border-box;display:flex;align-items:stretch;width:100%;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-8);overflow:hidden}.sui-color-picker__sip{display:flex;align-items:center;justify-content:center;padding:6px;border:none;border-right:1px solid var(--sui-stroke-soft-200);background:var(--sui-bg-white-0);color:var(--sui-icon-sub-600);cursor:pointer;appearance:none;flex-shrink:0;transition:background-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}.sui-color-picker__sip:hover{background:var(--sui-bg-weak-50)}.sui-color-picker__sip:focus-visible{outline:2px solid var(--sui-stroke-strong-950, var(--sui-text-strong-950));outline-offset:-2px}.sui-color-picker__sip:active{transform:none}.sui-color-picker__hex-cell{box-sizing:border-box;display:flex;align-items:center;flex:1 0 0;min-width:0;padding:6px 10px;border-right:1px solid var(--sui-stroke-soft-200)}.sui-color-picker__hex-cell:last-child{border-right:none}.sui-color-picker__hex-field{flex:1 0 0;min-width:0;width:100%;border:none;outline:none;background:transparent;padding:0;appearance:none;color:var(--sui-text-strong-950);font-family:inherit;font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing);text-transform:uppercase}.sui-color-picker__hex-field::placeholder{color:var(--sui-text-soft-400);text-transform:none}.sui-color-picker__opacity-cell{box-sizing:border-box;display:flex;align-items:center;flex-shrink:0;padding:6px 10px}.sui-color-picker__opacity-field{width:3.5ch;border:none;outline:none;background:transparent;padding:0;appearance:none;text-align:right;color:var(--sui-text-sub-600);font-family:inherit;font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing)}.sui-color-picker__opacity-suffix{color:var(--sui-text-sub-600);font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing)}.sui-color-picker__panel-label{margin:0;font-size:var(--sui-type-paragraph-xs-size);font-weight:var(--sui-type-paragraph-xs-weight);line-height:var(--sui-type-paragraph-xs-line);letter-spacing:var(--sui-type-paragraph-xs-spacing);color:var(--sui-text-sub-600)}.sui-color-picker__grid{display:flex;flex-wrap:wrap;gap:4px;width:100%}\n"], dependencies: [{ kind: "component", type: SuiIcon, selector: "sui-icon", inputs: ["name", "size", "color"] }, { kind: "component", type: SuiColorDot, selector: "sui-color-dot", inputs: ["color", "customHex", "state", "ariaLabel"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3982
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SuiColorPicker, isStandalone: true, selector: "sui-color-picker", inputs: { colors: { classPropertyName: "colors", publicName: "colors", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, recommendedLabel: { classPropertyName: "recommendedLabel", publicName: "recommendedLabel", isSignal: true, isRequired: false, transformFunction: null }, presetsOnly: { classPropertyName: "presetsOnly", publicName: "presetsOnly", isSignal: true, isRequired: false, transformFunction: null }, alpha: { classPropertyName: "alpha", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null }, eyedropper: { classPropertyName: "eyedropper", publicName: "eyedropper", isSignal: true, isRequired: false, transformFunction: null }, showFormatLabel: { classPropertyName: "showFormatLabel", publicName: "showFormatLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", change: "change" }, host: { properties: { "attr.data-sui-open": "open() ? \"\" : null", "attr.data-sui-disabled": "disabled() ? \"\" : null", "attr.data-sui-presets-only": "presetsOnly() ? \"\" : null" } }, viewQueries: [{ propertyName: "_menu", first: true, predicate: SuiDropdownMenu, descendants: true, isSignal: true }, { propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true, isSignal: true }, { propertyName: "hueRef", first: true, predicate: ["hueSlider"], descendants: true, isSignal: true }, { propertyName: "opacityRef", first: true, predicate: ["opacitySlider"], descendants: true, isSignal: true }], ngImport: i0, template: "<button\n #trigger\n type=\"button\"\n class=\"sui-color-picker__trigger\"\n [disabled]=\"disabled() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open() ? 'true' : 'false'\"\n (click)=\"toggle()\"\n (touchstart)=\"$event.currentTarget\"\n (touchend)=\"$any($event.currentTarget).blur()\"\n>\n @if (value()) {\n @if (currentPreset()) {\n <sui-color-dot [color]=\"currentPreset()!\" state=\"selected\" class=\"sui-color-picker__preview\" />\n } @else {\n <sui-color-dot [customHex]=\"previewColor()\" state=\"selected\" class=\"sui-color-picker__preview\" />\n }\n } @else {\n <span class=\"sui-color-picker__placeholder-dot\" aria-hidden=\"true\"></span>\n }\n <span class=\"sui-color-picker__label\">{{ currentLabel() }}</span>\n <sui-icon name=\"arrow-down-s-line\" [size]=\"16\" class=\"sui-color-picker__chevron\" />\n</button>\n\n<!-- Panel en CDK overlay (via sui-dropdown-menu): escapa cualquier overflow\n clipping del contenedor (Storybook docs, modales, tablas). El contenido del\n ng-template mantiene el scoping de estilos del componente. -->\n<sui-dropdown-menu\n [origin]=\"trigger\"\n [disabled]=\"disabled()\"\n panelClass=\"sui-color-picker__overlay-pane\"\n (openedChange)=\"onMenuOpenedChange($event)\"\n>\n <ng-template suiDropdownPanel>\n <div\n class=\"sui-color-picker__panel\"\n role=\"dialog\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n @if (!presetsOnly()) {\n <!-- Secci\u00F3n \"Colors\" \u2014 Figma 4456:115716 -->\n <div class=\"sui-color-picker__section sui-color-picker__section--colors\">\n <!-- HSV Color Spectrum \u2014 Figma 4456:115425 -->\n <div\n #canvas\n class=\"sui-color-picker__canvas\"\n role=\"application\"\n aria-label=\"Saturaci\u00F3n y brillo\"\n [style.--hue-color]=\"hueColor()\"\n (pointerdown)=\"onCanvasPointerDown($event)\"\n (pointermove)=\"onCanvasPointerMove($event)\"\n (pointerup)=\"onCanvasPointerUp($event)\"\n (pointercancel)=\"onCanvasPointerUp($event)\"\n >\n <div\n class=\"sui-color-picker__canvas-thumb\"\n [style.left]=\"canvasThumbX()\"\n [style.top]=\"canvasThumbY()\"\n [style.background-color]=\"currentHex()\"\n ></div>\n </div>\n\n <!-- Hue slider \u2014 Figma 4415:53669 -->\n <div\n #hueSlider\n class=\"sui-color-picker__slider sui-color-picker__hue\"\n role=\"slider\"\n aria-label=\"Matiz\"\n aria-valuemin=\"0\"\n aria-valuemax=\"360\"\n [attr.aria-valuenow]=\"hueRounded()\"\n (pointerdown)=\"onHuePointerDown($event)\"\n (pointermove)=\"onHuePointerMove($event)\"\n (pointerup)=\"onHuePointerUp($event)\"\n (pointercancel)=\"onHuePointerUp($event)\"\n >\n <div class=\"sui-color-picker__slider-track\"></div>\n <div\n class=\"sui-color-picker__slider-thumb\"\n [style.left]=\"hueThumbX()\"\n [style.background-color]=\"hueColor()\"\n ></div>\n </div>\n\n <!-- Opacity slider \u2014 Figma 4415:53670. Visible por default (alpha on);\n ocultable con [alpha]=false. -->\n @if (alpha()) {\n <div\n #opacitySlider\n class=\"sui-color-picker__slider sui-color-picker__opacity\"\n role=\"slider\"\n aria-label=\"Opacidad\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"alphaPercent()\"\n [style.--opacity-color]=\"opacityColor()\"\n (pointerdown)=\"onOpacityPointerDown($event)\"\n (pointermove)=\"onOpacityPointerMove($event)\"\n (pointerup)=\"onOpacityPointerUp($event)\"\n (pointercancel)=\"onOpacityPointerUp($event)\"\n >\n <div class=\"sui-color-picker__slider-track\"></div>\n <div\n class=\"sui-color-picker__slider-thumb\"\n [style.left]=\"opacityThumbX()\"\n [style.--thumb-color]=\"opacityThumbColor()\"\n ></div>\n </div>\n }\n\n <!-- Color Input \u2014 Figma 4456:115720 -->\n <div class=\"sui-color-picker__color-input\">\n <!-- Format label \"Hex\" + chevron \u2014 Figma 4456:115721.\n Hex-only por ahora (ver PENDING VERIFY en el .ts). -->\n @if (showFormatLabel()) {\n <div class=\"sui-color-picker__format\" aria-hidden=\"true\">\n <span class=\"sui-color-picker__format-label\">Hex</span>\n <sui-icon name=\"arrow-down-s-line\" [size]=\"20\" class=\"sui-color-picker__format-chevron\" />\n </div>\n }\n\n <!-- Input row \u2014 Figma 4456:115724: sip | hex | opacity -->\n <div class=\"sui-color-picker__input-row\">\n <!-- Sip (eyedropper) \u2014 Figma 4456:115796. Solo con EyeDropper API. -->\n @if (showEyedropper()) {\n <button\n type=\"button\"\n class=\"sui-color-picker__sip\"\n aria-label=\"Cuentagotas\"\n (click)=\"onEyedropper()\"\n (touchstart)=\"$event.currentTarget\"\n (touchend)=\"$any($event.currentTarget).blur()\"\n >\n <sui-icon name=\"sip-line\" [size]=\"20\" />\n </button>\n }\n\n <!-- Hex field \u2014 Figma 4456:115725 / 4456:115726: el texto va SIN\n prefijo '#' (Figma muestra \"335CFF\" pelado dentro de la celda). -->\n <div class=\"sui-color-picker__hex-cell\">\n <input\n class=\"sui-color-picker__hex-field\"\n type=\"text\"\n inputmode=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n maxlength=\"8\"\n aria-label=\"Valor hexadecimal\"\n [value]=\"hexDisplayValue()\"\n (input)=\"onHexInputChange($event)\"\n (keydown)=\"onHexKeyDown($event)\"\n (blur)=\"onHexCommit()\"\n />\n </div>\n\n <!-- Opacity field \"%\" \u2014 Figma 4456:115727. Visible por default\n (alpha on); ocultable con [alpha]=false. -->\n @if (alpha()) {\n <div class=\"sui-color-picker__opacity-cell\">\n <input\n class=\"sui-color-picker__opacity-field\"\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n maxlength=\"3\"\n aria-label=\"Opacidad\"\n [value]=\"alphaPercent()\"\n (change)=\"onOpacityFieldCommit($event)\"\n (keydown)=\"onHexKeyDown($event)\"\n />\n <span class=\"sui-color-picker__opacity-suffix\" aria-hidden=\"true\">%</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Secci\u00F3n \"Recommended Colors\" \u2014 Figma 4456:115765 -->\n <div class=\"sui-color-picker__section sui-color-picker__section--recommended\">\n <p class=\"sui-color-picker__panel-label\">{{ recommendedLabel() }}</p>\n <div class=\"sui-color-picker__grid\" role=\"radiogroup\">\n @for (c of colors(); track c) {\n <sui-color-dot\n [color]=\"c\"\n [state]=\"currentPreset() === c ? 'selected' : 'default'\"\n (clicked)=\"onPresetClick(c)\"\n />\n }\n </div>\n </div>\n </div>\n </ng-template>\n</sui-dropdown-menu>\n", styles: ["@charset \"UTF-8\";:host{position:relative;display:inline-block;font-family:var(--sui-font-sans);font-feature-settings:\"ss11\" 1,\"calt\" 0,\"liga\" 0}.sui-color-picker__trigger{display:inline-flex;align-items:center;gap:8px;padding:6px 10px 6px 6px;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-8);cursor:pointer;appearance:none;font:inherit;color:var(--sui-text-strong-950);font-size:var(--sui-type-label-sm-size);font-weight:var(--sui-type-label-sm-weight);line-height:var(--sui-type-label-sm-line);letter-spacing:var(--sui-type-label-sm-spacing);transition:background-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out),border-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}.sui-color-picker__trigger:hover{background:var(--sui-bg-weak-50)}:host([data-sui-open]) .sui-color-picker__trigger{border-color:var(--sui-stroke-strong-950, var(--sui-text-strong-950))}.sui-color-picker__trigger:focus-visible{outline:2px solid var(--sui-stroke-strong-950, var(--sui-text-strong-950));outline-offset:2px}.sui-color-picker__trigger:active{transform:none}:host([data-sui-disabled]) .sui-color-picker__trigger{background:var(--sui-bg-weak-50);color:var(--sui-text-disabled-300);cursor:not-allowed;pointer-events:none}.sui-color-picker__placeholder-dot{display:inline-block;width:24px;height:24px;border-radius:50%;background:var(--sui-bg-soft-200)}.sui-color-picker__label{flex:1 0 auto;white-space:nowrap}.sui-color-picker__chevron{flex-shrink:0;color:var(--sui-icon-sub-600);transition:transform var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}:host([data-sui-open]) .sui-color-picker__chevron{transform:rotate(180deg)}.sui-color-picker__panel{width:272px;margin-top:6px;box-sizing:border-box;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-16);box-shadow:var(--sui-shadow-medium);overflow:hidden;display:flex;flex-direction:column;animation:sui-color-picker__fade var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}@keyframes sui-color-picker__fade{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.sui-color-picker__section{box-sizing:border-box;display:flex;flex-direction:column;gap:12px;padding:16px;width:100%}.sui-color-picker__section--colors{border-bottom:1px solid var(--sui-stroke-soft-200)}.sui-color-picker__section--recommended{gap:8px}:host([data-sui-presets-only]) .sui-color-picker__section--recommended{border-bottom:none}.sui-color-picker__canvas{position:relative;width:100%;height:232px;border-radius:var(--sui-radius-8);cursor:crosshair;-webkit-user-select:none;user-select:none;touch-action:none;overflow:hidden;background-color:var(--hue-color, #335cff);background-image:linear-gradient(to top,#000,#0000),linear-gradient(to right,#fff,#fff0)}.sui-color-picker__canvas-thumb{position:absolute;width:16px;height:16px;border-radius:50%;border:2px solid var(--sui-static-white, #fff);box-shadow:0 0 0 1px #00000026,0 1px 2px #0003;transform:translate(-50%,-50%);pointer-events:none}.sui-color-picker__slider{position:relative;width:100%;height:16px;cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:none}.sui-color-picker__slider-track{position:absolute;left:0;right:0;top:4px;height:8px;border-radius:var(--sui-radius-full);overflow:hidden}.sui-color-picker__hue .sui-color-picker__slider-track{background-image:linear-gradient(to right,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.sui-color-picker__opacity .sui-color-picker__slider-track{background-color:#fff;background-image:linear-gradient(to right,transparent,var(--opacity-color, #335cff)),conic-gradient(#ebebeb 0 25%,transparent 0 50%,#ebebeb 0 75%,transparent 0);background-size:auto,10.88px 10.88px}.sui-color-picker__slider-thumb{position:absolute;top:50%;width:16px;height:16px;border-radius:50%;border:2px solid var(--sui-static-white, #fff);box-shadow:0 0 0 1px #00000026,0 1px 2px #0003;transform:translate(-50%,-50%);pointer-events:none}.sui-color-picker__opacity .sui-color-picker__slider-thumb{background-image:linear-gradient(var(--thumb-color, #335cff),var(--thumb-color, #335cff)),conic-gradient(#ebebeb 0 25%,#fff 0 50%,#ebebeb 0 75%,#fff 0);background-size:auto,6px 6px}.sui-color-picker__color-input{display:flex;flex-direction:column;gap:4px;width:100%}.sui-color-picker__format{display:flex;align-items:center;gap:2px;appearance:none;border:none;background:transparent;padding:0;cursor:default;font:inherit;color:var(--sui-text-strong-950)}.sui-color-picker__format-label{font-size:var(--sui-type-label-sm-size);font-weight:var(--sui-type-label-sm-weight);line-height:var(--sui-type-label-sm-line);letter-spacing:var(--sui-type-label-sm-spacing);white-space:nowrap}.sui-color-picker__format-chevron{flex-shrink:0;color:var(--sui-icon-sub-600)}.sui-color-picker__input-row{box-sizing:border-box;display:flex;align-items:stretch;width:100%;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-8);overflow:hidden}.sui-color-picker__sip{display:flex;align-items:center;justify-content:center;padding:6px;border:none;border-right:1px solid var(--sui-stroke-soft-200);background:var(--sui-bg-white-0);color:var(--sui-icon-sub-600);cursor:pointer;appearance:none;flex-shrink:0;transition:background-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}.sui-color-picker__sip:hover{background:var(--sui-bg-weak-50)}.sui-color-picker__sip:focus-visible{outline:2px solid var(--sui-stroke-strong-950, var(--sui-text-strong-950));outline-offset:-2px}.sui-color-picker__sip:active{transform:none}.sui-color-picker__hex-cell{box-sizing:border-box;display:flex;align-items:center;flex:1 0 0;min-width:0;padding:6px 10px;border-right:1px solid var(--sui-stroke-soft-200)}.sui-color-picker__hex-cell:last-child{border-right:none}.sui-color-picker__hex-field{flex:1 0 0;min-width:0;width:100%;border:none;outline:none;background:transparent;padding:0;appearance:none;color:var(--sui-text-strong-950);font-family:inherit;font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing);text-transform:uppercase}.sui-color-picker__hex-field::placeholder{color:var(--sui-text-soft-400);text-transform:none}.sui-color-picker__opacity-cell{box-sizing:border-box;display:flex;align-items:center;flex-shrink:0;padding:6px 10px}.sui-color-picker__opacity-field{width:3.5ch;border:none;outline:none;background:transparent;padding:0;appearance:none;text-align:right;color:var(--sui-text-sub-600);font-family:inherit;font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing)}.sui-color-picker__opacity-suffix{color:var(--sui-text-sub-600);font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing)}.sui-color-picker__panel-label{margin:0;font-size:var(--sui-type-paragraph-xs-size);font-weight:var(--sui-type-paragraph-xs-weight);line-height:var(--sui-type-paragraph-xs-line);letter-spacing:var(--sui-type-paragraph-xs-spacing);color:var(--sui-text-sub-600)}.sui-color-picker__grid{display:flex;flex-wrap:wrap;gap:4px;width:100%}\n"], dependencies: [{ kind: "component", type: SuiIcon, selector: "sui-icon", inputs: ["name", "size", "color"] }, { kind: "component", type: SuiColorDot, selector: "sui-color-dot", inputs: ["color", "customHex", "state", "ariaLabel"], outputs: ["clicked"] }, { kind: "component", type: SuiDropdownMenu, selector: "sui-dropdown-menu", inputs: ["matchWidth", "minWidth", "position", "viewportMargin", "panelClass", "disabled", "popupRole", "origin"], outputs: ["openedChange"], exportAs: ["suiDropdownMenu"] }, { kind: "directive", type: SuiDropdownPanel, selector: "ng-template[suiDropdownPanel]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3777
3983
  }
3778
3984
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SuiColorPicker, decorators: [{
3779
3985
  type: Component,
3780
- args: [{ selector: 'sui-color-picker', standalone: true, imports: [SuiIcon, SuiColorDot], changeDetection: ChangeDetectionStrategy.OnPush, host: {
3986
+ args: [{ selector: 'sui-color-picker', standalone: true, imports: [SuiIcon, SuiColorDot, SuiDropdownMenu, SuiDropdownPanel], changeDetection: ChangeDetectionStrategy.OnPush, host: {
3781
3987
  '[attr.data-sui-open]': 'open() ? "" : null',
3782
3988
  '[attr.data-sui-disabled]': 'disabled() ? "" : null',
3783
3989
  '[attr.data-sui-presets-only]': 'presetsOnly() ? "" : null',
3784
- }, template: "<button\n type=\"button\"\n class=\"sui-color-picker__trigger\"\n [disabled]=\"disabled() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open() ? 'true' : 'false'\"\n (click)=\"toggle()\"\n (touchstart)=\"$event.currentTarget\"\n (touchend)=\"$any($event.currentTarget).blur()\"\n>\n @if (value()) {\n @if (currentPreset()) {\n <sui-color-dot [color]=\"currentPreset()!\" state=\"selected\" class=\"sui-color-picker__preview\" />\n } @else {\n <sui-color-dot [customHex]=\"previewColor()\" state=\"selected\" class=\"sui-color-picker__preview\" />\n }\n } @else {\n <span class=\"sui-color-picker__placeholder-dot\" aria-hidden=\"true\"></span>\n }\n <span class=\"sui-color-picker__label\">{{ currentLabel() }}</span>\n <sui-icon name=\"arrow-down-s-line\" [size]=\"16\" class=\"sui-color-picker__chevron\" />\n</button>\n\n@if (open()) {\n <div\n class=\"sui-color-picker__panel\"\n role=\"dialog\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n @if (!presetsOnly()) {\n <!-- Secci\u00F3n \"Colors\" \u2014 Figma 4456:115716 -->\n <div class=\"sui-color-picker__section sui-color-picker__section--colors\">\n <!-- HSV Color Spectrum \u2014 Figma 4456:115425 -->\n <div\n #canvas\n class=\"sui-color-picker__canvas\"\n role=\"application\"\n aria-label=\"Saturaci\u00F3n y brillo\"\n [style.--hue-color]=\"hueColor()\"\n (pointerdown)=\"onCanvasPointerDown($event)\"\n (pointermove)=\"onCanvasPointerMove($event)\"\n (pointerup)=\"onCanvasPointerUp($event)\"\n (pointercancel)=\"onCanvasPointerUp($event)\"\n >\n <div\n class=\"sui-color-picker__canvas-thumb\"\n [style.left]=\"canvasThumbX()\"\n [style.top]=\"canvasThumbY()\"\n [style.background-color]=\"currentHex()\"\n ></div>\n </div>\n\n <!-- Hue slider \u2014 Figma 4415:53669 -->\n <div\n #hueSlider\n class=\"sui-color-picker__slider sui-color-picker__hue\"\n role=\"slider\"\n aria-label=\"Matiz\"\n aria-valuemin=\"0\"\n aria-valuemax=\"360\"\n [attr.aria-valuenow]=\"hueRounded()\"\n (pointerdown)=\"onHuePointerDown($event)\"\n (pointermove)=\"onHuePointerMove($event)\"\n (pointerup)=\"onHuePointerUp($event)\"\n (pointercancel)=\"onHuePointerUp($event)\"\n >\n <div class=\"sui-color-picker__slider-track\"></div>\n <div\n class=\"sui-color-picker__slider-thumb\"\n [style.left]=\"hueThumbX()\"\n [style.background-color]=\"hueColor()\"\n ></div>\n </div>\n\n <!-- Opacity slider \u2014 Figma 4415:53670. Visible por default (alpha on);\n ocultable con [alpha]=false. -->\n @if (alpha()) {\n <div\n #opacitySlider\n class=\"sui-color-picker__slider sui-color-picker__opacity\"\n role=\"slider\"\n aria-label=\"Opacidad\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"alphaPercent()\"\n [style.--opacity-color]=\"opacityColor()\"\n (pointerdown)=\"onOpacityPointerDown($event)\"\n (pointermove)=\"onOpacityPointerMove($event)\"\n (pointerup)=\"onOpacityPointerUp($event)\"\n (pointercancel)=\"onOpacityPointerUp($event)\"\n >\n <div class=\"sui-color-picker__slider-track\"></div>\n <div\n class=\"sui-color-picker__slider-thumb\"\n [style.left]=\"opacityThumbX()\"\n [style.--thumb-color]=\"opacityThumbColor()\"\n ></div>\n </div>\n }\n\n <!-- Color Input \u2014 Figma 4456:115720 -->\n <div class=\"sui-color-picker__color-input\">\n <!-- Format label \"Hex\" + chevron \u2014 Figma 4456:115721.\n Hex-only por ahora (ver PENDING VERIFY en el .ts). -->\n @if (showFormatLabel()) {\n <div class=\"sui-color-picker__format\" aria-hidden=\"true\">\n <span class=\"sui-color-picker__format-label\">Hex</span>\n <sui-icon name=\"arrow-down-s-line\" [size]=\"20\" class=\"sui-color-picker__format-chevron\" />\n </div>\n }\n\n <!-- Input row \u2014 Figma 4456:115724: sip | hex | opacity -->\n <div class=\"sui-color-picker__input-row\">\n <!-- Sip (eyedropper) \u2014 Figma 4456:115796. Solo con EyeDropper API. -->\n @if (showEyedropper()) {\n <button\n type=\"button\"\n class=\"sui-color-picker__sip\"\n aria-label=\"Cuentagotas\"\n (click)=\"onEyedropper()\"\n (touchstart)=\"$event.currentTarget\"\n (touchend)=\"$any($event.currentTarget).blur()\"\n >\n <sui-icon name=\"sip-line\" [size]=\"20\" />\n </button>\n }\n\n <!-- Hex field \u2014 Figma 4456:115725 / 4456:115726: el texto va SIN\n prefijo '#' (Figma muestra \"335CFF\" pelado dentro de la celda). -->\n <div class=\"sui-color-picker__hex-cell\">\n <input\n class=\"sui-color-picker__hex-field\"\n type=\"text\"\n inputmode=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n maxlength=\"8\"\n aria-label=\"Valor hexadecimal\"\n [value]=\"hexDisplayValue()\"\n (input)=\"onHexInputChange($event)\"\n (keydown)=\"onHexKeyDown($event)\"\n (blur)=\"onHexCommit()\"\n />\n </div>\n\n <!-- Opacity field \"%\" \u2014 Figma 4456:115727. Visible por default\n (alpha on); ocultable con [alpha]=false. -->\n @if (alpha()) {\n <div class=\"sui-color-picker__opacity-cell\">\n <input\n class=\"sui-color-picker__opacity-field\"\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n maxlength=\"3\"\n aria-label=\"Opacidad\"\n [value]=\"alphaPercent()\"\n (change)=\"onOpacityFieldCommit($event)\"\n (keydown)=\"onHexKeyDown($event)\"\n />\n <span class=\"sui-color-picker__opacity-suffix\" aria-hidden=\"true\">%</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Secci\u00F3n \"Recommended Colors\" \u2014 Figma 4456:115765 -->\n <div class=\"sui-color-picker__section sui-color-picker__section--recommended\">\n <p class=\"sui-color-picker__panel-label\">{{ recommendedLabel() }}</p>\n <div class=\"sui-color-picker__grid\" role=\"radiogroup\">\n @for (c of colors(); track c) {\n <sui-color-dot\n [color]=\"c\"\n [state]=\"currentPreset() === c ? 'selected' : 'default'\"\n (clicked)=\"onPresetClick(c)\"\n />\n }\n </div>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{position:relative;display:inline-block;font-family:var(--sui-font-sans);font-feature-settings:\"ss11\" 1,\"calt\" 0,\"liga\" 0}.sui-color-picker__trigger{display:inline-flex;align-items:center;gap:8px;padding:6px 10px 6px 6px;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-8);cursor:pointer;appearance:none;font:inherit;color:var(--sui-text-strong-950);font-size:var(--sui-type-label-sm-size);font-weight:var(--sui-type-label-sm-weight);line-height:var(--sui-type-label-sm-line);letter-spacing:var(--sui-type-label-sm-spacing);transition:background-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out),border-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}.sui-color-picker__trigger:hover{background:var(--sui-bg-weak-50)}:host([data-sui-open]) .sui-color-picker__trigger{border-color:var(--sui-stroke-strong-950, var(--sui-text-strong-950))}.sui-color-picker__trigger:focus-visible{outline:2px solid var(--sui-stroke-strong-950, var(--sui-text-strong-950));outline-offset:2px}.sui-color-picker__trigger:active{transform:none}:host([data-sui-disabled]) .sui-color-picker__trigger{background:var(--sui-bg-weak-50);color:var(--sui-text-disabled-300);cursor:not-allowed;pointer-events:none}.sui-color-picker__placeholder-dot{display:inline-block;width:24px;height:24px;border-radius:50%;background:var(--sui-bg-soft-200)}.sui-color-picker__label{flex:1 0 auto;white-space:nowrap}.sui-color-picker__chevron{flex-shrink:0;color:var(--sui-icon-sub-600);transition:transform var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}:host([data-sui-open]) .sui-color-picker__chevron{transform:rotate(180deg)}.sui-color-picker__panel{position:absolute;top:calc(100% + 6px);left:0;z-index:100;width:272px;box-sizing:border-box;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-16);box-shadow:var(--sui-shadow-medium);overflow:hidden;display:flex;flex-direction:column;animation:sui-color-picker__fade var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}@keyframes sui-color-picker__fade{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.sui-color-picker__section{box-sizing:border-box;display:flex;flex-direction:column;gap:12px;padding:16px;width:100%}.sui-color-picker__section--colors{border-bottom:1px solid var(--sui-stroke-soft-200)}.sui-color-picker__section--recommended{gap:8px}:host([data-sui-presets-only]) .sui-color-picker__section--recommended{border-bottom:none}.sui-color-picker__canvas{position:relative;width:100%;height:232px;border-radius:var(--sui-radius-8);cursor:crosshair;-webkit-user-select:none;user-select:none;touch-action:none;overflow:hidden;background-color:var(--hue-color, #335cff);background-image:linear-gradient(to top,#000,#0000),linear-gradient(to right,#fff,#fff0)}.sui-color-picker__canvas-thumb{position:absolute;width:16px;height:16px;border-radius:50%;border:2px solid var(--sui-static-white, #fff);box-shadow:0 0 0 1px #00000026,0 1px 2px #0003;transform:translate(-50%,-50%);pointer-events:none}.sui-color-picker__slider{position:relative;width:100%;height:16px;cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:none}.sui-color-picker__slider-track{position:absolute;left:0;right:0;top:4px;height:8px;border-radius:var(--sui-radius-full);overflow:hidden}.sui-color-picker__hue .sui-color-picker__slider-track{background-image:linear-gradient(to right,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.sui-color-picker__opacity .sui-color-picker__slider-track{background-color:#fff;background-image:linear-gradient(to right,transparent,var(--opacity-color, #335cff)),conic-gradient(#ebebeb 0 25%,transparent 0 50%,#ebebeb 0 75%,transparent 0);background-size:auto,10.88px 10.88px}.sui-color-picker__slider-thumb{position:absolute;top:50%;width:16px;height:16px;border-radius:50%;border:2px solid var(--sui-static-white, #fff);box-shadow:0 0 0 1px #00000026,0 1px 2px #0003;transform:translate(-50%,-50%);pointer-events:none}.sui-color-picker__opacity .sui-color-picker__slider-thumb{background-image:linear-gradient(var(--thumb-color, #335cff),var(--thumb-color, #335cff)),conic-gradient(#ebebeb 0 25%,#fff 0 50%,#ebebeb 0 75%,#fff 0);background-size:auto,6px 6px}.sui-color-picker__color-input{display:flex;flex-direction:column;gap:4px;width:100%}.sui-color-picker__format{display:flex;align-items:center;gap:2px;appearance:none;border:none;background:transparent;padding:0;cursor:default;font:inherit;color:var(--sui-text-strong-950)}.sui-color-picker__format-label{font-size:var(--sui-type-label-sm-size);font-weight:var(--sui-type-label-sm-weight);line-height:var(--sui-type-label-sm-line);letter-spacing:var(--sui-type-label-sm-spacing);white-space:nowrap}.sui-color-picker__format-chevron{flex-shrink:0;color:var(--sui-icon-sub-600)}.sui-color-picker__input-row{box-sizing:border-box;display:flex;align-items:stretch;width:100%;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-8);overflow:hidden}.sui-color-picker__sip{display:flex;align-items:center;justify-content:center;padding:6px;border:none;border-right:1px solid var(--sui-stroke-soft-200);background:var(--sui-bg-white-0);color:var(--sui-icon-sub-600);cursor:pointer;appearance:none;flex-shrink:0;transition:background-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}.sui-color-picker__sip:hover{background:var(--sui-bg-weak-50)}.sui-color-picker__sip:focus-visible{outline:2px solid var(--sui-stroke-strong-950, var(--sui-text-strong-950));outline-offset:-2px}.sui-color-picker__sip:active{transform:none}.sui-color-picker__hex-cell{box-sizing:border-box;display:flex;align-items:center;flex:1 0 0;min-width:0;padding:6px 10px;border-right:1px solid var(--sui-stroke-soft-200)}.sui-color-picker__hex-cell:last-child{border-right:none}.sui-color-picker__hex-field{flex:1 0 0;min-width:0;width:100%;border:none;outline:none;background:transparent;padding:0;appearance:none;color:var(--sui-text-strong-950);font-family:inherit;font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing);text-transform:uppercase}.sui-color-picker__hex-field::placeholder{color:var(--sui-text-soft-400);text-transform:none}.sui-color-picker__opacity-cell{box-sizing:border-box;display:flex;align-items:center;flex-shrink:0;padding:6px 10px}.sui-color-picker__opacity-field{width:3.5ch;border:none;outline:none;background:transparent;padding:0;appearance:none;text-align:right;color:var(--sui-text-sub-600);font-family:inherit;font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing)}.sui-color-picker__opacity-suffix{color:var(--sui-text-sub-600);font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing)}.sui-color-picker__panel-label{margin:0;font-size:var(--sui-type-paragraph-xs-size);font-weight:var(--sui-type-paragraph-xs-weight);line-height:var(--sui-type-paragraph-xs-line);letter-spacing:var(--sui-type-paragraph-xs-spacing);color:var(--sui-text-sub-600)}.sui-color-picker__grid{display:flex;flex-wrap:wrap;gap:4px;width:100%}\n"] }]
3785
- }], propDecorators: { colors: [{ type: i0.Input, args: [{ isSignal: true, alias: "colors", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], recommendedLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "recommendedLabel", required: false }] }], presetsOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "presetsOnly", required: false }] }], alpha: [{ type: i0.Input, args: [{ isSignal: true, alias: "alpha", required: false }] }], eyedropper: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyedropper", required: false }] }], showFormatLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFormatLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], change: [{ type: i0.Output, args: ["change"] }], canvasRef: [{ type: i0.ViewChild, args: ['canvas', { isSignal: true }] }], hueRef: [{ type: i0.ViewChild, args: ['hueSlider', { isSignal: true }] }], opacityRef: [{ type: i0.ViewChild, args: ['opacitySlider', { isSignal: true }] }], onDocumentClick: [{
3786
- type: HostListener,
3787
- args: ['document:click', ['$event.target']]
3788
- }], onEscape: [{
3789
- type: HostListener,
3790
- args: ['document:keydown.escape']
3791
- }] } });
3990
+ }, template: "<button\n #trigger\n type=\"button\"\n class=\"sui-color-picker__trigger\"\n [disabled]=\"disabled() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"open() ? 'true' : 'false'\"\n (click)=\"toggle()\"\n (touchstart)=\"$event.currentTarget\"\n (touchend)=\"$any($event.currentTarget).blur()\"\n>\n @if (value()) {\n @if (currentPreset()) {\n <sui-color-dot [color]=\"currentPreset()!\" state=\"selected\" class=\"sui-color-picker__preview\" />\n } @else {\n <sui-color-dot [customHex]=\"previewColor()\" state=\"selected\" class=\"sui-color-picker__preview\" />\n }\n } @else {\n <span class=\"sui-color-picker__placeholder-dot\" aria-hidden=\"true\"></span>\n }\n <span class=\"sui-color-picker__label\">{{ currentLabel() }}</span>\n <sui-icon name=\"arrow-down-s-line\" [size]=\"16\" class=\"sui-color-picker__chevron\" />\n</button>\n\n<!-- Panel en CDK overlay (via sui-dropdown-menu): escapa cualquier overflow\n clipping del contenedor (Storybook docs, modales, tablas). El contenido del\n ng-template mantiene el scoping de estilos del componente. -->\n<sui-dropdown-menu\n [origin]=\"trigger\"\n [disabled]=\"disabled()\"\n panelClass=\"sui-color-picker__overlay-pane\"\n (openedChange)=\"onMenuOpenedChange($event)\"\n>\n <ng-template suiDropdownPanel>\n <div\n class=\"sui-color-picker__panel\"\n role=\"dialog\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n @if (!presetsOnly()) {\n <!-- Secci\u00F3n \"Colors\" \u2014 Figma 4456:115716 -->\n <div class=\"sui-color-picker__section sui-color-picker__section--colors\">\n <!-- HSV Color Spectrum \u2014 Figma 4456:115425 -->\n <div\n #canvas\n class=\"sui-color-picker__canvas\"\n role=\"application\"\n aria-label=\"Saturaci\u00F3n y brillo\"\n [style.--hue-color]=\"hueColor()\"\n (pointerdown)=\"onCanvasPointerDown($event)\"\n (pointermove)=\"onCanvasPointerMove($event)\"\n (pointerup)=\"onCanvasPointerUp($event)\"\n (pointercancel)=\"onCanvasPointerUp($event)\"\n >\n <div\n class=\"sui-color-picker__canvas-thumb\"\n [style.left]=\"canvasThumbX()\"\n [style.top]=\"canvasThumbY()\"\n [style.background-color]=\"currentHex()\"\n ></div>\n </div>\n\n <!-- Hue slider \u2014 Figma 4415:53669 -->\n <div\n #hueSlider\n class=\"sui-color-picker__slider sui-color-picker__hue\"\n role=\"slider\"\n aria-label=\"Matiz\"\n aria-valuemin=\"0\"\n aria-valuemax=\"360\"\n [attr.aria-valuenow]=\"hueRounded()\"\n (pointerdown)=\"onHuePointerDown($event)\"\n (pointermove)=\"onHuePointerMove($event)\"\n (pointerup)=\"onHuePointerUp($event)\"\n (pointercancel)=\"onHuePointerUp($event)\"\n >\n <div class=\"sui-color-picker__slider-track\"></div>\n <div\n class=\"sui-color-picker__slider-thumb\"\n [style.left]=\"hueThumbX()\"\n [style.background-color]=\"hueColor()\"\n ></div>\n </div>\n\n <!-- Opacity slider \u2014 Figma 4415:53670. Visible por default (alpha on);\n ocultable con [alpha]=false. -->\n @if (alpha()) {\n <div\n #opacitySlider\n class=\"sui-color-picker__slider sui-color-picker__opacity\"\n role=\"slider\"\n aria-label=\"Opacidad\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"alphaPercent()\"\n [style.--opacity-color]=\"opacityColor()\"\n (pointerdown)=\"onOpacityPointerDown($event)\"\n (pointermove)=\"onOpacityPointerMove($event)\"\n (pointerup)=\"onOpacityPointerUp($event)\"\n (pointercancel)=\"onOpacityPointerUp($event)\"\n >\n <div class=\"sui-color-picker__slider-track\"></div>\n <div\n class=\"sui-color-picker__slider-thumb\"\n [style.left]=\"opacityThumbX()\"\n [style.--thumb-color]=\"opacityThumbColor()\"\n ></div>\n </div>\n }\n\n <!-- Color Input \u2014 Figma 4456:115720 -->\n <div class=\"sui-color-picker__color-input\">\n <!-- Format label \"Hex\" + chevron \u2014 Figma 4456:115721.\n Hex-only por ahora (ver PENDING VERIFY en el .ts). -->\n @if (showFormatLabel()) {\n <div class=\"sui-color-picker__format\" aria-hidden=\"true\">\n <span class=\"sui-color-picker__format-label\">Hex</span>\n <sui-icon name=\"arrow-down-s-line\" [size]=\"20\" class=\"sui-color-picker__format-chevron\" />\n </div>\n }\n\n <!-- Input row \u2014 Figma 4456:115724: sip | hex | opacity -->\n <div class=\"sui-color-picker__input-row\">\n <!-- Sip (eyedropper) \u2014 Figma 4456:115796. Solo con EyeDropper API. -->\n @if (showEyedropper()) {\n <button\n type=\"button\"\n class=\"sui-color-picker__sip\"\n aria-label=\"Cuentagotas\"\n (click)=\"onEyedropper()\"\n (touchstart)=\"$event.currentTarget\"\n (touchend)=\"$any($event.currentTarget).blur()\"\n >\n <sui-icon name=\"sip-line\" [size]=\"20\" />\n </button>\n }\n\n <!-- Hex field \u2014 Figma 4456:115725 / 4456:115726: el texto va SIN\n prefijo '#' (Figma muestra \"335CFF\" pelado dentro de la celda). -->\n <div class=\"sui-color-picker__hex-cell\">\n <input\n class=\"sui-color-picker__hex-field\"\n type=\"text\"\n inputmode=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n maxlength=\"8\"\n aria-label=\"Valor hexadecimal\"\n [value]=\"hexDisplayValue()\"\n (input)=\"onHexInputChange($event)\"\n (keydown)=\"onHexKeyDown($event)\"\n (blur)=\"onHexCommit()\"\n />\n </div>\n\n <!-- Opacity field \"%\" \u2014 Figma 4456:115727. Visible por default\n (alpha on); ocultable con [alpha]=false. -->\n @if (alpha()) {\n <div class=\"sui-color-picker__opacity-cell\">\n <input\n class=\"sui-color-picker__opacity-field\"\n type=\"text\"\n inputmode=\"numeric\"\n autocomplete=\"off\"\n maxlength=\"3\"\n aria-label=\"Opacidad\"\n [value]=\"alphaPercent()\"\n (change)=\"onOpacityFieldCommit($event)\"\n (keydown)=\"onHexKeyDown($event)\"\n />\n <span class=\"sui-color-picker__opacity-suffix\" aria-hidden=\"true\">%</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Secci\u00F3n \"Recommended Colors\" \u2014 Figma 4456:115765 -->\n <div class=\"sui-color-picker__section sui-color-picker__section--recommended\">\n <p class=\"sui-color-picker__panel-label\">{{ recommendedLabel() }}</p>\n <div class=\"sui-color-picker__grid\" role=\"radiogroup\">\n @for (c of colors(); track c) {\n <sui-color-dot\n [color]=\"c\"\n [state]=\"currentPreset() === c ? 'selected' : 'default'\"\n (clicked)=\"onPresetClick(c)\"\n />\n }\n </div>\n </div>\n </div>\n </ng-template>\n</sui-dropdown-menu>\n", styles: ["@charset \"UTF-8\";:host{position:relative;display:inline-block;font-family:var(--sui-font-sans);font-feature-settings:\"ss11\" 1,\"calt\" 0,\"liga\" 0}.sui-color-picker__trigger{display:inline-flex;align-items:center;gap:8px;padding:6px 10px 6px 6px;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-8);cursor:pointer;appearance:none;font:inherit;color:var(--sui-text-strong-950);font-size:var(--sui-type-label-sm-size);font-weight:var(--sui-type-label-sm-weight);line-height:var(--sui-type-label-sm-line);letter-spacing:var(--sui-type-label-sm-spacing);transition:background-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out),border-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}.sui-color-picker__trigger:hover{background:var(--sui-bg-weak-50)}:host([data-sui-open]) .sui-color-picker__trigger{border-color:var(--sui-stroke-strong-950, var(--sui-text-strong-950))}.sui-color-picker__trigger:focus-visible{outline:2px solid var(--sui-stroke-strong-950, var(--sui-text-strong-950));outline-offset:2px}.sui-color-picker__trigger:active{transform:none}:host([data-sui-disabled]) .sui-color-picker__trigger{background:var(--sui-bg-weak-50);color:var(--sui-text-disabled-300);cursor:not-allowed;pointer-events:none}.sui-color-picker__placeholder-dot{display:inline-block;width:24px;height:24px;border-radius:50%;background:var(--sui-bg-soft-200)}.sui-color-picker__label{flex:1 0 auto;white-space:nowrap}.sui-color-picker__chevron{flex-shrink:0;color:var(--sui-icon-sub-600);transition:transform var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}:host([data-sui-open]) .sui-color-picker__chevron{transform:rotate(180deg)}.sui-color-picker__panel{width:272px;margin-top:6px;box-sizing:border-box;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-16);box-shadow:var(--sui-shadow-medium);overflow:hidden;display:flex;flex-direction:column;animation:sui-color-picker__fade var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}@keyframes sui-color-picker__fade{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.sui-color-picker__section{box-sizing:border-box;display:flex;flex-direction:column;gap:12px;padding:16px;width:100%}.sui-color-picker__section--colors{border-bottom:1px solid var(--sui-stroke-soft-200)}.sui-color-picker__section--recommended{gap:8px}:host([data-sui-presets-only]) .sui-color-picker__section--recommended{border-bottom:none}.sui-color-picker__canvas{position:relative;width:100%;height:232px;border-radius:var(--sui-radius-8);cursor:crosshair;-webkit-user-select:none;user-select:none;touch-action:none;overflow:hidden;background-color:var(--hue-color, #335cff);background-image:linear-gradient(to top,#000,#0000),linear-gradient(to right,#fff,#fff0)}.sui-color-picker__canvas-thumb{position:absolute;width:16px;height:16px;border-radius:50%;border:2px solid var(--sui-static-white, #fff);box-shadow:0 0 0 1px #00000026,0 1px 2px #0003;transform:translate(-50%,-50%);pointer-events:none}.sui-color-picker__slider{position:relative;width:100%;height:16px;cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:none}.sui-color-picker__slider-track{position:absolute;left:0;right:0;top:4px;height:8px;border-radius:var(--sui-radius-full);overflow:hidden}.sui-color-picker__hue .sui-color-picker__slider-track{background-image:linear-gradient(to right,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.sui-color-picker__opacity .sui-color-picker__slider-track{background-color:#fff;background-image:linear-gradient(to right,transparent,var(--opacity-color, #335cff)),conic-gradient(#ebebeb 0 25%,transparent 0 50%,#ebebeb 0 75%,transparent 0);background-size:auto,10.88px 10.88px}.sui-color-picker__slider-thumb{position:absolute;top:50%;width:16px;height:16px;border-radius:50%;border:2px solid var(--sui-static-white, #fff);box-shadow:0 0 0 1px #00000026,0 1px 2px #0003;transform:translate(-50%,-50%);pointer-events:none}.sui-color-picker__opacity .sui-color-picker__slider-thumb{background-image:linear-gradient(var(--thumb-color, #335cff),var(--thumb-color, #335cff)),conic-gradient(#ebebeb 0 25%,#fff 0 50%,#ebebeb 0 75%,#fff 0);background-size:auto,6px 6px}.sui-color-picker__color-input{display:flex;flex-direction:column;gap:4px;width:100%}.sui-color-picker__format{display:flex;align-items:center;gap:2px;appearance:none;border:none;background:transparent;padding:0;cursor:default;font:inherit;color:var(--sui-text-strong-950)}.sui-color-picker__format-label{font-size:var(--sui-type-label-sm-size);font-weight:var(--sui-type-label-sm-weight);line-height:var(--sui-type-label-sm-line);letter-spacing:var(--sui-type-label-sm-spacing);white-space:nowrap}.sui-color-picker__format-chevron{flex-shrink:0;color:var(--sui-icon-sub-600)}.sui-color-picker__input-row{box-sizing:border-box;display:flex;align-items:stretch;width:100%;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-8);overflow:hidden}.sui-color-picker__sip{display:flex;align-items:center;justify-content:center;padding:6px;border:none;border-right:1px solid var(--sui-stroke-soft-200);background:var(--sui-bg-white-0);color:var(--sui-icon-sub-600);cursor:pointer;appearance:none;flex-shrink:0;transition:background-color var(--sui-motion-duration-x-fast) var(--sui-motion-ease-out)}.sui-color-picker__sip:hover{background:var(--sui-bg-weak-50)}.sui-color-picker__sip:focus-visible{outline:2px solid var(--sui-stroke-strong-950, var(--sui-text-strong-950));outline-offset:-2px}.sui-color-picker__sip:active{transform:none}.sui-color-picker__hex-cell{box-sizing:border-box;display:flex;align-items:center;flex:1 0 0;min-width:0;padding:6px 10px;border-right:1px solid var(--sui-stroke-soft-200)}.sui-color-picker__hex-cell:last-child{border-right:none}.sui-color-picker__hex-field{flex:1 0 0;min-width:0;width:100%;border:none;outline:none;background:transparent;padding:0;appearance:none;color:var(--sui-text-strong-950);font-family:inherit;font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing);text-transform:uppercase}.sui-color-picker__hex-field::placeholder{color:var(--sui-text-soft-400);text-transform:none}.sui-color-picker__opacity-cell{box-sizing:border-box;display:flex;align-items:center;flex-shrink:0;padding:6px 10px}.sui-color-picker__opacity-field{width:3.5ch;border:none;outline:none;background:transparent;padding:0;appearance:none;text-align:right;color:var(--sui-text-sub-600);font-family:inherit;font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing)}.sui-color-picker__opacity-suffix{color:var(--sui-text-sub-600);font-size:var(--sui-type-paragraph-sm-size);font-weight:var(--sui-type-paragraph-sm-weight);line-height:var(--sui-type-paragraph-sm-line);letter-spacing:var(--sui-type-paragraph-sm-spacing)}.sui-color-picker__panel-label{margin:0;font-size:var(--sui-type-paragraph-xs-size);font-weight:var(--sui-type-paragraph-xs-weight);line-height:var(--sui-type-paragraph-xs-line);letter-spacing:var(--sui-type-paragraph-xs-spacing);color:var(--sui-text-sub-600)}.sui-color-picker__grid{display:flex;flex-wrap:wrap;gap:4px;width:100%}\n"] }]
3991
+ }], propDecorators: { colors: [{ type: i0.Input, args: [{ isSignal: true, alias: "colors", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], recommendedLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "recommendedLabel", required: false }] }], presetsOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "presetsOnly", required: false }] }], alpha: [{ type: i0.Input, args: [{ isSignal: true, alias: "alpha", required: false }] }], eyedropper: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyedropper", required: false }] }], showFormatLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFormatLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], change: [{ type: i0.Output, args: ["change"] }], _menu: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SuiDropdownMenu), { isSignal: true }] }], canvasRef: [{ type: i0.ViewChild, args: ['canvas', { isSignal: true }] }], hueRef: [{ type: i0.ViewChild, args: ['hueSlider', { isSignal: true }] }], opacityRef: [{ type: i0.ViewChild, args: ['opacitySlider', { isSignal: true }] }] } });
3792
3992
 
3793
3993
  class SuiKbd {
3794
3994
  variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
@@ -4476,218 +4676,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
4476
4676
  }, template: "<div class=\"sui-dropdown-item__leading\">\n <ng-content select=\"[slot=leading]\" />\n</div>\n\n<div class=\"sui-dropdown-item__text\">\n <span class=\"sui-dropdown-item__label\">{{ label() }}</span>\n @if (hasSublabel()) {\n <span class=\"sui-dropdown-item__sublabel\">{{ sublabel() }}</span>\n }\n @if (hasDescription()) {\n <span class=\"sui-dropdown-item__description\">{{ description() }}</span>\n }\n</div>\n\n<div class=\"sui-dropdown-item__trailing\">\n <!-- Canon trailing-slot (Figma 405:6432): el consumer proyecta en este orden DOM\n fijo \u2192 badge primero, luego shortcut (provisto por el input shortcut() abajo),\n luego toggle/button al final. El gap del flex (8px) los separa.\n Pill button (variante Large): bg primary-alpha-10, p-6, radius-8, label primary\n Inter Medium 14/20. Link button (variante Small): texto primary sin bg.\n El componente no fija sublots \u2014 el orden lo controla el consumer seg\u00FAn este\n patr\u00F3n para mantener consistencia visual vs Figma. -->\n <ng-content select=\"[slot=trailing]\" />\n @if (shortcut()) {\n <span class=\"sui-dropdown-item__shortcut\">{{ shortcut() }}</span>\n }\n @if (showCheck()) {\n <!-- Check icon autom\u00E1tico en mode='option' cuando selected=true.\n Remixicon check-line (ri-check-line) v\u00EDa sui-icon \u2014 mismo glyph que\n el resto del proyecto, color heredado de .sui-dropdown-item__check. -->\n <sui-icon class=\"sui-dropdown-item__check\" name=\"check-line\" [size]=\"20\" />\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;align-items:center;gap:8px;padding:8px;background:var(--sui-bg-white-0);border-radius:var(--sui-radius-8, 8px);cursor:pointer;outline:none;width:100%;box-sizing:border-box;font-family:var(--sui-font-sans);transition:background .1s ease-out,box-shadow .1s ease-out;-webkit-user-select:none;user-select:none}:host:hover{background:var(--sui-bg-weak-50)}:host:active:not([data-sui-disabled]),:host:focus-visible{box-shadow:0 0 0 2px var(--sui-bg-white-0),0 0 0 4px var(--sui-primary-alpha-10)}:host([data-sui-size=small]){min-height:36px;padding:8px;gap:8px}:host([data-sui-size=large]){min-height:56px;padding:8px;gap:12px;border-radius:var(--sui-radius-10, 10px)}:host([data-sui-size=large]) .sui-dropdown-item__text{flex-direction:column;align-items:flex-start;gap:4px}:host([data-sui-size=large]) .sui-dropdown-item__label{font-weight:var(--sui-type-label-sm-weight, 500)}:host([data-sui-size=xlarge]){min-height:76px;padding:8px;gap:16px;border-radius:var(--sui-radius-12, 12px)}:host([data-sui-size=xlarge]) .sui-dropdown-item__text{flex-direction:column;align-items:flex-start;gap:4px}:host([data-sui-size=xlarge]) .sui-dropdown-item__label{font-size:var(--sui-type-label-md-size, 16px);line-height:var(--sui-type-label-md-line, 24px);letter-spacing:var(--sui-type-label-md-spacing, -.011em);font-weight:var(--sui-type-label-md-weight, 500)}:host([data-sui-size=xlarge]) .sui-dropdown-item__description{font-size:var(--sui-type-paragraph-sm-size, 14px);line-height:var(--sui-type-paragraph-sm-line, 20px);letter-spacing:var(--sui-type-paragraph-sm-spacing, -.006em)}.sui-dropdown-item__leading{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center}.sui-dropdown-item__leading:empty{display:none}:host([data-sui-size=large][data-sui-framed]) .sui-dropdown-item__leading,:host([data-sui-size=xlarge][data-sui-framed]) .sui-dropdown-item__leading{padding:10px;border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-full, 999px);box-shadow:var(--sui-regular-shadow-x-small, 0 1px 2px 0 rgba(10, 13, 20, .031372549));box-sizing:border-box}:host([data-sui-size=xlarge][data-sui-framed]) .sui-dropdown-item__leading{padding:12px}.sui-dropdown-item__text{flex:1 1 0;min-width:0;display:flex;align-items:center;gap:4px;font-feature-settings:\"ss11\" 1,\"calt\" 0,\"liga\" 0}.sui-dropdown-item__label{font-size:var(--sui-type-paragraph-sm-size, 14px);line-height:var(--sui-type-paragraph-sm-line, 20px);letter-spacing:var(--sui-type-paragraph-sm-spacing, -.006em);font-weight:var(--sui-type-paragraph-sm-weight, 400);color:var(--sui-text-strong-950);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sui-dropdown-item__sublabel{font-size:var(--sui-type-paragraph-xs-size, 12px);line-height:var(--sui-type-paragraph-xs-line, 16px);letter-spacing:var(--sui-type-paragraph-xs-spacing, 0);font-weight:var(--sui-type-paragraph-xs-weight, 400);color:var(--sui-text-soft-400);flex-shrink:0}.sui-dropdown-item__description{font-size:var(--sui-type-paragraph-xs-size, 12px);line-height:var(--sui-type-paragraph-xs-line, 16px);letter-spacing:var(--sui-type-paragraph-xs-spacing, 0);font-weight:var(--sui-type-paragraph-xs-weight, 400);color:var(--sui-text-sub-600);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}.sui-dropdown-item__trailing{flex-shrink:0;display:inline-flex;align-items:center;gap:8px}.sui-dropdown-item__trailing:empty{display:none}.sui-dropdown-item__shortcut{display:inline-flex;align-items:center;padding:2px 6px;background:var(--sui-bg-white-0);border:1px solid var(--sui-stroke-soft-200);border-radius:var(--sui-radius-4, 4px);font-family:var(--sui-font-sans);font-size:12px;line-height:16px;font-weight:500;color:var(--sui-text-sub-600);letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;flex-shrink:0}:host([data-sui-mode=option][data-sui-selected]){background:var(--sui-primary-alpha-10)}:host([data-sui-mode=option][data-sui-selected]) .sui-dropdown-item__label,:host([data-sui-mode=option][data-sui-selected]) .sui-dropdown-item__sublabel,:host([data-sui-mode=option][data-sui-selected]) .sui-dropdown-item__check{color:var(--sui-primary-base)}:host([data-sui-mode=option][data-sui-selected]):hover{background:var(--sui-primary-alpha-16)}.sui-dropdown-item__check{width:20px;height:20px;flex-shrink:0;color:var(--sui-primary-base)}:host([data-sui-highlighted]:not([data-sui-selected])){background:var(--sui-bg-weak-50)}:host([data-sui-disabled]){cursor:not-allowed;pointer-events:none}:host([data-sui-disabled]) .sui-dropdown-item__label,:host([data-sui-disabled]) .sui-dropdown-item__sublabel,:host([data-sui-disabled]) .sui-dropdown-item__description{color:var(--sui-text-disabled-300)}:host([data-sui-disabled]) .sui-dropdown-item__leading{color:var(--sui-icon-disabled-300, #d1d1d1)}:host([data-sui-disabled]) .sui-dropdown-item__leading>:not(sui-icon){opacity:.48}:host([data-sui-disabled]):hover{background:var(--sui-bg-white-0)}\n"] }]
4477
4677
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], sublabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "sublabel", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], leadingFramed: [{ type: i0.Input, args: [{ isSignal: true, alias: "leadingFramed", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], highlighted: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlighted", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], shortcut: [{ type: i0.Input, args: [{ isSignal: true, alias: "shortcut", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }] } });
4478
4678
 
4479
- /**
4480
- * Structural directive — marca un `<ng-template>` como panel del
4481
- * sui-dropdown-menu. El componente padre captura este TemplateRef
4482
- * via `@ContentChild(SuiDropdownPanel)` y lo monta en el CDK Overlay
4483
- * cuando se abre el menu.
4484
- *
4485
- * Usa el patrón canon Angular (similar a `*matMenuContent` de Material):
4486
- * un directive que se aplica al `<ng-template>` SE convierte en una
4487
- * marca semántica, evitando que Angular renderee el contenido inline
4488
- * cuando no hay `<ng-content>` matching para el template.
4489
- *
4490
- * Uso:
4491
- * <sui-dropdown-menu>
4492
- * <sui-compact-select trigger ... />
4493
- * <ng-template suiDropdownPanel>
4494
- * <sui-dropdown-item ...></sui-dropdown-item>
4495
- * </ng-template>
4496
- * </sui-dropdown-menu>
4497
- */
4498
- class SuiDropdownPanel {
4499
- template = inject(TemplateRef);
4500
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SuiDropdownPanel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
4501
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.17", type: SuiDropdownPanel, isStandalone: true, selector: "ng-template[suiDropdownPanel]", ngImport: i0 });
4502
- }
4503
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SuiDropdownPanel, decorators: [{
4504
- type: Directive,
4505
- args: [{
4506
- selector: 'ng-template[suiDropdownPanel]',
4507
- standalone: true,
4508
- }]
4509
- }] });
4510
-
4511
- /* Posiciones con fallback HORIZONTAL: la primera (start-aligned, abre a la
4512
- derecha) es la default; si el trigger está pegado al borde derecho y no entra,
4513
- CDK cae a la end-aligned (abre hacia la izquierda) y recién después flipea
4514
- vertical. Sin esto, un trigger en la esquina derecha (ej. kebab ⋮ de una card)
4515
- recorta el panel fuera del viewport. */
4516
- const POSITIONS_BELOW = [
4517
- { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
4518
- { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
4519
- { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
4520
- { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
4521
- ];
4522
- const POSITIONS_ABOVE = [
4523
- { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
4524
- { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
4525
- { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
4526
- { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
4527
- ];
4528
- class SuiDropdownMenu {
4529
- /** Si true, el panel matchea el ancho del trigger. Default: auto width. */
4530
- matchWidth = input(false, ...(ngDevMode ? [{ debugName: "matchWidth" }] : /* istanbul ignore next */ []));
4531
- /** Min-width fijo del panel (px). Si matchWidth + minWidth: `max(triggerWidth, minWidth)`. */
4532
- minWidth = input(null, ...(ngDevMode ? [{ debugName: "minWidth" }] : /* istanbul ignore next */ []));
4533
- /** Position primary. Default: auto = below con fallback above. */
4534
- position = input('auto', ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
4535
- /** Margen 8px del viewport edge (canon SUI). */
4536
- viewportMargin = input(8, ...(ngDevMode ? [{ debugName: "viewportMargin" }] : /* istanbul ignore next */ []));
4537
- /** Panel custom class para overrides de bg/padding/etc desde el consumer. */
4538
- panelClass = input('', ...(ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []));
4539
- /** Disabled — no abre el menu al clickear el trigger. */
4540
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4541
- /**
4542
- * Rol del popup para `aria-haspopup` del trigger. Default 'menu' (los items
4543
- * proyectados llevan role="menuitem"). Los consumers de tipo listbox
4544
- * (sui-select / compact-selects, items con role="option") DEBEN pasar
4545
- * `popupRole="listbox"`. Ver SuiDropdownMenuPopupRole.
4546
- */
4547
- popupRole = input('menu', ...(ngDevMode ? [{ debugName: "popupRole" }] : /* istanbul ignore next */ []));
4548
- /**
4549
- * Origin externo. Si presente, el menu se ancla a ese elemento y NO renderea
4550
- * el trigger wrapper interno (el parent maneja el click). Caso de uso:
4551
- * compact-selects donde el host element es el trigger visual.
4552
- */
4553
- origin = input(null, ...(ngDevMode ? [{ debugName: "origin" }] : /* istanbul ignore next */ []));
4554
- openedChange = output();
4555
- /** Signal pública — wire al `[open]` del trigger para visual state. */
4556
- isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
4557
- /** Wrapper del trigger projection — captura el click. NO existe si `[origin]` se pasa. */
4558
- _triggerWrap;
4559
- /** Panel directive proyectado — capturado via `[suiDropdownPanel]` en un ng-template. */
4560
- _panel;
4561
- _getTriggerEl() {
4562
- const o = this.origin();
4563
- if (o)
4564
- return o instanceof ElementRef ? o.nativeElement : o;
4565
- return this._triggerWrap?.nativeElement ?? null;
4566
- }
4567
- _vcr = inject(ViewContainerRef);
4568
- _destroyRef = inject(DestroyRef);
4569
- _overlay = inject(Overlay);
4570
- _overlayRef = null;
4571
- _overlaySubs = null;
4572
- _resizeObserver = null;
4573
- constructor() {
4574
- this._destroyRef.onDestroy(() => this.close());
4575
- }
4576
- onTriggerClick(event) {
4577
- if (this.disabled())
4578
- return;
4579
- event.stopPropagation();
4580
- this.toggle();
4581
- }
4582
- onTriggerKeydown(event) {
4583
- if (this.disabled())
4584
- return;
4585
- if (event.key === 'Enter' || event.key === ' ') {
4586
- event.preventDefault();
4587
- this.toggle();
4588
- }
4589
- else if (event.key === 'ArrowDown') {
4590
- event.preventDefault();
4591
- this.open();
4592
- }
4593
- }
4594
- toggle() {
4595
- if (this.isOpen())
4596
- this.close();
4597
- else
4598
- this.open();
4599
- }
4600
- open() {
4601
- if (this.disabled() || this._overlayRef || !this._panel)
4602
- return;
4603
- const triggerEl = this._getTriggerEl();
4604
- if (!triggerEl)
4605
- return;
4606
- const positions = this.position() === 'above' ? POSITIONS_ABOVE : POSITIONS_BELOW;
4607
- const positionStrategy = this._overlay
4608
- .position()
4609
- .flexibleConnectedTo(triggerEl)
4610
- .withPositions(positions)
4611
- .withPush(false)
4612
- .withFlexibleDimensions(true)
4613
- .withViewportMargin(this.viewportMargin());
4614
- const triggerWidth = triggerEl.getBoundingClientRect().width;
4615
- const minW = this.minWidth();
4616
- const effectiveMinWidth = this.matchWidth() && minW !== null
4617
- ? Math.max(triggerWidth, minW)
4618
- : minW;
4619
- const config = {
4620
- positionStrategy,
4621
- scrollStrategy: this._overlay.scrollStrategies.reposition(),
4622
- hasBackdrop: true,
4623
- backdropClass: 'cdk-overlay-transparent-backdrop',
4624
- panelClass: ['sui-dropdown-menu__overlay-pane', this.panelClass()].filter(Boolean),
4625
- disposeOnNavigation: true,
4626
- };
4627
- if (this.matchWidth() && minW === null) {
4628
- config.width = `${triggerWidth}px`;
4629
- }
4630
- if (effectiveMinWidth !== null) {
4631
- config.minWidth = effectiveMinWidth;
4632
- }
4633
- this._overlayRef = this._overlay.create(config);
4634
- const portal = new TemplatePortal(this._panel.template, this._vcr);
4635
- this._overlayRef.attach(portal);
4636
- /* Width sync mientras el viewport cambia (canon sui-select). Solo aplica
4637
- cuando matchWidth fija width o minWidth — para que el panel siga el trigger. */
4638
- if (this.matchWidth() && typeof ResizeObserver !== 'undefined') {
4639
- this._resizeObserver = new ResizeObserver(() => {
4640
- if (!this._overlayRef)
4641
- return;
4642
- const w = triggerEl.getBoundingClientRect().width;
4643
- if (minW === null) {
4644
- this._overlayRef.updateSize({ width: `${w}px` });
4645
- }
4646
- else {
4647
- this._overlayRef.updateSize({ minWidth: Math.max(w, minW) });
4648
- }
4649
- this._overlayRef.updatePosition();
4650
- });
4651
- this._resizeObserver.observe(triggerEl);
4652
- }
4653
- this._overlaySubs = new Subscription();
4654
- this._overlaySubs.add(this._overlayRef.backdropClick().subscribe(() => this.close()));
4655
- this._overlaySubs.add(this._overlayRef.keydownEvents().subscribe((e) => {
4656
- if (e.key === 'Escape')
4657
- this.close();
4658
- }));
4659
- this._overlaySubs.add(this._overlayRef.detachments().subscribe(() => {
4660
- this.isOpen.set(false);
4661
- }));
4662
- this.isOpen.set(true);
4663
- this.openedChange.emit(true);
4664
- }
4665
- close() {
4666
- if (!this._overlayRef)
4667
- return;
4668
- this._overlaySubs?.unsubscribe();
4669
- this._overlaySubs = null;
4670
- this._resizeObserver?.disconnect();
4671
- this._resizeObserver = null;
4672
- this._overlayRef.dispose();
4673
- this._overlayRef = null;
4674
- this.isOpen.set(false);
4675
- this.openedChange.emit(false);
4676
- }
4677
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SuiDropdownMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
4678
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SuiDropdownMenu, isStandalone: true, selector: "sui-dropdown-menu", inputs: { matchWidth: { classPropertyName: "matchWidth", publicName: "matchWidth", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, viewportMargin: { classPropertyName: "viewportMargin", publicName: "viewportMargin", isSignal: true, isRequired: false, transformFunction: null }, panelClass: { classPropertyName: "panelClass", publicName: "panelClass", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, popupRole: { classPropertyName: "popupRole", publicName: "popupRole", isSignal: true, isRequired: false, transformFunction: null }, origin: { classPropertyName: "origin", publicName: "origin", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openedChange: "openedChange" }, queries: [{ propertyName: "_panel", first: true, predicate: SuiDropdownPanel, descendants: true }], viewQueries: [{ propertyName: "_triggerWrap", first: true, predicate: ["triggerWrap"], descendants: true }], exportAs: ["suiDropdownMenu"], ngImport: i0, template: "<!-- Trigger wrapper \u2014 captura click y keydown del trigger child. Solo se\n renderea cuando NO se pasa [origin] (modo content-projection canon).\n Cuando se pasa [origin], el parent maneja el trigger y llama\n menu.toggle() desde su propio click handler. -->\n@if (!origin()) {\n <span\n #triggerWrap\n class=\"sui-dropdown-menu__trigger\"\n [attr.aria-haspopup]=\"popupRole()\"\n [attr.aria-expanded]=\"isOpen()\"\n (click)=\"onTriggerClick($event)\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n <ng-content select=\"[trigger]\" />\n </span>\n}\n", styles: ["@charset \"UTF-8\";:host{display:contents}.sui-dropdown-menu__trigger{display:inline-block;cursor:pointer;outline:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4679
- }
4680
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SuiDropdownMenu, decorators: [{
4681
- type: Component,
4682
- args: [{ selector: 'sui-dropdown-menu', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'suiDropdownMenu', template: "<!-- Trigger wrapper \u2014 captura click y keydown del trigger child. Solo se\n renderea cuando NO se pasa [origin] (modo content-projection canon).\n Cuando se pasa [origin], el parent maneja el trigger y llama\n menu.toggle() desde su propio click handler. -->\n@if (!origin()) {\n <span\n #triggerWrap\n class=\"sui-dropdown-menu__trigger\"\n [attr.aria-haspopup]=\"popupRole()\"\n [attr.aria-expanded]=\"isOpen()\"\n (click)=\"onTriggerClick($event)\"\n (keydown)=\"onTriggerKeydown($event)\"\n >\n <ng-content select=\"[trigger]\" />\n </span>\n}\n", styles: ["@charset \"UTF-8\";:host{display:contents}.sui-dropdown-menu__trigger{display:inline-block;cursor:pointer;outline:none}\n"] }]
4683
- }], ctorParameters: () => [], propDecorators: { matchWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "matchWidth", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], viewportMargin: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportMargin", required: false }] }], panelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelClass", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], popupRole: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupRole", required: false }] }], origin: [{ type: i0.Input, args: [{ isSignal: true, alias: "origin", required: false }] }], openedChange: [{ type: i0.Output, args: ["openedChange"] }], _triggerWrap: [{
4684
- type: ViewChild,
4685
- args: ['triggerWrap', { static: false }]
4686
- }], _panel: [{
4687
- type: ContentChild,
4688
- args: [SuiDropdownPanel]
4689
- }] } });
4690
-
4691
4679
  class SuiCompactSelectForInput {
4692
4680
  size = input('medium', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
4693
4681
  value = input('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
@@ -14603,8 +14591,13 @@ class SuiTable {
14603
14591
  let sx = 0, sy = 0, lx = 0, ly = 0;
14604
14592
  let axis = null;
14605
14593
  let pageScroller = null;
14606
- /* Velocidad vertical (px/ms) + timestamp del último move, para el fling. */
14607
- let vy = 0, lastT = 0, momentumRaf = 0, momentumTs = 0;
14594
+ /* Velocidad vertical (px/ms) para el fling. CRÍTICO: se estima con una
14595
+ ventana de samples (~100ms) tipo VelocityTracker de Android — NO con el
14596
+ último move suelto: el dedo siempre desacelera antes de soltar, así que
14597
+ la velocidad instantánea final es ~0 y el fling moría → scroll "lento". */
14598
+ let vy = 0, momentumRaf = 0, momentumTs = 0;
14599
+ const samples = [];
14600
+ const VELOCITY_WINDOW_MS = 100;
14608
14601
  const findPageScroller = () => {
14609
14602
  let n = el.parentElement;
14610
14603
  while (n && n !== this.doc.body) {
@@ -14631,7 +14624,8 @@ class SuiTable {
14631
14624
  sy = ly = t.clientY;
14632
14625
  axis = null;
14633
14626
  vy = 0;
14634
- lastT = e.timeStamp;
14627
+ samples.length = 0;
14628
+ samples.push({ y: t.clientY, t: e.timeStamp });
14635
14629
  pageScroller = findPageScroller();
14636
14630
  };
14637
14631
  const onMove = (e) => {
@@ -14657,22 +14651,27 @@ class SuiTable {
14657
14651
  el.scrollLeft -= t.clientX - lx;
14658
14652
  }
14659
14653
  else if (pageScroller) {
14660
- const dy = t.clientY - ly;
14661
- pageScroller.scrollTop -= dy;
14662
- const dt = e.timeStamp - lastT;
14663
- if (dt > 0)
14664
- vy = dy / dt; // px/ms (positivo = dedo bajando)
14654
+ pageScroller.scrollTop -= t.clientY - ly;
14655
+ samples.push({ y: t.clientY, t: e.timeStamp });
14656
+ while (samples.length > 1 && e.timeStamp - samples[0].t > VELOCITY_WINDOW_MS)
14657
+ samples.shift();
14665
14658
  }
14666
14659
  e.preventDefault();
14667
14660
  lx = t.clientX;
14668
14661
  ly = t.clientY;
14669
- lastT = e.timeStamp;
14670
14662
  };
14671
14663
  const onEnd = () => {
14672
14664
  /* Fling: continúa el scroll de la página con la velocidad de salida y
14673
14665
  fricción exponencial frame-rate-independent (para que en 120Hz no vuele).
14674
14666
  Replica el momentum nativo que el manejo manual del touch pierde. */
14675
- if (axis !== 'y' || !pageScroller || Math.abs(vy) < 0.05)
14667
+ if (axis !== 'y' || !pageScroller)
14668
+ return;
14669
+ /* Velocidad de salida = promedio de la ventana (primer→último sample). */
14670
+ const first = samples[0];
14671
+ const last = samples[samples.length - 1];
14672
+ const win = last && first ? last.t - first.t : 0;
14673
+ vy = win > 0 ? (last.y - first.y) / win : 0;
14674
+ if (Math.abs(vy) < 0.05)
14676
14675
  return;
14677
14676
  const scroller = pageScroller;
14678
14677
  momentumTs = 0;