@moni-labs/moni-ui 0.3.0 → 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 +5230 -1007
  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 +484 -53
  31. package/dist/components/moni-carousel.d.ts.map +1 -1
  32. package/dist/components/moni-carousel.js +1024 -245
  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 +1129 -269
  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
@@ -11,43 +11,43 @@ import { MoniElement, sharedStyles } from './_base/index.js';
11
11
  import type { MoniStep } from './moni-step.js';
12
12
 
13
13
  /**
14
- * Material Design 3 Stepper component.
14
+ * Componente Material Design 3 Stepper (Pasos/Progresión).
15
15
  *
16
- * A container for a linear progression of `<moni-step>` elements. Steppers
17
- * convey progress through numbered steps and indicate the user's current
18
- * position within a flow.
16
+ * Un contenedor para una progresión lineal de elementos `<moni-step>`. Los steppers
17
+ * transmiten el progreso a través de pasos numerados e indican la posición
18
+ * actual del usuario dentro de un flujo.
19
19
  *
20
- * **M3 spec reference:** `m3-docs/components/progress-indicators/specs.md`
20
+ * **Referencia a la especificación M3:** `m3-docs/components/progress-indicators/specs.md`
21
21
  *
22
- * **Orchestration:**
23
- * This component acts as the orchestrator for its slotted `<moni-step>` children.
24
- * Whenever the `current` property changes, or children are added/removed, the
25
- * stepper iterates over all child steps and injects their state:
26
- * - Assigns the sequential `index` (0-based) to each step.
27
- * - Sets `active=true` on the step matching the `current` index.
28
- * - Sets `completed=true` on all steps prior to the `current` index.
22
+ * **Orquestación:**
23
+ * Este componente actúa como orquestador para sus hijos `<moni-step>` en los slots.
24
+ * Cada vez que cambia la propiedad `current` o se añaden/eliminan hijos, el
25
+ * stepper itera sobre todos los pasos hijos e inyecta su estado:
26
+ * - Asigna el `index` secuencial (basado en 0) a cada paso.
27
+ * - Establece `active=true` en el paso que coincide con el índice `current`.
28
+ * - Establece `completed=true` en todos los pasos anteriores al índice `current`.
29
29
  *
30
- * **Visual layout:**
31
- * The stepper manages the layout (flex row or column based on `orientation`)
32
- * and ensures the connector lines between steps are rendered correctly via
33
- * CSS pseudo-elements defined in the child `<moni-step>` styling.
30
+ * **Diseño visual:**
31
+ * El stepper maneja el diseño (flex row o column basado en `orientation`)
32
+ * y asegura que las líneas conectoras entre los pasos se rendericen correctamente a través de
33
+ * pseudo-elementos CSS definidos en los estilos del `<moni-step>` hijo.
34
34
  *
35
35
  * @example
36
36
  * ```html
37
37
  * <moni-stepper current="1" orientation="horizontal">
38
- * <moni-step title="Step 1"></moni-step>
39
- * <moni-step title="Step 2"></moni-step>
40
- * <moni-step title="Step 3"></moni-step>
38
+ * <moni-step title="Paso 1"></moni-step>
39
+ * <moni-step title="Paso 2"></moni-step>
40
+ * <moni-step title="Paso 3"></moni-step>
41
41
  * </moni-stepper>
42
42
  *
43
43
  * <script>
44
44
  * const stepper = document.querySelector('moni-stepper');
45
- * // Move to next step
45
+ * // Mover al siguiente paso
46
46
  * stepper.current = 2;
47
47
  * </script>
48
48
  * ```
49
49
  *
50
- * @slot default - `<moni-step>` elements.
50
+ * @slot default - Elementos `<moni-step>`.
51
51
  */
52
52
  @customElement('moni-stepper')
