@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,116 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, signal, computed, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
+
4
+ /** Icono por defecto para cada tipo */
5
+ const DEFAULT_ICONS = {
6
+ info: 'ℹ',
7
+ success: '✓',
8
+ warning: '⚠',
9
+ error: '✕',
10
+ };
11
+ let _seq = 0;
12
+ /**
13
+ * NeuralUI Alert Component
14
+ *
15
+ * Mensaje de alerta contextual con soporte de icono personalizado, slot de acciones y cierre.
16
+ *
17
+ * Uso: <neu-alert type="success">Operación completada</neu-alert>
18
+ * <neu-alert type="error" [closable]="true" (closed)="onClosed()">Error de red</neu-alert>
19
+ */
20
+ class NeuAlertComponent {
21
+ /** Tipo semántico / Semantic type */
22
+ type = input('info', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
23
+ /** Título opcional sobre el mensaje / Optional title above message */
24
+ title = input('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
25
+ /** Icono personalizado. Vacío = usa el icono por defecto del tipo / Custom icon. Empty = use default type icon */
26
+ icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
27
+ /** Muestra el icono / Shows the icon */
28
+ showIcon = input(true, ...(ngDevMode ? [{ debugName: "showIcon" }] : /* istanbul ignore next */ []));
29
+ /** Permite cerrarse / Can be dismissed */
30
+ closable = input(false, ...(ngDevMode ? [{ debugName: "closable" }] : /* istanbul ignore next */ []));
31
+ /** Variante outline (sin fondo sólido) / Outline variant (no solid background) */
32
+ outline = input(false, ...(ngDevMode ? [{ debugName: "outline" }] : /* istanbul ignore next */ []));
33
+ /** Aria-label para el botón de cierre / Aria-label for the close button */
34
+ closeLabel = input('Cerrar alerta', ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
35
+ /** Emitido cuando se descarta la alerta / Emitted when the alert is dismissed */
36
+ closed = output();
37
+ _id = `neu-alert-${++_seq}`;
38
+ _dismissed = signal(false, ...(ngDevMode ? [{ debugName: "_dismissed" }] : /* istanbul ignore next */ []));
39
+ _resolvedIcon = computed(() => this.icon() || DEFAULT_ICONS[this.type()], ...(ngDevMode ? [{ debugName: "_resolvedIcon" }] : /* istanbul ignore next */ []));
40
+ liveRegion = computed(() => (this.type() === 'error' ? 'assertive' : 'polite'), ...(ngDevMode ? [{ debugName: "liveRegion" }] : /* istanbul ignore next */ []));
41
+ hostClasses = computed(() => ({
42
+ 'neu-alert': true,
43
+ [`neu-alert--${this.type()}`]: true,
44
+ 'neu-alert--outline': this.outline(),
45
+ 'neu-alert--closable': this.closable(),
46
+ 'neu-alert--with-icon': this.showIcon(),
47
+ 'neu-alert--dismissed': this._dismissed(),
48
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
49
+ dismiss() {
50
+ this._dismissed.set(true);
51
+ this.closed.emit();
52
+ }
53
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
54
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuAlertComponent, isStandalone: true, selector: "neu-alert", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, showIcon: { classPropertyName: "showIcon", publicName: "showIcon", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, outline: { classPropertyName: "outline", publicName: "outline", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, host: { attributes: { "role": "alert" }, properties: { "class": "hostClasses()", "attr.aria-live": "liveRegion()" } }, ngImport: i0, template: `
55
+ @if (!_dismissed()) {
56
+ @if (showIcon()) {
57
+ <span class="neu-alert__icon" aria-hidden="true">{{ _resolvedIcon() }}</span>
58
+ }
59
+ <div class="neu-alert__body">
60
+ @if (title()) {
61
+ <strong class="neu-alert__title">{{ title() }}</strong>
62
+ }
63
+ <div class="neu-alert__content">
64
+ <ng-content />
65
+ </div>
66
+ <ng-content select="[neu-alert-actions]" />
67
+ </div>
68
+ @if (closable()) {
69
+ <button
70
+ class="neu-alert__close"
71
+ type="button"
72
+ [attr.aria-label]="closeLabel()"
73
+ (click)="dismiss()"
74
+ >
75
+ ×
76
+ </button>
77
+ }
78
+ }
79
+ `, isInline: true, styles: ["@charset \"UTF-8\";.neu-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 16px;border-radius:var(--neu-radius-md, 8px);border:1px solid transparent;font-size:.875rem;line-height:1.5;position:relative}.neu-alert--dismissed{display:none}.neu-alert--info{background:var(--neu-alert-info-bg, #eff6ff);border-color:var(--neu-alert-info-border, #bfdbfe);color:var(--neu-alert-info-text, #1d4ed8)}.neu-alert--success{background:var(--neu-alert-success-bg, #f0fdf4);border-color:var(--neu-alert-success-border, #bbf7d0);color:var(--neu-alert-success-text, #15803d)}.neu-alert--warning{background:var(--neu-alert-warning-bg, #fffbeb);border-color:var(--neu-alert-warning-border, #fde68a);color:var(--neu-alert-warning-text, #b45309)}.neu-alert--error{background:var(--neu-alert-error-bg, #fef2f2);border-color:var(--neu-alert-error-border, #fecaca);color:var(--neu-alert-error-text, #b91c1c)}.neu-alert--outline{background:transparent}.neu-alert__icon{flex-shrink:0;font-size:1.1em;line-height:1.5;font-style:normal}.neu-alert__body{flex:1;min-width:0}.neu-alert__title{display:block;font-weight:600;margin-bottom:2px}.neu-alert__content{opacity:.9}.neu-alert__close{all:unset;cursor:pointer;flex-shrink:0;font-size:1.1rem;line-height:1;opacity:.6;transition:opacity .15s;padding:2px 4px;border-radius:var(--neu-radius-sm, 4px)}.neu-alert__close:hover{opacity:1;background:var(--neu-alert-dismiss-hover-bg)}.neu-alert__close:focus-visible{outline:2px solid var(--neu-focus-ring, #0ea5e9);outline-offset:1px;opacity:1}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
80
+ }
81
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuAlertComponent, decorators: [{
82
+ type: Component,
83
+ args: [{ selector: 'neu-alert', imports: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': 'hostClasses()', role: 'alert', '[attr.aria-live]': 'liveRegion()' }, template: `
84
+ @if (!_dismissed()) {
85
+ @if (showIcon()) {
86
+ <span class="neu-alert__icon" aria-hidden="true">{{ _resolvedIcon() }}</span>
87
+ }
88
+ <div class="neu-alert__body">
89
+ @if (title()) {
90
+ <strong class="neu-alert__title">{{ title() }}</strong>
91
+ }
92
+ <div class="neu-alert__content">
93
+ <ng-content />
94
+ </div>
95
+ <ng-content select="[neu-alert-actions]" />
96
+ </div>
97
+ @if (closable()) {
98
+ <button
99
+ class="neu-alert__close"
100
+ type="button"
101
+ [attr.aria-label]="closeLabel()"
102
+ (click)="dismiss()"
103
+ >
104
+ ×
105
+ </button>
106
+ }
107
+ }
108
+ `, styles: ["@charset \"UTF-8\";.neu-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 16px;border-radius:var(--neu-radius-md, 8px);border:1px solid transparent;font-size:.875rem;line-height:1.5;position:relative}.neu-alert--dismissed{display:none}.neu-alert--info{background:var(--neu-alert-info-bg, #eff6ff);border-color:var(--neu-alert-info-border, #bfdbfe);color:var(--neu-alert-info-text, #1d4ed8)}.neu-alert--success{background:var(--neu-alert-success-bg, #f0fdf4);border-color:var(--neu-alert-success-border, #bbf7d0);color:var(--neu-alert-success-text, #15803d)}.neu-alert--warning{background:var(--neu-alert-warning-bg, #fffbeb);border-color:var(--neu-alert-warning-border, #fde68a);color:var(--neu-alert-warning-text, #b45309)}.neu-alert--error{background:var(--neu-alert-error-bg, #fef2f2);border-color:var(--neu-alert-error-border, #fecaca);color:var(--neu-alert-error-text, #b91c1c)}.neu-alert--outline{background:transparent}.neu-alert__icon{flex-shrink:0;font-size:1.1em;line-height:1.5;font-style:normal}.neu-alert__body{flex:1;min-width:0}.neu-alert__title{display:block;font-weight:600;margin-bottom:2px}.neu-alert__content{opacity:.9}.neu-alert__close{all:unset;cursor:pointer;flex-shrink:0;font-size:1.1rem;line-height:1;opacity:.6;transition:opacity .15s;padding:2px 4px;border-radius:var(--neu-radius-sm, 4px)}.neu-alert__close:hover{opacity:1;background:var(--neu-alert-dismiss-hover-bg)}.neu-alert__close:focus-visible{outline:2px solid var(--neu-focus-ring, #0ea5e9);outline-offset:1px;opacity:1}\n"] }]
109
+ }], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], showIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcon", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], outline: [{ type: i0.Input, args: [{ isSignal: true, alias: "outline", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
110
+
111
+ /**
112
+ * Generated bundle index. Do not edit.
113
+ */
114
+
115
+ export { NeuAlertComponent };
116
+ //# sourceMappingURL=neural-ui-core-alert.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-alert.mjs","sources":["../../../../projects/ui-core/alert/neu-alert.component.ts","../../../../projects/ui-core/alert/neural-ui-core-alert.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n output,\n signal,\n} from '@angular/core';\n\nexport type NeuAlertType = 'info' | 'success' | 'warning' | 'error';\n\n/** Icono por defecto para cada tipo */\nconst DEFAULT_ICONS: Record<NeuAlertType, string> = {\n info: 'ℹ',\n success: '✓',\n warning: '⚠',\n error: '✕',\n};\n\nlet _seq = 0;\n\n/**\n * NeuralUI Alert Component\n *\n * Mensaje de alerta contextual con soporte de icono personalizado, slot de acciones y cierre.\n *\n * Uso: <neu-alert type=\"success\">Operación completada</neu-alert>\n * <neu-alert type=\"error\" [closable]=\"true\" (closed)=\"onClosed()\">Error de red</neu-alert>\n */\n@Component({\n selector: 'neu-alert',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses()', role: 'alert', '[attr.aria-live]': 'liveRegion()' },\n template: `\n @if (!_dismissed()) {\n @if (showIcon()) {\n <span class=\"neu-alert__icon\" aria-hidden=\"true\">{{ _resolvedIcon() }}</span>\n }\n <div class=\"neu-alert__body\">\n @if (title()) {\n <strong class=\"neu-alert__title\">{{ title() }}</strong>\n }\n <div class=\"neu-alert__content\">\n <ng-content />\n </div>\n <ng-content select=\"[neu-alert-actions]\" />\n </div>\n @if (closable()) {\n <button\n class=\"neu-alert__close\"\n type=\"button\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"dismiss()\"\n >\n ×\n </button>\n }\n }\n `,\n styleUrl: './neu-alert.component.scss',\n})\nexport class NeuAlertComponent {\n /** Tipo semántico / Semantic type */\n readonly type = input<NeuAlertType>('info');\n\n /** Título opcional sobre el mensaje / Optional title above message */\n readonly title = input<string>('');\n\n /** Icono personalizado. Vacío = usa el icono por defecto del tipo / Custom icon. Empty = use default type icon */\n readonly icon = input<string>('');\n\n /** Muestra el icono / Shows the icon */\n readonly showIcon = input<boolean>(true);\n\n /** Permite cerrarse / Can be dismissed */\n readonly closable = input<boolean>(false);\n\n /** Variante outline (sin fondo sólido) / Outline variant (no solid background) */\n readonly outline = input<boolean>(false);\n\n /** Aria-label para el botón de cierre / Aria-label for the close button */\n readonly closeLabel = input<string>('Cerrar alerta');\n\n /** Emitido cuando se descarta la alerta / Emitted when the alert is dismissed */\n readonly closed = output<void>();\n\n readonly _id = `neu-alert-${++_seq}`;\n readonly _dismissed = signal(false);\n\n readonly _resolvedIcon = computed(() => this.icon() || DEFAULT_ICONS[this.type()]);\n\n readonly liveRegion = computed(() => (this.type() === 'error' ? 'assertive' : 'polite'));\n\n readonly hostClasses = computed(() => ({\n 'neu-alert': true,\n [`neu-alert--${this.type()}`]: true,\n 'neu-alert--outline': this.outline(),\n 'neu-alert--closable': this.closable(),\n 'neu-alert--with-icon': this.showIcon(),\n 'neu-alert--dismissed': this._dismissed(),\n }));\n\n dismiss(): void {\n this._dismissed.set(true);\n this.closed.emit();\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,332 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, signal, computed, inject, ElementRef, forwardRef, HostListener, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
+
5
+ let _seq = 0;
6
+ /**
7
+ * NeuralUI Autocomplete
8
+ *
9
+ * Input con lista de sugerencias filtradas y navegación por teclado.
10
+ * Implementa CVA para uso en formularios reactivos.
11
+ *
12
+ * Uso:
13
+ * <neu-autocomplete
14
+ * [options]="opts"
15
+ * placeholder="Buscar…"
16
+ * [formControl]="selectedCtrl"
17
+ * (optionSelected)="onSelect($event)"
18
+ * />
19
+ */
20
+ class NeuAutocompleteComponent {
21
+ // ── Inputs / Outputs ────────────────────────────────────────────
22
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
23
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
24
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
25
+ emptyLabel = input('Sin resultados', ...(ngDevMode ? [{ debugName: "emptyLabel" }] : /* istanbul ignore next */ []));
26
+ minLength = input(0, ...(ngDevMode ? [{ debugName: "minLength" }] : /* istanbul ignore next */ []));
27
+ /** Muestra el label como flotante (true) o estático encima del campo (false) / Shows the label as floating (true) or static above the field (false) */
28
+ floatingLabel = input(false, ...(ngDevMode ? [{ debugName: "floatingLabel" }] : /* istanbul ignore next */ []));
29
+ /** Tamaño del campo: 'sm' = 36px | 'md' = 48px | 'lg' = 56px / Field size */
30
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
31
+ /** Emitido al seleccionar una opción / Emitted when an option is selected */
32
+ optionSelected = output();
33
+ /** Emitido al cambiar el texto del input / Emitted on query change */
34
+ queryChange = output();
35
+ // ── Internal state ───────────────────────────────────────────────
36
+ _id = `neu-autocomplete-${++_seq}`;
37
+ _listId = `${this._id}-list`;
38
+ _query = signal('', ...(ngDevMode ? [{ debugName: "_query" }] : /* istanbul ignore next */ []));
39
+ _isOpen = signal(false, ...(ngDevMode ? [{ debugName: "_isOpen" }] : /* istanbul ignore next */ []));
40
+ _activeIndex = signal(-1, ...(ngDevMode ? [{ debugName: "_activeIndex" }] : /* istanbul ignore next */ []));
41
+ _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
42
+ _focused = signal(false, ...(ngDevMode ? [{ debugName: "_focused" }] : /* istanbul ignore next */ []));
43
+ _onChange = () => { };
44
+ _onTouched = () => { };
45
+ // ── Computed ─────────────────────────────────────────────────────
46
+ _filtered = computed(() => {
47
+ const q = this._query().toLowerCase().trim();
48
+ // Con query vacía nunca mostramos opciones (no queremos comportarnos como un select).
49
+ // Empty query → never show options (autocomplete ≠ select).
50
+ if (!q)
51
+ return [];
52
+ if (q.length < this.minLength())
53
+ return [];
54
+ return this.options().filter((o) => o.label.toLowerCase().includes(q));
55
+ }, ...(ngDevMode ? [{ debugName: "_filtered" }] : /* istanbul ignore next */ []));
56
+ _activeId = computed(() => {
57
+ const i = this._activeIndex();
58
+ return i >= 0 ? this._optionId(i) : null;
59
+ }, ...(ngDevMode ? [{ debugName: "_activeId" }] : /* istanbul ignore next */ []));
60
+ _optionId(i) {
61
+ return `${this._listId}-opt-${i}`;
62
+ }
63
+ // ── HostListener close on outside click ─────────────────────────
64
+ _el = inject((ElementRef));
65
+ onDocClick(e) {
66
+ if (!this._el.nativeElement.contains(e.target)) {
67
+ this._isOpen.set(false);
68
+ }
69
+ }
70
+ // ── Event handlers ───────────────────────────────────────────────
71
+ onQueryChange(q) {
72
+ this._query.set(q);
73
+ this._activeIndex.set(-1);
74
+ // Sólo abrimos si hay texto suficiente; si no, cerramos.
75
+ // Only open when there is enough text; otherwise close.
76
+ const minLen = Math.max(1, this.minLength());
77
+ this._isOpen.set(q.trim().length >= minLen);
78
+ this.queryChange.emit(q);
79
+ // CVA — emit null when query is cleared
80
+ if (!q) {
81
+ this._onChange(null);
82
+ }
83
+ }
84
+ _onFocus() {
85
+ this._focused.set(true);
86
+ // Abre el dropdown solo si ya hay texto escrito / Only open when there is already a query.
87
+ if (this._query().trim().length >= Math.max(1, this.minLength())) {
88
+ this._isOpen.set(true);
89
+ }
90
+ }
91
+ _onBlur() {
92
+ this._focused.set(false);
93
+ this._onTouched();
94
+ // Small delay to allow mousedown on option to fire first
95
+ setTimeout(() => this._isOpen.set(false), 150);
96
+ }
97
+ onKeyDown(e) {
98
+ const total = this._filtered().length;
99
+ if (!total && e.key !== 'Escape')
100
+ return;
101
+ switch (e.key) {
102
+ case 'ArrowDown':
103
+ e.preventDefault();
104
+ this._activeIndex.update((i) => Math.min(i + 1, total - 1));
105
+ this._isOpen.set(true);
106
+ break;
107
+ case 'ArrowUp':
108
+ e.preventDefault();
109
+ this._activeIndex.update((i) => Math.max(i - 1, 0));
110
+ break;
111
+ case 'Enter': {
112
+ const idx = this._activeIndex();
113
+ const opt = this._filtered()[idx];
114
+ if (opt && !opt.disabled)
115
+ this.selectOption(opt);
116
+ break;
117
+ }
118
+ case 'Escape':
119
+ this._isOpen.set(false);
120
+ this._activeIndex.set(-1);
121
+ break;
122
+ }
123
+ }
124
+ selectOption(opt) {
125
+ if (opt.disabled)
126
+ return;
127
+ this._query.set(opt.label);
128
+ this._isOpen.set(false);
129
+ this._activeIndex.set(-1);
130
+ this._onChange(opt.value);
131
+ this.optionSelected.emit(opt);
132
+ }
133
+ clear() {
134
+ this._query.set('');
135
+ this._onChange(null);
136
+ this._isOpen.set(false);
137
+ this.queryChange.emit('');
138
+ }
139
+ // ── CVA ──────────────────────────────────────────────────────────
140
+ writeValue(val) {
141
+ if (val === null || val === undefined) {
142
+ this._query.set('');
143
+ return;
144
+ }
145
+ const match = this.options().find((o) => o.value === val);
146
+ this._query.set(match ? match.label : String(val));
147
+ }
148
+ registerOnChange(fn) {
149
+ this._onChange = fn;
150
+ }
151
+ registerOnTouched(fn) {
152
+ this._onTouched = fn;
153
+ }
154
+ setDisabledState(d) {
155
+ this._cvaDisabled.set(d);
156
+ }
157
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuAutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
158
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuAutocompleteComponent, isStandalone: true, selector: "neu-autocomplete", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, emptyLabel: { classPropertyName: "emptyLabel", publicName: "emptyLabel", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, floatingLabel: { classPropertyName: "floatingLabel", publicName: "floatingLabel", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelected: "optionSelected", queryChange: "queryChange" }, host: { listeners: { "document:mousedown": "onDocClick($event)" }, properties: { "class.neu-autocomplete--open": "_isOpen()", "class.neu-autocomplete--disabled": "_cvaDisabled()", "class.neu-autocomplete--floating": "floatingLabel()", "class.neu-autocomplete--focused": "_focused()", "class.neu-autocomplete--has-value": "!!_query()", "class.neu-autocomplete--sm": "size() === \"sm\"", "class.neu-autocomplete--lg": "size() === \"lg\"" }, classAttribute: "neu-autocomplete" }, providers: [
159
+ {
160
+ provide: NG_VALUE_ACCESSOR,
161
+ useExisting: forwardRef(() => NeuAutocompleteComponent),
162
+ multi: true,
163
+ },
164
+ ], ngImport: i0, template: `
165
+ @if (!floatingLabel() && label()) {
166
+ <label class="neu-autocomplete__label" [for]="_id">{{ label() }}</label>
167
+ }
168
+ <div
169
+ class="neu-autocomplete__input-wrap"
170
+ [class.neu-autocomplete__input-wrap--focused]="_focused()"
171
+ [class.neu-autocomplete__input-wrap--has-value]="!!_query()"
172
+ >
173
+ <input
174
+ #inputEl
175
+ class="neu-autocomplete__input"
176
+ type="text"
177
+ autocomplete="off"
178
+ role="combobox"
179
+ [id]="_id"
180
+ [attr.placeholder]="floatingLabel() ? ' ' : placeholder() || null"
181
+ [attr.aria-label]="label() || placeholder()"
182
+ [attr.aria-expanded]="_isOpen()"
183
+ [attr.aria-controls]="_listId"
184
+ [attr.aria-activedescendant]="_activeId()"
185
+ [disabled]="_cvaDisabled()"
186
+ [value]="_query()"
187
+ (input)="onQueryChange($any($event.target).value)"
188
+ (focus)="_onFocus()"
189
+ (blur)="_onBlur()"
190
+ (keydown)="onKeyDown($event)"
191
+ />
192
+ @if (floatingLabel() && label()) {
193
+ <label class="neu-autocomplete__floating-label" [for]="_id">{{ label() }}</label>
194
+ }
195
+ @if (_query() && !_cvaDisabled()) {
196
+ <button
197
+ type="button"
198
+ class="neu-autocomplete__clear"
199
+ aria-label="Limpiar"
200
+ tabindex="-1"
201
+ (click)="clear()"
202
+ >
203
+ ×
204
+ </button>
205
+ }
206
+ </div>
207
+ @if (_isOpen() && _filtered().length) {
208
+ <ul
209
+ class="neu-autocomplete__list"
210
+ role="listbox"
211
+ [id]="_listId"
212
+ [attr.aria-label]="label() || placeholder()"
213
+ >
214
+ @for (opt of _filtered(); track opt.label; let i = $index) {
215
+ <li
216
+ class="neu-autocomplete__option"
217
+ role="option"
218
+ [id]="_optionId(i)"
219
+ [class.neu-autocomplete__option--active]="_activeIndex() === i"
220
+ [class.neu-autocomplete__option--disabled]="opt.disabled"
221
+ [attr.aria-selected]="_activeIndex() === i"
222
+ [attr.aria-disabled]="opt.disabled ?? false"
223
+ (mousedown)="selectOption(opt)"
224
+ >
225
+ {{ opt.label }}
226
+ </li>
227
+ }
228
+ </ul>
229
+ }
230
+ @if (_isOpen() && !_filtered().length) {
231
+ <div class="neu-autocomplete__empty" role="status">{{ emptyLabel() }}</div>
232
+ }
233
+ `, isInline: true, styles: ["@charset \"UTF-8\";.neu-autocomplete{position:relative;display:block}.neu-autocomplete--disabled{opacity:.55;pointer-events:none}.neu-autocomplete__label{display:block;margin-bottom:6px;font-size:var(--neu-text-sm, .875rem);font-weight:500;color:var(--neu-text, #0f172a);line-height:1.4}.neu-autocomplete__input-wrap{position:relative;display:flex;align-items:center}.neu-autocomplete__input{width:100%;height:48px;padding:0 36px 0 12px;border:1.5px solid var(--neu-border, #d1d5db);border-radius:var(--neu-radius, 8px);background:var(--neu-surface, #ffffff);color:var(--neu-text, #111);font-size:.875rem;line-height:1.4;outline:none;transition:border-color var(--neu-transition, .12s),box-shadow var(--neu-transition, .12s),background-color var(--neu-transition, .12s)}.neu-autocomplete__input:hover{border-color:var(--neu-border-hover, var(--neu-border, #d1d5db))}.neu-autocomplete__input:focus{border-color:var(--neu-primary, #0ea5e9);box-shadow:var(--neu-focus-ring, 0 0 0 3px rgba(14, 165, 233, .2))}.neu-autocomplete__input[disabled]{background:var(--neu-surface-2, #f3f4f6);color:var(--neu-text-disabled, #9ca3af)}.neu-autocomplete--floating .neu-autocomplete__input::placeholder{color:transparent;transition:color var(--neu-transition)}.neu-autocomplete--floating.neu-autocomplete--focused .neu-autocomplete__input::placeholder{color:var(--neu-text-disabled, #9ca3af)}.neu-autocomplete__floating-label{position:absolute;left:var(--neu-space-3, 12px);top:50%;transform:translateY(-50%);font-size:var(--neu-text-base, .875rem);color:var(--neu-text-muted, #6b7280);pointer-events:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - var(--neu-space-6, 24px));transition:top var(--neu-transition, .12s),font-size var(--neu-transition, .12s),color var(--neu-transition, .12s),transform var(--neu-transition, .12s),padding var(--neu-transition, .12s),background var(--neu-transition, .12s)}.neu-autocomplete--focused .neu-autocomplete__floating-label,.neu-autocomplete--has-value .neu-autocomplete__floating-label{top:0;transform:translateY(-50%);font-size:12px;font-weight:600;letter-spacing:.01em;background:var(--neu-surface, #fff);padding:0 4px;left:calc(var(--neu-space-3, 12px) - 4px)}.neu-autocomplete--focused .neu-autocomplete__floating-label{color:var(--neu-primary, #0ea5e9)}.neu-autocomplete--floating.neu-autocomplete--focused .neu-autocomplete__input{border-color:var(--neu-primary, #0ea5e9);box-shadow:var(--neu-focus-ring, 0 0 0 3px rgba(14, 165, 233, .2))}.neu-autocomplete__clear{all:unset;position:absolute;right:10px;display:flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;font-size:.875rem;color:var(--neu-text-muted, #6b7280);cursor:pointer}.neu-autocomplete__clear:hover{color:var(--neu-text, #111);background:var(--neu-surface-3, #e5e7eb)}.neu-autocomplete__list{all:unset;display:block;position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--neu-surface, #ffffff);border:1.5px solid var(--neu-border, #e5e7eb);border-radius:var(--neu-radius-lg, 12px);box-shadow:var(--neu-shadow-lg, 0 8px 20px -4px rgba(0, 0, 0, .1));max-height:240px;overflow-y:auto;padding:6px;z-index:1000;animation:neu-ac-in 80ms ease}@keyframes neu-ac-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.neu-autocomplete__option{display:flex;align-items:center;padding:7px 10px;border-radius:var(--neu-radius-md, 8px);font-size:.875rem;color:var(--neu-text, #111);cursor:pointer}.neu-autocomplete__option:hover:not(.neu-autocomplete__option--disabled){background:var(--neu-surface-2, #f3f4f6)}.neu-autocomplete__option--active{background:var(--neu-primary-50, rgba(14, 165, 233, .12));color:var(--neu-primary, #0ea5e9);font-weight:500}.neu-autocomplete__option--disabled{opacity:.4;cursor:not-allowed}.neu-autocomplete__empty{position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--neu-surface, #ffffff);border:1.5px solid var(--neu-border, #e5e7eb);border-radius:var(--neu-radius-lg, 12px);padding:12px 16px;font-size:.875rem;color:var(--neu-text-muted, #6b7280);text-align:center}.neu-autocomplete--sm .neu-autocomplete__input{height:36px;font-size:var(--neu-text-sm, .875rem)}.neu-autocomplete--lg .neu-autocomplete__input{height:56px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
234
+ }
235
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuAutocompleteComponent, decorators: [{
236
+ type: Component,
237
+ args: [{ selector: 'neu-autocomplete', imports: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
238
+ {
239
+ provide: NG_VALUE_ACCESSOR,
240
+ useExisting: forwardRef(() => NeuAutocompleteComponent),
241
+ multi: true,
242
+ },
243
+ ], host: {
244
+ class: 'neu-autocomplete',
245
+ '[class.neu-autocomplete--open]': '_isOpen()',
246
+ '[class.neu-autocomplete--disabled]': '_cvaDisabled()',
247
+ '[class.neu-autocomplete--floating]': 'floatingLabel()',
248
+ '[class.neu-autocomplete--focused]': '_focused()',
249
+ '[class.neu-autocomplete--has-value]': '!!_query()',
250
+ '[class.neu-autocomplete--sm]': 'size() === "sm"',
251
+ '[class.neu-autocomplete--lg]': 'size() === "lg"',
252
+ }, template: `
253
+ @if (!floatingLabel() && label()) {
254
+ <label class="neu-autocomplete__label" [for]="_id">{{ label() }}</label>
255
+ }
256
+ <div
257
+ class="neu-autocomplete__input-wrap"
258
+ [class.neu-autocomplete__input-wrap--focused]="_focused()"
259
+ [class.neu-autocomplete__input-wrap--has-value]="!!_query()"
260
+ >
261
+ <input
262
+ #inputEl
263
+ class="neu-autocomplete__input"
264
+ type="text"
265
+ autocomplete="off"
266
+ role="combobox"
267
+ [id]="_id"
268
+ [attr.placeholder]="floatingLabel() ? ' ' : placeholder() || null"
269
+ [attr.aria-label]="label() || placeholder()"
270
+ [attr.aria-expanded]="_isOpen()"
271
+ [attr.aria-controls]="_listId"
272
+ [attr.aria-activedescendant]="_activeId()"
273
+ [disabled]="_cvaDisabled()"
274
+ [value]="_query()"
275
+ (input)="onQueryChange($any($event.target).value)"
276
+ (focus)="_onFocus()"
277
+ (blur)="_onBlur()"
278
+ (keydown)="onKeyDown($event)"
279
+ />
280
+ @if (floatingLabel() && label()) {
281
+ <label class="neu-autocomplete__floating-label" [for]="_id">{{ label() }}</label>
282
+ }
283
+ @if (_query() && !_cvaDisabled()) {
284
+ <button
285
+ type="button"
286
+ class="neu-autocomplete__clear"
287
+ aria-label="Limpiar"
288
+ tabindex="-1"
289
+ (click)="clear()"
290
+ >
291
+ ×
292
+ </button>
293
+ }
294
+ </div>
295
+ @if (_isOpen() && _filtered().length) {
296
+ <ul
297
+ class="neu-autocomplete__list"
298
+ role="listbox"
299
+ [id]="_listId"
300
+ [attr.aria-label]="label() || placeholder()"
301
+ >
302
+ @for (opt of _filtered(); track opt.label; let i = $index) {
303
+ <li
304
+ class="neu-autocomplete__option"
305
+ role="option"
306
+ [id]="_optionId(i)"
307
+ [class.neu-autocomplete__option--active]="_activeIndex() === i"
308
+ [class.neu-autocomplete__option--disabled]="opt.disabled"
309
+ [attr.aria-selected]="_activeIndex() === i"
310
+ [attr.aria-disabled]="opt.disabled ?? false"
311
+ (mousedown)="selectOption(opt)"
312
+ >
313
+ {{ opt.label }}
314
+ </li>
315
+ }
316
+ </ul>
317
+ }
318
+ @if (_isOpen() && !_filtered().length) {
319
+ <div class="neu-autocomplete__empty" role="status">{{ emptyLabel() }}</div>
320
+ }
321
+ `, styles: ["@charset \"UTF-8\";.neu-autocomplete{position:relative;display:block}.neu-autocomplete--disabled{opacity:.55;pointer-events:none}.neu-autocomplete__label{display:block;margin-bottom:6px;font-size:var(--neu-text-sm, .875rem);font-weight:500;color:var(--neu-text, #0f172a);line-height:1.4}.neu-autocomplete__input-wrap{position:relative;display:flex;align-items:center}.neu-autocomplete__input{width:100%;height:48px;padding:0 36px 0 12px;border:1.5px solid var(--neu-border, #d1d5db);border-radius:var(--neu-radius, 8px);background:var(--neu-surface, #ffffff);color:var(--neu-text, #111);font-size:.875rem;line-height:1.4;outline:none;transition:border-color var(--neu-transition, .12s),box-shadow var(--neu-transition, .12s),background-color var(--neu-transition, .12s)}.neu-autocomplete__input:hover{border-color:var(--neu-border-hover, var(--neu-border, #d1d5db))}.neu-autocomplete__input:focus{border-color:var(--neu-primary, #0ea5e9);box-shadow:var(--neu-focus-ring, 0 0 0 3px rgba(14, 165, 233, .2))}.neu-autocomplete__input[disabled]{background:var(--neu-surface-2, #f3f4f6);color:var(--neu-text-disabled, #9ca3af)}.neu-autocomplete--floating .neu-autocomplete__input::placeholder{color:transparent;transition:color var(--neu-transition)}.neu-autocomplete--floating.neu-autocomplete--focused .neu-autocomplete__input::placeholder{color:var(--neu-text-disabled, #9ca3af)}.neu-autocomplete__floating-label{position:absolute;left:var(--neu-space-3, 12px);top:50%;transform:translateY(-50%);font-size:var(--neu-text-base, .875rem);color:var(--neu-text-muted, #6b7280);pointer-events:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:calc(100% - var(--neu-space-6, 24px));transition:top var(--neu-transition, .12s),font-size var(--neu-transition, .12s),color var(--neu-transition, .12s),transform var(--neu-transition, .12s),padding var(--neu-transition, .12s),background var(--neu-transition, .12s)}.neu-autocomplete--focused .neu-autocomplete__floating-label,.neu-autocomplete--has-value .neu-autocomplete__floating-label{top:0;transform:translateY(-50%);font-size:12px;font-weight:600;letter-spacing:.01em;background:var(--neu-surface, #fff);padding:0 4px;left:calc(var(--neu-space-3, 12px) - 4px)}.neu-autocomplete--focused .neu-autocomplete__floating-label{color:var(--neu-primary, #0ea5e9)}.neu-autocomplete--floating.neu-autocomplete--focused .neu-autocomplete__input{border-color:var(--neu-primary, #0ea5e9);box-shadow:var(--neu-focus-ring, 0 0 0 3px rgba(14, 165, 233, .2))}.neu-autocomplete__clear{all:unset;position:absolute;right:10px;display:flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;font-size:.875rem;color:var(--neu-text-muted, #6b7280);cursor:pointer}.neu-autocomplete__clear:hover{color:var(--neu-text, #111);background:var(--neu-surface-3, #e5e7eb)}.neu-autocomplete__list{all:unset;display:block;position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--neu-surface, #ffffff);border:1.5px solid var(--neu-border, #e5e7eb);border-radius:var(--neu-radius-lg, 12px);box-shadow:var(--neu-shadow-lg, 0 8px 20px -4px rgba(0, 0, 0, .1));max-height:240px;overflow-y:auto;padding:6px;z-index:1000;animation:neu-ac-in 80ms ease}@keyframes neu-ac-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.neu-autocomplete__option{display:flex;align-items:center;padding:7px 10px;border-radius:var(--neu-radius-md, 8px);font-size:.875rem;color:var(--neu-text, #111);cursor:pointer}.neu-autocomplete__option:hover:not(.neu-autocomplete__option--disabled){background:var(--neu-surface-2, #f3f4f6)}.neu-autocomplete__option--active{background:var(--neu-primary-50, rgba(14, 165, 233, .12));color:var(--neu-primary, #0ea5e9);font-weight:500}.neu-autocomplete__option--disabled{opacity:.4;cursor:not-allowed}.neu-autocomplete__empty{position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--neu-surface, #ffffff);border:1.5px solid var(--neu-border, #e5e7eb);border-radius:var(--neu-radius-lg, 12px);padding:12px 16px;font-size:.875rem;color:var(--neu-text-muted, #6b7280);text-align:center}.neu-autocomplete--sm .neu-autocomplete__input{height:36px;font-size:var(--neu-text-sm, .875rem)}.neu-autocomplete--lg .neu-autocomplete__input{height:56px}\n"] }]
322
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], emptyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyLabel", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], floatingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "floatingLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }], queryChange: [{ type: i0.Output, args: ["queryChange"] }], onDocClick: [{
323
+ type: HostListener,
324
+ args: ['document:mousedown', ['$event']]
325
+ }] } });
326
+
327
+ /**
328
+ * Generated bundle index. Do not edit.
329
+ */
330
+
331
+ export { NeuAutocompleteComponent };
332
+ //# sourceMappingURL=neural-ui-core-autocomplete.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-autocomplete.mjs","sources":["../../../../projects/ui-core/autocomplete/neu-autocomplete.component.ts","../../../../projects/ui-core/autocomplete/neural-ui-core-autocomplete.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n HostListener,\n ViewEncapsulation,\n computed,\n forwardRef,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nexport interface NeuAutocompleteOption {\n /** Valor / Value */\n value: unknown;\n /** Texto visible / Display text */\n label: string;\n /** Desactiva la opción / Disables the option */\n disabled?: boolean;\n}\n\nlet _seq = 0;\n\n/**\n * NeuralUI Autocomplete\n *\n * Input con lista de sugerencias filtradas y navegación por teclado.\n * Implementa CVA para uso en formularios reactivos.\n *\n * Uso:\n * <neu-autocomplete\n * [options]=\"opts\"\n * placeholder=\"Buscar…\"\n * [formControl]=\"selectedCtrl\"\n * (optionSelected)=\"onSelect($event)\"\n * />\n */\n@Component({\n selector: 'neu-autocomplete',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => NeuAutocompleteComponent),\n multi: true,\n },\n ],\n host: {\n class: 'neu-autocomplete',\n '[class.neu-autocomplete--open]': '_isOpen()',\n '[class.neu-autocomplete--disabled]': '_cvaDisabled()',\n '[class.neu-autocomplete--floating]': 'floatingLabel()',\n '[class.neu-autocomplete--focused]': '_focused()',\n '[class.neu-autocomplete--has-value]': '!!_query()',\n '[class.neu-autocomplete--sm]': 'size() === \"sm\"',\n '[class.neu-autocomplete--lg]': 'size() === \"lg\"',\n },\n template: `\n @if (!floatingLabel() && label()) {\n <label class=\"neu-autocomplete__label\" [for]=\"_id\">{{ label() }}</label>\n }\n <div\n class=\"neu-autocomplete__input-wrap\"\n [class.neu-autocomplete__input-wrap--focused]=\"_focused()\"\n [class.neu-autocomplete__input-wrap--has-value]=\"!!_query()\"\n >\n <input\n #inputEl\n class=\"neu-autocomplete__input\"\n type=\"text\"\n autocomplete=\"off\"\n role=\"combobox\"\n [id]=\"_id\"\n [attr.placeholder]=\"floatingLabel() ? ' ' : placeholder() || null\"\n [attr.aria-label]=\"label() || placeholder()\"\n [attr.aria-expanded]=\"_isOpen()\"\n [attr.aria-controls]=\"_listId\"\n [attr.aria-activedescendant]=\"_activeId()\"\n [disabled]=\"_cvaDisabled()\"\n [value]=\"_query()\"\n (input)=\"onQueryChange($any($event.target).value)\"\n (focus)=\"_onFocus()\"\n (blur)=\"_onBlur()\"\n (keydown)=\"onKeyDown($event)\"\n />\n @if (floatingLabel() && label()) {\n <label class=\"neu-autocomplete__floating-label\" [for]=\"_id\">{{ label() }}</label>\n }\n @if (_query() && !_cvaDisabled()) {\n <button\n type=\"button\"\n class=\"neu-autocomplete__clear\"\n aria-label=\"Limpiar\"\n tabindex=\"-1\"\n (click)=\"clear()\"\n >\n ×\n </button>\n }\n </div>\n @if (_isOpen() && _filtered().length) {\n <ul\n class=\"neu-autocomplete__list\"\n role=\"listbox\"\n [id]=\"_listId\"\n [attr.aria-label]=\"label() || placeholder()\"\n >\n @for (opt of _filtered(); track opt.label; let i = $index) {\n <li\n class=\"neu-autocomplete__option\"\n role=\"option\"\n [id]=\"_optionId(i)\"\n [class.neu-autocomplete__option--active]=\"_activeIndex() === i\"\n [class.neu-autocomplete__option--disabled]=\"opt.disabled\"\n [attr.aria-selected]=\"_activeIndex() === i\"\n [attr.aria-disabled]=\"opt.disabled ?? false\"\n (mousedown)=\"selectOption(opt)\"\n >\n {{ opt.label }}\n </li>\n }\n </ul>\n }\n @if (_isOpen() && !_filtered().length) {\n <div class=\"neu-autocomplete__empty\" role=\"status\">{{ emptyLabel() }}</div>\n }\n `,\n styleUrl: './neu-autocomplete.component.scss',\n})\nexport class NeuAutocompleteComponent implements ControlValueAccessor {\n // ── Inputs / Outputs ────────────────────────────────────────────\n readonly options = input<NeuAutocompleteOption[]>([]);\n readonly placeholder = input<string>('');\n readonly label = input<string>('');\n readonly emptyLabel = input<string>('Sin resultados');\n readonly minLength = input<number>(0);\n /** Muestra el label como flotante (true) o estático encima del campo (false) / Shows the label as floating (true) or static above the field (false) */\n readonly floatingLabel = input<boolean>(false);\n /** Tamaño del campo: 'sm' = 36px | 'md' = 48px | 'lg' = 56px / Field size */\n readonly size = input<'sm' | 'md' | 'lg'>('md');\n\n /** Emitido al seleccionar una opción / Emitted when an option is selected */\n readonly optionSelected = output<NeuAutocompleteOption>();\n\n /** Emitido al cambiar el texto del input / Emitted on query change */\n readonly queryChange = output<string>();\n\n // ── Internal state ───────────────────────────────────────────────\n readonly _id = `neu-autocomplete-${++_seq}`;\n readonly _listId = `${this._id}-list`;\n\n readonly _query = signal('');\n readonly _isOpen = signal(false);\n readonly _activeIndex = signal(-1);\n readonly _cvaDisabled = signal(false);\n readonly _focused = signal(false);\n\n private _onChange: (v: unknown) => void = () => {};\n private _onTouched: () => void = () => {};\n\n // ── Computed ─────────────────────────────────────────────────────\n readonly _filtered = computed(() => {\n const q = this._query().toLowerCase().trim();\n // Con query vacía nunca mostramos opciones (no queremos comportarnos como un select).\n // Empty query → never show options (autocomplete ≠ select).\n if (!q) return [];\n if (q.length < this.minLength()) return [];\n return this.options().filter((o) => o.label.toLowerCase().includes(q));\n });\n\n readonly _activeId = computed(() => {\n const i = this._activeIndex();\n return i >= 0 ? this._optionId(i) : null;\n });\n\n _optionId(i: number): string {\n return `${this._listId}-opt-${i}`;\n }\n\n // ── HostListener close on outside click ─────────────────────────\n private readonly _el = inject(ElementRef<HTMLElement>);\n\n @HostListener('document:mousedown', ['$event'])\n onDocClick(e: MouseEvent): void {\n if (!this._el.nativeElement.contains(e.target as Node)) {\n this._isOpen.set(false);\n }\n }\n\n // ── Event handlers ───────────────────────────────────────────────\n onQueryChange(q: string): void {\n this._query.set(q);\n this._activeIndex.set(-1);\n // Sólo abrimos si hay texto suficiente; si no, cerramos.\n // Only open when there is enough text; otherwise close.\n const minLen = Math.max(1, this.minLength());\n this._isOpen.set(q.trim().length >= minLen);\n this.queryChange.emit(q);\n // CVA — emit null when query is cleared\n if (!q) {\n this._onChange(null);\n }\n }\n\n _onFocus(): void {\n this._focused.set(true);\n // Abre el dropdown solo si ya hay texto escrito / Only open when there is already a query.\n if (this._query().trim().length >= Math.max(1, this.minLength())) {\n this._isOpen.set(true);\n }\n }\n\n _onBlur(): void {\n this._focused.set(false);\n this._onTouched();\n // Small delay to allow mousedown on option to fire first\n setTimeout(() => this._isOpen.set(false), 150);\n }\n\n onKeyDown(e: KeyboardEvent): void {\n const total = this._filtered().length;\n if (!total && e.key !== 'Escape') return;\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n this._activeIndex.update((i) => Math.min(i + 1, total - 1));\n this._isOpen.set(true);\n break;\n case 'ArrowUp':\n e.preventDefault();\n this._activeIndex.update((i) => Math.max(i - 1, 0));\n break;\n case 'Enter': {\n const idx = this._activeIndex();\n const opt = this._filtered()[idx];\n if (opt && !opt.disabled) this.selectOption(opt);\n break;\n }\n case 'Escape':\n this._isOpen.set(false);\n this._activeIndex.set(-1);\n break;\n }\n }\n\n selectOption(opt: NeuAutocompleteOption): void {\n if (opt.disabled) return;\n this._query.set(opt.label);\n this._isOpen.set(false);\n this._activeIndex.set(-1);\n this._onChange(opt.value);\n this.optionSelected.emit(opt);\n }\n\n clear(): void {\n this._query.set('');\n this._onChange(null);\n this._isOpen.set(false);\n this.queryChange.emit('');\n }\n\n // ── CVA ──────────────────────────────────────────────────────────\n writeValue(val: unknown): void {\n if (val === null || val === undefined) {\n this._query.set('');\n return;\n }\n const match = this.options().find((o) => o.value === val);\n this._query.set(match ? match.label : String(val));\n }\n\n registerOnChange(fn: (v: unknown) => void): void {\n this._onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this._onTouched = fn;\n }\n\n setDisabledState(d: boolean): void {\n this._cvaDisabled.set(d);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}