@neural-ui/core 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +12 -6
  2. package/fesm2022/neural-ui-core-accordion.mjs +8 -6
  3. package/fesm2022/neural-ui-core-accordion.mjs.map +1 -1
  4. package/fesm2022/neural-ui-core-autocomplete.mjs +121 -29
  5. package/fesm2022/neural-ui-core-autocomplete.mjs.map +1 -1
  6. package/fesm2022/neural-ui-core-badge.mjs +2 -2
  7. package/fesm2022/neural-ui-core-badge.mjs.map +1 -1
  8. package/fesm2022/neural-ui-core-button.mjs +2 -2
  9. package/fesm2022/neural-ui-core-button.mjs.map +1 -1
  10. package/fesm2022/neural-ui-core-chip.mjs +2 -2
  11. package/fesm2022/neural-ui-core-chip.mjs.map +1 -1
  12. package/fesm2022/neural-ui-core-color-picker.mjs +3 -9
  13. package/fesm2022/neural-ui-core-color-picker.mjs.map +1 -1
  14. package/fesm2022/neural-ui-core-filter-bar.mjs +2 -2
  15. package/fesm2022/neural-ui-core-filter-bar.mjs.map +1 -1
  16. package/fesm2022/neural-ui-core-modal.mjs +81 -31
  17. package/fesm2022/neural-ui-core-modal.mjs.map +1 -1
  18. package/fesm2022/neural-ui-core-multiselect.mjs +258 -99
  19. package/fesm2022/neural-ui-core-multiselect.mjs.map +1 -1
  20. package/fesm2022/neural-ui-core-nav.mjs +4 -6
  21. package/fesm2022/neural-ui-core-nav.mjs.map +1 -1
  22. package/fesm2022/neural-ui-core-select.mjs +247 -94
  23. package/fesm2022/neural-ui-core-select.mjs.map +1 -1
  24. package/fesm2022/neural-ui-core-sidebar.mjs +3 -2
  25. package/fesm2022/neural-ui-core-sidebar.mjs.map +1 -1
  26. package/fesm2022/neural-ui-core-slider.mjs +2 -2
  27. package/fesm2022/neural-ui-core-slider.mjs.map +1 -1
  28. package/fesm2022/neural-ui-core-split-button.mjs +2 -2
  29. package/fesm2022/neural-ui-core-split-button.mjs.map +1 -1
  30. package/fesm2022/neural-ui-core-table.mjs +168 -21
  31. package/fesm2022/neural-ui-core-table.mjs.map +1 -1
  32. package/fesm2022/neural-ui-core-tabs.mjs +11 -4
  33. package/fesm2022/neural-ui-core-tabs.mjs.map +1 -1
  34. package/fesm2022/neural-ui-core-toolbar.mjs +2 -2
  35. package/fesm2022/neural-ui-core-toolbar.mjs.map +1 -1
  36. package/fesm2022/neural-ui-core-url-state.mjs +90 -10
  37. package/fesm2022/neural-ui-core-url-state.mjs.map +1 -1
  38. package/fesm2022/neural-ui-core-virtual-list.mjs +52 -22
  39. package/fesm2022/neural-ui-core-virtual-list.mjs.map +1 -1
  40. package/package.json +1 -1
  41. package/styles/_tokens.scss +8 -8
  42. package/types/neural-ui-core-autocomplete.d.ts +10 -1
  43. package/types/neural-ui-core-color-picker.d.ts +0 -1
  44. package/types/neural-ui-core-modal.d.ts +22 -16
  45. package/types/neural-ui-core-multiselect.d.ts +12 -1
  46. package/types/neural-ui-core-select.d.ts +12 -1
  47. package/types/neural-ui-core-sidebar.d.ts +1 -0
  48. package/types/neural-ui-core-table.d.ts +23 -3
  49. package/types/neural-ui-core-tabs.d.ts +1 -0
  50. package/types/neural-ui-core-url-state.d.ts +9 -0
  51. package/types/neural-ui-core-virtual-list.d.ts +17 -1
