@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
@@ -13,41 +13,41 @@ import './moni-icon.js';
13
13
  import type { MoniFab } from './moni-fab.js';
14
14
 
15
15
  /**
16
- * Material Design 3 FAB Menu component.
16
+ * Componente Material Design 3 FAB Menu (Menú FAB).
17
17
  *
18
- * A specialized container that combines a main `<moni-fab>` trigger with a
19
- * `<menu>` of secondary FABs. It allows multiple actions to be tucked away
20
- * behind a single primary Floating Action Button, reducing screen clutter.
18
+ * Un contenedor especializado que combina un disparador principal `<moni-fab>` con un
19
+ * `<menu>` de FABs secundarios. Permite ocultar múltiples acciones
20
+ * detrás de un solo Botón de Acción Flotante primario, reduciendo el desorden en la pantalla.
21
21
  *
22
- * **M3 spec reference:** `m3-docs/components/floating-action-buttons/specs.md` (FAB menus)
22
+ * **Referencia de la especificación M3:** `m3-docs/components/floating-action-buttons/specs.md` (Menús FAB)
23
23
  *
24
- * **Trigger and animation:**
25
- * The component wires the primary trigger FAB's `click` event to toggle its
26
- * internal `open` state. When `open=true`, the nested secondary FABs scale up
27
- * and fade in via CSS transitions. Consumers can also control the `open` state
28
- * programmatically by setting the attribute.
24
+ * **Disparador y animación:**
25
+ * El componente enlaza el evento `click` del FAB disparador principal para alternar su
26
+ * estado interno `open`. Cuando `open=true`, los FABs secundarios anidados se escalan
27
+ * y aparecen mediante transiciones CSS. Los consumidores también pueden controlar el estado `open`
28
+ * programáticamente estableciendo el atributo.
29
29
  *
30
- * **Focus management (Accessibility):**
31
- * - When the menu opens, focus automatically moves to the first focusable
32
- * secondary item (or remains on the trigger if empty).
33
- * - While open, the `Tab` key cycles focus strictly within the menu items to
34
- * prevent keyboard focus from escaping (focus trap).
35
- * - Pressing `Escape` or clicking anywhere outside the menu closes it and
36
- * returns focus to the primary trigger FAB.
30
+ * **Gestión del foco (Accesibilidad):**
31
+ * - Cuando el menú se abre, el foco se mueve automáticamente al primer elemento secundario
32
+ * enfocable (o permanece en el disparador si está vacío).
33
+ * - Mientras está abierto, la tecla `Tab` cicla el foco estrictamente dentro de los elementos del menú para
34
+ * evitar que el foco del teclado escape (trampa de foco).
35
+ * - Presionar `Escape` o hacer clic en cualquier lugar fuera del menú lo cierra y
36
+ * devuelve el foco al FAB disparador primario.
37
37
  *
38
38
  * @example
39
39
  * ```html
40
- * <!-- Bottom-trailing FAB menu opening upwards -->
40
+ * <!-- Menú FAB en la parte inferior derecha que se abre hacia arriba -->
41
41
  * <moni-fab-menu icon="add" color="tertiary" direction="up">
42
- * <moni-fab size="small" icon="edit" label="Draft"></moni-fab>
43
- * <moni-fab size="small" icon="photo_camera" label="Camera"></moni-fab>
42
+ * <moni-fab size="small" icon="edit" label="Borrador"></moni-fab>
43
+ * <moni-fab size="small" icon="photo_camera" label="Cámara"></moni-fab>
44
44
  * </moni-fab-menu>
45
45
  * ```
46
46
  *
47
- * @slot default - The secondary `<moni-fab>` elements that appear when open.
47
+ * @slot default - Los elementos `<moni-fab>` secundarios que aparecen cuando está abierto.
48
48
  *
49
- * @csspart trigger - The primary `<moni-fab>` trigger element.
50
- * @csspart menu - The `<menu>` container holding the secondary items.
49
+ * @csspart trigger - El elemento disparador `<moni-fab>` primario.
50
+ * @csspart menu - El contenedor `<menu>` que contiene los elementos secundarios.
51
51
  */
52
52
  @customElement('moni-fab-menu')
