@moni-labs/moni-ui 0.3.1 → 0.3.2

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 (251) hide show
  1. package/custom-elements.json +5181 -1043
  2. package/dist/components/_base/moni-element.d.ts +16 -9
  3. package/dist/components/_base/moni-element.d.ts.map +1 -1
  4. package/dist/components/_base/moni-element.js +63 -9
  5. package/dist/components/index.js +2 -2
  6. package/dist/components/loading-shapes.d.ts +19 -0
  7. package/dist/components/loading-shapes.d.ts.map +1 -1
  8. package/dist/components/loading-shapes.js +19 -0
  9. package/dist/components/moni-app-bar.d.ts +72 -62
  10. package/dist/components/moni-app-bar.d.ts.map +1 -1
  11. package/dist/components/moni-app-bar.js +72 -62
  12. package/dist/components/moni-badge.d.ts +64 -65
  13. package/dist/components/moni-badge.d.ts.map +1 -1
  14. package/dist/components/moni-badge.js +64 -66
  15. package/dist/components/moni-bottom-sheet.d.ts +129 -61
  16. package/dist/components/moni-bottom-sheet.d.ts.map +1 -1
  17. package/dist/components/moni-bottom-sheet.js +217 -131
  18. package/dist/components/moni-button-group.d.ts +107 -29
  19. package/dist/components/moni-button-group.d.ts.map +1 -1
  20. package/dist/components/moni-button-group.js +107 -29
  21. package/dist/components/moni-button-segment.d.ts +53 -12
  22. package/dist/components/moni-button-segment.d.ts.map +1 -1
  23. package/dist/components/moni-button-segment.js +53 -12
  24. package/dist/components/moni-button.d.ts +116 -38
  25. package/dist/components/moni-button.d.ts.map +1 -1
  26. package/dist/components/moni-button.js +128 -38
  27. package/dist/components/moni-card.d.ts +68 -50
  28. package/dist/components/moni-card.d.ts.map +1 -1
  29. package/dist/components/moni-card.js +68 -50
  30. package/dist/components/moni-carousel.d.ts +471 -53
  31. package/dist/components/moni-carousel.d.ts.map +1 -1
  32. package/dist/components/moni-carousel.js +862 -264
  33. package/dist/components/moni-checkbox.d.ts +49 -46
  34. package/dist/components/moni-checkbox.d.ts.map +1 -1
  35. package/dist/components/moni-checkbox.js +55 -48
  36. package/dist/components/moni-chip.d.ts +100 -33
  37. package/dist/components/moni-chip.d.ts.map +1 -1
  38. package/dist/components/moni-chip.js +104 -34
  39. package/dist/components/moni-color-field.d.ts +99 -24
  40. package/dist/components/moni-color-field.d.ts.map +1 -1
  41. package/dist/components/moni-color-field.js +99 -24
  42. package/dist/components/moni-context-menu.d.ts +72 -26
  43. package/dist/components/moni-context-menu.d.ts.map +1 -1
  44. package/dist/components/moni-context-menu.js +76 -30
  45. package/dist/components/moni-dialog.d.ts +84 -59
  46. package/dist/components/moni-dialog.d.ts.map +1 -1
  47. package/dist/components/moni-dialog.js +105 -58
  48. package/dist/components/moni-divider.d.ts +39 -27
  49. package/dist/components/moni-divider.d.ts.map +1 -1
  50. package/dist/components/moni-divider.js +42 -30
  51. package/dist/components/moni-expansion.d.ts +32 -22
  52. package/dist/components/moni-expansion.d.ts.map +1 -1
  53. package/dist/components/moni-expansion.js +32 -22
  54. package/dist/components/moni-fab-menu.d.ts +61 -23
  55. package/dist/components/moni-fab-menu.d.ts.map +1 -1
  56. package/dist/components/moni-fab-menu.js +69 -31
  57. package/dist/components/moni-fab.d.ts +86 -28
  58. package/dist/components/moni-fab.d.ts.map +1 -1
  59. package/dist/components/moni-fab.js +92 -34
  60. package/dist/components/moni-file-field.d.ts +118 -30
  61. package/dist/components/moni-file-field.d.ts.map +1 -1
  62. package/dist/components/moni-file-field.js +118 -30
  63. package/dist/components/moni-icon.d.ts +40 -35
  64. package/dist/components/moni-icon.d.ts.map +1 -1
  65. package/dist/components/moni-icon.js +43 -38
  66. package/dist/components/moni-list-item.d.ts +46 -33
  67. package/dist/components/moni-list-item.d.ts.map +1 -1
  68. package/dist/components/moni-list-item.js +61 -41
  69. package/dist/components/moni-list.d.ts +30 -20
  70. package/dist/components/moni-list.d.ts.map +1 -1
  71. package/dist/components/moni-list.js +36 -21
  72. package/dist/components/moni-loading-indicator.d.ts +54 -22
  73. package/dist/components/moni-loading-indicator.d.ts.map +1 -1
  74. package/dist/components/moni-loading-indicator.js +54 -22
  75. package/dist/components/moni-menu-item.d.ts +23 -16
  76. package/dist/components/moni-menu-item.d.ts.map +1 -1
  77. package/dist/components/moni-menu-item.js +23 -16
  78. package/dist/components/moni-menu.d.ts +81 -28
  79. package/dist/components/moni-menu.d.ts.map +1 -1
  80. package/dist/components/moni-menu.js +81 -30
  81. package/dist/components/moni-morph-modal.d.ts +141 -0
  82. package/dist/components/moni-morph-modal.d.ts.map +1 -1
  83. package/dist/components/moni-morph-modal.js +295 -58
  84. package/dist/components/moni-nav-item.d.ts +59 -29
  85. package/dist/components/moni-nav-item.d.ts.map +1 -1
  86. package/dist/components/moni-nav-item.js +59 -29
  87. package/dist/components/moni-nav.d.ts +94 -35
  88. package/dist/components/moni-nav.d.ts.map +1 -1
  89. package/dist/components/moni-nav.js +94 -35
  90. package/dist/components/moni-progress.d.ts +99 -55
  91. package/dist/components/moni-progress.d.ts.map +1 -1
  92. package/dist/components/moni-progress.js +99 -55
  93. package/dist/components/moni-radio.d.ts +62 -50
  94. package/dist/components/moni-radio.d.ts.map +1 -1
  95. package/dist/components/moni-radio.js +65 -52
  96. package/dist/components/moni-ripple.d.ts +74 -59
  97. package/dist/components/moni-ripple.d.ts.map +1 -1
  98. package/dist/components/moni-ripple.js +74 -59
  99. package/dist/components/moni-segmented-button.d.ts +91 -16
  100. package/dist/components/moni-segmented-button.d.ts.map +1 -1
  101. package/dist/components/moni-segmented-button.js +94 -17
  102. package/dist/components/moni-select-option.d.ts +30 -23
  103. package/dist/components/moni-select-option.d.ts.map +1 -1
  104. package/dist/components/moni-select-option.js +30 -23
  105. package/dist/components/moni-select.d.ts +300 -35
  106. package/dist/components/moni-select.d.ts.map +1 -1
  107. package/dist/components/moni-select.js +323 -39
  108. package/dist/components/moni-side-sheet.d.ts +94 -32
  109. package/dist/components/moni-side-sheet.d.ts.map +1 -1
  110. package/dist/components/moni-side-sheet.js +94 -32
  111. package/dist/components/moni-slider.d.ts +152 -41
  112. package/dist/components/moni-slider.d.ts.map +1 -1
  113. package/dist/components/moni-slider.js +154 -42
  114. package/dist/components/moni-snackbar.d.ts +53 -43
  115. package/dist/components/moni-snackbar.d.ts.map +1 -1
  116. package/dist/components/moni-snackbar.js +53 -43
  117. package/dist/components/moni-split-button.d.ts +63 -18
  118. package/dist/components/moni-split-button.d.ts.map +1 -1
  119. package/dist/components/moni-split-button.js +63 -18
  120. package/dist/components/moni-step.d.ts +36 -24
  121. package/dist/components/moni-step.d.ts.map +1 -1
  122. package/dist/components/moni-step.js +41 -29
  123. package/dist/components/moni-stepper.d.ts +64 -27
  124. package/dist/components/moni-stepper.d.ts.map +1 -1
  125. package/dist/components/moni-stepper.js +72 -34
  126. package/dist/components/moni-switch.d.ts +57 -46
  127. package/dist/components/moni-switch.d.ts.map +1 -1
  128. package/dist/components/moni-switch.js +59 -47
  129. package/dist/components/moni-tab.d.ts +26 -18
  130. package/dist/components/moni-tab.d.ts.map +1 -1
  131. package/dist/components/moni-tab.js +29 -21
  132. package/dist/components/moni-tabs.d.ts +34 -28
  133. package/dist/components/moni-tabs.d.ts.map +1 -1
  134. package/dist/components/moni-tabs.js +34 -28
  135. package/dist/components/moni-text-field.d.ts +139 -29
  136. package/dist/components/moni-text-field.d.ts.map +1 -1
  137. package/dist/components/moni-text-field.js +157 -31
  138. package/dist/components/moni-textarea.d.ts +127 -29
  139. package/dist/components/moni-textarea.d.ts.map +1 -1
  140. package/dist/components/moni-textarea.js +131 -32
  141. package/dist/components/moni-time-picker.d.ts +114 -24
  142. package/dist/components/moni-time-picker.d.ts.map +1 -1
  143. package/dist/components/moni-time-picker.js +127 -33
  144. package/dist/components/moni-toolbar.d.ts +37 -23
  145. package/dist/components/moni-toolbar.d.ts.map +1 -1
  146. package/dist/components/moni-toolbar.js +37 -23
  147. package/dist/components/moni-tooltip.d.ts +100 -36
  148. package/dist/components/moni-tooltip.d.ts.map +1 -1
  149. package/dist/components/moni-tooltip.js +104 -40
  150. package/dist/components/moni-typography.d.ts +68 -30
  151. package/dist/components/moni-typography.d.ts.map +1 -1
  152. package/dist/components/moni-typography.js +70 -32
  153. package/dist/styles/tokens.css +7 -7
  154. package/dist/types/events.d.ts +17 -0
  155. package/dist/types/events.d.ts.map +1 -0
  156. package/dist/types/events.js +1 -0
  157. package/dist/utils/event-emitter.d.ts +13 -0
  158. package/dist/utils/event-emitter.d.ts.map +1 -0
  159. package/dist/utils/event-emitter.js +13 -0
  160. package/package.json +2 -1
  161. package/src/components/_base/moni-element.ts +73 -9
  162. package/src/components/index.ts +2 -2
  163. package/src/components/loading-shapes.ts +19 -0
  164. package/src/components/moni-app-bar.test.ts +10 -10
  165. package/src/components/moni-app-bar.ts +72 -62
  166. package/src/components/moni-badge.ts +64 -66
  167. package/src/components/moni-bottom-sheet.test.ts +39 -45
  168. package/src/components/moni-bottom-sheet.ts +222 -131
  169. package/src/components/moni-button-group.test.ts +14 -14
  170. package/src/components/moni-button-group.ts +107 -29
  171. package/src/components/moni-button-segment.ts +53 -12
  172. package/src/components/moni-button.test.ts +28 -26
  173. package/src/components/moni-button.ts +139 -39
  174. package/src/components/moni-card.test.ts +6 -6
  175. package/src/components/moni-card.ts +70 -50
  176. package/src/components/moni-carousel.test.ts +12 -12
  177. package/src/components/moni-carousel.ts +953 -290
  178. package/src/components/moni-checkbox.test.ts +7 -7
  179. package/src/components/moni-checkbox.ts +62 -48
  180. package/src/components/moni-chip.test.ts +17 -17
  181. package/src/components/moni-chip.ts +112 -34
  182. package/src/components/moni-color-field.test.ts +5 -5
  183. package/src/components/moni-color-field.ts +108 -24
  184. package/src/components/moni-context-menu.test.ts +11 -11
  185. package/src/components/moni-context-menu.ts +76 -30
  186. package/src/components/moni-dialog.ts +108 -59
  187. package/src/components/moni-divider.test.ts +4 -4
  188. package/src/components/moni-divider.ts +42 -30
  189. package/src/components/moni-expansion.ts +32 -22
  190. package/src/components/moni-fab-menu.test.ts +11 -11
  191. package/src/components/moni-fab-menu.ts +69 -31
  192. package/src/components/moni-fab.test.ts +16 -16
  193. package/src/components/moni-fab.ts +100 -34
  194. package/src/components/moni-file-field.test.ts +8 -8
  195. package/src/components/moni-file-field.ts +132 -30
  196. package/src/components/moni-icon.test.ts +11 -11
  197. package/src/components/moni-icon.ts +43 -38
  198. package/src/components/moni-list-item.test.ts +10 -10
  199. package/src/components/moni-list-item.ts +61 -41
  200. package/src/components/moni-list.ts +37 -21
  201. package/src/components/moni-loading-indicator.test.ts +3 -3
  202. package/src/components/moni-loading-indicator.ts +54 -22
  203. package/src/components/moni-menu-item.ts +23 -16
  204. package/src/components/moni-menu.test.ts +7 -7
  205. package/src/components/moni-menu.ts +87 -30
  206. package/src/components/moni-morph-modal.test.ts +18 -17
  207. package/src/components/moni-morph-modal.ts +327 -60
  208. package/src/components/moni-nav-item.ts +59 -29
  209. package/src/components/moni-nav.test.ts +11 -11
  210. package/src/components/moni-nav.ts +94 -35
  211. package/src/components/moni-progress.test.ts +9 -9
  212. package/src/components/moni-progress.ts +100 -56
  213. package/src/components/moni-radio.test.ts +8 -8
  214. package/src/components/moni-radio.ts +71 -51
  215. package/src/components/moni-ripple.ts +74 -59
  216. package/src/components/moni-segmented-button.test.ts +4 -4
  217. package/src/components/moni-segmented-button.ts +94 -17
  218. package/src/components/moni-select-option.ts +30 -23
  219. package/src/components/moni-select.test.ts +11 -11
  220. package/src/components/moni-select.ts +361 -50
  221. package/src/components/moni-side-sheet.test.ts +7 -7
  222. package/src/components/moni-side-sheet.ts +94 -32
  223. package/src/components/moni-slider.test.ts +7 -7
  224. package/src/components/moni-slider.ts +165 -42
  225. package/src/components/moni-snackbar.test.ts +8 -8
  226. package/src/components/moni-snackbar.ts +53 -43
  227. package/src/components/moni-split-button.ts +63 -18
  228. package/src/components/moni-step.test.ts +7 -7
  229. package/src/components/moni-step.ts +41 -29
  230. package/src/components/moni-stepper.test.ts +7 -7
  231. package/src/components/moni-stepper.ts +72 -34
  232. package/src/components/moni-switch.test.ts +12 -12
  233. package/src/components/moni-switch.ts +66 -47
  234. package/src/components/moni-tab.test.ts +5 -5
  235. package/src/components/moni-tab.ts +29 -21
  236. package/src/components/moni-tabs.ts +34 -28
  237. package/src/components/moni-text-field.test.ts +13 -13
  238. package/src/components/moni-text-field.ts +181 -30
  239. package/src/components/moni-textarea.test.ts +17 -17
  240. package/src/components/moni-textarea.ts +155 -31
  241. package/src/components/moni-time-picker.test.ts +5 -5
  242. package/src/components/moni-time-picker.ts +128 -33
  243. package/src/components/moni-toolbar.test.ts +8 -8
  244. package/src/components/moni-toolbar.ts +37 -23
  245. package/src/components/moni-tooltip.test.ts +15 -15
  246. package/src/components/moni-tooltip.ts +107 -40
  247. package/src/components/moni-typography.test.ts +10 -10
  248. package/src/components/moni-typography.ts +73 -32
  249. package/src/styles/tokens.css +7 -7
  250. package/src/types/events.ts +17 -0
  251. package/src/utils/event-emitter.ts +26 -0
