@neural-ui/core 1.2.1 → 1.3.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 (239) hide show
  1. package/README.md +56 -88
  2. package/accordion/package.json +4 -0
  3. package/alert/package.json +4 -0
  4. package/autocomplete/package.json +4 -0
  5. package/avatar/package.json +4 -0
  6. package/badge/package.json +4 -0
  7. package/block-ui/package.json +4 -0
  8. package/breadcrumb/package.json +4 -0
  9. package/button/package.json +4 -0
  10. package/card/package.json +4 -0
  11. package/chart/package.json +4 -0
  12. package/checkbox/package.json +4 -0
  13. package/chip/package.json +4 -0
  14. package/code-block/package.json +4 -0
  15. package/color-picker/package.json +4 -0
  16. package/command-palette/package.json +4 -0
  17. package/confirm-dialog/package.json +4 -0
  18. package/context-menu/package.json +4 -0
  19. package/dashboard-grid/package.json +4 -0
  20. package/date-input/package.json +4 -0
  21. package/divider/package.json +4 -0
  22. package/empty-state/package.json +4 -0
  23. package/fesm2022/neural-ui-core-accordion.mjs +162 -0
  24. package/fesm2022/neural-ui-core-accordion.mjs.map +1 -0
  25. package/fesm2022/neural-ui-core-alert.mjs +116 -0
  26. package/fesm2022/neural-ui-core-alert.mjs.map +1 -0
  27. package/fesm2022/neural-ui-core-autocomplete.mjs +332 -0
  28. package/fesm2022/neural-ui-core-autocomplete.mjs.map +1 -0
  29. package/fesm2022/neural-ui-core-avatar.mjs +109 -0
  30. package/fesm2022/neural-ui-core-avatar.mjs.map +1 -0
  31. package/fesm2022/neural-ui-core-badge.mjs +54 -0
  32. package/fesm2022/neural-ui-core-badge.mjs.map +1 -0
  33. package/fesm2022/neural-ui-core-block-ui.mjs +95 -0
  34. package/fesm2022/neural-ui-core-block-ui.mjs.map +1 -0
  35. package/fesm2022/neural-ui-core-breadcrumb.mjs +84 -0
  36. package/fesm2022/neural-ui-core-breadcrumb.mjs.map +1 -0
  37. package/fesm2022/neural-ui-core-button.mjs +125 -0
  38. package/fesm2022/neural-ui-core-button.mjs.map +1 -0
  39. package/fesm2022/neural-ui-core-card.mjs +69 -0
  40. package/fesm2022/neural-ui-core-card.mjs.map +1 -0
  41. package/fesm2022/neural-ui-core-chart.mjs +287 -0
  42. package/fesm2022/neural-ui-core-chart.mjs.map +1 -0
  43. package/fesm2022/neural-ui-core-checkbox.mjs +138 -0
  44. package/fesm2022/neural-ui-core-checkbox.mjs.map +1 -0
  45. package/fesm2022/neural-ui-core-chip.mjs +130 -0
  46. package/fesm2022/neural-ui-core-chip.mjs.map +1 -0
  47. package/fesm2022/neural-ui-core-code-block.mjs +250 -0
  48. package/fesm2022/neural-ui-core-code-block.mjs.map +1 -0
  49. package/fesm2022/neural-ui-core-color-picker.mjs +435 -0
  50. package/fesm2022/neural-ui-core-color-picker.mjs.map +1 -0
  51. package/fesm2022/neural-ui-core-command-palette.mjs +235 -0
  52. package/fesm2022/neural-ui-core-command-palette.mjs.map +1 -0
  53. package/fesm2022/neural-ui-core-confirm-dialog.mjs +118 -0
  54. package/fesm2022/neural-ui-core-confirm-dialog.mjs.map +1 -0
  55. package/fesm2022/neural-ui-core-context-menu.mjs +158 -0
  56. package/fesm2022/neural-ui-core-context-menu.mjs.map +1 -0
  57. package/fesm2022/neural-ui-core-dashboard-grid.mjs +144 -0
  58. package/fesm2022/neural-ui-core-dashboard-grid.mjs.map +1 -0
  59. package/fesm2022/neural-ui-core-date-input.mjs +1332 -0
  60. package/fesm2022/neural-ui-core-date-input.mjs.map +1 -0
  61. package/fesm2022/neural-ui-core-divider.mjs +54 -0
  62. package/fesm2022/neural-ui-core-divider.mjs.map +1 -0
  63. package/fesm2022/neural-ui-core-empty-state.mjs +84 -0
  64. package/fesm2022/neural-ui-core-empty-state.mjs.map +1 -0
  65. package/fesm2022/neural-ui-core-filter-bar.mjs +118 -0
  66. package/fesm2022/neural-ui-core-filter-bar.mjs.map +1 -0
  67. package/fesm2022/neural-ui-core-icon.mjs +50 -0
  68. package/fesm2022/neural-ui-core-icon.mjs.map +1 -0
  69. package/fesm2022/neural-ui-core-image-viewer.mjs +309 -0
  70. package/fesm2022/neural-ui-core-image-viewer.mjs.map +1 -0
  71. package/fesm2022/neural-ui-core-input-otp.mjs +192 -0
  72. package/fesm2022/neural-ui-core-input-otp.mjs.map +1 -0
  73. package/fesm2022/neural-ui-core-input.mjs +320 -0
  74. package/fesm2022/neural-ui-core-input.mjs.map +1 -0
  75. package/fesm2022/neural-ui-core-knob.mjs +323 -0
  76. package/fesm2022/neural-ui-core-knob.mjs.map +1 -0
  77. package/fesm2022/neural-ui-core-meter-group.mjs +122 -0
  78. package/fesm2022/neural-ui-core-meter-group.mjs.map +1 -0
  79. package/fesm2022/neural-ui-core-modal.mjs +156 -0
  80. package/fesm2022/neural-ui-core-modal.mjs.map +1 -0
  81. package/fesm2022/neural-ui-core-multiselect.mjs +748 -0
  82. package/fesm2022/neural-ui-core-multiselect.mjs.map +1 -0
  83. package/fesm2022/neural-ui-core-nav.mjs +952 -0
  84. package/fesm2022/neural-ui-core-nav.mjs.map +1 -0
  85. package/fesm2022/neural-ui-core-notification-center.mjs +264 -0
  86. package/fesm2022/neural-ui-core-notification-center.mjs.map +1 -0
  87. package/fesm2022/neural-ui-core-number-input.mjs +331 -0
  88. package/fesm2022/neural-ui-core-number-input.mjs.map +1 -0
  89. package/fesm2022/neural-ui-core-pagination.mjs +198 -0
  90. package/fesm2022/neural-ui-core-pagination.mjs.map +1 -0
  91. package/fesm2022/neural-ui-core-popover.mjs +207 -0
  92. package/fesm2022/neural-ui-core-popover.mjs.map +1 -0
  93. package/fesm2022/neural-ui-core-progress-bar.mjs +105 -0
  94. package/fesm2022/neural-ui-core-progress-bar.mjs.map +1 -0
  95. package/fesm2022/neural-ui-core-radio.mjs +171 -0
  96. package/fesm2022/neural-ui-core-radio.mjs.map +1 -0
  97. package/fesm2022/neural-ui-core-rating.mjs +151 -0
  98. package/fesm2022/neural-ui-core-rating.mjs.map +1 -0
  99. package/fesm2022/neural-ui-core-select.mjs +638 -0
  100. package/fesm2022/neural-ui-core-select.mjs.map +1 -0
  101. package/fesm2022/neural-ui-core-sidebar.mjs +214 -0
  102. package/fesm2022/neural-ui-core-sidebar.mjs.map +1 -0
  103. package/fesm2022/neural-ui-core-skeleton.mjs +40 -0
  104. package/fesm2022/neural-ui-core-skeleton.mjs.map +1 -0
  105. package/fesm2022/neural-ui-core-slider.mjs +146 -0
  106. package/fesm2022/neural-ui-core-slider.mjs.map +1 -0
  107. package/fesm2022/neural-ui-core-spinner.mjs +113 -0
  108. package/fesm2022/neural-ui-core-spinner.mjs.map +1 -0
  109. package/fesm2022/neural-ui-core-split-button.mjs +252 -0
  110. package/fesm2022/neural-ui-core-split-button.mjs.map +1 -0
  111. package/fesm2022/neural-ui-core-splitter.mjs +174 -0
  112. package/fesm2022/neural-ui-core-splitter.mjs.map +1 -0
  113. package/fesm2022/neural-ui-core-stats-card.mjs +163 -0
  114. package/fesm2022/neural-ui-core-stats-card.mjs.map +1 -0
  115. package/fesm2022/neural-ui-core-stepper.mjs +204 -0
  116. package/fesm2022/neural-ui-core-stepper.mjs.map +1 -0
  117. package/fesm2022/neural-ui-core-switch.mjs +111 -0
  118. package/fesm2022/neural-ui-core-switch.mjs.map +1 -0
  119. package/fesm2022/neural-ui-core-table.mjs +1860 -0
  120. package/fesm2022/neural-ui-core-table.mjs.map +1 -0
  121. package/fesm2022/neural-ui-core-tabs.mjs +246 -0
  122. package/fesm2022/neural-ui-core-tabs.mjs.map +1 -0
  123. package/fesm2022/neural-ui-core-textarea.mjs +188 -0
  124. package/fesm2022/neural-ui-core-textarea.mjs.map +1 -0
  125. package/fesm2022/neural-ui-core-timeline.mjs +117 -0
  126. package/fesm2022/neural-ui-core-timeline.mjs.map +1 -0
  127. package/fesm2022/neural-ui-core-toast.mjs +171 -0
  128. package/fesm2022/neural-ui-core-toast.mjs.map +1 -0
  129. package/fesm2022/neural-ui-core-toggle-button-group.mjs +162 -0
  130. package/fesm2022/neural-ui-core-toggle-button-group.mjs.map +1 -0
  131. package/fesm2022/neural-ui-core-toolbar.mjs +67 -0
  132. package/fesm2022/neural-ui-core-toolbar.mjs.map +1 -0
  133. package/fesm2022/neural-ui-core-tooltip.mjs +151 -0
  134. package/fesm2022/neural-ui-core-tooltip.mjs.map +1 -0
  135. package/fesm2022/neural-ui-core-url-state.mjs +96 -0
  136. package/fesm2022/neural-ui-core-url-state.mjs.map +1 -0
  137. package/fesm2022/neural-ui-core-virtual-list.mjs +126 -0
  138. package/fesm2022/neural-ui-core-virtual-list.mjs.map +1 -0
  139. package/fesm2022/neural-ui-core.mjs +11 -8544
  140. package/fesm2022/neural-ui-core.mjs.map +1 -1
  141. package/filter-bar/package.json +4 -0
  142. package/icon/package.json +4 -0
  143. package/image-viewer/package.json +4 -0
  144. package/input/package.json +4 -0
  145. package/input-otp/package.json +4 -0
  146. package/knob/package.json +4 -0
  147. package/meter-group/package.json +4 -0
  148. package/modal/package.json +4 -0
  149. package/multiselect/package.json +4 -0
  150. package/nav/package.json +4 -0
  151. package/notification-center/package.json +4 -0
  152. package/number-input/package.json +4 -0
  153. package/package.json +252 -5
  154. package/pagination/package.json +4 -0
  155. package/popover/package.json +4 -0
  156. package/progress-bar/package.json +4 -0
  157. package/radio/package.json +4 -0
  158. package/rating/package.json +4 -0
  159. package/select/package.json +4 -0
  160. package/sidebar/package.json +4 -0
  161. package/skeleton/package.json +4 -0
  162. package/slider/package.json +4 -0
  163. package/spinner/package.json +4 -0
  164. package/split-button/package.json +4 -0
  165. package/splitter/package.json +4 -0
  166. package/stats-card/package.json +4 -0
  167. package/stepper/package.json +4 -0
  168. package/styles/_tokens.scss +202 -0
  169. package/styles.scss +1 -0
  170. package/switch/package.json +4 -0
  171. package/table/package.json +4 -0
  172. package/tabs/package.json +4 -0
  173. package/textarea/package.json +4 -0
  174. package/timeline/package.json +4 -0
  175. package/toast/package.json +4 -0
  176. package/toggle-button-group/package.json +4 -0
  177. package/toolbar/package.json +4 -0
  178. package/tooltip/package.json +4 -0
  179. package/types/neural-ui-core-accordion.d.ts +55 -0
  180. package/types/neural-ui-core-alert.d.ts +47 -0
  181. package/types/neural-ui-core-autocomplete.d.ts +69 -0
  182. package/types/neural-ui-core-avatar.d.ts +39 -0
  183. package/types/neural-ui-core-badge.d.ts +36 -0
  184. package/types/neural-ui-core-block-ui.d.ts +46 -0
  185. package/types/neural-ui-core-breadcrumb.d.ts +38 -0
  186. package/types/neural-ui-core-button.d.ts +55 -0
  187. package/types/neural-ui-core-card.d.ts +37 -0
  188. package/types/neural-ui-core-chart.d.ts +236 -0
  189. package/types/neural-ui-core-checkbox.d.ts +33 -0
  190. package/types/neural-ui-core-chip.d.ts +53 -0
  191. package/types/neural-ui-core-code-block.d.ts +55 -0
  192. package/types/neural-ui-core-color-picker.d.ts +55 -0
  193. package/types/neural-ui-core-command-palette.d.ts +56 -0
  194. package/types/neural-ui-core-confirm-dialog.d.ts +50 -0
  195. package/types/neural-ui-core-context-menu.d.ts +66 -0
  196. package/types/neural-ui-core-dashboard-grid.d.ts +41 -0
  197. package/types/neural-ui-core-date-input.d.ts +178 -0
  198. package/types/neural-ui-core-divider.d.ts +20 -0
  199. package/types/neural-ui-core-empty-state.d.ts +32 -0
  200. package/types/neural-ui-core-filter-bar.d.ts +49 -0
  201. package/types/neural-ui-core-icon.d.ts +33 -0
  202. package/types/neural-ui-core-image-viewer.d.ts +67 -0
  203. package/types/neural-ui-core-input-otp.d.ts +49 -0
  204. package/types/neural-ui-core-input.d.ts +86 -0
  205. package/types/neural-ui-core-knob.d.ts +68 -0
  206. package/types/neural-ui-core-meter-group.d.ts +52 -0
  207. package/types/neural-ui-core-modal.d.ts +54 -0
  208. package/types/neural-ui-core-multiselect.d.ts +129 -0
  209. package/types/neural-ui-core-nav.d.ts +69 -0
  210. package/types/neural-ui-core-notification-center.d.ts +60 -0
  211. package/types/neural-ui-core-number-input.d.ts +63 -0
  212. package/types/neural-ui-core-pagination.d.ts +30 -0
  213. package/types/neural-ui-core-popover.d.ts +73 -0
  214. package/types/neural-ui-core-progress-bar.d.ts +35 -0
  215. package/types/neural-ui-core-radio.d.ts +51 -0
  216. package/types/neural-ui-core-rating.d.ts +34 -0
  217. package/types/neural-ui-core-select.d.ts +161 -0
  218. package/types/neural-ui-core-sidebar.d.ts +57 -0
  219. package/types/neural-ui-core-skeleton.d.ts +22 -0
  220. package/types/neural-ui-core-slider.d.ts +42 -0
  221. package/types/neural-ui-core-spinner.d.ts +38 -0
  222. package/types/neural-ui-core-split-button.d.ts +65 -0
  223. package/types/neural-ui-core-splitter.d.ts +28 -0
  224. package/types/neural-ui-core-stats-card.d.ts +39 -0
  225. package/types/neural-ui-core-stepper.d.ts +51 -0
  226. package/types/neural-ui-core-switch.d.ts +34 -0
  227. package/types/neural-ui-core-table.d.ts +282 -0
  228. package/types/neural-ui-core-tabs.d.ts +76 -0
  229. package/types/neural-ui-core-textarea.d.ts +52 -0
  230. package/types/neural-ui-core-timeline.d.ts +33 -0
  231. package/types/neural-ui-core-toast.d.ts +70 -0
  232. package/types/neural-ui-core-toggle-button-group.d.ts +63 -0
  233. package/types/neural-ui-core-toolbar.d.ts +36 -0
  234. package/types/neural-ui-core-tooltip.d.ts +48 -0
  235. package/types/neural-ui-core-url-state.d.ts +58 -0
  236. package/types/neural-ui-core-virtual-list.d.ts +60 -0
  237. package/types/neural-ui-core.d.ts +3 -2105
  238. package/url-state/package.json +4 -0
  239. package/virtual-list/package.json +4 -0
