@moni-labs/moni-ui 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +5181 -1043
- package/dist/components/_base/moni-element.d.ts +16 -9
- package/dist/components/_base/moni-element.d.ts.map +1 -1
- package/dist/components/_base/moni-element.js +63 -9
- package/dist/components/index.js +2 -2
- package/dist/components/loading-shapes.d.ts +19 -0
- package/dist/components/loading-shapes.d.ts.map +1 -1
- package/dist/components/loading-shapes.js +19 -0
- package/dist/components/moni-app-bar.d.ts +72 -62
- package/dist/components/moni-app-bar.d.ts.map +1 -1
- package/dist/components/moni-app-bar.js +72 -62
- package/dist/components/moni-badge.d.ts +64 -65
- package/dist/components/moni-badge.d.ts.map +1 -1
- package/dist/components/moni-badge.js +64 -66
- package/dist/components/moni-bottom-sheet.d.ts +129 -61
- package/dist/components/moni-bottom-sheet.d.ts.map +1 -1
- package/dist/components/moni-bottom-sheet.js +217 -131
- package/dist/components/moni-button-group.d.ts +107 -29
- package/dist/components/moni-button-group.d.ts.map +1 -1
- package/dist/components/moni-button-group.js +107 -29
- package/dist/components/moni-button-segment.d.ts +53 -12
- package/dist/components/moni-button-segment.d.ts.map +1 -1
- package/dist/components/moni-button-segment.js +53 -12
- package/dist/components/moni-button.d.ts +116 -38
- package/dist/components/moni-button.d.ts.map +1 -1
- package/dist/components/moni-button.js +128 -38
- package/dist/components/moni-card.d.ts +68 -50
- package/dist/components/moni-card.d.ts.map +1 -1
- package/dist/components/moni-card.js +68 -50
- package/dist/components/moni-carousel.d.ts +471 -53
- package/dist/components/moni-carousel.d.ts.map +1 -1
- package/dist/components/moni-carousel.js +862 -264
- package/dist/components/moni-checkbox.d.ts +49 -46
- package/dist/components/moni-checkbox.d.ts.map +1 -1
- package/dist/components/moni-checkbox.js +55 -48
- package/dist/components/moni-chip.d.ts +100 -33
- package/dist/components/moni-chip.d.ts.map +1 -1
- package/dist/components/moni-chip.js +104 -34
- package/dist/components/moni-color-field.d.ts +99 -24
- package/dist/components/moni-color-field.d.ts.map +1 -1
- package/dist/components/moni-color-field.js +99 -24
- package/dist/components/moni-context-menu.d.ts +72 -26
- package/dist/components/moni-context-menu.d.ts.map +1 -1
- package/dist/components/moni-context-menu.js +76 -30
- package/dist/components/moni-dialog.d.ts +84 -59
- package/dist/components/moni-dialog.d.ts.map +1 -1
- package/dist/components/moni-dialog.js +105 -58
- package/dist/components/moni-divider.d.ts +39 -27
- package/dist/components/moni-divider.d.ts.map +1 -1
- package/dist/components/moni-divider.js +42 -30
- package/dist/components/moni-expansion.d.ts +32 -22
- package/dist/components/moni-expansion.d.ts.map +1 -1
- package/dist/components/moni-expansion.js +32 -22
- package/dist/components/moni-fab-menu.d.ts +61 -23
- package/dist/components/moni-fab-menu.d.ts.map +1 -1
- package/dist/components/moni-fab-menu.js +69 -31
- package/dist/components/moni-fab.d.ts +86 -28
- package/dist/components/moni-fab.d.ts.map +1 -1
- package/dist/components/moni-fab.js +92 -34
- package/dist/components/moni-file-field.d.ts +118 -30
- package/dist/components/moni-file-field.d.ts.map +1 -1
- package/dist/components/moni-file-field.js +118 -30
- package/dist/components/moni-icon.d.ts +40 -35
- package/dist/components/moni-icon.d.ts.map +1 -1
- package/dist/components/moni-icon.js +43 -38
- package/dist/components/moni-list-item.d.ts +46 -33
- package/dist/components/moni-list-item.d.ts.map +1 -1
- package/dist/components/moni-list-item.js +61 -41
- package/dist/components/moni-list.d.ts +30 -20
- package/dist/components/moni-list.d.ts.map +1 -1
- package/dist/components/moni-list.js +36 -21
- package/dist/components/moni-loading-indicator.d.ts +54 -22
- package/dist/components/moni-loading-indicator.d.ts.map +1 -1
- package/dist/components/moni-loading-indicator.js +54 -22
- package/dist/components/moni-menu-item.d.ts +23 -16
- package/dist/components/moni-menu-item.d.ts.map +1 -1
- package/dist/components/moni-menu-item.js +23 -16
- package/dist/components/moni-menu.d.ts +81 -28
- package/dist/components/moni-menu.d.ts.map +1 -1
- package/dist/components/moni-menu.js +81 -30
- package/dist/components/moni-morph-modal.d.ts +141 -0
- package/dist/components/moni-morph-modal.d.ts.map +1 -1
- package/dist/components/moni-morph-modal.js +295 -58
- package/dist/components/moni-nav-item.d.ts +59 -29
- package/dist/components/moni-nav-item.d.ts.map +1 -1
- package/dist/components/moni-nav-item.js +59 -29
- package/dist/components/moni-nav.d.ts +94 -35
- package/dist/components/moni-nav.d.ts.map +1 -1
- package/dist/components/moni-nav.js +94 -35
- package/dist/components/moni-progress.d.ts +99 -55
- package/dist/components/moni-progress.d.ts.map +1 -1
- package/dist/components/moni-progress.js +99 -55
- package/dist/components/moni-radio.d.ts +62 -50
- package/dist/components/moni-radio.d.ts.map +1 -1
- package/dist/components/moni-radio.js +65 -52
- package/dist/components/moni-ripple.d.ts +74 -59
- package/dist/components/moni-ripple.d.ts.map +1 -1
- package/dist/components/moni-ripple.js +74 -59
- package/dist/components/moni-segmented-button.d.ts +91 -16
- package/dist/components/moni-segmented-button.d.ts.map +1 -1
- package/dist/components/moni-segmented-button.js +94 -17
- package/dist/components/moni-select-option.d.ts +30 -23
- package/dist/components/moni-select-option.d.ts.map +1 -1
- package/dist/components/moni-select-option.js +30 -23
- package/dist/components/moni-select.d.ts +300 -35
- package/dist/components/moni-select.d.ts.map +1 -1
- package/dist/components/moni-select.js +323 -39
- package/dist/components/moni-side-sheet.d.ts +94 -32
- package/dist/components/moni-side-sheet.d.ts.map +1 -1
- package/dist/components/moni-side-sheet.js +94 -32
- package/dist/components/moni-slider.d.ts +152 -41
- package/dist/components/moni-slider.d.ts.map +1 -1
- package/dist/components/moni-slider.js +154 -42
- package/dist/components/moni-snackbar.d.ts +53 -43
- package/dist/components/moni-snackbar.d.ts.map +1 -1
- package/dist/components/moni-snackbar.js +53 -43
- package/dist/components/moni-split-button.d.ts +63 -18
- package/dist/components/moni-split-button.d.ts.map +1 -1
- package/dist/components/moni-split-button.js +63 -18
- package/dist/components/moni-step.d.ts +36 -24
- package/dist/components/moni-step.d.ts.map +1 -1
- package/dist/components/moni-step.js +41 -29
- package/dist/components/moni-stepper.d.ts +64 -27
- package/dist/components/moni-stepper.d.ts.map +1 -1
- package/dist/components/moni-stepper.js +72 -34
- package/dist/components/moni-switch.d.ts +57 -46
- package/dist/components/moni-switch.d.ts.map +1 -1
- package/dist/components/moni-switch.js +59 -47
- package/dist/components/moni-tab.d.ts +26 -18
- package/dist/components/moni-tab.d.ts.map +1 -1
- package/dist/components/moni-tab.js +29 -21
- package/dist/components/moni-tabs.d.ts +34 -28
- package/dist/components/moni-tabs.d.ts.map +1 -1
- package/dist/components/moni-tabs.js +34 -28
- package/dist/components/moni-text-field.d.ts +139 -29
- package/dist/components/moni-text-field.d.ts.map +1 -1
- package/dist/components/moni-text-field.js +157 -31
- package/dist/components/moni-textarea.d.ts +127 -29
- package/dist/components/moni-textarea.d.ts.map +1 -1
- package/dist/components/moni-textarea.js +131 -32
- package/dist/components/moni-time-picker.d.ts +114 -24
- package/dist/components/moni-time-picker.d.ts.map +1 -1
- package/dist/components/moni-time-picker.js +127 -33
- package/dist/components/moni-toolbar.d.ts +37 -23
- package/dist/components/moni-toolbar.d.ts.map +1 -1
- package/dist/components/moni-toolbar.js +37 -23
- package/dist/components/moni-tooltip.d.ts +100 -36
- package/dist/components/moni-tooltip.d.ts.map +1 -1
- package/dist/components/moni-tooltip.js +104 -40
- package/dist/components/moni-typography.d.ts +68 -30
- package/dist/components/moni-typography.d.ts.map +1 -1
- package/dist/components/moni-typography.js +70 -32
- package/dist/styles/tokens.css +7 -7
- package/dist/types/events.d.ts +17 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +1 -0
- package/dist/utils/event-emitter.d.ts +13 -0
- package/dist/utils/event-emitter.d.ts.map +1 -0
- package/dist/utils/event-emitter.js +13 -0
- package/package.json +2 -1
- package/src/components/_base/moni-element.ts +73 -9
- package/src/components/index.ts +2 -2
- package/src/components/loading-shapes.ts +19 -0
- package/src/components/moni-app-bar.test.ts +10 -10
- package/src/components/moni-app-bar.ts +72 -62
- package/src/components/moni-badge.ts +64 -66
- package/src/components/moni-bottom-sheet.test.ts +39 -45
- package/src/components/moni-bottom-sheet.ts +222 -131
- package/src/components/moni-button-group.test.ts +14 -14
- package/src/components/moni-button-group.ts +107 -29
- package/src/components/moni-button-segment.ts +53 -12
- package/src/components/moni-button.test.ts +28 -26
- package/src/components/moni-button.ts +139 -39
- package/src/components/moni-card.test.ts +6 -6
- package/src/components/moni-card.ts +70 -50
- package/src/components/moni-carousel.test.ts +12 -12
- package/src/components/moni-carousel.ts +953 -290
- package/src/components/moni-checkbox.test.ts +7 -7
- package/src/components/moni-checkbox.ts +62 -48
- package/src/components/moni-chip.test.ts +17 -17
- package/src/components/moni-chip.ts +112 -34
- package/src/components/moni-color-field.test.ts +5 -5
- package/src/components/moni-color-field.ts +108 -24
- package/src/components/moni-context-menu.test.ts +11 -11
- package/src/components/moni-context-menu.ts +76 -30
- package/src/components/moni-dialog.ts +108 -59
- package/src/components/moni-divider.test.ts +4 -4
- package/src/components/moni-divider.ts +42 -30
- package/src/components/moni-expansion.ts +32 -22
- package/src/components/moni-fab-menu.test.ts +11 -11
- package/src/components/moni-fab-menu.ts +69 -31
- package/src/components/moni-fab.test.ts +16 -16
- package/src/components/moni-fab.ts +100 -34
- package/src/components/moni-file-field.test.ts +8 -8
- package/src/components/moni-file-field.ts +132 -30
- package/src/components/moni-icon.test.ts +11 -11
- package/src/components/moni-icon.ts +43 -38
- package/src/components/moni-list-item.test.ts +10 -10
- package/src/components/moni-list-item.ts +61 -41
- package/src/components/moni-list.ts +37 -21
- package/src/components/moni-loading-indicator.test.ts +3 -3
- package/src/components/moni-loading-indicator.ts +54 -22
- package/src/components/moni-menu-item.ts +23 -16
- package/src/components/moni-menu.test.ts +7 -7
- package/src/components/moni-menu.ts +87 -30
- package/src/components/moni-morph-modal.test.ts +18 -17
- package/src/components/moni-morph-modal.ts +327 -60
- package/src/components/moni-nav-item.ts +59 -29
- package/src/components/moni-nav.test.ts +11 -11
- package/src/components/moni-nav.ts +94 -35
- package/src/components/moni-progress.test.ts +9 -9
- package/src/components/moni-progress.ts +100 -56
- package/src/components/moni-radio.test.ts +8 -8
- package/src/components/moni-radio.ts +71 -51
- package/src/components/moni-ripple.ts +74 -59
- package/src/components/moni-segmented-button.test.ts +4 -4
- package/src/components/moni-segmented-button.ts +94 -17
- package/src/components/moni-select-option.ts +30 -23
- package/src/components/moni-select.test.ts +11 -11
- package/src/components/moni-select.ts +361 -50
- package/src/components/moni-side-sheet.test.ts +7 -7
- package/src/components/moni-side-sheet.ts +94 -32
- package/src/components/moni-slider.test.ts +7 -7
- package/src/components/moni-slider.ts +165 -42
- package/src/components/moni-snackbar.test.ts +8 -8
- package/src/components/moni-snackbar.ts +53 -43
- package/src/components/moni-split-button.ts +63 -18
- package/src/components/moni-step.test.ts +7 -7
- package/src/components/moni-step.ts +41 -29
- package/src/components/moni-stepper.test.ts +7 -7
- package/src/components/moni-stepper.ts +72 -34
- package/src/components/moni-switch.test.ts +12 -12
- package/src/components/moni-switch.ts +66 -47
- package/src/components/moni-tab.test.ts +5 -5
- package/src/components/moni-tab.ts +29 -21
- package/src/components/moni-tabs.ts +34 -28
- package/src/components/moni-text-field.test.ts +13 -13
- package/src/components/moni-text-field.ts +181 -30
- package/src/components/moni-textarea.test.ts +17 -17
- package/src/components/moni-textarea.ts +155 -31
- package/src/components/moni-time-picker.test.ts +5 -5
- package/src/components/moni-time-picker.ts +128 -33
- package/src/components/moni-toolbar.test.ts +8 -8
- package/src/components/moni-toolbar.ts +37 -23
- package/src/components/moni-tooltip.test.ts +15 -15
- package/src/components/moni-tooltip.ts +107 -40
- package/src/components/moni-typography.test.ts +10 -10
- package/src/components/moni-typography.ts +73 -32
- package/src/styles/tokens.css +7 -7
- package/src/types/events.ts +17 -0
- package/src/utils/event-emitter.ts +26 -0
|
@@ -15,85 +15,178 @@ import { customElement, property, state, query } from 'lit/decorators.js';
|
|
|
15
15
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
16
16
|
import { classMap } from 'lit/directives/class-map.js';
|
|
17
17
|
import { MoniElement, sharedStyles, fieldStyles } from './_base/index.js';
|
|
18
|
+
import { emitMoniEvent } from '../utils/event-emitter.js';
|
|
18
19
|
import './moni-icon.js';
|
|
19
20
|
import './moni-progress.js';
|
|
20
21
|
import './moni-select-option.js';
|
|
21
22
|
/**
|
|
22
|
-
* Material Design 3 Select (
|
|
23
|
+
* Componente Material Design 3 Select (Menú Desplegable).
|
|
23
24
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* M3
|
|
25
|
+
* Un menú desplegable select personalizado completo con capacidad de búsqueda, grupos de opciones,
|
|
26
|
+
* navegación por teclado, animación y un modo de cajón/hoja (drawer/sheet) móvil opcional.
|
|
27
|
+
* Reemplaza el elemento nativo `<select>` con una alternativa completamente estilizada y accesible
|
|
28
|
+
* que cumple con M3.
|
|
28
29
|
*
|
|
29
|
-
* **
|
|
30
|
+
* **Referencia a la especificación M3:** `m3-docs/components/menus/specs.md` (menús desplegables)
|
|
30
31
|
*
|
|
31
|
-
* **
|
|
32
|
-
* - Filled
|
|
33
|
-
* -
|
|
34
|
-
* -
|
|
35
|
-
* -
|
|
36
|
-
* -
|
|
37
|
-
*
|
|
38
|
-
* -
|
|
39
|
-
* -
|
|
40
|
-
* -
|
|
32
|
+
* **Resumen de características:**
|
|
33
|
+
* - Variantes Filled y Outlined que coinciden con los estilos de campo de texto de M3.
|
|
34
|
+
* - Etiqueta flotante (floating label) con la animación estándar de etiqueta flotante de estilos de campo.
|
|
35
|
+
* - Modo de búsqueda: el atributo `searchable` agrega un campo de entrada de filtro en línea.
|
|
36
|
+
* - Grupos de opciones: elementos `<moni-select-group>` en el slot para opciones jerárquicas.
|
|
37
|
+
* - Cajón móvil: el atributo `drawer` abre las opciones en un `<moni-bottom-sheet>`
|
|
38
|
+
* en lugar de un popup desplegable, ideal para interfaces táctiles (touch UIs).
|
|
39
|
+
* - Navegación por teclado: Teclas de flecha, Enter, Escape y Tab según combobox de ARIA.
|
|
40
|
+
* - Estado de carga: `loading` muestra un progreso circular indeterminado.
|
|
41
|
+
* - Soporte multi-valor: `multiple` permite selección múltiple.
|
|
41
42
|
*
|
|
42
|
-
* **
|
|
43
|
-
*
|
|
44
|
-
* 1. **
|
|
45
|
-
* 2.
|
|
43
|
+
* **Fuentes de opciones:**
|
|
44
|
+
* Las opciones se pueden proporcionar de dos maneras:
|
|
45
|
+
* 1. **Elementos `<moni-select-option>` en la ranura (slot)** (por defecto, recomendado para SSR).
|
|
46
|
+
* 2. **Propiedad `options`** — un array de `DropdownNode[]` para un control completamente programático.
|
|
46
47
|
*
|
|
47
|
-
* **Value binding:**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* `'change'
|
|
48
|
+
* **Vinculación de valores (Value binding):**
|
|
49
|
+
* La propiedad `value` contiene la cadena de valor de la opción actualmente seleccionada.
|
|
50
|
+
* Para selección múltiple, `values` contiene `string[]`. Al cambiar, se dispara un
|
|
51
|
+
* evento compuesto `'moni-change'`.
|
|
51
52
|
*
|
|
52
|
-
* @fires change -
|
|
53
|
-
*
|
|
53
|
+
* @fires moni-change - Burbujea y está compuesto. Se dispara cuando el valor seleccionado cambia.
|
|
54
|
+
* Lee `element.value` (o `element.values` para `multiple`).
|
|
54
55
|
*
|
|
55
56
|
* @example
|
|
56
57
|
* ```html
|
|
57
|
-
* <moni-select label="
|
|
58
|
-
* <moni-select-option value="us">
|
|
59
|
-
* <moni-select-option value="gb">
|
|
60
|
-
* <moni-select-option value="de">
|
|
58
|
+
* <moni-select label="País" name="country" variant="outlined">
|
|
59
|
+
* <moni-select-option value="us">Estados Unidos</moni-select-option>
|
|
60
|
+
* <moni-select-option value="gb">Reino Unido</moni-select-option>
|
|
61
|
+
* <moni-select-option value="de">Alemania</moni-select-option>
|
|
61
62
|
* </moni-select>
|
|
62
63
|
*
|
|
63
|
-
* <!--
|
|
64
|
-
* <moni-select label="
|
|
64
|
+
* <!-- Select con búsqueda -->
|
|
65
|
+
* <moni-select label="Idioma" searchable>
|
|
65
66
|
* <moni-select-option value="ts">TypeScript</moni-select-option>
|
|
66
67
|
* <moni-select-option value="py">Python</moni-select-option>
|
|
67
68
|
* </moni-select>
|
|
68
69
|
* ```
|
|
69
70
|
*
|
|
70
|
-
* @slot default - `<moni-select-option>`
|
|
71
|
+
* @slot default - Hijos `<moni-select-option>` o `<moni-select-group>`.
|
|
71
72
|
*
|
|
72
|
-
* @csspart field -
|
|
73
|
-
* @csspart dropdown -
|
|
73
|
+
* @csspart field - El contenedor div exterior `.field`.
|
|
74
|
+
* @csspart dropdown - El contenedor flotante de la lista de opciones.
|
|
74
75
|
*/
|
|
75
76
|
let MoniSelect = class MoniSelect extends MoniElement {
|
|
77
|
+
static { this.formAssociated = true; }
|
|
76
78
|
constructor() {
|
|
77
|
-
super(
|
|
79
|
+
super();
|
|
80
|
+
/**
|
|
81
|
+
* Nombre del campo select, usado para el envío del formulario.
|
|
82
|
+
* @type {string}
|
|
83
|
+
*/
|
|
78
84
|
this.name = '';
|
|
85
|
+
/**
|
|
86
|
+
* Texto de la etiqueta flotante.
|
|
87
|
+
* @type {string}
|
|
88
|
+
*/
|
|
79
89
|
this.label = '';
|
|
90
|
+
/**
|
|
91
|
+
* Variante visual del campo select.
|
|
92
|
+
* @type {'filled' | 'outlined'}
|
|
93
|
+
* @default 'filled'
|
|
94
|
+
*/
|
|
80
95
|
this.variant = 'filled';
|
|
96
|
+
/**
|
|
97
|
+
* Define las dimensiones del campo select.
|
|
98
|
+
* @type {'small' | 'medium' | 'large' | 'extra'}
|
|
99
|
+
* @default 'medium'
|
|
100
|
+
*/
|
|
81
101
|
this.size = 'medium';
|
|
102
|
+
/**
|
|
103
|
+
* Forma (radio de borde) del campo.
|
|
104
|
+
* @type {'round' | 'square' | 'no-round'}
|
|
105
|
+
* @default 'no-round'
|
|
106
|
+
*/
|
|
82
107
|
this.shape = 'no-round';
|
|
108
|
+
/**
|
|
109
|
+
* Deshabilita el campo select.
|
|
110
|
+
* @type {boolean}
|
|
111
|
+
*/
|
|
83
112
|
this.disabled = false;
|
|
113
|
+
/**
|
|
114
|
+
* Si es verdadero, muestra un indicador de carga (progreso lineal).
|
|
115
|
+
* @type {boolean}
|
|
116
|
+
*/
|
|
84
117
|
this.loading = false;
|
|
118
|
+
/**
|
|
119
|
+
* Texto de ayuda mostrado debajo del campo.
|
|
120
|
+
* @type {string}
|
|
121
|
+
*/
|
|
85
122
|
this.helper = '';
|
|
123
|
+
/**
|
|
124
|
+
* Texto de error mostrado debajo del campo cuando `error` es true.
|
|
125
|
+
* Reemplaza al texto de ayuda.
|
|
126
|
+
* @type {string}
|
|
127
|
+
*/
|
|
86
128
|
this.errorText = '';
|
|
129
|
+
/**
|
|
130
|
+
* Si es verdadero, establece el campo en un estado de error.
|
|
131
|
+
* @type {boolean}
|
|
132
|
+
*/
|
|
87
133
|
this.error = false;
|
|
134
|
+
/**
|
|
135
|
+
* El valor actual del select.
|
|
136
|
+
* @type {string}
|
|
137
|
+
*/
|
|
88
138
|
this.value = '';
|
|
139
|
+
/**
|
|
140
|
+
* Nombre del icono principal (leading icon, Material Symbols).
|
|
141
|
+
* @type {string}
|
|
142
|
+
*/
|
|
89
143
|
this.icon = '';
|
|
144
|
+
/**
|
|
145
|
+
* Nombre del icono final (trailing icon, Material Symbols) que indica el estado del menú desplegable.
|
|
146
|
+
* @type {string}
|
|
147
|
+
* @default 'arrow_drop_down'
|
|
148
|
+
*/
|
|
90
149
|
this.trailingIcon = 'arrow_drop_down';
|
|
150
|
+
/**
|
|
151
|
+
* Habilita un campo de búsqueda en la parte superior del menú desplegable para filtrar opciones.
|
|
152
|
+
* @type {boolean}
|
|
153
|
+
*/
|
|
91
154
|
this.searchable = false;
|
|
155
|
+
/**
|
|
156
|
+
* Muestra un botón de limpieza cuando se selecciona un valor para restablecer fácilmente el campo.
|
|
157
|
+
* @type {boolean}
|
|
158
|
+
*/
|
|
92
159
|
this.clearable = false;
|
|
160
|
+
/**
|
|
161
|
+
* Renderiza las opciones como una hoja inferior (bottom sheet, ideal para dispositivos móviles) en lugar de un menú desplegable.
|
|
162
|
+
* @type {boolean}
|
|
163
|
+
*/
|
|
93
164
|
this.sheet = false;
|
|
165
|
+
/**
|
|
166
|
+
* Texto de marcador de posición (placeholder) mostrado cuando no hay ningún valor seleccionado.
|
|
167
|
+
* @type {string}
|
|
168
|
+
*/
|
|
94
169
|
this.placeholder = '';
|
|
170
|
+
/**
|
|
171
|
+
* Estrategia de posicionamiento para el menú desplegable.
|
|
172
|
+
* @type {'absolute' | 'fixed'}
|
|
173
|
+
* @default 'absolute'
|
|
174
|
+
*/
|
|
95
175
|
this.positioning = 'absolute';
|
|
176
|
+
/**
|
|
177
|
+
* Ubicación preferida del menú desplegable con respecto al disparador (trigger).
|
|
178
|
+
* @type {'top' | 'bottom' | 'left' | 'right' | 'auto'}
|
|
179
|
+
* @default 'auto'
|
|
180
|
+
*/
|
|
96
181
|
this.placement = 'auto';
|
|
182
|
+
/**
|
|
183
|
+
* Restricción de ancho del menú desplegable.
|
|
184
|
+
* - `'trigger'`: Coincide con el ancho del campo de entrada.
|
|
185
|
+
* - `'auto'`: Coincide con el ancho del contenido del menú desplegable.
|
|
186
|
+
* - O cualquier valor CSS de ancho válido (ej. '200px').
|
|
187
|
+
* @type {string}
|
|
188
|
+
* @default 'trigger'
|
|
189
|
+
*/
|
|
97
190
|
this.dropdownWidth = 'trigger';
|
|
98
191
|
this._open = false;
|
|
99
192
|
this._searchQuery = '';
|
|
@@ -103,23 +196,39 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
103
196
|
this._menuStyle = '';
|
|
104
197
|
this._useInlineCategories = false;
|
|
105
198
|
this._drilldownPath = [];
|
|
199
|
+
/**
|
|
200
|
+
* Cierra el Dropdown cuando el usuario hace clic fuera de las dimensiones del componente.
|
|
201
|
+
* Utiliza `e.composedPath()` para poder atravesar barreras de Shadow DOM si el select
|
|
202
|
+
* está dentro de un contenedor web-component externo.
|
|
203
|
+
*/
|
|
106
204
|
this._handleOutsideClick = (e) => {
|
|
107
205
|
if (this._open && !e.composedPath().includes(this)) {
|
|
108
206
|
this._closeDropdown();
|
|
109
207
|
}
|
|
110
208
|
};
|
|
209
|
+
/**
|
|
210
|
+
* Handler de Scroll global.
|
|
211
|
+
* Fuerza un recálculo de la posición y dimensiones del dropdown si el usuario
|
|
212
|
+
* hace scroll en la página mientras el select permanece abierto.
|
|
213
|
+
*/
|
|
111
214
|
this._handleScroll = () => {
|
|
112
215
|
if (this._open) {
|
|
113
216
|
this._determineActualPlacement();
|
|
114
217
|
this._updateMenuPosition();
|
|
115
218
|
}
|
|
116
219
|
};
|
|
220
|
+
/**
|
|
221
|
+
* Handler de Resize global.
|
|
222
|
+
* Asegura que el popover del select no quede fuera del viewport cuando
|
|
223
|
+
* se cambia el tamaño de la ventana o se rota la pantalla en dispositivos móviles.
|
|
224
|
+
*/
|
|
117
225
|
this._handleResize = () => {
|
|
118
226
|
if (this._open) {
|
|
119
227
|
this._determineActualPlacement();
|
|
120
228
|
this._updateMenuPosition();
|
|
121
229
|
}
|
|
122
230
|
};
|
|
231
|
+
this._internals = this.attachInternals();
|
|
123
232
|
}
|
|
124
233
|
static { this.styles = [
|
|
125
234
|
sharedStyles,
|
|
@@ -445,20 +554,40 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
445
554
|
}
|
|
446
555
|
`
|
|
447
556
|
]; }
|
|
557
|
+
/**
|
|
558
|
+
* Hook del ciclo de vida (Lit).
|
|
559
|
+
* Vincula event listeners globales necesarios para la lógica del dropdown.
|
|
560
|
+
* Se usa `capture: true` en el evento de scroll para poder reaccionar
|
|
561
|
+
* al desplazamiento de cualquier contenedor padre y reposicionar el menú a tiempo.
|
|
562
|
+
*/
|
|
448
563
|
connectedCallback() {
|
|
449
564
|
super.connectedCallback();
|
|
450
565
|
document.addEventListener('click', this._handleOutsideClick);
|
|
451
566
|
window.addEventListener('scroll', this._handleScroll, { capture: true });
|
|
452
567
|
window.addEventListener('resize', this._handleResize);
|
|
453
568
|
}
|
|
569
|
+
/**
|
|
570
|
+
* Hook del ciclo de vida (Lit).
|
|
571
|
+
* Desmonta rigurosamente todos los listeners globales para prevenir memory leaks
|
|
572
|
+
* o llamadas accidentales cuando el select ya fue destruido del DOM.
|
|
573
|
+
*/
|
|
454
574
|
disconnectedCallback() {
|
|
455
575
|
super.disconnectedCallback();
|
|
456
576
|
document.removeEventListener('click', this._handleOutsideClick);
|
|
457
577
|
window.removeEventListener('scroll', this._handleScroll, { capture: true });
|
|
458
578
|
window.removeEventListener('resize', this._handleResize);
|
|
459
579
|
}
|
|
580
|
+
/**
|
|
581
|
+
* Hook de actualización reactiva (Lit).
|
|
582
|
+
* Verifica si alguna propiedad relacionada con el tamaño del contenido (como la búsqueda,
|
|
583
|
+
* la ruta de anidación o el estado abierto) ha cambiado, para forzar un recálculo
|
|
584
|
+
* de la altura animada del contenedor `drilldown-wrapper`.
|
|
585
|
+
*/
|
|
460
586
|
updated(changedProperties) {
|
|
461
587
|
super.updated(changedProperties);
|
|
588
|
+
if (changedProperties.has('value')) {
|
|
589
|
+
this._internals?.setFormValue?.(this.value);
|
|
590
|
+
}
|
|
462
591
|
if (changedProperties.has('_open') ||
|
|
463
592
|
changedProperties.has('_drilldownPath') ||
|
|
464
593
|
changedProperties.has('_searchQuery') ||
|
|
@@ -467,6 +596,12 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
467
596
|
this._updateWrapperHeight();
|
|
468
597
|
}
|
|
469
598
|
}
|
|
599
|
+
/**
|
|
600
|
+
* Lógica de animación de altura (Height Animation).
|
|
601
|
+
* Cuando se navega entre categorías anidadas (`_drilldownPath`), las listas hijas pueden
|
|
602
|
+
* tener diferentes tamaños. Este método lee el `scrollHeight` del panel activo y lo aplica
|
|
603
|
+
* imperativamente al `wrapper` para disparar una transición CSS suave en la altura del dropdown.
|
|
604
|
+
*/
|
|
470
605
|
_updateWrapperHeight() {
|
|
471
606
|
const wrapper = this.shadowRoot?.querySelector('.drilldown-wrapper');
|
|
472
607
|
if (!wrapper)
|
|
@@ -486,6 +621,17 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
486
621
|
}
|
|
487
622
|
});
|
|
488
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* Motor de Cálculo de Colisiones de la Interfaz (Collision Detection).
|
|
626
|
+
*
|
|
627
|
+
* @logic
|
|
628
|
+
* 1. Calcula las coordenadas espaciales (`getBoundingClientRect`) del select base.
|
|
629
|
+
* 2. Compara el espacio disponible (`spaceAbove`, `spaceBelow`, `spaceRight`, `spaceLeft`)
|
|
630
|
+
* respecto al viewport (`window.innerHeight`, `window.innerWidth`).
|
|
631
|
+
* 3. Si el dropdown no cabe en la posición preferida (`placement`), revierte lógicamente
|
|
632
|
+
* la posición (ej: si `bottom` desborda, lo abre hacia `top`).
|
|
633
|
+
* 4. Actualiza `_actualPlacement` con la dirección segura calculada.
|
|
634
|
+
*/
|
|
489
635
|
_determineActualPlacement() {
|
|
490
636
|
if (this.sheet) {
|
|
491
637
|
this._actualPlacement = 'bottom';
|
|
@@ -537,6 +683,12 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
537
683
|
}
|
|
538
684
|
}
|
|
539
685
|
}
|
|
686
|
+
/**
|
|
687
|
+
* Aplica las coordenadas y dimensiones absolutas o fijas al menú desplegable.
|
|
688
|
+
* Este método es fundamental cuando `positioning === 'fixed'`, ya que el dropdown
|
|
689
|
+
* se saca del flujo normal del documento para evitar `overflow: hidden` de los padres.
|
|
690
|
+
* Mapea el `width` para coincidir opcionalmente con el gatillo (`dropdownWidth="trigger"`).
|
|
691
|
+
*/
|
|
540
692
|
_updateMenuPosition() {
|
|
541
693
|
if (!this._open || this.sheet)
|
|
542
694
|
return;
|
|
@@ -595,6 +747,12 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
595
747
|
`;
|
|
596
748
|
}
|
|
597
749
|
}
|
|
750
|
+
/**
|
|
751
|
+
* Devuelve un string de estilos inline para el menú desplegable.
|
|
752
|
+
* Si la posición es `fixed` (calculada dinámicamente para evadir overflow),
|
|
753
|
+
* retorna el bloque de coordenadas generado por `_updateMenuPosition`.
|
|
754
|
+
* Si no, asigna anchos basados en la propiedad `dropdownWidth` (auto, trigger, custom).
|
|
755
|
+
*/
|
|
598
756
|
_getMenuStyle() {
|
|
599
757
|
if (this.positioning === 'fixed') {
|
|
600
758
|
return this._menuStyle;
|
|
@@ -607,6 +765,18 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
607
765
|
}
|
|
608
766
|
return '';
|
|
609
767
|
}
|
|
768
|
+
/**
|
|
769
|
+
* Parseador del Shadow DOM (Slot Change Handler).
|
|
770
|
+
*
|
|
771
|
+
* @logic
|
|
772
|
+
* 1. Cuando los hijos proyectados cambian (ej. se agregan nuevos `<option>`),
|
|
773
|
+
* lee iterativamente todos los nodos del Light DOM.
|
|
774
|
+
* 2. Si encuentra `<optgroup>` o `<moni-select-option group="A/B">`, construye dinámicamente
|
|
775
|
+
* un árbol estructural de `GroupNode` anidados.
|
|
776
|
+
* 3. Si encuentra opciones, las inyecta en el grupo correspondiente del árbol.
|
|
777
|
+
* 4. Almacena el árbol resultante en `_parsedOptions` para que el componente
|
|
778
|
+
* pueda renderizar sus propios submenús nativos independientemente del markup inicial.
|
|
779
|
+
*/
|
|
610
780
|
_onSlotChange() {
|
|
611
781
|
if (!this._slot)
|
|
612
782
|
return;
|
|
@@ -656,6 +826,10 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
656
826
|
this._parsedOptions = tree;
|
|
657
827
|
this.requestUpdate();
|
|
658
828
|
}
|
|
829
|
+
/**
|
|
830
|
+
* Alterna el estado de apertura/cierre del dropdown, previniendo
|
|
831
|
+
* la acción si el componente está deshabilitado.
|
|
832
|
+
*/
|
|
659
833
|
_toggleDropdown(e) {
|
|
660
834
|
if (e) {
|
|
661
835
|
e.stopPropagation();
|
|
@@ -669,6 +843,11 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
669
843
|
this._openDropdown();
|
|
670
844
|
}
|
|
671
845
|
}
|
|
846
|
+
/**
|
|
847
|
+
* Manejador de clics en el campo de texto (Trigger).
|
|
848
|
+
* Si el componente es `searchable` (permite escribir para buscar), un clic
|
|
849
|
+
* sólo abrirá el menú (no lo cerrará si ya estaba abierto, para permitir al usuario seguir escribiendo).
|
|
850
|
+
*/
|
|
672
851
|
_onInputClick(e) {
|
|
673
852
|
if (this.disabled)
|
|
674
853
|
return;
|
|
@@ -681,28 +860,39 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
681
860
|
this._toggleDropdown(e);
|
|
682
861
|
}
|
|
683
862
|
}
|
|
863
|
+
/**
|
|
864
|
+
* Despliega el menú del select y coordina la accesibilidad, posicionamiento y foco inicial.
|
|
865
|
+
*/
|
|
684
866
|
_openDropdown() {
|
|
685
867
|
this._open = true;
|
|
686
868
|
this._activeIndex = -1;
|
|
687
869
|
this._drilldownPath = [];
|
|
870
|
+
// Calculamos la dirección del menú emergente en base al espacio disponible en la pantalla
|
|
688
871
|
this._determineActualPlacement();
|
|
689
|
-
//
|
|
872
|
+
// Medimos dinámicamente si hay espacio suficiente para renderizar submenús laterales (Desktop).
|
|
873
|
+
// Si es móvil (<600px) o no hay 160px de espacio libre a los lados, forzamos un diseño plano ("inline categories")
|
|
874
|
+
// para evitar que los submenús se salgan del viewport.
|
|
690
875
|
const rect = this.getBoundingClientRect();
|
|
691
876
|
const spaceOnRight = window.innerWidth - rect.right;
|
|
692
877
|
const spaceOnLeft = rect.left;
|
|
693
878
|
this._useInlineCategories = window.innerWidth < 600 || (spaceOnRight < 160 && spaceOnLeft < 160);
|
|
879
|
+
// Si el menú tiene position="fixed", necesitamos computar y aplicar las coordenadas absolutas en pixels
|
|
694
880
|
if (this.positioning === 'fixed') {
|
|
695
881
|
this._updateMenuPosition();
|
|
696
882
|
}
|
|
883
|
+
// Lógica especial si el componente está configurado como "searchable" (Input filtrable)
|
|
697
884
|
if (this.searchable) {
|
|
698
885
|
const selectedOpt = this._findOptionByValue(this.value);
|
|
699
886
|
this._searchQuery = selectedOpt ? selectedOpt.label : '';
|
|
887
|
+
// Esperamos un tick para que el input o bottom-sheet sea renderizado antes de solicitar el foco
|
|
700
888
|
setTimeout(() => {
|
|
701
889
|
if (this._input && !this.sheet) {
|
|
890
|
+
// Foco en el input principal (Desktop) y seleccionamos todo el texto para fácil reemplazo
|
|
702
891
|
this._input.focus();
|
|
703
892
|
this._input.select();
|
|
704
893
|
}
|
|
705
894
|
else if (this.sheet) {
|
|
895
|
+
// Foco en la caja de búsqueda inyectada dentro del bottom-sheet (Mobile)
|
|
706
896
|
const sheetInput = this.shadowRoot?.querySelector('.sheet-search-input');
|
|
707
897
|
if (sheetInput) {
|
|
708
898
|
sheetInput.focus();
|
|
@@ -712,6 +902,10 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
712
902
|
}, 50);
|
|
713
903
|
}
|
|
714
904
|
}
|
|
905
|
+
/**
|
|
906
|
+
* Cierra el dropdown y purga la consulta de búsqueda (`_searchQuery`)
|
|
907
|
+
* para que la próxima vez que se abra, la lista muestre todas las opciones por defecto.
|
|
908
|
+
*/
|
|
715
909
|
_closeDropdown() {
|
|
716
910
|
this._open = false;
|
|
717
911
|
this._searchQuery = '';
|
|
@@ -720,6 +914,15 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
720
914
|
_findOptionByValue(value) {
|
|
721
915
|
return this._getFlatOptions(this._parsedOptions).find(opt => opt.value === value);
|
|
722
916
|
}
|
|
917
|
+
/**
|
|
918
|
+
* Evento de input (tecleo) en la barra de búsqueda.
|
|
919
|
+
*
|
|
920
|
+
* @logic
|
|
921
|
+
* 1. Actualiza `_searchQuery` y fuerza la apertura del dropdown.
|
|
922
|
+
* 2. Si es `clearable` y el usuario borró todo, emite eventos vacíos.
|
|
923
|
+
* 3. Busca coincidencias exactas en el texto. Si el usuario teclea exactamente el
|
|
924
|
+
* label de una opción existente, la selecciona automáticamente (auto-completado nativo).
|
|
925
|
+
*/
|
|
723
926
|
_onSearchInput(e) {
|
|
724
927
|
const query = e.target.value;
|
|
725
928
|
this._searchQuery = query;
|
|
@@ -727,8 +930,7 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
727
930
|
this._activeIndex = -1;
|
|
728
931
|
if (this.clearable && query.trim() === '') {
|
|
729
932
|
this.value = '';
|
|
730
|
-
this
|
|
731
|
-
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
|
933
|
+
emitMoniEvent(this, 'moni-change', { detail: { value: this.value, originalEvent: e } });
|
|
732
934
|
return;
|
|
733
935
|
}
|
|
734
936
|
const flat = this._getFlatOptions(this._parsedOptions);
|
|
@@ -737,6 +939,11 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
737
939
|
this._selectOption(exactMatch);
|
|
738
940
|
}
|
|
739
941
|
}
|
|
942
|
+
/**
|
|
943
|
+
* Confirma la selección de una opción específica.
|
|
944
|
+
* Actualiza el valor (estado reactivo), cierra el dropdown y despacha los eventos
|
|
945
|
+
* estándar HTML `input` y `change` para integrarse fluidamente con formularios nativos o frameworks.
|
|
946
|
+
*/
|
|
740
947
|
_selectOption(option) {
|
|
741
948
|
if (option.disabled)
|
|
742
949
|
return;
|
|
@@ -744,7 +951,14 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
744
951
|
this._closeDropdown();
|
|
745
952
|
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
746
953
|
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
|
954
|
+
emitMoniEvent(this, 'moni-change', { detail: { value: this.value } });
|
|
747
955
|
}
|
|
956
|
+
/**
|
|
957
|
+
* Maneja el Hover sobre categorías en modo Desktop (Submenús emergentes).
|
|
958
|
+
* Detecta dinámicamente si el submenú se saldrá del lado derecho de la pantalla.
|
|
959
|
+
* Si es así, altera las clases de CSS (`open-left` vs `open-right`) para que
|
|
960
|
+
* el menú flote en la dirección opuesta, evitando desbordamientos de viewport (Overflow prevention).
|
|
961
|
+
*/
|
|
748
962
|
_onSubmenuMouseEnter(e) {
|
|
749
963
|
const headerEl = e.currentTarget;
|
|
750
964
|
const submenu = headerEl.querySelector(':scope > .submenu');
|
|
@@ -763,16 +977,23 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
763
977
|
submenu.classList.add('open-right');
|
|
764
978
|
}
|
|
765
979
|
}
|
|
980
|
+
/**
|
|
981
|
+
* Gestiona la navegación completa por teclado siguiendo el patrón WAI-ARIA Combobox.
|
|
982
|
+
* Soporta flechas direccionales, selección (Enter), y cierre (Escape).
|
|
983
|
+
*/
|
|
766
984
|
_onKeyDown(e) {
|
|
767
985
|
if (this.disabled)
|
|
768
986
|
return;
|
|
987
|
+
// Obtenemos solo las opciones que están visualmente disponibles en la jerarquía actual
|
|
769
988
|
const visible = this._getVisibleOptions();
|
|
770
989
|
if (e.key === 'ArrowDown') {
|
|
771
990
|
e.preventDefault();
|
|
772
991
|
if (!this._open) {
|
|
992
|
+
// Si está cerrado, ArrowDown lo abre (comportamiento estándar nativo)
|
|
773
993
|
this._openDropdown();
|
|
774
994
|
}
|
|
775
995
|
else if (visible.length > 0) {
|
|
996
|
+
// Navegamos circularmente hacia abajo
|
|
776
997
|
this._activeIndex = (this._activeIndex + 1) % visible.length;
|
|
777
998
|
this._scrollToActive();
|
|
778
999
|
}
|
|
@@ -780,6 +1001,7 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
780
1001
|
else if (e.key === 'ArrowUp') {
|
|
781
1002
|
e.preventDefault();
|
|
782
1003
|
if (this._open && visible.length > 0) {
|
|
1004
|
+
// Navegamos circularmente hacia arriba (+visible.length evita valores negativos en JS)
|
|
783
1005
|
this._activeIndex = (this._activeIndex - 1 + visible.length) % visible.length;
|
|
784
1006
|
this._scrollToActive();
|
|
785
1007
|
}
|
|
@@ -787,20 +1009,27 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
787
1009
|
else if (e.key === 'Enter') {
|
|
788
1010
|
e.preventDefault();
|
|
789
1011
|
if (this._open) {
|
|
1012
|
+
// Si está abierto y hay una opción resaltada, simulamos el click para seleccionarla
|
|
790
1013
|
if (this._activeIndex >= 0 && this._activeIndex < visible.length) {
|
|
791
1014
|
this._selectOption(visible[this._activeIndex]);
|
|
792
1015
|
}
|
|
793
1016
|
}
|
|
794
1017
|
else {
|
|
1018
|
+
// Enter cuando está cerrado simplemente abre el menú
|
|
795
1019
|
this._openDropdown();
|
|
796
1020
|
}
|
|
797
1021
|
}
|
|
798
1022
|
else if (e.key === 'Escape') {
|
|
799
1023
|
e.preventDefault();
|
|
1024
|
+
// Escapa el menú sin hacer ninguna selección
|
|
800
1025
|
this._closeDropdown();
|
|
801
1026
|
}
|
|
802
1027
|
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Asegura que el elemento resaltado por teclado sea visible dentro del contenedor scrolleable.
|
|
1030
|
+
*/
|
|
803
1031
|
_scrollToActive() {
|
|
1032
|
+
// Se requiere setTimeout para darle tiempo a Lit a renderizar la clase 'active-nav'
|
|
804
1033
|
setTimeout(() => {
|
|
805
1034
|
const activeEl = this.shadowRoot?.querySelector('.option-item.active-nav');
|
|
806
1035
|
if (activeEl) {
|
|
@@ -808,6 +1037,10 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
808
1037
|
}
|
|
809
1038
|
}, 0);
|
|
810
1039
|
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Aplana el árbol jerárquico recursivo de <moni-select-group> a un array unidimensional de opciones puras.
|
|
1042
|
+
* Esencial para que la búsqueda por texto funcione globalmente ignorando las categorías.
|
|
1043
|
+
*/
|
|
811
1044
|
_getFlatOptions(nodes) {
|
|
812
1045
|
const flat = [];
|
|
813
1046
|
const traverse = (n) => {
|
|
@@ -841,6 +1074,13 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
841
1074
|
}
|
|
842
1075
|
return filtered;
|
|
843
1076
|
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Motor de Renderizado: Lista Raíz (Modo Móvil / Drilldown).
|
|
1079
|
+
*
|
|
1080
|
+
* Renderiza las opciones iniciales y los grupos base de la categoría actual (`_drilldownPath`).
|
|
1081
|
+
* Asigna clases dinámicas (`selected`, `active-nav`) para la retroalimentación visual
|
|
1082
|
+
* del teclado y el valor actualmente seleccionado.
|
|
1083
|
+
*/
|
|
844
1084
|
_renderRootList() {
|
|
845
1085
|
let visibleIndex = 0;
|
|
846
1086
|
return this._parsedOptions.map(node => {
|
|
@@ -865,6 +1105,13 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
865
1105
|
`;
|
|
866
1106
|
});
|
|
867
1107
|
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Motor de Renderizado: Subcategorías (Modo Móvil / Drilldown).
|
|
1110
|
+
*
|
|
1111
|
+
* A diferencia de desktop (donde los submenús flotan a los lados), en dispositivos
|
|
1112
|
+
* móviles o espacios constreñidos, el menú transiciona in-situ. Este método renderiza
|
|
1113
|
+
* el header del grupo activo, un botón de "Regresar" (Arrow Back) y las opciones hijas.
|
|
1114
|
+
*/
|
|
868
1115
|
_renderSubcategoryList() {
|
|
869
1116
|
if (this._drilldownPath.length === 0)
|
|
870
1117
|
return nothing;
|
|
@@ -928,6 +1175,14 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
928
1175
|
</li>
|
|
929
1176
|
`;
|
|
930
1177
|
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Orquestador principal de renderizado de la lista de opciones.
|
|
1180
|
+
*
|
|
1181
|
+
* Si el usuario introdujo texto (`_searchQuery`), renderiza los resultados filtrados.
|
|
1182
|
+
* Si la lista original está vacía, muestra un mensaje amigable (Empty State).
|
|
1183
|
+
* Determina condicionalmente si usar el modo plano/Drilldown (Móviles) o
|
|
1184
|
+
* el modo Desktop con submenús flotantes (`_renderDesktopNode`).
|
|
1185
|
+
*/
|
|
931
1186
|
_renderOptionsList(filtered) {
|
|
932
1187
|
if (this._parsedOptions.length === 0) {
|
|
933
1188
|
return html `<li class="no-options">No options found</li>`;
|
|
@@ -977,6 +1232,35 @@ let MoniSelect = class MoniSelect extends MoniElement {
|
|
|
977
1232
|
const flatIndexRef = { value: 0 };
|
|
978
1233
|
return this._parsedOptions.map(node => this._renderDesktopNode(node, flatIndexRef));
|
|
979
1234
|
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Ensambla el Shadow DOM del campo select, coordinando dos modos de interfaz de usuario distintos.
|
|
1237
|
+
*
|
|
1238
|
+
* **Resolución del valor a mostrar (Display value):**
|
|
1239
|
+
* El contenido de texto visible en el input está determinado por prioridad:
|
|
1240
|
+
* 1. Mientras el menú desplegable está abierto Y `searchable=true` Y NO es modo sheet:
|
|
1241
|
+
* muestra `_searchQuery` (el texto del filtro en vivo que el usuario está escribiendo).
|
|
1242
|
+
* 2. De lo contrario: muestra `selectedOpt.label` (la etiqueta de la opción actualmente
|
|
1243
|
+
* seleccionada) o vuelve a la cadena vacía.
|
|
1244
|
+
*
|
|
1245
|
+
* **Dos rutas de renderizado:**
|
|
1246
|
+
* - **Modo Sheet (`sheet=true`):** Renderiza un `<moni-bottom-sheet>` como un
|
|
1247
|
+
* cajón (drawer) superpuesto a pantalla completa, ideal para IU táctiles móviles. La lista de opciones
|
|
1248
|
+
* se renderiza dentro de la hoja en lugar del menú desplegable.
|
|
1249
|
+
* - **Modo desplegable (por defecto):** Renderiza un `.dropdown-menu` flotante que
|
|
1250
|
+
* se abre debajo o arriba del campo. La cadena `_menuStyle` (calculada por
|
|
1251
|
+
* `_computeMenuStyle()`) inyecta los `top`, `left`, `width`, y
|
|
1252
|
+
* sobreescrituras `position: fixed` correctas para contenedores con scroll (scroll-contained) y parents con overflow cortado (overflow-clipping).
|
|
1253
|
+
*
|
|
1254
|
+
* **Patrón combobox ARIA:**
|
|
1255
|
+
* El input de texto lleva `role="combobox"`, `aria-haspopup="listbox"` y
|
|
1256
|
+
* `aria-expanded` para satisfacer los requisitos del combobox WCAG 2.1 para lectores de pantalla.
|
|
1257
|
+
* `aria-activedescendant` se establece al ID de la opción actualmente enfocada por teclado
|
|
1258
|
+
* para habilitar el movimiento del cursor virtual sin gestión del foco.
|
|
1259
|
+
*
|
|
1260
|
+
* **Composición de `fieldClasses`:**
|
|
1261
|
+
* Sigue la convención de nomenclatura de clases de estilos de campo de BeerCSS. `prefix` se agrega
|
|
1262
|
+
* cuando `icon` está establecido (para desplazar el relleno -padding- inline-start del input de texto para el icono).
|
|
1263
|
+
*/
|
|
980
1264
|
render() {
|
|
981
1265
|
const hasLeading = Boolean(this.icon);
|
|
982
1266
|
const filtered = this._getFilteredOptions();
|