@everymatrix/general-input 1.22.0 → 1.22.1
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/dist/cjs/checkbox-group-input_10.cjs.entry.js +27763 -17684
- package/dist/components/active-mixin.js +97 -7
- package/dist/components/checkbox-group-input2.js +2863 -390
- package/dist/components/date-input2.js +6683 -2984
- package/dist/components/field-mixin.js +2423 -3723
- package/dist/components/input-field-shared-styles.js +993 -242
- package/dist/components/password-input2.js +1870 -94
- package/dist/components/vaadin-button.js +1531 -157
- package/dist/components/vaadin-combo-box.js +2655 -783
- package/dist/components/virtual-keyboard-controller.js +1163 -1742
- package/dist/esm/checkbox-group-input_10.entry.js +27763 -17684
- package/dist/general-input/general-input.esm.js +1 -1
- package/dist/general-input/p-983d18d7.entry.js +4143 -0
- package/package.json +9 -6
- package/dist/components/pattern-mixin.js +0 -85
- package/dist/general-input/p-553c91f3.entry.js +0 -3583
- /package/dist/types/Users/{catalin.poclid/Documents/work → adrian.pripon/Documents/Work}/widgets-stencil/packages/general-input/.stencil/packages/general-input/stencil.config.d.ts +0 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { i,
|
|
2
|
-
import { b as isIOS } from './input-field-shared-styles.js';
|
|
1
|
+
import { i, y as registerStyles, z as getDeepActiveElement, A as getFocusableElements, b as isElementFocused, d as dedupingMixin } from './field-mixin.js';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* @license
|
|
6
|
-
* Copyright (c) 2017 -
|
|
5
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
7
6
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
8
7
|
*/
|
|
9
8
|
|
|
@@ -74,7 +73,113 @@ const overlay = i`
|
|
|
74
73
|
|
|
75
74
|
registerStyles('', overlay, { moduleId: 'lumo-overlay' });
|
|
76
75
|
|
|
77
|
-
|
|
76
|
+
/**
|
|
77
|
+
* @license
|
|
78
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
79
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
const menuOverlayCore = i`
|
|
83
|
+
:host([opening]),
|
|
84
|
+
:host([closing]) {
|
|
85
|
+
animation: 0.14s lumo-overlay-dummy-animation;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
[part='overlay'] {
|
|
89
|
+
will-change: opacity, transform;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
:host([opening]) [part='overlay'] {
|
|
93
|
+
animation: 0.1s lumo-menu-overlay-enter ease-out both;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@keyframes lumo-menu-overlay-enter {
|
|
97
|
+
0% {
|
|
98
|
+
opacity: 0;
|
|
99
|
+
transform: translateY(-4px);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
:host([closing]) [part='overlay'] {
|
|
104
|
+
animation: 0.1s lumo-menu-overlay-exit both;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@keyframes lumo-menu-overlay-exit {
|
|
108
|
+
100% {
|
|
109
|
+
opacity: 0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
`;
|
|
113
|
+
|
|
114
|
+
registerStyles('', menuOverlayCore, { moduleId: 'lumo-menu-overlay-core' });
|
|
115
|
+
|
|
116
|
+
const menuOverlayExt = i`
|
|
117
|
+
/* Small viewport (bottom sheet) styles */
|
|
118
|
+
/* Use direct media queries instead of the state attributes ([phone] and [fullscreen]) provided by the elements */
|
|
119
|
+
@media (max-width: 420px), (max-height: 420px) {
|
|
120
|
+
:host {
|
|
121
|
+
top: 0 !important;
|
|
122
|
+
right: 0 !important;
|
|
123
|
+
bottom: var(--vaadin-overlay-viewport-bottom, 0) !important;
|
|
124
|
+
left: 0 !important;
|
|
125
|
+
align-items: stretch !important;
|
|
126
|
+
justify-content: flex-end !important;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
[part='overlay'] {
|
|
130
|
+
max-height: 50vh;
|
|
131
|
+
width: 100vw;
|
|
132
|
+
border-radius: 0;
|
|
133
|
+
box-shadow: var(--lumo-box-shadow-xl);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* The content part scrolls instead of the overlay part, because of the gradient fade-out */
|
|
137
|
+
[part='content'] {
|
|
138
|
+
padding: 30px var(--lumo-space-m);
|
|
139
|
+
max-height: inherit;
|
|
140
|
+
box-sizing: border-box;
|
|
141
|
+
-webkit-overflow-scrolling: touch;
|
|
142
|
+
overflow: auto;
|
|
143
|
+
-webkit-mask-image: linear-gradient(transparent, #000 40px, #000 calc(100% - 40px), transparent);
|
|
144
|
+
mask-image: linear-gradient(transparent, #000 40px, #000 calc(100% - 40px), transparent);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
[part='backdrop'] {
|
|
148
|
+
display: block;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Animations */
|
|
152
|
+
|
|
153
|
+
:host([opening]) [part='overlay'] {
|
|
154
|
+
animation: 0.2s lumo-mobile-menu-overlay-enter cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
:host([closing]),
|
|
158
|
+
:host([closing]) [part='backdrop'] {
|
|
159
|
+
animation-delay: 0.14s;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
:host([closing]) [part='overlay'] {
|
|
163
|
+
animation: 0.14s 0.14s lumo-mobile-menu-overlay-exit cubic-bezier(0.55, 0.055, 0.675, 0.19) both;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@keyframes lumo-mobile-menu-overlay-enter {
|
|
168
|
+
0% {
|
|
169
|
+
transform: translateY(150%);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@keyframes lumo-mobile-menu-overlay-exit {
|
|
174
|
+
100% {
|
|
175
|
+
transform: translateY(150%);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
`;
|
|
179
|
+
|
|
180
|
+
const menuOverlay = [overlay, menuOverlayCore, menuOverlayExt];
|
|
181
|
+
|
|
182
|
+
registerStyles('', menuOverlay, { moduleId: 'lumo-menu-overlay' });
|
|
78
183
|
|
|
79
184
|
/**
|
|
80
185
|
@license
|
|
@@ -150,851 +255,362 @@ function afterNextRender(context, callback, args) {
|
|
|
150
255
|
}
|
|
151
256
|
|
|
152
257
|
/**
|
|
153
|
-
@license
|
|
154
|
-
Copyright (c)
|
|
155
|
-
|
|
156
|
-
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
|
157
|
-
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
|
158
|
-
Code distributed by Google as part of the polymer project is also
|
|
159
|
-
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
|
160
|
-
*/
|
|
161
|
-
|
|
162
|
-
// Common implementation for mixin & behavior
|
|
163
|
-
function mutablePropertyChange(inst, property, value, old, mutableData) {
|
|
164
|
-
let isObject;
|
|
165
|
-
if (mutableData) {
|
|
166
|
-
isObject = (typeof value === 'object' && value !== null);
|
|
167
|
-
// Pull `old` for Objects from temp cache, but treat `null` as a primitive
|
|
168
|
-
if (isObject) {
|
|
169
|
-
old = inst.__dataTemp[property];
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// Strict equality check, but return false for NaN===NaN
|
|
173
|
-
let shouldChange = (old !== value && (old === old || value === value));
|
|
174
|
-
// Objects are stored in temporary cache (cleared at end of
|
|
175
|
-
// turn), which is used for dirty-checking
|
|
176
|
-
if (isObject && shouldChange) {
|
|
177
|
-
inst.__dataTemp[property] = value;
|
|
178
|
-
}
|
|
179
|
-
return shouldChange;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Element class mixin to skip strict dirty-checking for objects and arrays
|
|
184
|
-
* (always consider them to be "dirty"), for use on elements utilizing
|
|
185
|
-
* `PropertyEffects`
|
|
186
|
-
*
|
|
187
|
-
* By default, `PropertyEffects` performs strict dirty checking on
|
|
188
|
-
* objects, which means that any deep modifications to an object or array will
|
|
189
|
-
* not be propagated unless "immutable" data patterns are used (i.e. all object
|
|
190
|
-
* references from the root to the mutation were changed).
|
|
191
|
-
*
|
|
192
|
-
* Polymer also provides a proprietary data mutation and path notification API
|
|
193
|
-
* (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient
|
|
194
|
-
* mutation and notification of deep changes in an object graph to all elements
|
|
195
|
-
* bound to the same object graph.
|
|
196
|
-
*
|
|
197
|
-
* In cases where neither immutable patterns nor the data mutation API can be
|
|
198
|
-
* used, applying this mixin will cause Polymer to skip dirty checking for
|
|
199
|
-
* objects and arrays (always consider them to be "dirty"). This allows a
|
|
200
|
-
* user to make a deep modification to a bound object graph, and then either
|
|
201
|
-
* simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath`
|
|
202
|
-
* (e.g. `this.notifyPath('items')`) to update the tree. Note that all
|
|
203
|
-
* elements that wish to be updated based on deep mutations must apply this
|
|
204
|
-
* mixin or otherwise skip strict dirty checking for objects/arrays.
|
|
205
|
-
* Specifically, any elements in the binding tree between the source of a
|
|
206
|
-
* mutation and the consumption of it must apply this mixin or enable the
|
|
207
|
-
* `OptionalMutableData` mixin.
|
|
208
|
-
*
|
|
209
|
-
* In order to make the dirty check strategy configurable, see
|
|
210
|
-
* `OptionalMutableData`.
|
|
211
|
-
*
|
|
212
|
-
* Note, the performance characteristics of propagating large object graphs
|
|
213
|
-
* will be worse as opposed to using strict dirty checking with immutable
|
|
214
|
-
* patterns or Polymer's path notification API.
|
|
215
|
-
*
|
|
216
|
-
* @mixinFunction
|
|
217
|
-
* @polymer
|
|
218
|
-
* @summary Element class mixin to skip strict dirty-checking for objects
|
|
219
|
-
* and arrays
|
|
220
|
-
* @template T
|
|
221
|
-
* @param {function(new:T)} superClass Class to apply mixin to.
|
|
222
|
-
* @return {function(new:T)} superClass with mixin applied.
|
|
258
|
+
* @license
|
|
259
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
260
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
223
261
|
*/
|
|
224
|
-
const MutableData = dedupingMixin(superClass => {
|
|
225
262
|
|
|
226
|
-
|
|
227
|
-
* @polymer
|
|
228
|
-
* @mixinClass
|
|
229
|
-
* @implements {Polymer_MutableData}
|
|
230
|
-
*/
|
|
231
|
-
class MutableData extends superClass {
|
|
232
|
-
/**
|
|
233
|
-
* Overrides `PropertyEffects` to provide option for skipping
|
|
234
|
-
* strict equality checking for Objects and Arrays.
|
|
235
|
-
*
|
|
236
|
-
* This method pulls the value to dirty check against from the `__dataTemp`
|
|
237
|
-
* cache (rather than the normal `__data` cache) for Objects. Since the temp
|
|
238
|
-
* cache is cleared at the end of a turn, this implementation allows
|
|
239
|
-
* side-effects of deep object changes to be processed by re-setting the
|
|
240
|
-
* same object (using the temp cache as an in-turn backstop to prevent
|
|
241
|
-
* cycles due to 2-way notification).
|
|
242
|
-
*
|
|
243
|
-
* @param {string} property Property name
|
|
244
|
-
* @param {*} value New property value
|
|
245
|
-
* @param {*} old Previous property value
|
|
246
|
-
* @return {boolean} Whether the property should be considered a change
|
|
247
|
-
* @protected
|
|
248
|
-
*/
|
|
249
|
-
_shouldPropertyChange(property, value, old) {
|
|
250
|
-
return mutablePropertyChange(this, property, value, old, true);
|
|
251
|
-
}
|
|
263
|
+
const testUserAgent = (regexp) => regexp.test(navigator.userAgent);
|
|
252
264
|
|
|
253
|
-
|
|
265
|
+
const testPlatform = (regexp) => regexp.test(navigator.platform);
|
|
254
266
|
|
|
255
|
-
|
|
267
|
+
const testVendor = (regexp) => regexp.test(navigator.vendor);
|
|
256
268
|
|
|
257
|
-
|
|
269
|
+
testUserAgent(/Android/u);
|
|
258
270
|
|
|
259
|
-
|
|
260
|
-
* Element class mixin to add the optional ability to skip strict
|
|
261
|
-
* dirty-checking for objects and arrays (always consider them to be
|
|
262
|
-
* "dirty") by setting a `mutable-data` attribute on an element instance.
|
|
263
|
-
*
|
|
264
|
-
* By default, `PropertyEffects` performs strict dirty checking on
|
|
265
|
-
* objects, which means that any deep modifications to an object or array will
|
|
266
|
-
* not be propagated unless "immutable" data patterns are used (i.e. all object
|
|
267
|
-
* references from the root to the mutation were changed).
|
|
268
|
-
*
|
|
269
|
-
* Polymer also provides a proprietary data mutation and path notification API
|
|
270
|
-
* (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient
|
|
271
|
-
* mutation and notification of deep changes in an object graph to all elements
|
|
272
|
-
* bound to the same object graph.
|
|
273
|
-
*
|
|
274
|
-
* In cases where neither immutable patterns nor the data mutation API can be
|
|
275
|
-
* used, applying this mixin will allow Polymer to skip dirty checking for
|
|
276
|
-
* objects and arrays (always consider them to be "dirty"). This allows a
|
|
277
|
-
* user to make a deep modification to a bound object graph, and then either
|
|
278
|
-
* simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath`
|
|
279
|
-
* (e.g. `this.notifyPath('items')`) to update the tree. Note that all
|
|
280
|
-
* elements that wish to be updated based on deep mutations must apply this
|
|
281
|
-
* mixin or otherwise skip strict dirty checking for objects/arrays.
|
|
282
|
-
* Specifically, any elements in the binding tree between the source of a
|
|
283
|
-
* mutation and the consumption of it must enable this mixin or apply the
|
|
284
|
-
* `MutableData` mixin.
|
|
285
|
-
*
|
|
286
|
-
* While this mixin adds the ability to forgo Object/Array dirty checking,
|
|
287
|
-
* the `mutableData` flag defaults to false and must be set on the instance.
|
|
288
|
-
*
|
|
289
|
-
* Note, the performance characteristics of propagating large object graphs
|
|
290
|
-
* will be worse by relying on `mutableData: true` as opposed to using
|
|
291
|
-
* strict dirty checking with immutable patterns or Polymer's path notification
|
|
292
|
-
* API.
|
|
293
|
-
*
|
|
294
|
-
* @mixinFunction
|
|
295
|
-
* @polymer
|
|
296
|
-
* @summary Element class mixin to optionally skip strict dirty-checking
|
|
297
|
-
* for objects and arrays
|
|
298
|
-
*/
|
|
299
|
-
const OptionalMutableData = dedupingMixin(superClass => {
|
|
271
|
+
testUserAgent(/Chrome/u) && testVendor(/Google Inc/u);
|
|
300
272
|
|
|
301
|
-
|
|
302
|
-
* @mixinClass
|
|
303
|
-
* @polymer
|
|
304
|
-
* @implements {Polymer_OptionalMutableData}
|
|
305
|
-
*/
|
|
306
|
-
class OptionalMutableData extends superClass {
|
|
273
|
+
testUserAgent(/Firefox/u);
|
|
307
274
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return {
|
|
311
|
-
/**
|
|
312
|
-
* Instance-level flag for configuring the dirty-checking strategy
|
|
313
|
-
* for this element. When true, Objects and Arrays will skip dirty
|
|
314
|
-
* checking, otherwise strict equality checking will be used.
|
|
315
|
-
*/
|
|
316
|
-
mutableData: Boolean
|
|
317
|
-
};
|
|
318
|
-
}
|
|
275
|
+
// IPadOS 13 lies and says it's a Mac, but we can distinguish by detecting touch support.
|
|
276
|
+
const isIPad = testPlatform(/^iPad/u) || (testPlatform(/^Mac/u) && navigator.maxTouchPoints > 1);
|
|
319
277
|
|
|
320
|
-
|
|
321
|
-
* Overrides `PropertyEffects` to provide option for skipping
|
|
322
|
-
* strict equality checking for Objects and Arrays.
|
|
323
|
-
*
|
|
324
|
-
* When `this.mutableData` is true on this instance, this method
|
|
325
|
-
* pulls the value to dirty check against from the `__dataTemp` cache
|
|
326
|
-
* (rather than the normal `__data` cache) for Objects. Since the temp
|
|
327
|
-
* cache is cleared at the end of a turn, this implementation allows
|
|
328
|
-
* side-effects of deep object changes to be processed by re-setting the
|
|
329
|
-
* same object (using the temp cache as an in-turn backstop to prevent
|
|
330
|
-
* cycles due to 2-way notification).
|
|
331
|
-
*
|
|
332
|
-
* @param {string} property Property name
|
|
333
|
-
* @param {*} value New property value
|
|
334
|
-
* @param {*} old Previous property value
|
|
335
|
-
* @return {boolean} Whether the property should be considered a change
|
|
336
|
-
* @protected
|
|
337
|
-
*/
|
|
338
|
-
_shouldPropertyChange(property, value, old) {
|
|
339
|
-
return mutablePropertyChange(this, property, value, old, this.mutableData);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
278
|
+
const isIPhone = testPlatform(/^iPhone/u);
|
|
342
279
|
|
|
343
|
-
|
|
280
|
+
const isIOS = isIPhone || isIPad;
|
|
344
281
|
|
|
345
|
-
|
|
282
|
+
testUserAgent(/^((?!chrome|android).)*safari/iu);
|
|
346
283
|
|
|
347
|
-
|
|
348
|
-
|
|
284
|
+
(() => {
|
|
285
|
+
try {
|
|
286
|
+
document.createEvent('TouchEvent');
|
|
287
|
+
return true;
|
|
288
|
+
} catch (e) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
})();
|
|
349
292
|
|
|
350
293
|
/**
|
|
351
|
-
@license
|
|
352
|
-
Copyright (c) 2017
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
|
356
|
-
Code distributed by Google as part of the polymer project is also
|
|
357
|
-
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
|
358
|
-
*/
|
|
359
|
-
|
|
360
|
-
// Base class for HTMLTemplateElement extension that has property effects
|
|
361
|
-
// machinery for propagating host properties to children. This is an ES5
|
|
362
|
-
// class only because Babel (incorrectly) requires super() in the class
|
|
363
|
-
// constructor even though no `this` is used and it returns an instance.
|
|
364
|
-
let newInstance = null;
|
|
294
|
+
* @license
|
|
295
|
+
* Copyright (c) 2017 Anton Korzunov
|
|
296
|
+
* SPDX-License-Identifier: MIT
|
|
297
|
+
*/
|
|
365
298
|
|
|
366
299
|
/**
|
|
367
|
-
* @
|
|
368
|
-
*
|
|
369
|
-
*
|
|
300
|
+
* @fileoverview
|
|
301
|
+
*
|
|
302
|
+
* This module includes JS code copied from the `aria-hidden` package:
|
|
303
|
+
* https://github.com/theKashey/aria-hidden/blob/master/src/index.ts
|
|
370
304
|
*/
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
305
|
+
|
|
306
|
+
/** @type {WeakMap<Element, number>} */
|
|
307
|
+
let counterMap = new WeakMap();
|
|
308
|
+
|
|
309
|
+
/** @type {WeakMap<Element, boolean>} */
|
|
310
|
+
let uncontrolledNodes = new WeakMap();
|
|
311
|
+
|
|
312
|
+
/** @type {Record<string, WeakMap<Element, number>>} */
|
|
313
|
+
let markerMap = {};
|
|
314
|
+
|
|
315
|
+
/** @type {number} */
|
|
316
|
+
let lockCount = 0;
|
|
378
317
|
|
|
379
318
|
/**
|
|
380
|
-
* @
|
|
381
|
-
* @
|
|
382
|
-
* @extends {HTMLTemplateElementExtension}
|
|
383
|
-
* @private
|
|
319
|
+
* @param {?Node} node
|
|
320
|
+
* @return {boolean}
|
|
384
321
|
*/
|
|
385
|
-
const
|
|
322
|
+
const isElement = (node) => node && node.nodeType === Node.ELEMENT_NODE;
|
|
386
323
|
|
|
387
324
|
/**
|
|
388
|
-
* @
|
|
389
|
-
* @implements {Polymer_MutableData}
|
|
390
|
-
* @extends {DataTemplate}
|
|
391
|
-
* @private
|
|
325
|
+
* @param {...unknown} args
|
|
392
326
|
*/
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
function upgradeTemplate(template, constructor) {
|
|
397
|
-
newInstance = template;
|
|
398
|
-
Object.setPrototypeOf(template, constructor.prototype);
|
|
399
|
-
new constructor();
|
|
400
|
-
newInstance = null;
|
|
401
|
-
}
|
|
327
|
+
const logError = (...args) => {
|
|
328
|
+
console.error(`Error: ${args.join(' ')}. Skip setting aria-hidden.`);
|
|
329
|
+
};
|
|
402
330
|
|
|
403
331
|
/**
|
|
404
|
-
*
|
|
405
|
-
* @
|
|
406
|
-
* @
|
|
407
|
-
* @implements {Polymer_PropertyEffects}
|
|
408
|
-
* @private
|
|
332
|
+
* @param {HTMLElement} parent
|
|
333
|
+
* @param {Element[]} targets
|
|
334
|
+
* @return {Element[]}
|
|
409
335
|
*/
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
n.textContent = '';
|
|
422
|
-
} else {
|
|
423
|
-
n.textContent = n.__polymerTextContent__;
|
|
424
|
-
}
|
|
425
|
-
// remove and replace slot
|
|
426
|
-
} else if (n.localName === 'slot') {
|
|
427
|
-
if (hide) {
|
|
428
|
-
n.__polymerReplaced__ = document.createComment('hidden-slot');
|
|
429
|
-
wrap(wrap(n).parentNode).replaceChild(n.__polymerReplaced__, n);
|
|
430
|
-
} else {
|
|
431
|
-
const replace = n.__polymerReplaced__;
|
|
432
|
-
if (replace) {
|
|
433
|
-
wrap(wrap(replace).parentNode).replaceChild(n, replace);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
336
|
+
const correctTargets = (parent, targets) => {
|
|
337
|
+
if (!isElement(parent)) {
|
|
338
|
+
logError(parent, 'is not a valid element');
|
|
339
|
+
return [];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return targets
|
|
343
|
+
.map((target) => {
|
|
344
|
+
if (!isElement(target)) {
|
|
345
|
+
logError(target, 'is not a valid element');
|
|
346
|
+
return null;
|
|
436
347
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
} else {
|
|
443
|
-
n.style.display = n.__polymerDisplay__;
|
|
348
|
+
|
|
349
|
+
let node = target;
|
|
350
|
+
while (node && node !== parent) {
|
|
351
|
+
if (parent.contains(node)) {
|
|
352
|
+
return target;
|
|
444
353
|
}
|
|
354
|
+
node = node.getRootNode().host;
|
|
445
355
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
356
|
+
|
|
357
|
+
logError(target, 'is not contained inside', parent);
|
|
358
|
+
return null;
|
|
359
|
+
})
|
|
360
|
+
.filter((x) => Boolean(x));
|
|
361
|
+
};
|
|
453
362
|
|
|
454
363
|
/**
|
|
455
|
-
*
|
|
456
|
-
* @
|
|
457
|
-
* @
|
|
458
|
-
* @
|
|
364
|
+
* Marks everything except given node(or nodes) as aria-hidden
|
|
365
|
+
* @param {Element | Element[]} originalTarget - elements to keep on the page
|
|
366
|
+
* @param {HTMLElement} [parentNode] - top element, defaults to document.body
|
|
367
|
+
* @param {String} [markerName] - a special attribute to mark every node
|
|
368
|
+
* @param {String} [controlAttribute] - html Attribute to control
|
|
369
|
+
* @return {Function}
|
|
459
370
|
*/
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
this.root = this._stampTemplate(this.__dataHost);
|
|
466
|
-
// Save list of stamped children
|
|
467
|
-
let children = [];
|
|
468
|
-
/** @suppress {invalidCasts} */
|
|
469
|
-
this.children = /** @type {!NodeList} */ (children);
|
|
470
|
-
// Polymer 1.x did not use `Polymer.dom` here so not bothering.
|
|
471
|
-
for (let n = this.root.firstChild; n; n=n.nextSibling) {
|
|
472
|
-
children.push(n);
|
|
473
|
-
n.__templatizeInstance = this;
|
|
474
|
-
}
|
|
475
|
-
if (this.__templatizeOwner &&
|
|
476
|
-
this.__templatizeOwner.__hideTemplateChildren__) {
|
|
477
|
-
this._showHideChildren(true);
|
|
478
|
-
}
|
|
479
|
-
// Flush props only when props are passed if instance props exist
|
|
480
|
-
// or when there isn't instance props.
|
|
481
|
-
let options = this.__templatizeOptions;
|
|
482
|
-
if ((props && options.instanceProps) || !options.instanceProps) {
|
|
483
|
-
this._enableProperties();
|
|
484
|
-
}
|
|
371
|
+
const applyAttributeToOthers = (originalTarget, parentNode, markerName, controlAttribute) => {
|
|
372
|
+
const targets = correctTargets(parentNode, Array.isArray(originalTarget) ? originalTarget : [originalTarget]);
|
|
373
|
+
|
|
374
|
+
if (!markerMap[markerName]) {
|
|
375
|
+
markerMap[markerName] = new WeakMap();
|
|
485
376
|
}
|
|
377
|
+
|
|
378
|
+
const markerCounter = markerMap[markerName];
|
|
379
|
+
|
|
380
|
+
/** @type {Element[]} */
|
|
381
|
+
const hiddenNodes = [];
|
|
382
|
+
|
|
383
|
+
/** @type {Set<Node>} */
|
|
384
|
+
const elementsToKeep = new Set();
|
|
385
|
+
|
|
386
|
+
/** @type {Set<Node>} */
|
|
387
|
+
const elementsToStop = new Set(targets);
|
|
388
|
+
|
|
486
389
|
/**
|
|
487
|
-
*
|
|
488
|
-
* sets any properties stored in `__hostProps`.
|
|
489
|
-
* @private
|
|
490
|
-
* @param {Object} props Object of property name-value pairs to set.
|
|
491
|
-
* @return {void}
|
|
390
|
+
* @param {?Node} el
|
|
492
391
|
*/
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
for (let hprop in this.__hostProps) {
|
|
497
|
-
this._setPendingProperty(hprop, this.__dataHost['_host_' + hprop]);
|
|
498
|
-
}
|
|
392
|
+
const keep = (el) => {
|
|
393
|
+
if (!el || elementsToKeep.has(el)) {
|
|
394
|
+
return;
|
|
499
395
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
396
|
+
|
|
397
|
+
elementsToKeep.add(el);
|
|
398
|
+
|
|
399
|
+
const slot = el.assignedSlot;
|
|
400
|
+
if (slot) {
|
|
401
|
+
keep(slot);
|
|
504
402
|
}
|
|
505
|
-
|
|
403
|
+
|
|
404
|
+
keep(el.parentNode || el.host);
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
targets.forEach(keep);
|
|
408
|
+
|
|
506
409
|
/**
|
|
507
|
-
*
|
|
508
|
-
* called on instances from the `options.forwardHostProp` callback
|
|
509
|
-
* to propagate changes of host properties to each instance.
|
|
510
|
-
*
|
|
511
|
-
* Note this method enqueues the change, which are flushed as a batch.
|
|
512
|
-
*
|
|
513
|
-
* @param {string} prop Property or path name
|
|
514
|
-
* @param {*} value Value of the property to forward
|
|
515
|
-
* @return {void}
|
|
410
|
+
* @param {?Node} el
|
|
516
411
|
*/
|
|
517
|
-
|
|
518
|
-
if (
|
|
519
|
-
|
|
412
|
+
const deep = (parent) => {
|
|
413
|
+
if (!parent || elementsToStop.has(parent)) {
|
|
414
|
+
return;
|
|
520
415
|
}
|
|
521
|
-
}
|
|
522
416
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
* @param {function(!Event):void} handler Listener function to add
|
|
530
|
-
* @return {void}
|
|
531
|
-
*/
|
|
532
|
-
_addEventListenerToNode(node, eventName, handler) {
|
|
533
|
-
if (this._methodHost && this.__templatizeOptions.parentModel) {
|
|
534
|
-
// If this instance should be considered a parent model, decorate
|
|
535
|
-
// events this template instance as `model`
|
|
536
|
-
this._methodHost._addEventListenerToNode(node, eventName, (e) => {
|
|
537
|
-
e.model = this;
|
|
538
|
-
handler(e);
|
|
539
|
-
});
|
|
540
|
-
} else {
|
|
541
|
-
// Otherwise delegate to the template's host (which could be)
|
|
542
|
-
// another template instance
|
|
543
|
-
let templateHost = this.__dataHost.__dataHost;
|
|
544
|
-
if (templateHost) {
|
|
545
|
-
templateHost._addEventListenerToNode(node, eventName, handler);
|
|
417
|
+
const root = parent.shadowRoot;
|
|
418
|
+
const children = root ? [...parent.children, ...root.children] : [...parent.children];
|
|
419
|
+
children.forEach((node) => {
|
|
420
|
+
// Skip elements that don't need to be hidden
|
|
421
|
+
if (['template', 'script', 'style'].includes(node.localName)) {
|
|
422
|
+
return;
|
|
546
423
|
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* Shows or hides the template instance top level child elements. For
|
|
551
|
-
* text nodes, `textContent` is removed while "hidden" and replaced when
|
|
552
|
-
* "shown."
|
|
553
|
-
* @param {boolean} hide Set to true to hide the children;
|
|
554
|
-
* set to false to show them.
|
|
555
|
-
* @return {void}
|
|
556
|
-
* @protected
|
|
557
|
-
*/
|
|
558
|
-
_showHideChildren(hide) {
|
|
559
|
-
showHideChildren(hide, this.children);
|
|
560
|
-
}
|
|
561
|
-
/**
|
|
562
|
-
* Overrides default property-effects implementation to intercept
|
|
563
|
-
* textContent bindings while children are "hidden" and cache in
|
|
564
|
-
* private storage for later retrieval.
|
|
565
|
-
*
|
|
566
|
-
* @override
|
|
567
|
-
* @param {!Node} node The node to set a property on
|
|
568
|
-
* @param {string} prop The property to set
|
|
569
|
-
* @param {*} value The value to set
|
|
570
|
-
* @return {void}
|
|
571
|
-
* @protected
|
|
572
|
-
*/
|
|
573
|
-
_setUnmanagedPropertyToNode(node, prop, value) {
|
|
574
|
-
if (node.__hideTemplateChildren__ &&
|
|
575
|
-
node.nodeType == Node.TEXT_NODE && prop == 'textContent') {
|
|
576
|
-
node.__polymerTextContent__ = value;
|
|
577
|
-
} else {
|
|
578
|
-
super._setUnmanagedPropertyToNode(node, prop, value);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
/**
|
|
582
|
-
* Find the parent model of this template instance. The parent model
|
|
583
|
-
* is either another templatize instance that had option `parentModel: true`,
|
|
584
|
-
* or else the host element.
|
|
585
|
-
*
|
|
586
|
-
* @return {!Polymer_PropertyEffects} The parent model of this instance
|
|
587
|
-
*/
|
|
588
|
-
get parentModel() {
|
|
589
|
-
let model = this.__parentModel;
|
|
590
|
-
if (!model) {
|
|
591
|
-
let options;
|
|
592
|
-
model = this;
|
|
593
|
-
do {
|
|
594
|
-
// A template instance's `__dataHost` is a <template>
|
|
595
|
-
// `model.__dataHost.__dataHost` is the template's host
|
|
596
|
-
model = model.__dataHost.__dataHost;
|
|
597
|
-
} while ((options = model.__templatizeOptions) && !options.parentModel);
|
|
598
|
-
this.__parentModel = model;
|
|
599
|
-
}
|
|
600
|
-
return model;
|
|
601
|
-
}
|
|
602
424
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
*/
|
|
611
|
-
dispatchEvent(event) { // eslint-disable-line no-unused-vars
|
|
612
|
-
return true;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
425
|
+
if (elementsToKeep.has(node)) {
|
|
426
|
+
deep(node);
|
|
427
|
+
} else {
|
|
428
|
+
const attr = node.getAttribute(controlAttribute);
|
|
429
|
+
const alreadyHidden = attr !== null && attr !== 'false';
|
|
430
|
+
const counterValue = (counterMap.get(node) || 0) + 1;
|
|
431
|
+
const markerValue = (markerCounter.get(node) || 0) + 1;
|
|
615
432
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
* @implements {Polymer_MutableData}
|
|
620
|
-
* @private
|
|
621
|
-
*/
|
|
622
|
-
const MutableTemplateInstanceBase = MutableData(
|
|
623
|
-
// This cast shouldn't be neccessary, but Closure doesn't understand that
|
|
624
|
-
// TemplateInstanceBase is a constructor function.
|
|
625
|
-
/** @type {function(new:TemplateInstanceBase)} */ (TemplateInstanceBase));
|
|
626
|
-
|
|
627
|
-
function findMethodHost(template) {
|
|
628
|
-
// Technically this should be the owner of the outermost template.
|
|
629
|
-
// In shadow dom, this is always getRootNode().host, but we can
|
|
630
|
-
// approximate this via cooperation with our dataHost always setting
|
|
631
|
-
// `_methodHost` as long as there were bindings (or id's) on this
|
|
632
|
-
// instance causing it to get a dataHost.
|
|
633
|
-
let templateHost = template.__dataHost;
|
|
634
|
-
return templateHost && templateHost._methodHost || templateHost;
|
|
635
|
-
}
|
|
433
|
+
counterMap.set(node, counterValue);
|
|
434
|
+
markerCounter.set(node, markerValue);
|
|
435
|
+
hiddenNodes.push(node);
|
|
636
436
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
*/
|
|
641
|
-
function createTemplatizerClass(template, templateInfo, options) {
|
|
642
|
-
/**
|
|
643
|
-
* @constructor
|
|
644
|
-
* @extends {TemplateInstanceBase}
|
|
645
|
-
*/
|
|
646
|
-
let templatizerBase = options.mutableData ?
|
|
647
|
-
MutableTemplateInstanceBase : TemplateInstanceBase;
|
|
648
|
-
|
|
649
|
-
// Affordance for global mixins onto TemplatizeInstance
|
|
650
|
-
if (templatize.mixin) {
|
|
651
|
-
templatizerBase = templatize.mixin(templatizerBase);
|
|
652
|
-
}
|
|
437
|
+
if (counterValue === 1 && alreadyHidden) {
|
|
438
|
+
uncontrolledNodes.set(node, true);
|
|
439
|
+
}
|
|
653
440
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
* @private
|
|
658
|
-
*/
|
|
659
|
-
let klass = class extends templatizerBase { };
|
|
660
|
-
/** @override */
|
|
661
|
-
klass.prototype.__templatizeOptions = options;
|
|
662
|
-
klass.prototype._bindTemplate(template);
|
|
663
|
-
addNotifyEffects(klass, template, templateInfo, options);
|
|
664
|
-
return klass;
|
|
665
|
-
}
|
|
441
|
+
if (markerValue === 1) {
|
|
442
|
+
node.setAttribute(markerName, 'true');
|
|
443
|
+
}
|
|
666
444
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
* properties that the host binds to the template using the `_host_` prefix.
|
|
670
|
-
*
|
|
671
|
-
* @suppress {missingProperties} class.prototype is not defined for some reason
|
|
672
|
-
*/
|
|
673
|
-
function addPropagateEffects(target, templateInfo, options, methodHost) {
|
|
674
|
-
let userForwardHostProp = options.forwardHostProp;
|
|
675
|
-
if (userForwardHostProp && templateInfo.hasHostProps) {
|
|
676
|
-
// Under the `removeNestedTemplates` optimization, a custom element like
|
|
677
|
-
// `dom-if` or `dom-repeat` can itself be treated as the "template"; this
|
|
678
|
-
// flag is used to switch between upgrading a `<template>` to be a property
|
|
679
|
-
// effects client vs. adding the effects directly to the custom element
|
|
680
|
-
const isTemplate = target.localName == 'template';
|
|
681
|
-
// Provide data API and property effects on memoized template class
|
|
682
|
-
let klass = templateInfo.templatizeTemplateClass;
|
|
683
|
-
if (!klass) {
|
|
684
|
-
if (isTemplate) {
|
|
685
|
-
/**
|
|
686
|
-
* @constructor
|
|
687
|
-
* @extends {DataTemplate}
|
|
688
|
-
*/
|
|
689
|
-
let templatizedBase =
|
|
690
|
-
options.mutableData ? MutableDataTemplate : DataTemplate;
|
|
691
|
-
|
|
692
|
-
// NOTE: due to https://github.com/google/closure-compiler/issues/2928,
|
|
693
|
-
// combining the next two lines into one assignment causes a spurious
|
|
694
|
-
// type error.
|
|
695
|
-
/** @private */
|
|
696
|
-
class TemplatizedTemplate extends templatizedBase {}
|
|
697
|
-
klass = templateInfo.templatizeTemplateClass = TemplatizedTemplate;
|
|
698
|
-
} else {
|
|
699
|
-
/**
|
|
700
|
-
* @constructor
|
|
701
|
-
* @extends {PolymerElement}
|
|
702
|
-
*/
|
|
703
|
-
const templatizedBase = target.constructor;
|
|
704
|
-
|
|
705
|
-
// Create a cached subclass of the base custom element class onto which
|
|
706
|
-
// to put the template-specific propagate effects
|
|
707
|
-
// NOTE: due to https://github.com/google/closure-compiler/issues/2928,
|
|
708
|
-
// combining the next two lines into one assignment causes a spurious
|
|
709
|
-
// type error.
|
|
710
|
-
/** @private */
|
|
711
|
-
class TemplatizedTemplateExtension extends templatizedBase {}
|
|
712
|
-
klass = templateInfo.templatizeTemplateClass =
|
|
713
|
-
TemplatizedTemplateExtension;
|
|
714
|
-
}
|
|
715
|
-
// Add template - >instances effects
|
|
716
|
-
// and host <- template effects
|
|
717
|
-
let hostProps = templateInfo.hostProps;
|
|
718
|
-
for (let prop in hostProps) {
|
|
719
|
-
klass.prototype._addPropertyEffect('_host_' + prop,
|
|
720
|
-
klass.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,
|
|
721
|
-
{fn: createForwardHostPropEffect(prop, userForwardHostProp)});
|
|
722
|
-
klass.prototype._createNotifyingProperty('_host_' + prop);
|
|
723
|
-
}
|
|
724
|
-
if (legacyWarnings && methodHost) {
|
|
725
|
-
warnOnUndeclaredProperties(templateInfo, options, methodHost);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
// Mix any pre-bound data into __data; no need to flush this to
|
|
729
|
-
// instances since they pull from the template at instance-time
|
|
730
|
-
if (target.__dataProto) {
|
|
731
|
-
// Note, generally `__dataProto` could be chained, but it's guaranteed
|
|
732
|
-
// to not be since this is a vanilla template we just added effects to
|
|
733
|
-
Object.assign(target.__data, target.__dataProto);
|
|
734
|
-
}
|
|
735
|
-
if (isTemplate) {
|
|
736
|
-
upgradeTemplate(target, klass);
|
|
737
|
-
// Clear any pending data for performance
|
|
738
|
-
target.__dataTemp = {};
|
|
739
|
-
target.__dataPending = null;
|
|
740
|
-
target.__dataOld = null;
|
|
741
|
-
target._enableProperties();
|
|
742
|
-
} else {
|
|
743
|
-
// Swizzle the cached subclass prototype onto the custom element
|
|
744
|
-
Object.setPrototypeOf(target, klass.prototype);
|
|
745
|
-
// Check for any pre-bound instance host properties, and do the
|
|
746
|
-
// instance property delete/assign dance for those (directly into data;
|
|
747
|
-
// not need to go through accessor since they are pulled at instance time)
|
|
748
|
-
const hostProps = templateInfo.hostProps;
|
|
749
|
-
for (let prop in hostProps) {
|
|
750
|
-
prop = '_host_' + prop;
|
|
751
|
-
if (prop in target) {
|
|
752
|
-
const val = target[prop];
|
|
753
|
-
delete target[prop];
|
|
754
|
-
target.__data[prop] = val;
|
|
445
|
+
if (!alreadyHidden) {
|
|
446
|
+
node.setAttribute(controlAttribute, 'true');
|
|
755
447
|
}
|
|
756
448
|
}
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
/* eslint-enable valid-jsdoc */
|
|
761
|
-
|
|
762
|
-
function createForwardHostPropEffect(hostProp, userForwardHostProp) {
|
|
763
|
-
return function forwardHostProp(template, prop, props) {
|
|
764
|
-
userForwardHostProp.call(template.__templatizeOwner,
|
|
765
|
-
prop.substring('_host_'.length), props[prop]);
|
|
449
|
+
});
|
|
766
450
|
};
|
|
767
|
-
}
|
|
768
451
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
452
|
+
deep(parentNode);
|
|
453
|
+
|
|
454
|
+
elementsToKeep.clear();
|
|
455
|
+
|
|
456
|
+
lockCount += 1;
|
|
457
|
+
|
|
458
|
+
return () => {
|
|
459
|
+
hiddenNodes.forEach((node) => {
|
|
460
|
+
const counterValue = counterMap.get(node) - 1;
|
|
461
|
+
const markerValue = markerCounter.get(node) - 1;
|
|
462
|
+
|
|
463
|
+
counterMap.set(node, counterValue);
|
|
464
|
+
markerCounter.set(node, markerValue);
|
|
465
|
+
|
|
466
|
+
if (!counterValue) {
|
|
467
|
+
if (uncontrolledNodes.has(node)) {
|
|
468
|
+
uncontrolledNodes.delete(node);
|
|
469
|
+
} else {
|
|
470
|
+
node.removeAttribute(controlAttribute);
|
|
471
|
+
}
|
|
786
472
|
}
|
|
787
|
-
klass.prototype._addPropertyEffect(hprop,
|
|
788
|
-
klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,
|
|
789
|
-
{fn: createNotifyHostPropEffect()});
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
473
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
474
|
+
if (!markerValue) {
|
|
475
|
+
node.removeAttribute(markerName);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
lockCount -= 1;
|
|
800
480
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
481
|
+
if (!lockCount) {
|
|
482
|
+
// clear
|
|
483
|
+
counterMap = new WeakMap();
|
|
484
|
+
counterMap = new WeakMap();
|
|
485
|
+
uncontrolledNodes = new WeakMap();
|
|
486
|
+
markerMap = {};
|
|
487
|
+
}
|
|
804
488
|
};
|
|
805
|
-
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Marks everything except given node(or nodes) as aria-hidden
|
|
493
|
+
* @param {Element | Element[]} originalTarget - elements to keep on the page
|
|
494
|
+
* @param {HTMLElement} [parentNode] - top element, defaults to document.body
|
|
495
|
+
* @param {String} [markerName] - a special attribute to mark every node
|
|
496
|
+
* @return {Function} undo command
|
|
497
|
+
*/
|
|
498
|
+
const hideOthers = (originalTarget, parentNode = document.body, markerName = 'data-aria-hidden') => {
|
|
499
|
+
const targets = Array.from(Array.isArray(originalTarget) ? originalTarget : [originalTarget]);
|
|
500
|
+
|
|
501
|
+
if (parentNode) {
|
|
502
|
+
// We should not hide ariaLive elements - https://github.com/theKashey/aria-hidden/issues/10
|
|
503
|
+
targets.push(...Array.from(parentNode.querySelectorAll('[aria-live]')));
|
|
504
|
+
}
|
|
806
505
|
|
|
506
|
+
return applyAttributeToOthers(targets, parentNode, markerName, 'aria-hidden');
|
|
507
|
+
};
|
|
807
508
|
|
|
808
509
|
/**
|
|
809
|
-
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
*
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
* The constructor returned takes a single argument dictionary of initial
|
|
819
|
-
* property values to propagate into template bindings. Additionally
|
|
820
|
-
* host properties can be forwarded in, and instance properties can be
|
|
821
|
-
* notified out by providing optional callbacks in the `options` dictionary.
|
|
822
|
-
*
|
|
823
|
-
* Valid configuration in `options` are as follows:
|
|
824
|
-
*
|
|
825
|
-
* - `forwardHostProp(property, value)`: Called when a property referenced
|
|
826
|
-
* in the template changed on the template's host. As this library does
|
|
827
|
-
* not retain references to templates instanced by the user, it is the
|
|
828
|
-
* templatize owner's responsibility to forward host property changes into
|
|
829
|
-
* user-stamped instances. The `instance.forwardHostProp(property, value)`
|
|
830
|
-
* method on the generated class should be called to forward host
|
|
831
|
-
* properties into the template to prevent unnecessary property-changed
|
|
832
|
-
* notifications. Any properties referenced in the template that are not
|
|
833
|
-
* defined in `instanceProps` will be notified up to the template's host
|
|
834
|
-
* automatically.
|
|
835
|
-
* - `instanceProps`: Dictionary of property names that will be added
|
|
836
|
-
* to the instance by the templatize owner. These properties shadow any
|
|
837
|
-
* host properties, and changes within the template to these properties
|
|
838
|
-
* will result in `notifyInstanceProp` being called.
|
|
839
|
-
* - `mutableData`: When `true`, the generated class will skip strict
|
|
840
|
-
* dirty-checking for objects and arrays (always consider them to be
|
|
841
|
-
* "dirty").
|
|
842
|
-
* - `notifyInstanceProp(instance, property, value)`: Called when
|
|
843
|
-
* an instance property changes. Users may choose to call `notifyPath`
|
|
844
|
-
* on e.g. the owner to notify the change.
|
|
845
|
-
* - `parentModel`: When `true`, events handled by declarative event listeners
|
|
846
|
-
* (`on-event="handler"`) will be decorated with a `model` property pointing
|
|
847
|
-
* to the template instance that stamped it. It will also be returned
|
|
848
|
-
* from `instance.parentModel` in cases where template instance nesting
|
|
849
|
-
* causes an inner model to shadow an outer model.
|
|
850
|
-
*
|
|
851
|
-
* All callbacks are called bound to the `owner`. Any context
|
|
852
|
-
* needed for the callbacks (such as references to `instances` stamped)
|
|
853
|
-
* should be stored on the `owner` such that they can be retrieved via
|
|
854
|
-
* `this`.
|
|
855
|
-
*
|
|
856
|
-
* When `options.forwardHostProp` is declared as an option, any properties
|
|
857
|
-
* referenced in the template will be automatically forwarded from the host of
|
|
858
|
-
* the `<template>` to instances, with the exception of any properties listed in
|
|
859
|
-
* the `options.instanceProps` object. `instanceProps` are assumed to be
|
|
860
|
-
* managed by the owner of the instances, either passed into the constructor
|
|
861
|
-
* or set after the fact. Note, any properties passed into the constructor will
|
|
862
|
-
* always be set to the instance (regardless of whether they would normally
|
|
863
|
-
* be forwarded from the host).
|
|
864
|
-
*
|
|
865
|
-
* Note that `templatize()` can be run only once for a given `<template>`.
|
|
866
|
-
* Further calls will result in an error. Also, there is a special
|
|
867
|
-
* behavior if the template was duplicated through a mechanism such as
|
|
868
|
-
* `<dom-repeat>` or `<test-fixture>`. In this case, all calls to
|
|
869
|
-
* `templatize()` return the same class for all duplicates of a template.
|
|
870
|
-
* The class returned from `templatize()` is generated only once using
|
|
871
|
-
* the `options` from the first call. This means that any `options`
|
|
872
|
-
* provided to subsequent calls will be ignored. Therefore, it is very
|
|
873
|
-
* important not to close over any variables inside the callbacks. Also,
|
|
874
|
-
* arrow functions must be avoided because they bind the outer `this`.
|
|
875
|
-
* Inside the callbacks, any contextual information can be accessed
|
|
876
|
-
* through `this`, which points to the `owner`.
|
|
510
|
+
* @license
|
|
511
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
512
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
513
|
+
*/
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* A controller for handling modal state on the elements with `dialog` and `alertdialog` role.
|
|
517
|
+
* See https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-modal
|
|
877
518
|
*
|
|
878
|
-
*
|
|
879
|
-
*
|
|
880
|
-
* any optional callbacks will be bound to this owner.
|
|
881
|
-
* @param {Object=} options Options dictionary (see summary for details)
|
|
882
|
-
* @return {function(new:TemplateInstanceBase, Object=)} Generated class bound
|
|
883
|
-
* to the template provided
|
|
884
|
-
* @suppress {invalidCasts}
|
|
519
|
+
* Note, the actual `role` and `aria-modal` attributes are supposed to be handled by the
|
|
520
|
+
* consumer web component. This is done in to ensure the controller only does one thing.
|
|
885
521
|
*/
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
522
|
+
class AriaModalController {
|
|
523
|
+
/**
|
|
524
|
+
* @param {HTMLElement} host
|
|
525
|
+
*/
|
|
526
|
+
constructor(host, callback) {
|
|
527
|
+
/**
|
|
528
|
+
* The controller host element.
|
|
529
|
+
*
|
|
530
|
+
* @type {HTMLElement}
|
|
531
|
+
*/
|
|
532
|
+
this.host = host;
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* The callback used to detect which element
|
|
536
|
+
* to use as a target. Defaults to the host.
|
|
537
|
+
*
|
|
538
|
+
* @type {Function}
|
|
539
|
+
*/
|
|
540
|
+
this.callback = typeof callback === 'function' ? callback : () => host;
|
|
896
541
|
}
|
|
897
|
-
|
|
898
|
-
const ctor = owner ? owner.constructor : TemplateInstanceBase;
|
|
899
|
-
let templateInfo = ctor._parseTemplate(template);
|
|
900
|
-
// Get memoized base class for the prototypical template, which
|
|
901
|
-
// includes property effects for binding template & forwarding
|
|
542
|
+
|
|
902
543
|
/**
|
|
903
|
-
*
|
|
904
|
-
*
|
|
544
|
+
* Make the controller host modal by hiding other elements from screen readers
|
|
545
|
+
* using `aria-hidden` attribute (can be replaced with `inert` in the future).
|
|
546
|
+
*
|
|
547
|
+
* The method name is chosen to align with the one provided by native `<dialog>`:
|
|
548
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal
|
|
905
549
|
*/
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
templateInfo.templatizeInstanceClass = baseClass;
|
|
550
|
+
showModal() {
|
|
551
|
+
const targets = this.callback();
|
|
552
|
+
this.__showOthers = hideOthers(targets);
|
|
910
553
|
}
|
|
911
|
-
const methodHost = findMethodHost(template);
|
|
912
|
-
// Host property forwarding must be installed onto template instance
|
|
913
|
-
addPropagateEffects(template, templateInfo, options, methodHost);
|
|
914
|
-
// Subclass base class and add reference for this specific template
|
|
915
|
-
/** @private */
|
|
916
|
-
let klass = class TemplateInstance extends baseClass {};
|
|
917
|
-
/** @override */
|
|
918
|
-
klass.prototype._methodHost = methodHost;
|
|
919
|
-
/** @override */
|
|
920
|
-
klass.prototype.__dataHost = /** @type {!DataTemplate} */ (template);
|
|
921
|
-
/** @override */
|
|
922
|
-
klass.prototype.__templatizeOwner = /** @type {!Object} */ (owner);
|
|
923
|
-
/** @override */
|
|
924
|
-
klass.prototype.__hostProps = templateInfo.hostProps;
|
|
925
|
-
klass = /** @type {function(new:TemplateInstanceBase)} */(klass); //eslint-disable-line no-self-assign
|
|
926
|
-
return klass;
|
|
927
|
-
}
|
|
928
554
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
const effects = propertyEffects[prop];
|
|
938
|
-
for (let i=0; i<effects.length; i++) {
|
|
939
|
-
const {part} = effects[i].info;
|
|
940
|
-
if (!(part.signature && part.signature.static)) {
|
|
941
|
-
console.warn(`Property '${prop}' used in template but not ` +
|
|
942
|
-
`declared in 'properties'; attribute will not be observed.`);
|
|
943
|
-
break;
|
|
944
|
-
}
|
|
945
|
-
}
|
|
555
|
+
/**
|
|
556
|
+
* Remove `aria-hidden` from other elements unless there are any other
|
|
557
|
+
* controller hosts on the page activated by using `showModal()` call.
|
|
558
|
+
*/
|
|
559
|
+
close() {
|
|
560
|
+
if (this.__showOthers) {
|
|
561
|
+
this.__showOthers();
|
|
562
|
+
this.__showOthers = null;
|
|
946
563
|
}
|
|
947
564
|
}
|
|
948
565
|
}
|
|
949
566
|
|
|
950
567
|
/**
|
|
951
|
-
*
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
* `TemplateInstanceBase`, and should be used to manipulate data
|
|
955
|
-
* associated with this template instance.
|
|
956
|
-
*
|
|
957
|
-
* Example:
|
|
958
|
-
*
|
|
959
|
-
* let model = modelForElement(el);
|
|
960
|
-
* if (model.index < 10) {
|
|
961
|
-
* model.set('item.checked', true);
|
|
962
|
-
* }
|
|
963
|
-
*
|
|
964
|
-
* @param {HTMLElement} template The model will be returned for
|
|
965
|
-
* elements stamped from this template (accepts either an HTMLTemplateElement)
|
|
966
|
-
* or a `<dom-if>`/`<dom-repeat>` element when using `removeNestedTemplates`
|
|
967
|
-
* optimization.
|
|
968
|
-
* @param {Node=} node Node for which to return a template model.
|
|
969
|
-
* @return {TemplateInstanceBase} Template instance representing the
|
|
970
|
-
* binding scope for the element
|
|
568
|
+
* @license
|
|
569
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
570
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
971
571
|
*/
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* A controller for saving a focused node and restoring focus to it later.
|
|
575
|
+
*/
|
|
576
|
+
class FocusRestorationController {
|
|
577
|
+
/**
|
|
578
|
+
* Saves the given node as a target for restoring focus to
|
|
579
|
+
* when `restoreFocus()` is called. If no node is provided,
|
|
580
|
+
* the currently focused node in the DOM is saved as a target.
|
|
581
|
+
*
|
|
582
|
+
* @param {Node | null | undefined} focusNode
|
|
583
|
+
*/
|
|
584
|
+
saveFocus(focusNode) {
|
|
585
|
+
this.focusNode = focusNode || getDeepActiveElement();
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Restores focus to the target node that was saved previously with `saveFocus()`.
|
|
590
|
+
*/
|
|
591
|
+
restoreFocus() {
|
|
592
|
+
const focusNode = this.focusNode;
|
|
593
|
+
if (!focusNode) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (getDeepActiveElement() === document.body) {
|
|
598
|
+
// In Firefox and Safari, focusing the node synchronously
|
|
599
|
+
// doesn't work as expected when the overlay is closing on outside click.
|
|
600
|
+
// These browsers force focus to move to the body element and retain it
|
|
601
|
+
// there until the next event loop iteration.
|
|
602
|
+
setTimeout(() => focusNode.focus());
|
|
986
603
|
} else {
|
|
987
|
-
|
|
988
|
-
// a __templatizeInstance is found
|
|
989
|
-
node = wrap(node).parentNode;
|
|
604
|
+
focusNode.focus();
|
|
990
605
|
}
|
|
606
|
+
|
|
607
|
+
this.focusNode = null;
|
|
991
608
|
}
|
|
992
|
-
return null;
|
|
993
609
|
}
|
|
994
610
|
|
|
995
611
|
/**
|
|
996
612
|
* @license
|
|
997
|
-
* Copyright (c) 2021 -
|
|
613
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
998
614
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
999
615
|
*/
|
|
1000
616
|
|
|
@@ -1026,6 +642,27 @@ class FocusTrapController {
|
|
|
1026
642
|
this.__onKeyDown = this.__onKeyDown.bind(this);
|
|
1027
643
|
}
|
|
1028
644
|
|
|
645
|
+
/**
|
|
646
|
+
* An array of tab-ordered focusable elements inside the trap node.
|
|
647
|
+
*
|
|
648
|
+
* @return {HTMLElement[]}
|
|
649
|
+
* @private
|
|
650
|
+
*/
|
|
651
|
+
get __focusableElements() {
|
|
652
|
+
return getFocusableElements(this.__trapNode);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* The index of the element inside the trap node that currently has focus.
|
|
657
|
+
*
|
|
658
|
+
* @return {HTMLElement | undefined}
|
|
659
|
+
* @private
|
|
660
|
+
*/
|
|
661
|
+
get __focusedElementIndex() {
|
|
662
|
+
const focusableElements = this.__focusableElements;
|
|
663
|
+
return focusableElements.indexOf(focusableElements.filter(isElementFocused).pop());
|
|
664
|
+
}
|
|
665
|
+
|
|
1029
666
|
hostConnected() {
|
|
1030
667
|
document.addEventListener('keydown', this.__onKeyDown);
|
|
1031
668
|
}
|
|
@@ -1124,1123 +761,845 @@ class FocusTrapController {
|
|
|
1124
761
|
element.select();
|
|
1125
762
|
}
|
|
1126
763
|
}
|
|
1127
|
-
|
|
1128
|
-
/**
|
|
1129
|
-
* An array of tab-ordered focusable elements inside the trap node.
|
|
1130
|
-
*
|
|
1131
|
-
* @return {HTMLElement[]}
|
|
1132
|
-
* @private
|
|
1133
|
-
*/
|
|
1134
|
-
get __focusableElements() {
|
|
1135
|
-
return getFocusableElements(this.__trapNode);
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
/**
|
|
1139
|
-
* The index of the element inside the trap node that currently has focus.
|
|
1140
|
-
*
|
|
1141
|
-
* @return {HTMLElement | undefined}
|
|
1142
|
-
* @private
|
|
1143
|
-
*/
|
|
1144
|
-
get __focusedElementIndex() {
|
|
1145
|
-
const focusableElements = this.__focusableElements;
|
|
1146
|
-
return focusableElements.indexOf(focusableElements.filter(isElementFocused).pop());
|
|
1147
|
-
}
|
|
1148
764
|
}
|
|
1149
765
|
|
|
1150
766
|
/**
|
|
1151
767
|
* @license
|
|
1152
|
-
* Copyright (c)
|
|
768
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
1153
769
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
1154
770
|
*/
|
|
1155
771
|
|
|
1156
772
|
/**
|
|
773
|
+
* @typedef ReactiveController
|
|
774
|
+
* @type {import('lit').ReactiveController}
|
|
775
|
+
*/
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* A mixin for connecting controllers to the element.
|
|
1157
779
|
*
|
|
1158
|
-
*
|
|
1159
|
-
* can be populated in two ways: imperatively by using renderer callback function and
|
|
1160
|
-
* declaratively by using Polymer's Templates.
|
|
1161
|
-
*
|
|
1162
|
-
* ### Rendering
|
|
1163
|
-
*
|
|
1164
|
-
* By default, the overlay uses the content provided by using the renderer callback function.
|
|
1165
|
-
*
|
|
1166
|
-
* The renderer function provides `root`, `owner`, `model` arguments when applicable.
|
|
1167
|
-
* Generate DOM content by using `model` object properties if needed, append it to the `root`
|
|
1168
|
-
* element and control the state of the host element by accessing `owner`. Before generating new
|
|
1169
|
-
* content, users are able to check if there is already content in `root` for reusing it.
|
|
1170
|
-
*
|
|
1171
|
-
* ```html
|
|
1172
|
-
* <vaadin-overlay id="overlay"></vaadin-overlay>
|
|
1173
|
-
* ```
|
|
1174
|
-
* ```js
|
|
1175
|
-
* const overlay = document.querySelector('#overlay');
|
|
1176
|
-
* overlay.renderer = function(root) {
|
|
1177
|
-
* root.textContent = "Overlay content";
|
|
1178
|
-
* };
|
|
1179
|
-
* ```
|
|
1180
|
-
*
|
|
1181
|
-
* Renderer is called on the opening of the overlay and each time the related model is updated.
|
|
1182
|
-
* DOM generated during the renderer call can be reused
|
|
1183
|
-
* in the next renderer call and will be provided with the `root` argument.
|
|
1184
|
-
* On first call it will be empty.
|
|
1185
|
-
*
|
|
1186
|
-
* **NOTE:** when the renderer property is defined, the `<template>` content is not used.
|
|
1187
|
-
*
|
|
1188
|
-
* ### Templating
|
|
1189
|
-
*
|
|
1190
|
-
* Alternatively, the content can be provided with Polymer Template.
|
|
1191
|
-
* Overlay finds the first child template and uses that in case renderer callback function
|
|
1192
|
-
* is not provided. You can also set a custom template using the `template` property.
|
|
1193
|
-
*
|
|
1194
|
-
* After the content from the template is stamped, the `content` property
|
|
1195
|
-
* points to the content container.
|
|
1196
|
-
*
|
|
1197
|
-
* The overlay provides `forwardHostProp` when calling
|
|
1198
|
-
* `Polymer.Templatize.templatize` for the template, so that the bindings
|
|
1199
|
-
* from the parent scope propagate to the content.
|
|
1200
|
-
*
|
|
1201
|
-
* ### Styling
|
|
1202
|
-
*
|
|
1203
|
-
* To style the overlay content, use styles in the parent scope:
|
|
1204
|
-
*
|
|
1205
|
-
* - If the overlay is used in a component, then the component styles
|
|
1206
|
-
* apply the overlay content.
|
|
1207
|
-
* - If the overlay is used in the global DOM scope, then global styles
|
|
1208
|
-
* apply to the overlay content.
|
|
1209
|
-
*
|
|
1210
|
-
* See examples for styling the overlay content in the live demos.
|
|
1211
|
-
*
|
|
1212
|
-
* The following Shadow DOM parts are available for styling the overlay component itself:
|
|
1213
|
-
*
|
|
1214
|
-
* Part name | Description
|
|
1215
|
-
* -----------|---------------------------------------------------------|
|
|
1216
|
-
* `backdrop` | Backdrop of the overlay
|
|
1217
|
-
* `overlay` | Container for position/sizing/alignment of the content
|
|
1218
|
-
* `content` | Content of the overlay
|
|
1219
|
-
*
|
|
1220
|
-
* The following state attributes are available for styling:
|
|
1221
|
-
*
|
|
1222
|
-
* Attribute | Description | Part
|
|
1223
|
-
* ---|---|---
|
|
1224
|
-
* `opening` | Applied just after the overlay is attached to the DOM. You can apply a CSS @keyframe animation for this state. | `:host`
|
|
1225
|
-
* `closing` | Applied just before the overlay is detached from the DOM. You can apply a CSS @keyframe animation for this state. | `:host`
|
|
1226
|
-
*
|
|
1227
|
-
* The following custom CSS properties are available for styling:
|
|
1228
|
-
*
|
|
1229
|
-
* Custom CSS property | Description | Default value
|
|
1230
|
-
* ---|---|---
|
|
1231
|
-
* `--vaadin-overlay-viewport-bottom` | Bottom offset of the visible viewport area | `0` or detected offset
|
|
1232
|
-
*
|
|
1233
|
-
* See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
|
|
1234
|
-
*
|
|
1235
|
-
* @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
|
|
1236
|
-
* @fires {CustomEvent} vaadin-overlay-open - Fired after the overlay is opened.
|
|
1237
|
-
* @fires {CustomEvent} vaadin-overlay-close - Fired before the overlay will be closed. If canceled the closing of the overlay is canceled as well.
|
|
1238
|
-
* @fires {CustomEvent} vaadin-overlay-closing - Fired when the overlay will be closed.
|
|
1239
|
-
* @fires {CustomEvent} vaadin-overlay-outside-click - Fired before the overlay will be closed on outside click. If canceled the closing of the overlay is canceled as well.
|
|
1240
|
-
* @fires {CustomEvent} vaadin-overlay-escape-press - Fired before the overlay will be closed on ESC button press. If canceled the closing of the overlay is canceled as well.
|
|
1241
|
-
*
|
|
1242
|
-
* @extends HTMLElement
|
|
1243
|
-
* @mixes ThemableMixin
|
|
1244
|
-
* @mixes DirMixin
|
|
1245
|
-
* @mixes ControllerMixin
|
|
780
|
+
* @polymerMixin
|
|
1246
781
|
*/
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
position: fixed;
|
|
1254
|
-
|
|
1255
|
-
/* Despite of what the names say, <vaadin-overlay> is just a container
|
|
1256
|
-
for position/sizing/alignment. The actual overlay is the overlay part. */
|
|
782
|
+
const ControllerMixin = dedupingMixin((superClass) => {
|
|
783
|
+
// If the superclass extends from LitElement,
|
|
784
|
+
// use its own controllers implementation.
|
|
785
|
+
if (typeof superClass.prototype.addController === 'function') {
|
|
786
|
+
return superClass;
|
|
787
|
+
}
|
|
1257
788
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
right: 0;
|
|
1262
|
-
bottom: var(--vaadin-overlay-viewport-bottom);
|
|
1263
|
-
left: 0;
|
|
789
|
+
return class ControllerMixinClass extends superClass {
|
|
790
|
+
constructor() {
|
|
791
|
+
super();
|
|
1264
792
|
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
justify-content: center;
|
|
793
|
+
/**
|
|
794
|
+
* @type {Set<ReactiveController>}
|
|
795
|
+
*/
|
|
796
|
+
this.__controllers = new Set();
|
|
797
|
+
}
|
|
1271
798
|
|
|
1272
|
-
|
|
1273
|
-
|
|
799
|
+
/** @protected */
|
|
800
|
+
connectedCallback() {
|
|
801
|
+
super.connectedCallback();
|
|
1274
802
|
|
|
1275
|
-
|
|
1276
|
-
|
|
803
|
+
this.__controllers.forEach((c) => {
|
|
804
|
+
if (c.hostConnected) {
|
|
805
|
+
c.hostConnected();
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
}
|
|
1277
809
|
|
|
1278
|
-
|
|
1279
|
-
|
|
810
|
+
/** @protected */
|
|
811
|
+
disconnectedCallback() {
|
|
812
|
+
super.disconnectedCallback();
|
|
1280
813
|
|
|
1281
|
-
|
|
1282
|
-
|
|
814
|
+
this.__controllers.forEach((c) => {
|
|
815
|
+
if (c.hostDisconnected) {
|
|
816
|
+
c.hostDisconnected();
|
|
1283
817
|
}
|
|
818
|
+
});
|
|
819
|
+
}
|
|
1284
820
|
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
821
|
+
/**
|
|
822
|
+
* Registers a controller to participate in the element update cycle.
|
|
823
|
+
*
|
|
824
|
+
* @param {ReactiveController} controller
|
|
825
|
+
* @protected
|
|
826
|
+
*/
|
|
827
|
+
addController(controller) {
|
|
828
|
+
this.__controllers.add(controller);
|
|
829
|
+
// Call hostConnected if a controller is added after the element is attached.
|
|
830
|
+
if (this.$ !== undefined && this.isConnected && controller.hostConnected) {
|
|
831
|
+
controller.hostConnected();
|
|
832
|
+
}
|
|
833
|
+
}
|
|
1289
834
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
835
|
+
/**
|
|
836
|
+
* Removes a controller from the element.
|
|
837
|
+
*
|
|
838
|
+
* @param {ReactiveController} controller
|
|
839
|
+
* @protected
|
|
840
|
+
*/
|
|
841
|
+
removeController(controller) {
|
|
842
|
+
this.__controllers.delete(controller);
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
});
|
|
1294
846
|
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
847
|
+
/**
|
|
848
|
+
* @license
|
|
849
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
850
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
851
|
+
*/
|
|
1298
852
|
|
|
1299
|
-
|
|
1300
|
-
|
|
853
|
+
/**
|
|
854
|
+
* @polymerMixin
|
|
855
|
+
* @mixes ControllerMixin
|
|
856
|
+
*/
|
|
857
|
+
const OverlayFocusMixin = (superClass) =>
|
|
858
|
+
class OverlayFocusMixin extends ControllerMixin(superClass) {
|
|
859
|
+
static get properties() {
|
|
860
|
+
return {
|
|
861
|
+
/**
|
|
862
|
+
* When true, opening the overlay moves focus to the first focusable child,
|
|
863
|
+
* or to the overlay part with tabindex if there are no focusable children.
|
|
864
|
+
* @attr {boolean} focus-trap
|
|
865
|
+
*/
|
|
866
|
+
focusTrap: {
|
|
867
|
+
type: Boolean,
|
|
868
|
+
value: false,
|
|
869
|
+
},
|
|
1301
870
|
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
right: 0;
|
|
1311
|
-
pointer-events: auto;
|
|
1312
|
-
}
|
|
1313
|
-
</style>
|
|
1314
|
-
|
|
1315
|
-
<div id="backdrop" part="backdrop" hidden$="[[!withBackdrop]]"></div>
|
|
1316
|
-
<div part="overlay" id="overlay" tabindex="0">
|
|
1317
|
-
<div part="content" id="content">
|
|
1318
|
-
<slot></slot>
|
|
1319
|
-
</div>
|
|
1320
|
-
</div>
|
|
1321
|
-
`;
|
|
1322
|
-
}
|
|
871
|
+
/**
|
|
872
|
+
* Set to true to enable restoring of focus when overlay is closed.
|
|
873
|
+
* @attr {boolean} restore-focus-on-close
|
|
874
|
+
*/
|
|
875
|
+
restoreFocusOnClose: {
|
|
876
|
+
type: Boolean,
|
|
877
|
+
value: false,
|
|
878
|
+
},
|
|
1323
879
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
880
|
+
/**
|
|
881
|
+
* Set to specify the element which should be focused on overlay close,
|
|
882
|
+
* if `restoreFocusOnClose` is set to true.
|
|
883
|
+
* @type {HTMLElement}
|
|
884
|
+
*/
|
|
885
|
+
restoreFocusNode: {
|
|
886
|
+
type: HTMLElement,
|
|
887
|
+
},
|
|
888
|
+
};
|
|
889
|
+
}
|
|
1327
890
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
/**
|
|
1331
|
-
* When true, the overlay is visible and attached to body.
|
|
1332
|
-
*/
|
|
1333
|
-
opened: {
|
|
1334
|
-
type: Boolean,
|
|
1335
|
-
notify: true,
|
|
1336
|
-
observer: '_openedChanged',
|
|
1337
|
-
reflectToAttribute: true,
|
|
1338
|
-
},
|
|
891
|
+
constructor() {
|
|
892
|
+
super();
|
|
1339
893
|
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
owner: Element,
|
|
894
|
+
this.__ariaModalController = new AriaModalController(this);
|
|
895
|
+
this.__focusTrapController = new FocusTrapController(this);
|
|
896
|
+
this.__focusRestorationController = new FocusRestorationController();
|
|
897
|
+
}
|
|
1345
898
|
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
*
|
|
1350
|
-
* - `root` The root container DOM element. Append your content to it.
|
|
1351
|
-
* - `owner` The host element of the renderer function.
|
|
1352
|
-
* - `model` The object with the properties related with rendering.
|
|
1353
|
-
* @type {OverlayRenderer | null | undefined}
|
|
1354
|
-
*/
|
|
1355
|
-
renderer: Function,
|
|
899
|
+
/** @protected */
|
|
900
|
+
ready() {
|
|
901
|
+
super.ready();
|
|
1356
902
|
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
*/
|
|
1361
|
-
template: {
|
|
1362
|
-
type: Object,
|
|
1363
|
-
notify: true,
|
|
1364
|
-
},
|
|
1365
|
-
|
|
1366
|
-
/**
|
|
1367
|
-
* References the content container after the template is stamped.
|
|
1368
|
-
* @type {!HTMLElement | undefined}
|
|
1369
|
-
*/
|
|
1370
|
-
content: {
|
|
1371
|
-
type: Object,
|
|
1372
|
-
notify: true,
|
|
1373
|
-
},
|
|
1374
|
-
|
|
1375
|
-
/**
|
|
1376
|
-
* When true the overlay has backdrop on top of content when opened.
|
|
1377
|
-
* @type {boolean}
|
|
1378
|
-
*/
|
|
1379
|
-
withBackdrop: {
|
|
1380
|
-
type: Boolean,
|
|
1381
|
-
value: false,
|
|
1382
|
-
reflectToAttribute: true,
|
|
1383
|
-
},
|
|
1384
|
-
|
|
1385
|
-
/**
|
|
1386
|
-
* Object with properties that is passed to `renderer` function
|
|
1387
|
-
*/
|
|
1388
|
-
model: Object,
|
|
1389
|
-
|
|
1390
|
-
/**
|
|
1391
|
-
* When true the overlay won't disable the main content, showing
|
|
1392
|
-
* it doesn’t change the functionality of the user interface.
|
|
1393
|
-
* @type {boolean}
|
|
1394
|
-
*/
|
|
1395
|
-
modeless: {
|
|
1396
|
-
type: Boolean,
|
|
1397
|
-
value: false,
|
|
1398
|
-
reflectToAttribute: true,
|
|
1399
|
-
observer: '_modelessChanged',
|
|
1400
|
-
},
|
|
1401
|
-
|
|
1402
|
-
/**
|
|
1403
|
-
* When set to true, the overlay is hidden. This also closes the overlay
|
|
1404
|
-
* immediately in case there is a closing animation in progress.
|
|
1405
|
-
* @type {boolean}
|
|
1406
|
-
*/
|
|
1407
|
-
hidden: {
|
|
1408
|
-
type: Boolean,
|
|
1409
|
-
reflectToAttribute: true,
|
|
1410
|
-
observer: '_hiddenChanged',
|
|
1411
|
-
},
|
|
1412
|
-
|
|
1413
|
-
/**
|
|
1414
|
-
* When true move focus to the first focusable element in the overlay,
|
|
1415
|
-
* or to the overlay if there are no focusable elements.
|
|
1416
|
-
* @type {boolean}
|
|
1417
|
-
*/
|
|
1418
|
-
focusTrap: {
|
|
1419
|
-
type: Boolean,
|
|
1420
|
-
value: false,
|
|
1421
|
-
},
|
|
1422
|
-
|
|
1423
|
-
/**
|
|
1424
|
-
* Set to true to enable restoring of focus when overlay is closed.
|
|
1425
|
-
* @type {boolean}
|
|
1426
|
-
*/
|
|
1427
|
-
restoreFocusOnClose: {
|
|
1428
|
-
type: Boolean,
|
|
1429
|
-
value: false,
|
|
1430
|
-
},
|
|
1431
|
-
|
|
1432
|
-
/**
|
|
1433
|
-
* Set to specify the element which should be focused on overlay close,
|
|
1434
|
-
* if `restoreFocusOnClose` is set to true.
|
|
1435
|
-
* @type {HTMLElement}
|
|
1436
|
-
*/
|
|
1437
|
-
restoreFocusNode: {
|
|
1438
|
-
type: HTMLElement,
|
|
1439
|
-
},
|
|
1440
|
-
|
|
1441
|
-
/** @private */
|
|
1442
|
-
_mouseDownInside: {
|
|
1443
|
-
type: Boolean,
|
|
1444
|
-
},
|
|
1445
|
-
|
|
1446
|
-
/** @private */
|
|
1447
|
-
_mouseUpInside: {
|
|
1448
|
-
type: Boolean,
|
|
1449
|
-
},
|
|
1450
|
-
|
|
1451
|
-
/** @private */
|
|
1452
|
-
_instance: {
|
|
1453
|
-
type: Object,
|
|
1454
|
-
},
|
|
1455
|
-
|
|
1456
|
-
/** @private */
|
|
1457
|
-
_originalContentPart: Object,
|
|
1458
|
-
|
|
1459
|
-
/** @private */
|
|
1460
|
-
_contentNodes: Array,
|
|
1461
|
-
|
|
1462
|
-
/** @private */
|
|
1463
|
-
_oldOwner: Element,
|
|
1464
|
-
|
|
1465
|
-
/** @private */
|
|
1466
|
-
_oldModel: Object,
|
|
1467
|
-
|
|
1468
|
-
/** @private */
|
|
1469
|
-
_oldTemplate: Object,
|
|
1470
|
-
|
|
1471
|
-
/** @private */
|
|
1472
|
-
_oldRenderer: Object,
|
|
1473
|
-
|
|
1474
|
-
/** @private */
|
|
1475
|
-
_oldOpened: Boolean,
|
|
1476
|
-
};
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
static get observers() {
|
|
1480
|
-
return ['_templateOrRendererChanged(template, renderer, owner, model, opened)'];
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
constructor() {
|
|
1484
|
-
super();
|
|
1485
|
-
this._boundMouseDownListener = this._mouseDownListener.bind(this);
|
|
1486
|
-
this._boundMouseUpListener = this._mouseUpListener.bind(this);
|
|
1487
|
-
this._boundOutsideClickListener = this._outsideClickListener.bind(this);
|
|
1488
|
-
this._boundKeydownListener = this._keydownListener.bind(this);
|
|
1489
|
-
|
|
1490
|
-
this._observer = new FlattenedNodesObserver(this, (info) => {
|
|
1491
|
-
this._setTemplateFromNodes(info.addedNodes);
|
|
1492
|
-
});
|
|
1493
|
-
|
|
1494
|
-
// Listener for preventing closing of the paper-dialog and all components extending `iron-overlay-behavior`.
|
|
1495
|
-
this._boundIronOverlayCanceledListener = this._ironOverlayCanceled.bind(this);
|
|
1496
|
-
|
|
1497
|
-
/* c8 ignore next 3 */
|
|
1498
|
-
if (isIOS) {
|
|
1499
|
-
this._boundIosResizeListener = () => this._detectIosNavbar();
|
|
903
|
+
this.addController(this.__ariaModalController);
|
|
904
|
+
this.addController(this.__focusTrapController);
|
|
905
|
+
this.addController(this.__focusRestorationController);
|
|
1500
906
|
}
|
|
1501
907
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
// the document click event listener (_outsideClickListener) may never
|
|
1513
|
-
// get invoked on iOS Safari (reproducible in <vaadin-dialog>
|
|
1514
|
-
// and <vaadin-context-menu>).
|
|
1515
|
-
this.addEventListener('click', () => {});
|
|
1516
|
-
this.$.backdrop.addEventListener('click', () => {});
|
|
1517
|
-
|
|
1518
|
-
this.addController(this.__focusTrapController);
|
|
1519
|
-
}
|
|
908
|
+
/**
|
|
909
|
+
* Release focus and restore focus after the overlay is closed.
|
|
910
|
+
*
|
|
911
|
+
* @protected
|
|
912
|
+
*/
|
|
913
|
+
_resetFocus() {
|
|
914
|
+
if (this.focusTrap) {
|
|
915
|
+
this.__ariaModalController.close();
|
|
916
|
+
this.__focusTrapController.releaseFocus();
|
|
917
|
+
}
|
|
1520
918
|
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
if (!this.opened) {
|
|
1525
|
-
return;
|
|
919
|
+
if (this.restoreFocusOnClose && this._shouldRestoreFocus()) {
|
|
920
|
+
this.__focusRestorationController.restoreFocus();
|
|
921
|
+
}
|
|
1526
922
|
}
|
|
1527
923
|
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
} else {
|
|
1538
|
-
this.style.setProperty('--vaadin-overlay-viewport-bottom', '0');
|
|
924
|
+
/**
|
|
925
|
+
* Save the previously focused node when the overlay starts to open.
|
|
926
|
+
*
|
|
927
|
+
* @protected
|
|
928
|
+
*/
|
|
929
|
+
_saveFocus() {
|
|
930
|
+
if (this.restoreFocusOnClose) {
|
|
931
|
+
this.__focusRestorationController.saveFocus(this.restoreFocusNode);
|
|
932
|
+
}
|
|
1539
933
|
}
|
|
1540
|
-
}
|
|
1541
934
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
* @event vaadin-overlay-close
|
|
1553
|
-
* fired before the `vaadin-overlay` will be closed. If canceled the closing of the overlay is canceled as well.
|
|
1554
|
-
*/
|
|
1555
|
-
close(sourceEvent) {
|
|
1556
|
-
const evt = new CustomEvent('vaadin-overlay-close', {
|
|
1557
|
-
bubbles: true,
|
|
1558
|
-
cancelable: true,
|
|
1559
|
-
detail: { sourceEvent },
|
|
1560
|
-
});
|
|
1561
|
-
this.dispatchEvent(evt);
|
|
1562
|
-
if (!evt.defaultPrevented) {
|
|
1563
|
-
this.opened = false;
|
|
935
|
+
/**
|
|
936
|
+
* Trap focus within the overlay after opening has completed.
|
|
937
|
+
*
|
|
938
|
+
* @protected
|
|
939
|
+
*/
|
|
940
|
+
_trapFocus() {
|
|
941
|
+
if (this.focusTrap) {
|
|
942
|
+
this.__ariaModalController.showModal();
|
|
943
|
+
this.__focusTrapController.trapFocus(this.$.overlay);
|
|
944
|
+
}
|
|
1564
945
|
}
|
|
1565
|
-
}
|
|
1566
946
|
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
947
|
+
/**
|
|
948
|
+
* Returns true if focus is still inside the overlay or on the body element,
|
|
949
|
+
* otherwise false.
|
|
950
|
+
*
|
|
951
|
+
* Focus shouldn't be restored if it's been moved elsewhere by another
|
|
952
|
+
* component or as a result of a user interaction e.g. the user clicked
|
|
953
|
+
* on a button outside the overlay while the overlay was open.
|
|
954
|
+
*
|
|
955
|
+
* @protected
|
|
956
|
+
* @return {boolean}
|
|
957
|
+
*/
|
|
958
|
+
_shouldRestoreFocus() {
|
|
959
|
+
const activeElement = getDeepActiveElement();
|
|
960
|
+
return activeElement === document.body || this._deepContains(activeElement);
|
|
1575
961
|
}
|
|
1576
|
-
}
|
|
1577
962
|
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
963
|
+
/**
|
|
964
|
+
* Returns true if the overlay contains the given node,
|
|
965
|
+
* including those within shadow DOM trees.
|
|
966
|
+
*
|
|
967
|
+
* @param {Node} node
|
|
968
|
+
* @return {boolean}
|
|
969
|
+
* @protected
|
|
970
|
+
*/
|
|
971
|
+
_deepContains(node) {
|
|
972
|
+
if (this.contains(node)) {
|
|
973
|
+
return true;
|
|
974
|
+
}
|
|
975
|
+
let n = node;
|
|
976
|
+
const doc = node.ownerDocument;
|
|
977
|
+
// Walk from node to `this` or `document`
|
|
978
|
+
while (n && n !== doc && n !== this) {
|
|
979
|
+
n = n.parentNode || n.host;
|
|
980
|
+
}
|
|
981
|
+
return n === this;
|
|
1585
982
|
}
|
|
1586
|
-
}
|
|
983
|
+
};
|
|
1587
984
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
*/
|
|
1594
|
-
requestContentUpdate() {
|
|
1595
|
-
if (this.renderer) {
|
|
1596
|
-
this.renderer.call(this.owner, this.content, this.owner, this.model);
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
985
|
+
/**
|
|
986
|
+
* @license
|
|
987
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
988
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
989
|
+
*/
|
|
1599
990
|
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
991
|
+
/**
|
|
992
|
+
* Returns all attached overlays in visual stacking order.
|
|
993
|
+
* @private
|
|
994
|
+
*/
|
|
995
|
+
const getAttachedInstances = () =>
|
|
996
|
+
Array.from(document.body.children)
|
|
997
|
+
.filter((el) => el instanceof HTMLElement && el._hasOverlayStackMixin && !el.hasAttribute('closing'))
|
|
998
|
+
.sort((a, b) => a.__zIndex - b.__zIndex || 0);
|
|
1604
999
|
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1000
|
+
/**
|
|
1001
|
+
* Returns true if the overlay is the last one in the opened overlays stack.
|
|
1002
|
+
* @param {HTMLElement} overlay
|
|
1003
|
+
* @return {boolean}
|
|
1004
|
+
* @protected
|
|
1005
|
+
*/
|
|
1006
|
+
const isLastOverlay = (overlay) => overlay === getAttachedInstances().pop();
|
|
1609
1007
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1008
|
+
/**
|
|
1009
|
+
* @polymerMixin
|
|
1010
|
+
*/
|
|
1011
|
+
const OverlayStackMixin = (superClass) =>
|
|
1012
|
+
class OverlayStackMixin extends superClass {
|
|
1013
|
+
constructor() {
|
|
1014
|
+
super();
|
|
1614
1015
|
|
|
1615
|
-
|
|
1616
|
-
* We need to listen on 'click' / 'tap' event and capture it and close the overlay before
|
|
1617
|
-
* propagating the event to the listener in the button. Otherwise, if the clicked button would call
|
|
1618
|
-
* open(), this would happen: https://www.youtube.com/watch?v=Z86V_ICUCD4
|
|
1619
|
-
*
|
|
1620
|
-
* @event vaadin-overlay-outside-click
|
|
1621
|
-
* fired before the `vaadin-overlay` will be closed on outside click. If canceled the closing of the overlay is canceled as well.
|
|
1622
|
-
*
|
|
1623
|
-
* @private
|
|
1624
|
-
*/
|
|
1625
|
-
_outsideClickListener(event) {
|
|
1626
|
-
if (event.composedPath().includes(this.$.overlay) || this._mouseDownInside || this._mouseUpInside) {
|
|
1627
|
-
this._mouseDownInside = false;
|
|
1628
|
-
this._mouseUpInside = false;
|
|
1629
|
-
return;
|
|
1630
|
-
}
|
|
1631
|
-
if (!this._last) {
|
|
1632
|
-
return;
|
|
1016
|
+
this._hasOverlayStackMixin = true;
|
|
1633
1017
|
}
|
|
1634
1018
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
this.close(event);
|
|
1019
|
+
/**
|
|
1020
|
+
* Returns true if this is the last one in the opened overlays stack.
|
|
1021
|
+
*
|
|
1022
|
+
* @return {boolean}
|
|
1023
|
+
* @protected
|
|
1024
|
+
*/
|
|
1025
|
+
get _last() {
|
|
1026
|
+
return isLastOverlay(this);
|
|
1644
1027
|
}
|
|
1645
|
-
}
|
|
1646
1028
|
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1029
|
+
/**
|
|
1030
|
+
* Brings the overlay as visually the frontmost one.
|
|
1031
|
+
*/
|
|
1032
|
+
bringToFront() {
|
|
1033
|
+
let zIndex = '';
|
|
1034
|
+
const frontmost = getAttachedInstances()
|
|
1035
|
+
.filter((o) => o !== this)
|
|
1036
|
+
.pop();
|
|
1037
|
+
if (frontmost) {
|
|
1038
|
+
const frontmostZIndex = frontmost.__zIndex;
|
|
1039
|
+
zIndex = frontmostZIndex + 1;
|
|
1040
|
+
}
|
|
1041
|
+
this.style.zIndex = zIndex;
|
|
1042
|
+
this.__zIndex = zIndex || parseFloat(getComputedStyle(this).zIndex);
|
|
1656
1043
|
}
|
|
1657
1044
|
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1045
|
+
/** @protected */
|
|
1046
|
+
_enterModalState() {
|
|
1047
|
+
if (document.body.style.pointerEvents !== 'none') {
|
|
1048
|
+
// Set body pointer-events to 'none' to disable mouse interactions with
|
|
1049
|
+
// other document nodes.
|
|
1050
|
+
this._previousDocumentPointerEvents = document.body.style.pointerEvents;
|
|
1051
|
+
document.body.style.pointerEvents = 'none';
|
|
1052
|
+
}
|
|
1662
1053
|
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1054
|
+
// Disable pointer events in other attached overlays
|
|
1055
|
+
getAttachedInstances().forEach((el) => {
|
|
1056
|
+
if (el !== this) {
|
|
1057
|
+
el.$.overlay.style.pointerEvents = 'none';
|
|
1058
|
+
}
|
|
1668
1059
|
});
|
|
1669
|
-
|
|
1060
|
+
}
|
|
1670
1061
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1062
|
+
/** @protected */
|
|
1063
|
+
_exitModalState() {
|
|
1064
|
+
if (this._previousDocumentPointerEvents !== undefined) {
|
|
1065
|
+
// Restore body pointer-events
|
|
1066
|
+
document.body.style.pointerEvents = this._previousDocumentPointerEvents;
|
|
1067
|
+
delete this._previousDocumentPointerEvents;
|
|
1673
1068
|
}
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
1069
|
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
this._setTemplateFromNodes(Array.from(this.children));
|
|
1680
|
-
}
|
|
1070
|
+
// Restore pointer events in the previous overlay(s)
|
|
1071
|
+
const instances = getAttachedInstances();
|
|
1681
1072
|
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1073
|
+
let el;
|
|
1074
|
+
// Use instances.pop() to ensure the reverse order
|
|
1075
|
+
while ((el = instances.pop())) {
|
|
1076
|
+
if (el === this) {
|
|
1077
|
+
// Skip the current instance
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
el.$.overlay.style.removeProperty('pointer-events');
|
|
1081
|
+
if (!el.modeless) {
|
|
1082
|
+
// Stop after the last modal
|
|
1083
|
+
break;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1691
1086
|
}
|
|
1087
|
+
};
|
|
1692
1088
|
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1089
|
+
/**
|
|
1090
|
+
* @license
|
|
1091
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
1092
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
1093
|
+
*/
|
|
1697
1094
|
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1095
|
+
/**
|
|
1096
|
+
* @polymerMixin
|
|
1097
|
+
* @mixes OverlayFocusMixin
|
|
1098
|
+
* @mixes OverlayStackMixin
|
|
1099
|
+
*/
|
|
1100
|
+
const OverlayMixin = (superClass) =>
|
|
1101
|
+
class OverlayMixin extends OverlayFocusMixin(OverlayStackMixin(superClass)) {
|
|
1102
|
+
static get properties() {
|
|
1103
|
+
return {
|
|
1104
|
+
/**
|
|
1105
|
+
* When true, the overlay is visible and attached to body.
|
|
1106
|
+
*/
|
|
1107
|
+
opened: {
|
|
1108
|
+
type: Boolean,
|
|
1109
|
+
notify: true,
|
|
1110
|
+
observer: '_openedChanged',
|
|
1111
|
+
reflectToAttribute: true,
|
|
1112
|
+
},
|
|
1702
1113
|
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1114
|
+
/**
|
|
1115
|
+
* Owner element passed with renderer function
|
|
1116
|
+
* @type {HTMLElement}
|
|
1117
|
+
*/
|
|
1118
|
+
owner: {
|
|
1119
|
+
type: Object,
|
|
1120
|
+
},
|
|
1706
1121
|
|
|
1707
|
-
|
|
1122
|
+
/**
|
|
1123
|
+
* Object with properties that is passed to `renderer` function
|
|
1124
|
+
*/
|
|
1125
|
+
model: {
|
|
1126
|
+
type: Object,
|
|
1127
|
+
},
|
|
1708
1128
|
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1129
|
+
/**
|
|
1130
|
+
* Custom function for rendering the content of the overlay.
|
|
1131
|
+
* Receives three arguments:
|
|
1132
|
+
*
|
|
1133
|
+
* - `root` The root container DOM element. Append your content to it.
|
|
1134
|
+
* - `owner` The host element of the renderer function.
|
|
1135
|
+
* - `model` The object with the properties related with rendering.
|
|
1136
|
+
* @type {OverlayRenderer | null | undefined}
|
|
1137
|
+
*/
|
|
1138
|
+
renderer: {
|
|
1139
|
+
type: Object,
|
|
1140
|
+
},
|
|
1716
1141
|
|
|
1717
|
-
|
|
1142
|
+
/**
|
|
1143
|
+
* When true the overlay won't disable the main content, showing
|
|
1144
|
+
* it doesn't change the functionality of the user interface.
|
|
1145
|
+
* @type {boolean}
|
|
1146
|
+
*/
|
|
1147
|
+
modeless: {
|
|
1148
|
+
type: Boolean,
|
|
1149
|
+
value: false,
|
|
1150
|
+
reflectToAttribute: true,
|
|
1151
|
+
observer: '_modelessChanged',
|
|
1152
|
+
},
|
|
1718
1153
|
|
|
1719
|
-
|
|
1154
|
+
/**
|
|
1155
|
+
* When set to true, the overlay is hidden. This also closes the overlay
|
|
1156
|
+
* immediately in case there is a closing animation in progress.
|
|
1157
|
+
* @type {boolean}
|
|
1158
|
+
*/
|
|
1159
|
+
hidden: {
|
|
1160
|
+
type: Boolean,
|
|
1161
|
+
reflectToAttribute: true,
|
|
1162
|
+
observer: '_hiddenChanged',
|
|
1163
|
+
},
|
|
1720
1164
|
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1165
|
+
/**
|
|
1166
|
+
* When true the overlay has backdrop on top of content when opened.
|
|
1167
|
+
* @type {boolean}
|
|
1168
|
+
*/
|
|
1169
|
+
withBackdrop: {
|
|
1170
|
+
type: Boolean,
|
|
1171
|
+
value: false,
|
|
1172
|
+
reflectToAttribute: true,
|
|
1173
|
+
},
|
|
1174
|
+
};
|
|
1724
1175
|
}
|
|
1725
|
-
}
|
|
1726
1176
|
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
if (hidden && this.hasAttribute('closing')) {
|
|
1730
|
-
this._flushAnimation('closing');
|
|
1177
|
+
static get observers() {
|
|
1178
|
+
return ['_rendererOrDataChanged(renderer, owner, model, opened)'];
|
|
1731
1179
|
}
|
|
1732
|
-
}
|
|
1733
1180
|
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
* @protected
|
|
1737
|
-
*/
|
|
1738
|
-
_shouldAnimate() {
|
|
1739
|
-
const name = getComputedStyle(this).getPropertyValue('animation-name');
|
|
1740
|
-
const hidden = getComputedStyle(this).getPropertyValue('display') === 'none';
|
|
1741
|
-
return !hidden && name && name !== 'none';
|
|
1742
|
-
}
|
|
1181
|
+
constructor() {
|
|
1182
|
+
super();
|
|
1743
1183
|
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
*/
|
|
1749
|
-
_enqueueAnimation(type, callback) {
|
|
1750
|
-
const handler = `__${type}Handler`;
|
|
1751
|
-
const listener = (event) => {
|
|
1752
|
-
if (event && event.target !== this) {
|
|
1753
|
-
return;
|
|
1754
|
-
}
|
|
1755
|
-
callback();
|
|
1756
|
-
this.removeEventListener('animationend', listener);
|
|
1757
|
-
delete this[handler];
|
|
1758
|
-
};
|
|
1759
|
-
this[handler] = listener;
|
|
1760
|
-
this.addEventListener('animationend', listener);
|
|
1761
|
-
}
|
|
1184
|
+
this._boundMouseDownListener = this._mouseDownListener.bind(this);
|
|
1185
|
+
this._boundMouseUpListener = this._mouseUpListener.bind(this);
|
|
1186
|
+
this._boundOutsideClickListener = this._outsideClickListener.bind(this);
|
|
1187
|
+
this._boundKeydownListener = this._keydownListener.bind(this);
|
|
1762
1188
|
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
_flushAnimation(type) {
|
|
1768
|
-
const handler = `__${type}Handler`;
|
|
1769
|
-
if (typeof this[handler] === 'function') {
|
|
1770
|
-
this[handler]();
|
|
1189
|
+
/* c8 ignore next 3 */
|
|
1190
|
+
if (isIOS) {
|
|
1191
|
+
this._boundIosResizeListener = () => this._detectIosNavbar();
|
|
1192
|
+
}
|
|
1771
1193
|
}
|
|
1772
|
-
}
|
|
1773
1194
|
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
this._flushAnimation('closing');
|
|
1778
|
-
}
|
|
1779
|
-
this._attachOverlay();
|
|
1780
|
-
if (!this.modeless) {
|
|
1781
|
-
this._enterModalState();
|
|
1782
|
-
}
|
|
1783
|
-
this.setAttribute('opening', '');
|
|
1195
|
+
/** @protected */
|
|
1196
|
+
ready() {
|
|
1197
|
+
super.ready();
|
|
1784
1198
|
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
this.
|
|
1199
|
+
// Need to add dummy click listeners to this and the backdrop or else
|
|
1200
|
+
// the document click event listener (_outsideClickListener) may never
|
|
1201
|
+
// get invoked on iOS Safari (reproducible in <vaadin-dialog>
|
|
1202
|
+
// and <vaadin-context-menu>).
|
|
1203
|
+
this.addEventListener('click', () => {});
|
|
1204
|
+
this.$.backdrop.addEventListener('click', () => {});
|
|
1791
1205
|
}
|
|
1792
|
-
}
|
|
1793
|
-
|
|
1794
|
-
/** @protected */
|
|
1795
|
-
_attachOverlay() {
|
|
1796
|
-
this._placeholder = document.createComment('vaadin-overlay-placeholder');
|
|
1797
|
-
this.parentNode.insertBefore(this._placeholder, this);
|
|
1798
|
-
document.body.appendChild(this);
|
|
1799
|
-
this.bringToFront();
|
|
1800
|
-
}
|
|
1801
|
-
|
|
1802
|
-
/** @protected */
|
|
1803
|
-
_finishOpening() {
|
|
1804
|
-
document.addEventListener('iron-overlay-canceled', this._boundIronOverlayCanceledListener);
|
|
1805
|
-
this.removeAttribute('opening');
|
|
1806
|
-
}
|
|
1807
1206
|
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
this._detachOverlay();
|
|
1812
|
-
this.$.overlay.style.removeProperty('pointer-events');
|
|
1813
|
-
this.removeAttribute('closing');
|
|
1814
|
-
}
|
|
1207
|
+
/** @protected */
|
|
1208
|
+
connectedCallback() {
|
|
1209
|
+
super.connectedCallback();
|
|
1815
1210
|
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
* @protected
|
|
1821
|
-
*/
|
|
1822
|
-
_animatedClosing() {
|
|
1823
|
-
if (this.hasAttribute('opening')) {
|
|
1824
|
-
this._flushAnimation('opening');
|
|
1825
|
-
}
|
|
1826
|
-
if (this._placeholder) {
|
|
1827
|
-
this._exitModalState();
|
|
1828
|
-
|
|
1829
|
-
// Use this.restoreFocusNode if specified, otherwise fallback to the node
|
|
1830
|
-
// which was focused before opening the overlay.
|
|
1831
|
-
const restoreFocusNode = this.restoreFocusNode || this.__restoreFocusNode;
|
|
1832
|
-
|
|
1833
|
-
if (this.restoreFocusOnClose && restoreFocusNode) {
|
|
1834
|
-
// If the activeElement is `<body>` or inside the overlay,
|
|
1835
|
-
// we are allowed to restore the focus. In all the other
|
|
1836
|
-
// cases focus might have been moved elsewhere by another
|
|
1837
|
-
// component or by the user interaction (e.g. click on a
|
|
1838
|
-
// button outside the overlay).
|
|
1839
|
-
const activeElement = this._getActiveElement();
|
|
1840
|
-
|
|
1841
|
-
if (activeElement === document.body || this._deepContains(activeElement)) {
|
|
1842
|
-
// Focusing the restoreFocusNode doesn't always work synchronously on Firefox and Safari
|
|
1843
|
-
// (e.g. combo-box overlay close on outside click).
|
|
1844
|
-
setTimeout(() => restoreFocusNode.focus());
|
|
1845
|
-
}
|
|
1846
|
-
this.__restoreFocusNode = null;
|
|
1211
|
+
/* c8 ignore next 3 */
|
|
1212
|
+
if (this._boundIosResizeListener) {
|
|
1213
|
+
this._detectIosNavbar();
|
|
1214
|
+
window.addEventListener('resize', this._boundIosResizeListener);
|
|
1847
1215
|
}
|
|
1216
|
+
}
|
|
1848
1217
|
|
|
1849
|
-
|
|
1850
|
-
|
|
1218
|
+
/** @protected */
|
|
1219
|
+
disconnectedCallback() {
|
|
1220
|
+
super.disconnectedCallback();
|
|
1851
1221
|
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
});
|
|
1856
|
-
} else {
|
|
1857
|
-
this._finishClosing();
|
|
1222
|
+
/* c8 ignore next 3 */
|
|
1223
|
+
if (this._boundIosResizeListener) {
|
|
1224
|
+
window.removeEventListener('resize', this._boundIosResizeListener);
|
|
1858
1225
|
}
|
|
1859
1226
|
}
|
|
1860
|
-
}
|
|
1861
|
-
|
|
1862
|
-
/** @protected */
|
|
1863
|
-
_detachOverlay() {
|
|
1864
|
-
this._placeholder.parentNode.insertBefore(this, this._placeholder);
|
|
1865
|
-
this._placeholder.parentNode.removeChild(this._placeholder);
|
|
1866
|
-
}
|
|
1867
1227
|
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
.
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
* Returns true if this is the last one in the opened overlays stack
|
|
1880
|
-
* @return {boolean}
|
|
1881
|
-
* @protected
|
|
1882
|
-
*/
|
|
1883
|
-
get _last() {
|
|
1884
|
-
return this === Overlay.__attachedInstances.pop();
|
|
1885
|
-
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Requests an update for the content of the overlay.
|
|
1230
|
+
* While performing the update, it invokes the renderer passed in the `renderer` property.
|
|
1231
|
+
*
|
|
1232
|
+
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
1233
|
+
*/
|
|
1234
|
+
requestContentUpdate() {
|
|
1235
|
+
if (this.renderer) {
|
|
1236
|
+
this.renderer.call(this.owner, this, this.owner, this.model);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1886
1239
|
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1240
|
+
/**
|
|
1241
|
+
* @param {Event=} sourceEvent
|
|
1242
|
+
*/
|
|
1243
|
+
close(sourceEvent) {
|
|
1244
|
+
const evt = new CustomEvent('vaadin-overlay-close', {
|
|
1245
|
+
bubbles: true,
|
|
1246
|
+
cancelable: true,
|
|
1247
|
+
detail: { sourceEvent },
|
|
1248
|
+
});
|
|
1249
|
+
this.dispatchEvent(evt);
|
|
1250
|
+
if (!evt.defaultPrevented) {
|
|
1251
|
+
this.opened = false;
|
|
1893
1252
|
}
|
|
1894
|
-
} else {
|
|
1895
|
-
this._removeGlobalListeners();
|
|
1896
|
-
this._exitModalState();
|
|
1897
1253
|
}
|
|
1898
|
-
}
|
|
1899
1254
|
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
document.documentElement.addEventListener('click', this._boundOutsideClickListener, true);
|
|
1907
|
-
}
|
|
1255
|
+
/** @private */
|
|
1256
|
+
_detectIosNavbar() {
|
|
1257
|
+
/* c8 ignore next 15 */
|
|
1258
|
+
if (!this.opened) {
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1908
1261
|
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
if (document.body.style.pointerEvents !== 'none') {
|
|
1912
|
-
// Set body pointer-events to 'none' to disable mouse interactions with
|
|
1913
|
-
// other document nodes.
|
|
1914
|
-
this._previousDocumentPointerEvents = document.body.style.pointerEvents;
|
|
1915
|
-
document.body.style.pointerEvents = 'none';
|
|
1916
|
-
}
|
|
1262
|
+
const innerHeight = window.innerHeight;
|
|
1263
|
+
const innerWidth = window.innerWidth;
|
|
1917
1264
|
|
|
1918
|
-
|
|
1919
|
-
Overlay.__attachedInstances.forEach((el) => {
|
|
1920
|
-
if (el !== this) {
|
|
1921
|
-
el.shadowRoot.querySelector('[part="overlay"]').style.pointerEvents = 'none';
|
|
1922
|
-
}
|
|
1923
|
-
});
|
|
1924
|
-
}
|
|
1265
|
+
const landscape = innerWidth > innerHeight;
|
|
1925
1266
|
|
|
1926
|
-
|
|
1927
|
-
_removeGlobalListeners() {
|
|
1928
|
-
document.removeEventListener('mousedown', this._boundMouseDownListener);
|
|
1929
|
-
document.removeEventListener('mouseup', this._boundMouseUpListener);
|
|
1930
|
-
document.documentElement.removeEventListener('click', this._boundOutsideClickListener, true);
|
|
1931
|
-
}
|
|
1267
|
+
const clientHeight = document.documentElement.clientHeight;
|
|
1932
1268
|
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
document.body.style.pointerEvents = this._previousDocumentPointerEvents;
|
|
1938
|
-
delete this._previousDocumentPointerEvents;
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
// Restore pointer events in the previous overlay(s)
|
|
1942
|
-
const instances = Overlay.__attachedInstances;
|
|
1943
|
-
let el;
|
|
1944
|
-
// Use instances.pop() to ensure the reverse order
|
|
1945
|
-
while ((el = instances.pop())) {
|
|
1946
|
-
if (el === this) {
|
|
1947
|
-
// Skip the current instance
|
|
1948
|
-
continue;
|
|
1949
|
-
}
|
|
1950
|
-
el.shadowRoot.querySelector('[part="overlay"]').style.removeProperty('pointer-events');
|
|
1951
|
-
if (!el.modeless) {
|
|
1952
|
-
// Stop after the last modal
|
|
1953
|
-
break;
|
|
1269
|
+
if (landscape && clientHeight > innerHeight) {
|
|
1270
|
+
this.style.setProperty('--vaadin-overlay-viewport-bottom', `${clientHeight - innerHeight}px`);
|
|
1271
|
+
} else {
|
|
1272
|
+
this.style.setProperty('--vaadin-overlay-viewport-bottom', '0');
|
|
1954
1273
|
}
|
|
1955
1274
|
}
|
|
1956
|
-
}
|
|
1957
1275
|
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1276
|
+
/** @private */
|
|
1277
|
+
_addGlobalListeners() {
|
|
1278
|
+
document.addEventListener('mousedown', this._boundMouseDownListener);
|
|
1279
|
+
document.addEventListener('mouseup', this._boundMouseUpListener);
|
|
1280
|
+
// Firefox leaks click to document on contextmenu even if prevented
|
|
1281
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=990614
|
|
1282
|
+
document.documentElement.addEventListener('click', this._boundOutsideClickListener, true);
|
|
1962
1283
|
}
|
|
1963
1284
|
|
|
1964
|
-
|
|
1285
|
+
/** @private */
|
|
1286
|
+
_removeGlobalListeners() {
|
|
1287
|
+
document.removeEventListener('mousedown', this._boundMouseDownListener);
|
|
1288
|
+
document.removeEventListener('mouseup', this._boundMouseUpListener);
|
|
1289
|
+
document.documentElement.removeEventListener('click', this._boundOutsideClickListener, true);
|
|
1290
|
+
}
|
|
1965
1291
|
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1292
|
+
/** @private */
|
|
1293
|
+
_rendererOrDataChanged(renderer, owner, model, opened) {
|
|
1294
|
+
const ownerOrModelChanged = this._oldOwner !== owner || this._oldModel !== model;
|
|
1295
|
+
this._oldModel = model;
|
|
1296
|
+
this._oldOwner = owner;
|
|
1297
|
+
|
|
1298
|
+
const rendererChanged = this._oldRenderer !== renderer;
|
|
1299
|
+
this._oldRenderer = renderer;
|
|
1300
|
+
|
|
1301
|
+
const openedChanged = this._oldOpened !== opened;
|
|
1302
|
+
this._oldOpened = opened;
|
|
1303
|
+
|
|
1304
|
+
if (rendererChanged) {
|
|
1305
|
+
this.innerHTML = '';
|
|
1306
|
+
// Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
|
|
1307
|
+
// When clearing the rendered content, this part needs to be manually disposed of.
|
|
1308
|
+
// Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
|
|
1309
|
+
delete this._$litPart$;
|
|
1969
1310
|
}
|
|
1970
|
-
});
|
|
1971
1311
|
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
this.$.content = this._originalContentPart;
|
|
1976
|
-
this._originalContentPart = undefined;
|
|
1312
|
+
if (opened && renderer && (rendererChanged || openedChanged || ownerOrModelChanged)) {
|
|
1313
|
+
this.requestContentUpdate();
|
|
1314
|
+
}
|
|
1977
1315
|
}
|
|
1978
1316
|
|
|
1979
|
-
|
|
1317
|
+
/** @private */
|
|
1318
|
+
_modelessChanged(modeless) {
|
|
1319
|
+
if (!modeless) {
|
|
1320
|
+
if (this.opened) {
|
|
1321
|
+
this._addGlobalListeners();
|
|
1322
|
+
this._enterModalState();
|
|
1323
|
+
}
|
|
1324
|
+
} else {
|
|
1325
|
+
this._removeGlobalListeners();
|
|
1326
|
+
this._exitModalState();
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1980
1329
|
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1330
|
+
/** @private */
|
|
1331
|
+
_openedChanged(opened, wasOpened) {
|
|
1332
|
+
if (opened) {
|
|
1333
|
+
this._saveFocus();
|
|
1984
1334
|
|
|
1985
|
-
|
|
1986
|
-
* @param {!HTMLTemplateElement} template
|
|
1987
|
-
* @protected
|
|
1988
|
-
*/
|
|
1989
|
-
_stampOverlayTemplate(template) {
|
|
1990
|
-
this._removeOldContent();
|
|
1991
|
-
|
|
1992
|
-
if (!template._Templatizer) {
|
|
1993
|
-
template._Templatizer = templatize(template, this, {
|
|
1994
|
-
forwardHostProp(prop, value) {
|
|
1995
|
-
if (this._instance) {
|
|
1996
|
-
this._instance.forwardHostProp(prop, value);
|
|
1997
|
-
}
|
|
1998
|
-
},
|
|
1999
|
-
});
|
|
2000
|
-
}
|
|
1335
|
+
this._animatedOpening();
|
|
2001
1336
|
|
|
2002
|
-
|
|
2003
|
-
|
|
1337
|
+
afterNextRender(this, () => {
|
|
1338
|
+
this._trapFocus();
|
|
2004
1339
|
|
|
2005
|
-
|
|
1340
|
+
const evt = new CustomEvent('vaadin-overlay-open', { bubbles: true });
|
|
1341
|
+
this.dispatchEvent(evt);
|
|
1342
|
+
});
|
|
2006
1343
|
|
|
2007
|
-
|
|
2008
|
-
if (!this.$.content.shadowRoot) {
|
|
2009
|
-
this.$.content.attachShadow({ mode: 'open' });
|
|
2010
|
-
}
|
|
1344
|
+
document.addEventListener('keydown', this._boundKeydownListener);
|
|
2011
1345
|
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
)
|
|
1346
|
+
if (!this.modeless) {
|
|
1347
|
+
this._addGlobalListeners();
|
|
1348
|
+
}
|
|
1349
|
+
} else if (wasOpened) {
|
|
1350
|
+
this._resetFocus();
|
|
2016
1351
|
|
|
2017
|
-
|
|
2018
|
-
scopeCssText = scopeCssText.replace(/:host/g, ':host-nomatch');
|
|
1352
|
+
this._animatedClosing();
|
|
2019
1353
|
|
|
2020
|
-
|
|
2021
|
-
// Append a style to the content shadowRoot
|
|
2022
|
-
const style = document.createElement('style');
|
|
2023
|
-
style.textContent = scopeCssText;
|
|
2024
|
-
this.$.content.shadowRoot.appendChild(style);
|
|
2025
|
-
this._contentNodes.unshift(style);
|
|
2026
|
-
}
|
|
1354
|
+
document.removeEventListener('keydown', this._boundKeydownListener);
|
|
2027
1355
|
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
this.content = this;
|
|
1356
|
+
if (!this.modeless) {
|
|
1357
|
+
this._removeGlobalListeners();
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
2033
1360
|
}
|
|
2034
|
-
}
|
|
2035
1361
|
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
this.renderer = undefined;
|
|
1362
|
+
/** @private */
|
|
1363
|
+
_hiddenChanged(hidden) {
|
|
1364
|
+
if (hidden && this.hasAttribute('closing')) {
|
|
1365
|
+
this._flushAnimation('closing');
|
|
1366
|
+
}
|
|
2042
1367
|
}
|
|
2043
|
-
}
|
|
2044
1368
|
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
1369
|
+
/**
|
|
1370
|
+
* @return {boolean}
|
|
1371
|
+
* @private
|
|
1372
|
+
*/
|
|
1373
|
+
_shouldAnimate() {
|
|
1374
|
+
const style = getComputedStyle(this);
|
|
1375
|
+
const name = style.getPropertyValue('animation-name');
|
|
1376
|
+
const hidden = style.getPropertyValue('display') === 'none';
|
|
1377
|
+
return !hidden && name && name !== 'none';
|
|
2051
1378
|
}
|
|
2052
1379
|
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
this
|
|
2069
|
-
|
|
2070
|
-
// When clearing the rendered content, this part needs to be manually disposed of.
|
|
2071
|
-
// Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
|
|
2072
|
-
delete this.content._$litPart$;
|
|
1380
|
+
/**
|
|
1381
|
+
* @param {string} type
|
|
1382
|
+
* @param {Function} callback
|
|
1383
|
+
* @private
|
|
1384
|
+
*/
|
|
1385
|
+
_enqueueAnimation(type, callback) {
|
|
1386
|
+
const handler = `__${type}Handler`;
|
|
1387
|
+
const listener = (event) => {
|
|
1388
|
+
if (event && event.target !== this) {
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
callback();
|
|
1392
|
+
this.removeEventListener('animationend', listener);
|
|
1393
|
+
delete this[handler];
|
|
1394
|
+
};
|
|
1395
|
+
this[handler] = listener;
|
|
1396
|
+
this.addEventListener('animationend', listener);
|
|
2073
1397
|
}
|
|
2074
1398
|
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
1399
|
+
/**
|
|
1400
|
+
* @param {string} type
|
|
1401
|
+
* @protected
|
|
1402
|
+
*/
|
|
1403
|
+
_flushAnimation(type) {
|
|
1404
|
+
const handler = `__${type}Handler`;
|
|
1405
|
+
if (typeof this[handler] === 'function') {
|
|
1406
|
+
this[handler]();
|
|
2080
1407
|
}
|
|
2081
1408
|
}
|
|
2082
|
-
}
|
|
2083
1409
|
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
}
|
|
2095
|
-
return active;
|
|
2096
|
-
}
|
|
1410
|
+
/** @private */
|
|
1411
|
+
_animatedOpening() {
|
|
1412
|
+
if (this.parentNode === document.body && this.hasAttribute('closing')) {
|
|
1413
|
+
this._flushAnimation('closing');
|
|
1414
|
+
}
|
|
1415
|
+
this._attachOverlay();
|
|
1416
|
+
if (!this.modeless) {
|
|
1417
|
+
this._enterModalState();
|
|
1418
|
+
}
|
|
1419
|
+
this.setAttribute('opening', '');
|
|
2097
1420
|
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
return true;
|
|
1421
|
+
if (this._shouldAnimate()) {
|
|
1422
|
+
this._enqueueAnimation('opening', () => {
|
|
1423
|
+
this._finishOpening();
|
|
1424
|
+
});
|
|
1425
|
+
} else {
|
|
1426
|
+
this._finishOpening();
|
|
1427
|
+
}
|
|
2106
1428
|
}
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
1429
|
+
|
|
1430
|
+
/** @private */
|
|
1431
|
+
_attachOverlay() {
|
|
1432
|
+
this._placeholder = document.createComment('vaadin-overlay-placeholder');
|
|
1433
|
+
this.parentNode.insertBefore(this._placeholder, this);
|
|
1434
|
+
document.body.appendChild(this);
|
|
1435
|
+
this.bringToFront();
|
|
2112
1436
|
}
|
|
2113
|
-
return n === this;
|
|
2114
|
-
}
|
|
2115
1437
|
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
let zIndex = '';
|
|
2121
|
-
const frontmost = Overlay.__attachedInstances.filter((o) => o !== this).pop();
|
|
2122
|
-
if (frontmost) {
|
|
2123
|
-
const frontmostZIndex = frontmost.__zIndex;
|
|
2124
|
-
zIndex = frontmostZIndex + 1;
|
|
2125
|
-
}
|
|
2126
|
-
this.style.zIndex = zIndex;
|
|
2127
|
-
this.__zIndex = zIndex || parseFloat(getComputedStyle(this).zIndex);
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
1438
|
+
/** @private */
|
|
1439
|
+
_finishOpening() {
|
|
1440
|
+
this.removeAttribute('opening');
|
|
1441
|
+
}
|
|
2130
1442
|
|
|
2131
|
-
|
|
1443
|
+
/** @private */
|
|
1444
|
+
_finishClosing() {
|
|
1445
|
+
this._detachOverlay();
|
|
1446
|
+
this.$.overlay.style.removeProperty('pointer-events');
|
|
1447
|
+
this.removeAttribute('closing');
|
|
1448
|
+
this.dispatchEvent(new CustomEvent('vaadin-overlay-closed'));
|
|
1449
|
+
}
|
|
2132
1450
|
|
|
2133
|
-
/**
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
1451
|
+
/** @private */
|
|
1452
|
+
_animatedClosing() {
|
|
1453
|
+
if (this.hasAttribute('opening')) {
|
|
1454
|
+
this._flushAnimation('opening');
|
|
1455
|
+
}
|
|
1456
|
+
if (this._placeholder) {
|
|
1457
|
+
this._exitModalState();
|
|
1458
|
+
this.setAttribute('closing', '');
|
|
1459
|
+
this.dispatchEvent(new CustomEvent('vaadin-overlay-closing'));
|
|
1460
|
+
|
|
1461
|
+
if (this._shouldAnimate()) {
|
|
1462
|
+
this._enqueueAnimation('closing', () => {
|
|
1463
|
+
this._finishClosing();
|
|
1464
|
+
});
|
|
1465
|
+
} else {
|
|
1466
|
+
this._finishClosing();
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
2138
1470
|
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
1471
|
+
/** @private */
|
|
1472
|
+
_detachOverlay() {
|
|
1473
|
+
this._placeholder.parentNode.insertBefore(this, this._placeholder);
|
|
1474
|
+
this._placeholder.parentNode.removeChild(this._placeholder);
|
|
1475
|
+
}
|
|
2144
1476
|
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
1477
|
+
/** @private */
|
|
1478
|
+
_mouseDownListener(event) {
|
|
1479
|
+
this._mouseDownInside = event.composedPath().indexOf(this.$.overlay) >= 0;
|
|
1480
|
+
}
|
|
2148
1481
|
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
1482
|
+
/** @private */
|
|
1483
|
+
_mouseUpListener(event) {
|
|
1484
|
+
this._mouseUpInside = event.composedPath().indexOf(this.$.overlay) >= 0;
|
|
1485
|
+
}
|
|
2152
1486
|
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
1487
|
+
/**
|
|
1488
|
+
* Whether to close the overlay on outside click or not.
|
|
1489
|
+
* Override this method to customize the closing logic.
|
|
1490
|
+
*
|
|
1491
|
+
* @param {Event} _event
|
|
1492
|
+
* @return {boolean}
|
|
1493
|
+
* @protected
|
|
1494
|
+
*/
|
|
1495
|
+
_shouldCloseOnOutsideClick(_event) {
|
|
1496
|
+
return this._last;
|
|
2157
1497
|
}
|
|
2158
|
-
}
|
|
2159
1498
|
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
1499
|
+
/**
|
|
1500
|
+
* Outside click listener used in capture phase to close the overlay before
|
|
1501
|
+
* propagating the event to the listener on the element that triggered it.
|
|
1502
|
+
* Otherwise, calling `open()` would result in closing and re-opening.
|
|
1503
|
+
*
|
|
1504
|
+
* @private
|
|
1505
|
+
*/
|
|
1506
|
+
_outsideClickListener(event) {
|
|
1507
|
+
if (event.composedPath().includes(this.$.overlay) || this._mouseDownInside || this._mouseUpInside) {
|
|
1508
|
+
this._mouseDownInside = false;
|
|
1509
|
+
this._mouseUpInside = false;
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
2163
1512
|
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
}
|
|
2168
|
-
}
|
|
2169
|
-
`;
|
|
1513
|
+
if (!this._shouldCloseOnOutsideClick(event)) {
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
2170
1516
|
|
|
2171
|
-
|
|
1517
|
+
const evt = new CustomEvent('vaadin-overlay-outside-click', {
|
|
1518
|
+
bubbles: true,
|
|
1519
|
+
cancelable: true,
|
|
1520
|
+
detail: { sourceEvent: event },
|
|
1521
|
+
});
|
|
1522
|
+
this.dispatchEvent(evt);
|
|
2172
1523
|
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
@media (max-width: 420px), (max-height: 420px) {
|
|
2177
|
-
:host {
|
|
2178
|
-
top: 0 !important;
|
|
2179
|
-
right: 0 !important;
|
|
2180
|
-
bottom: var(--vaadin-overlay-viewport-bottom, 0) !important;
|
|
2181
|
-
left: 0 !important;
|
|
2182
|
-
align-items: stretch !important;
|
|
2183
|
-
justify-content: flex-end !important;
|
|
1524
|
+
if (this.opened && !evt.defaultPrevented) {
|
|
1525
|
+
this.close(event);
|
|
1526
|
+
}
|
|
2184
1527
|
}
|
|
2185
1528
|
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
1529
|
+
/**
|
|
1530
|
+
* Listener used to close whe overlay on Escape press, if it is the last one.
|
|
1531
|
+
* @private
|
|
1532
|
+
*/
|
|
1533
|
+
_keydownListener(event) {
|
|
1534
|
+
if (!this._last) {
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
2192
1537
|
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
box-sizing: border-box;
|
|
2198
|
-
-webkit-overflow-scrolling: touch;
|
|
2199
|
-
overflow: auto;
|
|
2200
|
-
-webkit-mask-image: linear-gradient(transparent, #000 40px, #000 calc(100% - 40px), transparent);
|
|
2201
|
-
mask-image: linear-gradient(transparent, #000 40px, #000 calc(100% - 40px), transparent);
|
|
2202
|
-
}
|
|
1538
|
+
// Only close modeless overlay on Esc press when it contains focus
|
|
1539
|
+
if (this.modeless && !event.composedPath().includes(this.$.overlay)) {
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
2203
1542
|
|
|
2204
|
-
|
|
2205
|
-
|
|
1543
|
+
if (event.key === 'Escape') {
|
|
1544
|
+
const evt = new CustomEvent('vaadin-overlay-escape-press', {
|
|
1545
|
+
bubbles: true,
|
|
1546
|
+
cancelable: true,
|
|
1547
|
+
detail: { sourceEvent: event },
|
|
1548
|
+
});
|
|
1549
|
+
this.dispatchEvent(evt);
|
|
1550
|
+
|
|
1551
|
+
if (this.opened && !evt.defaultPrevented) {
|
|
1552
|
+
this.close(event);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
2206
1555
|
}
|
|
1556
|
+
};
|
|
2207
1557
|
|
|
2208
|
-
|
|
1558
|
+
/**
|
|
1559
|
+
* @license
|
|
1560
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
1561
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
1562
|
+
*/
|
|
2209
1563
|
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
1564
|
+
/**
|
|
1565
|
+
* Returns an array of ancestor root nodes for the given node.
|
|
1566
|
+
*
|
|
1567
|
+
* A root node is either a document node or a document fragment node (Shadow Root).
|
|
1568
|
+
* The array is collected by a bottom-up DOM traversing that starts with the given node
|
|
1569
|
+
* and involves both the light DOM and ancestor shadow DOM trees.
|
|
1570
|
+
*
|
|
1571
|
+
* @param {Node} node
|
|
1572
|
+
* @return {Node[]}
|
|
1573
|
+
*/
|
|
1574
|
+
function getAncestorRootNodes(node) {
|
|
1575
|
+
const result = [];
|
|
2213
1576
|
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
1577
|
+
while (node) {
|
|
1578
|
+
if (node.nodeType === Node.DOCUMENT_NODE) {
|
|
1579
|
+
result.push(node);
|
|
1580
|
+
break;
|
|
2217
1581
|
}
|
|
2218
1582
|
|
|
2219
|
-
|
|
2220
|
-
|
|
1583
|
+
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
1584
|
+
result.push(node);
|
|
1585
|
+
node = node.host;
|
|
1586
|
+
continue;
|
|
2221
1587
|
}
|
|
2222
|
-
}
|
|
2223
1588
|
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
1589
|
+
if (node.assignedSlot) {
|
|
1590
|
+
node = node.assignedSlot;
|
|
1591
|
+
continue;
|
|
2227
1592
|
}
|
|
2228
|
-
}
|
|
2229
1593
|
|
|
2230
|
-
|
|
2231
|
-
100% {
|
|
2232
|
-
transform: translateY(150%);
|
|
2233
|
-
}
|
|
1594
|
+
node = node.parentNode;
|
|
2234
1595
|
}
|
|
2235
|
-
`;
|
|
2236
|
-
|
|
2237
|
-
const menuOverlay = [overlay, menuOverlayCore, menuOverlayExt];
|
|
2238
1596
|
|
|
2239
|
-
|
|
1597
|
+
return result;
|
|
1598
|
+
}
|
|
2240
1599
|
|
|
2241
1600
|
/**
|
|
2242
1601
|
* @license
|
|
2243
|
-
* Copyright (c) 2017 -
|
|
1602
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
2244
1603
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2245
1604
|
*/
|
|
2246
1605
|
|
|
@@ -2430,10 +1789,6 @@ const PositionMixin = (superClass) =>
|
|
|
2430
1789
|
}
|
|
2431
1790
|
}
|
|
2432
1791
|
|
|
2433
|
-
get __isRTL() {
|
|
2434
|
-
return this.getAttribute('dir') === 'rtl';
|
|
2435
|
-
}
|
|
2436
|
-
|
|
2437
1792
|
__positionSettingsChanged() {
|
|
2438
1793
|
this._updatePosition();
|
|
2439
1794
|
}
|
|
@@ -2457,9 +1812,9 @@ const PositionMixin = (superClass) =>
|
|
|
2457
1812
|
const shouldAlignStartVertically = this.__shouldAlignStartVertically(targetRect);
|
|
2458
1813
|
this.style.justifyContent = shouldAlignStartVertically ? 'flex-start' : 'flex-end';
|
|
2459
1814
|
|
|
2460
|
-
const
|
|
2461
|
-
const
|
|
2462
|
-
|
|
1815
|
+
const isRTL = this.__isRTL;
|
|
1816
|
+
const shouldAlignStartHorizontally = this.__shouldAlignStartHorizontally(targetRect, isRTL);
|
|
1817
|
+
const flexStart = (!isRTL && shouldAlignStartHorizontally) || (isRTL && !shouldAlignStartHorizontally);
|
|
2463
1818
|
this.style.alignItems = flexStart ? 'flex-start' : 'flex-end';
|
|
2464
1819
|
|
|
2465
1820
|
// Get the overlay rect after possible overlay alignment changes
|
|
@@ -2618,7 +1973,73 @@ const PositionMixin = (superClass) =>
|
|
|
2618
1973
|
|
|
2619
1974
|
/**
|
|
2620
1975
|
* @license
|
|
2621
|
-
* Copyright (c)
|
|
1976
|
+
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
1977
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
1978
|
+
*/
|
|
1979
|
+
|
|
1980
|
+
const overlayStyles = i`
|
|
1981
|
+
:host {
|
|
1982
|
+
z-index: 200;
|
|
1983
|
+
position: fixed;
|
|
1984
|
+
|
|
1985
|
+
/* Despite of what the names say, <vaadin-overlay> is just a container
|
|
1986
|
+
for position/sizing/alignment. The actual overlay is the overlay part. */
|
|
1987
|
+
|
|
1988
|
+
/* Default position constraints: the entire viewport. Note: themes can
|
|
1989
|
+
override this to introduce gaps between the overlay and the viewport. */
|
|
1990
|
+
inset: 0;
|
|
1991
|
+
bottom: var(--vaadin-overlay-viewport-bottom);
|
|
1992
|
+
|
|
1993
|
+
/* Use flexbox alignment for the overlay part. */
|
|
1994
|
+
display: flex;
|
|
1995
|
+
flex-direction: column; /* makes dropdowns sizing easier */
|
|
1996
|
+
/* Align to center by default. */
|
|
1997
|
+
align-items: center;
|
|
1998
|
+
justify-content: center;
|
|
1999
|
+
|
|
2000
|
+
/* Allow centering when max-width/max-height applies. */
|
|
2001
|
+
margin: auto;
|
|
2002
|
+
|
|
2003
|
+
/* The host is not clickable, only the overlay part is. */
|
|
2004
|
+
pointer-events: none;
|
|
2005
|
+
|
|
2006
|
+
/* Remove tap highlight on touch devices. */
|
|
2007
|
+
-webkit-tap-highlight-color: transparent;
|
|
2008
|
+
|
|
2009
|
+
/* CSS API for host */
|
|
2010
|
+
--vaadin-overlay-viewport-bottom: 0;
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
:host([hidden]),
|
|
2014
|
+
:host(:not([opened]):not([closing])) {
|
|
2015
|
+
display: none !important;
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
[part='overlay'] {
|
|
2019
|
+
-webkit-overflow-scrolling: touch;
|
|
2020
|
+
overflow: auto;
|
|
2021
|
+
pointer-events: auto;
|
|
2022
|
+
|
|
2023
|
+
/* Prevent overflowing the host */
|
|
2024
|
+
max-width: 100%;
|
|
2025
|
+
box-sizing: border-box;
|
|
2026
|
+
|
|
2027
|
+
-webkit-tap-highlight-color: initial; /* reenable tap highlight inside */
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
[part='backdrop'] {
|
|
2031
|
+
z-index: -1;
|
|
2032
|
+
content: '';
|
|
2033
|
+
background: rgba(0, 0, 0, 0.5);
|
|
2034
|
+
position: fixed;
|
|
2035
|
+
inset: 0;
|
|
2036
|
+
pointer-events: auto;
|
|
2037
|
+
}
|
|
2038
|
+
`;
|
|
2039
|
+
|
|
2040
|
+
/**
|
|
2041
|
+
* @license
|
|
2042
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
2622
2043
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2623
2044
|
*/
|
|
2624
2045
|
|
|
@@ -2655,4 +2076,4 @@ class VirtualKeyboardController {
|
|
|
2655
2076
|
}
|
|
2656
2077
|
}
|
|
2657
2078
|
|
|
2658
|
-
export {
|
|
2079
|
+
export { OverlayMixin as O, PositionMixin as P, VirtualKeyboardController as V, afterNextRender as a, overlay as b, menuOverlayCore as c, hideOthers as h, menuOverlay as m, overlayStyles as o };
|