@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.
- package/custom-elements.json +5230 -1007
- 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 +484 -53
- package/dist/components/moni-carousel.d.ts.map +1 -1
- package/dist/components/moni-carousel.js +1024 -245
- 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 +1129 -269
- 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
|
@@ -11,43 +11,43 @@ import { MoniElement, sharedStyles } from './_base/index.js';
|
|
|
11
11
|
import type { MoniStep } from './moni-step.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Material Design 3 Stepper
|
|
14
|
+
* Componente Material Design 3 Stepper (Pasos/Progresión).
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Un contenedor para una progresión lineal de elementos `<moni-step>`. Los steppers
|
|
17
|
+
* transmiten el progreso a través de pasos numerados e indican la posición
|
|
18
|
+
* actual del usuario dentro de un flujo.
|
|
19
19
|
*
|
|
20
|
-
* **
|
|
20
|
+
* **Referencia a la especificación M3:** `m3-docs/components/progress-indicators/specs.md`
|
|
21
21
|
*
|
|
22
|
-
* **
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* stepper
|
|
26
|
-
* -
|
|
27
|
-
* -
|
|
28
|
-
* -
|
|
22
|
+
* **Orquestación:**
|
|
23
|
+
* Este componente actúa como orquestador para sus hijos `<moni-step>` en los slots.
|
|
24
|
+
* Cada vez que cambia la propiedad `current` o se añaden/eliminan hijos, el
|
|
25
|
+
* stepper itera sobre todos los pasos hijos e inyecta su estado:
|
|
26
|
+
* - Asigna el `index` secuencial (basado en 0) a cada paso.
|
|
27
|
+
* - Establece `active=true` en el paso que coincide con el índice `current`.
|
|
28
|
+
* - Establece `completed=true` en todos los pasos anteriores al índice `current`.
|
|
29
29
|
*
|
|
30
|
-
* **
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
30
|
+
* **Diseño visual:**
|
|
31
|
+
* El stepper maneja el diseño (flex row o column basado en `orientation`)
|
|
32
|
+
* y asegura que las líneas conectoras entre los pasos se rendericen correctamente a través de
|
|
33
|
+
* pseudo-elementos CSS definidos en los estilos del `<moni-step>` hijo.
|
|
34
34
|
*
|
|
35
35
|
* @example
|
|
36
36
|
* ```html
|
|
37
37
|
* <moni-stepper current="1" orientation="horizontal">
|
|
38
|
-
* <moni-step title="
|
|
39
|
-
* <moni-step title="
|
|
40
|
-
* <moni-step title="
|
|
38
|
+
* <moni-step title="Paso 1"></moni-step>
|
|
39
|
+
* <moni-step title="Paso 2"></moni-step>
|
|
40
|
+
* <moni-step title="Paso 3"></moni-step>
|
|
41
41
|
* </moni-stepper>
|
|
42
42
|
*
|
|
43
43
|
* <script>
|
|
44
44
|
* const stepper = document.querySelector('moni-stepper');
|
|
45
|
-
* //
|
|
45
|
+
* // Mover al siguiente paso
|
|
46
46
|
* stepper.current = 2;
|
|
47
47
|
* </script>
|
|
48
48
|
* ```
|
|
49
49
|
*
|
|
50
|
-
* @slot default - `<moni-step
|
|
50
|
+
* @slot default - Elementos `<moni-step>`.
|
|
51
51
|
*/
|
|
52
52
|
@customElement('moni-stepper')
|
|
53
53
|
export class MoniStepper extends MoniElement {
|
|
@@ -58,6 +58,11 @@ export class MoniStepper extends MoniElement {
|
|
|
58
58
|
@queryAssignedElements({ selector: 'moni-step' })
|
|
59
59
|
private _steps!: MoniStep[];
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Hook del ciclo de vida reactivo (Lit).
|
|
63
|
+
* Cuando la orientación o el índice actual cambian, sincroniza
|
|
64
|
+
* el estado hacia todos los elementos hijos (`<moni-step>`).
|
|
65
|
+
*/
|
|
61
66
|
override updated(changed: Map<string, unknown>) {
|
|
62
67
|
if (
|
|
63
68
|
changed.has('current') ||
|
|
@@ -69,24 +74,39 @@ export class MoniStepper extends MoniElement {
|
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
77
|
+
* Propaga el índice del paso actual a cada elemento hijo `<moni-step>` en los slots,
|
|
78
|
+
* calculando e inyectando el estado de `index`, `completed` y `active`.
|
|
79
|
+
*
|
|
80
|
+
* **Diferimiento por microtarea (`Promise.resolve().then(...)`):**
|
|
81
|
+
* `@queryAssignedElements` de Lit se ejecuta sincrónicamente y no está disponible hasta
|
|
82
|
+
* después del primer renderizado conectado. Diferirlo a una microtarea asegura que los hijos
|
|
83
|
+
* en los slots hayan terminado su propio ciclo de vida `firstUpdated()` antes de que les escribamos
|
|
84
|
+
* el estado. Sin este diferimiento, escribir `step.active = true` durante el `updated()`
|
|
85
|
+
* del padre puede desencadenar renderizados hijos antes de que se apliquen sus estilos.
|
|
76
86
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
87
|
+
* **Respaldo de `querySelectorAll`:**
|
|
88
|
+
* `_steps` (de `@queryAssignedElements`) devuelve un array vacío antes del primer
|
|
89
|
+
* renderizado o cuando no hay elementos `<moni-step>` presentes en el slot. El respaldo
|
|
90
|
+
* a `querySelectorAll('moni-step')` maneja casos extremos donde se añaden pasos
|
|
91
|
+
* programáticamente sin pasar por el mecanismo de slots de Lit.
|
|
92
|
+
*
|
|
93
|
+
* **Bucle de inyección de estado:**
|
|
94
|
+
* Para cada `<moni-step>` en la posición `stepIndex`:
|
|
95
|
+
* - `stepElement.index = stepIndex` — proporciona el número de paso para su etiqueta.
|
|
96
|
+
* - `stepElement.completed = stepIndex < current` — verdadero para todos los pasos anteriores a `current`.
|
|
97
|
+
* - `stepElement.active = stepIndex === current` — verdadero solo para el paso en `current`.
|
|
79
98
|
*/
|
|
80
99
|
private _syncSteps() {
|
|
81
100
|
Promise.resolve().then(() => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
101
|
+
// Usa el resultado de @queryAssignedElements de Lit primero; usa una consulta DOM en vivo como respaldo.
|
|
102
|
+
let stepElements = this._steps || [];
|
|
103
|
+
if (stepElements.length === 0) {
|
|
104
|
+
stepElements = Array.from(this.querySelectorAll('moni-step')) as MoniStep[];
|
|
85
105
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
106
|
+
stepElements.forEach((stepElement, stepIndex) => {
|
|
107
|
+
stepElement.index = stepIndex;
|
|
108
|
+
stepElement.completed = stepIndex < this.current;
|
|
109
|
+
stepElement.active = stepIndex === this.current;
|
|
90
110
|
});
|
|
91
111
|
});
|
|
92
112
|
}
|
|
@@ -117,12 +137,30 @@ export class MoniStepper extends MoniElement {
|
|
|
117
137
|
`
|
|
118
138
|
];
|
|
119
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Renderiza el contenedor de lista ordenada (`<ol>`) que contiene los hijos `<moni-step>` en los slots.
|
|
142
|
+
*
|
|
143
|
+
* **Anatomía:**
|
|
144
|
+
* - El `<ol>` usa `list-style: none` y un diseño flex (fila o columna, basado en
|
|
145
|
+
* `orientation`) en lugar del renderizado nativo de lista ordenada, porque la
|
|
146
|
+
* visualización del número de paso es manejada por cada hijo `<moni-step>`.
|
|
147
|
+
* - La vinculación del evento `@slotchange` asegura que se llame a `_syncSteps()`
|
|
148
|
+
* cada vez que el consumidor añade o elimina elementos `<moni-step>` en tiempo de ejecución,
|
|
149
|
+
* manteniendo los estados `index`, `active` y `completed` sincronizados.
|
|
150
|
+
* - Cada `<moni-step>` en el slot está estilizado con `flex: 1` para que todos los pasos
|
|
151
|
+
* compartan el ancho disponible equitativamente (horizontal) o la altura (vertical).
|
|
152
|
+
*/
|
|
120
153
|
override render() {
|
|
121
154
|
return html`<ol class="list" part="stepper">
|
|
122
155
|
<slot @slotchange=${this._onSlotChange}></slot>
|
|
123
156
|
</ol>`;
|
|
124
157
|
}
|
|
125
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Handler dinámico del slot.
|
|
161
|
+
* Detecta si el usuario añade o elimina pasos nativos (`<moni-step>`)
|
|
162
|
+
* desde el DOM en tiempo de ejecución, forzando una re-sincronización.
|
|
163
|
+
*/
|
|
126
164
|
private _onSlotChange = () => {
|
|
127
165
|
// Steps may have been added/removed; re-sync.
|
|
128
166
|
this._syncSteps();
|
|
@@ -14,7 +14,7 @@ describe('moni-switch', () => {
|
|
|
14
14
|
el.remove();
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
it('
|
|
17
|
+
it('renderiza un input y un span que contiene pseudo-elementos track/thumb', async () => {
|
|
18
18
|
await el.updateComplete;
|
|
19
19
|
const input = el.shadowRoot?.querySelector('input');
|
|
20
20
|
const span = el.shadowRoot?.querySelector('span');
|
|
@@ -22,7 +22,7 @@ describe('moni-switch', () => {
|
|
|
22
22
|
expect(span).toBeTruthy();
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
it('input
|
|
25
|
+
it('el input tiene role=switch', async () => {
|
|
26
26
|
await el.updateComplete;
|
|
27
27
|
const input = el.shadowRoot?.querySelector(
|
|
28
28
|
'input'
|
|
@@ -30,7 +30,7 @@ describe('moni-switch', () => {
|
|
|
30
30
|
expect(input.getAttribute('role')).toBe('switch');
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
it('input
|
|
33
|
+
it('el input está contenido en el label (para el manejo de clics)', async () => {
|
|
34
34
|
await el.updateComplete;
|
|
35
35
|
const input = el.shadowRoot?.querySelector(
|
|
36
36
|
'input'
|
|
@@ -39,7 +39,7 @@ describe('moni-switch', () => {
|
|
|
39
39
|
expect(label?.contains(input)).toBe(true);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
it('
|
|
42
|
+
it('reenvía el estado checked al input', async () => {
|
|
43
43
|
el.checked = true;
|
|
44
44
|
await el.updateComplete;
|
|
45
45
|
const input = el.shadowRoot?.querySelector(
|
|
@@ -52,14 +52,14 @@ describe('moni-switch', () => {
|
|
|
52
52
|
expect(input.checked).toBe(false);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
it('
|
|
55
|
+
it('renderiza un span de label cuando se establece el atributo label', async () => {
|
|
56
56
|
el.label = 'Wi-Fi';
|
|
57
57
|
await el.updateComplete;
|
|
58
58
|
const label = el.shadowRoot?.querySelector('.label');
|
|
59
59
|
expect(label?.textContent?.trim()).toBe('Wi-Fi');
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
it('
|
|
62
|
+
it('reenvía disabled al input', async () => {
|
|
63
63
|
el.disabled = true;
|
|
64
64
|
await el.updateComplete;
|
|
65
65
|
const input = el.shadowRoot?.querySelector(
|
|
@@ -68,13 +68,13 @@ describe('moni-switch', () => {
|
|
|
68
68
|
expect(input.disabled).toBe(true);
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
it('
|
|
71
|
+
it('refleja el atributo checked', async () => {
|
|
72
72
|
el.checked = true;
|
|
73
73
|
await el.updateComplete;
|
|
74
74
|
expect(el.hasAttribute('checked')).toBe(true);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
it('
|
|
77
|
+
it('renderiza iconos de cierre y verificación cuando icon=true', async () => {
|
|
78
78
|
el.icon = true;
|
|
79
79
|
await el.updateComplete;
|
|
80
80
|
const icons = el.shadowRoot?.querySelectorAll('span > i');
|
|
@@ -84,14 +84,14 @@ describe('moni-switch', () => {
|
|
|
84
84
|
expect(icons?.[1].textContent).toBe('check');
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
it('
|
|
87
|
+
it('no renderiza un icono de pulgar cuando icon=false', async () => {
|
|
88
88
|
el.icon = false;
|
|
89
89
|
await el.updateComplete;
|
|
90
90
|
const icons = el.shadowRoot?.querySelectorAll('span > i');
|
|
91
91
|
expect(icons?.length).toBe(0);
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
it('
|
|
94
|
+
it('reenvía name al input', async () => {
|
|
95
95
|
el.name = 'wifi';
|
|
96
96
|
await el.updateComplete;
|
|
97
97
|
const input = el.shadowRoot?.querySelector(
|
|
@@ -100,7 +100,7 @@ describe('moni-switch', () => {
|
|
|
100
100
|
expect(input.name).toBe('wifi');
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
it('
|
|
103
|
+
it('reenvía value al input', async () => {
|
|
104
104
|
el.value = 'on';
|
|
105
105
|
await el.updateComplete;
|
|
106
106
|
const input = el.shadowRoot?.querySelector(
|
|
@@ -109,7 +109,7 @@ describe('moni-switch', () => {
|
|
|
109
109
|
expect(input.value).toBe('on');
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
it('input
|
|
112
|
+
it('el input se renderiza dentro del label del switch', async () => {
|
|
113
113
|
await el.updateComplete;
|
|
114
114
|
const input = el.shadowRoot?.querySelector('input');
|
|
115
115
|
expect(input).toBeTruthy();
|
|
@@ -12,125 +12,136 @@ import { MoniElement, sharedStyles } from './_base/index.js';
|
|
|
12
12
|
import './moni-icon.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Material Design 3 Switch
|
|
15
|
+
* Componente Material Design 3 Switch (Interruptor).
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* Los switches alternan el estado de una configuración individual entre encendido y apagado.
|
|
18
|
+
* Son el equivalente binario encendido/apagado de una casilla de verificación (checkbox),
|
|
19
|
+
* pero optimizados para alternar un solo estado en lugar de seleccionar de una lista.
|
|
20
20
|
*
|
|
21
|
-
* **
|
|
21
|
+
* **Referencia a la especificación M3:** `m3-docs/components/switch/specs.md`
|
|
22
22
|
*
|
|
23
|
-
* **M3
|
|
24
|
-
* - Track: 52dp × 32dp, 2dp
|
|
25
|
-
* -
|
|
26
|
-
* - State layer:
|
|
27
|
-
* -
|
|
23
|
+
* **Medidas M3:**
|
|
24
|
+
* - Pista (Track): 52dp × 32dp, borde de 2dp, forma de píldora con radio completo.
|
|
25
|
+
* - Control (Thumb): 16dp no seleccionado → 24dp seleccionado → 28dp presionado.
|
|
26
|
+
* - Capa de estado (State layer): ripple circular de 40dp en estados de hover/focus/pressed.
|
|
27
|
+
* - Icono (opcional): icono de 16dp renderizado dentro del control (thumb) cuando `icon` está activado.
|
|
28
28
|
*
|
|
29
|
-
* **
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* `<span>`
|
|
33
|
-
*
|
|
34
|
-
*
|
|
29
|
+
* **Arquitectura visual:**
|
|
30
|
+
* Al igual que `<moni-checkbox>`, el `<input type="checkbox" role="switch">` nativo
|
|
31
|
+
* ocupa espacio real en el diseño pero está oculto visualmente mediante `opacity: 0`. Dos
|
|
32
|
+
* pseudo-elementos de un `<span>` renderizan la pista (`::after`) y el control (`::before`).
|
|
33
|
+
* Cuando `icon=true`, los elementos `<i>` renderizan los glifos `close` y `check` dentro
|
|
34
|
+
* del control, y su visibilidad se alterna mediante CSS en base al estado verificado (checked).
|
|
35
35
|
*
|
|
36
|
-
* @fires change -
|
|
37
|
-
*
|
|
36
|
+
* @fires change - Burbujea y está compuesto. Se dispara cuando el switch se alterna.
|
|
37
|
+
* Lee `element.checked` para obtener el nuevo estado.
|
|
38
38
|
*
|
|
39
39
|
* @example
|
|
40
40
|
* ```html
|
|
41
|
-
* <moni-switch label="
|
|
42
|
-
* <moni-switch icon checked label="
|
|
41
|
+
* <moni-switch label="Modo oscuro" name="dark-mode"></moni-switch>
|
|
42
|
+
* <moni-switch icon checked label="Notificaciones"></moni-switch>
|
|
43
43
|
*
|
|
44
44
|
* <script>
|
|
45
45
|
* document.querySelector('moni-switch').addEventListener('change', (e) => {
|
|
46
|
-
* console.log('
|
|
46
|
+
* console.log('activado:', e.target.checked);
|
|
47
47
|
* });
|
|
48
48
|
* </script>
|
|
49
49
|
* ```
|
|
50
50
|
*
|
|
51
|
-
* @csspart switch -
|
|
51
|
+
* @csspart switch - El elemento `<label>` exterior que contiene el switch.
|
|
52
52
|
*/
|
|
53
53
|
@customElement('moni-switch')
|
|
54
54
|
export class MoniSwitch extends MoniElement {
|
|
55
|
+
static formAssociated = true;
|
|
56
|
+
private _internals: ElementInternals;
|
|
57
|
+
|
|
58
|
+
constructor() {
|
|
59
|
+
super();
|
|
60
|
+
this._internals = this.attachInternals();
|
|
61
|
+
}
|
|
62
|
+
|
|
55
63
|
/**
|
|
56
|
-
*
|
|
64
|
+
* Etiqueta de texto mostrada a la derecha del switch.
|
|
57
65
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
66
|
+
* Cuando no está vacía, se renderiza como un span de texto con padding después de la pista.
|
|
67
|
+
* Cuando está vacía, se renderiza el slot por defecto, permitiendo etiquetas HTML personalizadas.
|
|
60
68
|
*
|
|
61
69
|
* @default ''
|
|
62
70
|
*/
|
|
63
71
|
@property({ reflect: true }) label = '';
|
|
64
72
|
|
|
65
73
|
/**
|
|
66
|
-
*
|
|
74
|
+
* Indica si el switch está en el estado "encendido" (marcado/checked).
|
|
67
75
|
*
|
|
68
|
-
*
|
|
69
|
-
* -
|
|
70
|
-
* -
|
|
71
|
-
* -
|
|
76
|
+
* Cuando es `true`:
|
|
77
|
+
* - La pista se llena con el color `--primary`.
|
|
78
|
+
* - El control (thumb) crece de 16dp a 24dp y se desliza hacia el borde final (trailing).
|
|
79
|
+
* - El color del control cambia a `--on-primary`.
|
|
72
80
|
*
|
|
73
81
|
* @default false
|
|
74
82
|
*/
|
|
75
83
|
@property({ type: Boolean, reflect: true }) checked = false;
|
|
76
84
|
|
|
77
85
|
/**
|
|
78
|
-
*
|
|
86
|
+
* Cuando es `true`, el switch no es interactivo y se renderiza al 50% de opacidad.
|
|
79
87
|
*
|
|
80
88
|
* @default false
|
|
81
89
|
*/
|
|
82
90
|
@property({ type: Boolean, reflect: true }) disabled = false;
|
|
83
91
|
|
|
84
92
|
/**
|
|
85
|
-
*
|
|
93
|
+
* Cuando es `true`, renderiza glifos de iconos dentro del control (thumb).
|
|
86
94
|
*
|
|
87
|
-
*
|
|
88
|
-
* - Unchecked
|
|
89
|
-
* - Checked
|
|
95
|
+
* Utiliza ligaduras de Material Symbols:
|
|
96
|
+
* - Estado desmarcado (Unchecked): icono `close`.
|
|
97
|
+
* - Estado marcado (Checked): icono `check`.
|
|
90
98
|
*
|
|
91
|
-
*
|
|
99
|
+
* El tamaño del icono (16dp) se establece a través de la propiedad CSS `--_thumb`.
|
|
92
100
|
*
|
|
93
101
|
* @default false
|
|
94
102
|
*/
|
|
95
103
|
@property({ type: Boolean, reflect: true }) icon = false;
|
|
96
104
|
|
|
97
105
|
/**
|
|
98
|
-
*
|
|
106
|
+
* Reenviado al atributo nativo `<input name>` para participar en formularios.
|
|
99
107
|
*
|
|
100
108
|
* @default ''
|
|
101
109
|
*/
|
|
102
110
|
@property({ reflect: true }) name = '';
|
|
103
111
|
|
|
104
112
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
113
|
+
* Reenviado al atributo nativo `<input value>`.
|
|
114
|
+
* El valor enviado en un formulario cuando este switch está marcado.
|
|
107
115
|
*
|
|
108
116
|
* @default ''
|
|
109
117
|
*/
|
|
110
118
|
@property({ reflect: true }) value = '';
|
|
111
119
|
|
|
112
|
-
/**
|
|
120
|
+
/** Referencia directa al elemento input nativo para acceso programático. */
|
|
113
121
|
@query('input') private _input!: HTMLInputElement;
|
|
114
122
|
|
|
115
123
|
/**
|
|
116
|
-
*
|
|
124
|
+
* Sincroniza `checked` y `disabled` de forma imperativa en el input nativo después del renderizado.
|
|
117
125
|
*
|
|
118
|
-
* @param changed -
|
|
126
|
+
* @param changed - Mapa de los nombres de las propiedades modificadas a sus valores anteriores.
|
|
119
127
|
*/
|
|
120
128
|
override updated(changed: Map<string, unknown>) {
|
|
121
129
|
if (this._input) {
|
|
122
|
-
if (changed.has('checked'))
|
|
130
|
+
if (changed.has('checked')) {
|
|
131
|
+
this._input.checked = this.checked;
|
|
132
|
+
this._internals?.setFormValue?.(this.checked ? (this.value || 'on') : null);
|
|
133
|
+
}
|
|
123
134
|
if (changed.has('disabled')) this._input.disabled = this.disabled;
|
|
124
135
|
}
|
|
125
136
|
}
|
|
126
137
|
|
|
127
138
|
/**
|
|
128
|
-
*
|
|
139
|
+
* Maneja el evento nativo `change` del input.
|
|
129
140
|
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
141
|
+
* Actualiza `this.checked` y vuelve a despachar un evento `'change'` compuesto
|
|
142
|
+
* para que burbujee a través de los límites del shadow DOM.
|
|
132
143
|
*
|
|
133
|
-
* @param e -
|
|
144
|
+
* @param e - El evento nativo `change` desde el `<input>` oculto.
|
|
134
145
|
*/
|
|
135
146
|
private _onChange(e: Event) {
|
|
136
147
|
this.checked = (e.target as HTMLInputElement).checked;
|
|
@@ -300,6 +311,14 @@ export class MoniSwitch extends MoniElement {
|
|
|
300
311
|
`
|
|
301
312
|
];
|
|
302
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Renderiza el switch como un `<input type="checkbox">` oculto + pista visual mediante `<span>`.
|
|
316
|
+
*
|
|
317
|
+
* El input oculto ocupa espacio de diseño real (para el área de clic/hit area) pero tiene opacidad 0;
|
|
318
|
+
* el `<span>` proporciona la píldora visual y el control (thumb) (vía `::before`/`::after`).
|
|
319
|
+
* La vinculación `.checked` asegura que el estado del input en el DOM se mantenga sincronizado con
|
|
320
|
+
* `this.checked`.
|
|
321
|
+
*/
|
|
303
322
|
override render() {
|
|
304
323
|
return html`<label class=${this.icon ? 'switch icon' : 'switch'} part="switch">
|
|
305
324
|
<input
|
|
@@ -21,31 +21,31 @@ describe('moni-tab indicator-size', () => {
|
|
|
21
21
|
tabs.remove();
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
it('
|
|
24
|
+
it('refleja indicatorSize en el host', async () => {
|
|
25
25
|
tabs.indicatorSize = 'min';
|
|
26
26
|
await tabs.updateComplete;
|
|
27
27
|
expect(tabs.getAttribute('indicator-size')).toBe('min');
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
it('
|
|
30
|
+
it('refleja indicatorSize="max" en el host', async () => {
|
|
31
31
|
tabs.indicatorSize = 'max';
|
|
32
32
|
await tabs.updateComplete;
|
|
33
33
|
expect(tabs.getAttribute('indicator-size')).toBe('max');
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
it('
|
|
36
|
+
it('refleja indicatorSize="default" en el host', async () => {
|
|
37
37
|
tabs.indicatorSize = 'default';
|
|
38
38
|
await tabs.updateComplete;
|
|
39
39
|
expect(tabs.getAttribute('indicator-size')).toBe('default');
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
it('vertical
|
|
42
|
+
it('la orientación vertical se refleja en el host de las pestañas (tabs)', async () => {
|
|
43
43
|
tabs.vertical = true;
|
|
44
44
|
await tabs.updateComplete;
|
|
45
45
|
expect(tabs.hasAttribute('vertical')).toBe(true);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
it('
|
|
48
|
+
it('el estado activo de la pestaña refleja aria-selected=true', async () => {
|
|
49
49
|
tab.active = true;
|
|
50
50
|
await tab.updateComplete;
|
|
51
51
|
const a = tab.shadowRoot?.querySelector('a');
|
|
@@ -11,35 +11,35 @@ import { MoniElement, sharedStyles } from './_base/index.js';
|
|
|
11
11
|
import './moni-icon.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Material Design 3 Tab
|
|
14
|
+
* Componente Material Design 3 Tab (Pestaña).
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* `<moni-tabs
|
|
18
|
-
*
|
|
16
|
+
* Un elemento interactivo de pestaña individual diseñado para ser colocado dentro de un
|
|
17
|
+
* contenedor `<moni-tabs>`. Las pestañas organizan el contenido en diferentes pantallas,
|
|
18
|
+
* conjuntos de datos y otras interacciones.
|
|
19
19
|
*
|
|
20
|
-
* **
|
|
20
|
+
* **Referencia a la especificación M3:** `m3-docs/components/tabs/specs.md`
|
|
21
21
|
*
|
|
22
|
-
* **
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `vertical`
|
|
27
|
-
*
|
|
22
|
+
* **Diseño visual e interacción:**
|
|
23
|
+
* Internamente se renderiza como un elemento `<a>` para soportar el comportamiento nativo
|
|
24
|
+
* de enlace cuando se proporciona un `href`, pero se comporta visualmente como un botón de pestaña.
|
|
25
|
+
* Muestra una etiqueta de texto y un icono opcional de Material. Si el `<moni-tabs>` padre tiene el
|
|
26
|
+
* atributo `vertical` configurado, el diseño se ajusta automáticamente para apilar el icono
|
|
27
|
+
* encima del texto.
|
|
28
28
|
*
|
|
29
|
-
* **
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* **Estado:**
|
|
30
|
+
* El atributo `active` resalta la pestaña, aplicando el color principal (primary) al
|
|
31
|
+
* texto y renderizando la línea indicadora activa (manejado vía CSS en el contenedor
|
|
32
|
+
* padre o mediante pseudo-elementos).
|
|
33
33
|
*
|
|
34
34
|
* @example
|
|
35
35
|
* ```html
|
|
36
36
|
* <moni-tabs>
|
|
37
|
-
* <moni-tab active icon="home" label="
|
|
38
|
-
* <moni-tab icon="settings" label="
|
|
37
|
+
* <moni-tab active icon="home" label="Inicio"></moni-tab>
|
|
38
|
+
* <moni-tab icon="settings" label="Ajustes" href="/settings"></moni-tab>
|
|
39
39
|
* </moni-tabs>
|
|
40
40
|
* ```
|
|
41
41
|
*
|
|
42
|
-
* @csspart tab -
|
|
42
|
+
* @csspart tab - El elemento `<a>` interno que actúa como botón de pestaña.
|
|
43
43
|
*/
|
|
44
44
|
@customElement('moni-tab')
|
|
45
45
|
export class MoniTab extends MoniElement {
|
|
@@ -113,8 +113,8 @@ export class MoniTab extends MoniElement {
|
|
|
113
113
|
block-size: auto;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
/* M3 indicator-size='min': 24dp
|
|
117
|
-
|
|
116
|
+
/* M3 indicator-size='min': 24dp de altura (centrado en la pestaña) con
|
|
117
|
+
radio superior/inferior de 8dp — coincide con la especificación M3 § Indicator anatomy. */
|
|
118
118
|
:host-context(moni-tabs[indicator-size='min']) a.active::before {
|
|
119
119
|
margin: 0 auto;
|
|
120
120
|
max-inline-size: min(100%, 4rem);
|
|
@@ -130,13 +130,21 @@ export class MoniTab extends MoniElement {
|
|
|
130
130
|
border-radius: 0.5rem;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
/* M3 indicator-size='max':
|
|
133
|
+
/* M3 indicator-size='max': altura completa de la pestaña, sin redondeo. */
|
|
134
134
|
:host-context(moni-tabs[indicator-size='max']) a.active::before {
|
|
135
135
|
border-radius: 0;
|
|
136
136
|
}
|
|
137
137
|
`
|
|
138
138
|
];
|
|
139
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Renderiza el botón de pestaña con un icono opcional y etiqueta.
|
|
142
|
+
*
|
|
143
|
+
* El botón de pestaña es un elemento `<a>` con `aria-selected` para
|
|
144
|
+
* cumplimiento WCAG. La clase `active` controla la transición de subrayado
|
|
145
|
+
* del indicador CSS (a través del pseudo-elemento `::before`). El `<moni-icon>`
|
|
146
|
+
* opcional se renderiza antes del texto de la etiqueta cuando se establece `icon`.
|
|
147
|
+
*/
|
|
140
148
|
override render() {
|
|
141
149
|
return html`<a
|
|
142
150
|
href=${this.href || '#'}
|