@@ -28,6 +28,7 @@ class NeuTabsComponent {
28
28
  _dragStartX = 0;
29
29
  _dragStartScrollLeft = 0;
30
30
  _suppressNextClick = false;
31
+ _optimisticActiveTabId = signal(null, ...(ngDevMode ? [{ debugName: "_optimisticActiveTabId" }] : /* istanbul ignore next */ []));
31
32
  isDraggingNav = signal(false, ...(ngDevMode ? [{ debugName: "isDraggingNav" }] : /* istanbul ignore next */ []));
32
33
  _getUrlParamSignal(key) {
33
34
  let paramSignal = this._urlParamSignals.get(key);
@@ -59,12 +60,17 @@ class NeuTabsComponent {
59
60
  tabChange = output();
60
61
  /** ID de la pestaña activa (de la URL o la primera disponible) / Active tab ID (from the URL or the first available) */
61
62
  activeTabId = computed(() => {
63
+ const tabList = this.tabs();
62
64
  const fromUrl = this._readUrlParam(this.tabParam());
63
- const available = this.tabs().find((t) => t.id === fromUrl && !t.disabled);
65
+ const available = tabList.find((t) => t.id === fromUrl && !t.disabled);
64
66
  if (available)
65
67
  return available.id;
68
+ const optimistic = this._optimisticActiveTabId();
69
+ const optimisticAvailable = tabList.find((t) => t.id === optimistic && !t.disabled);
70
+ if (optimisticAvailable)
71
+ return optimisticAvailable.id;
66
72
  // Fallback: primera pestaña no deshabilitada
67
- return this.tabs().find((t) => !t.disabled)?.id ?? '';
73
+ return tabList.find((t) => !t.disabled)?.id ?? '';
68
74
  }, ...(ngDevMode ? [{ debugName: "activeTabId" }] : /* istanbul ignore next */ []));
69
75
  /** Posición del indicador calculada mediante medición DOM / Indicator position calculated via DOM measurement */
70
76
  _indicatorLeft = signal('0px', ...(ngDevMode ? [{ debugName: "_indicatorLeft" }] : /* istanbul ignore next */ []));
@@ -113,6 +119,7 @@ class NeuTabsComponent {
113
119
  selectTab(tab) {
114
120
  if (tab.disabled)
115
121
  return;
122
+ this._optimisticActiveTabId.set(tab.id);
116
123
  this.urlState.setParam(this.tabParam(), tab.id);
117
124
  this.tabChange.emit(tab.id);
118
125
  requestAnimationFrame(() => this._updateIndicator());
@@ -226,7 +233,7 @@ class NeuTabsComponent {
226
233
  <ng-content />
227
234
  </div>
228
235
  </div>
229
- `, isInline: true, styles: [".neu-tabs{display:flex;flex-direction:column}.neu-tabs__nav{position:relative;display:flex;border-bottom:1.5px solid var(--neu-border);overflow-x:auto;cursor:grab;touch-action:pan-x;scrollbar-width:none}.neu-tabs__nav::-webkit-scrollbar{display:none}.neu-tabs__nav--dragging{cursor:grabbing;-webkit-user-select:none;user-select:none}.neu-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--neu-space-2);padding:var(--neu-space-3) var(--neu-space-5);background:transparent;border:none;font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);font-weight:500;color:var(--neu-text-muted);cursor:pointer;white-space:nowrap;flex-shrink:0;transition:color var(--neu-transition),background-color var(--neu-transition);outline:none}.neu-tabs__tab:hover:not(:disabled){color:var(--neu-text);background:var(--neu-surface-2)}.neu-tabs__tab:focus-visible{outline:2px solid var(--neu-primary);outline-offset:-2px;border-radius:var(--neu-radius-sm) var(--neu-radius-sm) 0 0}.neu-tabs__tab--active{color:var(--neu-primary);font-weight:600}.neu-tabs__tab--disabled{opacity:.4;cursor:not-allowed}.neu-tabs__tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:var(--neu-radius-full);background:var(--neu-primary-100);color:var(--neu-primary);font-size:10px;font-weight:700;line-height:1}.neu-tabs__indicator{position:absolute;bottom:-1.5px;height:2.5px;background:var(--neu-primary);border-radius:var(--neu-radius-full) var(--neu-radius-full) 0 0;transition:left var(--neu-transition-slow),width var(--neu-transition-slow);pointer-events:none}.neu-tabs__panels{flex:1}.neu-tab-panel{padding-top:1.5rem;animation:neu-tab-fade .18s ease}@keyframes neu-tab-fade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
236
+ `, isInline: true, styles: [".neu-tabs{display:flex;flex-direction:column}.neu-tabs__nav{position:relative;display:flex;border-bottom:1.5px solid var(--neu-border);overflow-x:auto;cursor:grab;touch-action:pan-x;scrollbar-width:none}.neu-tabs__nav::-webkit-scrollbar{display:none}.neu-tabs__nav--dragging{cursor:grabbing;-webkit-user-select:none;user-select:none}.neu-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--neu-space-2);padding:var(--neu-space-3) var(--neu-space-5);background:transparent;border:none;font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);font-weight:500;color:color-mix(in srgb,var(--neu-text) 72%,var(--neu-surface) 28%);cursor:pointer;white-space:nowrap;flex-shrink:0;transition:color var(--neu-transition),background-color var(--neu-transition);outline:none}.neu-tabs__tab:hover:not(:disabled){color:var(--neu-text);background:var(--neu-surface-2)}.neu-tabs__tab:focus-visible{outline:2px solid var(--neu-primary);outline-offset:-2px;border-radius:var(--neu-radius-sm) var(--neu-radius-sm) 0 0}.neu-tabs__tab--active{color:var(--neu-primary-dark, var(--neu-primary));font-weight:600}.neu-tabs__tab--disabled{opacity:.4;cursor:not-allowed}.neu-tabs__tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:var(--neu-radius-full);background:var(--neu-primary-100);color:var(--neu-primary-dark, var(--neu-primary));font-size:10px;font-weight:700;line-height:1}.neu-tabs__indicator{position:absolute;bottom:-1.5px;height:2.5px;background:var(--neu-primary);border-radius:var(--neu-radius-full) var(--neu-radius-full) 0 0;transition:left var(--neu-transition-slow),width var(--neu-transition-slow);pointer-events:none}.neu-tabs__panels{flex:1}.neu-tab-panel{padding-top:1.5rem;animation:neu-tab-fade .18s ease}@keyframes neu-tab-fade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
230
237
  }
231
238
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuTabsComponent, decorators: [{
232
239
  type: Component,
@@ -277,7 +284,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
277
284
  <ng-content />
278
285
  </div>
279
286
  </div>
280
- `, styles: [".neu-tabs{display:flex;flex-direction:column}.neu-tabs__nav{position:relative;display:flex;border-bottom:1.5px solid var(--neu-border);overflow-x:auto;cursor:grab;touch-action:pan-x;scrollbar-width:none}.neu-tabs__nav::-webkit-scrollbar{display:none}.neu-tabs__nav--dragging{cursor:grabbing;-webkit-user-select:none;user-select:none}.neu-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--neu-space-2);padding:var(--neu-space-3) var(--neu-space-5);background:transparent;border:none;font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);font-weight:500;color:var(--neu-text-muted);cursor:pointer;white-space:nowrap;flex-shrink:0;transition:color var(--neu-transition),background-color var(--neu-transition);outline:none}.neu-tabs__tab:hover:not(:disabled){color:var(--neu-text);background:var(--neu-surface-2)}.neu-tabs__tab:focus-visible{outline:2px solid var(--neu-primary);outline-offset:-2px;border-radius:var(--neu-radius-sm) var(--neu-radius-sm) 0 0}.neu-tabs__tab--active{color:var(--neu-primary);font-weight:600}.neu-tabs__tab--disabled{opacity:.4;cursor:not-allowed}.neu-tabs__tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:var(--neu-radius-full);background:var(--neu-primary-100);color:var(--neu-primary);font-size:10px;font-weight:700;line-height:1}.neu-tabs__indicator{position:absolute;bottom:-1.5px;height:2.5px;background:var(--neu-primary);border-radius:var(--neu-radius-full) var(--neu-radius-full) 0 0;transition:left var(--neu-transition-slow),width var(--neu-transition-slow);pointer-events:none}.neu-tabs__panels{flex:1}.neu-tab-panel{padding-top:1.5rem;animation:neu-tab-fade .18s ease}@keyframes neu-tab-fade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
287
+ `, styles: [".neu-tabs{display:flex;flex-direction:column}.neu-tabs__nav{position:relative;display:flex;border-bottom:1.5px solid var(--neu-border);overflow-x:auto;cursor:grab;touch-action:pan-x;scrollbar-width:none}.neu-tabs__nav::-webkit-scrollbar{display:none}.neu-tabs__nav--dragging{cursor:grabbing;-webkit-user-select:none;user-select:none}.neu-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--neu-space-2);padding:var(--neu-space-3) var(--neu-space-5);background:transparent;border:none;font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);font-weight:500;color:color-mix(in srgb,var(--neu-text) 72%,var(--neu-surface) 28%);cursor:pointer;white-space:nowrap;flex-shrink:0;transition:color var(--neu-transition),background-color var(--neu-transition);outline:none}.neu-tabs__tab:hover:not(:disabled){color:var(--neu-text);background:var(--neu-surface-2)}.neu-tabs__tab:focus-visible{outline:2px solid var(--neu-primary);outline-offset:-2px;border-radius:var(--neu-radius-sm) var(--neu-radius-sm) 0 0}.neu-tabs__tab--active{color:var(--neu-primary-dark, var(--neu-primary));font-weight:600}.neu-tabs__tab--disabled{opacity:.4;cursor:not-allowed}.neu-tabs__tab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:var(--neu-radius-full);background:var(--neu-primary-100);color:var(--neu-primary-dark, var(--neu-primary));font-size:10px;font-weight:700;line-height:1}.neu-tabs__indicator{position:absolute;bottom:-1.5px;height:2.5px;background:var(--neu-primary);border-radius:var(--neu-radius-full) var(--neu-radius-full) 0 0;transition:left var(--neu-transition-slow),width var(--neu-transition-slow);pointer-events:none}.neu-tabs__panels{flex:1}.neu-tab-panel{padding-top:1.5rem;animation:neu-tab-fade .18s ease}@keyframes neu-tab-fade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
281
288
  }], ctorParameters: () => [], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: false }] }], tabParam: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabParam", required: false }] }], flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }] } });
282
289
  // ----------------------------------------------------------------
283
290
  // NeuTabPanelComponent — panel individual (usa DI para el contexto)
@@ -1 +1 @@
1
- {"version":3,"file":"neural-ui-core-tabs.mjs","sources":["../../../../projects/ui-core/tabs/neu-tabs.component.ts","../../../../projects/ui-core/tabs/neural-ui-core-tabs.ts"],"sourcesContent":["import {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n InjectionToken,\n OnDestroy,\n Signal,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { NeuUrlStateService } from '@neural-ui/core/url-state';\n\n// ----------------------------------------------------------------\n// Token de contexto — permite a NeuTabPanelComponent inyectar\n// la instancia padre sin pasar signals manualmente.\n// ----------------------------------------------------------------\nexport const NEU_TABS_CONTEXT = new InjectionToken<NeuTabsComponent>('NeuTabsContext');\n\nexport interface NeuTab {\n /** ID único de la pestaña — se usa como valor en la URL / Unique tab ID — used as the URL value */\n id: string;\n /** Etiqueta visible / Visible label */\n label: string;\n /** Badge opcional junto al label / Optional badge next to the label */\n badge?: string;\n /** Deshabilita la pestaña sin ocultarla / Disables the tab without hiding it */\n disabled?: boolean;\n}\n\n/**\n * NeuralUI Tabs Component\n *\n * Sistema de pestañas con estado sincronizado a la URL via NeuUrlStateService. / Tab system with state synchronized to the URL via NeuUrlStateService.\n * El panel activo se determina por ?{tabParam}={tabId}. / The active panel is determined by ?{tabParam}={tabId}.\n *\n * Uso:\n * <neu-tabs [tabs]=\"tabs\" tabParam=\"tab\">\n * <neu-tab-panel tabId=\"preview\">...</neu-tab-panel>\n * <neu-tab-panel tabId=\"api\">...</neu-tab-panel>\n * </neu-tabs>\n */\n@Component({\n selector: 'neu-tabs',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{ provide: NEU_TABS_CONTEXT, useExisting: NeuTabsComponent }],\n template: `\n <!-- Barra de pestañas -->\n <div class=\"neu-tabs\" [class.neu-tabs--flush]=\"flush()\">\n <div\n class=\"neu-tabs__nav\"\n role=\"tablist\"\n [attr.aria-label]=\"ariaLabel()\"\n [class.neu-tabs__nav--dragging]=\"isDraggingNav()\"\n #navRef\n (pointerdown)=\"startNavDrag($event)\"\n (pointermove)=\"moveNavDrag($event)\"\n (pointerup)=\"endNavDrag($event)\"\n (pointercancel)=\"endNavDrag($event)\"\n >\n @for (tab of tabs(); track tab.id) {\n <button\n class=\"neu-tabs__tab\"\n [class.neu-tabs__tab--active]=\"activeTabId() === tab.id\"\n [class.neu-tabs__tab--disabled]=\"tab.disabled\"\n role=\"tab\"\n [id]=\"'neu-tab-' + tab.id\"\n [attr.aria-selected]=\"activeTabId() === tab.id\"\n [attr.aria-controls]=\"'neu-tabpanel-' + tab.id\"\n [attr.tabindex]=\"activeTabId() === tab.id ? '0' : '-1'\"\n [disabled]=\"tab.disabled\"\n type=\"button\"\n (click)=\"handleTabClick($event, tab)\"\n (keydown.arrowRight)=\"focusTab($any($event), 1)\"\n (keydown.arrowLeft)=\"focusTab($any($event), -1)\"\n (keydown.home)=\"focusTab($any($event), 'first')\"\n (keydown.end)=\"focusTab($any($event), 'last')\"\n >\n {{ tab.label }}\n @if (tab.badge) {\n <span class=\"neu-tabs__tab-badge\">{{ tab.badge }}</span>\n }\n </button>\n }\n <!-- Indicador deslizante -->\n <span class=\"neu-tabs__indicator\" [style]=\"indicatorStyle()\"></span>\n </div>\n\n <!-- Paneles (proyectados desde NeuTabPanelComponent) -->\n <div class=\"neu-tabs__panels\">\n <ng-content />\n </div>\n </div>\n `,\n styleUrl: './neu-tabs.component.scss',\n})\nexport class NeuTabsComponent implements AfterViewInit, OnDestroy {\n private readonly urlState = inject(NeuUrlStateService);\n private readonly elRef = inject(ElementRef);\n private resizeObserver?: ResizeObserver;\n private readonly _urlParamSignals = new Map<string, Signal<string | null>>();\n private _dragPointerId: number | null = null;\n private _dragStartX = 0;\n private _dragStartScrollLeft = 0;\n private _suppressNextClick = false;\n readonly isDraggingNav = signal(false);\n\n private _getUrlParamSignal(key: string): Signal<string | null> {\n let paramSignal = this._urlParamSignals.get(key);\n if (!paramSignal) {\n paramSignal = this.urlState.getParam(key);\n this._urlParamSignals.set(key, paramSignal);\n }\n return paramSignal;\n }\n\n private _readUrlParam(key: string): string | null {\n return this._getUrlParamSignal(key)();\n }\n\n constructor() {\n // Actualizar indicador cuando activeTabId cambie — debe estar en el constructor (injection context)\n effect(() => {\n this.activeTabId(); // dependencia reactiva\n requestAnimationFrame(() => this._updateIndicator());\n });\n }\n\n /** Definición de pestañas / Tab definitions */\n tabs = input<NeuTab[]>([]);\n\n /** QueryParam que almacena la pestaña activa / QueryParam that stores the active tab */\n tabParam = input<string>('tab');\n\n /** Si true, elimina el padding interno de los paneles / If true, removes the internal padding from panels */\n flush = input<boolean>(false);\n\n /** Etiqueta accesible del rol tablist / Accessible label for the tablist role */\n ariaLabel = input<string>('Pestañas de contenido');\n\n /** Emite al cambiar de pestaña / Emits when the tab changes */\n tabChange = output<string>();\n\n /** ID de la pestaña activa (de la URL o la primera disponible) / Active tab ID (from the URL or the first available) */\n readonly activeTabId = computed(() => {\n const fromUrl = this._readUrlParam(this.tabParam());\n const available = this.tabs().find((t) => t.id === fromUrl && !t.disabled);\n if (available) return available.id;\n // Fallback: primera pestaña no deshabilitada\n return this.tabs().find((t) => !t.disabled)?.id ?? '';\n });\n\n /** Posición del indicador calculada mediante medición DOM / Indicator position calculated via DOM measurement */\n private readonly _indicatorLeft = signal('0px');\n private readonly _indicatorWidth = signal('0px');\n\n readonly indicatorStyle = computed(\n () => `left: ${this._indicatorLeft()}; width: ${this._indicatorWidth()}`,\n );\n\n ngAfterViewInit(): void {\n this._updateIndicator();\n // Actualizar cuando cambie el tamaño del nav (p.ej. resize de ventana)\n const nav = this.elRef.nativeElement.querySelector('.neu-tabs__nav');\n if (nav && typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => this._updateIndicator());\n this.resizeObserver.observe(nav);\n }\n }\n\n ngOnDestroy(): void {\n this.resizeObserver?.disconnect();\n }\n\n private _updateIndicator(): void {\n const nav: HTMLElement | null = this.elRef.nativeElement.querySelector('.neu-tabs__nav');\n if (!nav) return;\n const tabEls = nav.querySelectorAll<HTMLElement>('.neu-tabs__tab');\n const idx = this.tabs().findIndex((t) => t.id === this.activeTabId());\n const tabEl = tabEls[idx];\n if (tabEl) {\n this._indicatorLeft.set(tabEl.offsetLeft + 'px');\n this._indicatorWidth.set(tabEl.offsetWidth + 'px');\n if (typeof tabEl.scrollIntoView === 'function') {\n tabEl.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'nearest',\n });\n }\n }\n }\n\n handleTabClick(event: Event, tab: NeuTab): void {\n if (this._suppressNextClick) {\n event.preventDefault();\n event.stopPropagation();\n this._suppressNextClick = false;\n return;\n }\n this.selectTab(tab);\n }\n\n selectTab(tab: NeuTab): void {\n if (tab.disabled) return;\n this.urlState.setParam(this.tabParam(), tab.id);\n this.tabChange.emit(tab.id);\n requestAnimationFrame(() => this._updateIndicator());\n }\n\n startNavDrag(event: PointerEvent): void {\n if (event.pointerType === 'mouse' && event.button !== 0) return;\n const target = event.target as HTMLElement | null;\n if (!target?.closest('.neu-tabs__nav')) return;\n if (target.closest('.neu-tabs__tab')) return;\n\n const nav = event.currentTarget as HTMLElement;\n this._dragPointerId = event.pointerId;\n this._dragStartX = event.clientX;\n this._dragStartScrollLeft = nav.scrollLeft;\n this.isDraggingNav.set(false);\n nav.setPointerCapture(event.pointerId);\n }\n\n moveNavDrag(event: PointerEvent): void {\n if (this._dragPointerId !== event.pointerId) return;\n\n const nav = event.currentTarget as HTMLElement;\n const deltaX = event.clientX - this._dragStartX;\n if (!this.isDraggingNav() && Math.abs(deltaX) > 6) {\n this.isDraggingNav.set(true);\n this._suppressNextClick = true;\n }\n if (!this.isDraggingNav()) return;\n\n nav.scrollLeft = this._dragStartScrollLeft - deltaX;\n event.preventDefault();\n }\n\n endNavDrag(event: PointerEvent): void {\n if (this._dragPointerId !== event.pointerId) return;\n\n const nav = event.currentTarget as HTMLElement;\n if (nav.hasPointerCapture(event.pointerId)) {\n nav.releasePointerCapture(event.pointerId);\n }\n this._dragPointerId = null;\n if (this.isDraggingNav()) {\n requestAnimationFrame(() => this.isDraggingNav.set(false));\n }\n }\n\n /** Mueve el foco entre tabs con flechas (roving tabindex — WAI-ARIA Tabs Pattern) / Moves focus between tabs with arrows (roving tabindex — WAI-ARIA Tabs Pattern) */\n focusTab(event: Event, dir: 1 | -1 | 'first' | 'last'): void {\n event.preventDefault();\n const enabledTabs = this.tabs().filter((t) => !t.disabled);\n const currentIdx = enabledTabs.findIndex((t) => t.id === this.activeTabId());\n let nextIdx: number;\n if (dir === 'first') {\n nextIdx = 0;\n } else if (dir === 'last') {\n nextIdx = enabledTabs.length - 1;\n } else {\n nextIdx = (currentIdx + dir + enabledTabs.length) % enabledTabs.length;\n }\n const next = enabledTabs[nextIdx];\n this.selectTab(next);\n const btn = (this.elRef.nativeElement as HTMLElement).querySelector(\n `#neu-tab-${next.id}`,\n ) as HTMLElement | null;\n btn?.focus();\n }\n}\n\n// ----------------------------------------------------------------\n// NeuTabPanelComponent — panel individual (usa DI para el contexto)\n// ----------------------------------------------------------------\n\n/**\n * NeuralUI Tab Panel\n *\n * Panel de contenido asociado a una pestaña de NeuTabsComponent. / Content panel associated with a NeuTabsComponent tab.\n * Solo se renderiza (no oculta con CSS) cuando la pestaña está activa. / Only rendered (not hidden with CSS) when the tab is active.\n *\n * Uso: hijo directo de <neu-tabs>\n * <neu-tab-panel tabId=\"api\">...</neu-tab-panel>\n */\n@Component({\n selector: 'neu-tab-panel',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (isActive()) {\n <div\n class=\"neu-tab-panel\"\n role=\"tabpanel\"\n [id]=\"'neu-tabpanel-' + tabId()\"\n [attr.aria-labelledby]=\"'neu-tab-' + tabId()\"\n >\n <ng-content />\n </div>\n }\n `,\n})\nexport class NeuTabPanelComponent {\n private readonly tabs = inject(NEU_TABS_CONTEXT, { optional: true });\n\n /** ID que debe coincidir con NeuTab.id del padre / ID that must match the parent NeuTab.id */\n tabId = input.required<string>();\n\n readonly isActive = computed(() => this.tabs?.activeTabId() === this.tabId());\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;;AAkBA;AACA;AACA;AACA;MACa,gBAAgB,GAAG,IAAI,cAAc,CAAmB,gBAAgB;AAarF;;;;;;;;;;;AAWG;MAyDU,gBAAgB,CAAA;AACV,IAAA,QAAQ,GAAG,MAAM,CAAC,kBAAkB,CAAC;AACrC,IAAA,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC;AACnC,IAAA,cAAc;AACL,IAAA,gBAAgB,GAAG,IAAI,GAAG,EAAiC;IACpE,cAAc,GAAkB,IAAI;IACpC,WAAW,GAAG,CAAC;IACf,oBAAoB,GAAG,CAAC;IACxB,kBAAkB,GAAG,KAAK;AACzB,IAAA,aAAa,GAAG,MAAM,CAAC,KAAK,oFAAC;AAE9B,IAAA,kBAAkB,CAAC,GAAW,EAAA;QACpC,IAAI,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC;QAChD,IAAI,CAAC,WAAW,EAAE;YAChB,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;YACzC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC;QAC7C;AACA,QAAA,OAAO,WAAW;IACpB;AAEQ,IAAA,aAAa,CAAC,GAAW,EAAA;AAC/B,QAAA,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE;IACvC;AAEA,IAAA,WAAA,GAAA;;QAEE,MAAM,CAAC,MAAK;AACV,YAAA,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,qBAAqB,CAAC,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;AACtD,QAAA,CAAC,CAAC;IACJ;;AAGA,IAAA,IAAI,GAAG,KAAK,CAAW,EAAE,2EAAC;;AAG1B,IAAA,QAAQ,GAAG,KAAK,CAAS,KAAK,+EAAC;;AAG/B,IAAA,KAAK,GAAG,KAAK,CAAU,KAAK,4EAAC;;AAG7B,IAAA,SAAS,GAAG,KAAK,CAAS,uBAAuB,gFAAC;;IAGlD,SAAS,GAAG,MAAM,EAAU;;AAGnB,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAK;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC1E,QAAA,IAAI,SAAS;YAAE,OAAO,SAAS,CAAC,EAAE;;QAElC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE;AACvD,IAAA,CAAC,kFAAC;;AAGe,IAAA,cAAc,GAAG,MAAM,CAAC,KAAK,qFAAC;AAC9B,IAAA,eAAe,GAAG,MAAM,CAAC,KAAK,sFAAC;AAEvC,IAAA,cAAc,GAAG,QAAQ,CAChC,MAAM,SAAS,IAAI,CAAC,cAAc,EAAE,YAAY,IAAI,CAAC,eAAe,EAAE,CAAA,CAAE,qFACzE;IAED,eAAe,GAAA;QACb,IAAI,CAAC,gBAAgB,EAAE;;AAEvB,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,gBAAgB,CAAC;AACpE,QAAA,IAAI,GAAG,IAAI,OAAO,cAAc,KAAK,WAAW,EAAE;AAChD,YAAA,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;AACvE,YAAA,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC;QAClC;IACF;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,cAAc,EAAE,UAAU,EAAE;IACnC;IAEQ,gBAAgB,GAAA;AACtB,QAAA,MAAM,GAAG,GAAuB,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,gBAAgB,CAAC;AACxF,QAAA,IAAI,CAAC,GAAG;YAAE;QACV,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAc,gBAAgB,CAAC;QAClE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;AACrE,QAAA,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC;QACzB,IAAI,KAAK,EAAE;YACT,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC;YAChD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;AAClD,YAAA,IAAI,OAAO,KAAK,CAAC,cAAc,KAAK,UAAU,EAAE;gBAC9C,KAAK,CAAC,cAAc,CAAC;AACnB,oBAAA,QAAQ,EAAE,QAAQ;AAClB,oBAAA,KAAK,EAAE,SAAS;AAChB,oBAAA,MAAM,EAAE,SAAS;AAClB,iBAAA,CAAC;YACJ;QACF;IACF;IAEA,cAAc,CAAC,KAAY,EAAE,GAAW,EAAA;AACtC,QAAA,IAAI,IAAI,CAAC,kBAAkB,EAAE;YAC3B,KAAK,CAAC,cAAc,EAAE;YACtB,KAAK,CAAC,eAAe,EAAE;AACvB,YAAA,IAAI,CAAC,kBAAkB,GAAG,KAAK;YAC/B;QACF;AACA,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;IACrB;AAEA,IAAA,SAAS,CAAC,GAAW,EAAA;QACnB,IAAI,GAAG,CAAC,QAAQ;YAAE;AAClB,QAAA,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,qBAAqB,CAAC,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;IACtD;AAEA,IAAA,YAAY,CAAC,KAAmB,EAAA;QAC9B,IAAI,KAAK,CAAC,WAAW,KAAK,OAAO,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE;AACzD,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAA4B;AACjD,QAAA,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC;YAAE;AACxC,QAAA,IAAI,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAAE;AAEtC,QAAA,MAAM,GAAG,GAAG,KAAK,CAAC,aAA4B;AAC9C,QAAA,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,SAAS;AACrC,QAAA,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO;AAChC,QAAA,IAAI,CAAC,oBAAoB,GAAG,GAAG,CAAC,UAAU;AAC1C,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;AAC7B,QAAA,GAAG,CAAC,iBAAiB,CAAC,KAAK,CAAC,SAAS,CAAC;IACxC;AAEA,IAAA,WAAW,CAAC,KAAmB,EAAA;AAC7B,QAAA,IAAI,IAAI,CAAC,cAAc,KAAK,KAAK,CAAC,SAAS;YAAE;AAE7C,QAAA,MAAM,GAAG,GAAG,KAAK,CAAC,aAA4B;QAC9C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW;AAC/C,QAAA,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;AACjD,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;AAC5B,YAAA,IAAI,CAAC,kBAAkB,GAAG,IAAI;QAChC;AACA,QAAA,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;YAAE;QAE3B,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,oBAAoB,GAAG,MAAM;QACnD,KAAK,CAAC,cAAc,EAAE;IACxB;AAEA,IAAA,UAAU,CAAC,KAAmB,EAAA;AAC5B,QAAA,IAAI,IAAI,CAAC,cAAc,KAAK,KAAK,CAAC,SAAS;YAAE;AAE7C,QAAA,MAAM,GAAG,GAAG,KAAK,CAAC,aAA4B;QAC9C,IAAI,GAAG,CAAC,iBAAiB,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE;AAC1C,YAAA,GAAG,CAAC,qBAAqB,CAAC,KAAK,CAAC,SAAS,CAAC;QAC5C;AACA,QAAA,IAAI,CAAC,cAAc,GAAG,IAAI;AAC1B,QAAA,IAAI,IAAI,CAAC,aAAa,EAAE,EAAE;AACxB,YAAA,qBAAqB,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC5D;IACF;;IAGA,QAAQ,CAAC,KAAY,EAAE,GAA8B,EAAA;QACnD,KAAK,CAAC,cAAc,EAAE;AACtB,QAAA,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1D,MAAM,UAAU,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5E,QAAA,IAAI,OAAe;AACnB,QAAA,IAAI,GAAG,KAAK,OAAO,EAAE;YACnB,OAAO,GAAG,CAAC;QACb;AAAO,aAAA,IAAI,GAAG,KAAK,MAAM,EAAE;AACzB,YAAA,OAAO,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC;QAClC;aAAO;AACL,YAAA,OAAO,GAAG,CAAC,UAAU,GAAG,GAAG,GAAG,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,MAAM;QACxE;AACA,QAAA,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC;AACjC,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;AACpB,QAAA,MAAM,GAAG,GAAI,IAAI,CAAC,KAAK,CAAC,aAA6B,CAAC,aAAa,CACjE,YAAY,IAAI,CAAC,EAAE,CAAA,CAAE,CACA;QACvB,GAAG,EAAE,KAAK,EAAE;IACd;uGA/KW,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAhB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,gBAAgB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,SAAA,EAAA,WAAA,EAAA,EAAA,SAAA,EAnDhB,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAC/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,myDAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAGU,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAxD5B,SAAS;+BACE,UAAU,EAAA,OAAA,EACX,EAAE,EAAA,aAAA,EACI,iBAAiB,CAAC,IAAI,EAAA,eAAA,EACpB,uBAAuB,CAAC,MAAM,aACpC,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAA,gBAAkB,EAAE,CAAC,EAAA,QAAA,EAC/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,myDAAA,CAAA,EAAA;;AAqLH;AACA;AACA;AAEA;;;;;;;;AAQG;MAmBU,oBAAoB,CAAA;IACd,IAAI,GAAG,MAAM,CAAC,gBAAgB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;AAGpE,IAAA,KAAK,GAAG,KAAK,CAAC,QAAQ,2EAAU;AAEvB,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC,KAAK,EAAE,+EAAC;uGANlE,oBAAoB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAApB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,oBAAoB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAbrB;;;;;;;;;;;AAWT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAEU,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBAlBhC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,eAAe;AACzB,oBAAA,OAAO,EAAE,EAAE;oBACX,aAAa,EAAE,iBAAiB,CAAC,IAAI;oBACrC,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE;;;;;;;;;;;AAWT,EAAA,CAAA;AACF,iBAAA;;;ACvTD;;AAEG;;;;"}
1
+ {"version":3,"file":"neural-ui-core-tabs.mjs","sources":["../../../../projects/ui-core/tabs/neu-tabs.component.ts","../../../../projects/ui-core/tabs/neural-ui-core-tabs.ts"],"sourcesContent":["import {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n InjectionToken,\n OnDestroy,\n Signal,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { NeuUrlStateService } from '@neural-ui/core/url-state';\n\n// ----------------------------------------------------------------\n// Token de contexto — permite a NeuTabPanelComponent inyectar\n// la instancia padre sin pasar signals manualmente.\n// ----------------------------------------------------------------\nexport const NEU_TABS_CONTEXT = new InjectionToken<NeuTabsComponent>('NeuTabsContext');\n\nexport interface NeuTab {\n /** ID único de la pestaña — se usa como valor en la URL / Unique tab ID — used as the URL value */\n id: string;\n /** Etiqueta visible / Visible label */\n label: string;\n /** Badge opcional junto al label / Optional badge next to the label */\n badge?: string;\n /** Deshabilita la pestaña sin ocultarla / Disables the tab without hiding it */\n disabled?: boolean;\n}\n\n/**\n * NeuralUI Tabs Component\n *\n * Sistema de pestañas con estado sincronizado a la URL via NeuUrlStateService. / Tab system with state synchronized to the URL via NeuUrlStateService.\n * El panel activo se determina por ?{tabParam}={tabId}. / The active panel is determined by ?{tabParam}={tabId}.\n *\n * Uso:\n * <neu-tabs [tabs]=\"tabs\" tabParam=\"tab\">\n * <neu-tab-panel tabId=\"preview\">...</neu-tab-panel>\n * <neu-tab-panel tabId=\"api\">...</neu-tab-panel>\n * </neu-tabs>\n */\n@Component({\n selector: 'neu-tabs',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{ provide: NEU_TABS_CONTEXT, useExisting: NeuTabsComponent }],\n template: `\n <!-- Barra de pestañas -->\n <div class=\"neu-tabs\" [class.neu-tabs--flush]=\"flush()\">\n <div\n class=\"neu-tabs__nav\"\n role=\"tablist\"\n [attr.aria-label]=\"ariaLabel()\"\n [class.neu-tabs__nav--dragging]=\"isDraggingNav()\"\n #navRef\n (pointerdown)=\"startNavDrag($event)\"\n (pointermove)=\"moveNavDrag($event)\"\n (pointerup)=\"endNavDrag($event)\"\n (pointercancel)=\"endNavDrag($event)\"\n >\n @for (tab of tabs(); track tab.id) {\n <button\n class=\"neu-tabs__tab\"\n [class.neu-tabs__tab--active]=\"activeTabId() === tab.id\"\n [class.neu-tabs__tab--disabled]=\"tab.disabled\"\n role=\"tab\"\n [id]=\"'neu-tab-' + tab.id\"\n [attr.aria-selected]=\"activeTabId() === tab.id\"\n [attr.aria-controls]=\"'neu-tabpanel-' + tab.id\"\n [attr.tabindex]=\"activeTabId() === tab.id ? '0' : '-1'\"\n [disabled]=\"tab.disabled\"\n type=\"button\"\n (click)=\"handleTabClick($event, tab)\"\n (keydown.arrowRight)=\"focusTab($any($event), 1)\"\n (keydown.arrowLeft)=\"focusTab($any($event), -1)\"\n (keydown.home)=\"focusTab($any($event), 'first')\"\n (keydown.end)=\"focusTab($any($event), 'last')\"\n >\n {{ tab.label }}\n @if (tab.badge) {\n <span class=\"neu-tabs__tab-badge\">{{ tab.badge }}</span>\n }\n </button>\n }\n <!-- Indicador deslizante -->\n <span class=\"neu-tabs__indicator\" [style]=\"indicatorStyle()\"></span>\n </div>\n\n <!-- Paneles (proyectados desde NeuTabPanelComponent) -->\n <div class=\"neu-tabs__panels\">\n <ng-content />\n </div>\n </div>\n `,\n styleUrl: './neu-tabs.component.scss',\n})\nexport class NeuTabsComponent implements AfterViewInit, OnDestroy {\n private readonly urlState = inject(NeuUrlStateService);\n private readonly elRef = inject(ElementRef);\n private resizeObserver?: ResizeObserver;\n private readonly _urlParamSignals = new Map<string, Signal<string | null>>();\n private _dragPointerId: number | null = null;\n private _dragStartX = 0;\n private _dragStartScrollLeft = 0;\n private _suppressNextClick = false;\n private readonly _optimisticActiveTabId = signal<string | null>(null);\n readonly isDraggingNav = signal(false);\n\n private _getUrlParamSignal(key: string): Signal<string | null> {\n let paramSignal = this._urlParamSignals.get(key);\n if (!paramSignal) {\n paramSignal = this.urlState.getParam(key);\n this._urlParamSignals.set(key, paramSignal);\n }\n return paramSignal;\n }\n\n private _readUrlParam(key: string): string | null {\n return this._getUrlParamSignal(key)();\n }\n\n constructor() {\n // Actualizar indicador cuando activeTabId cambie — debe estar en el constructor (injection context)\n effect(() => {\n this.activeTabId(); // dependencia reactiva\n requestAnimationFrame(() => this._updateIndicator());\n });\n }\n\n /** Definición de pestañas / Tab definitions */\n tabs = input<NeuTab[]>([]);\n\n /** QueryParam que almacena la pestaña activa / QueryParam that stores the active tab */\n tabParam = input<string>('tab');\n\n /** Si true, elimina el padding interno de los paneles / If true, removes the internal padding from panels */\n flush = input<boolean>(false);\n\n /** Etiqueta accesible del rol tablist / Accessible label for the tablist role */\n ariaLabel = input<string>('Pestañas de contenido');\n\n /** Emite al cambiar de pestaña / Emits when the tab changes */\n tabChange = output<string>();\n\n /** ID de la pestaña activa (de la URL o la primera disponible) / Active tab ID (from the URL or the first available) */\n readonly activeTabId = computed(() => {\n const tabList = this.tabs();\n const fromUrl = this._readUrlParam(this.tabParam());\n const available = tabList.find((t) => t.id === fromUrl && !t.disabled);\n if (available) return available.id;\n\n const optimistic = this._optimisticActiveTabId();\n const optimisticAvailable = tabList.find((t) => t.id === optimistic && !t.disabled);\n if (optimisticAvailable) return optimisticAvailable.id;\n\n // Fallback: primera pestaña no deshabilitada\n return tabList.find((t) => !t.disabled)?.id ?? '';\n });\n\n /** Posición del indicador calculada mediante medición DOM / Indicator position calculated via DOM measurement */\n private readonly _indicatorLeft = signal('0px');\n private readonly _indicatorWidth = signal('0px');\n\n readonly indicatorStyle = computed(\n () => `left: ${this._indicatorLeft()}; width: ${this._indicatorWidth()}`,\n );\n\n ngAfterViewInit(): void {\n this._updateIndicator();\n // Actualizar cuando cambie el tamaño del nav (p.ej. resize de ventana)\n const nav = this.elRef.nativeElement.querySelector('.neu-tabs__nav');\n if (nav && typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => this._updateIndicator());\n this.resizeObserver.observe(nav);\n }\n }\n\n ngOnDestroy(): void {\n this.resizeObserver?.disconnect();\n }\n\n private _updateIndicator(): void {\n const nav: HTMLElement | null = this.elRef.nativeElement.querySelector('.neu-tabs__nav');\n if (!nav) return;\n const tabEls = nav.querySelectorAll<HTMLElement>('.neu-tabs__tab');\n const idx = this.tabs().findIndex((t) => t.id === this.activeTabId());\n const tabEl = tabEls[idx];\n if (tabEl) {\n this._indicatorLeft.set(tabEl.offsetLeft + 'px');\n this._indicatorWidth.set(tabEl.offsetWidth + 'px');\n if (typeof tabEl.scrollIntoView === 'function') {\n tabEl.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'nearest',\n });\n }\n }\n }\n\n handleTabClick(event: Event, tab: NeuTab): void {\n if (this._suppressNextClick) {\n event.preventDefault();\n event.stopPropagation();\n this._suppressNextClick = false;\n return;\n }\n this.selectTab(tab);\n }\n\n selectTab(tab: NeuTab): void {\n if (tab.disabled) return;\n this._optimisticActiveTabId.set(tab.id);\n this.urlState.setParam(this.tabParam(), tab.id);\n this.tabChange.emit(tab.id);\n requestAnimationFrame(() => this._updateIndicator());\n }\n\n startNavDrag(event: PointerEvent): void {\n if (event.pointerType === 'mouse' && event.button !== 0) return;\n const target = event.target as HTMLElement | null;\n if (!target?.closest('.neu-tabs__nav')) return;\n if (target.closest('.neu-tabs__tab')) return;\n\n const nav = event.currentTarget as HTMLElement;\n this._dragPointerId = event.pointerId;\n this._dragStartX = event.clientX;\n this._dragStartScrollLeft = nav.scrollLeft;\n this.isDraggingNav.set(false);\n nav.setPointerCapture(event.pointerId);\n }\n\n moveNavDrag(event: PointerEvent): void {\n if (this._dragPointerId !== event.pointerId) return;\n\n const nav = event.currentTarget as HTMLElement;\n const deltaX = event.clientX - this._dragStartX;\n if (!this.isDraggingNav() && Math.abs(deltaX) > 6) {\n this.isDraggingNav.set(true);\n this._suppressNextClick = true;\n }\n if (!this.isDraggingNav()) return;\n\n nav.scrollLeft = this._dragStartScrollLeft - deltaX;\n event.preventDefault();\n }\n\n endNavDrag(event: PointerEvent): void {\n if (this._dragPointerId !== event.pointerId) return;\n\n const nav = event.currentTarget as HTMLElement;\n if (nav.hasPointerCapture(event.pointerId)) {\n nav.releasePointerCapture(event.pointerId);\n }\n this._dragPointerId = null;\n if (this.isDraggingNav()) {\n requestAnimationFrame(() => this.isDraggingNav.set(false));\n }\n }\n\n /** Mueve el foco entre tabs con flechas (roving tabindex — WAI-ARIA Tabs Pattern) / Moves focus between tabs with arrows (roving tabindex — WAI-ARIA Tabs Pattern) */\n focusTab(event: Event, dir: 1 | -1 | 'first' | 'last'): void {\n event.preventDefault();\n const enabledTabs = this.tabs().filter((t) => !t.disabled);\n const currentIdx = enabledTabs.findIndex((t) => t.id === this.activeTabId());\n let nextIdx: number;\n if (dir === 'first') {\n nextIdx = 0;\n } else if (dir === 'last') {\n nextIdx = enabledTabs.length - 1;\n } else {\n nextIdx = (currentIdx + dir + enabledTabs.length) % enabledTabs.length;\n }\n const next = enabledTabs[nextIdx];\n this.selectTab(next);\n const btn = (this.elRef.nativeElement as HTMLElement).querySelector(\n `#neu-tab-${next.id}`,\n ) as HTMLElement | null;\n btn?.focus();\n }\n}\n\n// ----------------------------------------------------------------\n// NeuTabPanelComponent — panel individual (usa DI para el contexto)\n// ----------------------------------------------------------------\n\n/**\n * NeuralUI Tab Panel\n *\n * Panel de contenido asociado a una pestaña de NeuTabsComponent. / Content panel associated with a NeuTabsComponent tab.\n * Solo se renderiza (no oculta con CSS) cuando la pestaña está activa. / Only rendered (not hidden with CSS) when the tab is active.\n *\n * Uso: hijo directo de <neu-tabs>\n * <neu-tab-panel tabId=\"api\">...</neu-tab-panel>\n */\n@Component({\n selector: 'neu-tab-panel',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (isActive()) {\n <div\n class=\"neu-tab-panel\"\n role=\"tabpanel\"\n [id]=\"'neu-tabpanel-' + tabId()\"\n [attr.aria-labelledby]=\"'neu-tab-' + tabId()\"\n >\n <ng-content />\n </div>\n }\n `,\n})\nexport class NeuTabPanelComponent {\n private readonly tabs = inject(NEU_TABS_CONTEXT, { optional: true });\n\n /** ID que debe coincidir con NeuTab.id del padre / ID that must match the parent NeuTab.id */\n tabId = input.required<string>();\n\n readonly isActive = computed(() => this.tabs?.activeTabId() === this.tabId());\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
@@ -41,7 +41,7 @@ class NeuToolbarComponent {
41
41
  <ng-content select="[neu-toolbar-end]" />
42
42
  </div>
43
43
  <ng-content />
44
- `, isInline: true, styles: ["@charset \"UTF-8\";.neu-toolbar{display:flex;align-items:center;width:100%;gap:8px;background:var(--neu-toolbar-bg, var(--neu-surface-1, #ffffff));color:var(--neu-toolbar-color, var(--neu-text-primary, #111))}.neu-toolbar--sm{padding:6px 12px;min-height:40px}.neu-toolbar--md{padding:8px 16px;min-height:52px}.neu-toolbar--lg{padding:12px 20px;min-height:64px}.neu-toolbar--primary{background:var(--neu-toolbar-primary-bg, var(--neu-color-primary, #0ea5e9));color:var(--neu-toolbar-primary-color, #fff)}.neu-toolbar--none{background:transparent}.neu-toolbar--shadow{box-shadow:0 1px 4px #0000001a}.neu-toolbar--bordered{border-bottom:1px solid var(--neu-border-color, #e5e7eb)}.neu-toolbar__section{display:flex;align-items:center;gap:8px}.neu-toolbar__section--start{flex:0 0 auto;margin-right:auto}.neu-toolbar__section--center{flex:0 1 auto;justify-content:center}.neu-toolbar__section--end{flex:0 0 auto;margin-left:auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
44
+ `, isInline: true, styles: ["@charset \"UTF-8\";.neu-toolbar{display:flex;align-items:center;width:100%;gap:8px;background:var(--neu-toolbar-bg, var(--neu-surface-1, #ffffff));color:var(--neu-toolbar-color, var(--neu-text-primary, #111))}.neu-toolbar--sm{padding:6px 12px;min-height:40px}.neu-toolbar--md{padding:8px 16px;min-height:52px}.neu-toolbar--lg{padding:12px 20px;min-height:64px}.neu-toolbar--primary{background:var(--neu-toolbar-primary-bg, var(--neu-primary-dark, var(--neu-primary)));color:var(--neu-toolbar-primary-color, #fff)}.neu-toolbar--primary .neu-button--ghost{color:#ffffffeb}.neu-toolbar--primary .neu-button--ghost:hover:not(:disabled):not(.neu-button--disabled){background:#ffffff1f;color:#fff}.neu-toolbar--none{background:transparent}.neu-toolbar--shadow{box-shadow:0 1px 4px #0000001a}.neu-toolbar--bordered{border-bottom:1px solid var(--neu-border-color, #e5e7eb)}.neu-toolbar__section{display:flex;align-items:center;gap:8px}.neu-toolbar__section--start{flex:0 0 auto;margin-right:auto}.neu-toolbar__section--center{flex:0 1 auto;justify-content:center}.neu-toolbar__section--end{flex:0 0 auto;margin-left:auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
45
45
  }
46
46
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuToolbarComponent, decorators: [{
47
47
  type: Component,
@@ -56,7 +56,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
56
56
  <ng-content select="[neu-toolbar-end]" />
57
57
  </div>
58
58
  <ng-content />
59
- `, styles: ["@charset \"UTF-8\";.neu-toolbar{display:flex;align-items:center;width:100%;gap:8px;background:var(--neu-toolbar-bg, var(--neu-surface-1, #ffffff));color:var(--neu-toolbar-color, var(--neu-text-primary, #111))}.neu-toolbar--sm{padding:6px 12px;min-height:40px}.neu-toolbar--md{padding:8px 16px;min-height:52px}.neu-toolbar--lg{padding:12px 20px;min-height:64px}.neu-toolbar--primary{background:var(--neu-toolbar-primary-bg, var(--neu-color-primary, #0ea5e9));color:var(--neu-toolbar-primary-color, #fff)}.neu-toolbar--none{background:transparent}.neu-toolbar--shadow{box-shadow:0 1px 4px #0000001a}.neu-toolbar--bordered{border-bottom:1px solid var(--neu-border-color, #e5e7eb)}.neu-toolbar__section{display:flex;align-items:center;gap:8px}.neu-toolbar__section--start{flex:0 0 auto;margin-right:auto}.neu-toolbar__section--center{flex:0 1 auto;justify-content:center}.neu-toolbar__section--end{flex:0 0 auto;margin-left:auto}\n"] }]
59
+ `, styles: ["@charset \"UTF-8\";.neu-toolbar{display:flex;align-items:center;width:100%;gap:8px;background:var(--neu-toolbar-bg, var(--neu-surface-1, #ffffff));color:var(--neu-toolbar-color, var(--neu-text-primary, #111))}.neu-toolbar--sm{padding:6px 12px;min-height:40px}.neu-toolbar--md{padding:8px 16px;min-height:52px}.neu-toolbar--lg{padding:12px 20px;min-height:64px}.neu-toolbar--primary{background:var(--neu-toolbar-primary-bg, var(--neu-primary-dark, var(--neu-primary)));color:var(--neu-toolbar-primary-color, #fff)}.neu-toolbar--primary .neu-button--ghost{color:#ffffffeb}.neu-toolbar--primary .neu-button--ghost:hover:not(:disabled):not(.neu-button--disabled){background:#ffffff1f;color:#fff}.neu-toolbar--none{background:transparent}.neu-toolbar--shadow{box-shadow:0 1px 4px #0000001a}.neu-toolbar--bordered{border-bottom:1px solid var(--neu-border-color, #e5e7eb)}.neu-toolbar__section{display:flex;align-items:center;gap:8px}.neu-toolbar__section--start{flex:0 0 auto;margin-right:auto}.neu-toolbar__section--center{flex:0 1 auto;justify-content:center}.neu-toolbar__section--end{flex:0 0 auto;margin-left:auto}\n"] }]
60
60
  }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "shadow", required: false }] }], bordered: [{ type: i0.Input, args: [{ isSignal: true, alias: "bordered", required: false }] }], surface: [{ type: i0.Input, args: [{ isSignal: true, alias: "surface", required: false }] }] } });
