@neural-ui/core 1.2.1 → 1.3.1

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 +406 -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 +825 -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 +710 -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 +1872 -0
  120. package/fesm2022/neural-ui-core-table.mjs.map +1 -0
  121. package/fesm2022/neural-ui-core-tabs.mjs +338 -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 +75 -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 +138 -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 +170 -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 +285 -0
  228. package/types/neural-ui-core-tabs.d.ts +88 -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,109 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, signal, computed, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
+ import { NgClass } from '@angular/common';
4
+
5
+ /**
6
+ * NeuAvatar — Avatar circular o cuadrado para foto o iniciales.
7
+ *
8
+ * Uso:
9
+ * <neu-avatar name="Pedro Moreno" />
10
+ * <neu-avatar src="/assets/avatar.jpg" alt="Pedro" size="lg" />
11
+ * <neu-avatar name="PM" size="lg" color="blue" status="online" />
12
+ */
13
+ class NeuAvatarComponent {
14
+ /** URL de la imagen. Si falla la carga, muestra las iniciales. / Image URL. If loading fails, shows the initials. */
15
+ src = input('', ...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
16
+ /** Texto alternativo de la imagen. / Image alternative text. */
17
+ alt = input('', ...(ngDevMode ? [{ debugName: "alt" }] : /* istanbul ignore next */ []));
18
+ /** Nombre completo — se usan las iniciales como fallback. / Full name — initials are used as fallback. */
19
+ name = input('', ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
20
+ /** Tamaño: xs (24) | sm (32) | md (40) | lg (48) | xl (64). Por defecto 'md'. / Size: xs (24) | sm (32) | md (40) | lg (48) | xl (64). Default 'md'. */
21
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
22
+ /** Forma: 'circle' (default) o 'square'. / Shape: 'circle' (default) or 'square'. */
23
+ shape = input('circle', ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
24
+ /** Color de fondo para iniciales. / Background color for initials. */
25
+ color = input('blue', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
26
+ /** Indicador de presencia. / Presence indicator. */
27
+ status = input('', ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
28
+ /** @internal Imagen fallida / Failed image */
29
+ imgError = signal(false, ...(ngDevMode ? [{ debugName: "imgError" }] : /* istanbul ignore next */ []));
30
+ initials = computed(() => {
31
+ const n = this.name().trim();
32
+ if (!n)
33
+ return '?';
34
+ const parts = n.split(/\s+/);
35
+ return parts.length === 1
36
+ ? parts[0].slice(0, 2).toUpperCase()
37
+ : (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
38
+ }, ...(ngDevMode ? [{ debugName: "initials" }] : /* istanbul ignore next */ []));
39
+ hostClasses = computed(() => [
40
+ `neu-avatar--${this.size()}`,
41
+ `neu-avatar--${this.shape()}`,
42
+ ...(!this.src() || this.imgError() ? [`neu-avatar--${this.color()}`] : []),
43
+ ], ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
44
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuAvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
45
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuAvatarComponent, isStandalone: true, selector: "neu-avatar", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "neu-avatar-host" }, ngImport: i0, template: `
46
+ <span
47
+ class="neu-avatar"
48
+ [ngClass]="hostClasses()"
49
+ [attr.aria-label]="alt() || name() || 'Avatar'"
50
+ role="img"
51
+ >
52
+ @if (src() && !imgError()) {
53
+ <img
54
+ class="neu-avatar__img"
55
+ [src]="src()"
56
+ [alt]="alt() || name()"
57
+ (error)="imgError.set(true)"
58
+ />
59
+ } @else {
60
+ <span class="neu-avatar__initials" aria-hidden="true">{{ initials() }}</span>
61
+ }
62
+ </span>
63
+
64
+ @if (status()) {
65
+ <span
66
+ class="neu-avatar__status"
67
+ [ngClass]="'neu-avatar__status--' + status()"
68
+ aria-hidden="true"
69
+ ></span>
70
+ }
71
+ `, isInline: true, styles: [".neu-avatar-host{display:inline-block;vertical-align:middle;flex-shrink:0;position:relative}.neu-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;overflow:hidden;background:var(--neu-surface-2);color:var(--neu-text-muted);font-weight:600;font-family:var(--neu-font-sans);-webkit-user-select:none;user-select:none;flex-shrink:0}.neu-avatar--xs{width:24px;height:24px;font-size:10px}.neu-avatar--sm{width:32px;height:32px;font-size:12px}.neu-avatar--md{width:40px;height:40px;font-size:14px}.neu-avatar--lg{width:48px;height:48px;font-size:16px}.neu-avatar--xl{width:64px;height:64px;font-size:22px}.neu-avatar--circle{border-radius:50%}.neu-avatar--square{border-radius:var(--neu-radius)}.neu-avatar--blue{background:#dbeafe;color:#1d4ed8}.neu-avatar--violet{background:#ede9fe;color:#5b21b6}.neu-avatar--green{background:#dcfce7;color:#15803d}.neu-avatar--amber{background:#fef3c7;color:#b45309}.neu-avatar--red{background:#fee2e2;color:#b91c1c}.neu-avatar--slate{background:#f1f5f9;color:#475569}.neu-avatar__img{width:100%;height:100%;object-fit:cover;display:block}.neu-avatar__initials{line-height:1;letter-spacing:-.01em}.neu-avatar__status{position:absolute;bottom:3%;right:3%;width:25%;height:25%;min-width:8px;min-height:8px;border-radius:50%;border:2px solid var(--neu-surface);box-sizing:border-box;z-index:1}.neu-avatar__status--online{background:var(--neu-success)}.neu-avatar__status--offline{background:var(--neu-text-disabled)}.neu-avatar__status--busy{background:var(--neu-error)}.neu-avatar__status--away{background:var(--neu-warning)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
72
+ }
73
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuAvatarComponent, decorators: [{
74
+ type: Component,
75
+ args: [{ selector: 'neu-avatar', imports: [NgClass], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'neu-avatar-host' }, template: `
76
+ <span
77
+ class="neu-avatar"
78
+ [ngClass]="hostClasses()"
79
+ [attr.aria-label]="alt() || name() || 'Avatar'"
80
+ role="img"
81
+ >
82
+ @if (src() && !imgError()) {
83
+ <img
84
+ class="neu-avatar__img"
85
+ [src]="src()"
86
+ [alt]="alt() || name()"
87
+ (error)="imgError.set(true)"
88
+ />
89
+ } @else {
90
+ <span class="neu-avatar__initials" aria-hidden="true">{{ initials() }}</span>
91
+ }
92
+ </span>
93
+
94
+ @if (status()) {
95
+ <span
96
+ class="neu-avatar__status"
97
+ [ngClass]="'neu-avatar__status--' + status()"
98
+ aria-hidden="true"
99
+ ></span>
100
+ }
101
+ `, styles: [".neu-avatar-host{display:inline-block;vertical-align:middle;flex-shrink:0;position:relative}.neu-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;overflow:hidden;background:var(--neu-surface-2);color:var(--neu-text-muted);font-weight:600;font-family:var(--neu-font-sans);-webkit-user-select:none;user-select:none;flex-shrink:0}.neu-avatar--xs{width:24px;height:24px;font-size:10px}.neu-avatar--sm{width:32px;height:32px;font-size:12px}.neu-avatar--md{width:40px;height:40px;font-size:14px}.neu-avatar--lg{width:48px;height:48px;font-size:16px}.neu-avatar--xl{width:64px;height:64px;font-size:22px}.neu-avatar--circle{border-radius:50%}.neu-avatar--square{border-radius:var(--neu-radius)}.neu-avatar--blue{background:#dbeafe;color:#1d4ed8}.neu-avatar--violet{background:#ede9fe;color:#5b21b6}.neu-avatar--green{background:#dcfce7;color:#15803d}.neu-avatar--amber{background:#fef3c7;color:#b45309}.neu-avatar--red{background:#fee2e2;color:#b91c1c}.neu-avatar--slate{background:#f1f5f9;color:#475569}.neu-avatar__img{width:100%;height:100%;object-fit:cover;display:block}.neu-avatar__initials{line-height:1;letter-spacing:-.01em}.neu-avatar__status{position:absolute;bottom:3%;right:3%;width:25%;height:25%;min-width:8px;min-height:8px;border-radius:50%;border:2px solid var(--neu-surface);box-sizing:border-box;z-index:1}.neu-avatar__status--online{background:var(--neu-success)}.neu-avatar__status--offline{background:var(--neu-text-disabled)}.neu-avatar__status--busy{background:var(--neu-error)}.neu-avatar__status--away{background:var(--neu-warning)}\n"] }]
102
+ }], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }] } });
103
+
104
+ /**
105
+ * Generated bundle index. Do not edit.
106
+ */
107
+
108
+ export { NeuAvatarComponent };
109
+ //# sourceMappingURL=neural-ui-core-avatar.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-avatar.mjs","sources":["../../../../projects/ui-core/avatar/neu-avatar.component.ts","../../../../projects/ui-core/avatar/neural-ui-core-avatar.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n signal,\n} from '@angular/core';\nimport { NgClass } from '@angular/common';\n\nexport type NeuAvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\nexport type NeuAvatarShape = 'circle' | 'square';\nexport type NeuAvatarColor = 'blue' | 'violet' | 'green' | 'amber' | 'red' | 'slate';\nexport type NeuAvatarStatus = 'online' | 'offline' | 'busy' | 'away' | '';\n\n/**\n * NeuAvatar — Avatar circular o cuadrado para foto o iniciales.\n *\n * Uso:\n * <neu-avatar name=\"Pedro Moreno\" />\n * <neu-avatar src=\"/assets/avatar.jpg\" alt=\"Pedro\" size=\"lg\" />\n * <neu-avatar name=\"PM\" size=\"lg\" color=\"blue\" status=\"online\" />\n */\n@Component({\n selector: 'neu-avatar',\n imports: [NgClass],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: 'neu-avatar-host' },\n template: `\n <span\n class=\"neu-avatar\"\n [ngClass]=\"hostClasses()\"\n [attr.aria-label]=\"alt() || name() || 'Avatar'\"\n role=\"img\"\n >\n @if (src() && !imgError()) {\n <img\n class=\"neu-avatar__img\"\n [src]=\"src()\"\n [alt]=\"alt() || name()\"\n (error)=\"imgError.set(true)\"\n />\n } @else {\n <span class=\"neu-avatar__initials\" aria-hidden=\"true\">{{ initials() }}</span>\n }\n </span>\n\n @if (status()) {\n <span\n class=\"neu-avatar__status\"\n [ngClass]=\"'neu-avatar__status--' + status()\"\n aria-hidden=\"true\"\n ></span>\n }\n `,\n styleUrl: './neu-avatar.component.scss',\n})\nexport class NeuAvatarComponent {\n /** URL de la imagen. Si falla la carga, muestra las iniciales. / Image URL. If loading fails, shows the initials. */\n src = input<string>('');\n /** Texto alternativo de la imagen. / Image alternative text. */\n alt = input<string>('');\n /** Nombre completo — se usan las iniciales como fallback. / Full name — initials are used as fallback. */\n name = input<string>('');\n /** Tamaño: xs (24) | sm (32) | md (40) | lg (48) | xl (64). Por defecto 'md'. / Size: xs (24) | sm (32) | md (40) | lg (48) | xl (64). Default 'md'. */\n size = input<NeuAvatarSize>('md');\n /** Forma: 'circle' (default) o 'square'. / Shape: 'circle' (default) or 'square'. */\n shape = input<NeuAvatarShape>('circle');\n /** Color de fondo para iniciales. / Background color for initials. */\n color = input<NeuAvatarColor>('blue');\n /** Indicador de presencia. / Presence indicator. */\n status = input<NeuAvatarStatus>('');\n\n /** @internal Imagen fallida / Failed image */\n protected readonly imgError = signal(false);\n\n protected readonly initials = computed(() => {\n const n = this.name().trim();\n if (!n) return '?';\n const parts = n.split(/\\s+/);\n return parts.length === 1\n ? parts[0].slice(0, 2).toUpperCase()\n : (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n });\n\n protected readonly hostClasses = computed(() => [\n `neu-avatar--${this.size()}`,\n `neu-avatar--${this.shape()}`,\n ...(!this.src() || this.imgError() ? [`neu-avatar--${this.color()}`] : []),\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,54 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
+
4
+ /**
5
+ * NeuralUI Badge Component
6
+ *
7
+ * Etiqueta de estado compacta y semántica. / Compact and semantic status label.
8
+ *
9
+ * Uso: <neu-badge variant="success">Activo</neu-badge>
10
+ * <neu-badge variant="danger" [dot]="true">Error</neu-badge>
11
+ */
12
+ class NeuBadgeComponent {
13
+ /** Variante semántica / Semantic variant */
14
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
15
+ /** Tamaño / Size */
16
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
17
+ /** Muestra un punto de color a la izquierda / Shows a colored dot on the left */
18
+ dot = input(false, ...(ngDevMode ? [{ debugName: "dot" }] : /* istanbul ignore next */ []));
19
+ /** Estilo con solo borde (outline) sin relleno / Border-only style (outline) without fill */
20
+ outline = input(false, ...(ngDevMode ? [{ debugName: "outline" }] : /* istanbul ignore next */ []));
21
+ /** Estilo completamente redondeado (pill) / Fully rounded style (pill) */
22
+ pill = input(true, ...(ngDevMode ? [{ debugName: "pill" }] : /* istanbul ignore next */ []));
23
+ hostClasses = computed(() => ({
24
+ 'neu-badge': true,
25
+ [`neu-badge--${this.variant()}`]: true,
26
+ [`neu-badge--${this.size()}`]: true,
27
+ 'neu-badge--outline': this.outline(),
28
+ 'neu-badge--pill': this.pill(),
29
+ 'neu-badge--dot': this.dot(),
30
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
31
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
32
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuBadgeComponent, isStandalone: true, selector: "neu-badge", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null }, outline: { classPropertyName: "outline", publicName: "outline", isSignal: true, isRequired: false, transformFunction: null }, pill: { classPropertyName: "pill", publicName: "pill", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, ngImport: i0, template: `
33
+ @if (dot()) {
34
+ <span class="neu-badge__dot" aria-hidden="true"></span>
35
+ }
36
+ <ng-content />
37
+ `, isInline: true, styles: [".neu-badge{display:inline-flex;align-items:center;gap:5px;font-family:var(--neu-font-sans);font-weight:500;line-height:1;white-space:nowrap}.neu-badge--sm{padding:2px 8px;font-size:11px;border-radius:var(--neu-radius-sm)}.neu-badge--md{padding:4px 10px;font-size:var(--neu-text-xs);border-radius:var(--neu-radius)}.neu-badge--pill.neu-badge--sm,.neu-badge--pill.neu-badge--md{border-radius:var(--neu-radius-full)}.neu-badge--default{background:var(--neu-surface-2);color:var(--neu-text-muted);border:1px solid var(--neu-border)}.neu-badge--default .neu-badge__dot{background:var(--neu-text-disabled)}.neu-badge--success{background:var(--neu-success-bg);color:var(--neu-success-text);border:1px solid transparent}.neu-badge--success .neu-badge__dot{background:var(--neu-success)}.neu-badge--info{background:var(--neu-info-bg);color:var(--neu-info-text);border:1px solid transparent}.neu-badge--info .neu-badge__dot{background:var(--neu-info)}.neu-badge--warning{background:var(--neu-warning-bg);color:var(--neu-warning-text);border:1px solid transparent}.neu-badge--warning .neu-badge__dot{background:var(--neu-warning)}.neu-badge--danger{background:var(--neu-error-bg);color:var(--neu-error-text);border:1px solid transparent}.neu-badge--danger .neu-badge__dot{background:var(--neu-error)}.neu-badge--outline{background:transparent}.neu-badge--outline.neu-badge--success{border-color:var(--neu-success);background:transparent}.neu-badge--outline.neu-badge--info{border-color:var(--neu-info);background:transparent}.neu-badge--outline.neu-badge--warning{border-color:var(--neu-warning);background:transparent}.neu-badge--outline.neu-badge--danger{border-color:var(--neu-error);background:transparent}.neu-badge__dot{width:6px;height:6px;border-radius:var(--neu-radius-full);flex-shrink:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
38
+ }
39
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuBadgeComponent, decorators: [{
40
+ type: Component,
41
+ args: [{ selector: 'neu-badge', imports: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': 'hostClasses()' }, template: `
42
+ @if (dot()) {
43
+ <span class="neu-badge__dot" aria-hidden="true"></span>
44
+ }
45
+ <ng-content />
46
+ `, styles: [".neu-badge{display:inline-flex;align-items:center;gap:5px;font-family:var(--neu-font-sans);font-weight:500;line-height:1;white-space:nowrap}.neu-badge--sm{padding:2px 8px;font-size:11px;border-radius:var(--neu-radius-sm)}.neu-badge--md{padding:4px 10px;font-size:var(--neu-text-xs);border-radius:var(--neu-radius)}.neu-badge--pill.neu-badge--sm,.neu-badge--pill.neu-badge--md{border-radius:var(--neu-radius-full)}.neu-badge--default{background:var(--neu-surface-2);color:var(--neu-text-muted);border:1px solid var(--neu-border)}.neu-badge--default .neu-badge__dot{background:var(--neu-text-disabled)}.neu-badge--success{background:var(--neu-success-bg);color:var(--neu-success-text);border:1px solid transparent}.neu-badge--success .neu-badge__dot{background:var(--neu-success)}.neu-badge--info{background:var(--neu-info-bg);color:var(--neu-info-text);border:1px solid transparent}.neu-badge--info .neu-badge__dot{background:var(--neu-info)}.neu-badge--warning{background:var(--neu-warning-bg);color:var(--neu-warning-text);border:1px solid transparent}.neu-badge--warning .neu-badge__dot{background:var(--neu-warning)}.neu-badge--danger{background:var(--neu-error-bg);color:var(--neu-error-text);border:1px solid transparent}.neu-badge--danger .neu-badge__dot{background:var(--neu-error)}.neu-badge--outline{background:transparent}.neu-badge--outline.neu-badge--success{border-color:var(--neu-success);background:transparent}.neu-badge--outline.neu-badge--info{border-color:var(--neu-info);background:transparent}.neu-badge--outline.neu-badge--warning{border-color:var(--neu-warning);background:transparent}.neu-badge--outline.neu-badge--danger{border-color:var(--neu-error);background:transparent}.neu-badge__dot{width:6px;height:6px;border-radius:var(--neu-radius-full);flex-shrink:0}\n"] }]
47
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }], outline: [{ type: i0.Input, args: [{ isSignal: true, alias: "outline", required: false }] }], pill: [{ type: i0.Input, args: [{ isSignal: true, alias: "pill", required: false }] }] } });
48
+
49
+ /**
50
+ * Generated bundle index. Do not edit.
51
+ */
52
+
53
+ export { NeuBadgeComponent };
54
+ //# sourceMappingURL=neural-ui-core-badge.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-badge.mjs","sources":["../../../../projects/ui-core/badge/neu-badge.component.ts","../../../../projects/ui-core/badge/neural-ui-core-badge.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n} from '@angular/core';\n\nexport type NeuBadgeVariant = 'default' | 'success' | 'info' | 'warning' | 'danger';\nexport type NeuBadgeSize = 'sm' | 'md';\n\n/**\n * NeuralUI Badge Component\n *\n * Etiqueta de estado compacta y semántica. / Compact and semantic status label.\n *\n * Uso: <neu-badge variant=\"success\">Activo</neu-badge>\n * <neu-badge variant=\"danger\" [dot]=\"true\">Error</neu-badge>\n */\n@Component({\n selector: 'neu-badge',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses()' },\n template: `\n @if (dot()) {\n <span class=\"neu-badge__dot\" aria-hidden=\"true\"></span>\n }\n <ng-content />\n `,\n styleUrl: './neu-badge.component.scss',\n})\nexport class NeuBadgeComponent {\n /** Variante semántica / Semantic variant */\n variant = input<NeuBadgeVariant>('default');\n\n /** Tamaño / Size */\n size = input<NeuBadgeSize>('md');\n\n /** Muestra un punto de color a la izquierda / Shows a colored dot on the left */\n dot = input<boolean>(false);\n\n /** Estilo con solo borde (outline) sin relleno / Border-only style (outline) without fill */\n outline = input<boolean>(false);\n\n /** Estilo completamente redondeado (pill) / Fully rounded style (pill) */\n pill = input<boolean>(true);\n\n readonly hostClasses = computed(() => ({\n 'neu-badge': true,\n [`neu-badge--${this.variant()}`]: true,\n [`neu-badge--${this.size()}`]: true,\n 'neu-badge--outline': this.outline(),\n 'neu-badge--pill': this.pill(),\n 'neu-badge--dot': this.dot(),\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,95 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, ElementRef, effect, Directive } from '@angular/core';
3
+
4
+ /**
5
+ * NeuralUI BlockUI Component
6
+ *
7
+ * Superposición que bloquea la interacción de un contenedor mientras está activa.
8
+ *
9
+ * Uso (componente):
10
+ * <div style="position:relative">
11
+ * <neu-block-ui [blocked]="isLoading" message="Cargando..." />
12
+ * <form>...</form>
13
+ * </div>
14
+ *
15
+ * Uso (directiva en el contenedor padre):
16
+ * <div [neuBlockUI]="isLoading">
17
+ * <form>...</form>
18
+ * </div>
19
+ */
20
+ class NeuBlockUIComponent {
21
+ /** Activa la superposición / Activates the overlay */
22
+ blocked = input(false, ...(ngDevMode ? [{ debugName: "blocked" }] : /* istanbul ignore next */ []));
23
+ /** Mensaje opcional bajo el spinner / Optional message below the spinner */
24
+ message = input('', ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
25
+ /** Variante de opacidad del overlay */
26
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
27
+ hostClasses = computed(() => ({
28
+ 'neu-block-ui': true,
29
+ [`neu-block-ui--${this.variant()}`]: true,
30
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
31
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuBlockUIComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
32
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuBlockUIComponent, isStandalone: true, selector: "neu-block-ui", inputs: { blocked: { classPropertyName: "blocked", publicName: "blocked", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "status" }, properties: { "class": "hostClasses()", "attr.aria-busy": "blocked()", "attr.aria-hidden": "!blocked()" } }, ngImport: i0, template: `
33
+ @if (blocked()) {
34
+ <div class="neu-block-ui__overlay">
35
+ <div class="neu-block-ui__content">
36
+ <span class="neu-block-ui__spinner" aria-hidden="true"></span>
37
+ @if (message()) {
38
+ <span class="neu-block-ui__message">{{ message() }}</span>
39
+ }
40
+ </div>
41
+ </div>
42
+ }
43
+ `, isInline: true, styles: ["@charset \"UTF-8\";.neu-block-ui{position:absolute;inset:0;z-index:100;pointer-events:none;display:contents}.neu-block-ui__overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--neu-block-ui-bg, rgba(255, 255, 255, .7));border-radius:var(--neu-block-ui-radius, inherit);pointer-events:all;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);z-index:100}.neu-block-ui--dark .neu-block-ui__overlay{background:var(--neu-block-ui-bg, rgba(0, 0, 0, .5))}.neu-block-ui--light .neu-block-ui__overlay{background:var(--neu-block-ui-bg, rgba(255, 255, 255, .9))}.neu-block-ui__content{display:flex;flex-direction:column;align-items:center;gap:10px}.neu-block-ui__spinner{display:inline-block;width:32px;height:32px;border:3px solid var(--neu-spinner-track, #e5e7eb);border-top-color:var(--neu-color-primary, #0ea5e9);border-radius:50%;animation:neu-block-ui-spin .75s linear infinite}@keyframes neu-block-ui-spin{to{transform:rotate(360deg)}}.neu-block-ui__message{font-size:.875rem;color:var(--neu-text-secondary, #6b7280);font-weight:500}.neu-block-ui__host{position:relative}.neu-block-ui__host--blocked{overflow:hidden;pointer-events:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
44
+ }
45
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuBlockUIComponent, decorators: [{
46
+ type: Component,
47
+ args: [{ selector: 'neu-block-ui', imports: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
48
+ '[class]': 'hostClasses()',
49
+ '[attr.aria-busy]': 'blocked()',
50
+ '[attr.aria-hidden]': '!blocked()',
51
+ role: 'status',
52
+ }, template: `
53
+ @if (blocked()) {
54
+ <div class="neu-block-ui__overlay">
55
+ <div class="neu-block-ui__content">
56
+ <span class="neu-block-ui__spinner" aria-hidden="true"></span>
57
+ @if (message()) {
58
+ <span class="neu-block-ui__message">{{ message() }}</span>
59
+ }
60
+ </div>
61
+ </div>
62
+ }
63
+ `, styles: ["@charset \"UTF-8\";.neu-block-ui{position:absolute;inset:0;z-index:100;pointer-events:none;display:contents}.neu-block-ui__overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--neu-block-ui-bg, rgba(255, 255, 255, .7));border-radius:var(--neu-block-ui-radius, inherit);pointer-events:all;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);z-index:100}.neu-block-ui--dark .neu-block-ui__overlay{background:var(--neu-block-ui-bg, rgba(0, 0, 0, .5))}.neu-block-ui--light .neu-block-ui__overlay{background:var(--neu-block-ui-bg, rgba(255, 255, 255, .9))}.neu-block-ui__content{display:flex;flex-direction:column;align-items:center;gap:10px}.neu-block-ui__spinner{display:inline-block;width:32px;height:32px;border:3px solid var(--neu-spinner-track, #e5e7eb);border-top-color:var(--neu-color-primary, #0ea5e9);border-radius:50%;animation:neu-block-ui-spin .75s linear infinite}@keyframes neu-block-ui-spin{to{transform:rotate(360deg)}}.neu-block-ui__message{font-size:.875rem;color:var(--neu-text-secondary, #6b7280);font-weight:500}.neu-block-ui__host{position:relative}.neu-block-ui__host--blocked{overflow:hidden;pointer-events:none}\n"] }]
64
+ }], propDecorators: { blocked: [{ type: i0.Input, args: [{ isSignal: true, alias: "blocked", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }] } });
65
+ /**
66
+ * Directive [neuBlockUI] — Aplica el efecto BlockUI al elemento host.
67
+ *
68
+ * Uso: <div [neuBlockUI]="loading$ | async">...</div>
69
+ */
70
+ class NeuBlockUIDirective {
71
+ neuBlockUI = input(false, ...(ngDevMode ? [{ debugName: "neuBlockUI" }] : /* istanbul ignore next */ []));
72
+ _el = inject((ElementRef));
73
+ constructor() {
74
+ effect(() => {
75
+ const blocked = this.neuBlockUI();
76
+ this._el.nativeElement.classList.toggle('neu-block-ui__host--blocked', blocked);
77
+ });
78
+ }
79
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuBlockUIDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
80
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.8", type: NeuBlockUIDirective, isStandalone: true, selector: "[neuBlockUI]", inputs: { neuBlockUI: { classPropertyName: "neuBlockUI", publicName: "neuBlockUI", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.neu-block-ui__host": "true" } }, ngImport: i0 });
81
+ }
82
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuBlockUIDirective, decorators: [{
83
+ type: Directive,
84
+ args: [{
85
+ selector: '[neuBlockUI]',
86
+ host: { '[class.neu-block-ui__host]': 'true' },
87
+ }]
88
+ }], ctorParameters: () => [], propDecorators: { neuBlockUI: [{ type: i0.Input, args: [{ isSignal: true, alias: "neuBlockUI", required: false }] }] } });
89
+
90
+ /**
91
+ * Generated bundle index. Do not edit.
92
+ */
93
+
94
+ export { NeuBlockUIComponent, NeuBlockUIDirective };
95
+ //# sourceMappingURL=neural-ui-core-block-ui.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-block-ui.mjs","sources":["../../../../projects/ui-core/block-ui/neu-block-ui.component.ts","../../../../projects/ui-core/block-ui/neural-ui-core-block-ui.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n Directive,\n ElementRef,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n} from '@angular/core';\n\n/**\n * NeuralUI BlockUI Component\n *\n * Superposición que bloquea la interacción de un contenedor mientras está activa.\n *\n * Uso (componente):\n * <div style=\"position:relative\">\n * <neu-block-ui [blocked]=\"isLoading\" message=\"Cargando...\" />\n * <form>...</form>\n * </div>\n *\n * Uso (directiva en el contenedor padre):\n * <div [neuBlockUI]=\"isLoading\">\n * <form>...</form>\n * </div>\n */\n@Component({\n selector: 'neu-block-ui',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'hostClasses()',\n '[attr.aria-busy]': 'blocked()',\n '[attr.aria-hidden]': '!blocked()',\n role: 'status',\n },\n template: `\n @if (blocked()) {\n <div class=\"neu-block-ui__overlay\">\n <div class=\"neu-block-ui__content\">\n <span class=\"neu-block-ui__spinner\" aria-hidden=\"true\"></span>\n @if (message()) {\n <span class=\"neu-block-ui__message\">{{ message() }}</span>\n }\n </div>\n </div>\n }\n `,\n styleUrl: './neu-block-ui.component.scss',\n})\nexport class NeuBlockUIComponent {\n /** Activa la superposición / Activates the overlay */\n readonly blocked = input<boolean>(false);\n\n /** Mensaje opcional bajo el spinner / Optional message below the spinner */\n readonly message = input<string>('');\n\n /** Variante de opacidad del overlay */\n readonly variant = input<'default' | 'light' | 'dark'>('default');\n\n readonly hostClasses = computed(() => ({\n 'neu-block-ui': true,\n [`neu-block-ui--${this.variant()}`]: true,\n }));\n}\n\n/**\n * Directive [neuBlockUI] — Aplica el efecto BlockUI al elemento host.\n *\n * Uso: <div [neuBlockUI]=\"loading$ | async\">...</div>\n */\n@Directive({\n selector: '[neuBlockUI]',\n host: { '[class.neu-block-ui__host]': 'true' },\n})\nexport class NeuBlockUIDirective {\n readonly neuBlockUI = input<boolean>(false);\n\n private readonly _el = inject(ElementRef<HTMLElement>);\n\n constructor() {\n effect(() => {\n const blocked = this.neuBlockUI();\n this._el.nativeElement.classList.toggle('neu-block-ui__host--blocked', blocked);\n });\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,84 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
+ import { RouterLink } from '@angular/router';
4
+
5
+ /**
6
+ * NeuralUI Breadcrumb Component
7
+ *
8
+ * Ruta de navegación jerárquica. El último elemento se muestra
9
+ * como activo (sin enlace).
10
+ *
11
+ * Uso:
12
+ * <neu-breadcrumb [items]="breadcrumbs" />
13
+ *
14
+ * items = [
15
+ * { label: 'Inicio', route: '/' },
16
+ * { label: 'Componentes', route: '/components' },
17
+ * { label: 'Breadcrumb' },
18
+ * ];
19
+ */
20
+ class NeuBreadcrumbComponent {
21
+ /** Lista de ítems de navegación / Navigation item list */
22
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
23
+ /** Separador personalizable / Customizable separator */
24
+ separator = input('/', ...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
25
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
26
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuBreadcrumbComponent, isStandalone: true, selector: "neu-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
27
+ <nav class="neu-breadcrumb" aria-label="Breadcrumb">
28
+ <ol class="neu-breadcrumb__list">
29
+ @for (item of items(); track item.label; let last = $last) {
30
+ <li class="neu-breadcrumb__item" [class.neu-breadcrumb__item--active]="last">
31
+ @if (!last) {
32
+ @if (item.route) {
33
+ <a class="neu-breadcrumb__link" [routerLink]="item.route">{{ item.label }}</a>
34
+ } @else if (item.url) {
35
+ <a class="neu-breadcrumb__link" [href]="item.url" target="_blank" rel="noopener">{{
36
+ item.label
37
+ }}</a>
38
+ } @else {
39
+ <span class="neu-breadcrumb__link">{{ item.label }}</span>
40
+ }
41
+ <span class="neu-breadcrumb__separator" aria-hidden="true">{{ separator() }}</span>
42
+ } @else {
43
+ <span class="neu-breadcrumb__current" aria-current="page">{{ item.label }}</span>
44
+ }
45
+ </li>
46
+ }
47
+ </ol>
48
+ </nav>
49
+ `, isInline: true, styles: [".neu-breadcrumb{display:inline-flex}.neu-breadcrumb__list{display:flex;align-items:center;flex-wrap:wrap;gap:4px;list-style:none;margin:0;padding:0;font-family:var(--neu-font-sans);font-size:var(--neu-text-sm)}.neu-breadcrumb__item{display:inline-flex;align-items:center;gap:4px}.neu-breadcrumb__link{color:var(--neu-text-muted);text-decoration:none;transition:color var(--neu-transition);border-radius:var(--neu-radius-xs);outline:none}.neu-breadcrumb__link:hover{color:var(--neu-text)}.neu-breadcrumb__link:focus-visible{outline:2px solid var(--neu-primary);outline-offset:2px}.neu-breadcrumb__separator{color:var(--neu-text-disabled);font-size:var(--neu-text-xs);-webkit-user-select:none;user-select:none;margin:0 2px}.neu-breadcrumb__current{color:var(--neu-text);font-weight:500}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
50
+ }
51
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuBreadcrumbComponent, decorators: [{
52
+ type: Component,
53
+ args: [{ selector: 'neu-breadcrumb', imports: [RouterLink], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: `
54
+ <nav class="neu-breadcrumb" aria-label="Breadcrumb">
55
+ <ol class="neu-breadcrumb__list">
56
+ @for (item of items(); track item.label; let last = $last) {
57
+ <li class="neu-breadcrumb__item" [class.neu-breadcrumb__item--active]="last">
58
+ @if (!last) {
59
+ @if (item.route) {
60
+ <a class="neu-breadcrumb__link" [routerLink]="item.route">{{ item.label }}</a>
61
+ } @else if (item.url) {
62
+ <a class="neu-breadcrumb__link" [href]="item.url" target="_blank" rel="noopener">{{
63
+ item.label
64
+ }}</a>
65
+ } @else {
66
+ <span class="neu-breadcrumb__link">{{ item.label }}</span>
67
+ }
68
+ <span class="neu-breadcrumb__separator" aria-hidden="true">{{ separator() }}</span>
69
+ } @else {
70
+ <span class="neu-breadcrumb__current" aria-current="page">{{ item.label }}</span>
71
+ }
72
+ </li>
73
+ }
74
+ </ol>
75
+ </nav>
76
+ `, styles: [".neu-breadcrumb{display:inline-flex}.neu-breadcrumb__list{display:flex;align-items:center;flex-wrap:wrap;gap:4px;list-style:none;margin:0;padding:0;font-family:var(--neu-font-sans);font-size:var(--neu-text-sm)}.neu-breadcrumb__item{display:inline-flex;align-items:center;gap:4px}.neu-breadcrumb__link{color:var(--neu-text-muted);text-decoration:none;transition:color var(--neu-transition);border-radius:var(--neu-radius-xs);outline:none}.neu-breadcrumb__link:hover{color:var(--neu-text)}.neu-breadcrumb__link:focus-visible{outline:2px solid var(--neu-primary);outline-offset:2px}.neu-breadcrumb__separator{color:var(--neu-text-disabled);font-size:var(--neu-text-xs);-webkit-user-select:none;user-select:none;margin:0 2px}.neu-breadcrumb__current{color:var(--neu-text);font-weight:500}\n"] }]
77
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }] } });
78
+
79
+ /**
80
+ * Generated bundle index. Do not edit.
81
+ */
82
+
83
+ export { NeuBreadcrumbComponent };
84
+ //# sourceMappingURL=neural-ui-core-breadcrumb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-breadcrumb.mjs","sources":["../../../../projects/ui-core/breadcrumb/neu-breadcrumb.component.ts","../../../../projects/ui-core/breadcrumb/neural-ui-core-breadcrumb.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, ViewEncapsulation, input } from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nexport interface NeuBreadcrumbItem {\n /** Etiqueta visible / Visible label */\n label: string;\n /** Ruta interna (RouterLink) / Internal route (RouterLink) */\n route?: string | string[];\n /** URL externa / External URL */\n url?: string;\n /** Icono Lucide opcional / Optional Lucide icon */\n icon?: string;\n}\n\n/**\n * NeuralUI Breadcrumb Component\n *\n * Ruta de navegación jerárquica. El último elemento se muestra\n * como activo (sin enlace).\n *\n * Uso:\n * <neu-breadcrumb [items]=\"breadcrumbs\" />\n *\n * items = [\n * { label: 'Inicio', route: '/' },\n * { label: 'Componentes', route: '/components' },\n * { label: 'Breadcrumb' },\n * ];\n */\n@Component({\n selector: 'neu-breadcrumb',\n imports: [RouterLink],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <nav class=\"neu-breadcrumb\" aria-label=\"Breadcrumb\">\n <ol class=\"neu-breadcrumb__list\">\n @for (item of items(); track item.label; let last = $last) {\n <li class=\"neu-breadcrumb__item\" [class.neu-breadcrumb__item--active]=\"last\">\n @if (!last) {\n @if (item.route) {\n <a class=\"neu-breadcrumb__link\" [routerLink]=\"item.route\">{{ item.label }}</a>\n } @else if (item.url) {\n <a class=\"neu-breadcrumb__link\" [href]=\"item.url\" target=\"_blank\" rel=\"noopener\">{{\n item.label\n }}</a>\n } @else {\n <span class=\"neu-breadcrumb__link\">{{ item.label }}</span>\n }\n <span class=\"neu-breadcrumb__separator\" aria-hidden=\"true\">{{ separator() }}</span>\n } @else {\n <span class=\"neu-breadcrumb__current\" aria-current=\"page\">{{ item.label }}</span>\n }\n </li>\n }\n </ol>\n </nav>\n `,\n styleUrl: './neu-breadcrumb.component.scss',\n})\nexport class NeuBreadcrumbComponent {\n /** Lista de ítems de navegación / Navigation item list */\n items = input<NeuBreadcrumbItem[]>([]);\n\n /** Separador personalizable / Customizable separator */\n separator = input<string>('/');\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;;AAcA;;;;;;;;;;;;;;AAcG;MAgCU,sBAAsB,CAAA;;AAEjC,IAAA,KAAK,GAAG,KAAK,CAAsB,EAAE,4EAAC;;AAGtC,IAAA,SAAS,GAAG,KAAK,CAAS,GAAG,gFAAC;uGALnB,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAtB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,sBAAsB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA1BvB;;;;;;;;;;;;;;;;;;;;;;;AAuBT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,oxBAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EA1BS,UAAU,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,aAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,OAAA,EAAA,MAAA,EAAA,YAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,YAAA,EAAA,YAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FA6BT,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBA/BlC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,gBAAgB,EAAA,OAAA,EACjB,CAAC,UAAU,CAAC,EAAA,aAAA,EACN,iBAAiB,CAAC,IAAI,EAAA,eAAA,EACpB,uBAAuB,CAAC,MAAM,EAAA,QAAA,EACrC;;;;;;;;;;;;;;;;;;;;;;;AAuBT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,oxBAAA,CAAA,EAAA;;;ACzDH;;AAEG;;;;"}
@@ -0,0 +1,125 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, computed, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
+ import { NeuIconComponent } from '@neural-ui/core/icon';
4
+
5
+ /**
6
+ * NeuralUI Button Component
7
+ *
8
+ * Uso: <button neu-button variant="primary" size="md">Texto</button>
9
+ * Con icono: <button neu-button icon="lucideSave">Guardar</button>
10
+ * Solo icono: <button neu-button icon="lucideTrash2" [iconOnly]="true" />
11
+ *
12
+ * Signals: variant, size, disabled, loading, fullWidth, icon, iconPosition, iconOnly
13
+ * son inputs reactivos. El estado se computa automáticamente con computed().
14
+ */
15
+ class NeuButtonComponent {
16
+ /** Variante visual del botón / Visual button variant */
17
+ variant = input('primary', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
18
+ /** Tamaño del botón / Button size */
19
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
20
+ /** Deshabilita el botón y bloquea la interacción / Disables the button and blocks interaction */
21
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
22
+ /** Muestra un spinner y deshabilita mientras se procesa / Shows a spinner and disables while processing */
23
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
24
+ /** Ocupa el 100% del ancho de su contenedor / Takes up 100% of its container width */
25
+ fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : /* istanbul ignore next */ []));
26
+ /** Nombre del icono Lucide (ej: 'lucideSave', 'lucidePlus') / Lucide icon name (e.g. 'lucideSave', 'lucidePlus') */
27
+ icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
28
+ /** Posición del icono respecto al texto */
29
+ iconPosition = input('left', ...(ngDevMode ? [{ debugName: "iconPosition" }] : /* istanbul ignore next */ []));
30
+ /** Modo solo-icono: aplica padding cuadrado y oculta el ng-content / Icon-only mode: applies square padding and hides ng-content */
31
+ iconOnly = input(false, ...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
32
+ /** Etiqueta accesible obligatoria cuando se usa iconOnly (WCAG 4.1.2) / Required accessible label when using iconOnly (WCAG 4.1.2) */
33
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
34
+ /** Emite el evento de click cuando el botón está activo / Emits the click event when the button is active */
35
+ neuClick = output();
36
+ /** @internal — reenvía el click nativo al output Angular / forwards the native click to the Angular output */
37
+ _onHostClick(event) {
38
+ if (!this.isDisabled()) {
39
+ this.neuClick.emit(event);
40
+ }
41
+ }
42
+ isDisabled = computed(() => this.disabled() || this.loading(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
43
+ hasIcon = computed(() => !!this.icon(), ...(ngDevMode ? [{ debugName: "hasIcon" }] : /* istanbul ignore next */ []));
44
+ iconSize = computed(() => {
45
+ const map = { sm: '14px', md: '16px', lg: '18px' };
46
+ return map[this.size()];
47
+ }, ...(ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []));
48
+ hostClasses = computed(() => ({
49
+ 'neu-button': true,
50
+ [`neu-button--${this.variant()}`]: true,
51
+ [`neu-button--${this.size()}`]: true,
52
+ 'neu-button--loading': this.loading(),
53
+ 'neu-button--disabled': this.isDisabled(),
54
+ 'neu-button--full-width': this.fullWidth(),
55
+ 'neu-button--icon-only': this.iconOnly(),
56
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
57
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
58
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuButtonComponent, isStandalone: true, selector: "button[neu-button]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { neuClick: "neuClick" }, host: { listeners: { "click": "_onHostClick($event)" }, properties: { "class": "hostClasses()", "attr.disabled": "isDisabled() ? \"\" : null", "attr.aria-disabled": "isDisabled()", "attr.aria-busy": "loading()", "attr.aria-label": "ariaLabel() || null" } }, ngImport: i0, template: `
59
+ @if (loading()) {
60
+ <span class="neu-button__spinner" aria-hidden="true">
61
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
62
+ <circle
63
+ cx="12"
64
+ cy="12"
65
+ r="10"
66
+ stroke="currentColor"
67
+ stroke-width="3"
68
+ stroke-linecap="round"
69
+ stroke-dasharray="31.416"
70
+ stroke-dashoffset="10"
71
+ />
72
+ </svg>
73
+ </span>
74
+ }
75
+ @if (hasIcon() && iconPosition() === 'left') {
76
+ <neu-icon [name]="icon()" [size]="iconSize()" strokeWidth="2" aria-hidden="true" />
77
+ }
78
+ <ng-content />
79
+ @if (hasIcon() && iconPosition() === 'right') {
80
+ <neu-icon [name]="icon()" [size]="iconSize()" strokeWidth="2" aria-hidden="true" />
81
+ }
82
+ `, isInline: true, styles: [".neu-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--neu-space-2);border:1px solid transparent;border-radius:var(--neu-radius);font-family:var(--neu-font-sans);font-weight:500;line-height:1;cursor:pointer;text-decoration:none;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background-color var(--neu-transition),border-color var(--neu-transition),color var(--neu-transition),box-shadow var(--neu-transition),opacity var(--neu-transition);outline:none}.neu-button:focus-visible{box-shadow:var(--neu-focus-ring-strong)}.neu-button--sm{padding:var(--neu-space-2) var(--neu-space-3);font-size:var(--neu-text-sm);border-radius:var(--neu-radius-sm)}.neu-button--md{padding:var(--neu-space-2) var(--neu-space-5);font-size:var(--neu-text-base)}@media(min-width:400px){.neu-button--md{padding:.625rem var(--neu-space-6)}}.neu-button--lg{padding:var(--neu-space-3) var(--neu-space-8);font-size:var(--neu-text-lg)}.neu-button--full-width{width:100%}.neu-button--primary{background:var(--neu-primary);color:var(--neu-primary-fg);border-color:var(--neu-primary)}.neu-button--primary:hover:not(:disabled):not(.neu-button--disabled){background:var(--neu-primary-dark);border-color:var(--neu-primary-dark);box-shadow:var(--neu-shadow-glow)}.neu-button--primary:active:not(:disabled){background:var(--neu-primary-dark);transform:translateY(1px)}.neu-button--secondary{background:var(--neu-secondary);color:var(--neu-secondary-fg);border-color:var(--neu-secondary)}.neu-button--secondary:hover:not(:disabled):not(.neu-button--disabled){background:var(--neu-secondary-dark);border-color:var(--neu-secondary-dark)}.neu-button--outline{background:transparent;color:var(--neu-primary-light);border-color:var(--neu-primary)}.neu-button--outline:hover:not(:disabled):not(.neu-button--disabled){background:var(--neu-primary-50);border-color:var(--neu-primary-light)}.neu-button--ghost{background:transparent;color:var(--neu-text-muted);border-color:transparent}.neu-button--ghost:hover:not(:disabled):not(.neu-button--disabled){background:var(--neu-surface-2);color:var(--neu-text)}.neu-button--danger{background:var(--neu-error);color:var(--neu-primary-fg);border-color:var(--neu-error)}.neu-button--danger:hover:not(:disabled):not(.neu-button--disabled){background:#dc2626;border-color:#dc2626}.neu-button--disabled,.neu-button:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.neu-button--loading{cursor:wait;pointer-events:none}.neu-button--icon-only.neu-button--sm{width:30px;height:30px;padding:0}.neu-button--icon-only.neu-button--md{width:38px;height:38px;padding:0}.neu-button--icon-only.neu-button--lg{width:46px;height:46px;padding:0}.neu-button__spinner{display:inline-flex;flex-shrink:0;width:1em;height:1em}.neu-button__spinner svg{width:100%;height:100%;animation:neu-spin .8s linear infinite}@keyframes neu-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "component", type: NeuIconComponent, selector: "neu-icon", inputs: ["name", "strokeWidth", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
83
+ }
84
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuButtonComponent, decorators: [{
85
+ type: Component,
86
+ args: [{ selector: 'button[neu-button]', imports: [NeuIconComponent], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
87
+ '[class]': 'hostClasses()',
88
+ '[attr.disabled]': 'isDisabled() ? "" : null',
89
+ '[attr.aria-disabled]': 'isDisabled()',
90
+ '[attr.aria-busy]': 'loading()',
91
+ '[attr.aria-label]': 'ariaLabel() || null',
92
+ '(click)': '_onHostClick($event)',
93
+ }, template: `
94
+ @if (loading()) {
95
+ <span class="neu-button__spinner" aria-hidden="true">
96
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
97
+ <circle
98
+ cx="12"
99
+ cy="12"
100
+ r="10"
101
+ stroke="currentColor"
102
+ stroke-width="3"
103
+ stroke-linecap="round"
104
+ stroke-dasharray="31.416"
105
+ stroke-dashoffset="10"
106
+ />
107
+ </svg>
108
+ </span>
109
+ }
110
+ @if (hasIcon() && iconPosition() === 'left') {
111
+ <neu-icon [name]="icon()" [size]="iconSize()" strokeWidth="2" aria-hidden="true" />
112
+ }
113
+ <ng-content />
114
+ @if (hasIcon() && iconPosition() === 'right') {
115
+ <neu-icon [name]="icon()" [size]="iconSize()" strokeWidth="2" aria-hidden="true" />
116
+ }
117
+ `, styles: [".neu-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--neu-space-2);border:1px solid transparent;border-radius:var(--neu-radius);font-family:var(--neu-font-sans);font-weight:500;line-height:1;cursor:pointer;text-decoration:none;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background-color var(--neu-transition),border-color var(--neu-transition),color var(--neu-transition),box-shadow var(--neu-transition),opacity var(--neu-transition);outline:none}.neu-button:focus-visible{box-shadow:var(--neu-focus-ring-strong)}.neu-button--sm{padding:var(--neu-space-2) var(--neu-space-3);font-size:var(--neu-text-sm);border-radius:var(--neu-radius-sm)}.neu-button--md{padding:var(--neu-space-2) var(--neu-space-5);font-size:var(--neu-text-base)}@media(min-width:400px){.neu-button--md{padding:.625rem var(--neu-space-6)}}.neu-button--lg{padding:var(--neu-space-3) var(--neu-space-8);font-size:var(--neu-text-lg)}.neu-button--full-width{width:100%}.neu-button--primary{background:var(--neu-primary);color:var(--neu-primary-fg);border-color:var(--neu-primary)}.neu-button--primary:hover:not(:disabled):not(.neu-button--disabled){background:var(--neu-primary-dark);border-color:var(--neu-primary-dark);box-shadow:var(--neu-shadow-glow)}.neu-button--primary:active:not(:disabled){background:var(--neu-primary-dark);transform:translateY(1px)}.neu-button--secondary{background:var(--neu-secondary);color:var(--neu-secondary-fg);border-color:var(--neu-secondary)}.neu-button--secondary:hover:not(:disabled):not(.neu-button--disabled){background:var(--neu-secondary-dark);border-color:var(--neu-secondary-dark)}.neu-button--outline{background:transparent;color:var(--neu-primary-light);border-color:var(--neu-primary)}.neu-button--outline:hover:not(:disabled):not(.neu-button--disabled){background:var(--neu-primary-50);border-color:var(--neu-primary-light)}.neu-button--ghost{background:transparent;color:var(--neu-text-muted);border-color:transparent}.neu-button--ghost:hover:not(:disabled):not(.neu-button--disabled){background:var(--neu-surface-2);color:var(--neu-text)}.neu-button--danger{background:var(--neu-error);color:var(--neu-primary-fg);border-color:var(--neu-error)}.neu-button--danger:hover:not(:disabled):not(.neu-button--disabled){background:#dc2626;border-color:#dc2626}.neu-button--disabled,.neu-button:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.neu-button--loading{cursor:wait;pointer-events:none}.neu-button--icon-only.neu-button--sm{width:30px;height:30px;padding:0}.neu-button--icon-only.neu-button--md{width:38px;height:38px;padding:0}.neu-button--icon-only.neu-button--lg{width:46px;height:46px;padding:0}.neu-button__spinner{display:inline-flex;flex-shrink:0;width:1em;height:1em}.neu-button__spinner svg{width:100%;height:100%;animation:neu-spin .8s linear infinite}@keyframes neu-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
118
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPosition", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], neuClick: [{ type: i0.Output, args: ["neuClick"] }] } });
119
+
120
+ /**
121
+ * Generated bundle index. Do not edit.
122
+ */
123
+
124
+ export { NeuButtonComponent };
125
+ //# sourceMappingURL=neural-ui-core-button.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-button.mjs","sources":["../../../../projects/ui-core/button/neu-button.component.ts","../../../../projects/ui-core/button/neural-ui-core-button.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n output,\n} from '@angular/core';\nimport { NeuIconComponent } from '@neural-ui/core/icon';\n\nexport type NeuButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'outline';\nexport type NeuButtonSize = 'sm' | 'md' | 'lg';\nexport type NeuButtonIconPosition = 'left' | 'right';\n\n/**\n * NeuralUI Button Component\n *\n * Uso: <button neu-button variant=\"primary\" size=\"md\">Texto</button>\n * Con icono: <button neu-button icon=\"lucideSave\">Guardar</button>\n * Solo icono: <button neu-button icon=\"lucideTrash2\" [iconOnly]=\"true\" />\n *\n * Signals: variant, size, disabled, loading, fullWidth, icon, iconPosition, iconOnly\n * son inputs reactivos. El estado se computa automáticamente con computed().\n */\n@Component({\n selector: 'button[neu-button]',\n imports: [NeuIconComponent],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'hostClasses()',\n '[attr.disabled]': 'isDisabled() ? \"\" : null',\n '[attr.aria-disabled]': 'isDisabled()',\n '[attr.aria-busy]': 'loading()',\n '[attr.aria-label]': 'ariaLabel() || null',\n '(click)': '_onHostClick($event)',\n },\n template: `\n @if (loading()) {\n <span class=\"neu-button__spinner\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-dasharray=\"31.416\"\n stroke-dashoffset=\"10\"\n />\n </svg>\n </span>\n }\n @if (hasIcon() && iconPosition() === 'left') {\n <neu-icon [name]=\"icon()\" [size]=\"iconSize()\" strokeWidth=\"2\" aria-hidden=\"true\" />\n }\n <ng-content />\n @if (hasIcon() && iconPosition() === 'right') {\n <neu-icon [name]=\"icon()\" [size]=\"iconSize()\" strokeWidth=\"2\" aria-hidden=\"true\" />\n }\n `,\n styleUrl: './neu-button.component.scss',\n})\nexport class NeuButtonComponent {\n /** Variante visual del botón / Visual button variant */\n variant = input<NeuButtonVariant>('primary');\n\n /** Tamaño del botón / Button size */\n size = input<NeuButtonSize>('md');\n\n /** Deshabilita el botón y bloquea la interacción / Disables the button and blocks interaction */\n disabled = input<boolean>(false);\n\n /** Muestra un spinner y deshabilita mientras se procesa / Shows a spinner and disables while processing */\n loading = input<boolean>(false);\n\n /** Ocupa el 100% del ancho de su contenedor / Takes up 100% of its container width */\n fullWidth = input<boolean>(false);\n\n /** Nombre del icono Lucide (ej: 'lucideSave', 'lucidePlus') / Lucide icon name (e.g. 'lucideSave', 'lucidePlus') */\n icon = input<string>('');\n\n /** Posición del icono respecto al texto */\n iconPosition = input<NeuButtonIconPosition>('left');\n\n /** Modo solo-icono: aplica padding cuadrado y oculta el ng-content / Icon-only mode: applies square padding and hides ng-content */\n iconOnly = input<boolean>(false);\n\n /** Etiqueta accesible obligatoria cuando se usa iconOnly (WCAG 4.1.2) / Required accessible label when using iconOnly (WCAG 4.1.2) */\n ariaLabel = input<string>('');\n\n /** Emite el evento de click cuando el botón está activo / Emits the click event when the button is active */\n neuClick = output<MouseEvent>();\n\n /** @internal — reenvía el click nativo al output Angular / forwards the native click to the Angular output */\n _onHostClick(event: MouseEvent): void {\n if (!this.isDisabled()) {\n this.neuClick.emit(event);\n }\n }\n\n readonly isDisabled = computed(() => this.disabled() || this.loading());\n readonly hasIcon = computed(() => !!this.icon());\n\n readonly iconSize = computed(() => {\n const map: Record<NeuButtonSize, string> = { sm: '14px', md: '16px', lg: '18px' };\n return map[this.size()];\n });\n\n readonly hostClasses = computed(() => ({\n 'neu-button': true,\n [`neu-button--${this.variant()}`]: true,\n [`neu-button--${this.size()}`]: true,\n 'neu-button--loading': this.loading(),\n 'neu-button--disabled': this.isDisabled(),\n 'neu-button--full-width': this.fullWidth(),\n 'neu-button--icon-only': this.iconOnly(),\n }));\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}