@neural-ui/core 1.5.7 → 1.5.8

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,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { signal, computed, Injectable, inject, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
+ import * as i1 from '@angular/cdk/overlay';
4
+ import { Overlay, OverlayModule } from '@angular/cdk/overlay';
3
5
 
4
6
  const DEFAULT_ICONS = {
5
7
  info: 'ℹ️',
@@ -69,8 +71,34 @@ let _panelSeq = 0;
69
71
  */
70
72
  class NeuNotificationCenterComponent {
71
73
  _svc = inject(NeuNotificationService);
74
+ overlay = inject(Overlay);
72
75
  _isOpen = signal(false, ...(ngDevMode ? [{ debugName: "_isOpen" }] : /* istanbul ignore next */ []));
73
76
  _panelId = `neu-nc-panel-${++_panelSeq}`;
77
+ _viewportMargin = 16;
78
+ overlayPositions = [
79
+ {
80
+ originX: 'end',
81
+ originY: 'bottom',
82
+ overlayX: 'end',
83
+ overlayY: 'top',
84
+ offsetY: 8,
85
+ },
86
+ {
87
+ originX: 'end',
88
+ originY: 'top',
89
+ overlayX: 'end',
90
+ overlayY: 'bottom',
91
+ offsetY: -8,
92
+ },
93
+ {
94
+ originX: 'start',
95
+ originY: 'bottom',
96
+ overlayX: 'start',
97
+ overlayY: 'top',
98
+ offsetY: 8,
99
+ },
100
+ ];
101
+ overlayScrollStrategy = this.overlay.scrollStrategies.reposition();
74
102
  _toggle() {
75
103
  const opening = !this._isOpen();
76
104
  this._isOpen.set(opening);
@@ -95,6 +123,8 @@ class NeuNotificationCenterComponent {
95
123
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuNotificationCenterComponent, isStandalone: true, selector: "neu-notification-center", host: { properties: { "attr.aria-label": "\"Centro de notificaciones\"" }, classAttribute: "neu-nc" }, ngImport: i0, template: `
96
124
  <!-- Bell button -->
97
125
  <button
126
+ cdkOverlayOrigin
127
+ #notificationOrigin="cdkOverlayOrigin"
98
128
  type="button"
99
129
  class="neu-nc__bell"
100
130
  [attr.aria-expanded]="_isOpen()"
@@ -111,7 +141,19 @@ class NeuNotificationCenterComponent {
111
141
  </button>
112
142
 
113
143
  <!-- Panel -->
114
- @if (_isOpen()) {
144
+ <ng-template
145
+ cdkConnectedOverlay
146
+ [cdkConnectedOverlayOrigin]="notificationOrigin"
147
+ [cdkConnectedOverlayOpen]="_isOpen()"
148
+ [cdkConnectedOverlayPositions]="overlayPositions"
149
+ [cdkConnectedOverlayScrollStrategy]="overlayScrollStrategy"
150
+ [cdkConnectedOverlayHasBackdrop]="true"
151
+ [cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
152
+ [cdkConnectedOverlayPush]="true"
153
+ [cdkConnectedOverlayViewportMargin]="_viewportMargin"
154
+ (backdropClick)="_isOpen.set(false)"
155
+ (detach)="_isOpen.set(false)"
156
+ >
115
157
  <div
116
158
  class="neu-nc__panel"
117
159
  [id]="_panelId"
@@ -168,17 +210,19 @@ class NeuNotificationCenterComponent {
168
210
  }
169
211
  </div>
170
212
  </div>
171
- }
172
- `, isInline: true, styles: ["@charset \"UTF-8\";.neu-nc{position:relative;display:inline-block}.neu-nc__bell{all:unset;position:relative;display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:var(--neu-radius-lg, 12px);cursor:pointer;transition:background .12s}.neu-nc__bell:hover{background:var(--neu-surface-2, #f3f4f6)}.neu-nc__bell:focus-visible{outline:2px solid var(--neu-focus-ring, #0ea5e9);outline-offset:2px}.neu-nc__bell-icon{font-size:1.2rem}.neu-nc__badge{position:absolute;top:4px;right:4px;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:var(--neu-error);color:var(--neu-text-inverse);font-size:.625rem;font-weight:700;display:flex;align-items:center;justify-content:center;line-height:1}.neu-nc__panel{position:absolute;top:calc(100% + 8px);right:0;width:320px;max-height:420px;display:flex;flex-direction:column;background:var(--neu-surface-1, #ffffff);border:1px solid var(--neu-border-color, #e5e7eb);border-radius:var(--neu-radius-xl, 16px);box-shadow:0 12px 28px -6px #00000024;z-index:1001;overflow:hidden;animation:neu-nc-in .1s ease}@keyframes neu-nc-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.neu-nc__panel-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--neu-border-color, #e5e7eb);flex-shrink:0}.neu-nc__panel-title{font-size:.9375rem;font-weight:600;color:var(--neu-text-primary, #111)}.neu-nc__panel-actions{display:flex;gap:8px}.neu-nc__action-btn{all:unset;font-size:.75rem;color:var(--neu-color-primary, #0ea5e9);cursor:pointer;padding:2px 6px;border-radius:var(--neu-radius-sm, 4px)}.neu-nc__action-btn:hover{background:var(--neu-color-primary-alpha, rgba(14, 165, 233, .1))}.neu-nc__list{overflow-y:auto;flex:1;padding:6px}.neu-nc__empty{padding:24px;text-align:center;font-size:.875rem;color:var(--neu-text-secondary, #6b7280)}.neu-nc__item{display:flex;align-items:flex-start;gap:10px;padding:10px 10px 10px 12px;border-radius:var(--neu-radius-lg, 12px);position:relative;transition:background .1s}.neu-nc__item:hover{background:var(--neu-surface-2, #f3f4f6)}.neu-nc__item--unread:before{content:\"\";position:absolute;left:4px;top:50%;transform:translateY(-50%);width:6px;height:6px;border-radius:50%;background:var(--neu-color-primary, #0ea5e9)}.neu-nc__item-icon{font-size:1.1rem;flex-shrink:0;padding-top:2px}.neu-nc__item-body{flex:1;min-width:0}.neu-nc__item-title{margin:0 0 2px;font-size:.8125rem;font-weight:600;color:var(--neu-text-primary, #111)}.neu-nc__item-msg{margin:0 0 4px;font-size:.8125rem;color:var(--neu-text-secondary, #4b5563);line-height:1.4}.neu-nc__item-time{font-size:.6875rem;color:var(--neu-text-secondary, #9ca3af)}.neu-nc__item-close{all:unset;opacity:0;cursor:pointer;padding:2px 6px;border-radius:4px;font-size:1rem;color:var(--neu-text-secondary, #9ca3af);flex-shrink:0;transition:opacity .1s}.neu-nc__item:hover .neu-nc__item-close{opacity:1}.neu-nc__item-close:hover{color:var(--neu-text-primary, #111)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
213
+ </ng-template>
214
+ `, isInline: true, styles: ["@charset \"UTF-8\";.neu-nc{position:relative;display:inline-block}.neu-nc__bell{all:unset;position:relative;display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:var(--neu-radius-lg, 12px);cursor:pointer;transition:background .12s}.neu-nc__bell:hover{background:var(--neu-surface-2, #f3f4f6)}.neu-nc__bell:focus-visible{outline:2px solid var(--neu-focus-ring, #0ea5e9);outline-offset:2px}.neu-nc__bell-icon{font-size:1.2rem}.neu-nc__badge{position:absolute;top:4px;right:4px;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:var(--neu-error);color:var(--neu-text-inverse);font-size:.625rem;font-weight:700;display:flex;align-items:center;justify-content:center;line-height:1}.neu-nc__panel{position:relative;width:320px;max-height:420px;display:flex;flex-direction:column;background:var(--neu-surface-1, #ffffff);border:1px solid var(--neu-border-color, #e5e7eb);border-radius:var(--neu-radius-xl, 16px);box-shadow:0 12px 28px -6px #00000024;z-index:1001;overflow:hidden;animation:neu-nc-in .1s ease}@keyframes neu-nc-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.neu-nc__panel-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--neu-border-color, #e5e7eb);flex-shrink:0}.neu-nc__panel-title{font-size:.9375rem;font-weight:600;color:var(--neu-text-primary, #111)}.neu-nc__panel-actions{display:flex;gap:8px}.neu-nc__action-btn{all:unset;font-size:.75rem;color:var(--neu-color-primary, #0ea5e9);cursor:pointer;padding:2px 6px;border-radius:var(--neu-radius-sm, 4px)}.neu-nc__action-btn:hover{background:var(--neu-color-primary-alpha, rgba(14, 165, 233, .1))}.neu-nc__list{overflow-y:auto;flex:1;padding:6px}.neu-nc__empty{padding:24px;text-align:center;font-size:.875rem;color:var(--neu-text-secondary, #6b7280)}.neu-nc__item{display:flex;align-items:flex-start;gap:10px;padding:10px 10px 10px 12px;border-radius:var(--neu-radius-lg, 12px);position:relative;transition:background .1s}.neu-nc__item:hover{background:var(--neu-surface-2, #f3f4f6)}.neu-nc__item--unread:before{content:\"\";position:absolute;left:4px;top:50%;transform:translateY(-50%);width:6px;height:6px;border-radius:50%;background:var(--neu-color-primary, #0ea5e9)}.neu-nc__item-icon{font-size:1.1rem;flex-shrink:0;padding-top:2px}.neu-nc__item-body{flex:1;min-width:0}.neu-nc__item-title{margin:0 0 2px;font-size:.8125rem;font-weight:600;color:var(--neu-text-primary, #111)}.neu-nc__item-msg{margin:0 0 4px;font-size:.8125rem;color:var(--neu-text-secondary, #4b5563);line-height:1.4}.neu-nc__item-time{font-size:.6875rem;color:var(--neu-text-secondary, #9ca3af)}.neu-nc__item-close{all:unset;opacity:0;cursor:pointer;padding:2px 6px;border-radius:4px;font-size:1rem;color:var(--neu-text-secondary, #9ca3af);flex-shrink:0;transition:opacity .1s}.neu-nc__item:hover .neu-nc__item-close{opacity:1}.neu-nc__item-close:hover{color:var(--neu-text-primary, #111)}\n"], dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
173
215
  }
174
216
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuNotificationCenterComponent, decorators: [{
175
217
  type: Component,
176
- args: [{ selector: 'neu-notification-center', imports: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
218
+ args: [{ selector: 'neu-notification-center', imports: [OverlayModule], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
177
219
  class: 'neu-nc',
178
220
  '[attr.aria-label]': '"Centro de notificaciones"',
179
221
  }, template: `
180
222
  <!-- Bell button -->
181
223
  <button
224
+ cdkOverlayOrigin
225
+ #notificationOrigin="cdkOverlayOrigin"
182
226
  type="button"
183
227
  class="neu-nc__bell"
184
228
  [attr.aria-expanded]="_isOpen()"
@@ -195,7 +239,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
195
239
  </button>
196
240
 
197
241
  <!-- Panel -->
198
- @if (_isOpen()) {
242
+ <ng-template
243
+ cdkConnectedOverlay
244
+ [cdkConnectedOverlayOrigin]="notificationOrigin"
245
+ [cdkConnectedOverlayOpen]="_isOpen()"
246
+ [cdkConnectedOverlayPositions]="overlayPositions"
247
+ [cdkConnectedOverlayScrollStrategy]="overlayScrollStrategy"
248
+ [cdkConnectedOverlayHasBackdrop]="true"
249
+ [cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
250
+ [cdkConnectedOverlayPush]="true"
251
+ [cdkConnectedOverlayViewportMargin]="_viewportMargin"
252
+ (backdropClick)="_isOpen.set(false)"
253
+ (detach)="_isOpen.set(false)"
254
+ >
199
255
  <div
200
256
  class="neu-nc__panel"
201
257
  [id]="_panelId"
@@ -252,8 +308,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
252
308
  }
253
309
  </div>
254
310
  </div>
255
- }
256
- `, styles: ["@charset \"UTF-8\";.neu-nc{position:relative;display:inline-block}.neu-nc__bell{all:unset;position:relative;display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:var(--neu-radius-lg, 12px);cursor:pointer;transition:background .12s}.neu-nc__bell:hover{background:var(--neu-surface-2, #f3f4f6)}.neu-nc__bell:focus-visible{outline:2px solid var(--neu-focus-ring, #0ea5e9);outline-offset:2px}.neu-nc__bell-icon{font-size:1.2rem}.neu-nc__badge{position:absolute;top:4px;right:4px;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:var(--neu-error);color:var(--neu-text-inverse);font-size:.625rem;font-weight:700;display:flex;align-items:center;justify-content:center;line-height:1}.neu-nc__panel{position:absolute;top:calc(100% + 8px);right:0;width:320px;max-height:420px;display:flex;flex-direction:column;background:var(--neu-surface-1, #ffffff);border:1px solid var(--neu-border-color, #e5e7eb);border-radius:var(--neu-radius-xl, 16px);box-shadow:0 12px 28px -6px #00000024;z-index:1001;overflow:hidden;animation:neu-nc-in .1s ease}@keyframes neu-nc-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.neu-nc__panel-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--neu-border-color, #e5e7eb);flex-shrink:0}.neu-nc__panel-title{font-size:.9375rem;font-weight:600;color:var(--neu-text-primary, #111)}.neu-nc__panel-actions{display:flex;gap:8px}.neu-nc__action-btn{all:unset;font-size:.75rem;color:var(--neu-color-primary, #0ea5e9);cursor:pointer;padding:2px 6px;border-radius:var(--neu-radius-sm, 4px)}.neu-nc__action-btn:hover{background:var(--neu-color-primary-alpha, rgba(14, 165, 233, .1))}.neu-nc__list{overflow-y:auto;flex:1;padding:6px}.neu-nc__empty{padding:24px;text-align:center;font-size:.875rem;color:var(--neu-text-secondary, #6b7280)}.neu-nc__item{display:flex;align-items:flex-start;gap:10px;padding:10px 10px 10px 12px;border-radius:var(--neu-radius-lg, 12px);position:relative;transition:background .1s}.neu-nc__item:hover{background:var(--neu-surface-2, #f3f4f6)}.neu-nc__item--unread:before{content:\"\";position:absolute;left:4px;top:50%;transform:translateY(-50%);width:6px;height:6px;border-radius:50%;background:var(--neu-color-primary, #0ea5e9)}.neu-nc__item-icon{font-size:1.1rem;flex-shrink:0;padding-top:2px}.neu-nc__item-body{flex:1;min-width:0}.neu-nc__item-title{margin:0 0 2px;font-size:.8125rem;font-weight:600;color:var(--neu-text-primary, #111)}.neu-nc__item-msg{margin:0 0 4px;font-size:.8125rem;color:var(--neu-text-secondary, #4b5563);line-height:1.4}.neu-nc__item-time{font-size:.6875rem;color:var(--neu-text-secondary, #9ca3af)}.neu-nc__item-close{all:unset;opacity:0;cursor:pointer;padding:2px 6px;border-radius:4px;font-size:1rem;color:var(--neu-text-secondary, #9ca3af);flex-shrink:0;transition:opacity .1s}.neu-nc__item:hover .neu-nc__item-close{opacity:1}.neu-nc__item-close:hover{color:var(--neu-text-primary, #111)}\n"] }]
311
+ </ng-template>
312
+ `, styles: ["@charset \"UTF-8\";.neu-nc{position:relative;display:inline-block}.neu-nc__bell{all:unset;position:relative;display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:var(--neu-radius-lg, 12px);cursor:pointer;transition:background .12s}.neu-nc__bell:hover{background:var(--neu-surface-2, #f3f4f6)}.neu-nc__bell:focus-visible{outline:2px solid var(--neu-focus-ring, #0ea5e9);outline-offset:2px}.neu-nc__bell-icon{font-size:1.2rem}.neu-nc__badge{position:absolute;top:4px;right:4px;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:var(--neu-error);color:var(--neu-text-inverse);font-size:.625rem;font-weight:700;display:flex;align-items:center;justify-content:center;line-height:1}.neu-nc__panel{position:relative;width:320px;max-height:420px;display:flex;flex-direction:column;background:var(--neu-surface-1, #ffffff);border:1px solid var(--neu-border-color, #e5e7eb);border-radius:var(--neu-radius-xl, 16px);box-shadow:0 12px 28px -6px #00000024;z-index:1001;overflow:hidden;animation:neu-nc-in .1s ease}@keyframes neu-nc-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.neu-nc__panel-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--neu-border-color, #e5e7eb);flex-shrink:0}.neu-nc__panel-title{font-size:.9375rem;font-weight:600;color:var(--neu-text-primary, #111)}.neu-nc__panel-actions{display:flex;gap:8px}.neu-nc__action-btn{all:unset;font-size:.75rem;color:var(--neu-color-primary, #0ea5e9);cursor:pointer;padding:2px 6px;border-radius:var(--neu-radius-sm, 4px)}.neu-nc__action-btn:hover{background:var(--neu-color-primary-alpha, rgba(14, 165, 233, .1))}.neu-nc__list{overflow-y:auto;flex:1;padding:6px}.neu-nc__empty{padding:24px;text-align:center;font-size:.875rem;color:var(--neu-text-secondary, #6b7280)}.neu-nc__item{display:flex;align-items:flex-start;gap:10px;padding:10px 10px 10px 12px;border-radius:var(--neu-radius-lg, 12px);position:relative;transition:background .1s}.neu-nc__item:hover{background:var(--neu-surface-2, #f3f4f6)}.neu-nc__item--unread:before{content:\"\";position:absolute;left:4px;top:50%;transform:translateY(-50%);width:6px;height:6px;border-radius:50%;background:var(--neu-color-primary, #0ea5e9)}.neu-nc__item-icon{font-size:1.1rem;flex-shrink:0;padding-top:2px}.neu-nc__item-body{flex:1;min-width:0}.neu-nc__item-title{margin:0 0 2px;font-size:.8125rem;font-weight:600;color:var(--neu-text-primary, #111)}.neu-nc__item-msg{margin:0 0 4px;font-size:.8125rem;color:var(--neu-text-secondary, #4b5563);line-height:1.4}.neu-nc__item-time{font-size:.6875rem;color:var(--neu-text-secondary, #9ca3af)}.neu-nc__item-close{all:unset;opacity:0;cursor:pointer;padding:2px 6px;border-radius:4px;font-size:1rem;color:var(--neu-text-secondary, #9ca3af);flex-shrink:0;transition:opacity .1s}.neu-nc__item:hover .neu-nc__item-close{opacity:1}.neu-nc__item-close:hover{color:var(--neu-text-primary, #111)}\n"] }]
257
313
  }] });