53
53
  export class MoniStepper extends MoniElement {
@@ -58,6 +58,11 @@ export class MoniStepper extends MoniElement {
58
58
  @queryAssignedElements({ selector: 'moni-step' })
59
59
  private _steps!: MoniStep[];
60
60
 
61
+ /**
62
+ * Hook del ciclo de vida reactivo (Lit).
63
+ * Cuando la orientación o el índice actual cambian, sincroniza
64
+ * el estado hacia todos los elementos hijos (`<moni-step>`).
65
+ */
61
66
  override updated(changed: Map<string, unknown>) {
62
67
  if (
63
68
  changed.has('current') ||
@@ -69,24 +74,39 @@ export class MoniStepper extends MoniElement {
69
74
  }
70
75
 
71
76
  /**
72
- * Propagate the `current` index to each child <moni-step>:
73
- * - sets `index` from the child's position
74
- * - sets `completed` on every step before `current`
75
- * - sets `active` on the step at `current`
77
+ * Propaga el índice del paso actual a cada elemento hijo `<moni-step>` en los slots,
78
+ * calculando e inyectando el estado de `index`, `completed` y `active`.
79
+ *
80
+ * **Diferimiento por microtarea (`Promise.resolve().then(...)`):**
81
+ * `@queryAssignedElements` de Lit se ejecuta sincrónicamente y no está disponible hasta
82
+ * después del primer renderizado conectado. Diferirlo a una microtarea asegura que los hijos
83
+ * en los slots hayan terminado su propio ciclo de vida `firstUpdated()` antes de que les escribamos
84
+ * el estado. Sin este diferimiento, escribir `step.active = true` durante el `updated()`
85
+ * del padre puede desencadenar renderizados hijos antes de que se apliquen sus estilos.
76
86
  *
77
- * Deferred to a microtask so that slotted children are guaranteed
78
- * to have finished their first update cycle before we write to them.
87
+ * **Respaldo de `querySelectorAll`:**
88
+ * `_steps` (de `@queryAssignedElements`) devuelve un array vacío antes del primer
89
+ * renderizado o cuando no hay elementos `<moni-step>` presentes en el slot. El respaldo
90
+ * a `querySelectorAll('moni-step')` maneja casos extremos donde se añaden pasos
91
+ * programáticamente sin pasar por el mecanismo de slots de Lit.
92
+ *
93
+ * **Bucle de inyección de estado:**
94
+ * Para cada `<moni-step>` en la posición `stepIndex`:
95
+ * - `stepElement.index = stepIndex` — proporciona el número de paso para su etiqueta.
96
+ * - `stepElement.completed = stepIndex < current` — verdadero para todos los pasos anteriores a `current`.
97
+ * - `stepElement.active = stepIndex === current` — verdadero solo para el paso en `current`.
79
98
  */
80
99
  private _syncSteps() {
81
100
  Promise.resolve().then(() => {
82
- let steps = this._steps || [];
83
- if (steps.length === 0) {
84
- steps = Array.from(this.querySelectorAll('moni-step')) as MoniStep[];
101
+ // Usa el resultado de @queryAssignedElements de Lit primero; usa una consulta DOM en vivo como respaldo.
102
+ let stepElements = this._steps || [];
103
+ if (stepElements.length === 0) {
104
+ stepElements = Array.from(this.querySelectorAll('moni-step')) as MoniStep[];
85
105
  }
86
- steps.forEach((step, i) => {
87
- step.index = i;
88
- step.completed = i < this.current;
89
- step.active = i === this.current;
106
+ stepElements.forEach((stepElement, stepIndex) => {
107
+ stepElement.index = stepIndex;
108
+ stepElement.completed = stepIndex < this.current;
109
+ stepElement.active = stepIndex === this.current;
90
110
  });
91
111
  });
92
112
  }
@@ -117,12 +137,30 @@ export class MoniStepper extends MoniElement {
117
137
  `
118
138
  ];
119
139
 
140
+ /**
141
+ * Renderiza el contenedor de lista ordenada (`<ol>`) que contiene los hijos `<moni-step>` en los slots.
142
+ *
143
+ * **Anatomía:**
144
+ * - El `<ol>` usa `list-style: none` y un diseño flex (fila o columna, basado en
145
+ * `orientation`) en lugar del renderizado nativo de lista ordenada, porque la
146
+ * visualización del número de paso es manejada por cada hijo `<moni-step>`.
147
+ * - La vinculación del evento `@slotchange` asegura que se llame a `_syncSteps()`
148
+ * cada vez que el consumidor añade o elimina elementos `<moni-step>` en tiempo de ejecución,
149
+ * manteniendo los estados `index`, `active` y `completed` sincronizados.
150
+ * - Cada `<moni-step>` en el slot está estilizado con `flex: 1` para que todos los pasos
151
+ * compartan el ancho disponible equitativamente (horizontal) o la altura (vertical).
152
+ */
120
153
  override render() {
121
154
  return html`<ol class="list" part="stepper">
122
155
  <slot @slotchange=${this._onSlotChange}></slot>
123
156
  </ol>`;
124
157
  }
125
158
 
159
+ /**
160
+ * Handler dinámico del slot.
161
+ * Detecta si el usuario añade o elimina pasos nativos (`<moni-step>`)
162
+ * desde el DOM en tiempo de ejecución, forzando una re-sincronización.
163
+ */
126
164
  private _onSlotChange = () => {
127
165
  // Steps may have been added/removed; re-sync.
128
166
  this._syncSteps();
@@ -14,7 +14,7 @@ describe('moni-switch', () => {
14
14
  el.remove();
15
15
  });
16
16
 
17
- it('renders an input and a span containing track/thumb pseudo-elements', async () => {
17
+ it('renderiza un input y un span que contiene pseudo-elementos track/thumb', async () => {
18
18
  await el.updateComplete;
19
19
  const input = el.shadowRoot?.querySelector('input');
20
20
  const span = el.shadowRoot?.querySelector('span');
@@ -22,7 +22,7 @@ describe('moni-switch', () => {
22
22
  expect(span).toBeTruthy();
23
23
  });
24
24
 
25
- it('input has role=switch', async () => {
25
+ it('el input tiene role=switch', async () => {
26
26
  await el.updateComplete;
27
27
  const input = el.shadowRoot?.querySelector(
28
28
  'input'
@@ -30,7 +30,7 @@ describe('moni-switch', () => {
30
30
  expect(input.getAttribute('role')).toBe('switch');
31
31
  });
32
32
 
33
- it('input is contained in the label (for click handling)', async () => {
33
+ it('el input está contenido en el label (para el manejo de clics)', async () => {
34
34
  await el.updateComplete;
35
35
  const input = el.shadowRoot?.querySelector(
36
36
  'input'
@@ -39,7 +39,7 @@ describe('moni-switch', () => {
39
39
  expect(label?.contains(input)).toBe(true);
40
40
  });
41
41
 
42
- it('forwards checked state to the input', async () => {
42
+ it('reenvía el estado checked al input', async () => {
43
43
  el.checked = true;
44
44
  await el.updateComplete;
45
45
  const input = el.shadowRoot?.querySelector(
@@ -52,14 +52,14 @@ describe('moni-switch', () => {
52
52
  expect(input.checked).toBe(false);
53
53
  });
54
54
 
55
- it('renders a label span when label attribute is set', async () => {
55
+ it('renderiza un span de label cuando se establece el atributo label', async () => {
56
56
  el.label = 'Wi-Fi';
57
57
  await el.updateComplete;
58
58
  const label = el.shadowRoot?.querySelector('.label');
59
59
  expect(label?.textContent?.trim()).toBe('Wi-Fi');
60
60
  });
61
61
 
62
- it('forwards disabled to the input', async () => {
62
+ it('reenvía disabled al input', async () => {
63
63
  el.disabled = true;
64
64
  await el.updateComplete;
65
65
  const input = el.shadowRoot?.querySelector(
@@ -68,13 +68,13 @@ describe('moni-switch', () => {
68
68
  expect(input.disabled).toBe(true);
69
69
  });
70
70
 
71
- it('reflects the checked attribute', async () => {
71
+ it('refleja el atributo checked', async () => {
72
72
  el.checked = true;
73
73
  await el.updateComplete;
74
74
  expect(el.hasAttribute('checked')).toBe(true);
75
75
  });
76
76
 
77
- it('renders close and check icons when icon=true', async () => {
77
+ it('renderiza iconos de cierre y verificación cuando icon=true', async () => {
78
78
  el.icon = true;
79
79
  await el.updateComplete;
80
80
  const icons = el.shadowRoot?.querySelectorAll('span > i');
@@ -84,14 +84,14 @@ describe('moni-switch', () => {
84
84
  expect(icons?.[1].textContent).toBe('check');
85
85
  });
86
86
 
87
- it('does not render a thumb icon when icon=false', async () => {
87
+ it('no renderiza un icono de pulgar cuando icon=false', async () => {
88
88
  el.icon = false;
89
89
  await el.updateComplete;
90
90
  const icons = el.shadowRoot?.querySelectorAll('span > i');
91
91
  expect(icons?.length).toBe(0);
92
92
  });
93
93
 
94
- it('forwards name to the input', async () => {
94
+ it('reenvía name al input', async () => {
95
95
  el.name = 'wifi';
96
96
  await el.updateComplete;
97
97
  const input = el.shadowRoot?.querySelector(
@@ -100,7 +100,7 @@ describe('moni-switch', () => {
100
100
  expect(input.name).toBe('wifi');
101
101
  });
102
102
 
103
- it('forwards value to the input', async () => {
103
+ it('reenvía value al input', async () => {
104
104
  el.value = 'on';
105
105
  await el.updateComplete;
106
106
  const input = el.shadowRoot?.querySelector(
@@ -109,7 +109,7 @@ describe('moni-switch', () => {
109
109
  expect(input.value).toBe('on');
110
110
  });
111
111
 
112
- it('input is rendered inside the switch label', async () => {
112
+ it('el input se renderiza dentro del label del switch', async () => {
113
113
  await el.updateComplete;
114
114
  const input = el.shadowRoot?.querySelector('input');
115
115
  expect(input).toBeTruthy();
@@ -12,125 +12,136 @@ import { MoniElement, sharedStyles } from './_base/index.js';
12
12
  import './moni-icon.js';
13
13
 
14
14
  /**
15
- * Material Design 3 Switch component.
15
+ * Componente Material Design 3 Switch (Interruptor).
16
16
  *
17
- * Switches toggle the state of a single setting on or off. They are the
18
- * binary on/off equivalent of a checkbox, but optimized for toggling a
19
- * single state rather than selecting from a list.
17
+ * Los switches alternan el estado de una configuración individual entre encendido y apagado.
18
+ * Son el equivalente binario encendido/apagado de una casilla de verificación (checkbox),
19
+ * pero optimizados para alternar un solo estado en lugar de seleccionar de una lista.
20
20
  *
21
- * **M3 spec reference:** `m3-docs/components/switch/specs.md`
21
+ * **Referencia a la especificación M3:** `m3-docs/components/switch/specs.md`
22
22
  *
23
- * **M3 measurements:**
24
- * - Track: 52dp × 32dp, 2dp border, full-radius pill shape.
25
- * - Handle (thumb): 16dp unselected → 24dp selected → 28dp pressed.
26
- * - State layer: 40dp circular ripple on hover/focus/pressed states.
27
- * - Icon (optional): 16dp icon rendered inside the thumb when `icon` is set.
23
+ * **Medidas M3:**
24
+ * - Pista (Track): 52dp × 32dp, borde de 2dp, forma de píldora con radio completo.
25
+ * - Control (Thumb): 16dp no seleccionado → 24dp seleccionado → 28dp presionado.
26
+ * - Capa de estado (State layer): ripple circular de 40dp en estados de hover/focus/pressed.
27
+ * - Icono (opcional): icono de 16dp renderizado dentro del control (thumb) cuando `icon` está activado.
28
28
  *
29
- * **Visual architecture:**
30
- * Like `<moni-checkbox>`, the native `<input type="checkbox" role="switch">`
31
- * occupies real layout space but is visually hidden via `opacity: 0`. Two
32
- * `<span>` pseudo-elements render the track (`::after`) and thumb (`::before`).
33
- * When `icon=true`, `<i>` elements render `close` and `check` glyphs inside
34
- * the thumb, with visibility toggled via CSS based on the checked state.
29
+ * **Arquitectura visual:**
30
+ * Al igual que `<moni-checkbox>`, el `<input type="checkbox" role="switch">` nativo
31
+ * ocupa espacio real en el diseño pero está oculto visualmente mediante `opacity: 0`. Dos
32
+ * pseudo-elementos de un `<span>` renderizan la pista (`::after`) y el control (`::before`).
33
+ * Cuando `icon=true`, los elementos `<i>` renderizan los glifos `close` y `check` dentro
34
+ * del control, y su visibilidad se alterna mediante CSS en base al estado verificado (checked).
35
35
  *
36
- * @fires change - Bubbles and is composed. Fired when the switch is toggled.
37
- * Read `element.checked` for the new state.
36
+ * @fires change - Burbujea y está compuesto. Se dispara cuando el switch se alterna.
37
+ * Lee `element.checked` para obtener el nuevo estado.
38
38
  *
39
39
  * @example
40
40
  * ```html
41
- * <moni-switch label="Dark mode" name="dark-mode"></moni-switch>
42
- * <moni-switch icon checked label="Notifications"></moni-switch>
41
+ * <moni-switch label="Modo oscuro" name="dark-mode"></moni-switch>
42
+ * <moni-switch icon checked label="Notificaciones"></moni-switch>
43
43
  *
44
44
  * <script>
45
45
  * document.querySelector('moni-switch').addEventListener('change', (e) => {
46
- * console.log('enabled:', e.target.checked);
46
+ * console.log('activado:', e.target.checked);
47
47
  * });
48
48
  * </script>
49
49
  * ```
50
50
  *
51
- * @csspart switch - The outer `<label>` element containing the switch.
51
+ * @csspart switch - El elemento `<label>` exterior que contiene el switch.
52
52
  */
53
53
  @customElement('moni-switch')
54
54
  export class MoniSwitch extends MoniElement {
55
+ static formAssociated = true;
56
+ private _internals: ElementInternals;
57
+
58
+ constructor() {
59
+ super();
60
+ this._internals = this.attachInternals();
61
+ }
62
+
55
63
  /**
56
- * Text label displayed to the right of the switch.
64
+ * Etiqueta de texto mostrada a la derecha del switch.
57
65
  *
58
- * When non-empty, renders as a padded text span after the track.
59
- * When empty, the default slot is rendered, allowing custom HTML labels.
66
+ * Cuando no está vacía, se renderiza como un span de texto con padding después de la pista.
67
+ * Cuando está vacía, se renderiza el slot por defecto, permitiendo etiquetas HTML personalizadas.
60
68
  *
61
69
  * @default ''
62
70
  */
63
71
  @property({ reflect: true }) label = '';
64
72
 
65
73
  /**
66
- * Whether the switch is in the "on" (checked) state.
74
+ * Indica si el switch está en el estado "encendido" (marcado/checked).
67
75
  *
68
- * When `true`:
69
- * - Track fills with `--primary` color.
70
- * - Thumb grows from 16dp to 24dp and slides to the trailing edge.
71
- * - Thumb color changes to `--on-primary`.
76
+ * Cuando es `true`:
77
+ * - La pista se llena con el color `--primary`.
78
+ * - El control (thumb) crece de 16dp a 24dp y se desliza hacia el borde final (trailing).
79
+ * - El color del control cambia a `--on-primary`.
72
80
  *
73
81
  * @default false
74
82
  */
75
83
  @property({ type: Boolean, reflect: true }) checked = false;
76
84
 
77
85
  /**
78
- * When `true`, the switch is non-interactive and renders at 50% opacity.
86
+ * Cuando es `true`, el switch no es interactivo y se renderiza al 50% de opacidad.
79
87
  *
80
88
  * @default false
81
89
  */
82
90
  @property({ type: Boolean, reflect: true }) disabled = false;
83
91
 
84
92
  /**
85
- * When `true`, renders icon glyphs inside the thumb.
93
+ * Cuando es `true`, renderiza glifos de iconos dentro del control (thumb).
86
94
  *
87
- * Uses Material Symbols ligatures:
88
- * - Unchecked state: `close` icon.
89
- * - Checked state: `check` icon.
95
+ * Utiliza ligaduras de Material Symbols:
96
+ * - Estado desmarcado (Unchecked): icono `close`.
97
+ * - Estado marcado (Checked): icono `check`.
90
98
  *
91
- * The icon size (16dp) is set via the `--_thumb` CSS custom property.
99
+ * El tamaño del icono (16dp) se establece a través de la propiedad CSS `--_thumb`.
92
100
  *
93
101
  * @default false
94
102
  */
95
103
  @property({ type: Boolean, reflect: true }) icon = false;
96
104
 
97
105
  /**
98
- * Forwarded to the native `<input name>` attribute for form participation.
106
+ * Reenviado al atributo nativo `<input name>` para participar en formularios.
99
107
  *
100
108
  * @default ''
101
109
  */
102
110
  @property({ reflect: true }) name = '';
103
111
 
104
112
  /**
105
- * Forwarded to the native `<input value>` attribute.
106
- * The value submitted in a form when this switch is checked.
113
+ * Reenviado al atributo nativo `<input value>`.
114
+ * El valor enviado en un formulario cuando este switch está marcado.
107
115
  *
108
116
  * @default ''
109
117
  */
110
118
  @property({ reflect: true }) value = '';
111
119
 
112
- /** Direct reference to the native input element for programmatic access. */
120
+ /** Referencia directa al elemento input nativo para acceso programático. */
113
121
  @query('input') private _input!: HTMLInputElement;
114
122
 
115
123
  /**
116
- * Syncs `checked` and `disabled` imperatively to the native input after render.
124
+ * Sincroniza `checked` y `disabled` de forma imperativa en el input nativo después del renderizado.
117
125
  *
118
- * @param changed - Map of changed property names to their previous values.
126
+ * @param changed - Mapa de los nombres de las propiedades modificadas a sus valores anteriores.
119
127
  */
120
128
  override updated(changed: Map<string, unknown>) {
121
129
  if (this._input) {
122
- if (changed.has('checked')) this._input.checked = this.checked;
130
+ if (changed.has('checked')) {
131
+ this._input.checked = this.checked;
132
+ this._internals?.setFormValue?.(this.checked ? (this.value || 'on') : null);
133
+ }
123
134
  if (changed.has('disabled')) this._input.disabled = this.disabled;
124
135
  }
125
136
  }
126
137
 
127
138
  /**
128
- * Handles the native input `change` event.
139
+ * Maneja el evento nativo `change` del input.
129
140
  *
130
- * Updates `this.checked` and re-dispatches a composed `'change'` event
131
- * so it bubbles across shadow DOM boundaries.
141
+ * Actualiza `this.checked` y vuelve a despachar un evento `'change'` compuesto
142
+ * para que burbujee a través de los límites del shadow DOM.
132
143
  *
133
- * @param e - The native `change` event from the hidden `<input>`.
144
+ * @param e - El evento nativo `change` desde el `<input>` oculto.
134
145
  */
135
146
  private _onChange(e: Event) {
136
147
  this.checked = (e.target as HTMLInputElement).checked;
@@ -300,6 +311,14 @@ export class MoniSwitch extends MoniElement {
300
311
  `
301
312
  ];
302
313
 
314
+ /**
315
+ * Renderiza el switch como un `<input type="checkbox">` oculto + pista visual mediante `<span>`.
316
+ *
317
+ * El input oculto ocupa espacio de diseño real (para el área de clic/hit area) pero tiene opacidad 0;
318
+ * el `<span>` proporciona la píldora visual y el control (thumb) (vía `::before`/`::after`).
319
+ * La vinculación `.checked` asegura que el estado del input en el DOM se mantenga sincronizado con
320
+ * `this.checked`.
321
+ */
303
322
  override render() {
304
323
  return html`<label class=${this.icon ? 'switch icon' : 'switch'} part="switch">
305
324
  <input
@@ -21,31 +21,31 @@ describe('moni-tab indicator-size', () => {
21
21
  tabs.remove();
22
22
  });
23
23
 
24
- it('reflects indicatorSize on the host', async () => {
24
+ it('refleja indicatorSize en el host', async () => {
25
25
  tabs.indicatorSize = 'min';
26
26
  await tabs.updateComplete;
27
27
  expect(tabs.getAttribute('indicator-size')).toBe('min');
28
28
  });
29
29
 
30
- it('reflects indicatorSize="max" on the host', async () => {
30
+ it('refleja indicatorSize="max" en el host', async () => {
31
31
  tabs.indicatorSize = 'max';
32
32
  await tabs.updateComplete;
33
33
  expect(tabs.getAttribute('indicator-size')).toBe('max');
34
34
  });
35
35
 
36
- it('reflects indicatorSize="default" on the host', async () => {
36
+ it('refleja indicatorSize="default" en el host', async () => {
37
37
  tabs.indicatorSize = 'default';
38
38
  await tabs.updateComplete;
39
39
  expect(tabs.getAttribute('indicator-size')).toBe('default');
40
40
  });
41
41
 
42
- it('vertical orientation is reflected on the tabs host', async () => {
42
+ it('la orientación vertical se refleja en el host de las pestañas (tabs)', async () => {
43
43
  tabs.vertical = true;
44
44
  await tabs.updateComplete;
45
45
  expect(tabs.hasAttribute('vertical')).toBe(true);
46
46
  });
47
47
 
48
- it('tab active state reflects aria-selected=true', async () => {
48
+ it('el estado activo de la pestaña refleja aria-selected=true', async () => {
49
49
  tab.active = true;
50
50
  await tab.updateComplete;
51
51
  const a = tab.shadowRoot?.querySelector('a');
@@ -11,35 +11,35 @@ import { MoniElement, sharedStyles } from './_base/index.js';
11
11
  import './moni-icon.js';
12
12
 
13
13
  /**
14
- * Material Design 3 Tab component.
14
+ * Componente Material Design 3 Tab (Pestaña).
15
15
  *
16
- * An individual interactive tab element designed to be placed inside a
17
- * `<moni-tabs>` container. Tabs organize content across different screens,
18
- * data sets, and other interactions.
16
+ * Un elemento interactivo de pestaña individual diseñado para ser colocado dentro de un
17
+ * contenedor `<moni-tabs>`. Las pestañas organizan el contenido en diferentes pantallas,
18
+ * conjuntos de datos y otras interacciones.
19
19
  *
20
- * **M3 spec reference:** `m3-docs/components/tabs/specs.md`
20
+ * **Referencia a la especificación M3:** `m3-docs/components/tabs/specs.md`
21
21
  *
22
- * **Visual layout & interaction:**
23
- * Internally renders as an `<a>` element to support native link behavior when
24
- * an `href` is provided, but behaves visually as a tab button. It displays a
25
- * text label and an optional Material icon. If the parent `<moni-tabs>` has the
26
- * `vertical` attribute set, the layout automatically adjusts to stack the icon
27
- * above the text.
22
+ * **Diseño visual e interacción:**
23
+ * Internamente se renderiza como un elemento `<a>` para soportar el comportamiento nativo
24
+ * de enlace cuando se proporciona un `href`, pero se comporta visualmente como un botón de pestaña.
25
+ * Muestra una etiqueta de texto y un icono opcional de Material. Si el `<moni-tabs>` padre tiene el
26
+ * atributo `vertical` configurado, el diseño se ajusta automáticamente para apilar el icono
27
+ * encima del texto.
28
28
  *
29
- * **State:**
30
- * The `active` attribute highlights the tab, applying the primary color to the
31
- * text and rendering the active indicator line (handled via CSS in the parent
32
- * container or pseudo-elements).
29
+ * **Estado:**
30
+ * El atributo `active` resalta la pestaña, aplicando el color principal (primary) al
31
+ * texto y renderizando la línea indicadora activa (manejado vía CSS en el contenedor
32
+ * padre o mediante pseudo-elementos).
33
33
  *
34
34
  * @example
35
35
  * ```html
36
36
  * <moni-tabs>
37
- * <moni-tab active icon="home" label="Home"></moni-tab>
38
- * <moni-tab icon="settings" label="Settings" href="/settings"></moni-tab>
37
+ * <moni-tab active icon="home" label="Inicio"></moni-tab>
38
+ * <moni-tab icon="settings" label="Ajustes" href="/settings"></moni-tab>
39
39
  * </moni-tabs>
40
40
  * ```
41
41
  *
42
- * @csspart tab - The inner `<a>` element acting as the tab button.
42
+ * @csspart tab - El elemento `<a>` interno que actúa como botón de pestaña.
43
43
  */
44
44
  @customElement('moni-tab')
45
45
  export class MoniTab extends MoniElement {
@@ -113,8 +113,8 @@ export class MoniTab extends MoniElement {
113
113
  block-size: auto;
114
114
  }
115
115
 
116
- /* M3 indicator-size='min': 24dp tall (centered on tab) with
117
- 8dp top/bottom radiusmatches M3 spec § Indicator anatomy. */
116
+ /* M3 indicator-size='min': 24dp de altura (centrado en la pestaña) con
117
+ radio superior/inferior de 8dp coincide con la especificación M3 § Indicator anatomy. */
118
118
  :host-context(moni-tabs[indicator-size='min']) a.active::before {
119
119
  margin: 0 auto;
120
120
  max-inline-size: min(100%, 4rem);
@@ -130,13 +130,21 @@ export class MoniTab extends MoniElement {
130
130
  border-radius: 0.5rem;
131
131
  }
132
132
 
133
- /* M3 indicator-size='max': full tab height, no rounding. */
133
+ /* M3 indicator-size='max': altura completa de la pestaña, sin redondeo. */
134
134
  :host-context(moni-tabs[indicator-size='max']) a.active::before {
135
135
  border-radius: 0;
136
136
  }
137
137
  `
138
138
  ];
139
139
 
140
+ /**
141
+ * Renderiza el botón de pestaña con un icono opcional y etiqueta.
142
+ *
143
+ * El botón de pestaña es un elemento `<a>` con `aria-selected` para
144
+ * cumplimiento WCAG. La clase `active` controla la transición de subrayado
145
+ * del indicador CSS (a través del pseudo-elemento `::before`). El `<moni-icon>`
146
+ * opcional se renderiza antes del texto de la etiqueta cuando se establece `icon`.
147
+ */
140
148
  override render() {
141
149
  return html`<a
142
150
  href=${this.href || '#'}