@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,323 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, signal, computed, inject, ElementRef, forwardRef, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
+
5
+ let _seq = 0;
6
+ /**
7
+ * NeuralUI Knob Component
8
+ *
9
+ * Control de dial rotatorio para selección de valores numéricos. Implementa CVA.
10
+ *
11
+ * Uso: <neu-knob [min]="0" [max]="100" formControlName="volume" />
12
+ */
13
+ class NeuKnobComponent {
14
+ /** Valor mínimo / Min value */
15
+ min = input(0, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
16
+ /** Valor máximo / Max value */
17
+ max = input(100, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
18
+ /** Incremento por paso / Step increment */
19
+ step = input(1, ...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
20
+ /** Tamaño del dial en px / Dial size in px */
21
+ size = input(80, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
22
+ /** Muestra el valor numérico en el centro / Shows the numeric value in the center */
23
+ showValue = input(true, ...(ngDevMode ? [{ debugName: "showValue" }] : /* istanbul ignore next */ []));
24
+ /** Etiqueta visible / Visible label */
25
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
26
+ /** Emitido en cada cambio / Emitted on each change */
27
+ valueChange = output();
28
+ _id = `neu-knob-${++_seq}`;
29
+ _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
30
+ _value = signal(0, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
31
+ _strokeWidth = computed(() => Math.max(4, this.size() * 0.1), ...(ngDevMode ? [{ debugName: "_strokeWidth" }] : /* istanbul ignore next */ []));
32
+ _radius = computed(() => (this.size() - this._strokeWidth()) / 2 - 2, ...(ngDevMode ? [{ debugName: "_radius" }] : /* istanbul ignore next */ []));
33
+ _circumference = computed(() => 2 * Math.PI * this._radius(), ...(ngDevMode ? [{ debugName: "_circumference" }] : /* istanbul ignore next */ []));
34
+ /** Arc spans 260° (from -40° to 220°) */
35
+ _arcLength = computed(() => (260 / 360) * this._circumference(), ...(ngDevMode ? [{ debugName: "_arcLength" }] : /* istanbul ignore next */ []));
36
+ _normalizedValue = computed(() => {
37
+ const range = this.max() - this.min();
38
+ return range === 0 ? 0 : (this._value() - this.min()) / range;
39
+ }, ...(ngDevMode ? [{ debugName: "_normalizedValue" }] : /* istanbul ignore next */ []));
40
+ _arcDashArray = computed(() => `${this._arcLength()} ${this._circumference()}`, ...(ngDevMode ? [{ debugName: "_arcDashArray" }] : /* istanbul ignore next */ []));
41
+ _arcDashOffset = computed(() => this._arcLength() * (1 - this._normalizedValue()), ...(ngDevMode ? [{ debugName: "_arcDashOffset" }] : /* istanbul ignore next */ []));
42
+ // Indicator position on the track (relative to SVG center)
43
+ // El arco empieza en rotate(-220°) desde el Este del SVG.
44
+ // El punto indicador reposa en el Norte (12 en punto).
45
+ // Para que el indicador siga el arco, la rotación CW debe ser:
46
+ // θ = 230° + normalizedValue × 260°
47
+ // (arco de 7:40 → 1:20 pasando por 12:00 en la mitad)
48
+ // Arc starts at rotate(-220°) from East; indicator natural position is North (12 o'clock).
49
+ // Correct clockwise rotation formula: θ = 230 + normalizedValue × 260.
50
+ _indicatorAngle = computed(() => this._normalizedValue() * 260 + 230, ...(ngDevMode ? [{ debugName: "_indicatorAngle" }] : /* istanbul ignore next */ []));
51
+ _indicatorX = computed(() => this.size() / 2, ...(ngDevMode ? [{ debugName: "_indicatorX" }] : /* istanbul ignore next */ []));
52
+ _indicatorY = computed(() => this.size() / 2 - this._radius(), ...(ngDevMode ? [{ debugName: "_indicatorY" }] : /* istanbul ignore next */ []));
53
+ _dragStartAngle = 0;
54
+ _dragStartValue = 0;
55
+ _onChange = () => { };
56
+ _onTouched = () => { };
57
+ _el = inject((ElementRef));
58
+ /** Calcula el ángulo en grados desde el centro del dial al punto (x, y).
59
+ * Calculates the angle in degrees from the dial center to point (x, y). */
60
+ _getAngle(clientX, clientY) {
61
+ const dial = this._el.nativeElement.querySelector('.neu-knob__dial');
62
+ const rect = dial
63
+ ? dial.getBoundingClientRect()
64
+ : this._el.nativeElement.getBoundingClientRect();
65
+ const cx = rect.left + rect.width / 2;
66
+ const cy = rect.top + rect.height / 2;
67
+ return Math.atan2(clientY - cy, clientX - cx) * (180 / Math.PI);
68
+ }
69
+ hostClasses = computed(() => ({
70
+ 'neu-knob': true,
71
+ 'neu-knob--disabled': this._cvaDisabled(),
72
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
73
+ writeValue(val) {
74
+ this._value.set(this._clamp(val ?? this.min()));
75
+ }
76
+ registerOnChange(fn) {
77
+ this._onChange = fn;
78
+ }
79
+ registerOnTouched(fn) {
80
+ this._onTouched = fn;
81
+ }
82
+ setDisabledState(disabled) {
83
+ this._cvaDisabled.set(disabled);
84
+ }
85
+ onMouseDown(event) {
86
+ if (this._cvaDisabled())
87
+ return;
88
+ event.preventDefault();
89
+ this._dragStartAngle = this._getAngle(event.clientX, event.clientY);
90
+ this._dragStartValue = this._value();
91
+ const onMove = (e) => this._applyCircularDrag(e.clientX, e.clientY);
92
+ const onUp = () => {
93
+ window.removeEventListener('mousemove', onMove);
94
+ window.removeEventListener('mouseup', onUp);
95
+ };
96
+ window.addEventListener('mousemove', onMove);
97
+ window.addEventListener('mouseup', onUp);
98
+ this._onTouched();
99
+ }
100
+ onTouchStart(event) {
101
+ if (this._cvaDisabled())
102
+ return;
103
+ event.preventDefault();
104
+ this._dragStartAngle = this._getAngle(event.touches[0].clientX, event.touches[0].clientY);
105
+ this._dragStartValue = this._value();
106
+ const onMove = (e) => this._applyCircularDrag(e.touches[0].clientX, e.touches[0].clientY);
107
+ const onEnd = () => {
108
+ window.removeEventListener('touchmove', onMove);
109
+ window.removeEventListener('touchend', onEnd);
110
+ };
111
+ window.addEventListener('touchmove', onMove, { passive: false });
112
+ window.addEventListener('touchend', onEnd);
113
+ this._onTouched();
114
+ }
115
+ onKeyDown(event) {
116
+ if (this._cvaDisabled())
117
+ return;
118
+ let delta = 0;
119
+ if (event.key === 'ArrowUp' || event.key === 'ArrowRight')
120
+ delta = this.step();
121
+ else if (event.key === 'ArrowDown' || event.key === 'ArrowLeft')
122
+ delta = -this.step();
123
+ else if (event.key === 'Home') {
124
+ this._emit(this.min());
125
+ return;
126
+ }
127
+ else if (event.key === 'End') {
128
+ this._emit(this.max());
129
+ return;
130
+ }
131
+ if (delta !== 0) {
132
+ event.preventDefault();
133
+ this._emit(this._clamp(this._value() + delta));
134
+ }
135
+ }
136
+ /** Aplica el drag circular calculando el delta de ángulo respecto al punto inicial.
137
+ * Applies circular drag by computing the angle delta from the start point. */
138
+ _applyCircularDrag(clientX, clientY) {
139
+ let deltaAngle = this._getAngle(clientX, clientY) - this._dragStartAngle;
140
+ // Normalizar a [-180, 180] para evitar saltos al cruzar el eje / Normalize to avoid jumps
141
+ if (deltaAngle > 180)
142
+ deltaAngle -= 360;
143
+ if (deltaAngle < -180)
144
+ deltaAngle += 360;
145
+ const range = this.max() - this.min();
146
+ // 260° de arco = rango completo / 260° arc = full range
147
+ const valueDelta = (deltaAngle / 260) * range;
148
+ this._emit(this._clamp(this._dragStartValue + valueDelta));
149
+ }
150
+ _emit(value) {
151
+ const snapped = Math.round(value / this.step()) * this.step();
152
+ const clamped = this._clamp(snapped);
153
+ this._value.set(clamped);
154
+ this._onChange(clamped);
155
+ this.valueChange.emit(clamped);
156
+ }
157
+ _clamp(v) {
158
+ return Math.min(this.max(), Math.max(this.min(), v));
159
+ }
160
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuKnobComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
161
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuKnobComponent, isStandalone: true, selector: "neu-knob", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { properties: { "class": "hostClasses()" } }, providers: [
162
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NeuKnobComponent), multi: true },
163
+ ], ngImport: i0, template: `
164
+ <div
165
+ class="neu-knob__dial"
166
+ role="slider"
167
+ tabindex="0"
168
+ [attr.id]="_id"
169
+ [attr.aria-valuemin]="min()"
170
+ [attr.aria-valuemax]="max()"
171
+ [attr.aria-valuenow]="_value()"
172
+ [attr.aria-label]="label() || 'Knob'"
173
+ [attr.aria-disabled]="_cvaDisabled()"
174
+ [style.width.px]="size()"
175
+ [style.height.px]="size()"
176
+ (mousedown)="onMouseDown($event)"
177
+ (touchstart)="onTouchStart($event)"
178
+ (keydown)="onKeyDown($event)"
179
+ >
180
+ <svg
181
+ class="neu-knob__svg"
182
+ [attr.viewBox]="'0 0 ' + size() + ' ' + size()"
183
+ [attr.width]="size()"
184
+ [attr.height]="size()"
185
+ aria-hidden="true"
186
+ >
187
+ <!-- Track arc: 260° arc (same as value-arc) so the background only covers
188
+ the usable range, not a full 360° ring. -->
189
+ <circle
190
+ class="neu-knob__track"
191
+ [attr.cx]="size() / 2"
192
+ [attr.cy]="size() / 2"
193
+ [attr.r]="_radius()"
194
+ fill="none"
195
+ [attr.stroke-width]="_strokeWidth()"
196
+ stroke-linecap="round"
197
+ [attr.stroke-dasharray]="_arcDashArray()"
198
+ stroke-dashoffset="0"
199
+ [attr.transform]="'rotate(-220 ' + size() / 2 + ' ' + size() / 2 + ')'"
200
+ />
201
+ <!-- Value arc -->
202
+ <circle
203
+ class="neu-knob__value-arc"
204
+ [attr.cx]="size() / 2"
205
+ [attr.cy]="size() / 2"
206
+ [attr.r]="_radius()"
207
+ fill="none"
208
+ [attr.stroke-width]="_strokeWidth()"
209
+ stroke-linecap="round"
210
+ [attr.stroke-dasharray]="_arcDashArray()"
211
+ [attr.stroke-dashoffset]="_arcDashOffset()"
212
+ [attr.transform]="'rotate(-220 ' + size() / 2 + ' ' + size() / 2 + ')'"
213
+ />
214
+ <!-- Indicator dot -->
215
+ <circle
216
+ class="neu-knob__indicator"
217
+ [attr.cx]="_indicatorX()"
218
+ [attr.cy]="_indicatorY()"
219
+ [attr.r]="_strokeWidth() / 2 + 1"
220
+ [attr.transform]="
221
+ 'rotate(' + _indicatorAngle() + ' ' + size() / 2 + ' ' + size() / 2 + ')'
222
+ "
223
+ />
224
+ </svg>
225
+ @if (showValue()) {
226
+ <span
227
+ class="neu-knob__display"
228
+ style="color: var(--neu-knob-display-color, var(--neu-text, #0f172a)) !important; -webkit-text-fill-color: var(--neu-knob-display-color, var(--neu-text, #0f172a)) !important;"
229
+ >{{ _value() }}</span
230
+ >
231
+ }
232
+ </div>
233
+ @if (label()) {
234
+ <label class="neu-knob__label" [attr.for]="_id">{{ label() }}</label>
235
+ }
236
+ `, isInline: true, styles: ["@charset \"UTF-8\";.neu-knob{display:inline-flex;flex-direction:column;align-items:center;gap:6px;-webkit-user-select:none;user-select:none}.neu-knob--disabled{opacity:.5;pointer-events:none}.neu-knob__dial{position:relative;display:flex;align-items:center;justify-content:center;cursor:grab;border-radius:50%;touch-action:none}.neu-knob__dial:focus-visible{outline:2px solid var(--neu-focus-ring, #0ea5e9);outline-offset:3px}.neu-knob__dial:active{cursor:grabbing}.neu-knob__track{stroke:var(--neu-knob-track, var(--neu-border-color, #e5e7eb))}.neu-knob__value-arc{stroke:var(--neu-color-primary, #0ea5e9);transition:stroke-dashoffset 40ms linear}.neu-knob__indicator{fill:var(--neu-color-primary, #0ea5e9)}.neu-knob__display{position:absolute;font-size:.8125rem;font-weight:600;color:var(--neu-knob-display-color, var(--neu-text, #0f172a));-webkit-text-fill-color:var(--neu-knob-display-color, var(--neu-text, #0f172a));pointer-events:none;font-variant-numeric:tabular-nums}.neu-knob__label{font-size:.8125rem;color:var(--neu-knob-label-color, var(--neu-text-muted, #64748b));cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
237
+ }
238
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuKnobComponent, decorators: [{
239
+ type: Component,
240
+ args: [{ selector: 'neu-knob', imports: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': 'hostClasses()' }, providers: [
241
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NeuKnobComponent), multi: true },
242
+ ], template: `
243
+ <div
244
+ class="neu-knob__dial"
245
+ role="slider"
246
+ tabindex="0"
247
+ [attr.id]="_id"
248
+ [attr.aria-valuemin]="min()"
249
+ [attr.aria-valuemax]="max()"
250
+ [attr.aria-valuenow]="_value()"
251
+ [attr.aria-label]="label() || 'Knob'"
252
+ [attr.aria-disabled]="_cvaDisabled()"
253
+ [style.width.px]="size()"
254
+ [style.height.px]="size()"
255
+ (mousedown)="onMouseDown($event)"
256
+ (touchstart)="onTouchStart($event)"
257
+ (keydown)="onKeyDown($event)"
258
+ >
259
+ <svg
260
+ class="neu-knob__svg"
261
+ [attr.viewBox]="'0 0 ' + size() + ' ' + size()"
262
+ [attr.width]="size()"
263
+ [attr.height]="size()"
264
+ aria-hidden="true"
265
+ >
266
+ <!-- Track arc: 260° arc (same as value-arc) so the background only covers
267
+ the usable range, not a full 360° ring. -->
268
+ <circle
269
+ class="neu-knob__track"
270
+ [attr.cx]="size() / 2"
271
+ [attr.cy]="size() / 2"
272
+ [attr.r]="_radius()"
273
+ fill="none"
274
+ [attr.stroke-width]="_strokeWidth()"
275
+ stroke-linecap="round"
276
+ [attr.stroke-dasharray]="_arcDashArray()"
277
+ stroke-dashoffset="0"
278
+ [attr.transform]="'rotate(-220 ' + size() / 2 + ' ' + size() / 2 + ')'"
279
+ />
280
+ <!-- Value arc -->
281
+ <circle
282
+ class="neu-knob__value-arc"
283
+ [attr.cx]="size() / 2"
284
+ [attr.cy]="size() / 2"
285
+ [attr.r]="_radius()"
286
+ fill="none"
287
+ [attr.stroke-width]="_strokeWidth()"
288
+ stroke-linecap="round"
289
+ [attr.stroke-dasharray]="_arcDashArray()"
290
+ [attr.stroke-dashoffset]="_arcDashOffset()"
291
+ [attr.transform]="'rotate(-220 ' + size() / 2 + ' ' + size() / 2 + ')'"
292
+ />
293
+ <!-- Indicator dot -->
294
+ <circle
295
+ class="neu-knob__indicator"
296
+ [attr.cx]="_indicatorX()"
297
+ [attr.cy]="_indicatorY()"
298
+ [attr.r]="_strokeWidth() / 2 + 1"
299
+ [attr.transform]="
300
+ 'rotate(' + _indicatorAngle() + ' ' + size() / 2 + ' ' + size() / 2 + ')'
301
+ "
302
+ />
303
+ </svg>
304
+ @if (showValue()) {
305
+ <span
306
+ class="neu-knob__display"
307
+ style="color: var(--neu-knob-display-color, var(--neu-text, #0f172a)) !important; -webkit-text-fill-color: var(--neu-knob-display-color, var(--neu-text, #0f172a)) !important;"
308
+ >{{ _value() }}</span
309
+ >
310
+ }
311
+ </div>
312
+ @if (label()) {
313
+ <label class="neu-knob__label" [attr.for]="_id">{{ label() }}</label>
314
+ }
315
+ `, styles: ["@charset \"UTF-8\";.neu-knob{display:inline-flex;flex-direction:column;align-items:center;gap:6px;-webkit-user-select:none;user-select:none}.neu-knob--disabled{opacity:.5;pointer-events:none}.neu-knob__dial{position:relative;display:flex;align-items:center;justify-content:center;cursor:grab;border-radius:50%;touch-action:none}.neu-knob__dial:focus-visible{outline:2px solid var(--neu-focus-ring, #0ea5e9);outline-offset:3px}.neu-knob__dial:active{cursor:grabbing}.neu-knob__track{stroke:var(--neu-knob-track, var(--neu-border-color, #e5e7eb))}.neu-knob__value-arc{stroke:var(--neu-color-primary, #0ea5e9);transition:stroke-dashoffset 40ms linear}.neu-knob__indicator{fill:var(--neu-color-primary, #0ea5e9)}.neu-knob__display{position:absolute;font-size:.8125rem;font-weight:600;color:var(--neu-knob-display-color, var(--neu-text, #0f172a));-webkit-text-fill-color:var(--neu-knob-display-color, var(--neu-text, #0f172a));pointer-events:none;font-variant-numeric:tabular-nums}.neu-knob__label{font-size:.8125rem;color:var(--neu-knob-label-color, var(--neu-text-muted, #64748b));cursor:pointer}\n"] }]
316
+ }], propDecorators: { min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
317
+
318
+ /**
319
+ * Generated bundle index. Do not edit.
320
+ */
321
+
322
+ export { NeuKnobComponent };
323
+ //# sourceMappingURL=neural-ui-core-knob.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-knob.mjs","sources":["../../../../projects/ui-core/knob/neu-knob.component.ts","../../../../projects/ui-core/knob/neural-ui-core-knob.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\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\nlet _seq = 0;\n\n/**\n * NeuralUI Knob Component\n *\n * Control de dial rotatorio para selección de valores numéricos. Implementa CVA.\n *\n * Uso: <neu-knob [min]=\"0\" [max]=\"100\" formControlName=\"volume\" />\n */\n@Component({\n selector: 'neu-knob',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses()' },\n providers: [\n { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NeuKnobComponent), multi: true },\n ],\n template: `\n <div\n class=\"neu-knob__dial\"\n role=\"slider\"\n tabindex=\"0\"\n [attr.id]=\"_id\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"_value()\"\n [attr.aria-label]=\"label() || 'Knob'\"\n [attr.aria-disabled]=\"_cvaDisabled()\"\n [style.width.px]=\"size()\"\n [style.height.px]=\"size()\"\n (mousedown)=\"onMouseDown($event)\"\n (touchstart)=\"onTouchStart($event)\"\n (keydown)=\"onKeyDown($event)\"\n >\n <svg\n class=\"neu-knob__svg\"\n [attr.viewBox]=\"'0 0 ' + size() + ' ' + size()\"\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n aria-hidden=\"true\"\n >\n <!-- Track arc: 260° arc (same as value-arc) so the background only covers\n the usable range, not a full 360° ring. -->\n <circle\n class=\"neu-knob__track\"\n [attr.cx]=\"size() / 2\"\n [attr.cy]=\"size() / 2\"\n [attr.r]=\"_radius()\"\n fill=\"none\"\n [attr.stroke-width]=\"_strokeWidth()\"\n stroke-linecap=\"round\"\n [attr.stroke-dasharray]=\"_arcDashArray()\"\n stroke-dashoffset=\"0\"\n [attr.transform]=\"'rotate(-220 ' + size() / 2 + ' ' + size() / 2 + ')'\"\n />\n <!-- Value arc -->\n <circle\n class=\"neu-knob__value-arc\"\n [attr.cx]=\"size() / 2\"\n [attr.cy]=\"size() / 2\"\n [attr.r]=\"_radius()\"\n fill=\"none\"\n [attr.stroke-width]=\"_strokeWidth()\"\n stroke-linecap=\"round\"\n [attr.stroke-dasharray]=\"_arcDashArray()\"\n [attr.stroke-dashoffset]=\"_arcDashOffset()\"\n [attr.transform]=\"'rotate(-220 ' + size() / 2 + ' ' + size() / 2 + ')'\"\n />\n <!-- Indicator dot -->\n <circle\n class=\"neu-knob__indicator\"\n [attr.cx]=\"_indicatorX()\"\n [attr.cy]=\"_indicatorY()\"\n [attr.r]=\"_strokeWidth() / 2 + 1\"\n [attr.transform]=\"\n 'rotate(' + _indicatorAngle() + ' ' + size() / 2 + ' ' + size() / 2 + ')'\n \"\n />\n </svg>\n @if (showValue()) {\n <span\n class=\"neu-knob__display\"\n style=\"color: var(--neu-knob-display-color, var(--neu-text, #0f172a)) !important; -webkit-text-fill-color: var(--neu-knob-display-color, var(--neu-text, #0f172a)) !important;\"\n >{{ _value() }}</span\n >\n }\n </div>\n @if (label()) {\n <label class=\"neu-knob__label\" [attr.for]=\"_id\">{{ label() }}</label>\n }\n `,\n styleUrl: './neu-knob.component.scss',\n})\nexport class NeuKnobComponent implements ControlValueAccessor {\n /** Valor mínimo / Min value */\n readonly min = input<number>(0);\n /** Valor máximo / Max value */\n readonly max = input<number>(100);\n /** Incremento por paso / Step increment */\n readonly step = input<number>(1);\n /** Tamaño del dial en px / Dial size in px */\n readonly size = input<number>(80);\n /** Muestra el valor numérico en el centro / Shows the numeric value in the center */\n readonly showValue = input<boolean>(true);\n /** Etiqueta visible / Visible label */\n readonly label = input<string>('');\n /** Emitido en cada cambio / Emitted on each change */\n readonly valueChange = output<number>();\n\n readonly _id = `neu-knob-${++_seq}`;\n readonly _cvaDisabled = signal(false);\n readonly _value = signal(0);\n\n readonly _strokeWidth = computed(() => Math.max(4, this.size() * 0.1));\n readonly _radius = computed(() => (this.size() - this._strokeWidth()) / 2 - 2);\n readonly _circumference = computed(() => 2 * Math.PI * this._radius());\n /** Arc spans 260° (from -40° to 220°) */\n readonly _arcLength = computed(() => (260 / 360) * this._circumference());\n\n readonly _normalizedValue = computed(() => {\n const range = this.max() - this.min();\n return range === 0 ? 0 : (this._value() - this.min()) / range;\n });\n\n readonly _arcDashArray = computed(() => `${this._arcLength()} ${this._circumference()}`);\n readonly _arcDashOffset = computed(() => this._arcLength() * (1 - this._normalizedValue()));\n\n // Indicator position on the track (relative to SVG center)\n // El arco empieza en rotate(-220°) desde el Este del SVG.\n // El punto indicador reposa en el Norte (12 en punto).\n // Para que el indicador siga el arco, la rotación CW debe ser:\n // θ = 230° + normalizedValue × 260°\n // (arco de 7:40 → 1:20 pasando por 12:00 en la mitad)\n // Arc starts at rotate(-220°) from East; indicator natural position is North (12 o'clock).\n // Correct clockwise rotation formula: θ = 230 + normalizedValue × 260.\n readonly _indicatorAngle = computed(() => this._normalizedValue() * 260 + 230);\n readonly _indicatorX = computed(() => this.size() / 2);\n readonly _indicatorY = computed(() => this.size() / 2 - this._radius());\n\n private _dragStartAngle = 0;\n private _dragStartValue = 0;\n private _onChange: (v: number) => void = () => {};\n private _onTouched: () => void = () => {};\n\n private readonly _el = inject(ElementRef<HTMLElement>);\n\n /** Calcula el ángulo en grados desde el centro del dial al punto (x, y).\n * Calculates the angle in degrees from the dial center to point (x, y). */\n private _getAngle(clientX: number, clientY: number): number {\n const dial = this._el.nativeElement.querySelector('.neu-knob__dial') as HTMLElement;\n const rect = dial\n ? dial.getBoundingClientRect()\n : this._el.nativeElement.getBoundingClientRect();\n const cx = rect.left + rect.width / 2;\n const cy = rect.top + rect.height / 2;\n return Math.atan2(clientY - cy, clientX - cx) * (180 / Math.PI);\n }\n\n readonly hostClasses = computed(() => ({\n 'neu-knob': true,\n 'neu-knob--disabled': this._cvaDisabled(),\n }));\n\n writeValue(val: number | null): void {\n this._value.set(this._clamp(val ?? this.min()));\n }\n\n registerOnChange(fn: (v: number) => void): void {\n this._onChange = fn;\n }\n registerOnTouched(fn: () => void): void {\n this._onTouched = fn;\n }\n setDisabledState(disabled: boolean): void {\n this._cvaDisabled.set(disabled);\n }\n\n onMouseDown(event: MouseEvent): void {\n if (this._cvaDisabled()) return;\n event.preventDefault();\n this._dragStartAngle = this._getAngle(event.clientX, event.clientY);\n this._dragStartValue = this._value();\n const onMove = (e: MouseEvent) => this._applyCircularDrag(e.clientX, e.clientY);\n const onUp = () => {\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n };\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n this._onTouched();\n }\n\n onTouchStart(event: TouchEvent): void {\n if (this._cvaDisabled()) return;\n event.preventDefault();\n this._dragStartAngle = this._getAngle(event.touches[0].clientX, event.touches[0].clientY);\n this._dragStartValue = this._value();\n const onMove = (e: TouchEvent) =>\n this._applyCircularDrag(e.touches[0].clientX, e.touches[0].clientY);\n const onEnd = () => {\n window.removeEventListener('touchmove', onMove);\n window.removeEventListener('touchend', onEnd);\n };\n window.addEventListener('touchmove', onMove, { passive: false });\n window.addEventListener('touchend', onEnd);\n this._onTouched();\n }\n\n onKeyDown(event: KeyboardEvent): void {\n if (this._cvaDisabled()) return;\n let delta = 0;\n if (event.key === 'ArrowUp' || event.key === 'ArrowRight') delta = this.step();\n else if (event.key === 'ArrowDown' || event.key === 'ArrowLeft') delta = -this.step();\n else if (event.key === 'Home') {\n this._emit(this.min());\n return;\n } else if (event.key === 'End') {\n this._emit(this.max());\n return;\n }\n if (delta !== 0) {\n event.preventDefault();\n this._emit(this._clamp(this._value() + delta));\n }\n }\n\n /** Aplica el drag circular calculando el delta de ángulo respecto al punto inicial.\n * Applies circular drag by computing the angle delta from the start point. */\n private _applyCircularDrag(clientX: number, clientY: number): void {\n let deltaAngle = this._getAngle(clientX, clientY) - this._dragStartAngle;\n // Normalizar a [-180, 180] para evitar saltos al cruzar el eje / Normalize to avoid jumps\n if (deltaAngle > 180) deltaAngle -= 360;\n if (deltaAngle < -180) deltaAngle += 360;\n const range = this.max() - this.min();\n // 260° de arco = rango completo / 260° arc = full range\n const valueDelta = (deltaAngle / 260) * range;\n this._emit(this._clamp(this._dragStartValue + valueDelta));\n }\n\n private _emit(value: number): void {\n const snapped = Math.round(value / this.step()) * this.step();\n const clamped = this._clamp(snapped);\n this._value.set(clamped);\n this._onChange(clamped);\n this.valueChange.emit(clamped);\n }\n\n private _clamp(v: number): number {\n return Math.min(this.max(), Math.max(this.min(), v));\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
3
+ import { DecimalPipe } from '@angular/common';
4
+
5
+ /**
6
+ * NeuralUI MeterGroup Component
7
+ *
8
+ * Barra de progreso multi-segmento que muestra la distribución de valores.
9
+ *
10
+ * Uso:
11
+ * <neu-meter-group [items]="meters" [total]="100" />
12
+ */
13
+ class NeuMeterGroupComponent {
14
+ /** Segmentos del medidor / Meter segments */
15
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
16
+ /** Total de referencia (0 = suma de items) / Reference total (0 = sum of items) */
17
+ total = input(0, ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
18
+ /** Altura de la barra en px / Bar height in px */
19
+ height = input(12, ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
20
+ /** Esquinas redondeadas / Rounded corners */
21
+ rounded = input(true, ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
22
+ /** Muestra leyenda debajo / Shows legend below */
23
+ showLegend = input(true, ...(ngDevMode ? [{ debugName: "showLegend" }] : /* istanbul ignore next */ []));
24
+ /** Aria-label de la barra / Aria-label for the bar */
25
+ ariaLabel = input('Medidor', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
26
+ hostClasses = computed(() => ({
27
+ 'neu-meter-group': true,
28
+ 'neu-meter-group--rounded': this.rounded(),
29
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
30
+ _usedSum = computed(() => this.items().reduce((s, i) => s + i.value, 0), ...(ngDevMode ? [{ debugName: "_usedSum" }] : /* istanbul ignore next */ []));
31
+ _effectiveTotal = computed(() => this.total() > 0 ? this.total() : this._usedSum() || 1, ...(ngDevMode ? [{ debugName: "_effectiveTotal" }] : /* istanbul ignore next */ []));
32
+ _segments = computed(() => {
33
+ const total = this._effectiveTotal();
34
+ return this.items().map((item) => ({
35
+ ...item,
36
+ pct: Math.max(0, Math.min(100, (item.value / total) * 100)),
37
+ }));
38
+ }, ...(ngDevMode ? [{ debugName: "_segments" }] : /* istanbul ignore next */ []));
39
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuMeterGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
40
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuMeterGroupComponent, isStandalone: true, selector: "neu-meter-group", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, ngImport: i0, template: `
41
+ <div
42
+ class="neu-meter-group__bar"
43
+ role="meter"
44
+ [style.height.px]="height()"
45
+ [attr.aria-valuemin]="0"
46
+ [attr.aria-valuemax]="_effectiveTotal()"
47
+ [attr.aria-valuenow]="_usedSum()"
48
+ [attr.aria-label]="ariaLabel()"
49
+ >
50
+ @for (item of _segments(); track item.label) {
51
+ <div
52
+ class="neu-meter-group__segment"
53
+ [style.width.%]="item.pct"
54
+ [style.background]="item.color || null"
55
+ [attr.title]="item.label + ': ' + item.value"
56
+ [attr.data-index]="$index"
57
+ ></div>
58
+ }
59
+ </div>
60
+ @if (showLegend()) {
61
+ <ul class="neu-meter-group__legend" aria-hidden="true">
62
+ @for (item of _segments(); track item.label) {
63
+ <li class="neu-meter-group__legend-item">
64
+ <span
65
+ class="neu-meter-group__legend-dot"
66
+ [style.background]="item.color || null"
67
+ [attr.data-index]="$index"
68
+ ></span>
69
+ <span class="neu-meter-group__legend-label">{{ item.label }}</span>
70
+ <span class="neu-meter-group__legend-value">{{ item.pct | number: '1.0-1' }}%</span>
71
+ </li>
72
+ }
73
+ </ul>
74
+ }
75
+ `, isInline: true, styles: ["@charset \"UTF-8\";.neu-meter-group{display:flex;flex-direction:column;gap:10px;width:100%}.neu-meter-group__bar{display:flex;width:100%;height:12px;background:var(--neu-meter-track, #e5e7eb);overflow:hidden;border-radius:0}.neu-meter-group--rounded .neu-meter-group__bar{border-radius:var(--neu-radius-full, 9999px)}.neu-meter-group__segment{height:100%;transition:width .4s cubic-bezier(.4,0,.2,1);flex-shrink:0}.neu-meter-group__segment[data-index=\"0\"]:not([style*=background]){background:var(--neu-color-primary, #0ea5e9)}.neu-meter-group__segment[data-index=\"1\"]:not([style*=background]){background:var(--neu-color-success, #22c55e)}.neu-meter-group__segment[data-index=\"2\"]:not([style*=background]){background:var(--neu-color-warning, #f59e0b)}.neu-meter-group__segment[data-index=\"3\"]:not([style*=background]){background:var(--neu-color-danger, #ef4444)}.neu-meter-group__segment[data-index=\"4\"]:not([style*=background]){background:var(--neu-color-secondary, #8b5cf6)}.neu-meter-group__legend{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:8px 16px}.neu-meter-group__legend-item{display:flex;align-items:center;gap:6px;font-size:.8125rem;color:var(--neu-text-muted, #6b7280)}.neu-meter-group__legend-dot{display:inline-block;width:10px;height:10px;border-radius:50%;flex-shrink:0}.neu-meter-group__legend-dot[data-index=\"0\"]:not([style*=background]){background:var(--neu-color-primary, #0ea5e9)}.neu-meter-group__legend-dot[data-index=\"1\"]:not([style*=background]){background:var(--neu-color-success, #22c55e)}.neu-meter-group__legend-dot[data-index=\"2\"]:not([style*=background]){background:var(--neu-color-warning, #f59e0b)}.neu-meter-group__legend-dot[data-index=\"3\"]:not([style*=background]){background:var(--neu-color-danger, #ef4444)}.neu-meter-group__legend-dot[data-index=\"4\"]:not([style*=background]){background:var(--neu-color-secondary, #8b5cf6)}.neu-meter-group__legend-label{color:var(--neu-text, #111)}.neu-meter-group__legend-value{font-variant-numeric:tabular-nums;color:var(--neu-text-muted, #6b7280)}\n"], dependencies: [{ kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
76
+ }
77
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuMeterGroupComponent, decorators: [{
78
+ type: Component,
79
+ args: [{ selector: 'neu-meter-group', imports: [DecimalPipe], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': 'hostClasses()' }, template: `
80
+ <div
81
+ class="neu-meter-group__bar"
82
+ role="meter"
83
+ [style.height.px]="height()"
84
+ [attr.aria-valuemin]="0"
85
+ [attr.aria-valuemax]="_effectiveTotal()"
86
+ [attr.aria-valuenow]="_usedSum()"
87
+ [attr.aria-label]="ariaLabel()"
88
+ >
89
+ @for (item of _segments(); track item.label) {
90
+ <div
91
+ class="neu-meter-group__segment"
92
+ [style.width.%]="item.pct"
93
+ [style.background]="item.color || null"
94
+ [attr.title]="item.label + ': ' + item.value"
95
+ [attr.data-index]="$index"
96
+ ></div>
97
+ }
98
+ </div>
99
+ @if (showLegend()) {
100
+ <ul class="neu-meter-group__legend" aria-hidden="true">
101
+ @for (item of _segments(); track item.label) {
102
+ <li class="neu-meter-group__legend-item">
103
+ <span
104
+ class="neu-meter-group__legend-dot"
105
+ [style.background]="item.color || null"
106
+ [attr.data-index]="$index"
107
+ ></span>
108
+ <span class="neu-meter-group__legend-label">{{ item.label }}</span>
109
+ <span class="neu-meter-group__legend-value">{{ item.pct | number: '1.0-1' }}%</span>
110
+ </li>
111
+ }
112
+ </ul>
113
+ }
114
+ `, styles: ["@charset \"UTF-8\";.neu-meter-group{display:flex;flex-direction:column;gap:10px;width:100%}.neu-meter-group__bar{display:flex;width:100%;height:12px;background:var(--neu-meter-track, #e5e7eb);overflow:hidden;border-radius:0}.neu-meter-group--rounded .neu-meter-group__bar{border-radius:var(--neu-radius-full, 9999px)}.neu-meter-group__segment{height:100%;transition:width .4s cubic-bezier(.4,0,.2,1);flex-shrink:0}.neu-meter-group__segment[data-index=\"0\"]:not([style*=background]){background:var(--neu-color-primary, #0ea5e9)}.neu-meter-group__segment[data-index=\"1\"]:not([style*=background]){background:var(--neu-color-success, #22c55e)}.neu-meter-group__segment[data-index=\"2\"]:not([style*=background]){background:var(--neu-color-warning, #f59e0b)}.neu-meter-group__segment[data-index=\"3\"]:not([style*=background]){background:var(--neu-color-danger, #ef4444)}.neu-meter-group__segment[data-index=\"4\"]:not([style*=background]){background:var(--neu-color-secondary, #8b5cf6)}.neu-meter-group__legend{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:8px 16px}.neu-meter-group__legend-item{display:flex;align-items:center;gap:6px;font-size:.8125rem;color:var(--neu-text-muted, #6b7280)}.neu-meter-group__legend-dot{display:inline-block;width:10px;height:10px;border-radius:50%;flex-shrink:0}.neu-meter-group__legend-dot[data-index=\"0\"]:not([style*=background]){background:var(--neu-color-primary, #0ea5e9)}.neu-meter-group__legend-dot[data-index=\"1\"]:not([style*=background]){background:var(--neu-color-success, #22c55e)}.neu-meter-group__legend-dot[data-index=\"2\"]:not([style*=background]){background:var(--neu-color-warning, #f59e0b)}.neu-meter-group__legend-dot[data-index=\"3\"]:not([style*=background]){background:var(--neu-color-danger, #ef4444)}.neu-meter-group__legend-dot[data-index=\"4\"]:not([style*=background]){background:var(--neu-color-secondary, #8b5cf6)}.neu-meter-group__legend-label{color:var(--neu-text, #111)}.neu-meter-group__legend-value{font-variant-numeric:tabular-nums;color:var(--neu-text-muted, #6b7280)}\n"] }]
115
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
116
+
117
+ /**
118
+ * Generated bundle index. Do not edit.
119
+ */
120
+
121
+ export { NeuMeterGroupComponent };
122
+ //# sourceMappingURL=neural-ui-core-meter-group.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neural-ui-core-meter-group.mjs","sources":["../../../../projects/ui-core/meter-group/neu-meter-group.component.ts","../../../../projects/ui-core/meter-group/neural-ui-core-meter-group.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n} from '@angular/core';\nimport { DecimalPipe } from '@angular/common';\n\nexport interface NeuMeterItem {\n /** Etiqueta del segmento / Segment label */\n label: string;\n /** Valor numérico del segmento / Numeric value of the segment */\n value: number;\n /** Color CSS opcional / Optional CSS color */\n color?: string;\n}\n\n/**\n * NeuralUI MeterGroup Component\n *\n * Barra de progreso multi-segmento que muestra la distribución de valores.\n *\n * Uso:\n * <neu-meter-group [items]=\"meters\" [total]=\"100\" />\n */\n@Component({\n selector: 'neu-meter-group',\n imports: [DecimalPipe],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses()' },\n template: `\n <div\n class=\"neu-meter-group__bar\"\n role=\"meter\"\n [style.height.px]=\"height()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"_effectiveTotal()\"\n [attr.aria-valuenow]=\"_usedSum()\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n @for (item of _segments(); track item.label) {\n <div\n class=\"neu-meter-group__segment\"\n [style.width.%]=\"item.pct\"\n [style.background]=\"item.color || null\"\n [attr.title]=\"item.label + ': ' + item.value\"\n [attr.data-index]=\"$index\"\n ></div>\n }\n </div>\n @if (showLegend()) {\n <ul class=\"neu-meter-group__legend\" aria-hidden=\"true\">\n @for (item of _segments(); track item.label) {\n <li class=\"neu-meter-group__legend-item\">\n <span\n class=\"neu-meter-group__legend-dot\"\n [style.background]=\"item.color || null\"\n [attr.data-index]=\"$index\"\n ></span>\n <span class=\"neu-meter-group__legend-label\">{{ item.label }}</span>\n <span class=\"neu-meter-group__legend-value\">{{ item.pct | number: '1.0-1' }}%</span>\n </li>\n }\n </ul>\n }\n `,\n styleUrl: './neu-meter-group.component.scss',\n})\nexport class NeuMeterGroupComponent {\n /** Segmentos del medidor / Meter segments */\n readonly items = input<NeuMeterItem[]>([]);\n\n /** Total de referencia (0 = suma de items) / Reference total (0 = sum of items) */\n readonly total = input<number>(0);\n\n /** Altura de la barra en px / Bar height in px */\n readonly height = input<number>(12);\n\n /** Esquinas redondeadas / Rounded corners */\n readonly rounded = input<boolean>(true);\n\n /** Muestra leyenda debajo / Shows legend below */\n readonly showLegend = input<boolean>(true);\n\n /** Aria-label de la barra / Aria-label for the bar */\n readonly ariaLabel = input<string>('Medidor');\n\n readonly hostClasses = computed(() => ({\n 'neu-meter-group': true,\n 'neu-meter-group--rounded': this.rounded(),\n }));\n\n readonly _usedSum = computed(() => this.items().reduce((s, i) => s + i.value, 0));\n\n readonly _effectiveTotal = computed(() =>\n this.total() > 0 ? this.total() : this._usedSum() || 1,\n );\n\n readonly _segments = computed(() => {\n const total = this._effectiveTotal();\n return this.items().map((item) => ({\n ...item,\n pct: Math.max(0, Math.min(100, (item.value / total) * 100)),\n }));\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;;AAkBA;;;;;;;AAOG;MA6CU,sBAAsB,CAAA;;AAExB,IAAA,KAAK,GAAG,KAAK,CAAiB,EAAE,4EAAC;;AAGjC,IAAA,KAAK,GAAG,KAAK,CAAS,CAAC,4EAAC;;AAGxB,IAAA,MAAM,GAAG,KAAK,CAAS,EAAE,6EAAC;;AAG1B,IAAA,OAAO,GAAG,KAAK,CAAU,IAAI,8EAAC;;AAG9B,IAAA,UAAU,GAAG,KAAK,CAAU,IAAI,iFAAC;;AAGjC,IAAA,SAAS,GAAG,KAAK,CAAS,SAAS,gFAAC;AAEpC,IAAA,WAAW,GAAG,QAAQ,CAAC,OAAO;AACrC,QAAA,iBAAiB,EAAE,IAAI;AACvB,QAAA,0BAA0B,EAAE,IAAI,CAAC,OAAO,EAAE;AAC3C,KAAA,CAAC,kFAAC;AAEM,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,+EAAC;AAExE,IAAA,eAAe,GAAG,QAAQ,CAAC,MAClC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,sFACvD;AAEQ,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAK;AACjC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,EAAE;AACpC,QAAA,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM;AACjC,YAAA,GAAG,IAAI;YACP,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;AAC5D,SAAA,CAAC,CAAC;AACL,IAAA,CAAC,gFAAC;uGApCS,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,iBAAA,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,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,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,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,eAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAtCvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,ohEAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAvCS,WAAW,EAAA,IAAA,EAAA,QAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FA0CV,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBA5ClC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,iBAAiB,WAClB,CAAC,WAAW,CAAC,EAAA,aAAA,EACP,iBAAiB,CAAC,IAAI,EAAA,eAAA,EACpB,uBAAuB,CAAC,MAAM,EAAA,IAAA,EACzC,EAAE,SAAS,EAAE,eAAe,EAAE,EAAA,QAAA,EAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,ohEAAA,CAAA,EAAA;;;ACnEH;;AAEG;;;;"}