258
314
 
259
315
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"neural-ui-core-notification-center.mjs","sources":["../../../../projects/ui-core/notification-center/neu-notification-center.component.ts","../../../../projects/ui-core/notification-center/neural-ui-core-notification-center.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n Injectable,\n OnDestroy,\n ViewEncapsulation,\n computed,\n inject,\n signal,\n} from '@angular/core';\n\nexport type NeuNotificationType = 'info' | 'success' | 'warning' | 'error';\n\nexport interface NeuNotification {\n id: string;\n type: NeuNotificationType;\n title?: string;\n message: string;\n /** Auto-dismiss duration in ms (0 = persistent) */\n duration?: number;\n /** Icon text / emoji */\n icon?: string;\n timestamp: Date;\n read: boolean;\n}\n\nexport interface NeuNotificationOptions extends Omit<\n NeuNotification,\n 'id' | 'timestamp' | 'read'\n> {}\n\nconst DEFAULT_ICONS: Record<NeuNotificationType, string> = {\n info: 'ℹ️',\n success: '✅',\n warning: '⚠️',\n error: '❌',\n};\n\nlet _idSeq = 0;\n\n/**\n * NeuralUI NotificationService\n *\n * Servicio inyectable que gestiona la cola de notificaciones.\n *\n * Uso:\n * inject(NeuNotificationService).push({ type: 'success', message: '¡Guardado!' });\n */\n@Injectable({ providedIn: 'root' })\nexport class NeuNotificationService {\n readonly notifications = signal<NeuNotification[]>([]);\n\n readonly unreadCount = computed(() => this.notifications().filter((n) => !n.read).length);\n\n /** Agrega una notificación / Adds a notification */\n push(opts: Partial<NeuNotificationOptions> & Pick<NeuNotificationOptions, 'message'>): string {\n const id = `neu-notif-${++_idSeq}`;\n const n: NeuNotification = {\n id,\n type: opts.type ?? 'info',\n title: opts.title,\n message: opts.message,\n icon: opts.icon ?? DEFAULT_ICONS[opts.type ?? 'info'],\n duration: opts.duration ?? 5000,\n timestamp: new Date(),\n read: false,\n };\n this.notifications.update((list) => [n, ...list]);\n\n if (n.duration && n.duration > 0) {\n setTimeout(() => this.remove(id), n.duration);\n }\n return id;\n }\n\n /** Elimina una notificación / Removes a notification */\n remove(id: string): void {\n this.notifications.update((list) => list.filter((n) => n.id !== id));\n }\n\n /** Marca todas como leídas / Marks all as read */\n markAllRead(): void {\n this.notifications.update((list) => list.map((n) => ({ ...n, read: true })));\n }\n\n /** Elimina todas / Clears all */\n clearAll(): void {\n this.notifications.set([]);\n }\n}\n\nlet _panelSeq = 0;\n\n/**\n * NeuralUI NotificationCenter Component\n *\n * Icono de campana con badge de no leídos y panel de notificaciones\n * deslizante. Consume NeuNotificationService.\n *\n * Uso:\n * <neu-notification-center />\n */\n@Component({\n selector: 'neu-notification-center',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'neu-nc',\n '[attr.aria-label]': '\"Centro de notificaciones\"',\n },\n template: `\n <!-- Bell button -->\n <button\n type=\"button\"\n class=\"neu-nc__bell\"\n [attr.aria-expanded]=\"_isOpen()\"\n [attr.aria-controls]=\"_panelId\"\n [attr.aria-label]=\"'Notificaciones. ' + _svc.unreadCount() + ' sin leer'\"\n (click)=\"_toggle()\"\n >\n <span class=\"neu-nc__bell-icon\" aria-hidden=\"true\">🔔</span>\n @if (_svc.unreadCount() > 0) {\n <span class=\"neu-nc__badge\" aria-hidden=\"true\">{{\n _svc.unreadCount() > 99 ? '99+' : _svc.unreadCount()\n }}</span>\n }\n </button>\n\n <!-- Panel -->\n @if (_isOpen()) {\n <div\n class=\"neu-nc__panel\"\n [id]=\"_panelId\"\n role=\"dialog\"\n aria-modal=\"false\"\n aria-label=\"Notificaciones\"\n >\n <div class=\"neu-nc__panel-header\">\n <span class=\"neu-nc__panel-title\">Notificaciones</span>\n <div class=\"neu-nc__panel-actions\">\n @if (_svc.unreadCount() > 0) {\n <button type=\"button\" class=\"neu-nc__action-btn\" (click)=\"_svc.markAllRead()\">\n Leer todo\n </button>\n }\n @if (_svc.notifications().length > 0) {\n <button type=\"button\" class=\"neu-nc__action-btn\" (click)=\"_svc.clearAll()\">\n Limpiar\n </button>\n }\n </div>\n </div>\n\n <div class=\"neu-nc__list\" role=\"list\">\n @if (!_svc.notifications().length) {\n <div class=\"neu-nc__empty\" role=\"status\">No hay notificaciones</div>\n }\n @for (n of _svc.notifications(); track n.id) {\n <div\n class=\"neu-nc__item\"\n role=\"listitem\"\n [class.neu-nc__item--unread]=\"!n.read\"\n [class]=\"'neu-nc__item--' + n.type\"\n >\n <span class=\"neu-nc__item-icon\" aria-hidden=\"true\">{{ n.icon }}</span>\n <div class=\"neu-nc__item-body\">\n @if (n.title) {\n <p class=\"neu-nc__item-title\">{{ n.title }}</p>\n }\n <p class=\"neu-nc__item-msg\">{{ n.message }}</p>\n <time class=\"neu-nc__item-time\" [dateTime]=\"n.timestamp.toISOString()\">\n {{ _relativeTime(n.timestamp) }}\n </time>\n </div>\n <button\n type=\"button\"\n class=\"neu-nc__item-close\"\n [attr.aria-label]=\"'Cerrar notificación'\"\n (click)=\"_svc.remove(n.id)\"\n >\n ×\n </button>\n </div>\n }\n </div>\n </div>\n }\n `,\n styleUrl: './neu-notification-center.component.scss',\n})\nexport class NeuNotificationCenterComponent {\n readonly _svc = inject(NeuNotificationService);\n readonly _isOpen = signal(false);\n readonly _panelId = `neu-nc-panel-${++_panelSeq}`;\n\n _toggle(): void {\n const opening = !this._isOpen();\n this._isOpen.set(opening);\n if (opening) {\n // Mark all as read when panel opens\n setTimeout(() => this._svc.markAllRead(), 500);\n }\n }\n\n _relativeTime(date: Date): string {\n const diff = Date.now() - date.getTime();\n const mins = Math.floor(diff / 60000);\n if (mins < 1) return 'Ahora';\n if (mins < 60) return `Hace ${mins} min`;\n const hours = Math.floor(mins / 60);\n if (hours < 24) return `Hace ${hours}h`;\n return `Hace ${Math.floor(hours / 24)}d`;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"neural-ui-core-notification-center.mjs","sources":["../../../../projects/ui-core/notification-center/neu-notification-center.component.ts","../../../../projects/ui-core/notification-center/neural-ui-core-notification-center.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n Injectable,\n OnDestroy,\n ViewEncapsulation,\n computed,\n inject,\n signal,\n} from '@angular/core';\nimport { ConnectedPosition, Overlay, OverlayModule } from '@angular/cdk/overlay';\n\nexport type NeuNotificationType = 'info' | 'success' | 'warning' | 'error';\n\nexport interface NeuNotification {\n id: string;\n type: NeuNotificationType;\n title?: string;\n message: string;\n /** Auto-dismiss duration in ms (0 = persistent) */\n duration?: number;\n /** Icon text / emoji */\n icon?: string;\n timestamp: Date;\n read: boolean;\n}\n\nexport interface NeuNotificationOptions extends Omit<\n NeuNotification,\n 'id' | 'timestamp' | 'read'\n> {}\n\nconst DEFAULT_ICONS: Record<NeuNotificationType, string> = {\n info: 'ℹ️',\n success: '✅',\n warning: '⚠️',\n error: '❌',\n};\n\nlet _idSeq = 0;\n\n/**\n * NeuralUI NotificationService\n *\n * Servicio inyectable que gestiona la cola de notificaciones.\n *\n * Uso:\n * inject(NeuNotificationService).push({ type: 'success', message: '¡Guardado!' });\n */\n@Injectable({ providedIn: 'root' })\nexport class NeuNotificationService {\n readonly notifications = signal<NeuNotification[]>([]);\n\n readonly unreadCount = computed(() => this.notifications().filter((n) => !n.read).length);\n\n /** Agrega una notificación / Adds a notification */\n push(opts: Partial<NeuNotificationOptions> & Pick<NeuNotificationOptions, 'message'>): string {\n const id = `neu-notif-${++_idSeq}`;\n const n: NeuNotification = {\n id,\n type: opts.type ?? 'info',\n title: opts.title,\n message: opts.message,\n icon: opts.icon ?? DEFAULT_ICONS[opts.type ?? 'info'],\n duration: opts.duration ?? 5000,\n timestamp: new Date(),\n read: false,\n };\n this.notifications.update((list) => [n, ...list]);\n\n if (n.duration && n.duration > 0) {\n setTimeout(() => this.remove(id), n.duration);\n }\n return id;\n }\n\n /** Elimina una notificación / Removes a notification */\n remove(id: string): void {\n this.notifications.update((list) => list.filter((n) => n.id !== id));\n }\n\n /** Marca todas como leídas / Marks all as read */\n markAllRead(): void {\n this.notifications.update((list) => list.map((n) => ({ ...n, read: true })));\n }\n\n /** Elimina todas / Clears all */\n clearAll(): void {\n this.notifications.set([]);\n }\n}\n\nlet _panelSeq = 0;\n\n/**\n * NeuralUI NotificationCenter Component\n *\n * Icono de campana con badge de no leídos y panel de notificaciones\n * deslizante. Consume NeuNotificationService.\n *\n * Uso:\n * <neu-notification-center />\n */\n@Component({\n selector: 'neu-notification-center',\n imports: [OverlayModule],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'neu-nc',\n '[attr.aria-label]': '\"Centro de notificaciones\"',\n },\n template: `\n <!-- Bell button -->\n <button\n cdkOverlayOrigin\n #notificationOrigin=\"cdkOverlayOrigin\"\n type=\"button\"\n class=\"neu-nc__bell\"\n [attr.aria-expanded]=\"_isOpen()\"\n [attr.aria-controls]=\"_panelId\"\n [attr.aria-label]=\"'Notificaciones. ' + _svc.unreadCount() + ' sin leer'\"\n (click)=\"_toggle()\"\n >\n <span class=\"neu-nc__bell-icon\" aria-hidden=\"true\">🔔</span>\n @if (_svc.unreadCount() > 0) {\n <span class=\"neu-nc__badge\" aria-hidden=\"true\">{{\n _svc.unreadCount() > 99 ? '99+' : _svc.unreadCount()\n }}</span>\n }\n </button>\n\n <!-- Panel -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"notificationOrigin\"\n [cdkConnectedOverlayOpen]=\"_isOpen()\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayScrollStrategy]=\"overlayScrollStrategy\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n [cdkConnectedOverlayPush]=\"true\"\n [cdkConnectedOverlayViewportMargin]=\"_viewportMargin\"\n (backdropClick)=\"_isOpen.set(false)\"\n (detach)=\"_isOpen.set(false)\"\n >\n <div\n class=\"neu-nc__panel\"\n [id]=\"_panelId\"\n role=\"dialog\"\n aria-modal=\"false\"\n aria-label=\"Notificaciones\"\n >\n <div class=\"neu-nc__panel-header\">\n <span class=\"neu-nc__panel-title\">Notificaciones</span>\n <div class=\"neu-nc__panel-actions\">\n @if (_svc.unreadCount() > 0) {\n <button type=\"button\" class=\"neu-nc__action-btn\" (click)=\"_svc.markAllRead()\">\n Leer todo\n </button>\n }\n @if (_svc.notifications().length > 0) {\n <button type=\"button\" class=\"neu-nc__action-btn\" (click)=\"_svc.clearAll()\">\n Limpiar\n </button>\n }\n </div>\n </div>\n\n <div class=\"neu-nc__list\" role=\"list\">\n @if (!_svc.notifications().length) {\n <div class=\"neu-nc__empty\" role=\"status\">No hay notificaciones</div>\n }\n @for (n of _svc.notifications(); track n.id) {\n <div\n class=\"neu-nc__item\"\n role=\"listitem\"\n [class.neu-nc__item--unread]=\"!n.read\"\n [class]=\"'neu-nc__item--' + n.type\"\n >\n <span class=\"neu-nc__item-icon\" aria-hidden=\"true\">{{ n.icon }}</span>\n <div class=\"neu-nc__item-body\">\n @if (n.title) {\n <p class=\"neu-nc__item-title\">{{ n.title }}</p>\n }\n <p class=\"neu-nc__item-msg\">{{ n.message }}</p>\n <time class=\"neu-nc__item-time\" [dateTime]=\"n.timestamp.toISOString()\">\n {{ _relativeTime(n.timestamp) }}\n </time>\n </div>\n <button\n type=\"button\"\n class=\"neu-nc__item-close\"\n [attr.aria-label]=\"'Cerrar notificación'\"\n (click)=\"_svc.remove(n.id)\"\n >\n ×\n </button>\n </div>\n }\n </div>\n </div>\n </ng-template>\n `,\n styleUrl: './neu-notification-center.component.scss',\n})\nexport class NeuNotificationCenterComponent {\n readonly _svc = inject(NeuNotificationService);\n private readonly overlay = inject(Overlay);\n readonly _isOpen = signal(false);\n readonly _panelId = `neu-nc-panel-${++_panelSeq}`;\n readonly _viewportMargin = 16;\n readonly overlayPositions: ConnectedPosition[] = [\n {\n originX: 'end',\n originY: 'bottom',\n overlayX: 'end',\n overlayY: 'top',\n offsetY: 8,\n },\n {\n originX: 'end',\n originY: 'top',\n overlayX: 'end',\n overlayY: 'bottom',\n offsetY: -8,\n },\n {\n originX: 'start',\n originY: 'bottom',\n overlayX: 'start',\n overlayY: 'top',\n offsetY: 8,\n },\n ];\n readonly overlayScrollStrategy = this.overlay.scrollStrategies.reposition();\n\n _toggle(): void {\n const opening = !this._isOpen();\n this._isOpen.set(opening);\n if (opening) {\n // Mark all as read when panel opens\n setTimeout(() => this._svc.markAllRead(), 500);\n }\n }\n\n _relativeTime(date: Date): string {\n const diff = Date.now() - date.getTime();\n const mins = Math.floor(diff / 60000);\n if (mins < 1) return 'Ahora';\n if (mins < 60) return `Hace ${mins} min`;\n const hours = Math.floor(mins / 60);\n if (hours < 24) return `Hace ${hours}h`;\n return `Hace ${Math.floor(hours / 24)}d`;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, TemplateRef, Directive, ElementRef, viewChild, effect, untracked, contentChild, input, output, signal, computed, forwardRef, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
3
  import { NeuUrlStateService } from '@neural-ui/core/url-state';
4
- import { NgTemplateOutlet } from '@angular/common';
5
- import * as i1 from '@angular/cdk/scrolling';
4
+ import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
5
+ import * as i1 from '@angular/cdk/overlay';
6
+ import { Overlay, OverlayModule } from '@angular/cdk/overlay';
6
7
  import { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling';
7
8
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
8
9
 
@@ -68,8 +69,9 @@ let _neuSelectIdSeq = 0;
68
69
  */
69
70
  class NeuSelectComponent {
70
71
  elementRef = inject(ElementRef);
72
+ _document = inject(DOCUMENT);
73
+ _overlay = inject(Overlay);
71
74
  _urlState = inject(NeuUrlStateService);
72
- _mobileViewportMax = 768;
73
75
  _viewportMargin = 16;
74
76
  _panelMaxHeight = 240;
75
77
  _urlParamSignals = new Map();
@@ -149,6 +151,37 @@ class NeuSelectComponent {
149
151
  _value = signal(null, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
150
152
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
151
153
  searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : /* istanbul ignore next */ []));
154
+ overlayPositions = [
155
+ {
156
+ originX: 'start',
157
+ originY: 'bottom',
158
+ overlayX: 'start',
159
+ overlayY: 'top',
160
+ offsetY: 6,
161
+ },
162
+ {
163
+ originX: 'start',
164
+ originY: 'top',
165
+ overlayX: 'start',
166
+ overlayY: 'bottom',
167
+ offsetY: -6,
168
+ },
169
+ {
170
+ originX: 'end',
171
+ originY: 'bottom',
172
+ overlayX: 'end',
173
+ overlayY: 'top',
174
+ offsetY: 6,
175
+ },
176
+ {
177
+ originX: 'end',
178
+ originY: 'top',
179
+ overlayX: 'end',
180
+ overlayY: 'bottom',
181
+ offsetY: -6,
182
+ },
183
+ ];
184
+ overlayScrollStrategy = this._overlay.scrollStrategies.reposition();
152
185
  panelPosition = signal({
153
186
  position: null,
154
187
  top: null,
@@ -302,7 +335,10 @@ class NeuSelectComponent {
302
335
  this.close();
303
336
  }
304
337
  onDocumentClick(event) {
305
- if (!this.elementRef.nativeElement.contains(event.target)) {
338
+ const target = event.target;
339
+ const isInsideHost = this.elementRef.nativeElement.contains(target);
340
+ const isInsidePanel = !!target?.closest('.neu-select__panel');
341
+ if (!isInsideHost && !isInsidePanel) {
306
342
  this.close();
307
343
  }
308
344
  }
@@ -324,28 +360,16 @@ class NeuSelectComponent {
324
360
  const viewportWidth = window.innerWidth;
325
361
  const viewportHeight = window.innerHeight;
326
362
  const gap = 6;
327
- const configuratorControls = trigger.closest('.demo-configurator__controls');
328
- const controlsDisplay = configuratorControls
329
- ? window.getComputedStyle(configuratorControls).display
330
- : null;
331
- const boundaryRect = controlsDisplay === 'grid' ? null : configuratorControls?.getBoundingClientRect();
332
- const boundaryTop = boundaryRect
333
- ? Math.max(boundaryRect.top, this._viewportMargin)
334
- : this._viewportMargin;
335
- const boundaryBottom = boundaryRect
336
- ? Math.min(boundaryRect.bottom, viewportHeight - this._viewportMargin)
337
- : viewportHeight - this._viewportMargin;
338
363
  const width = Math.min(triggerRect.width, viewportWidth - this._viewportMargin * 2);
339
- const left = Math.min(Math.max(triggerRect.left, this._viewportMargin), viewportWidth - this._viewportMargin - width);
340
- const availableBelow = Math.max(0, boundaryBottom - triggerRect.bottom - gap);
341
- const availableAbove = Math.max(0, triggerRect.top - boundaryTop - gap);
364
+ const availableBelow = Math.max(0, viewportHeight - this._viewportMargin - triggerRect.bottom - gap);
365
+ const availableAbove = Math.max(0, triggerRect.top - this._viewportMargin - gap);
342
366
  const openAbove = availableAbove > availableBelow && availableAbove >= 140;
343
367
  const maxHeight = Math.max(140, openAbove ? availableAbove : availableBelow);
344
368
  this.panelPosition.set({
345
369
  position: 'fixed',
346
370
  top: openAbove ? 'auto' : `${triggerRect.bottom + gap}px`,
347
371
  bottom: openAbove ? `${viewportHeight - triggerRect.top + gap}px` : 'auto',
348
- left: `${left}px`,
372
+ left: `${Math.min(Math.max(triggerRect.left, this._viewportMargin), viewportWidth - width - this._viewportMargin)}px`,
349
373
  width: `${width}px`,
350
374
  maxHeight: `${maxHeight}px`,
351
375
  });
@@ -354,6 +378,9 @@ class NeuSelectComponent {
354
378
  requestAnimationFrame(() => this._viewport()?.checkViewportSize());
355
379
  }
356
380
  }
381
+ onOverlayPositionChange(event) {
382
+ this.isPanelAbove.set(event.connectionPair.overlayY === 'bottom');
383
+ }
357
384
  focusFirstOption() {
358
385
  const firstEnabled = this.filteredOptions().find((option) => !option.disabled);
359
386
  if (!firstEnabled) {
@@ -369,12 +396,13 @@ class NeuSelectComponent {
369
396
  this._viewport()?.checkViewportSize();
370
397
  }
371
398
  requestAnimationFrame(() => {
372
- const optionElement = this.elementRef.nativeElement.querySelector(`#neu-select-opt-${value}`);
399
+ const optionElement = this.elementRef.nativeElement.querySelector(`#neu-select-opt-${value}`) ?? this._document.getElementById(`neu-select-opt-${value}`);
373
400
  optionElement?.focus();
374
401
  });
375
402
  return;
376
403
  }
377
- const optionElement = this.elementRef.nativeElement.querySelector(`#neu-select-opt-${value}`);
404
+ const optionElement = this.elementRef.nativeElement.querySelector(`#neu-select-opt-${value}`) ??
405
+ this._document.getElementById(`neu-select-opt-${value}`);
378
406
  optionElement?.focus();
379
407
  }
380
408
  resetPanelPosition() {
@@ -389,7 +417,7 @@ class NeuSelectComponent {
389
417
  this.isPanelAbove.set(false);
390
418
  }
391
419
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
392
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuSelectComponent, isStandalone: true, selector: "neu-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, floatingLabel: { classPropertyName: "floatingLabel", publicName: "floatingLabel", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollVisibleItems: { classPropertyName: "virtualScrollVisibleItems", publicName: "virtualScrollVisibleItems", isSignal: true, isRequired: false, transformFunction: null }, noResultsMessage: { classPropertyName: "noResultsMessage", publicName: "noResultsMessage", isSignal: true, isRequired: false, transformFunction: null }, clearAriaLabel: { classPropertyName: "clearAriaLabel", publicName: "clearAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, urlParam: { classPropertyName: "urlParam", publicName: "urlParam", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "keydown.escape": "close()", "window:resize": "onWindowResize()", "window:scroll": "onWindowScroll()" } }, providers: [
420
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuSelectComponent, isStandalone: true, selector: "neu-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, floatingLabel: { classPropertyName: "floatingLabel", publicName: "floatingLabel", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollVisibleItems: { classPropertyName: "virtualScrollVisibleItems", publicName: "virtualScrollVisibleItems", isSignal: true, isRequired: false, transformFunction: null }, noResultsMessage: { classPropertyName: "noResultsMessage", publicName: "noResultsMessage", isSignal: true, isRequired: false, transformFunction: null }, clearAriaLabel: { classPropertyName: "clearAriaLabel", publicName: "clearAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, urlParam: { classPropertyName: "urlParam", publicName: "urlParam", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, host: { listeners: { "keydown.escape": "close()", "window:resize": "onWindowResize()" } }, providers: [
393
421
  {
394
422
  provide: NG_VALUE_ACCESSOR,
395
423
  useExisting: forwardRef(() => NeuSelectComponent),
@@ -416,6 +444,8 @@ class NeuSelectComponent {
416
444
  >
417
445
  <!-- Trigger ------>
418
446
  <div
447
+ cdkOverlayOrigin
448
+ #selectOrigin="cdkOverlayOrigin"
419
449
  class="neu-select__trigger"
420
450
  [id]="_triggerId"
421
451
  [attr.tabindex]="isDisabledFinal() ? '-1' : '0'"
@@ -493,7 +523,20 @@ class NeuSelectComponent {
493
523
  </div>
494
524
 
495
525
  <!-- Panel ------>
496
- @if (isOpen()) {
526
+ <ng-template
527
+ cdkConnectedOverlay
528
+ [cdkConnectedOverlayOrigin]="selectOrigin"
529
+ [cdkConnectedOverlayOpen]="isOpen()"
530
+ [cdkConnectedOverlayPositions]="overlayPositions"
531
+ [cdkConnectedOverlayScrollStrategy]="overlayScrollStrategy"
532
+ [cdkConnectedOverlayHasBackdrop]="true"
533
+ [cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
534
+ [cdkConnectedOverlayPush]="true"
535
+ [cdkConnectedOverlayViewportMargin]="_viewportMargin"
536
+ (backdropClick)="close()"
537
+ (detach)="close()"
538
+ (positionChange)="onOverlayPositionChange($event)"
539
+ >
497
540
  <div
498
541
  class="neu-select__panel"
499
542
  [class.neu-select__panel--above]="isPanelAbove()"
@@ -501,10 +544,6 @@ class NeuSelectComponent {
501
544
  role="listbox"
502
545
  [id]="_panelId"
503
546
  [attr.aria-label]="label()"
504
- [style.position]="panelPosition().position"
505
- [style.top]="panelPosition().top"
506
- [style.bottom]="panelPosition().bottom"
507
- [style.left]="panelPosition().left"
508
547
  [style.width]="panelPosition().width"
509
548
  [style.max-height]="panelPosition().maxHeight"
510
549
  >
@@ -613,7 +652,7 @@ class NeuSelectComponent {
613
652
  <div class="neu-select__empty">{{ noResultsMessage() }}</div>
614
653
  }
615
654
  </div>
616
- }
655
+ </ng-template>
617
656
  <div class="neu-select__sr-status" aria-live="polite" aria-atomic="true">
618
657
  {{ resultsAnnouncement() }}
619
658
  </div>
@@ -627,21 +666,19 @@ class NeuSelectComponent {
627
666
  } @else if (hint()) {
628
667
  <p class="neu-select__hint" [id]="_triggerId + '-hint'">{{ hint() }}</p>
629
668
  }
630
- `, isInline: true, styles: [".neu-select{position:relative;display:block}.neu-select--disabled{opacity:.6;pointer-events:none}.neu-select--sm .neu-select__trigger{height:36px;font-size:var(--neu-text-sm)}.neu-select--lg .neu-select__trigger{height:56px}.neu-select__trigger{display:flex;align-items:center;width:100%;height:48px;padding:0 var(--neu-space-3);padding-right:36px;background:var(--neu-surface);border:1.5px solid var(--neu-border);border-radius:var(--neu-radius);font-family:var(--neu-font-sans);font-size:var(--neu-text-base);color:var(--neu-text);cursor:pointer;text-align:left;outline:none;transition:border-color var(--neu-transition),box-shadow var(--neu-transition);position:relative}.neu-select__trigger:hover:not([aria-disabled=true]){border-color:var(--neu-border-hover)}.neu-select__trigger[aria-disabled=true]{cursor:not-allowed;background:var(--neu-surface-2)}.neu-select__trigger:focus-visible{border-color:var(--neu-primary);box-shadow:0 0 0 3px #007aff1f}.neu-select--open .neu-select__trigger{border-color:var(--neu-primary);box-shadow:0 0 0 3px #007aff1f;border-bottom-left-radius:0;border-bottom-right-radius:0}.neu-select--open-above .neu-select__trigger{border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:var(--neu-radius);border-bottom-right-radius:var(--neu-radius)}.neu-select--error .neu-select__trigger{border-color:var(--neu-error)}.neu-select--error .neu-select__trigger:focus-visible{box-shadow:0 0 0 3px #ef44441f}.neu-select__label{position:absolute;left:var(--neu-space-3);top:50%;transform:translateY(-50%);font-size:var(--neu-text-base);color:color-mix(in srgb,var(--neu-text) 68%,var(--neu-surface) 32%);pointer-events:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - var(--neu-space-6));transition:top var(--neu-transition),font-size var(--neu-transition),color var(--neu-transition),transform var(--neu-transition),padding var(--neu-transition),background var(--neu-transition)}.neu-select--open .neu-select__label,.neu-select--has-value .neu-select__label{top:0;transform:translateY(-50%);font-size:12px;font-weight:600;letter-spacing:.01em;background:var(--neu-surface);padding:0 4px;left:calc(var(--neu-space-3) - 4px)}.neu-select--open .neu-select__label{color:var(--neu-primary)}.neu-select--error .neu-select__label{color:var(--neu-error)}.neu-select--disabled .neu-select__label{background:var(--neu-surface-2)}.neu-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.neu-select--no-float .neu-select__value{padding-top:0}.neu-select__placeholder{color:color-mix(in srgb,var(--neu-text) 62%,var(--neu-surface) 38%)}.neu-select:not(.neu-select--no-float):not(.neu-select--open) .neu-select__placeholder{visibility:hidden}.neu-select__chevron{position:absolute;right:var(--neu-space-3);top:50%;transform:translateY(-50%);width:18px;height:18px;color:var(--neu-text-muted);flex-shrink:0;transition:transform var(--neu-transition)}.neu-select--open .neu-select__chevron{transform:translateY(-50%) rotate(180deg);color:var(--neu-primary)}.neu-select__clear{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-right:2px;padding:0;border:none;background:transparent;color:var(--neu-text-muted);cursor:pointer;border-radius:var(--neu-radius-sm);flex-shrink:0;transition:color var(--neu-transition),background var(--neu-transition)}.neu-select__clear svg{width:14px;height:14px}.neu-select__clear:hover{color:var(--neu-text);background:var(--neu-surface-3)}.neu-select__clear:focus-visible{outline:2px solid var(--neu-primary);outline-offset:2px}.neu-select__panel{position:absolute;top:100%;left:0;right:0;z-index:var(--neu-z-dropdown);background:var(--neu-surface);border:1.5px solid var(--neu-primary);border-top:none;border-bottom-left-radius:var(--neu-radius);border-bottom-right-radius:var(--neu-radius);box-shadow:var(--neu-shadow-lg);max-height:240px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--neu-surface-3) transparent;animation:neu-select-open .15s ease forwards}.neu-select__panel::-webkit-scrollbar{width:4px}.neu-select__panel::-webkit-scrollbar-thumb{background:var(--neu-surface-3);border-radius:99px}.neu-select__panel--above{border-top:1.5px solid var(--neu-primary);border-bottom:none;border-top-left-radius:var(--neu-radius);border-top-right-radius:var(--neu-radius);border-bottom-left-radius:0;border-bottom-right-radius:0}.neu-select__panel--virtual{overflow:visible}.neu-select__viewport{width:100%}@media(max-width:600px){.neu-select__panel{left:auto;right:0;width:min(max(100%,220px),100vw - 2rem);max-width:calc(100vw - 2rem)}}@keyframes neu-select-open{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.neu-select__option{display:flex;align-items:center;gap:var(--neu-space-2);padding:var(--neu-space-3) var(--neu-space-4);min-height:var(--neu-select-option-height, 44px);box-sizing:border-box;font-size:var(--neu-text-sm);color:var(--neu-text);cursor:pointer;transition:background-color var(--neu-transition)}.neu-select__option:hover:not(.neu-select__option--disabled){background:var(--neu-primary-50);color:var(--neu-primary)}.neu-select__option--selected{color:var(--neu-primary);font-weight:600;background:var(--neu-primary-50)}.neu-select__option--disabled{opacity:.4;cursor:not-allowed}.neu-select__check{width:14px;height:14px;flex-shrink:0}.neu-select__error{margin-top:var(--neu-space-1);font-size:var(--neu-text-xs);color:var(--neu-error-text);font-family:var(--neu-font-sans)}.neu-select__hint{margin-top:var(--neu-space-1);font-size:var(--neu-text-xs);color:var(--neu-text-muted);font-family:var(--neu-font-sans)}.neu-select__sr-status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.neu-select__static-label{display:block;font-size:var(--neu-text-sm);font-weight:500;color:var(--neu-text-muted);margin-bottom:var(--neu-space-2)}.neu-select__search{padding:var(--neu-space-2);border-bottom:1px solid var(--neu-border);position:sticky;top:0;background:var(--neu-surface);z-index:1}.neu-select__search-input{width:100%;height:34px;padding:0 var(--neu-space-3);border:1.5px solid var(--neu-border);border-radius:var(--neu-radius-sm);background:var(--neu-bg);font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);color:var(--neu-text);outline:none;transition:border-color var(--neu-transition),box-shadow var(--neu-transition)}.neu-select__search-input:focus{border-color:var(--neu-primary);box-shadow:0 0 0 2px #007aff1f}.neu-select__search-input::placeholder{color:var(--neu-text-disabled)}.neu-select__empty{padding:var(--neu-space-4);text-align:center;font-size:var(--neu-text-sm);color:var(--neu-text-disabled);font-family:var(--neu-font-sans)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
669
+ `, isInline: true, styles: [".neu-select{position:relative;display:block}.neu-select--disabled{opacity:.6;pointer-events:none}.neu-select--sm .neu-select__trigger{height:36px;font-size:var(--neu-text-sm)}.neu-select--lg .neu-select__trigger{height:56px}.neu-select__trigger{display:flex;align-items:center;width:100%;height:48px;padding:0 var(--neu-space-3);padding-right:36px;background:var(--neu-surface);border:1.5px solid var(--neu-border);border-radius:var(--neu-radius);font-family:var(--neu-font-sans);font-size:var(--neu-text-base);color:var(--neu-text);cursor:pointer;text-align:left;outline:none;transition:border-color var(--neu-transition),box-shadow var(--neu-transition);position:relative}.neu-select__trigger:hover:not([aria-disabled=true]){border-color:var(--neu-border-hover)}.neu-select__trigger[aria-disabled=true]{cursor:not-allowed;background:var(--neu-surface-2)}.neu-select__trigger:focus-visible{border-color:var(--neu-primary);box-shadow:0 0 0 3px #007aff1f}.neu-select--open .neu-select__trigger{border-color:var(--neu-primary);box-shadow:0 0 0 3px #007aff1f;border-bottom-left-radius:0;border-bottom-right-radius:0}.neu-select--open-above .neu-select__trigger{border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:var(--neu-radius);border-bottom-right-radius:var(--neu-radius)}.neu-select--error .neu-select__trigger{border-color:var(--neu-error)}.neu-select--error .neu-select__trigger:focus-visible{box-shadow:0 0 0 3px #ef44441f}.neu-select__label{position:absolute;left:var(--neu-space-3);top:50%;transform:translateY(-50%);font-size:var(--neu-text-base);color:color-mix(in srgb,var(--neu-text) 68%,var(--neu-surface) 32%);pointer-events:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - var(--neu-space-6));transition:top var(--neu-transition),font-size var(--neu-transition),color var(--neu-transition),transform var(--neu-transition),padding var(--neu-transition),background var(--neu-transition)}.neu-select--open .neu-select__label,.neu-select--has-value .neu-select__label{top:0;transform:translateY(-50%);font-size:12px;font-weight:600;letter-spacing:.01em;background:var(--neu-surface);padding:0 4px;left:calc(var(--neu-space-3) - 4px)}.neu-select--open .neu-select__label{color:var(--neu-primary)}.neu-select--error .neu-select__label{color:var(--neu-error)}.neu-select--disabled .neu-select__label{background:var(--neu-surface-2)}.neu-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.neu-select--no-float .neu-select__value{padding-top:0}.neu-select__placeholder{color:color-mix(in srgb,var(--neu-text) 62%,var(--neu-surface) 38%)}.neu-select:not(.neu-select--no-float):not(.neu-select--open) .neu-select__placeholder{visibility:hidden}.neu-select__chevron{position:absolute;right:var(--neu-space-3);top:50%;transform:translateY(-50%);width:18px;height:18px;color:var(--neu-text-muted);flex-shrink:0;transition:transform var(--neu-transition)}.neu-select--open .neu-select__chevron{transform:translateY(-50%) rotate(180deg);color:var(--neu-primary)}.neu-select__clear{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-right:2px;padding:0;border:none;background:transparent;color:var(--neu-text-muted);cursor:pointer;border-radius:var(--neu-radius-sm);flex-shrink:0;transition:color var(--neu-transition),background var(--neu-transition)}.neu-select__clear svg{width:14px;height:14px}.neu-select__clear:hover{color:var(--neu-text);background:var(--neu-surface-3)}.neu-select__clear:focus-visible{outline:2px solid var(--neu-primary);outline-offset:2px}.neu-select__panel{position:relative;z-index:var(--neu-z-dropdown);background:var(--neu-surface);border:1.5px solid var(--neu-primary);border-top:none;border-bottom-left-radius:var(--neu-radius);border-bottom-right-radius:var(--neu-radius);box-shadow:var(--neu-shadow-lg);max-height:240px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--neu-surface-3) transparent;animation:neu-select-open .15s ease forwards}.neu-select__panel::-webkit-scrollbar{width:4px}.neu-select__panel::-webkit-scrollbar-thumb{background:var(--neu-surface-3);border-radius:99px}.neu-select__panel--above{border-top:1.5px solid var(--neu-primary);border-bottom:none;border-top-left-radius:var(--neu-radius);border-top-right-radius:var(--neu-radius);border-bottom-left-radius:0;border-bottom-right-radius:0}.neu-select__panel--virtual{overflow:visible}.neu-select__viewport{width:100%}@media(max-width:600px){.neu-select__panel{left:auto;right:0;width:min(max(100%,220px),100vw - 2rem);max-width:calc(100vw - 2rem)}}@keyframes neu-select-open{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.neu-select__option{display:flex;align-items:center;gap:var(--neu-space-2);padding:var(--neu-space-3) var(--neu-space-4);min-height:var(--neu-select-option-height, 44px);box-sizing:border-box;font-size:var(--neu-text-sm);color:var(--neu-text);cursor:pointer;transition:background-color var(--neu-transition)}.neu-select__option:hover:not(.neu-select__option--disabled){background:var(--neu-primary-50);color:var(--neu-primary)}.neu-select__option--selected{color:var(--neu-primary);font-weight:600;background:var(--neu-primary-50)}.neu-select__option--disabled{opacity:.4;cursor:not-allowed}.neu-select__check{width:14px;height:14px;flex-shrink:0}.neu-select__error{margin-top:var(--neu-space-1);font-size:var(--neu-text-xs);color:var(--neu-error-text);font-family:var(--neu-font-sans)}.neu-select__hint{margin-top:var(--neu-space-1);font-size:var(--neu-text-xs);color:var(--neu-text-muted);font-family:var(--neu-font-sans)}.neu-select__sr-status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.neu-select__static-label{display:block;font-size:var(--neu-text-sm);font-weight:500;color:var(--neu-text-muted);margin-bottom:var(--neu-space-2)}.neu-select__search{padding:var(--neu-space-2);border-bottom:1px solid var(--neu-border);position:sticky;top:0;background:var(--neu-surface);z-index:1}.neu-select__search-input{width:100%;height:34px;padding:0 var(--neu-space-3);border:1.5px solid var(--neu-border);border-radius:var(--neu-radius-sm);background:var(--neu-bg);font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);color:var(--neu-text);outline:none;transition:border-color var(--neu-transition),box-shadow var(--neu-transition)}.neu-select__search-input:focus{border-color:var(--neu-primary);box-shadow:0 0 0 2px #007aff1f}.neu-select__search-input::placeholder{color:var(--neu-text-disabled)}.neu-select__empty{padding:var(--neu-space-4);text-align:center;font-size:var(--neu-text-sm);color:var(--neu-text-disabled);font-family:var(--neu-font-sans)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: i1.ɵɵCdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1.ɵɵCdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1.ɵɵCdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "ngmodule", type: ScrollingModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
631
670
  }
632
671
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuSelectComponent, decorators: [{
633
672
  type: Component,
634
- args: [{ selector: 'neu-select', imports: [NgTemplateOutlet, ScrollingModule], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
673
+ args: [{ selector: 'neu-select', imports: [NgTemplateOutlet, OverlayModule, ScrollingModule], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
635
674
  {
636
675
  provide: NG_VALUE_ACCESSOR,
637
676
  useExisting: forwardRef(() => NeuSelectComponent),
638
677
  multi: true,
639
678
  },
640
679
  ], host: {
641
- '(document:click)': 'onDocumentClick($event)',
642
680
  '(keydown.escape)': 'close()',
643
681
  '(window:resize)': 'onWindowResize()',
644
- '(window:scroll)': 'onWindowScroll()',
645
682
  }, template: `
646
683
  @if (!floatingLabel() && label()) {
647
684
  <label class="neu-select__static-label" [for]="_triggerId" (click)="focusTrigger()">{{
@@ -663,6 +700,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
663
700
  >
664
701
  <!-- Trigger ------>
665
702
  <div
703
+ cdkOverlayOrigin
704
+ #selectOrigin="cdkOverlayOrigin"
666
705
  class="neu-select__trigger"
667
706
  [id]="_triggerId"
668
707
  [attr.tabindex]="isDisabledFinal() ? '-1' : '0'"
@@ -740,7 +779,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
740
779
  </div>
741
780
 
742
781
  <!-- Panel ------>
743
- @if (isOpen()) {
782
+ <ng-template
783
+ cdkConnectedOverlay
784
+ [cdkConnectedOverlayOrigin]="selectOrigin"
785
+ [cdkConnectedOverlayOpen]="isOpen()"
786
+ [cdkConnectedOverlayPositions]="overlayPositions"
787
+ [cdkConnectedOverlayScrollStrategy]="overlayScrollStrategy"
788
+ [cdkConnectedOverlayHasBackdrop]="true"
789
+ [cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
790
+ [cdkConnectedOverlayPush]="true"
791
+ [cdkConnectedOverlayViewportMargin]="_viewportMargin"
792
+ (backdropClick)="close()"
793
+ (detach)="close()"
794
+ (positionChange)="onOverlayPositionChange($event)"
795
+ >
744
796
  <div
745
797
  class="neu-select__panel"
746
798
  [class.neu-select__panel--above]="isPanelAbove()"
@@ -748,10 +800,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
748
800
  role="listbox"
749
801
  [id]="_panelId"
750
802
  [attr.aria-label]="label()"
751
- [style.position]="panelPosition().position"
752
- [style.top]="panelPosition().top"
753
- [style.bottom]="panelPosition().bottom"
754
- [style.left]="panelPosition().left"
755
803
  [style.width]="panelPosition().width"
756
804
  [style.max-height]="panelPosition().maxHeight"
757
805
  >
@@ -860,7 +908,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
860
908
  <div class="neu-select__empty">{{ noResultsMessage() }}</div>
861
909
  }
862
910
  </div>
863
- }
911
+ </ng-template>
864
912
  <div class="neu-select__sr-status" aria-live="polite" aria-atomic="true">
865
913
  {{ resultsAnnouncement() }}
866
914
  </div>
@@ -874,7 +922,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
874
922
  } @else if (hint()) {
875
923
  <p class="neu-select__hint" [id]="_triggerId + '-hint'">{{ hint() }}</p>
876
924
  }
877
- `, styles: [".neu-select{position:relative;display:block}.neu-select--disabled{opacity:.6;pointer-events:none}.neu-select--sm .neu-select__trigger{height:36px;font-size:var(--neu-text-sm)}.neu-select--lg .neu-select__trigger{height:56px}.neu-select__trigger{display:flex;align-items:center;width:100%;height:48px;padding:0 var(--neu-space-3);padding-right:36px;background:var(--neu-surface);border:1.5px solid var(--neu-border);border-radius:var(--neu-radius);font-family:var(--neu-font-sans);font-size:var(--neu-text-base);color:var(--neu-text);cursor:pointer;text-align:left;outline:none;transition:border-color var(--neu-transition),box-shadow var(--neu-transition);position:relative}.neu-select__trigger:hover:not([aria-disabled=true]){border-color:var(--neu-border-hover)}.neu-select__trigger[aria-disabled=true]{cursor:not-allowed;background:var(--neu-surface-2)}.neu-select__trigger:focus-visible{border-color:var(--neu-primary);box-shadow:0 0 0 3px #007aff1f}.neu-select--open .neu-select__trigger{border-color:var(--neu-primary);box-shadow:0 0 0 3px #007aff1f;border-bottom-left-radius:0;border-bottom-right-radius:0}.neu-select--open-above .neu-select__trigger{border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:var(--neu-radius);border-bottom-right-radius:var(--neu-radius)}.neu-select--error .neu-select__trigger{border-color:var(--neu-error)}.neu-select--error .neu-select__trigger:focus-visible{box-shadow:0 0 0 3px #ef44441f}.neu-select__label{position:absolute;left:var(--neu-space-3);top:50%;transform:translateY(-50%);font-size:var(--neu-text-base);color:color-mix(in srgb,var(--neu-text) 68%,var(--neu-surface) 32%);pointer-events:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - var(--neu-space-6));transition:top var(--neu-transition),font-size var(--neu-transition),color var(--neu-transition),transform var(--neu-transition),padding var(--neu-transition),background var(--neu-transition)}.neu-select--open .neu-select__label,.neu-select--has-value .neu-select__label{top:0;transform:translateY(-50%);font-size:12px;font-weight:600;letter-spacing:.01em;background:var(--neu-surface);padding:0 4px;left:calc(var(--neu-space-3) - 4px)}.neu-select--open .neu-select__label{color:var(--neu-primary)}.neu-select--error .neu-select__label{color:var(--neu-error)}.neu-select--disabled .neu-select__label{background:var(--neu-surface-2)}.neu-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.neu-select--no-float .neu-select__value{padding-top:0}.neu-select__placeholder{color:color-mix(in srgb,var(--neu-text) 62%,var(--neu-surface) 38%)}.neu-select:not(.neu-select--no-float):not(.neu-select--open) .neu-select__placeholder{visibility:hidden}.neu-select__chevron{position:absolute;right:var(--neu-space-3);top:50%;transform:translateY(-50%);width:18px;height:18px;color:var(--neu-text-muted);flex-shrink:0;transition:transform var(--neu-transition)}.neu-select--open .neu-select__chevron{transform:translateY(-50%) rotate(180deg);color:var(--neu-primary)}.neu-select__clear{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-right:2px;padding:0;border:none;background:transparent;color:var(--neu-text-muted);cursor:pointer;border-radius:var(--neu-radius-sm);flex-shrink:0;transition:color var(--neu-transition),background var(--neu-transition)}.neu-select__clear svg{width:14px;height:14px}.neu-select__clear:hover{color:var(--neu-text);background:var(--neu-surface-3)}.neu-select__clear:focus-visible{outline:2px solid var(--neu-primary);outline-offset:2px}.neu-select__panel{position:absolute;top:100%;left:0;right:0;z-index:var(--neu-z-dropdown);background:var(--neu-surface);border:1.5px solid var(--neu-primary);border-top:none;border-bottom-left-radius:var(--neu-radius);border-bottom-right-radius:var(--neu-radius);box-shadow:var(--neu-shadow-lg);max-height:240px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--neu-surface-3) transparent;animation:neu-select-open .15s ease forwards}.neu-select__panel::-webkit-scrollbar{width:4px}.neu-select__panel::-webkit-scrollbar-thumb{background:var(--neu-surface-3);border-radius:99px}.neu-select__panel--above{border-top:1.5px solid var(--neu-primary);border-bottom:none;border-top-left-radius:var(--neu-radius);border-top-right-radius:var(--neu-radius);border-bottom-left-radius:0;border-bottom-right-radius:0}.neu-select__panel--virtual{overflow:visible}.neu-select__viewport{width:100%}@media(max-width:600px){.neu-select__panel{left:auto;right:0;width:min(max(100%,220px),100vw - 2rem);max-width:calc(100vw - 2rem)}}@keyframes neu-select-open{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.neu-select__option{display:flex;align-items:center;gap:var(--neu-space-2);padding:var(--neu-space-3) var(--neu-space-4);min-height:var(--neu-select-option-height, 44px);box-sizing:border-box;font-size:var(--neu-text-sm);color:var(--neu-text);cursor:pointer;transition:background-color var(--neu-transition)}.neu-select__option:hover:not(.neu-select__option--disabled){background:var(--neu-primary-50);color:var(--neu-primary)}.neu-select__option--selected{color:var(--neu-primary);font-weight:600;background:var(--neu-primary-50)}.neu-select__option--disabled{opacity:.4;cursor:not-allowed}.neu-select__check{width:14px;height:14px;flex-shrink:0}.neu-select__error{margin-top:var(--neu-space-1);font-size:var(--neu-text-xs);color:var(--neu-error-text);font-family:var(--neu-font-sans)}.neu-select__hint{margin-top:var(--neu-space-1);font-size:var(--neu-text-xs);color:var(--neu-text-muted);font-family:var(--neu-font-sans)}.neu-select__sr-status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.neu-select__static-label{display:block;font-size:var(--neu-text-sm);font-weight:500;color:var(--neu-text-muted);margin-bottom:var(--neu-space-2)}.neu-select__search{padding:var(--neu-space-2);border-bottom:1px solid var(--neu-border);position:sticky;top:0;background:var(--neu-surface);z-index:1}.neu-select__search-input{width:100%;height:34px;padding:0 var(--neu-space-3);border:1.5px solid var(--neu-border);border-radius:var(--neu-radius-sm);background:var(--neu-bg);font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);color:var(--neu-text);outline:none;transition:border-color var(--neu-transition),box-shadow var(--neu-transition)}.neu-select__search-input:focus{border-color:var(--neu-primary);box-shadow:0 0 0 2px #007aff1f}.neu-select__search-input::placeholder{color:var(--neu-text-disabled)}.neu-select__empty{padding:var(--neu-space-4);text-align:center;font-size:var(--neu-text-sm);color:var(--neu-text-disabled);font-family:var(--neu-font-sans)}\n"] }]
925
+ `, styles: [".neu-select{position:relative;display:block}.neu-select--disabled{opacity:.6;pointer-events:none}.neu-select--sm .neu-select__trigger{height:36px;font-size:var(--neu-text-sm)}.neu-select--lg .neu-select__trigger{height:56px}.neu-select__trigger{display:flex;align-items:center;width:100%;height:48px;padding:0 var(--neu-space-3);padding-right:36px;background:var(--neu-surface);border:1.5px solid var(--neu-border);border-radius:var(--neu-radius);font-family:var(--neu-font-sans);font-size:var(--neu-text-base);color:var(--neu-text);cursor:pointer;text-align:left;outline:none;transition:border-color var(--neu-transition),box-shadow var(--neu-transition);position:relative}.neu-select__trigger:hover:not([aria-disabled=true]){border-color:var(--neu-border-hover)}.neu-select__trigger[aria-disabled=true]{cursor:not-allowed;background:var(--neu-surface-2)}.neu-select__trigger:focus-visible{border-color:var(--neu-primary);box-shadow:0 0 0 3px #007aff1f}.neu-select--open .neu-select__trigger{border-color:var(--neu-primary);box-shadow:0 0 0 3px #007aff1f;border-bottom-left-radius:0;border-bottom-right-radius:0}.neu-select--open-above .neu-select__trigger{border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:var(--neu-radius);border-bottom-right-radius:var(--neu-radius)}.neu-select--error .neu-select__trigger{border-color:var(--neu-error)}.neu-select--error .neu-select__trigger:focus-visible{box-shadow:0 0 0 3px #ef44441f}.neu-select__label{position:absolute;left:var(--neu-space-3);top:50%;transform:translateY(-50%);font-size:var(--neu-text-base);color:color-mix(in srgb,var(--neu-text) 68%,var(--neu-surface) 32%);pointer-events:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - var(--neu-space-6));transition:top var(--neu-transition),font-size var(--neu-transition),color var(--neu-transition),transform var(--neu-transition),padding var(--neu-transition),background var(--neu-transition)}.neu-select--open .neu-select__label,.neu-select--has-value .neu-select__label{top:0;transform:translateY(-50%);font-size:12px;font-weight:600;letter-spacing:.01em;background:var(--neu-surface);padding:0 4px;left:calc(var(--neu-space-3) - 4px)}.neu-select--open .neu-select__label{color:var(--neu-primary)}.neu-select--error .neu-select__label{color:var(--neu-error)}.neu-select--disabled .neu-select__label{background:var(--neu-surface-2)}.neu-select__value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.neu-select--no-float .neu-select__value{padding-top:0}.neu-select__placeholder{color:color-mix(in srgb,var(--neu-text) 62%,var(--neu-surface) 38%)}.neu-select:not(.neu-select--no-float):not(.neu-select--open) .neu-select__placeholder{visibility:hidden}.neu-select__chevron{position:absolute;right:var(--neu-space-3);top:50%;transform:translateY(-50%);width:18px;height:18px;color:var(--neu-text-muted);flex-shrink:0;transition:transform var(--neu-transition)}.neu-select--open .neu-select__chevron{transform:translateY(-50%) rotate(180deg);color:var(--neu-primary)}.neu-select__clear{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin-right:2px;padding:0;border:none;background:transparent;color:var(--neu-text-muted);cursor:pointer;border-radius:var(--neu-radius-sm);flex-shrink:0;transition:color var(--neu-transition),background var(--neu-transition)}.neu-select__clear svg{width:14px;height:14px}.neu-select__clear:hover{color:var(--neu-text);background:var(--neu-surface-3)}.neu-select__clear:focus-visible{outline:2px solid var(--neu-primary);outline-offset:2px}.neu-select__panel{position:relative;z-index:var(--neu-z-dropdown);background:var(--neu-surface);border:1.5px solid var(--neu-primary);border-top:none;border-bottom-left-radius:var(--neu-radius);border-bottom-right-radius:var(--neu-radius);box-shadow:var(--neu-shadow-lg);max-height:240px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--neu-surface-3) transparent;animation:neu-select-open .15s ease forwards}.neu-select__panel::-webkit-scrollbar{width:4px}.neu-select__panel::-webkit-scrollbar-thumb{background:var(--neu-surface-3);border-radius:99px}.neu-select__panel--above{border-top:1.5px solid var(--neu-primary);border-bottom:none;border-top-left-radius:var(--neu-radius);border-top-right-radius:var(--neu-radius);border-bottom-left-radius:0;border-bottom-right-radius:0}.neu-select__panel--virtual{overflow:visible}.neu-select__viewport{width:100%}@media(max-width:600px){.neu-select__panel{left:auto;right:0;width:min(max(100%,220px),100vw - 2rem);max-width:calc(100vw - 2rem)}}@keyframes neu-select-open{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.neu-select__option{display:flex;align-items:center;gap:var(--neu-space-2);padding:var(--neu-space-3) var(--neu-space-4);min-height:var(--neu-select-option-height, 44px);box-sizing:border-box;font-size:var(--neu-text-sm);color:var(--neu-text);cursor:pointer;transition:background-color var(--neu-transition)}.neu-select__option:hover:not(.neu-select__option--disabled){background:var(--neu-primary-50);color:var(--neu-primary)}.neu-select__option--selected{color:var(--neu-primary);font-weight:600;background:var(--neu-primary-50)}.neu-select__option--disabled{opacity:.4;cursor:not-allowed}.neu-select__check{width:14px;height:14px;flex-shrink:0}.neu-select__error{margin-top:var(--neu-space-1);font-size:var(--neu-text-xs);color:var(--neu-error-text);font-family:var(--neu-font-sans)}.neu-select__hint{margin-top:var(--neu-space-1);font-size:var(--neu-text-xs);color:var(--neu-text-muted);font-family:var(--neu-font-sans)}.neu-select__sr-status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.neu-select__static-label{display:block;font-size:var(--neu-text-sm);font-weight:500;color:var(--neu-text-muted);margin-bottom:var(--neu-space-2)}.neu-select__search{padding:var(--neu-space-2);border-bottom:1px solid var(--neu-border);position:sticky;top:0;background:var(--neu-surface);z-index:1}.neu-select__search-input{width:100%;height:34px;padding:0 var(--neu-space-3);border:1.5px solid var(--neu-border);border-radius:var(--neu-radius-sm);background:var(--neu-bg);font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);color:var(--neu-text);outline:none;transition:border-color var(--neu-transition),box-shadow var(--neu-transition)}.neu-select__search-input:focus{border-color:var(--neu-primary);box-shadow:0 0 0 2px #007aff1f}.neu-select__search-input::placeholder{color:var(--neu-text-disabled)}.neu-select__empty{padding:var(--neu-space-4);text-align:center;font-size:var(--neu-text-sm);color:var(--neu-text-disabled);font-family:var(--neu-font-sans)}\n"] }]
878
926
  }], ctorParameters: () => [], propDecorators: { _viewport: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkVirtualScrollViewport), { isSignal: true }] }], itemTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NeuSelectItemDirective), { isSignal: true }] }], selectedItemTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NeuSelectSelectedDirective), { isSignal: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], floatingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "floatingLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], virtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScroll", required: false }] }], virtualScrollVisibleItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScrollVisibleItems", required: false }] }], noResultsMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "noResultsMessage", required: false }] }], clearAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearAriaLabel", required: false }] }], urlParam: [{ type: i0.Input, args: [{ isSignal: true, alias: "urlParam", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
879
927
 
880
928
  /**