61
61
 
62
62
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"neural-ui-core-toolbar.mjs","sources":["../../../../projects/ui-core/toolbar/neu-toolbar.component.ts","../../../../projects/ui-core/toolbar/neural-ui-core-toolbar.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n} from '@angular/core';\n\nexport type NeuToolbarSize = 'sm' | 'md' | 'lg';\n\n/**\n * NeuralUI Toolbar Component\n *\n * Barra horizontal con tres zonas de contenido: start (izquierda), center (centro) y end (derecha).\n *\n * Uso:\n * <neu-toolbar>\n * <span neu-toolbar-start>Logo</span>\n * <span neu-toolbar-center>Título</span>\n * <span neu-toolbar-end><neu-button>Acción</neu-button></span>\n * </neu-toolbar>\n */\n@Component({\n selector: 'neu-toolbar',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses()' },\n template: `\n <div class=\"neu-toolbar__section neu-toolbar__section--start\">\n <ng-content select=\"[neu-toolbar-start]\" />\n </div>\n <div class=\"neu-toolbar__section neu-toolbar__section--center\">\n <ng-content select=\"[neu-toolbar-center]\" />\n </div>\n <div class=\"neu-toolbar__section neu-toolbar__section--end\">\n <ng-content select=\"[neu-toolbar-end]\" />\n </div>\n <ng-content />\n `,\n styleUrl: './neu-toolbar.component.scss',\n})\nexport class NeuToolbarComponent {\n /** Tamaño de la toolbar / Toolbar size */\n readonly size = input<NeuToolbarSize>('md');\n\n /** Añade sombra en la parte inferior / Adds shadow at the bottom */\n readonly shadow = input<boolean>(false);\n\n /** Añade separador inferior / Adds bottom separator */\n readonly bordered = input<boolean>(false);\n\n /** Color de fondo personalizado vía CSS (pasa a la variable local) / Custom background color via CSS */\n readonly surface = input<'primary' | 'surface' | 'none'>('surface');\n\n readonly hostClasses = computed(() => ({\n 'neu-toolbar': true,\n [`neu-toolbar--${this.size()}`]: true,\n [`neu-toolbar--${this.surface()}`]: true,\n 'neu-toolbar--shadow': this.shadow(),\n 'neu-toolbar--bordered': this.bordered(),\n }));\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;AAUA;;;;;;;;;;;AAWG;MAqBU,mBAAmB,CAAA;;AAErB,IAAA,IAAI,GAAG,KAAK,CAAiB,IAAI,2EAAC;;AAGlC,IAAA,MAAM,GAAG,KAAK,CAAU,KAAK,6EAAC;;AAG9B,IAAA,QAAQ,GAAG,KAAK,CAAU,KAAK,+EAAC;;AAGhC,IAAA,OAAO,GAAG,KAAK,CAAiC,SAAS,8EAAC;AAE1D,IAAA,WAAW,GAAG,QAAQ,CAAC,OAAO;AACrC,QAAA,aAAa,EAAE,IAAI;QACnB,CAAC,CAAA,aAAA,EAAgB,IAAI,CAAC,IAAI,EAAE,CAAA,CAAE,GAAG,IAAI;QACrC,CAAC,CAAA,aAAA,EAAgB,IAAI,CAAC,OAAO,EAAE,CAAA,CAAE,GAAG,IAAI;AACxC,QAAA,qBAAqB,EAAE,IAAI,CAAC,MAAM,EAAE;AACpC,QAAA,uBAAuB,EAAE,IAAI,CAAC,QAAQ,EAAE;AACzC,KAAA,CAAC,kFAAC;uGAnBQ,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,mBAAmB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,eAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAdpB;;;;;;;;;;;AAWT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,i6BAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAGU,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBApB/B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,aAAa,WACd,EAAE,EAAA,aAAA,EACI,iBAAiB,CAAC,IAAI,EAAA,eAAA,EACpB,uBAAuB,CAAC,MAAM,QACzC,EAAE,SAAS,EAAE,eAAe,EAAE,EAAA,QAAA,EAC1B;;;;;;;;;;;AAWT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,i6BAAA,CAAA,EAAA;;;ACvCH;;AAEG;;;;"}
1
+ {"version":3,"file":"neural-ui-core-toolbar.mjs","sources":["../../../../projects/ui-core/toolbar/neu-toolbar.component.ts","../../../../projects/ui-core/toolbar/neural-ui-core-toolbar.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n} from '@angular/core';\n\nexport type NeuToolbarSize = 'sm' | 'md' | 'lg';\n\n/**\n * NeuralUI Toolbar Component\n *\n * Barra horizontal con tres zonas de contenido: start (izquierda), center (centro) y end (derecha).\n *\n * Uso:\n * <neu-toolbar>\n * <span neu-toolbar-start>Logo</span>\n * <span neu-toolbar-center>Título</span>\n * <span neu-toolbar-end><neu-button>Acción</neu-button></span>\n * </neu-toolbar>\n */\n@Component({\n selector: 'neu-toolbar',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses()' },\n template: `\n <div class=\"neu-toolbar__section neu-toolbar__section--start\">\n <ng-content select=\"[neu-toolbar-start]\" />\n </div>\n <div class=\"neu-toolbar__section neu-toolbar__section--center\">\n <ng-content select=\"[neu-toolbar-center]\" />\n </div>\n <div class=\"neu-toolbar__section neu-toolbar__section--end\">\n <ng-content select=\"[neu-toolbar-end]\" />\n </div>\n <ng-content />\n `,\n styleUrl: './neu-toolbar.component.scss',\n})\nexport class NeuToolbarComponent {\n /** Tamaño de la toolbar / Toolbar size */\n readonly size = input<NeuToolbarSize>('md');\n\n /** Añade sombra en la parte inferior / Adds shadow at the bottom */\n readonly shadow = input<boolean>(false);\n\n /** Añade separador inferior / Adds bottom separator */\n readonly bordered = input<boolean>(false);\n\n /** Color de fondo personalizado vía CSS (pasa a la variable local) / Custom background color via CSS */\n readonly surface = input<'primary' | 'surface' | 'none'>('surface');\n\n readonly hostClasses = computed(() => ({\n 'neu-toolbar': true,\n [`neu-toolbar--${this.size()}`]: true,\n [`neu-toolbar--${this.surface()}`]: true,\n 'neu-toolbar--shadow': this.shadow(),\n 'neu-toolbar--bordered': this.bordered(),\n }));\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;AAUA;;;;;;;;;;;AAWG;MAqBU,mBAAmB,CAAA;;AAErB,IAAA,IAAI,GAAG,KAAK,CAAiB,IAAI,2EAAC;;AAGlC,IAAA,MAAM,GAAG,KAAK,CAAU,KAAK,6EAAC;;AAG9B,IAAA,QAAQ,GAAG,KAAK,CAAU,KAAK,+EAAC;;AAGhC,IAAA,OAAO,GAAG,KAAK,CAAiC,SAAS,8EAAC;AAE1D,IAAA,WAAW,GAAG,QAAQ,CAAC,OAAO;AACrC,QAAA,aAAa,EAAE,IAAI;QACnB,CAAC,CAAA,aAAA,EAAgB,IAAI,CAAC,IAAI,EAAE,CAAA,CAAE,GAAG,IAAI;QACrC,CAAC,CAAA,aAAA,EAAgB,IAAI,CAAC,OAAO,EAAE,CAAA,CAAE,GAAG,IAAI;AACxC,QAAA,qBAAqB,EAAE,IAAI,CAAC,MAAM,EAAE;AACpC,QAAA,uBAAuB,EAAE,IAAI,CAAC,QAAQ,EAAE;AACzC,KAAA,CAAC,kFAAC;uGAnBQ,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,mBAAmB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,eAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAdpB;;;;;;;;;;;AAWT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,6lCAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAGU,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBApB/B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,aAAa,WACd,EAAE,EAAA,aAAA,EACI,iBAAiB,CAAC,IAAI,EAAA,eAAA,EACpB,uBAAuB,CAAC,MAAM,QACzC,EAAE,SAAS,EAAE,eAAe,EAAE,EAAA,QAAA,EAC1B;;;;;;;;;;;AAWT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,6lCAAA,CAAA,EAAA;;;ACvCH;;AAEG;;;;"}
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { computed, Injectable } from '@angular/core';
2
+ import { signal, computed, Injectable } from '@angular/core';
3
3
  import { toSignal } from '@angular/core/rxjs-interop';
4
4
  import * as i1 from '@angular/router';
5
5
  import { NavigationEnd } from '@angular/router';
@@ -20,6 +20,11 @@ import { filter, startWith, map } from 'rxjs/operators';
20
20
  class NeuUrlStateService {
21
21
  injector;
22
22
  router;
23
+ _pendingParams = {};
24
+ _pendingReplaceUrl = true;
25
+ _batchScheduled = false;
26
+ _pendingParamsState = signal({}, ...(ngDevMode ? [{ debugName: "_pendingParamsState" }] : /* istanbul ignore next */ []));
27
+ _routerParams;
23
28
  /**
24
29
  * Signal con el mapa completo de queryParams actual.
25
30
  * Se actualiza automáticamente en cada NavigationEnd.
@@ -28,10 +33,26 @@ class NeuUrlStateService {
28
33
  constructor(injector, router) {
29
34
  this.injector = injector;
30
35
  this.router = router;
31
- this.params = toSignal(this.router.events.pipe(filter((e) => e instanceof NavigationEnd), startWith(null), map(() => this.router.parseUrl(this.router.url).queryParams)), {
36
+ this._routerParams = toSignal(this.router.events.pipe(filter((e) => e instanceof NavigationEnd), startWith(null), map(() => this.router.parseUrl(this.router.url).queryParams)), {
32
37
  initialValue: this.router.parseUrl(this.router.url).queryParams,
33
38
  injector: this.injector,
34
39
  });
40
+ this.params = computed(() => {
41
+ const currentParams = this._routerParams();
42
+ const pendingParams = this._pendingParamsState();
43
+ if (Object.keys(pendingParams).length === 0) {
44
+ return currentParams;
45
+ }
46
+ const mergedParams = { ...currentParams };
47
+ for (const [key, value] of Object.entries(pendingParams)) {
48
+ if (value === null) {
49
+ delete mergedParams[key];
50
+ continue;
51
+ }
52
+ mergedParams[key] = value;
53
+ }
54
+ return mergedParams;
55
+ }, ...(ngDevMode ? [{ debugName: "params" }] : /* istanbul ignore next */ []));
35
56
  }