@@ -0,0 +1,309 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, signal, computed, ChangeDetectionStrategy, ViewEncapsulation, Component, input, Injector, Directive } from '@angular/core';
3
+ import { Overlay } from '@angular/cdk/overlay';
4
+ import { ComponentPortal } from '@angular/cdk/portal';
5
+
6
+ const NEU_IV_DATA = new InjectionToken('NEU_IV_DATA');
7
+ /**
8
+ * Componente interno del overlay del visor de imágenes.
9
+ * No debe usarse directamente — es instanciado por la directiva.
10
+ */
11
+ class NeuImageViewerOverlayComponent {
12
+ _data = inject(NEU_IV_DATA);
13
+ _index = signal(this._data.initialIndex, ...(ngDevMode ? [{ debugName: "_index" }] : /* istanbul ignore next */ []));
14
+ _scale = signal(1, ...(ngDevMode ? [{ debugName: "_scale" }] : /* istanbul ignore next */ []));
15
+ _panX = signal(0, ...(ngDevMode ? [{ debugName: "_panX" }] : /* istanbul ignore next */ []));
16
+ _panY = signal(0, ...(ngDevMode ? [{ debugName: "_panY" }] : /* istanbul ignore next */ []));
17
+ _dragging = false;
18
+ _dragStart = { x: 0, y: 0, px: 0, py: 0 };
19
+ _current = computed(() => this._data.items[this._index()], ...(ngDevMode ? [{ debugName: "_current" }] : /* istanbul ignore next */ []));
20
+ _transform = computed(() => `translate(${this._panX()}px, ${this._panY()}px) scale(${this._scale()})`, ...(ngDevMode ? [{ debugName: "_transform" }] : /* istanbul ignore next */ []));
21
+ _navigate(dir) {
22
+ this._index.update((i) => Math.max(0, Math.min(this._data.items.length - 1, i + dir)));
23
+ this._resetZoom();
24
+ }
25
+ _zoom(delta) {
26
+ this._scale.update((s) => Math.max(0.25, Math.min(5, s + delta)));
27
+ }
28
+ _resetZoom() {
29
+ this._scale.set(1);
30
+ this._panX.set(0);
31
+ this._panY.set(0);
32
+ }
33
+ _onWheel(e) {
34
+ e.preventDefault();
35
+ this._zoom(e.deltaY < 0 ? 0.1 : -0.1);
36
+ }
37
+ _onMouseDown(e) {
38
+ if (e.button !== 0)
39
+ return;
40
+ this._dragging = true;
41
+ this._dragStart = {
42
+ x: e.clientX,
43
+ y: e.clientY,
44
+ px: this._panX(),
45
+ py: this._panY(),
46
+ };
47
+ const onMove = (ev) => {
48
+ if (!this._dragging)
49
+ return;
50
+ this._panX.set(this._dragStart.px + ev.clientX - this._dragStart.x);
51
+ this._panY.set(this._dragStart.py + ev.clientY - this._dragStart.y);
52
+ };
53
+ const onUp = () => {
54
+ this._dragging = false;
55
+ window.removeEventListener('mousemove', onMove);
56
+ window.removeEventListener('mouseup', onUp);
57
+ };
58
+ window.addEventListener('mousemove', onMove);
59
+ window.addEventListener('mouseup', onUp);
60
+ }
61
+ _onBackdropClick(_e) {
62
+ this._data.close();
63
+ }
64
+ _onKey(e) {
65
+ switch (e.key) {
66
+ case 'ArrowLeft':
67
+ this._navigate(-1);
68
+ break;
69
+ case 'ArrowRight':
70
+ this._navigate(1);
71
+ break;
72
+ case 'Escape':
73
+ this._data.close();
74
+ break;
75
+ case '+':
76
+ this._zoom(0.25);
77
+ break;
78
+ case '-':
79
+ this._zoom(-0.25);
80
+ break;
81
+ }
82
+ }
83
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuImageViewerOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
84
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuImageViewerOverlayComponent, isStandalone: true, selector: "neu-image-viewer-overlay", host: { attributes: { "aria-modal": "true", "role": "dialog", "aria-label": "Visor de im\u00E1genes", "tabIndex": "0" }, listeners: { "keydown": "_onKey($event)", "click": "_onBackdropClick($event)" }, classAttribute: "neu-iv" }, ngImport: i0, template: `
85
+ <div class="neu-iv__overlay" (click)="$event.stopPropagation()">
86
+ <!-- Toolbar -->
87
+ <div class="neu-iv__toolbar">
88
+ <span class="neu-iv__counter">{{ _index() + 1 }} / {{ _data.items.length }}</span>
89
+ <div class="neu-iv__toolbar-actions">
90
+ <button type="button" class="neu-iv__btn" aria-label="Zoom in" (click)="_zoom(0.25)">
91
+
92
+ </button>
93
+ <button type="button" class="neu-iv__btn" aria-label="Zoom out" (click)="_zoom(-0.25)">
94
+
95
+ </button>
96
+ <button type="button" class="neu-iv__btn" aria-label="Reset zoom" (click)="_resetZoom()">
97
+
98
+ </button>
99
+ <button
100
+ type="button"
101
+ class="neu-iv__btn neu-iv__btn--close"
102
+ aria-label="Cerrar"
103
+ (click)="_data.close()"
104
+ >
105
+
106
+ </button>
107
+ </div>
108
+ </div>
109
+
110
+ <!-- Image -->
111
+ <div class="neu-iv__stage" (wheel)="_onWheel($event)" (mousedown)="_onMouseDown($event)">
112
+ <img
113
+ class="neu-iv__img"
114
+ [src]="_current().src"
115
+ [alt]="_current().alt ?? ''"
116
+ [style.transform]="_transform()"
117
+ draggable="false"
118
+ />
119
+ </div>
120
+
121
+ <!-- Caption -->
122
+ @if (_current().caption) {
123
+ <div class="neu-iv__caption">{{ _current().caption }}</div>
124
+ }
125
+
126
+ <!-- Prev/Next arrows -->
127
+ @if (_data.items.length > 1) {
128
+ <button
129
+ type="button"
130
+ class="neu-iv__arrow neu-iv__arrow--prev"
131
+ aria-label="Anterior"
132
+ [disabled]="_index() === 0"
133
+ (click)="_navigate(-1)"
134
+ >
135
+
136
+ </button>
137
+ <button
138
+ type="button"
139
+ class="neu-iv__arrow neu-iv__arrow--next"
140
+ aria-label="Siguiente"
141
+ [disabled]="_index() === _data.items.length - 1"
142
+ (click)="_navigate(1)"
143
+ >
144
+
145
+ </button>
146
+ }
147
+ </div>
148
+ `, isInline: true, styles: ["@charset \"UTF-8\";.neu-iv{position:fixed;inset:0;background:var(--neu-image-viewer-bg);display:flex;flex-direction:column;z-index:1300;outline:none}.neu-iv__overlay{position:relative;display:flex;flex-direction:column;height:100%;width:100%}.neu-iv__toolbar{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;flex-shrink:0}.neu-iv__counter{font-size:.875rem;color:var(--neu-image-viewer-muted)}.neu-iv__toolbar-actions{display:flex;gap:4px}.neu-iv__btn{all:unset;width:36px;height:36px;display:flex;align-items:center;justify-content:center;border-radius:8px;color:#fff;font-size:1.1rem;cursor:pointer;transition:background .1s}.neu-iv__btn:hover{background:var(--neu-image-viewer-btn-hover)}.neu-iv__btn--close{font-size:.875rem}.neu-iv__stage{flex:1;overflow:hidden;display:flex;align-items:center;justify-content:center;cursor:grab;-webkit-user-select:none;user-select:none}.neu-iv__stage:active{cursor:grabbing}.neu-iv__img{max-width:90%;max-height:80vh;object-fit:contain;transform-origin:center;transition:transform 60ms linear;pointer-events:none;border-radius:var(--neu-radius-lg, 12px)}.neu-iv__caption{flex-shrink:0;padding:10px 16px;text-align:center;font-size:.875rem;color:var(--neu-image-viewer-caption)}.neu-iv__arrow{all:unset;position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;display:flex;align-items:center;justify-content:center;border-radius:50%;background:var(--neu-image-viewer-arrow-bg);color:#fff;font-size:1.8rem;cursor:pointer;transition:background .12s}.neu-iv__arrow:hover:not(:disabled){background:var(--neu-image-viewer-arrow-hover)}.neu-iv__arrow:disabled{opacity:.25;cursor:default}.neu-iv__arrow--prev{left:16px}.neu-iv__arrow--next{right:16px}.neu-iv-trigger{display:inline-block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
149
+ }
150
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuImageViewerOverlayComponent, decorators: [{
151
+ type: Component,
152
+ args: [{ selector: 'neu-image-viewer-overlay', imports: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
153
+ class: 'neu-iv',
154
+ 'aria-modal': 'true',
155
+ role: 'dialog',
156
+ 'aria-label': 'Visor de imágenes',
157
+ tabIndex: '0',
158
+ '(keydown)': '_onKey($event)',
159
+ '(click)': '_onBackdropClick($event)',
160
+ }, template: `
161
+ <div class="neu-iv__overlay" (click)="$event.stopPropagation()">
162
+ <!-- Toolbar -->
163
+ <div class="neu-iv__toolbar">
164
+ <span class="neu-iv__counter">{{ _index() + 1 }} / {{ _data.items.length }}</span>
165
+ <div class="neu-iv__toolbar-actions">
166
+ <button type="button" class="neu-iv__btn" aria-label="Zoom in" (click)="_zoom(0.25)">
167
+
168
+ </button>
169
+ <button type="button" class="neu-iv__btn" aria-label="Zoom out" (click)="_zoom(-0.25)">
170
+
171
+ </button>
172
+ <button type="button" class="neu-iv__btn" aria-label="Reset zoom" (click)="_resetZoom()">
173
+
174
+ </button>
175
+ <button
176
+ type="button"
177
+ class="neu-iv__btn neu-iv__btn--close"
178
+ aria-label="Cerrar"
179
+ (click)="_data.close()"
180
+ >
181
+
182
+ </button>
183
+ </div>
184
+ </div>
185
+
186
+ <!-- Image -->
187
+ <div class="neu-iv__stage" (wheel)="_onWheel($event)" (mousedown)="_onMouseDown($event)">
188
+ <img
189
+ class="neu-iv__img"
190
+ [src]="_current().src"
191
+ [alt]="_current().alt ?? ''"
192
+ [style.transform]="_transform()"
193
+ draggable="false"
194
+ />
195
+ </div>
196
+
197
+ <!-- Caption -->
198
+ @if (_current().caption) {
199
+ <div class="neu-iv__caption">{{ _current().caption }}</div>
200
+ }
201
+
202
+ <!-- Prev/Next arrows -->
203
+ @if (_data.items.length > 1) {
204
+ <button
205
+ type="button"
206
+ class="neu-iv__arrow neu-iv__arrow--prev"
207
+ aria-label="Anterior"
208
+ [disabled]="_index() === 0"
209
+ (click)="_navigate(-1)"
210
+ >
211
+
212
+ </button>
213
+ <button
214
+ type="button"
215
+ class="neu-iv__arrow neu-iv__arrow--next"
216
+ aria-label="Siguiente"
217
+ [disabled]="_index() === _data.items.length - 1"
218
+ (click)="_navigate(1)"
219
+ >
220
+
221
+ </button>
222
+ }
223
+ </div>
224
+ `, styles: ["@charset \"UTF-8\";.neu-iv{position:fixed;inset:0;background:var(--neu-image-viewer-bg);display:flex;flex-direction:column;z-index:1300;outline:none}.neu-iv__overlay{position:relative;display:flex;flex-direction:column;height:100%;width:100%}.neu-iv__toolbar{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;flex-shrink:0}.neu-iv__counter{font-size:.875rem;color:var(--neu-image-viewer-muted)}.neu-iv__toolbar-actions{display:flex;gap:4px}.neu-iv__btn{all:unset;width:36px;height:36px;display:flex;align-items:center;justify-content:center;border-radius:8px;color:#fff;font-size:1.1rem;cursor:pointer;transition:background .1s}.neu-iv__btn:hover{background:var(--neu-image-viewer-btn-hover)}.neu-iv__btn--close{font-size:.875rem}.neu-iv__stage{flex:1;overflow:hidden;display:flex;align-items:center;justify-content:center;cursor:grab;-webkit-user-select:none;user-select:none}.neu-iv__stage:active{cursor:grabbing}.neu-iv__img{max-width:90%;max-height:80vh;object-fit:contain;transform-origin:center;transition:transform 60ms linear;pointer-events:none;border-radius:var(--neu-radius-lg, 12px)}.neu-iv__caption{flex-shrink:0;padding:10px 16px;text-align:center;font-size:.875rem;color:var(--neu-image-viewer-caption)}.neu-iv__arrow{all:unset;position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;display:flex;align-items:center;justify-content:center;border-radius:50%;background:var(--neu-image-viewer-arrow-bg);color:#fff;font-size:1.8rem;cursor:pointer;transition:background .12s}.neu-iv__arrow:hover:not(:disabled){background:var(--neu-image-viewer-arrow-hover)}.neu-iv__arrow:disabled{opacity:.25;cursor:default}.neu-iv__arrow--prev{left:16px}.neu-iv__arrow--next{right:16px}.neu-iv-trigger{display:inline-block}\n"] }]
225
+ }] });
226
+ /**
227
+ * NeuralUI ImageViewer Directive
228
+ *
229
+ * Adjunta un visor de imágenes en pantalla completa a cualquier elemento.
230
+ *
231
+ * Uso básico (imagen única):
232
+ * <img src="photo.jpg" [neuImageViewer]="src" />
233
+ *
234
+ * Uso con galería:
235
+ * <img src="photo.jpg" [neuImageViewer]="items" [neuIvIndex]="0" />
236
+ */
237
+ class NeuImageViewerDirective {
238
+ /** Puede ser una URL, NeuImageViewerItem o un array de items */
239
+ neuImageViewer = input('', ...(ngDevMode ? [{ debugName: "neuImageViewer" }] : /* istanbul ignore next */ []));
240
+ /** Índice inicial cuando se pasa un array */
241
+ neuIvIndex = input(0, ...(ngDevMode ? [{ debugName: "neuIvIndex" }] : /* istanbul ignore next */ []));
242
+ _overlay = inject(Overlay);
243
+ _injector = inject(Injector);
244
+ _overlayRef = null;
245
+ open() {
246
+ if (this._overlayRef)
247
+ return;
248
+ const items = this._normalizeItems();
249
+ const overlayRef = this._overlay.create({
250
+ hasBackdrop: false,
251
+ scrollStrategy: this._overlay.scrollStrategies.block(),
252
+ positionStrategy: this._overlay.position().global().centerHorizontally().centerVertically(),
253
+ });
254
+ const data = {
255
+ items,
256
+ initialIndex: Math.max(0, Math.min(this.neuIvIndex(), items.length - 1)),
257
+ close: () => this.close(),
258
+ };
259
+ const injector = Injector.create({
260
+ providers: [{ provide: NEU_IV_DATA, useValue: data }],
261
+ parent: this._injector,
262
+ });
263
+ const portal = new ComponentPortal(NeuImageViewerOverlayComponent, null, injector);
264
+ overlayRef.attach(portal);
265
+ this._overlayRef = overlayRef;
266
+ // Focus overlay for keyboard events
267
+ setTimeout(() => {
268
+ const el = overlayRef.overlayElement?.querySelector('.neu-iv');
269
+ el?.focus();
270
+ }, 50);
271
+ }
272
+ close() {
273
+ this._overlayRef?.detach();
274
+ this._overlayRef?.dispose();
275
+ this._overlayRef = null;
276
+ }
277
+ ngOnDestroy() {
278
+ this.close();
279
+ }
280
+ _normalizeItems() {
281
+ const v = this.neuImageViewer();
282
+ if (typeof v === 'string')
283
+ return [{ src: v }];
284
+ if (Array.isArray(v))
285
+ return v;
286
+ return [v];
287
+ }
288
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuImageViewerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
289
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.8", type: NeuImageViewerDirective, isStandalone: true, selector: "[neuImageViewer]", inputs: { neuImageViewer: { classPropertyName: "neuImageViewer", publicName: "neuImageViewer", isSignal: true, isRequired: false, transformFunction: null }, neuIvIndex: { classPropertyName: "neuIvIndex", publicName: "neuIvIndex", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "open()" }, styleAttribute: "cursor:pointer", classAttribute: "neu-iv-trigger" }, exportAs: ["neuImageViewer"], ngImport: i0 });
290
+ }
291
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuImageViewerDirective, decorators: [{
292
+ type: Directive,
293
+ args: [{
294
+ selector: '[neuImageViewer]',
295
+ exportAs: 'neuImageViewer',
296
+ host: {
297
+ class: 'neu-iv-trigger',
298
+ style: 'cursor:pointer',
299
+ '(click)': 'open()',
300
+ },
301
+ }]
302
+ }], propDecorators: { neuImageViewer: [{ type: i0.Input, args: [{ isSignal: true, alias: "neuImageViewer", required: false }] }], neuIvIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "neuIvIndex", required: false }] }] } });
303
+
304
+ /**
305
+ * Generated bundle index. Do not edit.
306
+ */
307
+
308
+ export { NEU_IV_DATA, NeuImageViewerDirective, NeuImageViewerOverlayComponent };
309
+ //# sourceMappingURL=neural-ui-core-image-viewer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-image-viewer.mjs","sources":["../../../../projects/ui-core/image-viewer/neu-image-viewer.component.ts","../../../../projects/ui-core/image-viewer/neural-ui-core-image-viewer.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n Directive,\n ElementRef,\n HostListener,\n Injector,\n OnDestroy,\n ViewEncapsulation,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { Overlay, OverlayRef } from '@angular/cdk/overlay';\nimport { ComponentPortal } from '@angular/cdk/portal';\n\nexport interface NeuImageViewerItem {\n src: string;\n alt?: string;\n caption?: string;\n}\n\n/** Token que comparte datos entre directiva y overlay */\nimport { InjectionToken } from '@angular/core';\n\nexport const NEU_IV_DATA = new InjectionToken<_NeuIvData>('NEU_IV_DATA');\n\ninterface _NeuIvData {\n items: NeuImageViewerItem[];\n initialIndex: number;\n close: () => void;\n}\n\n/**\n * Componente interno del overlay del visor de imágenes.\n * No debe usarse directamente — es instanciado por la directiva.\n */\n@Component({\n selector: 'neu-image-viewer-overlay',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'neu-iv',\n 'aria-modal': 'true',\n role: 'dialog',\n 'aria-label': 'Visor de imágenes',\n tabIndex: '0',\n '(keydown)': '_onKey($event)',\n '(click)': '_onBackdropClick($event)',\n },\n template: `\n <div class=\"neu-iv__overlay\" (click)=\"$event.stopPropagation()\">\n <!-- Toolbar -->\n <div class=\"neu-iv__toolbar\">\n <span class=\"neu-iv__counter\">{{ _index() + 1 }} / {{ _data.items.length }}</span>\n <div class=\"neu-iv__toolbar-actions\">\n <button type=\"button\" class=\"neu-iv__btn\" aria-label=\"Zoom in\" (click)=\"_zoom(0.25)\">\n +\n </button>\n <button type=\"button\" class=\"neu-iv__btn\" aria-label=\"Zoom out\" (click)=\"_zoom(-0.25)\">\n -\n </button>\n <button type=\"button\" class=\"neu-iv__btn\" aria-label=\"Reset zoom\" (click)=\"_resetZoom()\">\n ⟳\n </button>\n <button\n type=\"button\"\n class=\"neu-iv__btn neu-iv__btn--close\"\n aria-label=\"Cerrar\"\n (click)=\"_data.close()\"\n >\n ✕\n </button>\n </div>\n </div>\n\n <!-- Image -->\n <div class=\"neu-iv__stage\" (wheel)=\"_onWheel($event)\" (mousedown)=\"_onMouseDown($event)\">\n <img\n class=\"neu-iv__img\"\n [src]=\"_current().src\"\n [alt]=\"_current().alt ?? ''\"\n [style.transform]=\"_transform()\"\n draggable=\"false\"\n />\n </div>\n\n <!-- Caption -->\n @if (_current().caption) {\n <div class=\"neu-iv__caption\">{{ _current().caption }}</div>\n }\n\n <!-- Prev/Next arrows -->\n @if (_data.items.length > 1) {\n <button\n type=\"button\"\n class=\"neu-iv__arrow neu-iv__arrow--prev\"\n aria-label=\"Anterior\"\n [disabled]=\"_index() === 0\"\n (click)=\"_navigate(-1)\"\n >\n ‹\n </button>\n <button\n type=\"button\"\n class=\"neu-iv__arrow neu-iv__arrow--next\"\n aria-label=\"Siguiente\"\n [disabled]=\"_index() === _data.items.length - 1\"\n (click)=\"_navigate(1)\"\n >\n ›\n </button>\n }\n </div>\n `,\n styleUrl: './neu-image-viewer.component.scss',\n})\nexport class NeuImageViewerOverlayComponent {\n readonly _data = inject(NEU_IV_DATA);\n readonly _index = signal(this._data.initialIndex);\n readonly _scale = signal(1);\n readonly _panX = signal(0);\n readonly _panY = signal(0);\n\n private _dragging = false;\n private _dragStart = { x: 0, y: 0, px: 0, py: 0 };\n\n readonly _current = computed(() => this._data.items[this._index()]);\n\n readonly _transform = computed(\n () => `translate(${this._panX()}px, ${this._panY()}px) scale(${this._scale()})`,\n );\n\n _navigate(dir: -1 | 1): void {\n this._index.update((i) => Math.max(0, Math.min(this._data.items.length - 1, i + dir)));\n this._resetZoom();\n }\n\n _zoom(delta: number): void {\n this._scale.update((s) => Math.max(0.25, Math.min(5, s + delta)));\n }\n\n _resetZoom(): void {\n this._scale.set(1);\n this._panX.set(0);\n this._panY.set(0);\n }\n\n _onWheel(e: WheelEvent): void {\n e.preventDefault();\n this._zoom(e.deltaY < 0 ? 0.1 : -0.1);\n }\n\n _onMouseDown(e: MouseEvent): void {\n if (e.button !== 0) return;\n this._dragging = true;\n this._dragStart = {\n x: e.clientX,\n y: e.clientY,\n px: this._panX(),\n py: this._panY(),\n };\n const onMove = (ev: MouseEvent) => {\n if (!this._dragging) return;\n this._panX.set(this._dragStart.px + ev.clientX - this._dragStart.x);\n this._panY.set(this._dragStart.py + ev.clientY - this._dragStart.y);\n };\n const onUp = () => {\n this._dragging = false;\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n };\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n }\n\n _onBackdropClick(_e: MouseEvent): void {\n this._data.close();\n }\n\n _onKey(e: KeyboardEvent): void {\n switch (e.key) {\n case 'ArrowLeft':\n this._navigate(-1);\n break;\n case 'ArrowRight':\n this._navigate(1);\n break;\n case 'Escape':\n this._data.close();\n break;\n case '+':\n this._zoom(0.25);\n break;\n case '-':\n this._zoom(-0.25);\n break;\n }\n }\n}\n\n/**\n * NeuralUI ImageViewer Directive\n *\n * Adjunta un visor de imágenes en pantalla completa a cualquier elemento.\n *\n * Uso básico (imagen única):\n * <img src=\"photo.jpg\" [neuImageViewer]=\"src\" />\n *\n * Uso con galería:\n * <img src=\"photo.jpg\" [neuImageViewer]=\"items\" [neuIvIndex]=\"0\" />\n */\n@Directive({\n selector: '[neuImageViewer]',\n exportAs: 'neuImageViewer',\n host: {\n class: 'neu-iv-trigger',\n style: 'cursor:pointer',\n '(click)': 'open()',\n },\n})\nexport class NeuImageViewerDirective implements OnDestroy {\n /** Puede ser una URL, NeuImageViewerItem o un array de items */\n readonly neuImageViewer = input<string | NeuImageViewerItem | NeuImageViewerItem[]>('');\n /** Índice inicial cuando se pasa un array */\n readonly neuIvIndex = input<number>(0);\n\n private readonly _overlay = inject(Overlay);\n private readonly _injector = inject(Injector);\n private _overlayRef: OverlayRef | null = null;\n\n open(): void {\n if (this._overlayRef) return;\n\n const items = this._normalizeItems();\n const overlayRef = this._overlay.create({\n hasBackdrop: false,\n scrollStrategy: this._overlay.scrollStrategies.block(),\n positionStrategy: this._overlay.position().global().centerHorizontally().centerVertically(),\n });\n\n const data: _NeuIvData = {\n items,\n initialIndex: Math.max(0, Math.min(this.neuIvIndex(), items.length - 1)),\n close: () => this.close(),\n };\n\n const injector = Injector.create({\n providers: [{ provide: NEU_IV_DATA, useValue: data }],\n parent: this._injector,\n });\n\n const portal = new ComponentPortal(NeuImageViewerOverlayComponent, null, injector);\n overlayRef.attach(portal);\n this._overlayRef = overlayRef;\n\n // Focus overlay for keyboard events\n setTimeout(() => {\n const el = overlayRef.overlayElement?.querySelector<HTMLElement>('.neu-iv');\n el?.focus();\n }, 50);\n }\n\n close(): void {\n this._overlayRef?.detach();\n this._overlayRef?.dispose();\n this._overlayRef = null;\n }\n\n ngOnDestroy(): void {\n this.close();\n }\n\n private _normalizeItems(): NeuImageViewerItem[] {\n const v = this.neuImageViewer();\n if (typeof v === 'string') return [{ src: v }];\n if (Array.isArray(v)) return v;\n return [v];\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,192 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, signal, computed, viewChildren, forwardRef, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
+
5
+ let _seq = 0;
6
+ /**
7
+ * NeuralUI InputOTP Component
8
+ *
9
+ * N-digit OTP code input split into individual cells. Implements CVA.
10
+ *
11
+ * Uso: <neu-input-otp [length]="6" (completed)="onOtp($event)" formControlName="code" />
12
+ */
13
+ class NeuInputOTPComponent {
14
+ /** Número de celdas / Number of cells */
15
+ length = input(6, ...(ngDevMode ? [{ debugName: "length" }] : /* istanbul ignore next */ []));
16
+ /** Tipo de caracteres: numeric | alphanumeric / Character type */
17
+ type = input('numeric', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
18
+ /** Emitido cuando todas las celdas están completas / Emitted when all cells are filled */
19
+ completed = output();
20
+ /** Emitido en cada cambio parcial / Emitted on each partial change */
21
+ valueChange = output();
22
+ _id = `neu-input-otp-${++_seq}`;
23
+ _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
24
+ _digits = signal([], ...(ngDevMode ? [{ debugName: "_digits" }] : /* istanbul ignore next */ []));
25
+ _cells = computed(() => Array.from({ length: this.length() }), ...(ngDevMode ? [{ debugName: "_cells" }] : /* istanbul ignore next */ []));
26
+ /** Array de dígitos con padding de vacíos — nunca undefined / Digits array padded with empty strings — never undefined */
27
+ _paddedDigits = computed(() => Array.from({ length: this.length() }, (_, i) => this._digits()[i] ?? ''), ...(ngDevMode ? [{ debugName: "_paddedDigits" }] : /* istanbul ignore next */ []));
28
+ hostClasses = computed(() => ({
29
+ 'neu-input-otp': true,
30
+ [`neu-input-otp--len-${this.length()}`]: true,
31
+ 'neu-input-otp--disabled': this._cvaDisabled(),
32
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
33
+ _cellEls = viewChildren('cellEl', ...(ngDevMode ? [{ debugName: "_cellEls" }] : /* istanbul ignore next */ []));
34
+ _onChange = () => { };
35
+ _onTouched = () => { };
36
+ writeValue(val) {
37
+ const str = (val ?? '').substring(0, this.length());
38
+ this._digits.set(str.split(''));
39
+ }
40
+ registerOnChange(fn) {
41
+ this._onChange = fn;
42
+ }
43
+ registerOnTouched(fn) {
44
+ this._onTouched = fn;
45
+ }
46
+ setDisabledState(disabled) {
47
+ this._cvaDisabled.set(disabled);
48
+ }
49
+ onCellInput(event, index) {
50
+ const input = event.target;
51
+ const char = this._sanitize(input.value.slice(-1));
52
+ input.value = char;
53
+ this._digits.update((d) => {
54
+ const copy = [...d];
55
+ copy[index] = char;
56
+ return copy;
57
+ });
58
+ const value = this._digits().join('');
59
+ this._onChange(value);
60
+ this.valueChange.emit(value);
61
+ if (char && index < this.length() - 1) {
62
+ this._focusCell(index + 1);
63
+ }
64
+ if (value.length === this.length() && this._digits().every((d) => d !== '')) {
65
+ this.completed.emit(value);
66
+ }
67
+ }
68
+ onKeyDown(event, index) {
69
+ if (event.key === 'Backspace') {
70
+ const d = this._digits();
71
+ if (d[index]) {
72
+ this._digits.update((d) => {
73
+ const c = [...d];
74
+ c[index] = '';
75
+ return c;
76
+ });
77
+ const value = this._digits().join('');
78
+ this._onChange(value);
79
+ this.valueChange.emit(value);
80
+ }
81
+ else if (index > 0) {
82
+ this._focusCell(index - 1);
83
+ }
84
+ }
85
+ else if (event.key === 'ArrowLeft' && index > 0) {
86
+ this._focusCell(index - 1);
87
+ }
88
+ else if (event.key === 'ArrowRight' && index < this.length() - 1) {
89
+ this._focusCell(index + 1);
90
+ }
91
+ }
92
+ onPaste(event) {
93
+ event.preventDefault();
94
+ const raw = event.clipboardData?.getData('text') ?? '';
95
+ const sanitized = raw
96
+ .split('')
97
+ .map((c) => this._sanitize(c))
98
+ .filter(Boolean);
99
+ const digits = Array.from({ length: this.length() }, (_, i) => sanitized[i] ?? '');
100
+ this._digits.set(digits);
101
+ const value = digits.join('');
102
+ this._onChange(value);
103
+ this.valueChange.emit(value);
104
+ const lastFilled = Math.min(sanitized.length, this.length() - 1);
105
+ this._focusCell(lastFilled);
106
+ if (value.replace(/\s/g, '').length === this.length()) {
107
+ this.completed.emit(value);
108
+ }
109
+ }
110
+ onFocus(index) {
111
+ this._onTouched();
112
+ const el = this._cellEls()[index]?.nativeElement;
113
+ el?.select();
114
+ }
115
+ clear() {
116
+ this._digits.set([]);
117
+ this._onChange('');
118
+ this.valueChange.emit('');
119
+ this._focusCell(0);
120
+ }
121
+ _sanitize(char) {
122
+ if (!char)
123
+ return '';
124
+ return this.type() === 'numeric' ? (/\d/.test(char) ? char : '') : char.slice(0, 1);
125
+ }
126
+ _focusCell(index) {
127
+ this._cellEls()[index]?.nativeElement.focus();
128
+ }
129
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuInputOTPComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
130
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuInputOTPComponent, isStandalone: true, selector: "neu-input-otp", inputs: { length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { completed: "completed", valueChange: "valueChange" }, host: { properties: { "class": "hostClasses()" } }, providers: [
131
+ {
132
+ provide: NG_VALUE_ACCESSOR,
133
+ useExisting: forwardRef(() => NeuInputOTPComponent),
134
+ multi: true,
135
+ },
136
+ ], viewQueries: [{ propertyName: "_cellEls", predicate: ["cellEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
137
+ @for (cell of _cells(); track $index) {
138
+ <input
139
+ #cellEl
140
+ class="neu-input-otp__cell"
141
+ type="text"
142
+ inputmode="numeric"
143
+ maxlength="1"
144
+ [id]="_id + '_' + $index"
145
+ [value]="_paddedDigits()[$index]"
146
+ [disabled]="_cvaDisabled()"
147
+ [attr.aria-label]="'Dígito ' + ($index + 1) + ' de ' + length()"
148
+ [class.neu-input-otp__cell--filled]="!!_paddedDigits()[$index]"
149
+ (input)="onCellInput($event, $index)"
150
+ (keydown)="onKeyDown($event, $index)"
151
+ (paste)="onPaste($event)"
152
+ (focus)="onFocus($index)"
153
+ />
154
+ }
155
+ `, isInline: true, styles: ["@charset \"UTF-8\";.neu-input-otp{display:inline-flex;align-items:center;flex-wrap:wrap;max-width:100%;gap:var(--neu-otp-gap, 8px)}.neu-input-otp--disabled{opacity:.5;pointer-events:none}.neu-input-otp__cell{flex:0 0 auto;width:var(--neu-otp-cell-size, 44px);height:var(--neu-otp-cell-size, 44px);text-align:center;font-size:1.25rem;font-weight:600;border:2px solid var(--neu-otp-cell-border, var(--neu-border-color, #d1d5db));border-radius:var(--neu-otp-cell-radius, var(--neu-radius-md, 8px));background:var(--neu-otp-cell-bg, var(--neu-surface-1, #fff));color:var(--neu-text-primary, #111);caret-color:var(--neu-color-primary, #0ea5e9);outline:none;transition:border-color .15s,box-shadow .15s}.neu-input-otp__cell:focus{border-color:var(--neu-color-primary, #0ea5e9);box-shadow:0 0 0 3px var(--neu-focus-ring-alpha, rgba(14, 165, 233, .25))}.neu-input-otp__cell.neu-input-otp__cell--filled{border-color:var(--neu-color-primary, #0ea5e9)}.neu-input-otp__cell::-webkit-inner-spin-button,.neu-input-otp__cell::-webkit-outer-spin-button{display:none}.neu-input-otp__cell{-moz-appearance:textfield}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
156
+ }
157
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuInputOTPComponent, decorators: [{
158
+ type: Component,
159
+ args: [{ selector: 'neu-input-otp', imports: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': 'hostClasses()' }, providers: [
160
+ {
161
+ provide: NG_VALUE_ACCESSOR,
162
+ useExisting: forwardRef(() => NeuInputOTPComponent),
163
+ multi: true,
164
+ },
165
+ ], template: `
166
+ @for (cell of _cells(); track $index) {
167
+ <input
168
+ #cellEl
169
+ class="neu-input-otp__cell"
170
+ type="text"
171
+ inputmode="numeric"
172
+ maxlength="1"
173
+ [id]="_id + '_' + $index"
174
+ [value]="_paddedDigits()[$index]"
175
+ [disabled]="_cvaDisabled()"
176
+ [attr.aria-label]="'Dígito ' + ($index + 1) + ' de ' + length()"
177
+ [class.neu-input-otp__cell--filled]="!!_paddedDigits()[$index]"
178
+ (input)="onCellInput($event, $index)"
179
+ (keydown)="onKeyDown($event, $index)"
180
+ (paste)="onPaste($event)"
181
+ (focus)="onFocus($index)"
182
+ />
183
+ }
184
+ `, styles: ["@charset \"UTF-8\";.neu-input-otp{display:inline-flex;align-items:center;flex-wrap:wrap;max-width:100%;gap:var(--neu-otp-gap, 8px)}.neu-input-otp--disabled{opacity:.5;pointer-events:none}.neu-input-otp__cell{flex:0 0 auto;width:var(--neu-otp-cell-size, 44px);height:var(--neu-otp-cell-size, 44px);text-align:center;font-size:1.25rem;font-weight:600;border:2px solid var(--neu-otp-cell-border, var(--neu-border-color, #d1d5db));border-radius:var(--neu-otp-cell-radius, var(--neu-radius-md, 8px));background:var(--neu-otp-cell-bg, var(--neu-surface-1, #fff));color:var(--neu-text-primary, #111);caret-color:var(--neu-color-primary, #0ea5e9);outline:none;transition:border-color .15s,box-shadow .15s}.neu-input-otp__cell:focus{border-color:var(--neu-color-primary, #0ea5e9);box-shadow:0 0 0 3px var(--neu-focus-ring-alpha, rgba(14, 165, 233, .25))}.neu-input-otp__cell.neu-input-otp__cell--filled{border-color:var(--neu-color-primary, #0ea5e9)}.neu-input-otp__cell::-webkit-inner-spin-button,.neu-input-otp__cell::-webkit-outer-spin-button{display:none}.neu-input-otp__cell{-moz-appearance:textfield}\n"] }]
185
+ }], propDecorators: { length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], completed: [{ type: i0.Output, args: ["completed"] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], _cellEls: [{ type: i0.ViewChildren, args: ['cellEl', { isSignal: true }] }] } });
186
+
187
+ /**
188
+ * Generated bundle index. Do not edit.
189
+ */
190
+
191
+ export { NeuInputOTPComponent };
192
+ //# sourceMappingURL=neural-ui-core-input-otp.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-input-otp.mjs","sources":["../../../../projects/ui-core/input-otp/neu-input-otp.component.ts","../../../../projects/ui-core/input-otp/neural-ui-core-input-otp.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewEncapsulation,\n computed,\n forwardRef,\n inject,\n input,\n output,\n signal,\n viewChildren,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nlet _seq = 0;\n\n/**\n * NeuralUI InputOTP Component\n *\n * N-digit OTP code input split into individual cells. Implements CVA.\n *\n * Uso: <neu-input-otp [length]=\"6\" (completed)=\"onOtp($event)\" formControlName=\"code\" />\n */\n@Component({\n selector: 'neu-input-otp',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses()' },\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => NeuInputOTPComponent),\n multi: true,\n },\n ],\n template: `\n @for (cell of _cells(); track $index) {\n <input\n #cellEl\n class=\"neu-input-otp__cell\"\n type=\"text\"\n inputmode=\"numeric\"\n maxlength=\"1\"\n [id]=\"_id + '_' + $index\"\n [value]=\"_paddedDigits()[$index]\"\n [disabled]=\"_cvaDisabled()\"\n [attr.aria-label]=\"'Dígito ' + ($index + 1) + ' de ' + length()\"\n [class.neu-input-otp__cell--filled]=\"!!_paddedDigits()[$index]\"\n (input)=\"onCellInput($event, $index)\"\n (keydown)=\"onKeyDown($event, $index)\"\n (paste)=\"onPaste($event)\"\n (focus)=\"onFocus($index)\"\n />\n }\n `,\n styleUrl: './neu-input-otp.component.scss',\n})\nexport class NeuInputOTPComponent implements ControlValueAccessor {\n /** Número de celdas / Number of cells */\n readonly length = input<number>(6);\n\n /** Tipo de caracteres: numeric | alphanumeric / Character type */\n readonly type = input<'numeric' | 'alphanumeric'>('numeric');\n\n /** Emitido cuando todas las celdas están completas / Emitted when all cells are filled */\n readonly completed = output<string>();\n\n /** Emitido en cada cambio parcial / Emitted on each partial change */\n readonly valueChange = output<string>();\n\n readonly _id = `neu-input-otp-${++_seq}`;\n readonly _cvaDisabled = signal(false);\n readonly _digits = signal<string[]>([]);\n\n readonly _cells = computed(() => Array.from({ length: this.length() }));\n\n /** Array de dígitos con padding de vacíos — nunca undefined / Digits array padded with empty strings — never undefined */\n readonly _paddedDigits = computed(() =>\n Array.from({ length: this.length() }, (_, i) => this._digits()[i] ?? ''),\n );\n\n readonly hostClasses = computed(() => ({\n 'neu-input-otp': true,\n [`neu-input-otp--len-${this.length()}`]: true,\n 'neu-input-otp--disabled': this._cvaDisabled(),\n }));\n\n private readonly _cellEls = viewChildren<ElementRef<HTMLInputElement>>('cellEl');\n private _onChange: (v: string) => void = () => {};\n private _onTouched: () => void = () => {};\n\n writeValue(val: string | null): void {\n const str = (val ?? '').substring(0, this.length());\n this._digits.set(str.split(''));\n }\n\n registerOnChange(fn: (v: string) => void): void {\n this._onChange = fn;\n }\n registerOnTouched(fn: () => void): void {\n this._onTouched = fn;\n }\n setDisabledState(disabled: boolean): void {\n this._cvaDisabled.set(disabled);\n }\n\n onCellInput(event: Event, index: number): void {\n const input = event.target as HTMLInputElement;\n const char = this._sanitize(input.value.slice(-1));\n input.value = char;\n this._digits.update((d) => {\n const copy = [...d];\n copy[index] = char;\n return copy;\n });\n const value = this._digits().join('');\n this._onChange(value);\n this.valueChange.emit(value);\n if (char && index < this.length() - 1) {\n this._focusCell(index + 1);\n }\n if (value.length === this.length() && this._digits().every((d) => d !== '')) {\n this.completed.emit(value);\n }\n }\n\n onKeyDown(event: KeyboardEvent, index: number): void {\n if (event.key === 'Backspace') {\n const d = this._digits();\n if (d[index]) {\n this._digits.update((d) => {\n const c = [...d];\n c[index] = '';\n return c;\n });\n const value = this._digits().join('');\n this._onChange(value);\n this.valueChange.emit(value);\n } else if (index > 0) {\n this._focusCell(index - 1);\n }\n } else if (event.key === 'ArrowLeft' && index > 0) {\n this._focusCell(index - 1);\n } else if (event.key === 'ArrowRight' && index < this.length() - 1) {\n this._focusCell(index + 1);\n }\n }\n\n onPaste(event: ClipboardEvent): void {\n event.preventDefault();\n const raw = event.clipboardData?.getData('text') ?? '';\n const sanitized = raw\n .split('')\n .map((c) => this._sanitize(c))\n .filter(Boolean);\n const digits = Array.from({ length: this.length() }, (_, i) => sanitized[i] ?? '');\n this._digits.set(digits);\n const value = digits.join('');\n this._onChange(value);\n this.valueChange.emit(value);\n const lastFilled = Math.min(sanitized.length, this.length() - 1);\n this._focusCell(lastFilled);\n if (value.replace(/\\s/g, '').length === this.length()) {\n this.completed.emit(value);\n }\n }\n\n onFocus(index: number): void {\n this._onTouched();\n const el = this._cellEls()[index]?.nativeElement;\n el?.select();\n }\n\n clear(): void {\n this._digits.set([]);\n this._onChange('');\n this.valueChange.emit('');\n this._focusCell(0);\n }\n\n private _sanitize(char: string): string {\n if (!char) return '';\n return this.type() === 'numeric' ? (/\\d/.test(char) ? char : '') : char.slice(0, 1);\n }\n\n private _focusCell(index: number): void {\n this._cellEls()[index]?.nativeElement.focus();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}