@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
|
@@ -44,7 +44,7 @@ type Placement =
|
|
|
44
44
|
| 'viewport-bottom-right';
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
47
|
+
* Estado interno de coordenadas y estilos del objetivo de morphing antes de la animación.
|
|
48
48
|
*
|
|
49
49
|
* @internal
|
|
50
50
|
*/
|
|
@@ -56,7 +56,7 @@ interface TargetState {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
59
|
+
* Referencias a los elementos internos de icono y texto animados durante el morphing.
|
|
60
60
|
*
|
|
61
61
|
* @internal
|
|
62
62
|
*/
|
|
@@ -66,49 +66,49 @@ interface MorphElements {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* Material Design 3 Morph Modal
|
|
69
|
+
* Componente Material Design 3 Morph Modal.
|
|
70
70
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* modal
|
|
71
|
+
* Un diálogo altamente interactivo que usa animaciones GSAP FLIP (First, Last, Invert, Play)
|
|
72
|
+
* para "transformar" (morph) sin problemas cualquier elemento clickeado en la página en una superficie
|
|
73
|
+
* modal completa, y transformarlo de vuelta cuando se cierra.
|
|
74
74
|
*
|
|
75
|
-
* **
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
75
|
+
* **Coreografía de movimiento:**
|
|
76
|
+
* Este componente implementa el patrón complejo de transformación de contenedor de M3. Cuando
|
|
77
|
+
* se activa, el elemento origen se expande visualmente hacia el modal. El color de fondo,
|
|
78
|
+
* el radio del borde y la tipografía del modal se desvanecen y se funden
|
|
79
|
+
* perfectamente con el elemento de origen.
|
|
80
80
|
*
|
|
81
|
-
* **
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
81
|
+
* **Mecanismo de activación:**
|
|
82
|
+
* Los consumidores deben proporcionar la propiedad `triggerEvent` que contenga el `Event`
|
|
83
|
+
* de puntero/clic original que inició la acción de apertura. El componente extrae
|
|
84
|
+
* el `event.target` (o usa `clientX/Y` como respaldo) para determinar las coordenadas
|
|
85
|
+
* de origen exactas para la animación GSAP FLIP.
|
|
86
86
|
*
|
|
87
|
-
* **
|
|
88
|
-
*
|
|
89
|
-
* `Escape`
|
|
90
|
-
*
|
|
87
|
+
* **Comportamiento del diálogo:**
|
|
88
|
+
* Internamente, utiliza el elemento nativo `<dialog>`. Atrapa el foco, soporta
|
|
89
|
+
* `Escape` para cerrar, y maneja clics en el fondo/scrim (que activan la
|
|
90
|
+
* animación de morphing inverso antes de cerrar realmente el diálogo nativo).
|
|
91
91
|
*
|
|
92
92
|
* @example
|
|
93
93
|
* ```html
|
|
94
|
-
* <moni-morph-modal id="myModal" title="
|
|
95
|
-
* <p>
|
|
94
|
+
* <moni-morph-modal id="myModal" title="Detalles">
|
|
95
|
+
* <p>Este modal se transformó desde el botón que acabas de clickear.</p>
|
|
96
96
|
* </moni-morph-modal>
|
|
97
97
|
*
|
|
98
|
-
* <moni-button id="openBtn">
|
|
98
|
+
* <moni-button id="openBtn">Abrir Detalles</moni-button>
|
|
99
99
|
*
|
|
100
100
|
* <script>
|
|
101
101
|
* const modal = document.getElementById('myModal');
|
|
102
102
|
* document.getElementById('openBtn').addEventListener('click', (e) => {
|
|
103
|
-
* modal.triggerEvent = e; //
|
|
103
|
+
* modal.triggerEvent = e; // Pasa el evento para que sepa desde dónde transformarse
|
|
104
104
|
* modal.open = true;
|
|
105
105
|
* });
|
|
106
106
|
* </script>
|
|
107
107
|
* ```
|
|
108
108
|
*
|
|
109
|
-
* @slot default -
|
|
110
|
-
* @slot header -
|
|
111
|
-
* @slot actions -
|
|
109
|
+
* @slot default - El contenido del cuerpo principal del modal.
|
|
110
|
+
* @slot header - Contenido de encabezado personalizado (sobrescribe el atributo `title`).
|
|
111
|
+
* @slot actions - Botones de acción mostrados en la parte inferior derecha.
|
|
112
112
|
*/
|
|
113
113
|
|
|
114
114
|
const litBool = {
|
|
@@ -118,31 +118,123 @@ const litBool = {
|
|
|
118
118
|
|
|
119
119
|
@customElement('moni-morph-modal')
|
|
120
120
|
export class MoniMorphModal extends MoniElement {
|
|
121
|
+
/**
|
|
122
|
+
* Selector del elemento disparador desde el cual transformarse (ej. '#fab' o '.my-button').
|
|
123
|
+
* Si se omite, el modal aún puede activarse programáticamente estableciendo `open = true`.
|
|
124
|
+
* @type {string}
|
|
125
|
+
*/
|
|
121
126
|
@property({ reflect: true }) target = '';
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Controla el estado abierto/cerrado del modal.
|
|
130
|
+
* Puede establecerse directamente, o se gestiona internamente cuando se hace clic en el elemento `target`.
|
|
131
|
+
* @type {boolean}
|
|
132
|
+
*/
|
|
122
133
|
@property({ type: Boolean, reflect: true, converter: litBool }) open = false;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Si es true, renderiza un fondo y atrapa el foco, actuando como un verdadero diálogo modal.
|
|
137
|
+
* @type {boolean}
|
|
138
|
+
* @default true
|
|
139
|
+
*/
|
|
123
140
|
@property({ type: Boolean, reflect: true, converter: litBool }) modal = true;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Posición preferida del modal en relación con la ventana gráfica.
|
|
144
|
+
* @type {Placement}
|
|
145
|
+
* @default 'center'
|
|
146
|
+
*/
|
|
124
147
|
@property({ reflect: true }) placement: Placement = 'center';
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* El ancho del modal cuando está completamente expandido.
|
|
151
|
+
* @type {string}
|
|
152
|
+
* @default '22rem'
|
|
153
|
+
*/
|
|
125
154
|
@property({ reflect: true, attribute: 'expanded-width' }) expandedWidth =
|
|
126
155
|
'22rem';
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* La altura del modal cuando está completamente expandido.
|
|
159
|
+
* @type {string}
|
|
160
|
+
* @default '18rem'
|
|
161
|
+
*/
|
|
127
162
|
@property({ reflect: true, attribute: 'expanded-height' }) expandedHeight =
|
|
128
163
|
'18rem';
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Si es true, hacer clic fuera del modal lo cerrará.
|
|
167
|
+
* @type {boolean}
|
|
168
|
+
* @default true
|
|
169
|
+
*/
|
|
129
170
|
@property({ type: Boolean, reflect: true, attribute: 'close-on-click-outside', converter: litBool })
|
|
130
171
|
closeOnClickOutside = true;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Si es true, presionar la tecla Escape cerrará el modal.
|
|
175
|
+
* @type {boolean}
|
|
176
|
+
* @default true
|
|
177
|
+
*/
|
|
131
178
|
@property({ type: Boolean, reflect: true, attribute: 'close-on-esc', converter: litBool })
|
|
132
179
|
closeOnEsc = true;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Si es true, muestra un botón de icono de cierre por defecto en el encabezado.
|
|
183
|
+
* @type {boolean}
|
|
184
|
+
*/
|
|
133
185
|
@property({ type: Boolean, reflect: true, attribute: 'show-close-button', converter: litBool })
|
|
134
186
|
showCloseButton = false;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Si es true, intenta fundir/transformar una etiqueta del disparador hacia el encabezado del modal.
|
|
190
|
+
* @type {boolean}
|
|
191
|
+
*/
|
|
135
192
|
@property({ type: Boolean, reflect: true, attribute: 'morph-label', converter: litBool })
|
|
136
193
|
morphLabel = false;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Un selector CSS personalizado utilizado para encontrar el elemento de etiqueta específico dentro del target a transformar.
|
|
197
|
+
* @type {string}
|
|
198
|
+
*/
|
|
137
199
|
@property({ reflect: true, attribute: 'morph-label-selector' })
|
|
138
200
|
morphLabelSelector = '';
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Si es true, renderiza un fondo atenuado detrás del modal.
|
|
204
|
+
* @type {boolean}
|
|
205
|
+
* @default true
|
|
206
|
+
*/
|
|
139
207
|
@property({ type: Boolean, reflect: true, attribute: 'has-backdrop', converter: litBool })
|
|
140
208
|
hasBackdrop = true;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Si es true, oculta el elemento disparador (target) mientras el modal está abierto.
|
|
212
|
+
* @type {boolean}
|
|
213
|
+
*/
|
|
141
214
|
@property({ type: Boolean, reflect: true, attribute: 'hide-target', converter: litBool })
|
|
142
215
|
hideTarget = false;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Si es true, fuerza al modal a cubrir visualmente la ubicación original del target antes de expandirse.
|
|
219
|
+
* @type {boolean}
|
|
220
|
+
*/
|
|
143
221
|
@property({ type: Boolean, reflect: true, attribute: 'cover-target', converter: litBool })
|
|
144
222
|
coverTarget = false;
|
|
145
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Si es true, el modal calcula automáticamente su tamaño final basándose en el contenido interno (ignora expandedWidth y expandedHeight).
|
|
226
|
+
* @type {boolean}
|
|
227
|
+
*/
|
|
228
|
+
@property({ type: Boolean, reflect: true, attribute: 'auto-size', converter: litBool })
|
|
229
|
+
autoSize = false;
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Si es true, aplica un efecto de desenfoque (blur) al contenido durante la animación de entrada y salida.
|
|
233
|
+
* @type {boolean}
|
|
234
|
+
*/
|
|
235
|
+
@property({ type: Boolean, reflect: true, attribute: 'blur-content', converter: litBool })
|
|
236
|
+
blurContent = false;
|
|
237
|
+
|
|
146
238
|
@query('.panel') private _panel!: HTMLDivElement;
|
|
147
239
|
@query('.panel-inner') private _inner!: HTMLDivElement;
|
|
148
240
|
@query('.backdrop') private _backdrop!: HTMLDivElement;
|
|
@@ -161,8 +253,20 @@ export class MoniMorphModal extends MoniElement {
|
|
|
161
253
|
private static _openStack: MoniMorphModal[] = [];
|
|
162
254
|
private _targetStylesCache = new Map<HTMLElement, { opacity: string; color: string }>();
|
|
163
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Handler encapsulado para el evento click en el trigger.
|
|
258
|
+
* Dispara la animación de apertura.
|
|
259
|
+
*/
|
|
164
260
|
private _onTargetClick = () => this.show();
|
|
261
|
+
/**
|
|
262
|
+
* Handler encapsulado para interceptar teclas a nivel global (ej: Escape).
|
|
263
|
+
*/
|
|
165
264
|
private _onDocKeydown = (e: KeyboardEvent) => this._handleDocKeydown(e);
|
|
265
|
+
/**
|
|
266
|
+
* Handler de clics globales para detectar interacciones fuera del modal (Click outside).
|
|
267
|
+
* Sólo cierra el modal si `closeOnClickOutside` es true y el usuario no hizo clic
|
|
268
|
+
* dentro de las dimensiones del panel animado ni del botón original.
|
|
269
|
+
*/
|
|
166
270
|
private _onDocClick = (e: MouseEvent) => {
|
|
167
271
|
if (!this.open || !this._visible || !this.closeOnClickOutside) return;
|
|
168
272
|
if (e.composedPath().includes(this._panel)) return;
|
|
@@ -194,6 +298,12 @@ export class MoniMorphModal extends MoniElement {
|
|
|
194
298
|
super.disconnectedCallback();
|
|
195
299
|
}
|
|
196
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Hook del ciclo de vida reactivo (Lit).
|
|
303
|
+
* Observa cambios asíncronos en el atributo `target` para re-bindear el disparador,
|
|
304
|
+
* y reacciona a cambios programáticos de la propiedad `open` para ejecutar
|
|
305
|
+
* imperativamente la animación de apertura (`show`) o cierre (`hide`).
|
|
306
|
+
*/
|
|
197
307
|
override updated(changed: Map<string, unknown>) {
|
|
198
308
|
super.updated(changed);
|
|
199
309
|
if (changed.has('target')) {
|
|
@@ -205,15 +315,21 @@ export class MoniMorphModal extends MoniElement {
|
|
|
205
315
|
}
|
|
206
316
|
}
|
|
207
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Busca en el DOM el elemento que dispara el modal (el trigger) basándose en la propiedad `target`.
|
|
320
|
+
* Establece el listener de click que iniciará la animación de morphing.
|
|
321
|
+
*/
|
|
208
322
|
private _resolveTarget(): void {
|
|
209
323
|
if (!this.target) return;
|
|
210
324
|
|
|
211
325
|
const el = document.querySelector<HTMLElement>(this.target);
|
|
212
326
|
if (!el) {
|
|
213
|
-
|
|
327
|
+
// Es normal que no se encuentre el target si está condicionado por un #if en Svelte
|
|
328
|
+
// Se intentará resolver nuevamente al llamar a show()
|
|
214
329
|
return;
|
|
215
330
|
}
|
|
216
331
|
|
|
332
|
+
// Si el target cambió dinámicamente, limpiamos el listener anterior para evitar memory leaks
|
|
217
333
|
if (this._targetEl && this._targetEl !== el) {
|
|
218
334
|
this._targetEl.removeEventListener('click', this._onTargetClick);
|
|
219
335
|
}
|
|
@@ -221,8 +337,16 @@ export class MoniMorphModal extends MoniElement {
|
|
|
221
337
|
this._targetEl.addEventListener('click', this._onTargetClick);
|
|
222
338
|
}
|
|
223
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Dispara la apertura del modal y coordina la animación de FLIP (First, Last, Invert, Play) / Morphing.
|
|
342
|
+
* 1. Oculta visualmente el target original.
|
|
343
|
+
* 2. Posiciona el modal colapsado exactamente sobre el target original (mismas dimensiones, color, y radios).
|
|
344
|
+
* 3. Expande fluidamente el modal a sus dimensiones finales mediante transformaciones CSS.
|
|
345
|
+
*/
|
|
224
346
|
show(): void {
|
|
347
|
+
// Prevenimos ejecuciones concurrentes si ya se está animando o está visible
|
|
225
348
|
if (this._isAnimating || this._visible) return;
|
|
349
|
+
|
|
226
350
|
if (!this._targetEl) this._resolveTarget();
|
|
227
351
|
if (!this._targetEl) {
|
|
228
352
|
console.warn('[moni-morph-modal] cannot show: target not found');
|
|
@@ -230,17 +354,23 @@ export class MoniMorphModal extends MoniElement {
|
|
|
230
354
|
}
|
|
231
355
|
|
|
232
356
|
this._isAnimating = true;
|
|
357
|
+
|
|
358
|
+
// Flag interno para evitar loops infinitos en el hook `updated()`
|
|
233
359
|
this._handlingOpenChange = true;
|
|
234
360
|
this.open = true;
|
|
235
361
|
this._handlingOpenChange = false;
|
|
236
362
|
|
|
363
|
+
// Apilamos el modal globalmente para manejar correctamente el z-index en modales anidados
|
|
237
364
|
MoniMorphModal._openStack.push(this);
|
|
238
365
|
this._zIndex = 1000 + MoniMorphModal._openStack.length;
|
|
366
|
+
|
|
367
|
+
// Aplicamos variables CSS inline para gestionar las transiciones del backdrop y el modal
|
|
239
368
|
this.style.zIndex = String(this._zIndex);
|
|
240
369
|
this.style.setProperty('--_z-index', String(this._zIndex));
|
|
241
370
|
this.style.setProperty('--_backdrop-duration', '0.38s');
|
|
242
371
|
this.style.setProperty('--_backdrop-ease', 'cubic-bezier(0.15, 0.85, 0.2, 1)');
|
|
243
372
|
|
|
373
|
+
// Fase "First": Ocultamos o cubrimos el trigger original según la configuración del desarrollador
|
|
244
374
|
this._hideTargetContent();
|
|
245
375
|
|
|
246
376
|
void this.updateComplete.then(async () => {
|
|
@@ -248,13 +378,20 @@ export class MoniMorphModal extends MoniElement {
|
|
|
248
378
|
this._isAnimating = false;
|
|
249
379
|
return;
|
|
250
380
|
}
|
|
381
|
+
|
|
382
|
+
// Añadimos clase para prevenir interacciones durante la animación
|
|
251
383
|
this._panel.classList.add('is-animating');
|
|
252
384
|
|
|
385
|
+
// Esperamos un frame para asegurar que el navegador aplicó el `display: block`
|
|
386
|
+
// necesario antes de poder leer las dimensiones.
|
|
253
387
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
254
388
|
|
|
389
|
+
// 1. "First": Calculamos el estado visual actual del trigger
|
|
255
390
|
const targetState = this._getTargetState(this._targetEl);
|
|
256
391
|
const panel = this._panel;
|
|
257
392
|
|
|
393
|
+
// 2. "Invert": Posicionamos el modal como un clon visual exacto del trigger.
|
|
394
|
+
// Desactivamos la sombra y forzamos el color/borde para coincidir con el botón/FAB.
|
|
258
395
|
this._applyRect(panel, targetState.rect);
|
|
259
396
|
panel.style.backgroundColor = targetState.backgroundColor;
|
|
260
397
|
panel.style.color = targetState.color;
|
|
@@ -269,23 +406,35 @@ export class MoniMorphModal extends MoniElement {
|
|
|
269
406
|
this._backdrop.style.zIndex = String(this._zIndex - 1);
|
|
270
407
|
}
|
|
271
408
|
|
|
409
|
+
// Limpiamos elementos fantasma previos si el usuario interactuó muy rápido
|
|
272
410
|
this._cleanupMorphElements();
|
|
411
|
+
|
|
412
|
+
// Respetamos la preferencia de sistema de animaciones reducidas por accesibilidad (a11y)
|
|
273
413
|
const prefersReducedMotion = window.matchMedia(
|
|
274
414
|
'(prefers-reduced-motion: reduce)'
|
|
275
415
|
).matches;
|
|
416
|
+
|
|
417
|
+
// 3. Verificamos si debemos orquestar la animación compleja del label flotante cruzado
|
|
276
418
|
let shouldMorph = this._shouldMorphLabel() && !prefersReducedMotion;
|
|
277
419
|
const morphTargets: HTMLElement[] = [];
|
|
278
420
|
let source: any = null;
|
|
279
421
|
const headerWrapper = this.shadowRoot!.querySelector('.header-morph-wrapper') as HTMLElement;
|
|
280
422
|
|
|
281
423
|
if (shouldMorph) {
|
|
424
|
+
// Preparamos la estructura del header para que el texto real entre con un fade in
|
|
282
425
|
this._animateLabelOpen();
|
|
426
|
+
|
|
427
|
+
// Buscamos el ícono y el texto dentro del botón de origen (trigger)
|
|
283
428
|
source = this._resolveMorphSource();
|
|
429
|
+
|
|
430
|
+
// Si existe un nodo de texto, creamos un elemento flotante (fantasma) en el modal
|
|
284
431
|
if (source.text) {
|
|
285
432
|
const el = this._createMorphText(source.text.content, source.text.sourceEl);
|
|
286
433
|
this._positionElement(el, source.text.rect);
|
|
287
434
|
morphTargets.push(el);
|
|
288
435
|
}
|
|
436
|
+
|
|
437
|
+
// Hacemos lo mismo si el botón original contenía un icono
|
|
289
438
|
if (source.icon) {
|
|
290
439
|
const el = this._createMorphIcon(source.icon.element);
|
|
291
440
|
this._positionElement(el, source.icon.rect);
|
|
@@ -295,9 +444,11 @@ export class MoniMorphModal extends MoniElement {
|
|
|
295
444
|
}
|
|
296
445
|
|
|
297
446
|
if (shouldMorph && headerWrapper) {
|
|
447
|
+
// Ocultamos temporalmente el contenido final del header mientras animamos los elementos flotantes
|
|
298
448
|
headerWrapper.style.opacity = '0';
|
|
299
449
|
}
|
|
300
450
|
|
|
451
|
+
// Capturamos el estado "First" del panel (tamaño pequeño de botón) y de los elementos flotantes
|
|
301
452
|
const state = Flip.getState(panel, {
|
|
302
453
|
props: 'backgroundColor,borderRadius,color,boxShadow'
|
|
303
454
|
});
|
|
@@ -305,21 +456,53 @@ export class MoniMorphModal extends MoniElement {
|
|
|
305
456
|
props: 'color,fontSize,fontWeight,lineHeight'
|
|
306
457
|
});
|
|
307
458
|
|
|
308
|
-
|
|
459
|
+
// Fase "Last": Calculamos las dimensiones expandidas del modal basándonos en la configuración
|
|
460
|
+
let naturalSize: { width: number; height: number } | undefined;
|
|
461
|
+
if (this.autoSize) {
|
|
462
|
+
const prevW = panel.style.getPropertyValue('--_panel-width');
|
|
463
|
+
const prevH = panel.style.getPropertyValue('--_panel-height');
|
|
464
|
+
const prevPanelW = panel.style.width;
|
|
465
|
+
const prevPanelH = panel.style.height;
|
|
466
|
+
|
|
467
|
+
panel.style.width = 'auto';
|
|
468
|
+
panel.style.height = 'auto';
|
|
469
|
+
panel.style.setProperty('--_panel-width', 'max-content');
|
|
470
|
+
panel.style.setProperty('--_panel-height', 'max-content');
|
|
471
|
+
|
|
472
|
+
const bodyEl = this.shadowRoot!.querySelector('.body') as HTMLElement;
|
|
473
|
+
const prevOverflow = bodyEl ? bodyEl.style.overflow : '';
|
|
474
|
+
if (bodyEl) bodyEl.style.overflow = 'visible';
|
|
475
|
+
|
|
476
|
+
// Forzamos al panel-inner a expandirse para medir su contenido real
|
|
477
|
+
naturalSize = { width: this._inner.scrollWidth, height: this._inner.scrollHeight };
|
|
478
|
+
|
|
479
|
+
panel.style.setProperty('--_panel-width', prevW);
|
|
480
|
+
panel.style.setProperty('--_panel-height', prevH);
|
|
481
|
+
panel.style.width = prevPanelW;
|
|
482
|
+
panel.style.height = prevPanelH;
|
|
483
|
+
|
|
484
|
+
if (bodyEl) bodyEl.style.overflow = prevOverflow;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const finalRect = this._computeFinalRect(targetState.rect, naturalSize);
|
|
309
488
|
panel.style.setProperty('--_panel-width', `${finalRect.width}px`);
|
|
310
489
|
panel.style.setProperty('--_panel-height', `${finalRect.height}px`);
|
|
311
490
|
this._applyRect(panel, finalRect);
|
|
491
|
+
|
|
492
|
+
// Removemos las forzadas de estilo del trigger para que herede las propias del modal
|
|
312
493
|
panel.style.backgroundColor = '';
|
|
313
494
|
panel.style.color = '';
|
|
314
495
|
panel.style.borderRadius = '';
|
|
315
496
|
panel.style.boxShadow = '';
|
|
316
497
|
|
|
317
498
|
if (shouldMorph && source) {
|
|
499
|
+
// Medimos dónde van a terminar el icono y el texto dentro del modal ya expandido
|
|
318
500
|
const textRect = this._measureHeaderTextRect();
|
|
319
501
|
const iconRect = this._measureHeaderIconRect();
|
|
320
502
|
const headerComputed = window.getComputedStyle(this._header);
|
|
321
503
|
|
|
322
504
|
if (this._morphEls.text && textRect) {
|
|
505
|
+
// Movemos el texto flotante a su destino y le pedimos que adopte los estilos del header
|
|
323
506
|
this._positionElement(this._morphEls.text, textRect);
|
|
324
507
|
this._morphEls.text.style.color = headerComputed.color || 'var(--on-surface)';
|
|
325
508
|
this._morphEls.text.style.fontSize = headerComputed.fontSize || 'inherit';
|
|
@@ -328,6 +511,7 @@ export class MoniMorphModal extends MoniElement {
|
|
|
328
511
|
}
|
|
329
512
|
|
|
330
513
|
if (this._morphEls.icon && iconRect) {
|
|
514
|
+
// Hacemos lo mismo con el icono
|
|
331
515
|
this._positionElement(this._morphEls.icon, iconRect);
|
|
332
516
|
this._morphEls.icon.style.color = headerComputed.color || 'var(--on-surface)';
|
|
333
517
|
}
|
|
@@ -341,32 +525,38 @@ export class MoniMorphModal extends MoniElement {
|
|
|
341
525
|
onComplete: () => {
|
|
342
526
|
this._isAnimating = false;
|
|
343
527
|
this._visible = true;
|
|
344
|
-
this._panel.classList.remove('is-animating');
|
|
345
528
|
this._cleanupMorphElements();
|
|
346
529
|
if (headerWrapper) headerWrapper.style.opacity = '1';
|
|
347
530
|
}
|
|
348
531
|
});
|
|
349
532
|
|
|
350
533
|
if (morphTargets.length > 0) {
|
|
351
|
-
|
|
534
|
+
if (this.blurContent) {
|
|
535
|
+
gsap.set(morphTargets, { filter: 'blur(16px)' });
|
|
536
|
+
}
|
|
537
|
+
const flipConfig: any = {
|
|
352
538
|
targets: morphTargets,
|
|
353
539
|
duration: 0.38,
|
|
354
540
|
ease: 'morph-open',
|
|
355
541
|
zIndex: this._zIndex + 10
|
|
356
|
-
}
|
|
542
|
+
};
|
|
543
|
+
if (this.blurContent) {
|
|
544
|
+
flipConfig.filter = 'blur(0px)';
|
|
545
|
+
flipConfig.clearProps = 'filter';
|
|
546
|
+
}
|
|
547
|
+
Flip.from(morphState, flipConfig);
|
|
357
548
|
}
|
|
358
549
|
|
|
359
|
-
const
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
this.shadowRoot!.querySelector('.close-btn')
|
|
364
|
-
].filter(Boolean) as HTMLElement[];
|
|
550
|
+
const body = this.shadowRoot!.querySelector('.body');
|
|
551
|
+
const footer = this.shadowRoot!.querySelector('footer');
|
|
552
|
+
const closeBtn = this.shadowRoot!.querySelector('.close-btn');
|
|
553
|
+
const fadeTargets = [body, footer, closeBtn].filter(Boolean) as HTMLElement[];
|
|
365
554
|
|
|
366
|
-
|
|
555
|
+
const initialBlur = this.blurContent ? 'blur(16px)' : 'blur(0px)';
|
|
556
|
+
gsap.set(fadeTargets, { opacity: 0, y: 8, filter: initialBlur });
|
|
367
557
|
gsap.fromTo(
|
|
368
558
|
fadeTargets,
|
|
369
|
-
{ opacity: 0, y: 8, filter:
|
|
559
|
+
{ opacity: 0, y: 8, filter: initialBlur },
|
|
370
560
|
{
|
|
371
561
|
opacity: 1,
|
|
372
562
|
y: 0,
|
|
@@ -377,12 +567,20 @@ export class MoniMorphModal extends MoniElement {
|
|
|
377
567
|
ease: 'power2.out',
|
|
378
568
|
onComplete: () => {
|
|
379
569
|
gsap.set(fadeTargets, { clearProps: 'opacity,transform,filter' });
|
|
570
|
+
this._panel.classList.remove('is-animating');
|
|
380
571
|
}
|
|
381
572
|
}
|
|
382
573
|
);
|
|
383
574
|
});
|
|
384
575
|
}
|
|
385
576
|
|
|
577
|
+
/**
|
|
578
|
+
* Solución a un caso borde de FLIP Animations:
|
|
579
|
+
* Si el `.panel` original carece de un color de fondo sólido, la interpolación
|
|
580
|
+
* de GSAP puede comportarse erráticamente. Este método "asciende" el árbol del DOM
|
|
581
|
+
* hasta encontrar un padre con un color de fondo sólido y lo aplica, garantizando
|
|
582
|
+
* que el modal no colapse de forma transparente de regreso hacia el gatillo.
|
|
583
|
+
*/
|
|
386
584
|
private _ensureSolidPanelBackground() {
|
|
387
585
|
if (!this._panel) return;
|
|
388
586
|
let bg = window.getComputedStyle(this._panel).backgroundColor;
|
|
@@ -412,20 +610,19 @@ export class MoniMorphModal extends MoniElement {
|
|
|
412
610
|
this.style.setProperty('--_backdrop-duration', '0.3s');
|
|
413
611
|
this.style.setProperty('--_backdrop-ease', 'cubic-bezier(0.4, 0, 0.2, 1)');
|
|
414
612
|
|
|
415
|
-
const
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
this.shadowRoot!.querySelector('.close-btn')
|
|
420
|
-
].filter(Boolean) as HTMLElement[];
|
|
613
|
+
const body = this.shadowRoot!.querySelector('.body');
|
|
614
|
+
const footer = this.shadowRoot!.querySelector('footer');
|
|
615
|
+
const closeBtn = this.shadowRoot!.querySelector('.close-btn');
|
|
616
|
+
const fadeTargets = [body, footer, closeBtn].filter(Boolean) as HTMLElement[];
|
|
421
617
|
const headerWrapper = this.shadowRoot!.querySelector('.header-morph-wrapper') as HTMLElement;
|
|
422
618
|
|
|
619
|
+
const targetBlur = this.blurContent ? 'blur(12px)' : 'blur(0px)';
|
|
423
620
|
gsap.fromTo(
|
|
424
621
|
fadeTargets,
|
|
425
622
|
{ opacity: 1, filter: 'blur(0px)' },
|
|
426
623
|
{
|
|
427
624
|
opacity: 0,
|
|
428
|
-
filter:
|
|
625
|
+
filter: targetBlur,
|
|
429
626
|
duration: 0.12,
|
|
430
627
|
ease: 'power2.in',
|
|
431
628
|
onComplete: () => {
|
|
@@ -478,7 +675,34 @@ export class MoniMorphModal extends MoniElement {
|
|
|
478
675
|
});
|
|
479
676
|
|
|
480
677
|
const targetState = this._getTargetState(this._targetEl!);
|
|
481
|
-
|
|
678
|
+
|
|
679
|
+
let naturalSize: { width: number; height: number } | undefined;
|
|
680
|
+
if (this.autoSize) {
|
|
681
|
+
const prevW = panel.style.getPropertyValue('--_panel-width');
|
|
682
|
+
const prevH = panel.style.getPropertyValue('--_panel-height');
|
|
683
|
+
const prevPanelW = panel.style.width;
|
|
684
|
+
const prevPanelH = panel.style.height;
|
|
685
|
+
|
|
686
|
+
panel.style.width = 'auto';
|
|
687
|
+
panel.style.height = 'auto';
|
|
688
|
+
panel.style.setProperty('--_panel-width', 'max-content');
|
|
689
|
+
panel.style.setProperty('--_panel-height', 'max-content');
|
|
690
|
+
|
|
691
|
+
const bodyEl = this.shadowRoot!.querySelector('.body') as HTMLElement;
|
|
692
|
+
const prevOverflow = bodyEl ? bodyEl.style.overflow : '';
|
|
693
|
+
if (bodyEl) bodyEl.style.overflow = 'visible';
|
|
694
|
+
|
|
695
|
+
naturalSize = { width: this._inner.scrollWidth, height: this._inner.scrollHeight };
|
|
696
|
+
|
|
697
|
+
panel.style.setProperty('--_panel-width', prevW);
|
|
698
|
+
panel.style.setProperty('--_panel-height', prevH);
|
|
699
|
+
panel.style.width = prevPanelW;
|
|
700
|
+
panel.style.height = prevPanelH;
|
|
701
|
+
|
|
702
|
+
if (bodyEl) bodyEl.style.overflow = prevOverflow;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const finalRect = this._computeFinalRect(targetState.rect, naturalSize);
|
|
482
706
|
panel.style.setProperty('--_panel-width', `${finalRect.width}px`);
|
|
483
707
|
panel.style.setProperty('--_panel-height', `${finalRect.height}px`);
|
|
484
708
|
|
|
@@ -541,12 +765,19 @@ export class MoniMorphModal extends MoniElement {
|
|
|
541
765
|
});
|
|
542
766
|
|
|
543
767
|
if (morphTargets.length > 0) {
|
|
544
|
-
|
|
768
|
+
if (this.blurContent) {
|
|
769
|
+
gsap.set(morphTargets, { filter: 'blur(0px)' });
|
|
770
|
+
}
|
|
771
|
+
const flipConfig: any = {
|
|
545
772
|
targets: morphTargets,
|
|
546
773
|
duration: 0.3,
|
|
547
774
|
ease: 'morph-close',
|
|
548
775
|
zIndex: this._zIndex + 10
|
|
549
|
-
}
|
|
776
|
+
};
|
|
777
|
+
if (this.blurContent) {
|
|
778
|
+
flipConfig.filter = 'blur(12px)';
|
|
779
|
+
}
|
|
780
|
+
Flip.from(morphState, flipConfig);
|
|
550
781
|
}
|
|
551
782
|
}
|
|
552
783
|
}
|
|
@@ -566,6 +797,12 @@ export class MoniMorphModal extends MoniElement {
|
|
|
566
797
|
this.hide();
|
|
567
798
|
}
|
|
568
799
|
|
|
800
|
+
/**
|
|
801
|
+
* Fase "First" de la animación (Cierre del Trigger):
|
|
802
|
+
* Determina cómo desaparecer u ocultar el botón original mientras el modal ocupa su lugar.
|
|
803
|
+
* Mantiene el cache de opacidades y colores originales para poder restaurarlos
|
|
804
|
+
* idénticamente al cerrar el modal (evitando saltos visuales).
|
|
805
|
+
*/
|
|
569
806
|
private _hideTargetContent() {
|
|
570
807
|
if (!this._targetEl) return;
|
|
571
808
|
|
|
@@ -594,6 +831,10 @@ export class MoniMorphModal extends MoniElement {
|
|
|
594
831
|
}
|
|
595
832
|
}
|
|
596
833
|
|
|
834
|
+
/**
|
|
835
|
+
* Restaura puramente los estilos originales del botón Trigger tras terminar
|
|
836
|
+
* la animación de cierre, leyendo las opacidades/colores almacenados en caché.
|
|
837
|
+
*/
|
|
597
838
|
private _restoreTargetContent() {
|
|
598
839
|
if (!this._targetEl) return;
|
|
599
840
|
|
|
@@ -667,11 +908,11 @@ export class MoniMorphModal extends MoniElement {
|
|
|
667
908
|
}
|
|
668
909
|
|
|
669
910
|
private _animateLabelOpen(): void {
|
|
670
|
-
// No-op:
|
|
911
|
+
// No-op: el morphing de la etiqueta es manejado por el timeline unificado FLIP en show()
|
|
671
912
|
}
|
|
672
913
|
|
|
673
914
|
private _animateLabelClose(): void {
|
|
674
|
-
// No-op:
|
|
915
|
+
// No-op: el morphing de la etiqueta es manejado por el timeline unificado FLIP en hide()
|
|
675
916
|
}
|
|
676
917
|
|
|
677
918
|
private _createMorphText(sourceText: string, sourceEl: Element): HTMLElement {
|
|
@@ -1025,13 +1266,13 @@ export class MoniMorphModal extends MoniElement {
|
|
|
1025
1266
|
el.style.height = `${rect.height}px`;
|
|
1026
1267
|
}
|
|
1027
1268
|
|
|
1028
|
-
private _computeFinalRect(targetRect: DOMRect): DOMRect {
|
|
1269
|
+
private _computeFinalRect(targetRect: DOMRect, naturalSize?: { width: number; height: number }): DOMRect {
|
|
1029
1270
|
const vw = window.innerWidth;
|
|
1030
1271
|
const vh = window.innerHeight;
|
|
1031
1272
|
const padding = 16;
|
|
1032
1273
|
|
|
1033
|
-
let width = this._parseSize(this.expandedWidth, vw);
|
|
1034
|
-
let height = this._parseSize(this.expandedHeight, vh);
|
|
1274
|
+
let width = this.autoSize && naturalSize ? naturalSize.width : this._parseSize(this.expandedWidth, vw);
|
|
1275
|
+
let height = this.autoSize && naturalSize ? naturalSize.height : this._parseSize(this.expandedHeight, vh);
|
|
1035
1276
|
|
|
1036
1277
|
width = Math.min(width, vw - padding * 2);
|
|
1037
1278
|
height = Math.min(height, vh - padding * 2);
|
|
@@ -1206,7 +1447,7 @@ export class MoniMorphModal extends MoniElement {
|
|
|
1206
1447
|
overflow: hidden;
|
|
1207
1448
|
background-color: var(--surface-container-high);
|
|
1208
1449
|
color: var(--on-surface);
|
|
1209
|
-
border-radius: 1.75rem;
|
|
1450
|
+
border-radius: var(--moni-morph-panel-radius, 1.75rem);
|
|
1210
1451
|
box-shadow: var(--elevate2);
|
|
1211
1452
|
z-index: var(--_z-index, 100);
|
|
1212
1453
|
}
|
|
@@ -1235,13 +1476,18 @@ export class MoniMorphModal extends MoniElement {
|
|
|
1235
1476
|
|
|
1236
1477
|
.body {
|
|
1237
1478
|
flex: 1;
|
|
1238
|
-
overflow-y: auto;
|
|
1239
|
-
|
|
1240
|
-
|
|
1479
|
+
overflow-y: var(--moni-morph-body-overflow-y, auto);
|
|
1480
|
+
overflow-x: var(--moni-morph-body-overflow-x, hidden);
|
|
1481
|
+
padding: var(--moni-morph-body-padding, 0 1.5rem);
|
|
1482
|
+
scrollbar-gutter: var(--moni-morph-body-scrollbar-gutter, stable);
|
|
1241
1483
|
}
|
|
1242
1484
|
|
|
1243
1485
|
.panel.is-animating .body {
|
|
1244
|
-
overflow: hidden;
|
|
1486
|
+
overflow: hidden !important;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
.panel.is-animating .body::-webkit-scrollbar {
|
|
1490
|
+
display: none;
|
|
1245
1491
|
}
|
|
1246
1492
|
|
|
1247
1493
|
footer {
|
|
@@ -1291,6 +1537,27 @@ export class MoniMorphModal extends MoniElement {
|
|
|
1291
1537
|
`
|
|
1292
1538
|
];
|
|
1293
1539
|
|
|
1540
|
+
/**
|
|
1541
|
+
* Renderiza el caparazón del modal morph: fondo opcional + contenedor de panel.
|
|
1542
|
+
*
|
|
1543
|
+
* **Bandera `_visible`:**
|
|
1544
|
+
* `visible = this._visible || this.open` mantiene vivo el DOM mientras se reproduce
|
|
1545
|
+
* la animación de cierre de GSAP. `_visible` se establece en `true` al inicio de la apertura y
|
|
1546
|
+
* en `false` solo después de que se dispara el `onComplete` de la animación de cierre. Sin esto,
|
|
1547
|
+
* Lit eliminaría el panel del DOM antes de que la animación termine.
|
|
1548
|
+
*
|
|
1549
|
+
* **Condicional del fondo (backdrop):**
|
|
1550
|
+
* El div `.backdrop` solo se renderiza cuando `hasBackdrop=true`. Cuando `modal=false`,
|
|
1551
|
+
* el `background-color` del fondo se establece en `transparent` para que el fondo superpuesto
|
|
1552
|
+
* (scrim) sea invisible, pero los eventos del puntero aún se consumen (clic para cerrar).
|
|
1553
|
+
*
|
|
1554
|
+
* **Objetivo de morphing de GSAP:**
|
|
1555
|
+
* `.panel` es el objetivo de la animación GSAP. Al abrir: se anima desde el
|
|
1556
|
+
* `getBoundingClientRect()` del elemento anclaje (posición + tamaño) a su tamaño CSS natural
|
|
1557
|
+
* mediante `gsap.fromTo()`. Al cerrar: a la inversa. `.panel-inner` recibe una
|
|
1558
|
+
* contra-escala para evitar que el contenido interno se escale junto con el panel,
|
|
1559
|
+
* creando una ilusión física de "expandirse desde el origen".
|
|
1560
|
+
*/
|
|
1294
1561
|
override render() {
|
|
1295
1562
|
const visible = this._visible || this.open;
|
|
1296
1563
|
const headerEmpty = !this._hasHeader && !this.showCloseButton;
|