@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
@@ -10,125 +10,244 @@ import { customElement, property, state, 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
  import './moni-select-option.js';
16
17
 
17
18
  /**
18
- * Internal representation of a single option within the select dropdown.
19
+ * Representación interna de una opción individual dentro del menú desplegable del select.
19
20
  *
20
21
  * @internal
21
22
  */
22
23
  interface OptionNode {
23
- /** Discriminant property that identifies this node as an option. */
24
+ /** Propiedad discriminante que identifica este nodo como una opción. */
24
25
  type: 'option';
25
- /** The value submitted when this option is selected. */
26
+ /** El valor enviado cuando se selecciona esta opción. */
26
27
  value: string;
27
- /** The display label shown in the dropdown list. */
28
+ /** La etiqueta mostrada en la lista desplegable. */
28
29
  label: string;
29
- /** When `true`, the option is non-interactive and grayed out. */
30
+ /** Cuando es `true`, la opción no es interactiva y aparece atenuada (gris). */
30
31
  disabled?: boolean;
31
- /** Reference to the original slotted `<moni-select-option>` element, if any. */
32
+ /** Referencia al elemento original `<moni-select-option>` de la ranura (slot), si lo hay. */
32
33
  element?: HTMLElement;
33
- /** Optional group name for organizing options under a `<moni-select-group>`. */
34
+ /** Nombre de grupo opcional para organizar opciones bajo un `<moni-select-group>`. */
34
35
  group?: string;
35
36
  }
36
37
 
37
38
  /**
38
- * Internal representation of an option group within the select dropdown.
39
+ * Representación interna de un grupo de opciones dentro del menú desplegable del select.
39
40
  *
40
41
  * @internal
41
42
  */
42
43
  interface GroupNode {
43
- /** Discriminant property that identifies this node as a group. */
44
+ /** Propiedad discriminante que identifica este nodo como un grupo. */
44
45
  type: 'group';
45
- /** The group header label displayed above the group's options. */
46
+ /** La etiqueta de encabezado del grupo mostrada sobre las opciones del grupo. */
46
47
  label: string;
47
- /** The options nested within this group. */
48
+ /** Las opciones anidadas dentro de este grupo. */
48
49
  children: DropdownNode[];
49
50
  }
50
51
 
51
- /** Union type for any node in the dropdown tree. @internal */
52
+ /** Tipo de unión para cualquier nodo en el árbol del menú desplegable. @internal */
52
53
  type DropdownNode = OptionNode | GroupNode;
53
54
 
54
55
  /**
55
- * Material Design 3 Select (Dropdown) component.
56
+ * Componente Material Design 3 Select (Menú Desplegable).
56
57
  *
57
- * A fully-featured custom select dropdown with searchability, option groups,
58
- * keyboard navigation, animation, and optional mobile drawer/sheet mode.
59
- * Replaces the native `<select>` element with a fully styled, accessible
60
- * M3-compliant alternative.
58
+ * Un menú desplegable select personalizado completo con capacidad de búsqueda, grupos de opciones,
59
+ * navegación por teclado, animación y un modo de cajón/hoja (drawer/sheet) móvil opcional.
60
+ * Reemplaza el elemento nativo `<select>` con una alternativa completamente estilizada y accesible
61
+ * que cumple con M3.
61
62
  *
62
- * **M3 spec reference:** `m3-docs/components/menus/specs.md` (dropdown menus)
63
+ * **Referencia a la especificación M3:** `m3-docs/components/menus/specs.md` (menús desplegables)
63
64
  *
64
- * **Feature summary:**
65
- * - Filled and outlined variants matching the M3 text field styles.
66
- * - Floating label with the standard field-styles floating label animation.
67
- * - Searchable mode: `searchable` attribute adds an inline filter input.
68
- * - Option groups: slot `<moni-select-group>` elements for hierarchical options.
69
- * - Mobile drawer: `drawer` attribute opens options in a `<moni-bottom-sheet>`
70
- * instead of a dropdown popup, ideal for touch UIs.
71
- * - Keyboard navigation: Arrow keys, Enter, Escape, and Tab per ARIA combobox.
72
- * - Loading state: `loading` shows an indeterminate circular progress.
73
- * - Multi-value support: `multiple` enables multiple selection.
65
+ * **Resumen de características:**
66
+ * - Variantes Filled y Outlined que coinciden con los estilos de campo de texto de M3.
67
+ * - Etiqueta flotante (floating label) con la animación estándar de etiqueta flotante de estilos de campo.
68
+ * - Modo de búsqueda: el atributo `searchable` agrega un campo de entrada de filtro en línea.
69
+ * - Grupos de opciones: elementos `<moni-select-group>` en el slot para opciones jerárquicas.
70
+ * - Cajón móvil: el atributo `drawer` abre las opciones en un `<moni-bottom-sheet>`
71
+ * en lugar de un popup desplegable, ideal para interfaces táctiles (touch UIs).
72
+ * - Navegación por teclado: Teclas de flecha, Enter, Escape y Tab según combobox de ARIA.
73
+ * - Estado de carga: `loading` muestra un progreso circular indeterminado.
74
+ * - Soporte multi-valor: `multiple` permite selección múltiple.
74
75
  *
75
- * **Option sources:**
76
- * Options can be provided in two ways:
77
- * 1. **Slotted `<moni-select-option>` elements** (default, recommended for SSR).
78
- * 2. **`options` property** a `DropdownNode[]` array for fully programmatic control.
76
+ * **Fuentes de opciones:**
77
+ * Las opciones se pueden proporcionar de dos maneras:
78
+ * 1. **Elementos `<moni-select-option>` en la ranura (slot)** (por defecto, recomendado para SSR).
79
+ * 2. **Propiedad `options`**un array de `DropdownNode[]` para un control completamente programático.
79
80
  *
80
- * **Value binding:**
81
- * The `value` property holds the currently selected option's value string.
82
- * For multiple selection, `values` holds `string[]`. On change, a composed
83
- * `'change'` event is fired.
81
+ * **Vinculación de valores (Value binding):**
82
+ * La propiedad `value` contiene la cadena de valor de la opción actualmente seleccionada.
83
+ * Para selección múltiple, `values` contiene `string[]`. Al cambiar, se dispara un
84
+ * evento compuesto `'moni-change'`.
84
85
  *
85
- * @fires change - Bubbles and is composed. Fired when the selected value changes.
86
- * Read `element.value` (or `element.values` for `multiple`).
86
+ * @fires moni-change - Burbujea y está compuesto. Se dispara cuando el valor seleccionado cambia.
87
+ * Lee `element.value` (o `element.values` para `multiple`).
87
88
  *
88
89
  * @example
89
90
  * ```html
90
- * <moni-select label="Country" name="country" variant="outlined">
91
- * <moni-select-option value="us">United States</moni-select-option>
92
- * <moni-select-option value="gb">United Kingdom</moni-select-option>
93
- * <moni-select-option value="de">Germany</moni-select-option>
91
+ * <moni-select label="País" name="country" variant="outlined">
92
+ * <moni-select-option value="us">Estados Unidos</moni-select-option>
93
+ * <moni-select-option value="gb">Reino Unido</moni-select-option>
94
+ * <moni-select-option value="de">Alemania</moni-select-option>
94
95
  * </moni-select>
95
96
  *
96
- * <!-- Searchable select -->
97
- * <moni-select label="Language" searchable>
97
+ * <!-- Select con búsqueda -->
98
+ * <moni-select label="Idioma" searchable>
98
99
  * <moni-select-option value="ts">TypeScript</moni-select-option>
99
100
  * <moni-select-option value="py">Python</moni-select-option>
100
101
  * </moni-select>
101
102
  * ```
102
103
  *
103
- * @slot default - `<moni-select-option>` or `<moni-select-group>` children.
104
+ * @slot default - Hijos `<moni-select-option>` o `<moni-select-group>`.
104
105
  *
105
- * @csspart field - The outer `.field` div container.
106
- * @csspart dropdown - The floating option list container.
106
+ * @csspart field - El contenedor div exterior `.field`.
107
+ * @csspart dropdown - El contenedor flotante de la lista de opciones.
107
108
  */
108
109
  @customElement('moni-select')
109
110
  export class MoniSelect extends MoniElement {
111
+ static formAssociated = true;
112
+ private _internals: ElementInternals;
113
+
114
+ constructor() {
115
+ super();
116
+ this._internals = this.attachInternals();
117
+ }
118
+
119
+ /**
120
+ * Nombre del campo select, usado para el envío del formulario.
121
+ * @type {string}
122
+ */
110
123
  @property({ reflect: true }) name = '';
124
+
125
+ /**
126
+ * Texto de la etiqueta flotante.
127
+ * @type {string}
128
+ */
111
129
  @property({ reflect: true }) label = '';
130
+
131
+ /**
132
+ * Variante visual del campo select.
133
+ * @type {'filled' | 'outlined'}
134
+ * @default 'filled'
135
+ */
112
136
  @property({ reflect: true }) variant: 'filled' | 'outlined' = 'filled';
137
+
138
+ /**
139
+ * Define las dimensiones del campo select.
140
+ * @type {'small' | 'medium' | 'large' | 'extra'}
141
+ * @default 'medium'
142
+ */
113
143
  @property({ reflect: true })
114
144
  size: 'small' | 'medium' | 'large' | 'extra' = 'medium';
145
+
146
+ /**
147
+ * Forma (radio de borde) del campo.
148
+ * @type {'round' | 'square' | 'no-round'}
149
+ * @default 'no-round'
150
+ */
115
151
  @property({ reflect: true })
116
152
  shape: 'round' | 'square' | 'no-round' = 'no-round';
153
+
154
+ /**
155
+ * Deshabilita el campo select.
156
+ * @type {boolean}
157
+ */
117
158
  @property({ type: Boolean, reflect: true }) disabled = false;
159
+
160
+ /**
161
+ * Si es verdadero, muestra un indicador de carga (progreso lineal).
162
+ * @type {boolean}
163
+ */
118
164
  @property({ type: Boolean, reflect: true }) loading = false;
165
+
166
+ /**
167
+ * Texto de ayuda mostrado debajo del campo.
168
+ * @type {string}
169
+ */
119
170
  @property({ reflect: true }) helper = '';
171
+
172
+ /**
173
+ * Texto de error mostrado debajo del campo cuando `error` es true.
174
+ * Reemplaza al texto de ayuda.
175
+ * @type {string}
176
+ */
120
177
  @property({ reflect: true, attribute: 'error-text' }) errorText = '';
178
+
179
+ /**
180
+ * Si es verdadero, establece el campo en un estado de error.
181
+ * @type {boolean}
182
+ */
121
183
  @property({ type: Boolean, reflect: true }) error = false;
184
+
185
+ /**
186
+ * El valor actual del select.
187
+ * @type {string}
188
+ */
122
189
  @property({ reflect: true }) value = '';
190
+
191
+ /**
192
+ * Nombre del icono principal (leading icon, Material Symbols).
193
+ * @type {string}
194
+ */
123
195
  @property({ reflect: true }) icon = '';
196
+
197
+ /**
198
+ * Nombre del icono final (trailing icon, Material Symbols) que indica el estado del menú desplegable.
199
+ * @type {string}
200
+ * @default 'arrow_drop_down'
201
+ */
124
202
  @property({ reflect: true, attribute: 'trailing-icon' }) trailingIcon =
125
203
  'arrow_drop_down';
204
+
205
+ /**
206
+ * Habilita un campo de búsqueda en la parte superior del menú desplegable para filtrar opciones.
207
+ * @type {boolean}
208
+ */
126
209
  @property({ type: Boolean, reflect: true }) searchable = false;
210
+
211
+ /**
212
+ * Muestra un botón de limpieza cuando se selecciona un valor para restablecer fácilmente el campo.
213
+ * @type {boolean}
214
+ */
127
215
  @property({ type: Boolean, reflect: true }) clearable = false;
216
+
217
+ /**
218
+ * Renderiza las opciones como una hoja inferior (bottom sheet, ideal para dispositivos móviles) en lugar de un menú desplegable.
219
+ * @type {boolean}
220
+ */
128
221
  @property({ type: Boolean, reflect: true }) sheet = false;
222
+
223
+ /**
224
+ * Texto de marcador de posición (placeholder) mostrado cuando no hay ningún valor seleccionado.
225
+ * @type {string}
226
+ */
129
227
  @property({ reflect: true }) placeholder = '';
228
+
229
+ /**
230
+ * Estrategia de posicionamiento para el menú desplegable.
231
+ * @type {'absolute' | 'fixed'}
232
+ * @default 'absolute'
233
+ */
130
234
  @property({ reflect: true }) positioning: 'absolute' | 'fixed' = 'absolute';
235
+
236
+ /**
237
+ * Ubicación preferida del menú desplegable con respecto al disparador (trigger).
238
+ * @type {'top' | 'bottom' | 'left' | 'right' | 'auto'}
239
+ * @default 'auto'
240
+ */
131
241
  @property({ reflect: true }) placement: 'top' | 'bottom' | 'left' | 'right' | 'auto' = 'auto';
242
+
243
+ /**
244
+ * Restricción de ancho del menú desplegable.
245
+ * - `'trigger'`: Coincide con el ancho del campo de entrada.
246
+ * - `'auto'`: Coincide con el ancho del contenido del menú desplegable.
247
+ * - O cualquier valor CSS de ancho válido (ej. '200px').
248
+ * @type {string}
249
+ * @default 'trigger'
250
+ */
132
251
  @property({ reflect: true, attribute: 'dropdown-width' }) dropdownWidth = 'trigger';
133
252
 
134
253
  @state() private _open = false;
@@ -468,6 +587,12 @@ export class MoniSelect extends MoniElement {
468
587
  `
469
588
  ];
470
589
 
590
+ /**
591
+ * Hook del ciclo de vida (Lit).
592
+ * Vincula event listeners globales necesarios para la lógica del dropdown.
593
+ * Se usa `capture: true` en el evento de scroll para poder reaccionar
594
+ * al desplazamiento de cualquier contenedor padre y reposicionar el menú a tiempo.
595
+ */
471
596
  override connectedCallback() {
472
597
  super.connectedCallback();
473
598
  document.addEventListener('click', this._handleOutsideClick);
@@ -475,6 +600,11 @@ export class MoniSelect extends MoniElement {
475
600
  window.addEventListener('resize', this._handleResize);
476
601
  }
477
602
 
603
+ /**
604
+ * Hook del ciclo de vida (Lit).
605
+ * Desmonta rigurosamente todos los listeners globales para prevenir memory leaks
606
+ * o llamadas accidentales cuando el select ya fue destruido del DOM.
607
+ */
478
608
  override disconnectedCallback() {
479
609
  super.disconnectedCallback();
480
610
  document.removeEventListener('click', this._handleOutsideClick);
@@ -482,8 +612,17 @@ export class MoniSelect extends MoniElement {
482
612
  window.removeEventListener('resize', this._handleResize);
483
613
  }
484
614
 
615
+ /**
616
+ * Hook de actualización reactiva (Lit).
617
+ * Verifica si alguna propiedad relacionada con el tamaño del contenido (como la búsqueda,
618
+ * la ruta de anidación o el estado abierto) ha cambiado, para forzar un recálculo
619
+ * de la altura animada del contenedor `drilldown-wrapper`.
620
+ */
485
621
  override updated(changedProperties: Map<string | number | symbol, unknown>) {
486
622
  super.updated(changedProperties);
623
+ if (changedProperties.has('value')) {
624
+ this._internals?.setFormValue?.(this.value);
625
+ }
487
626
  if (
488
627
  changedProperties.has('_open') ||
489
628
  changedProperties.has('_drilldownPath') ||
@@ -495,6 +634,12 @@ export class MoniSelect extends MoniElement {
495
634
  }
496
635
  }
497
636
 
637
+ /**
638
+ * Lógica de animación de altura (Height Animation).
639
+ * Cuando se navega entre categorías anidadas (`_drilldownPath`), las listas hijas pueden
640
+ * tener diferentes tamaños. Este método lee el `scrollHeight` del panel activo y lo aplica
641
+ * imperativamente al `wrapper` para disparar una transición CSS suave en la altura del dropdown.
642
+ */
498
643
  private _updateWrapperHeight() {
499
644
  const wrapper = this.shadowRoot?.querySelector('.drilldown-wrapper') as HTMLElement;
500
645
  if (!wrapper) return;
@@ -515,12 +660,28 @@ export class MoniSelect extends MoniElement {
515
660
  });
516
661
  }
517
662
 
663
+ /**
664
+ * Cierra el Dropdown cuando el usuario hace clic fuera de las dimensiones del componente.
665
+ * Utiliza `e.composedPath()` para poder atravesar barreras de Shadow DOM si el select
666
+ * está dentro de un contenedor web-component externo.
667
+ */
518
668
  private _handleOutsideClick = (e: MouseEvent) => {
519
669
  if (this._open && !e.composedPath().includes(this)) {
520
670
  this._closeDropdown();
521
671
  }
522
672
  };
523
673
 
674
+ /**
675
+ * Motor de Cálculo de Colisiones de la Interfaz (Collision Detection).
676
+ *
677
+ * @logic
678
+ * 1. Calcula las coordenadas espaciales (`getBoundingClientRect`) del select base.
679
+ * 2. Compara el espacio disponible (`spaceAbove`, `spaceBelow`, `spaceRight`, `spaceLeft`)
680
+ * respecto al viewport (`window.innerHeight`, `window.innerWidth`).
681
+ * 3. Si el dropdown no cabe en la posición preferida (`placement`), revierte lógicamente
682
+ * la posición (ej: si `bottom` desborda, lo abre hacia `top`).
683
+ * 4. Actualiza `_actualPlacement` con la dirección segura calculada.
684
+ */
524
685
  private _determineActualPlacement() {
525
686
  if (this.sheet) {
526
687
  this._actualPlacement = 'bottom';
@@ -569,6 +730,11 @@ export class MoniSelect extends MoniElement {
569
730
  }
570
731
  }
571
732
 
733
+ /**
734
+ * Handler de Scroll global.
735
+ * Fuerza un recálculo de la posición y dimensiones del dropdown si el usuario
736
+ * hace scroll en la página mientras el select permanece abierto.
737
+ */
572
738
  private _handleScroll = () => {
573
739
  if (this._open) {
574
740
  this._determineActualPlacement();
@@ -576,6 +742,11 @@ export class MoniSelect extends MoniElement {
576
742
  }
577
743
  };
578
744
 
745
+ /**
746
+ * Handler de Resize global.
747
+ * Asegura que el popover del select no quede fuera del viewport cuando
748
+ * se cambia el tamaño de la ventana o se rota la pantalla en dispositivos móviles.
749
+ */
579
750
  private _handleResize = () => {
580
751
  if (this._open) {
581
752
  this._determineActualPlacement();
@@ -583,6 +754,12 @@ export class MoniSelect extends MoniElement {
583
754
  }
584
755
  };
585
756
 
757
+ /**
758
+ * Aplica las coordenadas y dimensiones absolutas o fijas al menú desplegable.
759
+ * Este método es fundamental cuando `positioning === 'fixed'`, ya que el dropdown
760
+ * se saca del flujo normal del documento para evitar `overflow: hidden` de los padres.
761
+ * Mapea el `width` para coincidir opcionalmente con el gatillo (`dropdownWidth="trigger"`).
762
+ */
586
763
  private _updateMenuPosition() {
587
764
  if (!this._open || this.sheet) return;
588
765
 
@@ -639,6 +816,12 @@ export class MoniSelect extends MoniElement {
639
816
  }
640
817
  }
641
818
 
819
+ /**
820
+ * Devuelve un string de estilos inline para el menú desplegable.
821
+ * Si la posición es `fixed` (calculada dinámicamente para evadir overflow),
822
+ * retorna el bloque de coordenadas generado por `_updateMenuPosition`.
823
+ * Si no, asigna anchos basados en la propiedad `dropdownWidth` (auto, trigger, custom).
824
+ */
642
825
  private _getMenuStyle() {
643
826
  if (this.positioning === 'fixed') {
644
827
  return this._menuStyle;
@@ -652,6 +835,18 @@ export class MoniSelect extends MoniElement {
652
835
  return '';
653
836
  }
654
837
 
838
+ /**
839
+ * Parseador del Shadow DOM (Slot Change Handler).
840
+ *
841
+ * @logic
842
+ * 1. Cuando los hijos proyectados cambian (ej. se agregan nuevos `<option>`),
843
+ * lee iterativamente todos los nodos del Light DOM.
844
+ * 2. Si encuentra `<optgroup>` o `<moni-select-option group="A/B">`, construye dinámicamente
845
+ * un árbol estructural de `GroupNode` anidados.
846
+ * 3. Si encuentra opciones, las inyecta en el grupo correspondiente del árbol.
847
+ * 4. Almacena el árbol resultante en `_parsedOptions` para que el componente
848
+ * pueda renderizar sus propios submenús nativos independientemente del markup inicial.
849
+ */
655
850
  private _onSlotChange() {
656
851
  if (!this._slot) return;
657
852
  const assigned = this._slot.assignedElements({ flatten: true });
@@ -705,6 +900,10 @@ export class MoniSelect extends MoniElement {
705
900
  this.requestUpdate();
706
901
  }
707
902
 
903
+ /**
904
+ * Alterna el estado de apertura/cierre del dropdown, previniendo
905
+ * la acción si el componente está deshabilitado.
906
+ */
708
907
  private _toggleDropdown(e?: Event) {
709
908
  if (e) {
710
909
  e.stopPropagation();
@@ -717,6 +916,11 @@ export class MoniSelect extends MoniElement {
717
916
  }
718
917
  }
719
918
 
919
+ /**
920
+ * Manejador de clics en el campo de texto (Trigger).
921
+ * Si el componente es `searchable` (permite escribir para buscar), un clic
922
+ * sólo abrirá el menú (no lo cerrará si ya estaba abierto, para permitir al usuario seguir escribiendo).
923
+ */
720
924
  private _onInputClick(e: MouseEvent) {
721
925
  if (this.disabled) return;
722
926
  if (this.searchable && !this.sheet) {
@@ -728,30 +932,43 @@ export class MoniSelect extends MoniElement {
728
932
  }
729
933
  }
730
934
 
935
+ /**
936
+ * Despliega el menú del select y coordina la accesibilidad, posicionamiento y foco inicial.
937
+ */
731
938
  private _openDropdown() {
732
939
  this._open = true;
733
940
  this._activeIndex = -1;
734
941
  this._drilldownPath = [];
735
942
 
943
+ // Calculamos la dirección del menú emergente en base al espacio disponible en la pantalla
736
944
  this._determineActualPlacement();
737
945
 
738
- // Measure viewport to determine if we should fall back to inline categories
946
+ // Medimos dinámicamente si hay espacio suficiente para renderizar submenús laterales (Desktop).
947
+ // Si es móvil (<600px) o no hay 160px de espacio libre a los lados, forzamos un diseño plano ("inline categories")
948
+ // para evitar que los submenús se salgan del viewport.
739
949
  const rect = this.getBoundingClientRect();
740
950
  const spaceOnRight = window.innerWidth - rect.right;
741
951
  const spaceOnLeft = rect.left;
742
952
  this._useInlineCategories = window.innerWidth < 600 || (spaceOnRight < 160 && spaceOnLeft < 160);
743
953
 
954
+ // Si el menú tiene position="fixed", necesitamos computar y aplicar las coordenadas absolutas en pixels
744
955
  if (this.positioning === 'fixed') {
745
956
  this._updateMenuPosition();
746
957
  }
958
+
959
+ // Lógica especial si el componente está configurado como "searchable" (Input filtrable)
747
960
  if (this.searchable) {
748
961
  const selectedOpt = this._findOptionByValue(this.value);
749
962
  this._searchQuery = selectedOpt ? selectedOpt.label : '';
963
+
964
+ // Esperamos un tick para que el input o bottom-sheet sea renderizado antes de solicitar el foco
750
965
  setTimeout(() => {
751
966
  if (this._input && !this.sheet) {
967
+ // Foco en el input principal (Desktop) y seleccionamos todo el texto para fácil reemplazo
752
968
  this._input.focus();
753
969
  this._input.select();
754
970
  } else if (this.sheet) {
971
+ // Foco en la caja de búsqueda inyectada dentro del bottom-sheet (Mobile)
755
972
  const sheetInput = this.shadowRoot?.querySelector('.sheet-search-input') as HTMLInputElement;
756
973
  if (sheetInput) {
757
974
  sheetInput.focus();
@@ -762,6 +979,10 @@ export class MoniSelect extends MoniElement {
762
979
  }
763
980
  }
764
981
 
982
+ /**
983
+ * Cierra el dropdown y purga la consulta de búsqueda (`_searchQuery`)
984
+ * para que la próxima vez que se abra, la lista muestre todas las opciones por defecto.
985
+ */
765
986
  private _closeDropdown() {
766
987
  this._open = false;
767
988
  this._searchQuery = '';
@@ -772,6 +993,15 @@ export class MoniSelect extends MoniElement {
772
993
  return this._getFlatOptions(this._parsedOptions).find(opt => opt.value === value);
773
994
  }
774
995
 
996
+ /**
997
+ * Evento de input (tecleo) en la barra de búsqueda.
998
+ *
999
+ * @logic
1000
+ * 1. Actualiza `_searchQuery` y fuerza la apertura del dropdown.
1001
+ * 2. Si es `clearable` y el usuario borró todo, emite eventos vacíos.
1002
+ * 3. Busca coincidencias exactas en el texto. Si el usuario teclea exactamente el
1003
+ * label de una opción existente, la selecciona automáticamente (auto-completado nativo).
1004
+ */
775
1005
  private _onSearchInput(e: Event) {
776
1006
  const query = (e.target as HTMLInputElement).value;
777
1007
  this._searchQuery = query;
@@ -780,8 +1010,7 @@ export class MoniSelect extends MoniElement {
780
1010
 
781
1011
  if (this.clearable && query.trim() === '') {
782
1012
  this.value = '';
783
- this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
784
- this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
1013
+ emitMoniEvent(this, 'moni-change', { detail: { value: this.value, originalEvent: e } });
785
1014
  return;
786
1015
  }
787
1016
 
@@ -794,6 +1023,11 @@ export class MoniSelect extends MoniElement {
794
1023
  }
795
1024
  }
796
1025
 
1026
+ /**
1027
+ * Confirma la selección de una opción específica.
1028
+ * Actualiza el valor (estado reactivo), cierra el dropdown y despacha los eventos
1029
+ * estándar HTML `input` y `change` para integrarse fluidamente con formularios nativos o frameworks.
1030
+ */
797
1031
  private _selectOption(option: OptionNode) {
798
1032
  if (option.disabled) return;
799
1033
  this.value = option.value;
@@ -801,8 +1035,15 @@ export class MoniSelect extends MoniElement {
801
1035
 
802
1036
  this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
803
1037
  this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
1038
+ emitMoniEvent(this, 'moni-change', { detail: { value: this.value } });
804
1039
  }
805
1040
 
1041
+ /**
1042
+ * Maneja el Hover sobre categorías en modo Desktop (Submenús emergentes).
1043
+ * Detecta dinámicamente si el submenú se saldrá del lado derecho de la pantalla.
1044
+ * Si es así, altera las clases de CSS (`open-left` vs `open-right`) para que
1045
+ * el menú flote en la dirección opuesta, evitando desbordamientos de viewport (Overflow prevention).
1046
+ */
806
1047
  private _onSubmenuMouseEnter(e: MouseEvent) {
807
1048
  const headerEl = e.currentTarget as HTMLElement;
808
1049
  const submenu = headerEl.querySelector(':scope > .submenu') as HTMLElement;
@@ -822,41 +1063,56 @@ export class MoniSelect extends MoniElement {
822
1063
  }
823
1064
  }
824
1065
 
1066
+ /**
1067
+ * Gestiona la navegación completa por teclado siguiendo el patrón WAI-ARIA Combobox.
1068
+ * Soporta flechas direccionales, selección (Enter), y cierre (Escape).
1069
+ */
825
1070
  private _onKeyDown(e: KeyboardEvent) {
826
1071
  if (this.disabled) return;
827
1072
 
1073
+ // Obtenemos solo las opciones que están visualmente disponibles en la jerarquía actual
828
1074
  const visible = this._getVisibleOptions();
829
1075
 
830
1076
  if (e.key === 'ArrowDown') {
831
1077
  e.preventDefault();
832
1078
  if (!this._open) {
1079
+ // Si está cerrado, ArrowDown lo abre (comportamiento estándar nativo)
833
1080
  this._openDropdown();
834
1081
  } else if (visible.length > 0) {
1082
+ // Navegamos circularmente hacia abajo
835
1083
  this._activeIndex = (this._activeIndex + 1) % visible.length;
836
1084
  this._scrollToActive();
837
1085
  }
838
1086
  } else if (e.key === 'ArrowUp') {
839
1087
  e.preventDefault();
840
1088
  if (this._open && visible.length > 0) {
1089
+ // Navegamos circularmente hacia arriba (+visible.length evita valores negativos en JS)
841
1090
  this._activeIndex = (this._activeIndex - 1 + visible.length) % visible.length;
842
1091
  this._scrollToActive();
843
1092
  }
844
1093
  } else if (e.key === 'Enter') {
845
1094
  e.preventDefault();
846
1095
  if (this._open) {
1096
+ // Si está abierto y hay una opción resaltada, simulamos el click para seleccionarla
847
1097
  if (this._activeIndex >= 0 && this._activeIndex < visible.length) {
848
1098
  this._selectOption(visible[this._activeIndex]);
849
1099
  }
850
1100
  } else {
1101
+ // Enter cuando está cerrado simplemente abre el menú
851
1102
  this._openDropdown();
852
1103
  }
853
1104
  } else if (e.key === 'Escape') {
854
1105
  e.preventDefault();
1106
+ // Escapa el menú sin hacer ninguna selección
855
1107
  this._closeDropdown();
856
1108
  }
857
1109
  }
858
1110
 
1111
+ /**
1112
+ * Asegura que el elemento resaltado por teclado sea visible dentro del contenedor scrolleable.
1113
+ */
859
1114
  private _scrollToActive() {
1115
+ // Se requiere setTimeout para darle tiempo a Lit a renderizar la clase 'active-nav'
860
1116
  setTimeout(() => {
861
1117
  const activeEl = this.shadowRoot?.querySelector('.option-item.active-nav');
862
1118
  if (activeEl) {
@@ -865,6 +1121,10 @@ export class MoniSelect extends MoniElement {
865
1121
  }, 0);
866
1122
  }
867
1123
 
1124
+ /**
1125
+ * Aplana el árbol jerárquico recursivo de <moni-select-group> a un array unidimensional de opciones puras.
1126
+ * Esencial para que la búsqueda por texto funcione globalmente ignorando las categorías.
1127
+ */
868
1128
  private _getFlatOptions(nodes: DropdownNode[]): OptionNode[] {
869
1129
  const flat: OptionNode[] = [];
870
1130
  const traverse = (n: DropdownNode) => {
@@ -902,6 +1162,13 @@ export class MoniSelect extends MoniElement {
902
1162
  return filtered;
903
1163
  }
904
1164
 
1165
+ /**
1166
+ * Motor de Renderizado: Lista Raíz (Modo Móvil / Drilldown).
1167
+ *
1168
+ * Renderiza las opciones iniciales y los grupos base de la categoría actual (`_drilldownPath`).
1169
+ * Asigna clases dinámicas (`selected`, `active-nav`) para la retroalimentación visual
1170
+ * del teclado y el valor actualmente seleccionado.
1171
+ */
905
1172
  private _renderRootList() {
906
1173
  let visibleIndex = 0;
907
1174
  return this._parsedOptions.map(node => {
@@ -928,6 +1195,13 @@ export class MoniSelect extends MoniElement {
928
1195
  });
929
1196
  }
930
1197
 
1198
+ /**
1199
+ * Motor de Renderizado: Subcategorías (Modo Móvil / Drilldown).
1200
+ *
1201
+ * A diferencia de desktop (donde los submenús flotan a los lados), en dispositivos
1202
+ * móviles o espacios constreñidos, el menú transiciona in-situ. Este método renderiza
1203
+ * el header del grupo activo, un botón de "Regresar" (Arrow Back) y las opciones hijas.
1204
+ */
931
1205
  private _renderSubcategoryList() {
932
1206
  if (this._drilldownPath.length === 0) return nothing;
933
1207
  const activeGroup = this._drilldownPath[this._drilldownPath.length - 1];
@@ -995,6 +1269,14 @@ export class MoniSelect extends MoniElement {
995
1269
  `;
996
1270
  }
997
1271
 
1272
+ /**
1273
+ * Orquestador principal de renderizado de la lista de opciones.
1274
+ *
1275
+ * Si el usuario introdujo texto (`_searchQuery`), renderiza los resultados filtrados.
1276
+ * Si la lista original está vacía, muestra un mensaje amigable (Empty State).
1277
+ * Determina condicionalmente si usar el modo plano/Drilldown (Móviles) o
1278
+ * el modo Desktop con submenús flotantes (`_renderDesktopNode`).
1279
+ */
998
1280
  private _renderOptionsList(filtered: OptionNode[]) {
999
1281
  if (this._parsedOptions.length === 0) {
1000
1282
  return html`<li class="no-options">No options found</li>`;
@@ -1049,6 +1331,35 @@ export class MoniSelect extends MoniElement {
1049
1331
  return this._parsedOptions.map(node => this._renderDesktopNode(node, flatIndexRef));
1050
1332
  }
1051
1333
 
1334
+ /**
1335
+ * Ensambla el Shadow DOM del campo select, coordinando dos modos de interfaz de usuario distintos.
1336
+ *
1337
+ * **Resolución del valor a mostrar (Display value):**
1338
+ * El contenido de texto visible en el input está determinado por prioridad:
1339
+ * 1. Mientras el menú desplegable está abierto Y `searchable=true` Y NO es modo sheet:
1340
+ * muestra `_searchQuery` (el texto del filtro en vivo que el usuario está escribiendo).
1341
+ * 2. De lo contrario: muestra `selectedOpt.label` (la etiqueta de la opción actualmente
1342
+ * seleccionada) o vuelve a la cadena vacía.
1343
+ *
1344
+ * **Dos rutas de renderizado:**
1345
+ * - **Modo Sheet (`sheet=true`):** Renderiza un `<moni-bottom-sheet>` como un
1346
+ * cajón (drawer) superpuesto a pantalla completa, ideal para IU táctiles móviles. La lista de opciones
1347
+ * se renderiza dentro de la hoja en lugar del menú desplegable.
1348
+ * - **Modo desplegable (por defecto):** Renderiza un `.dropdown-menu` flotante que
1349
+ * se abre debajo o arriba del campo. La cadena `_menuStyle` (calculada por
1350
+ * `_computeMenuStyle()`) inyecta los `top`, `left`, `width`, y
1351
+ * sobreescrituras `position: fixed` correctas para contenedores con scroll (scroll-contained) y parents con overflow cortado (overflow-clipping).
1352
+ *
1353
+ * **Patrón combobox ARIA:**
1354
+ * El input de texto lleva `role="combobox"`, `aria-haspopup="listbox"` y
1355
+ * `aria-expanded` para satisfacer los requisitos del combobox WCAG 2.1 para lectores de pantalla.
1356
+ * `aria-activedescendant` se establece al ID de la opción actualmente enfocada por teclado
1357
+ * para habilitar el movimiento del cursor virtual sin gestión del foco.
1358
+ *
1359
+ * **Composición de `fieldClasses`:**
1360
+ * Sigue la convención de nomenclatura de clases de estilos de campo de BeerCSS. `prefix` se agrega
1361
+ * cuando `icon` está establecido (para desplazar el relleno -padding- inline-start del input de texto para el icono).
1362
+ */
1052
1363
  override render() {
1053
1364
  const hasLeading = Boolean(this.icon);
1054
1365
  const filtered = this._getFilteredOptions();