@vaadin/overlay 24.1.5 → 24.2.0-alpha10
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/package.json +10 -8
- package/src/vaadin-overlay-mixin.d.ts +81 -0
- package/src/vaadin-overlay-mixin.js +472 -0
- package/src/vaadin-overlay-stack-mixin.d.ts +22 -0
- package/src/vaadin-overlay-stack-mixin.js +103 -0
- package/src/vaadin-overlay-styles.js +66 -0
- package/src/vaadin-overlay.d.ts +3 -74
- package/src/vaadin-overlay.js +7 -611
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/overlay",
|
|
3
|
-
"version": "24.
|
|
3
|
+
"version": "24.2.0-alpha10",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
"type": "module",
|
|
22
22
|
"files": [
|
|
23
23
|
"src",
|
|
24
|
+
"!src/vaadin-lit-overlay.d.ts",
|
|
25
|
+
"!src/vaadin-lit-overlay.js",
|
|
24
26
|
"theme",
|
|
25
27
|
"vaadin-*.d.ts",
|
|
26
28
|
"vaadin-*.js"
|
|
@@ -36,17 +38,17 @@
|
|
|
36
38
|
"dependencies": {
|
|
37
39
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
38
40
|
"@polymer/polymer": "^3.0.0",
|
|
39
|
-
"@vaadin/a11y-base": "
|
|
40
|
-
"@vaadin/component-base": "
|
|
41
|
-
"@vaadin/vaadin-lumo-styles": "
|
|
42
|
-
"@vaadin/vaadin-material-styles": "
|
|
43
|
-
"@vaadin/vaadin-themable-mixin": "
|
|
41
|
+
"@vaadin/a11y-base": "24.2.0-alpha10",
|
|
42
|
+
"@vaadin/component-base": "24.2.0-alpha10",
|
|
43
|
+
"@vaadin/vaadin-lumo-styles": "24.2.0-alpha10",
|
|
44
|
+
"@vaadin/vaadin-material-styles": "24.2.0-alpha10",
|
|
45
|
+
"@vaadin/vaadin-themable-mixin": "24.2.0-alpha10"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
46
48
|
"@esm-bundle/chai": "^4.3.4",
|
|
47
|
-
"@vaadin/testing-helpers": "^0.
|
|
49
|
+
"@vaadin/testing-helpers": "^0.5.0",
|
|
48
50
|
"lit": "^2.0.0",
|
|
49
51
|
"sinon": "^13.0.2"
|
|
50
52
|
},
|
|
51
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "ca16b5f88b00ae05fb6d7c7e9874525048e389f0"
|
|
52
54
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
import type { ControllerMixinClass } from '@vaadin/component-base/src/controller-mixin.js';
|
|
8
|
+
import type { OverlayFocusMixinClass } from './vaadin-overlay-focus-mixin.js';
|
|
9
|
+
import type { OverlayStackMixinClass } from './vaadin-overlay-stack-mixin.js';
|
|
10
|
+
|
|
11
|
+
export type OverlayRenderer = (root: HTMLElement, owner: HTMLElement, model?: object) => void;
|
|
12
|
+
|
|
13
|
+
export declare function OverlayMixin<T extends Constructor<HTMLElement>>(
|
|
14
|
+
base: T,
|
|
15
|
+
): Constructor<ControllerMixinClass> &
|
|
16
|
+
Constructor<OverlayFocusMixinClass> &
|
|
17
|
+
Constructor<OverlayMixinClass> &
|
|
18
|
+
Constructor<OverlayStackMixinClass> &
|
|
19
|
+
T;
|
|
20
|
+
|
|
21
|
+
export declare class OverlayMixinClass {
|
|
22
|
+
/**
|
|
23
|
+
* Owner element passed with renderer function
|
|
24
|
+
*/
|
|
25
|
+
owner: HTMLElement | null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Custom function for rendering the content of the overlay.
|
|
29
|
+
* Receives three arguments:
|
|
30
|
+
*
|
|
31
|
+
* - `root` The root container DOM element. Append your content to it.
|
|
32
|
+
* - `owner` The host element of the renderer function.
|
|
33
|
+
* - `model` The object with the properties related with rendering.
|
|
34
|
+
*/
|
|
35
|
+
renderer: OverlayRenderer | null | undefined;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* When true the overlay has backdrop on top of content when opened.
|
|
39
|
+
*/
|
|
40
|
+
withBackdrop: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Object with properties that is passed to `renderer` function
|
|
44
|
+
*/
|
|
45
|
+
model: object | null | undefined;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* When true, the overlay is visible and attached to body.
|
|
49
|
+
*/
|
|
50
|
+
opened: boolean | null | undefined;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* When true the overlay won't disable the main content, showing
|
|
54
|
+
* it doesn't change the functionality of the user interface.
|
|
55
|
+
*/
|
|
56
|
+
modeless: boolean;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* When set to true, the overlay is hidden. This also closes the overlay
|
|
60
|
+
* immediately in case there is a closing animation in progress.
|
|
61
|
+
*/
|
|
62
|
+
hidden: boolean;
|
|
63
|
+
|
|
64
|
+
close(sourceEvent?: Event | null): void;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Requests an update for the content of the overlay.
|
|
68
|
+
* While performing the update, it invokes the renderer passed in the `renderer` property.
|
|
69
|
+
*
|
|
70
|
+
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
71
|
+
*/
|
|
72
|
+
requestContentUpdate(): void;
|
|
73
|
+
|
|
74
|
+
protected _flushAnimation(type: 'closing' | 'opening'): void;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Whether to close the overlay on outside click or not.
|
|
78
|
+
* Override this method to customize the closing logic.
|
|
79
|
+
*/
|
|
80
|
+
protected _shouldCloseOnOutsideClick(event: Event): boolean;
|
|
81
|
+
}
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js';
|
|
7
|
+
import { isIOS } from '@vaadin/component-base/src/browser-utils.js';
|
|
8
|
+
import { OverlayFocusMixin } from './vaadin-overlay-focus-mixin.js';
|
|
9
|
+
import { OverlayStackMixin } from './vaadin-overlay-stack-mixin.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @polymerMixin
|
|
13
|
+
* @mixes OverlayFocusMixin
|
|
14
|
+
* @mixes OverlayStackMixin
|
|
15
|
+
*/
|
|
16
|
+
export const OverlayMixin = (superClass) =>
|
|
17
|
+
class OverlayMixin extends OverlayFocusMixin(OverlayStackMixin(superClass)) {
|
|
18
|
+
static get properties() {
|
|
19
|
+
return {
|
|
20
|
+
/**
|
|
21
|
+
* When true, the overlay is visible and attached to body.
|
|
22
|
+
*/
|
|
23
|
+
opened: {
|
|
24
|
+
type: Boolean,
|
|
25
|
+
notify: true,
|
|
26
|
+
observer: '_openedChanged',
|
|
27
|
+
reflectToAttribute: true,
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Owner element passed with renderer function
|
|
32
|
+
* @type {HTMLElement}
|
|
33
|
+
*/
|
|
34
|
+
owner: {
|
|
35
|
+
type: Object,
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Object with properties that is passed to `renderer` function
|
|
40
|
+
*/
|
|
41
|
+
model: {
|
|
42
|
+
type: Object,
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Custom function for rendering the content of the overlay.
|
|
47
|
+
* Receives three arguments:
|
|
48
|
+
*
|
|
49
|
+
* - `root` The root container DOM element. Append your content to it.
|
|
50
|
+
* - `owner` The host element of the renderer function.
|
|
51
|
+
* - `model` The object with the properties related with rendering.
|
|
52
|
+
* @type {OverlayRenderer | null | undefined}
|
|
53
|
+
*/
|
|
54
|
+
renderer: {
|
|
55
|
+
type: Object,
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* When true the overlay won't disable the main content, showing
|
|
60
|
+
* it doesn't change the functionality of the user interface.
|
|
61
|
+
* @type {boolean}
|
|
62
|
+
*/
|
|
63
|
+
modeless: {
|
|
64
|
+
type: Boolean,
|
|
65
|
+
value: false,
|
|
66
|
+
reflectToAttribute: true,
|
|
67
|
+
observer: '_modelessChanged',
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* When set to true, the overlay is hidden. This also closes the overlay
|
|
72
|
+
* immediately in case there is a closing animation in progress.
|
|
73
|
+
* @type {boolean}
|
|
74
|
+
*/
|
|
75
|
+
hidden: {
|
|
76
|
+
type: Boolean,
|
|
77
|
+
reflectToAttribute: true,
|
|
78
|
+
observer: '_hiddenChanged',
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* When true the overlay has backdrop on top of content when opened.
|
|
83
|
+
* @type {boolean}
|
|
84
|
+
*/
|
|
85
|
+
withBackdrop: {
|
|
86
|
+
type: Boolean,
|
|
87
|
+
value: false,
|
|
88
|
+
reflectToAttribute: true,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static get observers() {
|
|
94
|
+
return ['_rendererOrDataChanged(renderer, owner, model, opened)'];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
constructor() {
|
|
98
|
+
super();
|
|
99
|
+
|
|
100
|
+
this._boundMouseDownListener = this._mouseDownListener.bind(this);
|
|
101
|
+
this._boundMouseUpListener = this._mouseUpListener.bind(this);
|
|
102
|
+
this._boundOutsideClickListener = this._outsideClickListener.bind(this);
|
|
103
|
+
this._boundKeydownListener = this._keydownListener.bind(this);
|
|
104
|
+
|
|
105
|
+
/* c8 ignore next 3 */
|
|
106
|
+
if (isIOS) {
|
|
107
|
+
this._boundIosResizeListener = () => this._detectIosNavbar();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** @protected */
|
|
112
|
+
ready() {
|
|
113
|
+
super.ready();
|
|
114
|
+
|
|
115
|
+
// Need to add dummy click listeners to this and the backdrop or else
|
|
116
|
+
// the document click event listener (_outsideClickListener) may never
|
|
117
|
+
// get invoked on iOS Safari (reproducible in <vaadin-dialog>
|
|
118
|
+
// and <vaadin-context-menu>).
|
|
119
|
+
this.addEventListener('click', () => {});
|
|
120
|
+
this.$.backdrop.addEventListener('click', () => {});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** @protected */
|
|
124
|
+
connectedCallback() {
|
|
125
|
+
super.connectedCallback();
|
|
126
|
+
|
|
127
|
+
/* c8 ignore next 3 */
|
|
128
|
+
if (this._boundIosResizeListener) {
|
|
129
|
+
this._detectIosNavbar();
|
|
130
|
+
window.addEventListener('resize', this._boundIosResizeListener);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** @protected */
|
|
135
|
+
disconnectedCallback() {
|
|
136
|
+
super.disconnectedCallback();
|
|
137
|
+
|
|
138
|
+
/* c8 ignore next 3 */
|
|
139
|
+
if (this._boundIosResizeListener) {
|
|
140
|
+
window.removeEventListener('resize', this._boundIosResizeListener);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Requests an update for the content of the overlay.
|
|
146
|
+
* While performing the update, it invokes the renderer passed in the `renderer` property.
|
|
147
|
+
*
|
|
148
|
+
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
149
|
+
*/
|
|
150
|
+
requestContentUpdate() {
|
|
151
|
+
if (this.renderer) {
|
|
152
|
+
this.renderer.call(this.owner, this, this.owner, this.model);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @param {Event=} sourceEvent
|
|
158
|
+
*/
|
|
159
|
+
close(sourceEvent) {
|
|
160
|
+
const evt = new CustomEvent('vaadin-overlay-close', {
|
|
161
|
+
bubbles: true,
|
|
162
|
+
cancelable: true,
|
|
163
|
+
detail: { sourceEvent },
|
|
164
|
+
});
|
|
165
|
+
this.dispatchEvent(evt);
|
|
166
|
+
if (!evt.defaultPrevented) {
|
|
167
|
+
this.opened = false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** @private */
|
|
172
|
+
_detectIosNavbar() {
|
|
173
|
+
/* c8 ignore next 15 */
|
|
174
|
+
if (!this.opened) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const innerHeight = window.innerHeight;
|
|
179
|
+
const innerWidth = window.innerWidth;
|
|
180
|
+
|
|
181
|
+
const landscape = innerWidth > innerHeight;
|
|
182
|
+
|
|
183
|
+
const clientHeight = document.documentElement.clientHeight;
|
|
184
|
+
|
|
185
|
+
if (landscape && clientHeight > innerHeight) {
|
|
186
|
+
this.style.setProperty('--vaadin-overlay-viewport-bottom', `${clientHeight - innerHeight}px`);
|
|
187
|
+
} else {
|
|
188
|
+
this.style.setProperty('--vaadin-overlay-viewport-bottom', '0');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** @private */
|
|
193
|
+
_addGlobalListeners() {
|
|
194
|
+
document.addEventListener('mousedown', this._boundMouseDownListener);
|
|
195
|
+
document.addEventListener('mouseup', this._boundMouseUpListener);
|
|
196
|
+
// Firefox leaks click to document on contextmenu even if prevented
|
|
197
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=990614
|
|
198
|
+
document.documentElement.addEventListener('click', this._boundOutsideClickListener, true);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** @private */
|
|
202
|
+
_removeGlobalListeners() {
|
|
203
|
+
document.removeEventListener('mousedown', this._boundMouseDownListener);
|
|
204
|
+
document.removeEventListener('mouseup', this._boundMouseUpListener);
|
|
205
|
+
document.documentElement.removeEventListener('click', this._boundOutsideClickListener, true);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** @private */
|
|
209
|
+
_rendererOrDataChanged(renderer, owner, model, opened) {
|
|
210
|
+
const ownerOrModelChanged = this._oldOwner !== owner || this._oldModel !== model;
|
|
211
|
+
this._oldModel = model;
|
|
212
|
+
this._oldOwner = owner;
|
|
213
|
+
|
|
214
|
+
const rendererChanged = this._oldRenderer !== renderer;
|
|
215
|
+
this._oldRenderer = renderer;
|
|
216
|
+
|
|
217
|
+
const openedChanged = this._oldOpened !== opened;
|
|
218
|
+
this._oldOpened = opened;
|
|
219
|
+
|
|
220
|
+
if (rendererChanged) {
|
|
221
|
+
this.innerHTML = '';
|
|
222
|
+
// Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
|
|
223
|
+
// When clearing the rendered content, this part needs to be manually disposed of.
|
|
224
|
+
// Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
|
|
225
|
+
delete this._$litPart$;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (opened && renderer && (rendererChanged || openedChanged || ownerOrModelChanged)) {
|
|
229
|
+
this.requestContentUpdate();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** @private */
|
|
234
|
+
_modelessChanged(modeless) {
|
|
235
|
+
if (!modeless) {
|
|
236
|
+
if (this.opened) {
|
|
237
|
+
this._addGlobalListeners();
|
|
238
|
+
this._enterModalState();
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
this._removeGlobalListeners();
|
|
242
|
+
this._exitModalState();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** @private */
|
|
247
|
+
_openedChanged(opened, wasOpened) {
|
|
248
|
+
if (opened) {
|
|
249
|
+
this._saveFocus();
|
|
250
|
+
|
|
251
|
+
this._animatedOpening();
|
|
252
|
+
|
|
253
|
+
afterNextRender(this, () => {
|
|
254
|
+
this._trapFocus();
|
|
255
|
+
|
|
256
|
+
const evt = new CustomEvent('vaadin-overlay-open', { bubbles: true });
|
|
257
|
+
this.dispatchEvent(evt);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
document.addEventListener('keydown', this._boundKeydownListener);
|
|
261
|
+
|
|
262
|
+
if (!this.modeless) {
|
|
263
|
+
this._addGlobalListeners();
|
|
264
|
+
}
|
|
265
|
+
} else if (wasOpened) {
|
|
266
|
+
this._resetFocus();
|
|
267
|
+
|
|
268
|
+
this._animatedClosing();
|
|
269
|
+
|
|
270
|
+
document.removeEventListener('keydown', this._boundKeydownListener);
|
|
271
|
+
|
|
272
|
+
if (!this.modeless) {
|
|
273
|
+
this._removeGlobalListeners();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** @private */
|
|
279
|
+
_hiddenChanged(hidden) {
|
|
280
|
+
if (hidden && this.hasAttribute('closing')) {
|
|
281
|
+
this._flushAnimation('closing');
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @return {boolean}
|
|
287
|
+
* @private
|
|
288
|
+
*/
|
|
289
|
+
_shouldAnimate() {
|
|
290
|
+
const style = getComputedStyle(this);
|
|
291
|
+
const name = style.getPropertyValue('animation-name');
|
|
292
|
+
const hidden = style.getPropertyValue('display') === 'none';
|
|
293
|
+
return !hidden && name && name !== 'none';
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* @param {string} type
|
|
298
|
+
* @param {Function} callback
|
|
299
|
+
* @private
|
|
300
|
+
*/
|
|
301
|
+
_enqueueAnimation(type, callback) {
|
|
302
|
+
const handler = `__${type}Handler`;
|
|
303
|
+
const listener = (event) => {
|
|
304
|
+
if (event && event.target !== this) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
callback();
|
|
308
|
+
this.removeEventListener('animationend', listener);
|
|
309
|
+
delete this[handler];
|
|
310
|
+
};
|
|
311
|
+
this[handler] = listener;
|
|
312
|
+
this.addEventListener('animationend', listener);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* @param {string} type
|
|
317
|
+
* @protected
|
|
318
|
+
*/
|
|
319
|
+
_flushAnimation(type) {
|
|
320
|
+
const handler = `__${type}Handler`;
|
|
321
|
+
if (typeof this[handler] === 'function') {
|
|
322
|
+
this[handler]();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** @private */
|
|
327
|
+
_animatedOpening() {
|
|
328
|
+
if (this.parentNode === document.body && this.hasAttribute('closing')) {
|
|
329
|
+
this._flushAnimation('closing');
|
|
330
|
+
}
|
|
331
|
+
this._attachOverlay();
|
|
332
|
+
if (!this.modeless) {
|
|
333
|
+
this._enterModalState();
|
|
334
|
+
}
|
|
335
|
+
this.setAttribute('opening', '');
|
|
336
|
+
|
|
337
|
+
if (this._shouldAnimate()) {
|
|
338
|
+
this._enqueueAnimation('opening', () => {
|
|
339
|
+
this._finishOpening();
|
|
340
|
+
});
|
|
341
|
+
} else {
|
|
342
|
+
this._finishOpening();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** @private */
|
|
347
|
+
_attachOverlay() {
|
|
348
|
+
this._placeholder = document.createComment('vaadin-overlay-placeholder');
|
|
349
|
+
this.parentNode.insertBefore(this._placeholder, this);
|
|
350
|
+
document.body.appendChild(this);
|
|
351
|
+
this.bringToFront();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** @private */
|
|
355
|
+
_finishOpening() {
|
|
356
|
+
this.removeAttribute('opening');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** @private */
|
|
360
|
+
_finishClosing() {
|
|
361
|
+
this._detachOverlay();
|
|
362
|
+
this.$.overlay.style.removeProperty('pointer-events');
|
|
363
|
+
this.removeAttribute('closing');
|
|
364
|
+
this.dispatchEvent(new CustomEvent('vaadin-overlay-closed'));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** @private */
|
|
368
|
+
_animatedClosing() {
|
|
369
|
+
if (this.hasAttribute('opening')) {
|
|
370
|
+
this._flushAnimation('opening');
|
|
371
|
+
}
|
|
372
|
+
if (this._placeholder) {
|
|
373
|
+
this._exitModalState();
|
|
374
|
+
this.setAttribute('closing', '');
|
|
375
|
+
this.dispatchEvent(new CustomEvent('vaadin-overlay-closing'));
|
|
376
|
+
|
|
377
|
+
if (this._shouldAnimate()) {
|
|
378
|
+
this._enqueueAnimation('closing', () => {
|
|
379
|
+
this._finishClosing();
|
|
380
|
+
});
|
|
381
|
+
} else {
|
|
382
|
+
this._finishClosing();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** @private */
|
|
388
|
+
_detachOverlay() {
|
|
389
|
+
this._placeholder.parentNode.insertBefore(this, this._placeholder);
|
|
390
|
+
this._placeholder.parentNode.removeChild(this._placeholder);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** @private */
|
|
394
|
+
_mouseDownListener(event) {
|
|
395
|
+
this._mouseDownInside = event.composedPath().indexOf(this.$.overlay) >= 0;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** @private */
|
|
399
|
+
_mouseUpListener(event) {
|
|
400
|
+
this._mouseUpInside = event.composedPath().indexOf(this.$.overlay) >= 0;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Whether to close the overlay on outside click or not.
|
|
405
|
+
* Override this method to customize the closing logic.
|
|
406
|
+
*
|
|
407
|
+
* @param {Event} _event
|
|
408
|
+
* @return {boolean}
|
|
409
|
+
* @protected
|
|
410
|
+
*/
|
|
411
|
+
_shouldCloseOnOutsideClick(_event) {
|
|
412
|
+
return this._last;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Outside click listener used in capture phase to close the overlay before
|
|
417
|
+
* propagating the event to the listener on the element that triggered it.
|
|
418
|
+
* Otherwise, calling `open()` would result in closing and re-opening.
|
|
419
|
+
*
|
|
420
|
+
* @private
|
|
421
|
+
*/
|
|
422
|
+
_outsideClickListener(event) {
|
|
423
|
+
if (event.composedPath().includes(this.$.overlay) || this._mouseDownInside || this._mouseUpInside) {
|
|
424
|
+
this._mouseDownInside = false;
|
|
425
|
+
this._mouseUpInside = false;
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (!this._shouldCloseOnOutsideClick(event)) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const evt = new CustomEvent('vaadin-overlay-outside-click', {
|
|
434
|
+
bubbles: true,
|
|
435
|
+
cancelable: true,
|
|
436
|
+
detail: { sourceEvent: event },
|
|
437
|
+
});
|
|
438
|
+
this.dispatchEvent(evt);
|
|
439
|
+
|
|
440
|
+
if (this.opened && !evt.defaultPrevented) {
|
|
441
|
+
this.close(event);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Listener used to close whe overlay on Escape press, if it is the last one.
|
|
447
|
+
* @private
|
|
448
|
+
*/
|
|
449
|
+
_keydownListener(event) {
|
|
450
|
+
if (!this._last) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Only close modeless overlay on Esc press when it contains focus
|
|
455
|
+
if (this.modeless && !event.composedPath().includes(this.$.overlay)) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (event.key === 'Escape') {
|
|
460
|
+
const evt = new CustomEvent('vaadin-overlay-escape-press', {
|
|
461
|
+
bubbles: true,
|
|
462
|
+
cancelable: true,
|
|
463
|
+
detail: { sourceEvent: event },
|
|
464
|
+
});
|
|
465
|
+
this.dispatchEvent(evt);
|
|
466
|
+
|
|
467
|
+
if (this.opened && !evt.defaultPrevented) {
|
|
468
|
+
this.close(event);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
|
|
8
|
+
export declare function OverlayStackMixin<T extends Constructor<HTMLElement>>(
|
|
9
|
+
base: T,
|
|
10
|
+
): Constructor<OverlayStackMixinClass> & T;
|
|
11
|
+
|
|
12
|
+
export declare class OverlayStackMixinClass {
|
|
13
|
+
/**
|
|
14
|
+
* Returns true if this is the last one in the opened overlays stack.
|
|
15
|
+
*/
|
|
16
|
+
protected readonly _last: boolean;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Brings the overlay as visually the frontmost one.
|
|
20
|
+
*/
|
|
21
|
+
bringToFront(): void;
|
|
22
|
+
}
|