@@ -10,50 +10,50 @@ import { customElement, property } from 'lit/decorators.js';
10
10
  import { MoniElement, sharedStyles } from './_base/index.js';
11
11
 
12
12
  /**
13
- * Material Design 3 Tabs container component.
13
+ * Componente Material Design 3 Tabs container (Contenedor de pestañas).
14
14
  *
15
- * A navigational container that groups multiple `<moni-tab>` elements. Tabs
16
- * organize content into high-level categories and allow the user to switch
17
- * between them.
15
+ * Un contenedor de navegación que agrupa múltiples elementos `<moni-tab>`. Las pestañas
16
+ * organizan el contenido en categorías de alto nivel y permiten al usuario alternar
17
+ * entre ellas.
18
18
  *
19
- * **M3 spec reference:** `m3-docs/components/tabs/specs.md`
19
+ * **Referencia a la especificación M3:** `m3-docs/components/tabs/specs.md`
20
20
  *
21
- * **Modes (Primary vs Secondary):**
22
- * - `primary` (default): Used for top-level navigation at the highest hierarchy,
23
- * often placed directly below a top app bar. They span the full width and
24
- * feature a prominent active indicator.
25
- * - `secondary`: Used for deeper content hierarchies within a specific area
26
- * or page. They are typically more subtle.
21
+ * **Modos (Primario vs Secundario):**
22
+ * - `primary` (por defecto): Usado para navegación de nivel superior en la jerarquía más alta,
23
+ * a menudo colocado directamente debajo de una barra de aplicación superior (top app bar).
24
+ * Abarcan todo el ancho y presentan un indicador activo prominente.
25
+ * - `secondary`: Usado para jerarquías de contenido más profundas dentro de un área
26
+ * o página específica. Típicamente son más sutiles.
27
27
  *
28
- * **Layout & Alignment:**
29
- * - `scrollable`: If the number of tabs exceeds the container width, this
30
- * enables horizontal scrolling (`overflow-x: auto`) rather than squishing.
31
- * - `align`: Controls how the tabs are distributed (`default` space-around,
32
- * `left`, `center`, or `right`).
33
- * - `vertical`: Stacks the icon above the text label inside the child tabs.
28
+ * **Diseño y alineación:**
29
+ * - `scrollable`: Si el número de pestañas excede el ancho del contenedor, esto
30
+ * habilita el desplazamiento horizontal (`overflow-x: auto`) en lugar de aplastarlas.
31
+ * - `align`: Controla cómo se distribuyen las pestañas (`default` space-around,
32
+ * `left`, `center`, o `right`).
33
+ * - `vertical`: Apila el icono sobre la etiqueta de texto dentro de las pestañas hijas.
34
34
  *
35
- * **Active Indicator:**
36
- * The `indicator-size` attribute allows customizing the width of the active
37
- * underline indicator (`default` fits the tab content, `min` is narrow, `max`
38
- * fills the full tab width).
35
+ * **Indicador Activo:**
36
+ * El atributo `indicator-size` permite personalizar el ancho del
37
+ * indicador de subrayado activo (`default` se ajusta al contenido de la pestaña, `min` es estrecho, `max`
38
+ * llena todo el ancho de la pestaña).
39
39
  *
40
40
  * @example
41
41
  * ```html
42
- * <!-- Primary, scrollable tabs -->
42
+ * <!-- Pestañas primarias, con desplazamiento (scrollable) -->
43
43
  * <moni-tabs scrollable>
44
- * <moni-tab active label="Flights"></moni-tab>
45
- * <moni-tab label="Trips"></moni-tab>
46
- * <moni-tab label="Explore"></moni-tab>
44
+ * <moni-tab active label="Vuelos"></moni-tab>
45
+ * <moni-tab label="Viajes"></moni-tab>
46
+ * <moni-tab label="Explorar"></moni-tab>
47
47
  * </moni-tabs>
48
48
  *
49
- * <!-- Secondary, centered tabs with vertical layout -->
49
+ * <!-- Pestañas secundarias, centradas con diseño vertical -->
50
50
  * <moni-tabs mode="secondary" align="center" vertical>
51
51
  * <moni-tab active icon="video_camera_front" label="Video"></moni-tab>
52
- * <moni-tab icon="photo_camera" label="Photo"></moni-tab>
52
+ * <moni-tab icon="photo_camera" label="Foto"></moni-tab>
53
53
  * </moni-tabs>
54
54
  * ```
55
55
  *
56
- * @slot default - `<moni-tab>` child elements.
56
+ * @slot default - Elementos hijos `<moni-tab>`.
57
57
  */
