@neural-ui/core 1.3.1 → 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.
- package/README.md +12 -6
- package/fesm2022/neural-ui-core-accordion.mjs +8 -6
- package/fesm2022/neural-ui-core-accordion.mjs.map +1 -1
- package/fesm2022/neural-ui-core-autocomplete.mjs +121 -29
- package/fesm2022/neural-ui-core-autocomplete.mjs.map +1 -1
- package/fesm2022/neural-ui-core-badge.mjs +2 -2
- package/fesm2022/neural-ui-core-badge.mjs.map +1 -1
- package/fesm2022/neural-ui-core-button.mjs +2 -2
- package/fesm2022/neural-ui-core-button.mjs.map +1 -1
- package/fesm2022/neural-ui-core-chip.mjs +2 -2
- package/fesm2022/neural-ui-core-chip.mjs.map +1 -1
- package/fesm2022/neural-ui-core-color-picker.mjs +3 -9
- package/fesm2022/neural-ui-core-color-picker.mjs.map +1 -1
- package/fesm2022/neural-ui-core-filter-bar.mjs +2 -2
- package/fesm2022/neural-ui-core-filter-bar.mjs.map +1 -1
- package/fesm2022/neural-ui-core-modal.mjs +81 -31
- package/fesm2022/neural-ui-core-modal.mjs.map +1 -1
- package/fesm2022/neural-ui-core-multiselect.mjs +258 -99
- package/fesm2022/neural-ui-core-multiselect.mjs.map +1 -1
- package/fesm2022/neural-ui-core-nav.mjs +4 -6
- package/fesm2022/neural-ui-core-nav.mjs.map +1 -1
- package/fesm2022/neural-ui-core-select.mjs +247 -94
- package/fesm2022/neural-ui-core-select.mjs.map +1 -1
- package/fesm2022/neural-ui-core-sidebar.mjs +3 -2
- package/fesm2022/neural-ui-core-sidebar.mjs.map +1 -1
- package/fesm2022/neural-ui-core-slider.mjs +2 -2
- package/fesm2022/neural-ui-core-slider.mjs.map +1 -1
- package/fesm2022/neural-ui-core-split-button.mjs +2 -2
- package/fesm2022/neural-ui-core-split-button.mjs.map +1 -1
- package/fesm2022/neural-ui-core-table.mjs +168 -21
- package/fesm2022/neural-ui-core-table.mjs.map +1 -1
- package/fesm2022/neural-ui-core-tabs.mjs +13 -4
- package/fesm2022/neural-ui-core-tabs.mjs.map +1 -1
- package/fesm2022/neural-ui-core-toolbar.mjs +2 -2
- package/fesm2022/neural-ui-core-toolbar.mjs.map +1 -1
- package/fesm2022/neural-ui-core-url-state.mjs +90 -10
- package/fesm2022/neural-ui-core-url-state.mjs.map +1 -1
- package/fesm2022/neural-ui-core-virtual-list.mjs +52 -22
- package/fesm2022/neural-ui-core-virtual-list.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/_tokens.scss +8 -8
- package/types/neural-ui-core-autocomplete.d.ts +10 -1
- package/types/neural-ui-core-color-picker.d.ts +0 -1
- package/types/neural-ui-core-modal.d.ts +22 -16
- package/types/neural-ui-core-multiselect.d.ts +12 -1
- package/types/neural-ui-core-select.d.ts +12 -1
- package/types/neural-ui-core-sidebar.d.ts +1 -0
- package/types/neural-ui-core-table.d.ts +23 -3
- package/types/neural-ui-core-tabs.d.ts +1 -0
- package/types/neural-ui-core-url-state.d.ts +9 -0
- 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 =
|
|
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
|
|
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());
|
|
@@ -123,6 +130,8 @@ class NeuTabsComponent {
|
|
|
123
130
|
const target = event.target;
|
|
124
131
|
if (!target?.closest('.neu-tabs__nav'))
|
|
125
132
|
return;
|
|
133
|
+
if (target.closest('.neu-tabs__tab'))
|
|
134
|
+
return;
|
|
126
135
|
const nav = event.currentTarget;
|
|
127
136
|
this._dragPointerId = event.pointerId;
|
|
128
137
|
this._dragStartX = event.clientX;
|
|
@@ -224,7 +233,7 @@ class NeuTabsComponent {
|
|
|
224
233
|
<ng-content />
|
|
225
234
|
</div>
|
|
226
235
|
</div>
|
|
227
|
-
`, 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-
|
|
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 });
|
|
228
237
|
}
|
|
229
238
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuTabsComponent, decorators: [{
|
|
230
239
|
type: Component,
|
|
@@ -275,7 +284,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
275
284
|
<ng-content />
|
|
276
285
|
</div>
|
|
277
286
|
</div>
|
|
278
|
-
`, 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-
|
|
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"] }]
|
|
279
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"] }] } });
|
|
280
289
|
// ----------------------------------------------------------------
|
|
281
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\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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"}
|
|
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-
|
|
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-
|
|
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,
|
|
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.
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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 {
|
|
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
|
-
<
|
|
74
|
-
[
|
|
75
|
-
[
|
|
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: "
|
|
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,
|
|
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
|
-
<
|
|
104
|
-
[
|
|
105
|
-
[
|
|
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":";;;;;;MA+Ba,0BAA0B,CAAA;AAC5B,IAAA,IAAI,GAAG,KAAK,CAAC,QAAQ,0EAAW;AAChC,IAAA,KAAK,GAAG,KAAK,CAAC,QAAQ,2EAAU;AAChC,IAAA,QAAQ,GAAG,KAAK,CAAC,QAAQ,8EAAU;AACnC,IAAA,KAAK,GAAG,KAAK,CAAC,QAAQ,2EAAU;AAChC,IAAA,QAAQ,GAAG,KAAK,CAA4D,IAAI,+EAAC;IAEjF,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,8BAAA,EAAA,CAAA,CAAC;uGAPzE,0BAA0B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA1B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,0BAA0B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,sBAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,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,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,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,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAV3B;;;;;;;;AAQT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAVS,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAYf,0BAA0B,EAAA,UAAA,EAAA,CAAA;kBAftC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,sBAAsB;AAChC,oBAAA,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,gBAAgB,CAAC;oBAC3B,eAAe,EAAE,uBAAuB,CAAC,MAAM;AAC/C,oBAAA,QAAQ,EAAE;;;;;;;;AAQT,EAAA,CAAA;AACF,iBAAA;;AAWD;;;;;;;;;;;;AAYG;MAiCU,uBAAuB,CAAA;;AAEoB,IAAA,SAAS;AAE/D,IAAA,WAAA,GAAA;;;QAGE,eAAe,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC5D;IAEA,eAAe,GAAA;;;AAGb,QAAA,IAAI,CAAC,SAAS,EAAE,iBAAiB,EAAE;IACrC;;AAES,IAAA,KAAK,GAAG,KAAK,CAAM,EAAE,4EAAC;;AAGtB,IAAA,QAAQ,GAAG,KAAK,CAAS,EAAE,+EAAC;;AAG5B,IAAA,WAAW,GAAG,KAAK,CAAS,EAAE,kFAAC;;AAG/B,IAAA,UAAU,GAAG,KAAK,CAAS,gBAAgB,iFAAC;;IAG5C,OAAO,GAAG,KAAK,CAAsC,CAAC,CAAC,KAAK,CAAC,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,SAAA,EAAA,CAAA,8BAAA,EAAA,CAAA,CAAC;;IAG9D,SAAS,GAAG,MAAM,EAA8B;IAE9B,YAAY,GACrC,IAAI;AAEG,IAAA,gBAAgB,GAAG,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA,EAAA,CAAI,uFAAC;AAC9E,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,KAAK,CAAC,+EAAC;AAE7D,IAAA,QAAQ,GAAG,CAAC,KAAa,EAAE,IAAO,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;AAExD,IAAA,sBAAsB,CAC9B,QAAsD,EAAA;AAEtD,QAAA,OAAO,IAAI,CAAC,YAAY,IAAI,QAAQ;IACtC;IAEU,YAAY,CAAC,IAAO,EAAE,KAAa,EAAA;AAC3C,QAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE;IACnC;AAEU,IAAA,iBAAiB,CAAC,IAAO,EAAA;AACjC,QAAA,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB;uGArDW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAvB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,uBAAuB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,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,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,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,OAAA,EAAA,EAAA,SAAA,EAAA,WAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,cAAA,EAAA,oBAAA,EAAA,EAAA,cAAA,EAAA,kBAAA,EAAA,EAAA,OAAA,EAAA,CAAA,EAAA,YAAA,EAAA,cAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAiCpB,WAAW,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,WAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EA/Bd,wBAAwB,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAzBzB;;;;;;;;;;;;;;;;;;;;GAoBT,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,icAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EA3BS,eAAe,4jBAzBd,0BAA0B,EAAA,QAAA,EAAA,sBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAuD1B,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAhCnC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,kBAAkB,EAAA,OAAA,EACnB,CAAC,eAAe,EAAE,0BAA0B,CAAC,EAAA,aAAA,EACvC,iBAAiB,CAAC,IAAI,EAAA,eAAA,EACpB,uBAAuB,CAAC,MAAM,EAAA,IAAA,EACzC;AACJ,wBAAA,KAAK,EAAE,kBAAkB;AACzB,wBAAA,gBAAgB,EAAE,oBAAoB;qBACvC,EAAA,QAAA,EACS;;;;;;;;;;;;;;;;;;;;AAoBT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,icAAA,CAAA,EAAA;;sBAKA,SAAS;uBAAC,wBAAwB;;sBA+BlC,YAAY;uBAAC,WAAW;;;ACvH3B;;AAEG;;;;"}
|
package/package.json
CHANGED