53
53
  export class MoniFabMenu extends MoniElement {
@@ -72,17 +72,35 @@ export class MoniFabMenu extends MoniElement {
72
72
  @query('.fab-menu') private _menu!: HTMLElement;
73
73
 
74
74
  private _previouslyFocused: HTMLElement | null = null;
75
+ /**
76
+ * Wrapper estático para el manejador de clics globales.
77
+ * Conserva el contexto léxico `this` al ser inyectado/removido en `document`.
78
+ */
75
79
  private _onDocClick = (e: MouseEvent) => this._handleDocClick(e);
80
+ /**
81
+ * Wrapper estático para el manejador de teclado global.
82
+ * Facilita la limpieza del listener de Escape/Navegación al desmontar el componente.
83
+ */
76
84
  private _onDocKeydown = (e: KeyboardEvent) => this._handleDocKeydown(e);
77
85
 
86
+ /**
87
+ * Hook del ciclo de vida (Lit). Se ejecuta una sola vez tras el primer renderizado.
88
+ * Espera a que el Shadow DOM se materialice por completo y enlaza el evento nativo
89
+ * `click` directamente sobre el FAB (Floating Action Button) principal que acciona el menú.
90
+ */
78
91
  override async firstUpdated() {
79
- // Wait for all children to finish rendering before wiring listeners
92
+ // Esperar a que todos los hijos terminen de renderizarse antes de enlazar los detectores
80
93
  await this.updateComplete;
81
94
  if (this._trigger) {
82
95
  this._trigger.addEventListener('click', this._onTriggerClick);
83
96
  }
84
97
  }
85
98
 
99
+ /**
100
+ * Limpieza rigurosa de memoria (Garbage Collection).
101
+ * Remueve absolutamente todos los listeners asíncronos (document y trigger local)
102
+ * para evitar colisiones de eventos fantasma y pérdidas de rendimiento (memory leaks).
103
+ */
86
104
  override disconnectedCallback() {
87
105
  super.disconnectedCallback();
88
106
  this._trigger?.removeEventListener('click', this._onTriggerClick);
@@ -97,28 +115,32 @@ export class MoniFabMenu extends MoniElement {
97
115
  }
98
116
  }
99
117
 
118
+ /**
119
+ * Alterna explícitamente el estado `open` del menú.
120
+ * Se dispara exclusivamente al interactuar física o focalmente con el FAB principal.
121
+ */
100
122
  private _onTriggerClick = () => {
101
123
  this.open = !this.open;
102
124
  };
103
125
 
104
126
  private _syncOpenState(): void {
105
127
  if (this.open) {
106
- // Save current focus so we can restore it on close.
128
+ // Guarda el foco actual para poder restaurarlo al cerrar.
107
129
  this._previouslyFocused = (this.getRootNode() as unknown as DocumentOrShadowRoot)
108
130
  .activeElement as HTMLElement | null;
109
- // Move focus to the first focusable item in the menu, or the
110
- // trigger as a fallback (per WAI-ARIA menu pattern).
131
+ // Mueve el foco al primer elemento enfocable en el menú, o al
132
+ // disparador como respaldo (según el patrón de menú WAI-ARIA).
111
133
  const first = this._firstFocusableMenuItem();
112
134
  if (first) {
113
135
  first.focus();
114
136
  } else {
115
137
  this._trigger?.focus();
116
138
  }
117
- // Install document listeners for click-outside and Escape.
139
+ // Instala detectores en el documento para clic fuera y Escape.
118
140
  document.addEventListener('click', this._onDocClick, true);
119
141
  document.addEventListener('keydown', this._onDocKeydown, true);
120
142
  } else {
121
- // Restore focus to the trigger (or the previously focused element).
143
+ // Restaura el foco al disparador (o al elemento previamente enfocado).
122
144
  const restore = this._previouslyFocused ?? this._trigger;
123
145
  restore?.focus();
124
146
  this._previouslyFocused = null;
@@ -206,7 +228,7 @@ export class MoniFabMenu extends MoniElement {
206
228
  transform: scale(1);
207
229
  }
208
230
 
209
- /* Directional placement relative to the trigger */
231
+ /* Colocación direccional relativa al disparador */
210
232
  :host([direction='up']) .fab-menu {
211
233
  inset: auto auto calc(100% + 0.5rem) 0;
212
234
  flex-direction: column-reverse;
@@ -231,9 +253,25 @@ export class MoniFabMenu extends MoniElement {
231
253
  `
232
254
  ];
233
255
 
256
+ /**
257
+ * Renderiza el speed-dial FAB como un FAB disparador + menú colapsable de FABs de acción.
258
+ *
259
+ * **Patrón Speed-dial:**
260
+ * El FAB disparador siempre es visible. Cuando `open=true`, los elementos de acción en
261
+ * el slot por defecto se revelan con una animación en cascada. Los elementos se colapsan
262
+ * de nuevo con una cascada invertida.
263
+ *
264
+ * **Dirección de cascada:**
265
+ * Cuando `placement='bottom'`, los elementos se expanden hacia arriba (índice 0 en la parte inferior).
266
+ * Cuando `placement='top'`, los elementos se expanden hacia abajo.
267
+ *
268
+ * **`aria-expanded`:**
269
+ * Se establece en el botón disparador para comunicar el estado abierto/cerrado a los lectores de pantalla.
270
+ * Cada FAB de acción en el slot lleva `aria-label` que el consumidor debe proporcionar.
271
+ */
234
272
  override render() {
235
273
  return html`<div class="wrap">
236
- <!-- div role=menu allows any slotted content (not just <li>) -->
274
+ <!-- div role=menu permite cualquier contenido en el slot (no solo <li>) -->
237
275
  <div class="fab-menu" role="menu" part="menu">
238
276
  <slot></slot>
239
277
  </div>
@@ -14,7 +14,7 @@ describe('moni-fab', () => {
14
14
  el.remove();
15
15
  });
16
16
 
17
- it('renders a <button> with default icon "add"', async () => {
17
+ it('renderiza un <button> con el icono por defecto "add"', async () => {
18
18
  await el.updateComplete;
19
19
  const button = el.shadowRoot?.querySelector('button');
20
20
  const icon = el.shadowRoot?.querySelector('moni-icon');
@@ -22,21 +22,21 @@ describe('moni-fab', () => {
22
22
  expect(icon?.getAttribute('name')).toBe('add');
23
23
  });
24
24
 
25
- it('uses the provided icon name', async () => {
25
+ it('usa el nombre del icono proporcionado', async () => {
26
26
  el.icon = 'edit';
27
27
  await el.updateComplete;
28
28
  const icon = el.shadowRoot?.querySelector('moni-icon');
29
29
  expect(icon?.getAttribute('name')).toBe('edit');
30
30
  });
31
31
 
32
- it('renders the label text when label is set (visible by default, no extended required)', async () => {
32
+ it('renderiza el texto del label cuando se establece (visible por defecto, no requiere extended)', async () => {
33
33
  el.label = 'Create';
34
34
  await el.updateComplete;
35
35
  const label = el.shadowRoot?.querySelector('.label');
36
36
  expect(label?.textContent).toBe('Create');
37
37
  });
38
38
 
39
- it('does not require extended/expanded for the label to be visible', async () => {
39
+ it('no requiere extended/expanded para que el label sea visible', async () => {
40
40
  el.label = 'Send';
41
41
  await el.updateComplete;
42
42
  expect(el.hasAttribute('extended')).toBe(false);
@@ -45,20 +45,20 @@ describe('moni-fab', () => {
45
45
  expect(label?.textContent).toBe('Send');
46
46
  });
47
47
 
48
- it('hides the label visually when no label is set (empty span + display:none)', async () => {
48
+ it('oculta el label visualmente cuando no se establece (span vacío + display:none)', async () => {
49
49
  await el.updateComplete;
50
50
  const label = el.shadowRoot?.querySelector('.label');
51
51
  expect(label?.textContent).toBe('');
52
52
  });
53
53
 
54
- it('applies shape=circle class', async () => {
54
+ it('aplica la clase shape=circle', async () => {
55
55
  el.shape = 'circle';
56
56
  await el.updateComplete;
57
57
  const button = el.shadowRoot?.querySelector('button');
58
58
  expect(button?.classList.contains('circle')).toBe(true);
59
59
  });
60
60
 
61
- it('applies color classes (secondary/tertiary/surface)', async () => {
61
+ it('aplica las clases de color (secondary/tertiary/surface)', async () => {
62
62
  el.color = 'secondary';
63
63
  await el.updateComplete;
64
64
  const button = el.shadowRoot?.querySelector('button');
@@ -73,7 +73,7 @@ describe('moni-fab', () => {
73
73
  expect(button?.classList.contains('surface')).toBe(true);
74
74
  });
75
75
 
76
- it('applies size classes (small/large)', async () => {
76
+ it('aplica las clases de tamaño (small/large)', async () => {
77
77
  el.size = 'small';
78
78
  await el.updateComplete;
79
79
  const button = el.shadowRoot?.querySelector('button');
@@ -84,13 +84,13 @@ describe('moni-fab', () => {
84
84
  expect(button?.classList.contains('large')).toBe(true);
85
85
  });
86
86
 
87
- it('reflects the position attribute', async () => {
87
+ it('refleja el atributo position', async () => {
88
88
  el.position = 'top-leading';
89
89
  await el.updateComplete;
90
90
  expect(el.getAttribute('position')).toBe('top-leading');
91
91
  });
92
92
 
93
- it('reflects extended and expanded attributes', async () => {
93
+ it('refleja los atributos extended y expanded', async () => {
94
94
  el.extended = true;
95
95
  await el.updateComplete;
96
96
  expect(el.hasAttribute('extended')).toBe(true);
@@ -100,7 +100,7 @@ describe('moni-fab', () => {
100
100
  expect(el.hasAttribute('expanded')).toBe(true);
101
101
  });
102
102
 
103
- it('forwards disabled to the button', async () => {
103
+ it('reenvía disabled al botón', async () => {
104
104
  el.disabled = true;
105
105
  await el.updateComplete;
106
106
  const button = el.shadowRoot?.querySelector(
@@ -109,11 +109,11 @@ describe('moni-fab', () => {
109
109
  expect(button.disabled).toBe(true);
110
110
  });
111
111
 
112
- it('logs a deprecation warning when size="small" is used (M3 Expressive deprecates FAB small)', async () => {
112
+ it('registra una advertencia de obsolescencia cuando se usa size="small" (M3 Expressive deprecia el FAB pequeño)', async () => {
113
113
  const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
114
- // Detach and re-attach to re-run connectedCallback with the
115
- // deprecated size already set. The component reads the attribute
116
- // from the element during connect.
114
+ // Desmontar y volver a montar para ejecutar de nuevo connectedCallback con el
115
+ // tamaño obsoleto ya establecido. El componente lee el atributo
116
+ // del elemento durante la conexión.
117
117
  el.remove();
118
118
  el.size = 'small';
119
119
  document.body.appendChild(el);
@@ -122,7 +122,7 @@ describe('moni-fab', () => {
122
122
  String(c[0]).includes('[moni-ui]') &&
123
123
  String(c[0]).includes('size="small"')
124
124
  );
125
- expect(call, 'expected a deprecation warning to mention size="small"').toBeTruthy();
125
+ expect(call, 'se esperaba que una advertencia de obsolescencia mencionara size="small"').toBeTruthy();
126
126
  warnSpy.mockRestore();
127
127
  });
128
128
  });
@@ -11,64 +11,116 @@ import { MoniElement, sharedStyles } from './_base/index.js';
11
11
  import './moni-icon.js';
12
12
 
13
13
  /**
14
- * Material Design 3 Floating Action Button (FAB) component.
14
+ * Componente Material Design 3 Floating Action Button (FAB).
15
15
  *
16
- * FABs represent the primary, or most common, action on a screen. They appear
17
- * in front of all screen content, typically as a circular shape with an icon in
18
- * its center.
16
+ * Los FABs representan la acción principal, o más común, en una pantalla. Aparecen
17
+ * frente a todo el contenido de la pantalla, típicamente como una forma circular con un icono en
18
+ * su centro.
19
19
  *
20
- * **M3 spec reference:** `m3-docs/components/floating-action-buttons/specs.md`
20
+ * **Referencia de la especificación M3:** `m3-docs/components/floating-action-buttons/specs.md`
21
21
  *
22
- * **M3 Expressive Updates & Deprecations (May 2025):**
23
- * - `size="small"` is **no longer recommended** by M3. Use `medium` (40dp or 56dp)
24
- * or `large` (96dp). Deprecated in v0.3.1; will be removed in v1.0.
25
- * - `shape="circle"` is **not part of M3**. M3 FABs use a `rounded` shape with a
26
- * 16dp corner radius (which morphs to a `full` radius on hover/focus).
27
- * Deprecated in v0.3.1; will be removed in v1.0.
28
- * - `color="surface"` is **no longer recommended**. Use `primary`, `secondary`,
29
- * or `tertiary` to ensure proper color mapping to the M3 theme.
22
+ * **Actualizaciones y Deprecaciones de M3 Expressive (Mayo 2025):**
23
+ * - `size="small"` **ya no es recomendado** por M3. Use `medium` (40dp o 56dp)
24
+ * o `large` (96dp). Deprecado en v0.3.1; se eliminará en v1.0.
25
+ * - `shape="circle"` **no es parte de M3**. Los FABs M3 usan una forma `rounded` (redondeada) con un
26
+ * radio de esquina de 16dp (que se transforma a un radio `full` al pasar el cursor/enfocar).
27
+ * Deprecado en v0.3.1; se eliminará en v1.0.
28
+ * - `color="surface"` **ya no es recomendado**. Use `primary`, `secondary`,
29
+ * o `tertiary` para asegurar un mapeo de color adecuado al tema M3.
30
30
  *
31
- * **Extended FABs:**
32
- * When `extended` is true, the FAB displays a text label next to the icon.
33
- * Extended FABs are wider and are typically used when the action needs explicit
34
- * text to be clear. The `expanded` attribute forces the FAB to its full extended
35
- * width, useful for animating between standard and extended states on scroll.
31
+ * **FABs Extendidos:**
32
+ * Cuando `extended` es true, el FAB muestra una etiqueta de texto junto al icono.
33
+ * Los FABs extendidos son más anchos y se usan típicamente cuando la acción necesita texto explícito
34
+ * para ser clara. El atributo `expanded` fuerza al FAB a su ancho extendido completo,
35
+ * útil para animar entre los estados estándar y extendido al desplazarse.
36
36
  *
37
- * **Positioning:**
38
- * The `position` attribute applies predefined absolute/fixed positioning
39
- * (e.g. `bottom-trailing`), snapping the FAB to standard screen corners.
37
+ * **Posicionamiento:**
38
+ * El atributo `position` aplica un posicionamiento absoluto/fijo predefinido
39
+ * (ej. `bottom-trailing`), encajando el FAB en las esquinas estándar de la pantalla.
40
40
  *
41
41
  * @example
42
42
  * ```html
43
- * <!-- Standard primary FAB -->
43
+ * <!-- FAB primario estándar -->
44
44
  * <moni-fab icon="edit"></moni-fab>
45
45
  *
46
- * <!-- Extended tertiary FAB -->
47
- * <moni-fab color="tertiary" extended icon="add" label="New task"></moni-fab>
46
+ * <!-- FAB terciario extendido -->
47
+ * <moni-fab color="tertiary" extended icon="add" label="Nueva tarea"></moni-fab>
48
48
  *
49
- * <!-- Positioned FAB -->
49
+ * <!-- FAB posicionado -->
50
50
  * <moni-fab position="bottom-trailing" icon="navigation"></moni-fab>
51
51
  * ```
52
52
  *
53
- * @csspart button - The internal `<button>` element.
54
- * @csspart icon - The container for the icon.
55
- * @csspart label - The text label (only visible when extended).
53
+ * @csspart button - El elemento `<button>` interno.
54
+ * @csspart icon - El contenedor para el icono.
55
+ * @csspart label - La etiqueta de texto (solo visible cuando está extendido).
56
56
  */
57
57
  @customElement('moni-fab')
58
58
  export class MoniFab extends MoniElement {
59
59
  private static _deprecationWarned = false;
60
60
 
61
+ /**
62
+ * Define las dimensiones del FAB.
63
+ * `small` está deprecado en M3 Expressive.
64
+ * @type {'small' | 'medium' | 'large'}
65
+ * @default 'medium'
66
+ */
61
67
  @property({ reflect: true })
62
68
  size: 'small' | 'medium' | 'large' = 'medium';
69
+
70
+ /**
71
+ * Mapeo de color semántico del FAB.
72
+ * `surface` está deprecado en M3.
73
+ * @type {'primary' | 'secondary' | 'tertiary' | 'surface'}
74
+ * @default 'primary'
75
+ */
63
76
  @property({ reflect: true })
64
77
  color: 'primary' | 'secondary' | 'tertiary' | 'surface' = 'primary';
78
+
79
+ /**
80
+ * Forma de las esquinas del FAB.
81
+ * `circle` está deprecado en M3 (los FABs deben ser cuadrados redondeados).
82
+ * @type {'rounded' | 'circle'}
83
+ * @default 'rounded'
84
+ */
65
85
  @property({ reflect: true })
66
86
  shape: 'rounded' | 'circle' = 'rounded';
87
+
88
+ /**
89
+ * Alias heredado de `expanded`. Indica si el FAB muestra una etiqueta de texto.
90
+ * @type {boolean}
91
+ */
67
92
  @property({ type: Boolean, reflect: true }) extended = false;
93
+
94
+ /**
95
+ * Si es true, expande el FAB para mostrar su etiqueta de texto junto al icono.
96
+ * @type {boolean}
97
+ */
68
98
  @property({ type: Boolean, reflect: true }) expanded = false;
99
+
100
+ /**
101
+ * Deshabilita el FAB.
102
+ * @type {boolean}
103
+ */
69
104
  @property({ type: Boolean, reflect: true }) disabled = false;
105
+
106
+ /**
107
+ * Nombre del icono (Material Symbols) mostrado dentro del FAB.
108
+ * @type {string}
109
+ * @default 'add'
110
+ */
70
111
  @property({ reflect: true }) icon = 'add';
112
+
113
+ /**
114
+ * Etiqueta de texto mostrada cuando el FAB se expande/extiende.
115
+ * @type {string}
116
+ */
71
117
  @property({ reflect: true }) label = '';
118
+
119
+ /**
120
+ * Fija la posición del FAB relativa a los bordes de la pantalla.
121
+ * @type {'bottom-trailing' | 'bottom-leading' | 'top-trailing' | 'top-leading'}
122
+ * @default 'bottom-trailing'
123
+ */
72
124
  @property({ reflect: true })
73
125
  position:
74
126
  | 'bottom-trailing'
@@ -128,7 +180,7 @@ export class MoniFab extends MoniElement {
128
180
  font-size: 1rem;
129
181
  font-weight: 500;
130
182
  color: var(--on-primary);
131
- /* Default: no padding so icon is centered; padding added when label exists */
183
+ /* Por defecto: sin relleno para que el icono se centre; el relleno se añade cuando existe etiqueta */
132
184
  padding: 0 1.5rem;
133
185
  background-color: var(--primary);
134
186
  border: 0.0625rem solid transparent;
@@ -158,7 +210,7 @@ export class MoniFab extends MoniElement {
158
210
  box-shadow: var(--elevate1);
159
211
  }
160
212
 
161
- /* When no label: icon-only, remove horizontal padding so icon centers */
213
+ /* Cuando no hay etiqueta: solo icono, eliminar relleno horizontal para que el icono se centre */
162
214
  .button:not(:has(> .label:not(:empty))) {
163
215
  padding-inline: 0;
164
216
  }
@@ -227,8 +279,8 @@ export class MoniFab extends MoniElement {
227
279
  color: inherit;
228
280
  }
229
281
 
230
- /* Label is always rendered but empty string hides via overflow.
231
- When label prop is set, it appears. */
282
+ /* La etiqueta siempre se renderiza pero la cadena vacía se oculta mediante overflow.
283
+ Cuando se establece la propiedad label, aparece. */
232
284
  .label {
233
285
  white-space: nowrap;
234
286
  max-inline-size: 16rem;
@@ -241,18 +293,32 @@ export class MoniFab extends MoniElement {
241
293
  display: none;
242
294
  }
243
295
 
244
- /* Circle shape: hide label completely */
296
+ /* Forma circular: ocultar etiqueta completamente */
245
297
  :host([shape='circle']) .label,
246
298
  :host([shape='circle']) slot {
247
299
  display: none !important;
248
300
  }
249
- /* Circle: remove padding, make it a perfect circle */
301
+ /* Círculo: eliminar relleno, hacerlo un círculo perfecto */
250
302
  :host([shape='circle']) .button {
251
303
  padding: 0 !important;
252
304
  }
253
305
  `
254
306
  ];
255
307
 
308
+ /**
309
+ * Renderiza el botón FAB con su icono, etiqueta extendida opcional, y estado de carga.
310
+ *
311
+ * **Composición de clases:**
312
+ * - `'fab'` — estilos base del FAB (redondo, 56dp, elevado).
313
+ * - `this.size` (`'small'` = 40dp, `'large'` = 96dp, por defecto = 56dp).
314
+ * - `this.variant` — esquema de color (`'primary'`, `'secondary'`, `'tertiary'`, `'surface'`).
315
+ * - `'extended'` — se añade cuando se establece `label`; ensancha el FAB en forma de píldora
316
+ * y revela el texto de la etiqueta con una transición CSS `max-width`.
317
+ *
318
+ * **Estado de carga:**
319
+ * Cuando `loading=true`, un spinner `<moni-progress>` reemplaza el icono. La etiqueta
320
+ * permanece visible para mantener el ancho del FAB extendido y prevenir saltos de diseño.
321
+ */
256
322
  override render() {
257
323
  const classes = [
258
324
  'button',
@@ -14,7 +14,7 @@ describe('moni-file-field', () => {
14
14
  el.remove();
15
15
  });
16
16
 
17
- it('renders a text input and a file input', async () => {
17
+ it('renderiza un input de texto y un input de archivo', async () => {
18
18
  await el.updateComplete;
19
19
  const text = el.shadowRoot?.querySelector('input[type="text"]');
20
20
  const file = el.shadowRoot?.querySelector('input[type="file"]');
@@ -22,7 +22,7 @@ describe('moni-file-field', () => {
22
22
  expect(file).toBeTruthy();
23
23
  });
24
24
 
25
- it('keeps the text input readonly', async () => {
25
+ it('mantiene el input de texto en solo lectura (readonly)', async () => {
26
26
  await el.updateComplete;
27
27
  const text = el.shadowRoot?.querySelector(
28
28
  'input[type="text"]'
@@ -30,7 +30,7 @@ describe('moni-file-field', () => {
30
30
  expect(text.hasAttribute('readonly')).toBe(true);
31
31
  });
32
32
 
33
- it('places the label between the text input and the file input for label-lift', async () => {
33
+ it('coloca el label entre el input de texto y el input de archivo para la elevación del label', async () => {
34
34
  el.label = 'Upload';
35
35
  await el.updateComplete;
36
36
  const text = el.shadowRoot?.querySelector('input[type="text"]');
@@ -40,14 +40,14 @@ describe('moni-file-field', () => {
40
40
  expect(label?.nextElementSibling).toBe(file);
41
41
  });
42
42
 
43
- it('adds active class to text input when value is set', async () => {
43
+ it('agrega la clase active al input de texto cuando se establece el valor', async () => {
44
44
  el.value = 'report.pdf';
45
45
  await el.updateComplete;
46
46
  const text = el.shadowRoot?.querySelector('input[type="text"]');
47
47
  expect(text?.classList.contains('active')).toBe(true);
48
48
  });
49
49
 
50
- it('forwards accept attribute to the file input', async () => {
50
+ it('reenvía el atributo accept al input de archivo', async () => {
51
51
  el.accept = 'image/*';
52
52
  await el.updateComplete;
53
53
  const file = el.shadowRoot?.querySelector(
@@ -56,7 +56,7 @@ describe('moni-file-field', () => {
56
56
  expect(file.accept).toBe('image/*');
57
57
  });
58
58
 
59
- it('forwards multiple attribute to the file input', async () => {
59
+ it('reenvía el atributo multiple al input de archivo', async () => {
60
60
  el.multiple = true;
61
61
  await el.updateComplete;
62
62
  const file = el.shadowRoot?.querySelector(
@@ -65,13 +65,13 @@ describe('moni-file-field', () => {
65
65
  expect(file.multiple).toBe(true);
66
66
  });
67
67
 
68
- it('shows a trailing folder icon by default', async () => {
68
+ it('muestra un icono de carpeta (trailing folder icon) por defecto', async () => {
69
69
  await el.updateComplete;
70
70
  const trailing = el.shadowRoot?.querySelector('.trailing-icon');
71
71
  expect(trailing).toBeTruthy();
72
72
  });
73
73
 
74
- it('uses button-label as placeholder when no value', async () => {
74
+ it('usa button-label como placeholder cuando no hay valor', async () => {
75
75
  await el.updateComplete;
76
76
  const text = el.shadowRoot?.querySelector(
77
77
  'input[type="text"]'