58
58
  @customElement('moni-tabs')
59
59
  export class MoniTabs extends MoniElement {
@@ -106,6 +106,12 @@ export class MoniTabs extends MoniElement {
106
106
  `
107
107
  ];
108
108
 
109
+ /**
110
+ * Renderiza el contenedor de navegación.
111
+ * Construye dinámicamente las clases CSS que habilitan los selectores estructurales
112
+ * (`mode`, `align`, `indicatorSize`) permitiendo que el componente hijo `moni-tab`
113
+ * herede estilos contextuales sin necesidad de inyectar variables complejas.
114
+ */
109
115
  override render() {
110
116
  const classes = [
111
117
  'tabs',
@@ -14,7 +14,7 @@ describe('moni-text-field', () => {
14
14
  el.remove();
15
15
  });
16
16
 
17
- it('renders a field wrapper and an input', async () => {
17
+ it('renderiza un contenedor de campo (field wrapper) y un input', async () => {
18
18
  await el.updateComplete;
19
19
  const field = el.shadowRoot?.querySelector('.field');
20
20
  const input = el.shadowRoot?.querySelector('input');
@@ -22,49 +22,49 @@ describe('moni-text-field', () => {
22
22
  expect(input).toBeTruthy();
23
23
  });
24
24
 
25
- it('renders a leading icon when icon attribute is set', async () => {
25
+ it('renderiza un icono inicial (leading) cuando se establece el atributo icon', async () => {
26
26
  el.icon = 'search';
27
27
  await el.updateComplete;
28
28
  const leading = el.shadowRoot?.querySelector('.leading-icon');
29
29
  expect(leading).toBeTruthy();
30
30
  });
31
31
 
32
- it('renders a trailing icon when trailing-icon attribute is set', async () => {
32
+ it('renderiza un icono final (trailing) cuando se establece el atributo trailing-icon', async () => {
33
33
  el.setAttribute('trailing-icon', 'close');
34
34
  await el.updateComplete;
35
35
  const trailing = el.shadowRoot?.querySelector('.trailing-icon');
36
36
  expect(trailing).toBeTruthy();
37
37
  });
38
38
 
39
- it('renders prefix text when prefix attribute is set', async () => {
39
+ it('renderiza un texto prefijo cuando se establece el atributo prefix', async () => {
40
40
  el.prefix = 'https://';
41
41
  await el.updateComplete;
42
42
  const leading = el.shadowRoot?.querySelector('.leading-icon');
43
43
  expect(leading?.textContent?.trim()).toBe('https://');
44
44
  });
45
45
 
46
- it('renders suffix text when suffix attribute is set', async () => {
46
+ it('renderiza un texto sufijo cuando se establece el atributo suffix', async () => {
47
47
  el.suffix = '.com';
48
48
  await el.updateComplete;
49
49
  const trailing = el.shadowRoot?.querySelector('.trailing-icon');
50
50
  expect(trailing?.textContent?.trim()).toBe('.com');
51
51
  });
52
52
 
53
- it('applies square class on the field wrapper for shape=no-round', async () => {
53
+ it('aplica la clase square en el contenedor de campo para shape=no-round', async () => {
54
54
  el.shape = 'no-round';
55
55
  await el.updateComplete;
56
56
  const field = el.shadowRoot?.querySelector('.field');
57
57
  expect(field?.classList.contains('square')).toBe(true);
58
58
  });
59
59
 
60
- it('applies round class on the field wrapper for shape=round', async () => {
60
+ it('aplica la clase round en el contenedor de campo para shape=round', async () => {
61
61
  el.shape = 'round';
62
62
  await el.updateComplete;
63
63
  const field = el.shadowRoot?.querySelector('.field');
64
64
  expect(field?.classList.contains('round')).toBe(true);
65
65
  });
66
66
 
67
- it('does not apply shape class on the field wrapper for shape=square', async () => {
67
+ it('no aplica la clase de forma (shape) en el contenedor de campo para shape=square', async () => {
68
68
  el.shape = 'square';
69
69
  await el.updateComplete;
70
70
  const field = el.shadowRoot?.querySelector('.field');
@@ -72,7 +72,7 @@ describe('moni-text-field', () => {
72
72
  expect(field?.classList.contains('round')).toBe(false);
73
73
  });
74
74
 
75
- it('shows the loading spinner when loading is true', async () => {
75
+ it('muestra el indicador de carga cuando loading es true', async () => {
76
76
  el.loading = true;
77
77
  await el.updateComplete;
78
78
  const trailing = el.shadowRoot?.querySelector('.trailing-icon');
@@ -80,13 +80,13 @@ describe('moni-text-field', () => {
80
80
  expect(progress).toBeTruthy();
81
81
  });
82
82
 
83
- it('does not render a leading icon by default', async () => {
83
+ it('no renderiza un icono inicial por defecto', async () => {
84
84
  await el.updateComplete;
85
85
  const leading = el.shadowRoot?.querySelector('.leading-icon');
86
86
  expect(leading).toBeFalsy();
87
87
  });
88
88
 
89
- it('forwards value to the input', async () => {
89
+ it('reenvía value al input', async () => {
90
90
  el.value = 'hello world';
91
91
  await el.updateComplete;
92
92
  const input = el.shadowRoot?.querySelector(
@@ -95,14 +95,14 @@ describe('moni-text-field', () => {
95
95
  expect(input.value).toBe('hello world');
96
96
  });
97
97
 
98
- it('adds active class to input when value is set', async () => {
98
+ it('agrega la clase active al input cuando se establece el valor', async () => {
99
99
  el.value = 'something';
100
100
  await el.updateComplete;
101
101
  const input = el.shadowRoot?.querySelector('input');
102
102
  expect(input?.classList.contains('active')).toBe(true);
103
103
  });
104
104
 
105
- it('renders label and toggles active class based on value', async () => {
105
+ it('renderiza el label y alterna la clase active basándose en el valor', async () => {
106
106
  el.label = 'Email';
107
107
  await el.updateComplete;
108
108
  const label = el.shadowRoot?.querySelector('label');
@@ -10,70 +10,109 @@ import { customElement, property, query } from 'lit/decorators.js';
10
10
  import { ifDefined } from 'lit/directives/if-defined.js';
11
11
  import { classMap } from 'lit/directives/class-map.js';
12
12
  import { MoniElement, sharedStyles, fieldStyles } from './_base/index.js';
13
+ import { emitMoniEvent } from '../utils/event-emitter.js';
13
14
  import './moni-icon.js';
14
15
  import './moni-progress.js';
15
16
 
16
17
  /**
17
- * Material Design 3 Text Field component.
18
+ * Componente Material Design 3 Text Field (Campo de texto).
18
19
  *
19
- * A full-featured input field that wraps a native `<input>` inside the M3
20
- * field shell (`.field` class from `fieldStyles`). Supports floating labels,
21
- * filled and outlined variants, leading/trailing icons, helper text, and
22
- * error states.
20
+ * Un campo de entrada con todas las funciones que envuelve un `<input>` nativo dentro de la
21
+ * estructura de campo M3 (clase `.field` de `fieldStyles`). Soporta etiquetas flotantes,
22
+ * variantes llenas (filled) y contorneadas (outlined), iconos iniciales/finales (leading/trailing),
23
+ * texto de ayuda y estados de error.
23
24
  *
24
- * **M3 spec reference:** `m3-docs/components/text-fields/specs.md`
25
+ * **Referencia a la especificación M3:** `m3-docs/components/text-fields/specs.md`
25
26
  *
26
- * **Visual architecture:**
27
- * Uses `fieldStyles` for the complete CSS field shell. The field container
28
- * is a `<div class="field [modifiers]">` wrapping:
29
- * 1. Optional leading icon.
30
- * 2. Native `<input>` element.
31
- * 3. Floating `<label>` (when `label` is set).
32
- * 4. Optional trailing icon or loading spinner.
33
- * 5. `<output>` for helper/error text.
27
+ * **Arquitectura visual:**
28
+ * Utiliza `fieldStyles` para toda la estructura CSS del campo. El contenedor del campo
29
+ * es un `<div class="field [modifiers]">` que envuelve:
30
+ * 1. Icono inicial (leading) opcional.
31
+ * 2. Elemento `<input>` nativo.
32
+ * 3. `<label>` flotante (cuando se establece `label`).
33
+ * 4. Icono final (trailing) opcional o indicador de carga (spinner).
34
+ * 5. `<output>` para texto de ayuda/error.
34
35
  *
35
- * **Visual-only contract:**
36
- * This component does **not** listen to `@input` events to sync the `value`
37
- * attribute back from the underlying `<input>`. Consumers must either:
38
- * - Submit the native form (the `<input name>` participates in form submission).
39
- * - Listen to `change` or `input` events via standard DOM event handling.
40
- * - Use a framework binding (e.g. `bind:value` in Svelte, `v-model` in Vue).
36
+ * **Sincronización de eventos:**
37
+ * Emite `moni-input` en cada tipeo y `moni-change` al consolidar el valor (blur/enter).
38
+ * El valor interno del componente (`this.value`) se mantiene sincronizado automáticamente.
41
39
  *
42
40
  * @example
43
41
  * ```html
44
42
  * <moni-text-field
45
- * label="Email address"
43
+ * label="Dirección de correo"
46
44
  * type="email"
47
45
  * name="email"
48
46
  * icon="mail"
49
47
  * variant="outlined"
50
- * helper="We'll never share your email."
48
+ * helper="Nunca compartiremos tu correo."
51
49
  * ></moni-text-field>
52
50
  *
53
51
  * <moni-text-field
54
- * label="Amount"
52
+ * label="Monto"
55
53
  * type="number"
56
54
  * prefix="$"
57
55
  * error
58
- * error-text="Value must be positive"
56
+ * error-text="El valor debe ser positivo"
59
57
  * ></moni-text-field>
60
58
  * ```
61
59
  *
62
- * @csspart field - The outer `.field` div container.
63
- * @csspart input - The native `<input>` element.
64
- * @csspart label - The floating `<label>` element.
65
- * @csspart helper - The helper `<output>` element.
66
- * @csspart error-output - The error `<output>` element.
60
+ * @csspart field - El contenedor div `.field` exterior.
61
+ * @csspart input - El elemento `<input>` nativo.
62
+ * @csspart label - El elemento `<label>` flotante.
63
+ * @csspart helper - El elemento `<output>` de ayuda.
64
+ * @csspart error-output - El elemento `<output>` de error.
67
65
  */
68
66
  @customElement('moni-text-field')
69
67
  export class MoniTextField extends MoniElement {
68
+ static formAssociated = true;
69
+ private _internals: ElementInternals;
70
+
71
+ constructor() {
72
+ super();
73
+ this._internals = this.attachInternals();
74
+ }
75
+
76
+ /**
77
+ * El nombre del input, enviado con los datos del formulario.
78
+ * @type {string}
79
+ */
70
80
  @property({ reflect: true }) name = '';
81
+
82
+ /**
83
+ * El texto de la etiqueta flotante.
84
+ * @type {string}
85
+ */
71
86
  @property({ reflect: true }) label = '';
87
+
88
+ /**
89
+ * Variante visual del campo de texto.
90
+ * @type {'filled' | 'outlined'}
91
+ * @default 'filled'
92
+ */
72
93
  @property({ reflect: true }) variant: 'filled' | 'outlined' = 'filled';
94
+
95
+ /**
96
+ * Define las dimensiones del campo de texto.
97
+ * @type {'small' | 'medium' | 'large' | 'extra'}
98
+ * @default 'medium'
99
+ */
73
100
  @property({ reflect: true })
74
101
  size: 'small' | 'medium' | 'large' | 'extra' = 'medium';
102
+
103
+ /**
104
+ * Forma del radio del borde (border-radius) del campo.
105
+ * @type {'round' | 'square' | 'no-round'}
106
+ * @default 'no-round'
107
+ */
75
108
  @property({ reflect: true })
76
109
  shape: 'round' | 'square' | 'no-round' = 'no-round';
110
+
111
+ /**
112
+ * El tipo de input HTML nativo.
113
+ * @type {'text' | 'password' | 'email' | 'number' | 'tel' | 'url' | 'search'}
114
+ * @default 'text'
115
+ */
77
116
  @property({ reflect: true })
78
117
  type:
79
118
  | 'text'
@@ -83,29 +122,139 @@ export class MoniTextField extends MoniElement {
83
122
  | 'tel'
84
123
  | 'url'
85
124
  | 'search' = 'text';
125
+
126
+ /**
127
+ * Nombre del icono inicial (leading) (Material Symbols).
128
+ * @type {string}
129
+ */
86
130
  @property({ reflect: true }) icon = '';
131
+
132
+ /**
133
+ * Nombre del icono final (trailing) (Material Symbols).
134
+ * @type {string}
135
+ */
87
136
  @property({ reflect: true, attribute: 'trailing-icon' }) trailingIcon = '';
137
+
138
+ /**
139
+ * Prefijo de texto corto mostrado antes del valor del input.
140
+ * @type {string}
141
+ */
88
142
  @property({ reflect: true }) prefix = '';
143
+
144
+ /**
145
+ * Sufijo de texto corto mostrado después del valor del input.
146
+ * @type {string}
147
+ */
89
148
  @property({ reflect: true }) suffix = '';
149
+
150
+ /**
151
+ * Texto de ayuda mostrado debajo del campo.
152
+ * @type {string}
153
+ */
90
154
  @property({ reflect: true }) helper = '';
155
+
156
+ /**
157
+ * Texto de error mostrado debajo del campo cuando `error` es true.
158
+ * Sobrescribe el texto de ayuda.
159
+ * @type {string}
160
+ */
91
161
  @property({ reflect: true, attribute: 'error-text' }) errorText = '';
162
+
163
+ /**
164
+ * Si es true, establece el campo en un estado de error.
165
+ * @type {boolean}
166
+ */
92
167
  @property({ type: Boolean, reflect: true }) error = false;
168
+
169
+ /**
170
+ * Si es true, muestra un indicador de carga (progreso lineal/circular) al final.
171
+ * @type {boolean}
172
+ */
93
173
  @property({ type: Boolean, reflect: true }) loading = false;
174
+
175
+ /**
176
+ * Deshabilita el campo de texto.
177
+ * @type {boolean}
178
+ */
94
179
  @property({ type: Boolean, reflect: true }) disabled = false;
180
+
181
+ /**
182
+ * El valor actual del input.
183
+ * @type {string}
184
+ */
95
185
  @property({ reflect: true }) value = '';
186
+
187
+ /**
188
+ * Texto de marcador de posición (placeholder) mostrado cuando el input está vacío y la etiqueta es flotante.
189
+ * @type {string}
190
+ */
96
191
  @property({ reflect: true }) placeholder = '';
97
192
 
98
193
  @query('input') private _input!: HTMLInputElement;
99
194
 
195
+ /**
196
+ * Sincroniza imperativamente las propiedades DOM del `<input>` nativo después de cada actualización reactiva.
197
+ *
198
+ * Las vinculaciones `?value` y `?disabled` de Lit establecen *atributos* HTML, no *propiedades* DOM.
199
+ * Para un `<input>`, el atributo `value` solo establece el valor *inicial*; la propiedad DOM
200
+ * `input.value` refleja el valor actual tecleado por el usuario. Después de una actualización reactiva que
201
+ * cambia `this.value` (ej. reinicio programático), debemos escribir directamente a la propiedad
202
+ * DOM para sobrescribir lo que el usuario ha tecleado. Esto también es crítico para los consumidores
203
+ * de React y Vue que mutan la propiedad del Custom Element directamente, omitiendo el flujo
204
+ * de atributos de Lit.
205
+ *
206
+ * @param changed - Mapa de los nombres de propiedades a sus valores anteriores que causaron esta actualización.
207
+ */
100
208
  override updated(changed: Map<string, unknown>) {
101
209
  if (this._input) {
102
- if (changed.has('value')) this._input.value = this.value;
210
+ if (changed.has('value')) {
211
+ this._input.value = this.value;
212
+ this._internals?.setFormValue?.(this.value);
213
+ }
103
214
  if (changed.has('disabled')) this._input.disabled = this.disabled;
104
215
  }
105
216
  }
106
217
 
218
+ private _handleInput(e: Event) {
219
+ const target = e.target as HTMLInputElement;
220
+ this.value = target.value;
221
+ emitMoniEvent(this, 'moni-input', {
222
+ detail: { value: this.value, originalEvent: e }
223
+ });
224
+ }
225
+
226
+ private _handleChange(e: Event) {
227
+ const target = e.target as HTMLInputElement;
228
+ this.value = target.value;
229
+ emitMoniEvent(this, 'moni-change', {
230
+ detail: { value: this.value, originalEvent: e }
231
+ });
232
+ }
233
+
107
234
  static override styles = [sharedStyles, fieldStyles];
108
235
 
236
+ /**
237
+ * Renderiza el campo de texto con animación de etiqueta flotante, iconos iniciales/finales, y texto de ayuda.
238
+ *
239
+ * **Composición de `fieldClasses`:**
240
+ * - `field` — siempre presente; diseño base del campo.
241
+ * - `label` — habilita el comportamiento de la etiqueta flotante vía CSS.
242
+ * - `fill` / `border` — variante filled vs outlined.
243
+ * - `small` / `large` / `extra` — modificador de tamaño.
244
+ * - `prefix` — desplaza la etiqueta cuando hay un icono inicial (leading) presente.
245
+ * - `suffix` — reserva espacio final (trailing) para el icono/texto de sufijo.
246
+ * - `invalid` — aplica colores de error cuando `error=true` o falla la validación nativa.
247
+ *
248
+ * **Flotación de la etiqueta (Label float):**
249
+ * `isActive` es true cuando `value` no está vacío o se establece un `placeholder`.
250
+ * Los selectores CSS `.field.label input:focus ~ label`, `.field.label.active label`
251
+ * flotan la etiqueta a la parte superior del borde del campo. Este componente
252
+ * establece `isActive` para evitar que la etiqueta colapse cuando un valor está preestablecido.
253
+ *
254
+ * **Zonas inicial / final (Leading / trailing):**
255
+ * `hasLeading` y `hasTrailing` añaden condicionalmente las clases CSS `prefix`/`suffix`
256
+ * y renderizan el icono o los elementos de texto de prefijo/sufijo.
257
+ */
109
258
  override render() {
110
259
  const hasLeading = Boolean(this.icon) || Boolean(this.prefix);
111
260
  const hasTrailing =
@@ -166,6 +315,8 @@ export class MoniTextField extends MoniElement {
166
315
  .value=${this.value}
167
316
  name=${ifDefined(this.name || undefined)}
168
317
  class=${isActive ? 'active' : ''}
318
+ @input=${this._handleInput}
319
+ @change=${this._handleChange}
169
320
  />
170
321
  ${this.label
171
322
  ? html`<label
@@ -14,7 +14,7 @@ describe('moni-textarea', () => {
14
14
  el.remove();
15
15
  });
16
16
 
17
- it('renders a field wrapper and a textarea', async () => {
17
+ it('renderiza un contenedor de campo (field wrapper) y un textarea', async () => {
18
18
  await el.updateComplete;
19
19
  const field = el.shadowRoot?.querySelector('.field');
20
20
  const textarea = el.shadowRoot?.querySelector('textarea');
@@ -22,35 +22,35 @@ describe('moni-textarea', () => {
22
22
  expect(textarea).toBeTruthy();
23
23
  });
24
24
 
25
- it('renders a leading icon when icon attribute is set', async () => {
25
+ it('renderiza un icono inicial (leading) cuando se establece el atributo icon', async () => {
26
26
  el.icon = 'edit';
27
27
  await el.updateComplete;
28
28
  const leading = el.shadowRoot?.querySelector('.leading-icon');
29
29
  expect(leading).toBeTruthy();
30
30
  });
31
31
 
32
- it('renders a trailing icon when trailing-icon attribute is set', async () => {
32
+ it('renderiza un icono final (trailing) cuando se establece el atributo trailing-icon', async () => {
33
33
  el.setAttribute('trailing-icon', 'send');
34
34
  await el.updateComplete;
35
35
  const trailing = el.shadowRoot?.querySelector('.trailing-icon');
36
36
  expect(trailing).toBeTruthy();
37
37
  });
38
38
 
39
- it('renders prefix text when prefix attribute is set', async () => {
39
+ it('renderiza un texto prefijo cuando se establece el atributo prefix', async () => {
40
40
  el.prefix = '~';
41
41
  await el.updateComplete;
42
42
  const leading = el.shadowRoot?.querySelector('.leading-icon');
43
43
  expect(leading?.textContent?.trim()).toBe('~');
44
44
  });
45
45
 
46
- it('renders suffix text when suffix attribute is set', async () => {
46
+ it('renderiza un texto sufijo cuando se establece el atributo suffix', async () => {
47
47
  el.suffix = 'chars';
48
48
  await el.updateComplete;
49
49
  const trailing = el.shadowRoot?.querySelector('.trailing-icon');
50
50
  expect(trailing?.textContent?.trim()).toBe('chars');
51
51
  });
52
52
 
53
- it('shows the loading spinner when loading is true', async () => {
53
+ it('muestra el indicador de carga cuando loading es true', async () => {
54
54
  el.loading = true;
55
55
  await el.updateComplete;
56
56
  const trailing = el.shadowRoot?.querySelector('.trailing-icon');
@@ -58,7 +58,7 @@ describe('moni-textarea', () => {
58
58
  expect(progress).toBeTruthy();
59
59
  });
60
60
 
61
- it('forwards rows attribute to the textarea', async () => {
61
+ it('reenvía el atributo rows al textarea', async () => {
62
62
  el.rows = 5;
63
63
  await el.updateComplete;
64
64
  const textarea = el.shadowRoot?.querySelector(
@@ -67,7 +67,7 @@ describe('moni-textarea', () => {
67
67
  expect(textarea.rows).toBe(5);
68
68
  });
69
69
 
70
- it('forwards value to the textarea', async () => {
70
+ it('reenvía value al textarea', async () => {
71
71
  el.value = 'multi\nline\ntext';
72
72
  await el.updateComplete;
73
73
  const textarea = el.shadowRoot?.querySelector(
@@ -76,28 +76,28 @@ describe('moni-textarea', () => {
76
76
  expect(textarea.value).toBe('multi\nline\ntext');
77
77
  });
78
78
 
79
- it('adds active class to textarea when value is set', async () => {
79
+ it('agrega la clase active al textarea cuando se establece el valor', async () => {
80
80
  el.value = 'content';
81
81
  await el.updateComplete;
82
82
  const textarea = el.shadowRoot?.querySelector('textarea');
83
83
  expect(textarea?.classList.contains('active')).toBe(true);
84
84
  });
85
85
 
86
- it('applies square class on the field wrapper for shape=no-round', async () => {
86
+ it('aplica la clase square en el contenedor de campo para shape=no-round', async () => {
87
87
  el.shape = 'no-round';
88
88
  await el.updateComplete;
89
89
  const field = el.shadowRoot?.querySelector('.field');
90
90
  expect(field?.classList.contains('square')).toBe(true);
91
91
  });
92
92
 
93
- it('applies round class on the field wrapper for shape=round', async () => {
93
+ it('aplica la clase round en el contenedor de campo para shape=round', async () => {
94
94
  el.shape = 'round';
95
95
  await el.updateComplete;
96
96
  const field = el.shadowRoot?.querySelector('.field');
97
97
  expect(field?.classList.contains('round')).toBe(true);
98
98
  });
99
99
 
100
- it('does not apply shape class on the field wrapper for shape=square', async () => {
100
+ it('no aplica la clase de forma (shape) en el contenedor de campo para shape=square', async () => {
101
101
  el.shape = 'square';
102
102
  await el.updateComplete;
103
103
  const field = el.shadowRoot?.querySelector('.field');
@@ -107,20 +107,20 @@ describe('moni-textarea', () => {
107
107
 
108
108
  // ─── P3.3: maxlength + character counter ───
109
109
 
110
- it('does not render a counter by default', async () => {
110
+ it('no renderiza un contador por defecto', async () => {
111
111
  await el.updateComplete;
112
112
  const counter = el.shadowRoot?.querySelector('.counter');
113
113
  expect(counter).toBeFalsy();
114
114
  });
115
115
 
116
- it('renders a counter when maxlength is set', async () => {
116
+ it('renderiza un contador cuando se establece maxlength', async () => {
117
117
  el.maxlength = 100;
118
118
  await el.updateComplete;
119
119
  const counter = el.shadowRoot?.querySelector('.counter');
120
120
  expect(counter).toBeTruthy();
121
121
  });
122
122
 
123
- it('counter shows value.length / maxlength', async () => {
123
+ it('el contador muestra value.length / maxlength', async () => {
124
124
  el.maxlength = 100;
125
125
  el.value = 'Hello';
126
126
  await el.updateComplete;
@@ -128,7 +128,7 @@ describe('moni-textarea', () => {
128
128
  expect(counter?.textContent?.trim()).toBe('5 / 100');
129
129
  });
130
130
 
131
- it('no-counter suppresses the counter even when maxlength is set', async () => {
131
+ it('no-counter suprime el contador incluso cuando maxlength está establecido', async () => {
132
132
  el.maxlength = 100;
133
133
  el.noCounter = true;
134
134
  await el.updateComplete;
@@ -136,7 +136,7 @@ describe('moni-textarea', () => {
136
136
  expect(counter).toBeFalsy();
137
137
  });
138
138
 
139
- it('forwards maxlength to the inner textarea as maxLength', async () => {
139
+ it('reenvía maxlength al textarea interno como maxLength', async () => {
140
140
  el.maxlength = 50;
141
141
  await el.updateComplete;
142
142
  const textarea = el.shadowRoot?.querySelector(