36
57
  /**
37
58
  * Devuelve un Signal reactivo con el valor del parámetro indicado.
@@ -52,12 +73,65 @@ class NeuUrlStateService {
52
73
  * Pasar false para acciones que el usuario debe poder deshacer con Atrás.
53
74
  */
54
75
  setParam(key, value, replaceUrl = true) {
55
- void this.router.navigate([], {
56
- queryParams: { [key]: value },
57
- queryParamsHandling: 'merge',
58
- replaceUrl,
76
+ this._pendingParams[key] = value;
77
+ this._pendingParamsState.set({ ...this._pendingParams });
78
+ this._pendingReplaceUrl = this._pendingReplaceUrl && replaceUrl;
79
+ this._schedulePendingNavigation();
80
+ }
81
+ _schedulePendingNavigation() {
82
+ if (this._batchScheduled) {
83
+ return;
84
+ }
85
+ this._batchScheduled = true;
86
+ queueMicrotask(() => {
87
+ const pending = this._consumePendingNavigation();
88
+ if (!pending) {
89
+ return;
90
+ }
91
+ void this.router
92
+ .navigate([], {
93
+ queryParams: pending.params,
94
+ queryParamsHandling: 'merge',
95
+ replaceUrl: pending.replaceUrl,
96
+ })
97
+ .finally(() => this._finalizePendingNavigation(pending.params));
59
98
  });
60
99
  }
100
+ _consumePendingNavigation() {
101
+ const hasPendingParams = Object.keys(this._pendingParams).length > 0;
102
+ const replaceUrl = this._pendingReplaceUrl;
103
+ const params = { ...this._pendingParams };
104
+ this._pendingParams = {};
105
+ this._pendingParamsState.set({});
106
+ this._pendingReplaceUrl = true;
107
+ this._batchScheduled = false;
108
+ if (!hasPendingParams) {
109
+ return null;
110
+ }
111
+ return { params, replaceUrl };
112
+ }
113
+ _resetPendingNavigation() {
114
+ this._pendingParams = {};
115
+ this._pendingParamsState.set({});
116
+ this._pendingReplaceUrl = true;
117
+ this._batchScheduled = false;
118
+ }
119
+ _finalizePendingNavigation(flushedParams) {
120
+ const currentVisiblePending = { ...this._pendingParamsState() };
121
+ let changed = false;
122
+ for (const [key, value] of Object.entries(flushedParams)) {
123
+ if (Object.prototype.hasOwnProperty.call(this._pendingParams, key)) {
124
+ continue;
125
+ }
126
+ if (currentVisiblePending[key] === value) {
127
+ delete currentVisiblePending[key];
128
+ changed = true;
129
+ }
130
+ }
131
+ if (changed) {
132
+ this._pendingParamsState.set(currentVisiblePending);
133
+ }
134
+ }
61
135
  /**
62
136
  * Actualiza múltiples queryParams en una sola navegación.
63
137
  *
@@ -65,16 +139,22 @@ class NeuUrlStateService {
65
139
  * urlState.patchParams({ page: '1', q: 'Angular' });
66
140
  */
67
141
  patchParams(params, replaceUrl = true) {
68
- void this.router.navigate([], {
69
- queryParams: params,
142
+ const pending = this._consumePendingNavigation();
143
+ const mergedParams = pending ? { ...pending.params, ...params } : params;
144
+ this._pendingParamsState.set({ ...this._pendingParamsState(), ...mergedParams });
145
+ void this.router
146
+ .navigate([], {
147
+ queryParams: mergedParams,
70
148
  queryParamsHandling: 'merge',
71
- replaceUrl,
72
- });
149
+ replaceUrl: pending ? pending.replaceUrl && replaceUrl : replaceUrl,
150
+ })
151
+ .finally(() => this._finalizePendingNavigation(mergedParams));
73
152
  }
74
153
  /**
75
154
  * Elimina todos los queryParams de la URL de una vez.
76
155
  */
77
156
  clearParams(replaceUrl = true) {
157
+ this._resetPendingNavigation();
78
158
  void this.router.navigate([], {
79
159
  queryParams: {},
80
160
  replaceUrl,
@@ -1 +1 @@
1
- {"version":3,"file":"neural-ui-core-url-state.mjs","sources":["../../../../projects/ui-core/url-state/neu-url-state.service.ts","../../../../projects/ui-core/url-state/neural-ui-core-url-state.ts"],"sourcesContent":["import { Injectable, Injector, Signal, computed } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { NavigationEnd, Params, Router } from '@angular/router';\nimport { filter, map, startWith } from 'rxjs/operators';\n\n/**\n * NeuUrlStateService — El Sistema Nervioso de NeuralUI\n *\n * Sincroniza el estado de la UI con los QueryParams de la URL.\n * Completamente reactivo via Angular Signals.\n *\n * Uso:\n * const urlState = inject(NeuUrlStateService);\n * const page = urlState.getParam('page'); // Signal<string | null>\n * urlState.setParam('menu', 'open'); // Actualiza ?menu=open\n * urlState.patchParams({ page: '2', q: 'filter' }); // Actualiza múltiples\n */\n@Injectable({ providedIn: 'root' })\nexport class NeuUrlStateService {\n /**\n * Signal con el mapa completo de queryParams actual.\n * Se actualiza automáticamente en cada NavigationEnd.\n */\n readonly params: Signal<Params>;\n\n constructor(\n private readonly injector: Injector,\n private readonly router: Router,\n ) {\n this.params = toSignal(\n this.router.events.pipe(\n filter((e): e is NavigationEnd => e instanceof NavigationEnd),\n startWith(null as null),\n map(() => this.router.parseUrl(this.router.url).queryParams as Params),\n ),\n {\n initialValue: this.router.parseUrl(this.router.url).queryParams as Params,\n injector: this.injector,\n },\n );\n }\n\n /**\n * Devuelve un Signal reactivo con el valor del parámetro indicado.\n * Memorizar el resultado: no llamar en bucle pues crea un computed nuevo c/vez.\n *\n * @example\n * readonly menuOpen = computed(() => this.urlState.getParam('menu')() === 'open');\n */\n getParam(key: string): Signal<string | null> {\n return computed(() => (this.params()[key] as string | undefined) ?? null);\n }\n\n /**\n * Establece un único queryParam en la URL.\n *\n * @param key Nombre del parámetro\n * @param value Valor. Pasar `null` para eliminarlo de la URL.\n * @param replaceUrl Si true (default) usa replaceState — no ensucia el historial.\n * Pasar false para acciones que el usuario debe poder deshacer con Atrás.\n */\n setParam(key: string, value: string | null, replaceUrl = true): void {\n void this.router.navigate([], {\n queryParams: { [key]: value },\n queryParamsHandling: 'merge',\n replaceUrl,\n });\n }\n\n /**\n * Actualiza múltiples queryParams en una sola navegación.\n *\n * @example\n * urlState.patchParams({ page: '1', q: 'Angular' });\n */\n patchParams(params: Record<string, string | null>, replaceUrl = true): void {\n void this.router.navigate([], {\n queryParams: params,\n queryParamsHandling: 'merge',\n replaceUrl,\n });\n }\n\n /**\n * Elimina todos los queryParams de la URL de una vez.\n */\n clearParams(replaceUrl = true): void {\n void this.router.navigate([], {\n queryParams: {},\n replaceUrl,\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;;;;;AAKA;;;;;;;;;;;AAWG;MAEU,kBAAkB,CAAA;AAQV,IAAA,QAAA;AACA,IAAA,MAAA;AARnB;;;AAGG;AACM,IAAA,MAAM;IAEf,WAAA,CACmB,QAAkB,EAClB,MAAc,EAAA;QADd,IAAA,CAAA,QAAQ,GAAR,QAAQ;QACR,IAAA,CAAA,MAAM,GAAN,MAAM;QAEvB,IAAI,CAAC,MAAM,GAAG,QAAQ,CACpB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CACrB,MAAM,CAAC,CAAC,CAAC,KAAyB,CAAC,YAAY,aAAa,CAAC,EAC7D,SAAS,CAAC,IAAY,CAAC,EACvB,GAAG,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,WAAqB,CAAC,CACvE,EACD;AACE,YAAA,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,WAAqB;YACzE,QAAQ,EAAE,IAAI,CAAC,QAAQ;AACxB,SAAA,CACF;IACH;AAEA;;;;;;AAMG;AACH,IAAA,QAAQ,CAAC,GAAW,EAAA;AAClB,QAAA,OAAO,QAAQ,CAAC,MAAO,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAwB,IAAI,IAAI,CAAC;IAC3E;AAEA;;;;;;;AAOG;AACH,IAAA,QAAQ,CAAC,GAAW,EAAE,KAAoB,EAAE,UAAU,GAAG,IAAI,EAAA;AAC3D,QAAA,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;AAC5B,YAAA,WAAW,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,EAAE;AAC7B,YAAA,mBAAmB,EAAE,OAAO;YAC5B,UAAU;AACX,SAAA,CAAC;IACJ;AAEA;;;;;AAKG;AACH,IAAA,WAAW,CAAC,MAAqC,EAAE,UAAU,GAAG,IAAI,EAAA;AAClE,QAAA,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;AAC5B,YAAA,WAAW,EAAE,MAAM;AACnB,YAAA,mBAAmB,EAAE,OAAO;YAC5B,UAAU;AACX,SAAA,CAAC;IACJ;AAEA;;AAEG;IACH,WAAW,CAAC,UAAU,GAAG,IAAI,EAAA;AAC3B,QAAA,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;AAC5B,YAAA,WAAW,EAAE,EAAE;YACf,UAAU;AACX,SAAA,CAAC;IACJ;uGAzEW,kBAAkB,EAAA,IAAA,EAAA,CAAA,EAAA,KAAA,EAAA,EAAA,CAAA,QAAA,EAAA,EAAA,EAAA,KAAA,EAAA,EAAA,CAAA,MAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,kBAAkB,cADL,MAAM,EAAA,CAAA;;2FACnB,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAD9B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACjBlC;;AAEG;;;;"}
1
+ {"version":3,"file":"neural-ui-core-url-state.mjs","sources":["../../../../projects/ui-core/url-state/neu-url-state.service.ts","../../../../projects/ui-core/url-state/neural-ui-core-url-state.ts"],"sourcesContent":["import { Injectable, Injector, Signal, computed, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { NavigationEnd, Params, Router } from '@angular/router';\nimport { filter, map, startWith } from 'rxjs/operators';\n\n/**\n * NeuUrlStateService — El Sistema Nervioso de NeuralUI\n *\n * Sincroniza el estado de la UI con los QueryParams de la URL.\n * Completamente reactivo via Angular Signals.\n *\n * Uso:\n * const urlState = inject(NeuUrlStateService);\n * const page = urlState.getParam('page'); // Signal<string | null>\n * urlState.setParam('menu', 'open'); // Actualiza ?menu=open\n * urlState.patchParams({ page: '2', q: 'filter' }); // Actualiza múltiples\n */\n@Injectable({ providedIn: 'root' })\nexport class NeuUrlStateService {\n private _pendingParams: Record<string, string | null> = {};\n private _pendingReplaceUrl = true;\n private _batchScheduled = false;\n private readonly _pendingParamsState = signal<Record<string, string | null>>({});\n private readonly _routerParams: Signal<Params>;\n\n /**\n * Signal con el mapa completo de queryParams actual.\n * Se actualiza automáticamente en cada NavigationEnd.\n */\n readonly params: Signal<Params>;\n\n constructor(\n private readonly injector: Injector,\n private readonly router: Router,\n ) {\n this._routerParams = toSignal(\n this.router.events.pipe(\n filter((e): e is NavigationEnd => e instanceof NavigationEnd),\n startWith(null as null),\n map(() => this.router.parseUrl(this.router.url).queryParams as Params),\n ),\n {\n initialValue: this.router.parseUrl(this.router.url).queryParams as Params,\n injector: this.injector,\n },\n );\n\n this.params = computed(() => {\n const currentParams = this._routerParams();\n const pendingParams = this._pendingParamsState();\n if (Object.keys(pendingParams).length === 0) {\n return currentParams;\n }\n\n const mergedParams: Params = { ...currentParams };\n for (const [key, value] of Object.entries(pendingParams)) {\n if (value === null) {\n delete mergedParams[key];\n continue;\n }\n mergedParams[key] = value;\n }\n\n return mergedParams;\n });\n }\n\n /**\n * Devuelve un Signal reactivo con el valor del parámetro indicado.\n * Memorizar el resultado: no llamar en bucle pues crea un computed nuevo c/vez.\n *\n * @example\n * readonly menuOpen = computed(() => this.urlState.getParam('menu')() === 'open');\n */\n getParam(key: string): Signal<string | null> {\n return computed(() => (this.params()[key] as string | undefined) ?? null);\n }\n\n /**\n * Establece un único queryParam en la URL.\n *\n * @param key Nombre del parámetro\n * @param value Valor. Pasar `null` para eliminarlo de la URL.\n * @param replaceUrl Si true (default) usa replaceState — no ensucia el historial.\n * Pasar false para acciones que el usuario debe poder deshacer con Atrás.\n */\n setParam(key: string, value: string | null, replaceUrl = true): void {\n this._pendingParams[key] = value;\n this._pendingParamsState.set({ ...this._pendingParams });\n this._pendingReplaceUrl = this._pendingReplaceUrl && replaceUrl;\n this._schedulePendingNavigation();\n }\n\n private _schedulePendingNavigation(): void {\n if (this._batchScheduled) {\n return;\n }\n\n this._batchScheduled = true;\n queueMicrotask(() => {\n const pending = this._consumePendingNavigation();\n if (!pending) {\n return;\n }\n\n void this.router\n .navigate([], {\n queryParams: pending.params,\n queryParamsHandling: 'merge',\n replaceUrl: pending.replaceUrl,\n })\n .finally(() => this._finalizePendingNavigation(pending.params));\n });\n }\n\n private _consumePendingNavigation(): {\n params: Record<string, string | null>;\n replaceUrl: boolean;\n } | null {\n const hasPendingParams = Object.keys(this._pendingParams).length > 0;\n const replaceUrl = this._pendingReplaceUrl;\n const params = { ...this._pendingParams };\n\n this._pendingParams = {};\n this._pendingParamsState.set({});\n this._pendingReplaceUrl = true;\n this._batchScheduled = false;\n\n if (!hasPendingParams) {\n return null;\n }\n\n return { params, replaceUrl };\n }\n\n private _resetPendingNavigation(): void {\n this._pendingParams = {};\n this._pendingParamsState.set({});\n this._pendingReplaceUrl = true;\n this._batchScheduled = false;\n }\n\n private _finalizePendingNavigation(flushedParams: Record<string, string | null>): void {\n const currentVisiblePending = { ...this._pendingParamsState() };\n let changed = false;\n\n for (const [key, value] of Object.entries(flushedParams)) {\n if (Object.prototype.hasOwnProperty.call(this._pendingParams, key)) {\n continue;\n }\n\n if (currentVisiblePending[key] === value) {\n delete currentVisiblePending[key];\n changed = true;\n }\n }\n\n if (changed) {\n this._pendingParamsState.set(currentVisiblePending);\n }\n }\n\n /**\n * Actualiza múltiples queryParams en una sola navegación.\n *\n * @example\n * urlState.patchParams({ page: '1', q: 'Angular' });\n */\n patchParams(params: Record<string, string | null>, replaceUrl = true): void {\n const pending = this._consumePendingNavigation();\n const mergedParams = pending ? { ...pending.params, ...params } : params;\n this._pendingParamsState.set({ ...this._pendingParamsState(), ...mergedParams });\n void this.router\n .navigate([], {\n queryParams: mergedParams,\n queryParamsHandling: 'merge',\n replaceUrl: pending ? pending.replaceUrl && replaceUrl : replaceUrl,\n })\n .finally(() => this._finalizePendingNavigation(mergedParams));\n }\n\n /**\n * Elimina todos los queryParams de la URL de una vez.\n */\n clearParams(replaceUrl = true): void {\n this._resetPendingNavigation();\n void this.router.navigate([], {\n queryParams: {},\n replaceUrl,\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
@@ -1,9 +1,45 @@
1
1
  import * as i0 from '@angular/core';
2
- import { afterNextRender, input, output, computed, TemplateRef, ContentChild, ViewChild, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
2
+ import { input, computed, ChangeDetectionStrategy, Component, afterNextRender, output, TemplateRef, ContentChild, ViewChild, ViewEncapsulation } from '@angular/core';
3
3
  import * as i1 from '@angular/cdk/scrolling';
4
4
  import { ScrollingModule, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
5
5
  import { NgTemplateOutlet } from '@angular/common';
6
6
 
7
+ class NeuVirtualListRowComponent {
8
+ item = input.required(...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
9
+ index = input.required(...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
10
+ itemSize = input.required(...(ngDevMode ? [{ debugName: "itemSize" }] : /* istanbul ignore next */ []));
11
+ label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
12
+ template = input(null, ...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []));
13
+ context = computed(() => ({ $implicit: this.item(), index: this.index() }), ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
14
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuVirtualListRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
15
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuVirtualListRowComponent, isStandalone: true, selector: "neu-virtual-list-row", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, itemSize: { classPropertyName: "itemSize", publicName: "itemSize", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, template: { classPropertyName: "template", publicName: "template", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
16
+ @if (template(); as projectedTemplate) {
17
+ <ng-container [ngTemplateOutlet]="projectedTemplate" [ngTemplateOutletContext]="context()" />
18
+ } @else {
19
+ <div class="neu-virtual-list__item-default" [style.height.px]="itemSize()">
20
+ {{ label() }}
21
+ </div>
22
+ }
23
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
24
+ }
25
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuVirtualListRowComponent, decorators: [{
26
+ type: Component,
27
+ args: [{
28
+ selector: 'neu-virtual-list-row',
29
+ standalone: true,
30
+ imports: [NgTemplateOutlet],
31
+ changeDetection: ChangeDetectionStrategy.OnPush,
32
+ template: `
33
+ @if (template(); as projectedTemplate) {
34
+ <ng-container [ngTemplateOutlet]="projectedTemplate" [ngTemplateOutletContext]="context()" />
35
+ } @else {
36
+ <div class="neu-virtual-list__item-default" [style.height.px]="itemSize()">
37
+ {{ label() }}
38
+ </div>
39
+ }
40
+ `,
41
+ }]
42
+ }], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], itemSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemSize", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], template: [{ type: i0.Input, args: [{ isSignal: true, alias: "template", required: false }] }] } });
7
43
  /**
8
44
  * NeuralUI VirtualList
9
45
  *
@@ -57,12 +93,6 @@ class NeuVirtualListComponent {
57
93
  }
58
94
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuVirtualListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
59
95
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuVirtualListComponent, isStandalone: true, selector: "neu-virtual-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, itemSize: { classPropertyName: "itemSize", publicName: "itemSize", isSignal: true, isRequired: false, transformFunction: null }, visibleRows: { classPropertyName: "visibleRows", publicName: "visibleRows", isSignal: true, isRequired: false, transformFunction: null }, emptyLabel: { classPropertyName: "emptyLabel", publicName: "emptyLabel", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, host: { properties: { "style.height": "_containerHeight()" }, classAttribute: "neu-virtual-list" }, queries: [{ propertyName: "itemTemplate", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "_viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }], ngImport: i0, template: `
60
- <ng-template #defaultItem let-item>
61
- <div class="neu-virtual-list__item-default" [style.height.px]="itemSize()">
62
- {{ _defaultItemLabel(item) }}
63
- </div>
64
- </ng-template>
65
-
66
96
  <cdk-virtual-scroll-viewport
67
97
  class="neu-virtual-list__viewport"
68
98
  [itemSize]="itemSize()"
@@ -70,29 +100,26 @@ class NeuVirtualListComponent {
70
100
  tabindex="0"
71
101
  >
72
102
  <ng-container *cdkVirtualFor="let item of items(); let index = index; trackBy: _trackBy">
73
- <ng-container
74
- [ngTemplateOutlet]="_itemTemplateOrDefault(defaultItem)"
75
- [ngTemplateOutletContext]="_itemContext(item, index)"
103
+ <neu-virtual-list-row
104
+ [item]="item"
105
+ [index]="index"
106
+ [itemSize]="itemSize()"
107
+ [label]="_defaultItemLabel(item)"
108
+ [template]="itemTemplate"
76
109
  />
77
110
  </ng-container>
78
111
  </cdk-virtual-scroll-viewport>
79
112
  @if (_isEmpty()) {
80
113
  <div class="neu-virtual-list__empty" role="status">{{ emptyLabel() }}</div>
81
114
  }
82
- `, isInline: true, styles: [".neu-virtual-list{display:block;overflow:hidden}.neu-virtual-list__viewport{width:100%;overflow-y:auto}.neu-virtual-list__item-default{display:flex;align-items:center;padding:0 12px;border-bottom:1px solid var(--neu-border-color, #e5e7eb);font-size:.875rem;color:var(--neu-text-primary, #111)}.neu-virtual-list__empty{display:flex;align-items:center;justify-content:center;padding:32px;color:var(--neu-text-secondary, #6b7280);font-size:.875rem}\n"], dependencies: [{ 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"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
115
+ `, isInline: true, styles: [".neu-virtual-list{display:block;overflow:hidden}.neu-virtual-list__viewport{width:100%;overflow-y:auto}.neu-virtual-list__item-default{display:flex;align-items:center;padding:0 12px;border-bottom:1px solid var(--neu-border-color, #e5e7eb);font-size:.875rem;color:var(--neu-text-primary, #111)}.neu-virtual-list__empty{display:flex;align-items:center;justify-content:center;padding:32px;color:var(--neu-text-secondary, #6b7280);font-size:.875rem}\n"], dependencies: [{ 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"] }, { kind: "component", type: NeuVirtualListRowComponent, selector: "neu-virtual-list-row", inputs: ["item", "index", "itemSize", "label", "template"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
83
116
  }
84
117
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuVirtualListComponent, decorators: [{
85
118
  type: Component,
86
- args: [{ selector: 'neu-virtual-list', imports: [ScrollingModule, NgTemplateOutlet], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
119
+ args: [{ selector: 'neu-virtual-list', imports: [ScrollingModule, NeuVirtualListRowComponent], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
87
120
  class: 'neu-virtual-list',
88
121
  '[style.height]': '_containerHeight()',
89
122
  }, template: `
90
- <ng-template #defaultItem let-item>
91
- <div class="neu-virtual-list__item-default" [style.height.px]="itemSize()">
92
- {{ _defaultItemLabel(item) }}
93
- </div>
94
- </ng-template>
95
-
96
123
  <cdk-virtual-scroll-viewport
97
124
  class="neu-virtual-list__viewport"
98
125
  [itemSize]="itemSize()"
@@ -100,9 +127,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
100
127
  tabindex="0"
101
128
  >
102
129
  <ng-container *cdkVirtualFor="let item of items(); let index = index; trackBy: _trackBy">
103
- <ng-container
104
- [ngTemplateOutlet]="_itemTemplateOrDefault(defaultItem)"
105
- [ngTemplateOutletContext]="_itemContext(item, index)"
130
+ <neu-virtual-list-row
131
+ [item]="item"
132
+ [index]="index"
133
+ [itemSize]="itemSize()"
134
+ [label]="_defaultItemLabel(item)"
135
+ [template]="itemTemplate"
106
136
  />
107
137
  </ng-container>
108
138
  </cdk-virtual-scroll-viewport>
@@ -122,5 +152,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
122
152
  * Generated bundle index. Do not edit.
123
153
  */
124
154
 
125
- export { NeuVirtualListComponent };
155
+ export { NeuVirtualListComponent, NeuVirtualListRowComponent };
126
156
  //# sourceMappingURL=neural-ui-core-virtual-list.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"neural-ui-core-virtual-list.mjs","sources":["../../../../projects/ui-core/virtual-list/neu-virtual-list.component.ts","../../../../projects/ui-core/virtual-list/neural-ui-core-virtual-list.ts"],"sourcesContent":["import {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n ContentChild,\n TemplateRef,\n ViewChild,\n ViewEncapsulation,\n afterNextRender,\n computed,\n input,\n output,\n} from '@angular/core';\nimport { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling';\nimport { NgTemplateOutlet } from '@angular/common';\n\n/**\n * NeuralUI VirtualList\n *\n * Wrapper sobre CDK VirtualScrollViewport para listas de miles de ítems.\n * Proyecta contenido mediante `ng-template` con let-item.\n *\n * Uso:\n * <neu-virtual-list [items]=\"bigArray\" [itemSize]=\"40\">\n * <ng-template neuVirtualItem let-item>\n * <div class=\"my-row\">{{ item.name }}</div>\n * </ng-template>\n * </neu-virtual-list>\n */\n@Component({\n selector: 'neu-virtual-list',\n imports: [ScrollingModule, NgTemplateOutlet],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'neu-virtual-list',\n '[style.height]': '_containerHeight()',\n },\n template: `\n <ng-template #defaultItem let-item>\n <div class=\"neu-virtual-list__item-default\" [style.height.px]=\"itemSize()\">\n {{ _defaultItemLabel(item) }}\n </div>\n </ng-template>\n\n <cdk-virtual-scroll-viewport\n class=\"neu-virtual-list__viewport\"\n [itemSize]=\"itemSize()\"\n [style.height]=\"_containerHeight()\"\n tabindex=\"0\"\n >\n <ng-container *cdkVirtualFor=\"let item of items(); let index = index; trackBy: _trackBy\">\n <ng-container\n [ngTemplateOutlet]=\"_itemTemplateOrDefault(defaultItem)\"\n [ngTemplateOutletContext]=\"_itemContext(item, index)\"\n />\n </ng-container>\n </cdk-virtual-scroll-viewport>\n @if (_isEmpty()) {\n <div class=\"neu-virtual-list__empty\" role=\"status\">{{ emptyLabel() }}</div>\n }\n `,\n styleUrl: './neu-virtual-list.component.scss',\n})\nexport class NeuVirtualListComponent<T = unknown> implements AfterViewInit {\n /** Referencia al viewport CDK — para forzar remeasure tras render / CDK viewport ref — to force remeasure after render */\n @ViewChild(CdkVirtualScrollViewport) private readonly _viewport?: CdkVirtualScrollViewport;\n\n constructor() {\n // Fuerza checkViewportSize() tras el primer paint completo (necesario en modo zoneless)\n // Forces checkViewportSize() after the first full paint (needed in zoneless mode)\n afterNextRender(() => this._viewport?.checkViewportSize());\n }\n\n ngAfterViewInit(): void {\n // Segundo intento de medición sincrona por si afterNextRender no cubre todos los casos\n // Second synchronous measurement pass as a fallback\n this._viewport?.checkViewportSize();\n }\n /** Array de ítems / Item array */\n readonly items = input<T[]>([]);\n\n /** Altura de cada fila en px / Row height in px */\n readonly itemSize = input<number>(48);\n\n /** Número de filas visibles / Visible row count (sets container height) */\n readonly visibleRows = input<number>(10);\n\n /** Texto cuando la lista está vacía / Empty state text */\n readonly emptyLabel = input<string>('Sin resultados');\n\n /** Función de tracking / Tracking function */\n readonly trackBy = input<(index: number, item: T) => unknown>((i) => i);\n\n /** Emitido al hacer click en un ítem / Emitted on item click */\n readonly itemClick = output<{ item: T; index: number }>();\n\n @ContentChild(TemplateRef) itemTemplate: TemplateRef<{ $implicit: T; index: number }> | null =\n null;\n\n readonly _containerHeight = computed(() => `${this.itemSize() * this.visibleRows()}px`);\n readonly _isEmpty = computed(() => this.items().length === 0);\n\n _trackBy = (index: number, item: T) => this.trackBy()(index, item);\n\n protected _itemTemplateOrDefault(\n fallback: TemplateRef<{ $implicit: T; index: number }>,\n ): TemplateRef<{ $implicit: T; index: number }> {\n return this.itemTemplate ?? fallback;\n }\n\n protected _itemContext(item: T, index: number): { $implicit: T; index: number } {\n return { $implicit: item, index };\n }\n\n protected _defaultItemLabel(item: T): string {\n return String(item);\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-virtual-list.mjs","sources":["../../../../projects/ui-core/virtual-list/neu-virtual-list.component.ts","../../../../projects/ui-core/virtual-list/neural-ui-core-virtual-list.ts"],"sourcesContent":["import {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n ContentChild,\n TemplateRef,\n ViewChild,\n ViewEncapsulation,\n afterNextRender,\n computed,\n input,\n output,\n} from '@angular/core';\nimport { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling';\nimport { NgTemplateOutlet } from '@angular/common';\n\n@Component({\n selector: 'neu-virtual-list-row',\n standalone: true,\n imports: [NgTemplateOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (template(); as projectedTemplate) {\n <ng-container [ngTemplateOutlet]=\"projectedTemplate\" [ngTemplateOutletContext]=\"context()\" />\n } @else {\n <div class=\"neu-virtual-list__item-default\" [style.height.px]=\"itemSize()\">\n {{ label() }}\n </div>\n }\n `,\n})\nexport class NeuVirtualListRowComponent {\n readonly item = input.required<unknown>();\n readonly index = input.required<number>();\n readonly itemSize = input.required<number>();\n readonly label = input.required<string>();\n readonly template = input<TemplateRef<{ $implicit: unknown; index: number }> | null>(null);\n\n readonly context = computed(() => ({ $implicit: this.item(), index: this.index() }));\n}\n\n/**\n * NeuralUI VirtualList\n *\n * Wrapper sobre CDK VirtualScrollViewport para listas de miles de ítems.\n * Proyecta contenido mediante `ng-template` con let-item.\n *\n * Uso:\n * <neu-virtual-list [items]=\"bigArray\" [itemSize]=\"40\">\n * <ng-template neuVirtualItem let-item>\n * <div class=\"my-row\">{{ item.name }}</div>\n * </ng-template>\n * </neu-virtual-list>\n */\n@Component({\n selector: 'neu-virtual-list',\n imports: [ScrollingModule, NeuVirtualListRowComponent],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'neu-virtual-list',\n '[style.height]': '_containerHeight()',\n },\n template: `\n <cdk-virtual-scroll-viewport\n class=\"neu-virtual-list__viewport\"\n [itemSize]=\"itemSize()\"\n [style.height]=\"_containerHeight()\"\n tabindex=\"0\"\n >\n <ng-container *cdkVirtualFor=\"let item of items(); let index = index; trackBy: _trackBy\">\n <neu-virtual-list-row\n [item]=\"item\"\n [index]=\"index\"\n [itemSize]=\"itemSize()\"\n [label]=\"_defaultItemLabel(item)\"\n [template]=\"itemTemplate\"\n />\n </ng-container>\n </cdk-virtual-scroll-viewport>\n @if (_isEmpty()) {\n <div class=\"neu-virtual-list__empty\" role=\"status\">{{ emptyLabel() }}</div>\n }\n `,\n styleUrl: './neu-virtual-list.component.scss',\n})\nexport class NeuVirtualListComponent<T = unknown> implements AfterViewInit {\n /** Referencia al viewport CDK — para forzar remeasure tras render / CDK viewport ref — to force remeasure after render */\n @ViewChild(CdkVirtualScrollViewport) private readonly _viewport?: CdkVirtualScrollViewport;\n\n constructor() {\n // Fuerza checkViewportSize() tras el primer paint completo (necesario en modo zoneless)\n // Forces checkViewportSize() after the first full paint (needed in zoneless mode)\n afterNextRender(() => this._viewport?.checkViewportSize());\n }\n\n ngAfterViewInit(): void {\n // Segundo intento de medición sincrona por si afterNextRender no cubre todos los casos\n // Second synchronous measurement pass as a fallback\n this._viewport?.checkViewportSize();\n }\n /** Array de ítems / Item array */\n readonly items = input<T[]>([]);\n\n /** Altura de cada fila en px / Row height in px */\n readonly itemSize = input<number>(48);\n\n /** Número de filas visibles / Visible row count (sets container height) */\n readonly visibleRows = input<number>(10);\n\n /** Texto cuando la lista está vacía / Empty state text */\n readonly emptyLabel = input<string>('Sin resultados');\n\n /** Función de tracking / Tracking function */\n readonly trackBy = input<(index: number, item: T) => unknown>((i) => i);\n\n /** Emitido al hacer click en un ítem / Emitted on item click */\n readonly itemClick = output<{ item: T; index: number }>();\n\n @ContentChild(TemplateRef) itemTemplate: TemplateRef<{ $implicit: T; index: number }> | null =\n null;\n\n readonly _containerHeight = computed(() => `${this.itemSize() * this.visibleRows()}px`);\n readonly _isEmpty = computed(() => this.items().length === 0);\n\n _trackBy = (index: number, item: T) => this.trackBy()(index, item);\n\n protected _itemTemplateOrDefault(\n fallback: TemplateRef<{ $implicit: T; index: number }>,\n ): TemplateRef<{ $implicit: T; index: number }> {\n return this.itemTemplate ?? fallback;\n }\n\n protected _itemContext(item: T, index: number): { $implicit: T; index: number } {\n return { $implicit: item, index };\n }\n\n protected _defaultItemLabel(item: T): string {\n return String(item);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neural-ui/core",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "Modern Angular UI component library built with signals, standalone components, and OnPush change detection.",
5
5
  "author": "PedroMorenoTrujillo",
6
6
  "keywords": [