@everymatrix/general-input 0.0.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.
Files changed (121) hide show
  1. package/dist/cjs/checkbox-group-input_10.cjs.entry.js +34870 -0
  2. package/dist/cjs/general-input.cjs.entry.js +51 -0
  3. package/dist/cjs/general-input.cjs.js +19 -0
  4. package/dist/cjs/index-1768513d.js +1286 -0
  5. package/dist/cjs/index.cjs.js +2 -0
  6. package/dist/cjs/loader.cjs.js +21 -0
  7. package/dist/collection/collection-manifest.json +22 -0
  8. package/dist/collection/components/checkbox-group-input/checkbox-group-input.css +62 -0
  9. package/dist/collection/components/checkbox-group-input/checkbox-group-input.js +344 -0
  10. package/dist/collection/components/checkbox-input/checkbox-input.css +62 -0
  11. package/dist/collection/components/checkbox-input/checkbox-input.js +322 -0
  12. package/dist/collection/components/date-input/date-input.css +85 -0
  13. package/dist/collection/components/date-input/date-input.js +341 -0
  14. package/dist/collection/components/email-input/email-input.css +83 -0
  15. package/dist/collection/components/email-input/email-input.js +382 -0
  16. package/dist/collection/components/general-input/general-input.css +3 -0
  17. package/dist/collection/components/general-input/general-input.js +296 -0
  18. package/dist/collection/components/number-input/number-input.css +90 -0
  19. package/dist/collection/components/number-input/number-input.js +349 -0
  20. package/dist/collection/components/password-input/password-input.css +86 -0
  21. package/dist/collection/components/password-input/password-input.js +395 -0
  22. package/dist/collection/components/radio-input/radio-input.css +43 -0
  23. package/dist/collection/components/radio-input/radio-input.js +298 -0
  24. package/dist/collection/components/select-input/select-input.css +93 -0
  25. package/dist/collection/components/select-input/select-input.js +412 -0
  26. package/dist/collection/components/tel-input/tel-input.css +116 -0
  27. package/dist/collection/components/tel-input/tel-input.js +421 -0
  28. package/dist/collection/components/text-input/text-input.css +83 -0
  29. package/dist/collection/components/text-input/text-input.js +406 -0
  30. package/dist/collection/index.js +1 -0
  31. package/dist/collection/utils/locale.utils.js +26 -0
  32. package/dist/collection/utils/tooltipIcon.svg +5 -0
  33. package/dist/collection/utils/types.js +1 -0
  34. package/dist/collection/utils/utils.js +5 -0
  35. package/dist/components/active-mixin.js +975 -0
  36. package/dist/components/checkbox-group-input.d.ts +11 -0
  37. package/dist/components/checkbox-group-input.js +6 -0
  38. package/dist/components/checkbox-group-input2.js +5793 -0
  39. package/dist/components/checkbox-input.d.ts +11 -0
  40. package/dist/components/checkbox-input.js +6 -0
  41. package/dist/components/checkbox-input2.js +127 -0
  42. package/dist/components/date-input.d.ts +11 -0
  43. package/dist/components/date-input.js +6 -0
  44. package/dist/components/date-input2.js +5182 -0
  45. package/dist/components/email-input.d.ts +11 -0
  46. package/dist/components/email-input.js +6 -0
  47. package/dist/components/email-input2.js +150 -0
  48. package/dist/components/field-mixin.js +12712 -0
  49. package/dist/components/general-input.d.ts +11 -0
  50. package/dist/components/general-input.js +140 -0
  51. package/dist/components/index.d.ts +26 -0
  52. package/dist/components/index.js +1 -0
  53. package/dist/components/input-field-shared-styles.js +1067 -0
  54. package/dist/components/number-input.d.ts +11 -0
  55. package/dist/components/number-input.js +6 -0
  56. package/dist/components/number-input2.js +139 -0
  57. package/dist/components/password-input.d.ts +11 -0
  58. package/dist/components/password-input.js +6 -0
  59. package/dist/components/password-input2.js +879 -0
  60. package/dist/components/pattern-mixin.js +85 -0
  61. package/dist/components/radio-input.d.ts +11 -0
  62. package/dist/components/radio-input.js +6 -0
  63. package/dist/components/radio-input2.js +115 -0
  64. package/dist/components/select-input.d.ts +11 -0
  65. package/dist/components/select-input.js +6 -0
  66. package/dist/components/select-input2.js +166 -0
  67. package/dist/components/tel-input.d.ts +11 -0
  68. package/dist/components/tel-input.js +6 -0
  69. package/dist/components/tel-input2.js +178 -0
  70. package/dist/components/text-input.d.ts +11 -0
  71. package/dist/components/text-input.js +6 -0
  72. package/dist/components/text-input2.js +157 -0
  73. package/dist/components/tooltipIcon.js +30 -0
  74. package/dist/components/vaadin-button.js +461 -0
  75. package/dist/components/vaadin-combo-box.js +4329 -0
  76. package/dist/components/virtual-keyboard-controller.js +2693 -0
  77. package/dist/esm/checkbox-group-input_10.entry.js +34857 -0
  78. package/dist/esm/general-input.entry.js +47 -0
  79. package/dist/esm/general-input.js +17 -0
  80. package/dist/esm/index-7e24a6f1.js +1259 -0
  81. package/dist/esm/index.js +1 -0
  82. package/dist/esm/loader.js +17 -0
  83. package/dist/esm/polyfills/core-js.js +11 -0
  84. package/dist/esm/polyfills/css-shim.js +1 -0
  85. package/dist/esm/polyfills/dom.js +79 -0
  86. package/dist/esm/polyfills/es5-html-element.js +1 -0
  87. package/dist/esm/polyfills/index.js +34 -0
  88. package/dist/esm/polyfills/system.js +6 -0
  89. package/dist/general-input/general-input.esm.js +1 -0
  90. package/dist/general-input/index.esm.js +0 -0
  91. package/dist/general-input/p-61d76ec3.entry.js +1 -0
  92. package/dist/general-input/p-a79eb0a3.entry.js +4413 -0
  93. package/dist/general-input/p-fb647820.js +1 -0
  94. package/dist/index.cjs.js +1 -0
  95. package/dist/index.js +1 -0
  96. package/dist/stencil.config.js +22 -0
  97. package/dist/types/Users/adrian.pripon/Documents/Work/stencil/widgets-stencil/packages/general-input/.stencil/packages/general-input/stencil.config.d.ts +2 -0
  98. package/dist/types/components/checkbox-group-input/checkbox-group-input.d.ts +72 -0
  99. package/dist/types/components/checkbox-input/checkbox-input.d.ts +64 -0
  100. package/dist/types/components/date-input/date-input.d.ts +70 -0
  101. package/dist/types/components/email-input/email-input.d.ts +77 -0
  102. package/dist/types/components/general-input/general-input.d.ts +60 -0
  103. package/dist/types/components/number-input/number-input.d.ts +70 -0
  104. package/dist/types/components/password-input/password-input.d.ts +79 -0
  105. package/dist/types/components/radio-input/radio-input.d.ts +59 -0
  106. package/dist/types/components/select-input/select-input.d.ts +82 -0
  107. package/dist/types/components/tel-input/tel-input.d.ts +85 -0
  108. package/dist/types/components/text-input/text-input.d.ts +81 -0
  109. package/dist/types/components.d.ts +1140 -0
  110. package/dist/types/index.d.ts +1 -0
  111. package/dist/types/stencil-public-runtime.d.ts +1565 -0
  112. package/dist/types/utils/locale.utils.d.ts +5 -0
  113. package/dist/types/utils/types.d.ts +76 -0
  114. package/dist/types/utils/utils.d.ts +1 -0
  115. package/loader/cdn.js +3 -0
  116. package/loader/index.cjs.js +3 -0
  117. package/loader/index.d.ts +12 -0
  118. package/loader/index.es2017.js +3 -0
  119. package/loader/index.js +4 -0
  120. package/loader/package.json +10 -0
  121. package/package.json +26 -0
@@ -0,0 +1,2693 @@
1
+ import { i, r as registerStyles, d as dedupingMixin, H as PropertyEffects, J as strictTemplatePolicy, w as wrap, M as legacyWarnings, N as getFocusableElements, b as isElementFocused, T as ThemableMixin, G as DirMixin, C as ControllerMixin, P as PolymerElement, h as html, F as FlattenedNodesObserver, O as getAncestorRootNodes } from './field-mixin.js';
2
+
3
+ /**
4
+ * @license
5
+ * Copyright (c) 2017 - 2022 Vaadin Ltd.
6
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
7
+ */
8
+
9
+ const overlay = i`
10
+ :host {
11
+ top: var(--lumo-space-m);
12
+ right: var(--lumo-space-m);
13
+ bottom: var(--lumo-space-m);
14
+ left: var(--lumo-space-m);
15
+ /* Workaround for Edge issue (only on Surface), where an overflowing vaadin-list-box inside vaadin-select-overlay makes the overlay transparent */
16
+ /* stylelint-disable-next-line */
17
+ outline: 0px solid transparent;
18
+ }
19
+
20
+ [part='overlay'] {
21
+ background-color: var(--lumo-base-color);
22
+ background-image: linear-gradient(var(--lumo-tint-5pct), var(--lumo-tint-5pct));
23
+ border-radius: var(--lumo-border-radius-m);
24
+ box-shadow: 0 0 0 1px var(--lumo-shade-5pct), var(--lumo-box-shadow-m);
25
+ color: var(--lumo-body-text-color);
26
+ font-family: var(--lumo-font-family);
27
+ font-size: var(--lumo-font-size-m);
28
+ font-weight: 400;
29
+ line-height: var(--lumo-line-height-m);
30
+ letter-spacing: 0;
31
+ text-transform: none;
32
+ -webkit-text-size-adjust: 100%;
33
+ -webkit-font-smoothing: antialiased;
34
+ -moz-osx-font-smoothing: grayscale;
35
+ }
36
+
37
+ [part='content'] {
38
+ padding: var(--lumo-space-xs);
39
+ }
40
+
41
+ [part='backdrop'] {
42
+ background-color: var(--lumo-shade-20pct);
43
+ animation: 0.2s lumo-overlay-backdrop-enter both;
44
+ will-change: opacity;
45
+ }
46
+
47
+ @keyframes lumo-overlay-backdrop-enter {
48
+ 0% {
49
+ opacity: 0;
50
+ }
51
+ }
52
+
53
+ :host([closing]) [part='backdrop'] {
54
+ animation: 0.2s lumo-overlay-backdrop-exit both;
55
+ }
56
+
57
+ @keyframes lumo-overlay-backdrop-exit {
58
+ 100% {
59
+ opacity: 0;
60
+ }
61
+ }
62
+
63
+ @keyframes lumo-overlay-dummy-animation {
64
+ 0% {
65
+ opacity: 1;
66
+ }
67
+
68
+ 100% {
69
+ opacity: 1;
70
+ }
71
+ }
72
+ `;
73
+
74
+ registerStyles('', overlay, { moduleId: 'lumo-overlay' });
75
+
76
+ registerStyles('vaadin-overlay', overlay, { moduleId: 'lumo-vaadin-overlay' });
77
+
78
+ /**
79
+ @license
80
+ Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
81
+ This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
82
+ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
83
+ The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
84
+ Code distributed by Google as part of the polymer project is also
85
+ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
86
+ */
87
+
88
+ let scheduled = false;
89
+ let beforeRenderQueue = [];
90
+ let afterRenderQueue = [];
91
+
92
+ function schedule() {
93
+ scheduled = true;
94
+ // before next render
95
+ requestAnimationFrame(function() {
96
+ scheduled = false;
97
+ flushQueue(beforeRenderQueue);
98
+ // after the render
99
+ setTimeout(function() {
100
+ runQueue(afterRenderQueue);
101
+ });
102
+ });
103
+ }
104
+
105
+ function flushQueue(queue) {
106
+ while (queue.length) {
107
+ callMethod(queue.shift());
108
+ }
109
+ }
110
+
111
+ function runQueue(queue) {
112
+ for (let i=0, l=queue.length; i < l; i++) {
113
+ callMethod(queue.shift());
114
+ }
115
+ }
116
+
117
+ function callMethod(info) {
118
+ const context = info[0];
119
+ const callback = info[1];
120
+ const args = info[2];
121
+ try {
122
+ callback.apply(context, args);
123
+ } catch(e) {
124
+ setTimeout(() => {
125
+ throw e;
126
+ });
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Enqueues a callback which will be run after the next render, equivalent
132
+ * to one task (`setTimeout`) after the next `requestAnimationFrame`.
133
+ *
134
+ * This method is useful for tuning the first-render performance of an
135
+ * element or application by deferring non-critical work until after the
136
+ * first paint. Typical non-render-critical work may include adding UI
137
+ * event listeners and aria attributes.
138
+ *
139
+ * @param {*} context Context object the callback function will be bound to
140
+ * @param {function(...*):void} callback Callback function
141
+ * @param {!Array=} args An array of arguments to call the callback function with
142
+ * @return {void}
143
+ */
144
+ function afterNextRender(context, callback, args) {
145
+ if (!scheduled) {
146
+ schedule();
147
+ }
148
+ afterRenderQueue.push([context, callback, args]);
149
+ }
150
+
151
+ /**
152
+ @license
153
+ Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
154
+ This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
155
+ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
156
+ The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
157
+ Code distributed by Google as part of the polymer project is also
158
+ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
159
+ */
160
+
161
+ // Common implementation for mixin & behavior
162
+ function mutablePropertyChange(inst, property, value, old, mutableData) {
163
+ let isObject;
164
+ if (mutableData) {
165
+ isObject = (typeof value === 'object' && value !== null);
166
+ // Pull `old` for Objects from temp cache, but treat `null` as a primitive
167
+ if (isObject) {
168
+ old = inst.__dataTemp[property];
169
+ }
170
+ }
171
+ // Strict equality check, but return false for NaN===NaN
172
+ let shouldChange = (old !== value && (old === old || value === value));
173
+ // Objects are stored in temporary cache (cleared at end of
174
+ // turn), which is used for dirty-checking
175
+ if (isObject && shouldChange) {
176
+ inst.__dataTemp[property] = value;
177
+ }
178
+ return shouldChange;
179
+ }
180
+
181
+ /**
182
+ * Element class mixin to skip strict dirty-checking for objects and arrays
183
+ * (always consider them to be "dirty"), for use on elements utilizing
184
+ * `PropertyEffects`
185
+ *
186
+ * By default, `PropertyEffects` performs strict dirty checking on
187
+ * objects, which means that any deep modifications to an object or array will
188
+ * not be propagated unless "immutable" data patterns are used (i.e. all object
189
+ * references from the root to the mutation were changed).
190
+ *
191
+ * Polymer also provides a proprietary data mutation and path notification API
192
+ * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient
193
+ * mutation and notification of deep changes in an object graph to all elements
194
+ * bound to the same object graph.
195
+ *
196
+ * In cases where neither immutable patterns nor the data mutation API can be
197
+ * used, applying this mixin will cause Polymer to skip dirty checking for
198
+ * objects and arrays (always consider them to be "dirty"). This allows a
199
+ * user to make a deep modification to a bound object graph, and then either
200
+ * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath`
201
+ * (e.g. `this.notifyPath('items')`) to update the tree. Note that all
202
+ * elements that wish to be updated based on deep mutations must apply this
203
+ * mixin or otherwise skip strict dirty checking for objects/arrays.
204
+ * Specifically, any elements in the binding tree between the source of a
205
+ * mutation and the consumption of it must apply this mixin or enable the
206
+ * `OptionalMutableData` mixin.
207
+ *
208
+ * In order to make the dirty check strategy configurable, see
209
+ * `OptionalMutableData`.
210
+ *
211
+ * Note, the performance characteristics of propagating large object graphs
212
+ * will be worse as opposed to using strict dirty checking with immutable
213
+ * patterns or Polymer's path notification API.
214
+ *
215
+ * @mixinFunction
216
+ * @polymer
217
+ * @summary Element class mixin to skip strict dirty-checking for objects
218
+ * and arrays
219
+ * @template T
220
+ * @param {function(new:T)} superClass Class to apply mixin to.
221
+ * @return {function(new:T)} superClass with mixin applied.
222
+ */
223
+ const MutableData = dedupingMixin(superClass => {
224
+
225
+ /**
226
+ * @polymer
227
+ * @mixinClass
228
+ * @implements {Polymer_MutableData}
229
+ */
230
+ class MutableData extends superClass {
231
+ /**
232
+ * Overrides `PropertyEffects` to provide option for skipping
233
+ * strict equality checking for Objects and Arrays.
234
+ *
235
+ * This method pulls the value to dirty check against from the `__dataTemp`
236
+ * cache (rather than the normal `__data` cache) for Objects. Since the temp
237
+ * cache is cleared at the end of a turn, this implementation allows
238
+ * side-effects of deep object changes to be processed by re-setting the
239
+ * same object (using the temp cache as an in-turn backstop to prevent
240
+ * cycles due to 2-way notification).
241
+ *
242
+ * @param {string} property Property name
243
+ * @param {*} value New property value
244
+ * @param {*} old Previous property value
245
+ * @return {boolean} Whether the property should be considered a change
246
+ * @protected
247
+ */
248
+ _shouldPropertyChange(property, value, old) {
249
+ return mutablePropertyChange(this, property, value, old, true);
250
+ }
251
+
252
+ }
253
+
254
+ return MutableData;
255
+
256
+ });
257
+
258
+ /**
259
+ * Element class mixin to add the optional ability to skip strict
260
+ * dirty-checking for objects and arrays (always consider them to be
261
+ * "dirty") by setting a `mutable-data` attribute on an element instance.
262
+ *
263
+ * By default, `PropertyEffects` performs strict dirty checking on
264
+ * objects, which means that any deep modifications to an object or array will
265
+ * not be propagated unless "immutable" data patterns are used (i.e. all object
266
+ * references from the root to the mutation were changed).
267
+ *
268
+ * Polymer also provides a proprietary data mutation and path notification API
269
+ * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient
270
+ * mutation and notification of deep changes in an object graph to all elements
271
+ * bound to the same object graph.
272
+ *
273
+ * In cases where neither immutable patterns nor the data mutation API can be
274
+ * used, applying this mixin will allow Polymer to skip dirty checking for
275
+ * objects and arrays (always consider them to be "dirty"). This allows a
276
+ * user to make a deep modification to a bound object graph, and then either
277
+ * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath`
278
+ * (e.g. `this.notifyPath('items')`) to update the tree. Note that all
279
+ * elements that wish to be updated based on deep mutations must apply this
280
+ * mixin or otherwise skip strict dirty checking for objects/arrays.
281
+ * Specifically, any elements in the binding tree between the source of a
282
+ * mutation and the consumption of it must enable this mixin or apply the
283
+ * `MutableData` mixin.
284
+ *
285
+ * While this mixin adds the ability to forgo Object/Array dirty checking,
286
+ * the `mutableData` flag defaults to false and must be set on the instance.
287
+ *
288
+ * Note, the performance characteristics of propagating large object graphs
289
+ * will be worse by relying on `mutableData: true` as opposed to using
290
+ * strict dirty checking with immutable patterns or Polymer's path notification
291
+ * API.
292
+ *
293
+ * @mixinFunction
294
+ * @polymer
295
+ * @summary Element class mixin to optionally skip strict dirty-checking
296
+ * for objects and arrays
297
+ */
298
+ const OptionalMutableData = dedupingMixin(superClass => {
299
+
300
+ /**
301
+ * @mixinClass
302
+ * @polymer
303
+ * @implements {Polymer_OptionalMutableData}
304
+ */
305
+ class OptionalMutableData extends superClass {
306
+
307
+ /** @nocollapse */
308
+ static get properties() {
309
+ return {
310
+ /**
311
+ * Instance-level flag for configuring the dirty-checking strategy
312
+ * for this element. When true, Objects and Arrays will skip dirty
313
+ * checking, otherwise strict equality checking will be used.
314
+ */
315
+ mutableData: Boolean
316
+ };
317
+ }
318
+
319
+ /**
320
+ * Overrides `PropertyEffects` to provide option for skipping
321
+ * strict equality checking for Objects and Arrays.
322
+ *
323
+ * When `this.mutableData` is true on this instance, this method
324
+ * pulls the value to dirty check against from the `__dataTemp` cache
325
+ * (rather than the normal `__data` cache) for Objects. Since the temp
326
+ * cache is cleared at the end of a turn, this implementation allows
327
+ * side-effects of deep object changes to be processed by re-setting the
328
+ * same object (using the temp cache as an in-turn backstop to prevent
329
+ * cycles due to 2-way notification).
330
+ *
331
+ * @param {string} property Property name
332
+ * @param {*} value New property value
333
+ * @param {*} old Previous property value
334
+ * @return {boolean} Whether the property should be considered a change
335
+ * @protected
336
+ */
337
+ _shouldPropertyChange(property, value, old) {
338
+ return mutablePropertyChange(this, property, value, old, this.mutableData);
339
+ }
340
+ }
341
+
342
+ return OptionalMutableData;
343
+
344
+ });
345
+
346
+ // Export for use by legacy behavior
347
+ MutableData._mutablePropertyChange = mutablePropertyChange;
348
+
349
+ /**
350
+ @license
351
+ Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
352
+ This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
353
+ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
354
+ The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
355
+ Code distributed by Google as part of the polymer project is also
356
+ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
357
+ */
358
+
359
+ // Base class for HTMLTemplateElement extension that has property effects
360
+ // machinery for propagating host properties to children. This is an ES5
361
+ // class only because Babel (incorrectly) requires super() in the class
362
+ // constructor even though no `this` is used and it returns an instance.
363
+ let newInstance = null;
364
+
365
+ /**
366
+ * @constructor
367
+ * @extends {HTMLTemplateElement}
368
+ * @private
369
+ */
370
+ function HTMLTemplateElementExtension() { return newInstance; }
371
+ HTMLTemplateElementExtension.prototype = Object.create(HTMLTemplateElement.prototype, {
372
+ constructor: {
373
+ value: HTMLTemplateElementExtension,
374
+ writable: true
375
+ }
376
+ });
377
+
378
+ /**
379
+ * @constructor
380
+ * @implements {Polymer_PropertyEffects}
381
+ * @extends {HTMLTemplateElementExtension}
382
+ * @private
383
+ */
384
+ const DataTemplate = PropertyEffects(HTMLTemplateElementExtension);
385
+
386
+ /**
387
+ * @constructor
388
+ * @implements {Polymer_MutableData}
389
+ * @extends {DataTemplate}
390
+ * @private
391
+ */
392
+ const MutableDataTemplate = MutableData(DataTemplate);
393
+
394
+ // Applies a DataTemplate subclass to a <template> instance
395
+ function upgradeTemplate(template, constructor) {
396
+ newInstance = template;
397
+ Object.setPrototypeOf(template, constructor.prototype);
398
+ new constructor();
399
+ newInstance = null;
400
+ }
401
+
402
+ /**
403
+ * Base class for TemplateInstance.
404
+ * @constructor
405
+ * @extends {HTMLElement}
406
+ * @implements {Polymer_PropertyEffects}
407
+ * @private
408
+ */
409
+ const templateInstanceBase = PropertyEffects(class {});
410
+
411
+ function showHideChildren(hide, children) {
412
+ for (let i=0; i<children.length; i++) {
413
+ let n = children[i];
414
+ // Ignore non-changes
415
+ if (Boolean(hide) != Boolean(n.__hideTemplateChildren__)) {
416
+ // clear and restore text
417
+ if (n.nodeType === Node.TEXT_NODE) {
418
+ if (hide) {
419
+ n.__polymerTextContent__ = n.textContent;
420
+ n.textContent = '';
421
+ } else {
422
+ n.textContent = n.__polymerTextContent__;
423
+ }
424
+ // remove and replace slot
425
+ } else if (n.localName === 'slot') {
426
+ if (hide) {
427
+ n.__polymerReplaced__ = document.createComment('hidden-slot');
428
+ wrap(wrap(n).parentNode).replaceChild(n.__polymerReplaced__, n);
429
+ } else {
430
+ const replace = n.__polymerReplaced__;
431
+ if (replace) {
432
+ wrap(wrap(replace).parentNode).replaceChild(n, replace);
433
+ }
434
+ }
435
+ }
436
+ // hide and show nodes
437
+ else if (n.style) {
438
+ if (hide) {
439
+ n.__polymerDisplay__ = n.style.display;
440
+ n.style.display = 'none';
441
+ } else {
442
+ n.style.display = n.__polymerDisplay__;
443
+ }
444
+ }
445
+ }
446
+ n.__hideTemplateChildren__ = hide;
447
+ if (n._showHideChildren) {
448
+ n._showHideChildren(hide);
449
+ }
450
+ }
451
+ }
452
+
453
+ /**
454
+ * @polymer
455
+ * @customElement
456
+ * @appliesMixin PropertyEffects
457
+ * @unrestricted
458
+ */
459
+ class TemplateInstanceBase extends templateInstanceBase {
460
+ constructor(props) {
461
+ super();
462
+ this._configureProperties(props);
463
+ /** @type {!StampedTemplate} */
464
+ this.root = this._stampTemplate(this.__dataHost);
465
+ // Save list of stamped children
466
+ let children = [];
467
+ /** @suppress {invalidCasts} */
468
+ this.children = /** @type {!NodeList} */ (children);
469
+ // Polymer 1.x did not use `Polymer.dom` here so not bothering.
470
+ for (let n = this.root.firstChild; n; n=n.nextSibling) {
471
+ children.push(n);
472
+ n.__templatizeInstance = this;
473
+ }
474
+ if (this.__templatizeOwner &&
475
+ this.__templatizeOwner.__hideTemplateChildren__) {
476
+ this._showHideChildren(true);
477
+ }
478
+ // Flush props only when props are passed if instance props exist
479
+ // or when there isn't instance props.
480
+ let options = this.__templatizeOptions;
481
+ if ((props && options.instanceProps) || !options.instanceProps) {
482
+ this._enableProperties();
483
+ }
484
+ }
485
+ /**
486
+ * Configure the given `props` by calling `_setPendingProperty`. Also
487
+ * sets any properties stored in `__hostProps`.
488
+ * @private
489
+ * @param {Object} props Object of property name-value pairs to set.
490
+ * @return {void}
491
+ */
492
+ _configureProperties(props) {
493
+ let options = this.__templatizeOptions;
494
+ if (options.forwardHostProp) {
495
+ for (let hprop in this.__hostProps) {
496
+ this._setPendingProperty(hprop, this.__dataHost['_host_' + hprop]);
497
+ }
498
+ }
499
+ // Any instance props passed in the constructor will overwrite host props;
500
+ // normally this would be a user error but we don't specifically filter them
501
+ for (let iprop in props) {
502
+ this._setPendingProperty(iprop, props[iprop]);
503
+ }
504
+ }
505
+ /**
506
+ * Forwards a host property to this instance. This method should be
507
+ * called on instances from the `options.forwardHostProp` callback
508
+ * to propagate changes of host properties to each instance.
509
+ *
510
+ * Note this method enqueues the change, which are flushed as a batch.
511
+ *
512
+ * @param {string} prop Property or path name
513
+ * @param {*} value Value of the property to forward
514
+ * @return {void}
515
+ */
516
+ forwardHostProp(prop, value) {
517
+ if (this._setPendingPropertyOrPath(prop, value, false, true)) {
518
+ this.__dataHost._enqueueClient(this);
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Override point for adding custom or simulated event handling.
524
+ *
525
+ * @override
526
+ * @param {!Node} node Node to add event listener to
527
+ * @param {string} eventName Name of event
528
+ * @param {function(!Event):void} handler Listener function to add
529
+ * @return {void}
530
+ */
531
+ _addEventListenerToNode(node, eventName, handler) {
532
+ if (this._methodHost && this.__templatizeOptions.parentModel) {
533
+ // If this instance should be considered a parent model, decorate
534
+ // events this template instance as `model`
535
+ this._methodHost._addEventListenerToNode(node, eventName, (e) => {
536
+ e.model = this;
537
+ handler(e);
538
+ });
539
+ } else {
540
+ // Otherwise delegate to the template's host (which could be)
541
+ // another template instance
542
+ let templateHost = this.__dataHost.__dataHost;
543
+ if (templateHost) {
544
+ templateHost._addEventListenerToNode(node, eventName, handler);
545
+ }
546
+ }
547
+ }
548
+ /**
549
+ * Shows or hides the template instance top level child elements. For
550
+ * text nodes, `textContent` is removed while "hidden" and replaced when
551
+ * "shown."
552
+ * @param {boolean} hide Set to true to hide the children;
553
+ * set to false to show them.
554
+ * @return {void}
555
+ * @protected
556
+ */
557
+ _showHideChildren(hide) {
558
+ showHideChildren(hide, this.children);
559
+ }
560
+ /**
561
+ * Overrides default property-effects implementation to intercept
562
+ * textContent bindings while children are "hidden" and cache in
563
+ * private storage for later retrieval.
564
+ *
565
+ * @override
566
+ * @param {!Node} node The node to set a property on
567
+ * @param {string} prop The property to set
568
+ * @param {*} value The value to set
569
+ * @return {void}
570
+ * @protected
571
+ */
572
+ _setUnmanagedPropertyToNode(node, prop, value) {
573
+ if (node.__hideTemplateChildren__ &&
574
+ node.nodeType == Node.TEXT_NODE && prop == 'textContent') {
575
+ node.__polymerTextContent__ = value;
576
+ } else {
577
+ super._setUnmanagedPropertyToNode(node, prop, value);
578
+ }
579
+ }
580
+ /**
581
+ * Find the parent model of this template instance. The parent model
582
+ * is either another templatize instance that had option `parentModel: true`,
583
+ * or else the host element.
584
+ *
585
+ * @return {!Polymer_PropertyEffects} The parent model of this instance
586
+ */
587
+ get parentModel() {
588
+ let model = this.__parentModel;
589
+ if (!model) {
590
+ let options;
591
+ model = this;
592
+ do {
593
+ // A template instance's `__dataHost` is a <template>
594
+ // `model.__dataHost.__dataHost` is the template's host
595
+ model = model.__dataHost.__dataHost;
596
+ } while ((options = model.__templatizeOptions) && !options.parentModel);
597
+ this.__parentModel = model;
598
+ }
599
+ return model;
600
+ }
601
+
602
+ /**
603
+ * Stub of HTMLElement's `dispatchEvent`, so that effects that may
604
+ * dispatch events safely no-op.
605
+ *
606
+ * @param {Event} event Event to dispatch
607
+ * @return {boolean} Always true.
608
+ * @override
609
+ */
610
+ dispatchEvent(event) { // eslint-disable-line no-unused-vars
611
+ return true;
612
+ }
613
+ }
614
+
615
+ /**
616
+ * @constructor
617
+ * @extends {TemplateInstanceBase}
618
+ * @implements {Polymer_MutableData}
619
+ * @private
620
+ */
621
+ const MutableTemplateInstanceBase = MutableData(
622
+ // This cast shouldn't be neccessary, but Closure doesn't understand that
623
+ // TemplateInstanceBase is a constructor function.
624
+ /** @type {function(new:TemplateInstanceBase)} */ (TemplateInstanceBase));
625
+
626
+ function findMethodHost(template) {
627
+ // Technically this should be the owner of the outermost template.
628
+ // In shadow dom, this is always getRootNode().host, but we can
629
+ // approximate this via cooperation with our dataHost always setting
630
+ // `_methodHost` as long as there were bindings (or id's) on this
631
+ // instance causing it to get a dataHost.
632
+ let templateHost = template.__dataHost;
633
+ return templateHost && templateHost._methodHost || templateHost;
634
+ }
635
+
636
+ /* eslint-disable valid-jsdoc */
637
+ /**
638
+ * @suppress {missingProperties} class.prototype is not defined for some reason
639
+ */
640
+ function createTemplatizerClass(template, templateInfo, options) {
641
+ /**
642
+ * @constructor
643
+ * @extends {TemplateInstanceBase}
644
+ */
645
+ let templatizerBase = options.mutableData ?
646
+ MutableTemplateInstanceBase : TemplateInstanceBase;
647
+
648
+ // Affordance for global mixins onto TemplatizeInstance
649
+ if (templatize.mixin) {
650
+ templatizerBase = templatize.mixin(templatizerBase);
651
+ }
652
+
653
+ /**
654
+ * Anonymous class created by the templatize
655
+ * @constructor
656
+ * @private
657
+ */
658
+ let klass = class extends templatizerBase { };
659
+ /** @override */
660
+ klass.prototype.__templatizeOptions = options;
661
+ klass.prototype._bindTemplate(template);
662
+ addNotifyEffects(klass, template, templateInfo, options);
663
+ return klass;
664
+ }
665
+
666
+ /**
667
+ * Adds propagate effects from the template to the template instance for
668
+ * properties that the host binds to the template using the `_host_` prefix.
669
+ *
670
+ * @suppress {missingProperties} class.prototype is not defined for some reason
671
+ */
672
+ function addPropagateEffects(target, templateInfo, options, methodHost) {
673
+ let userForwardHostProp = options.forwardHostProp;
674
+ if (userForwardHostProp && templateInfo.hasHostProps) {
675
+ // Under the `removeNestedTemplates` optimization, a custom element like
676
+ // `dom-if` or `dom-repeat` can itself be treated as the "template"; this
677
+ // flag is used to switch between upgrading a `<template>` to be a property
678
+ // effects client vs. adding the effects directly to the custom element
679
+ const isTemplate = target.localName == 'template';
680
+ // Provide data API and property effects on memoized template class
681
+ let klass = templateInfo.templatizeTemplateClass;
682
+ if (!klass) {
683
+ if (isTemplate) {
684
+ /**
685
+ * @constructor
686
+ * @extends {DataTemplate}
687
+ */
688
+ let templatizedBase =
689
+ options.mutableData ? MutableDataTemplate : DataTemplate;
690
+
691
+ // NOTE: due to https://github.com/google/closure-compiler/issues/2928,
692
+ // combining the next two lines into one assignment causes a spurious
693
+ // type error.
694
+ /** @private */
695
+ class TemplatizedTemplate extends templatizedBase {}
696
+ klass = templateInfo.templatizeTemplateClass = TemplatizedTemplate;
697
+ } else {
698
+ /**
699
+ * @constructor
700
+ * @extends {PolymerElement}
701
+ */
702
+ const templatizedBase = target.constructor;
703
+
704
+ // Create a cached subclass of the base custom element class onto which
705
+ // to put the template-specific propagate effects
706
+ // NOTE: due to https://github.com/google/closure-compiler/issues/2928,
707
+ // combining the next two lines into one assignment causes a spurious
708
+ // type error.
709
+ /** @private */
710
+ class TemplatizedTemplateExtension extends templatizedBase {}
711
+ klass = templateInfo.templatizeTemplateClass =
712
+ TemplatizedTemplateExtension;
713
+ }
714
+ // Add template - >instances effects
715
+ // and host <- template effects
716
+ let hostProps = templateInfo.hostProps;
717
+ for (let prop in hostProps) {
718
+ klass.prototype._addPropertyEffect('_host_' + prop,
719
+ klass.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,
720
+ {fn: createForwardHostPropEffect(prop, userForwardHostProp)});
721
+ klass.prototype._createNotifyingProperty('_host_' + prop);
722
+ }
723
+ if (legacyWarnings && methodHost) {
724
+ warnOnUndeclaredProperties(templateInfo, options, methodHost);
725
+ }
726
+ }
727
+ // Mix any pre-bound data into __data; no need to flush this to
728
+ // instances since they pull from the template at instance-time
729
+ if (target.__dataProto) {
730
+ // Note, generally `__dataProto` could be chained, but it's guaranteed
731
+ // to not be since this is a vanilla template we just added effects to
732
+ Object.assign(target.__data, target.__dataProto);
733
+ }
734
+ if (isTemplate) {
735
+ upgradeTemplate(target, klass);
736
+ // Clear any pending data for performance
737
+ target.__dataTemp = {};
738
+ target.__dataPending = null;
739
+ target.__dataOld = null;
740
+ target._enableProperties();
741
+ } else {
742
+ // Swizzle the cached subclass prototype onto the custom element
743
+ Object.setPrototypeOf(target, klass.prototype);
744
+ // Check for any pre-bound instance host properties, and do the
745
+ // instance property delete/assign dance for those (directly into data;
746
+ // not need to go through accessor since they are pulled at instance time)
747
+ const hostProps = templateInfo.hostProps;
748
+ for (let prop in hostProps) {
749
+ prop = '_host_' + prop;
750
+ if (prop in target) {
751
+ const val = target[prop];
752
+ delete target[prop];
753
+ target.__data[prop] = val;
754
+ }
755
+ }
756
+ }
757
+ }
758
+ }
759
+ /* eslint-enable valid-jsdoc */
760
+
761
+ function createForwardHostPropEffect(hostProp, userForwardHostProp) {
762
+ return function forwardHostProp(template, prop, props) {
763
+ userForwardHostProp.call(template.__templatizeOwner,
764
+ prop.substring('_host_'.length), props[prop]);
765
+ };
766
+ }
767
+
768
+ function addNotifyEffects(klass, template, templateInfo, options) {
769
+ let hostProps = templateInfo.hostProps || {};
770
+ for (let iprop in options.instanceProps) {
771
+ delete hostProps[iprop];
772
+ let userNotifyInstanceProp = options.notifyInstanceProp;
773
+ if (userNotifyInstanceProp) {
774
+ klass.prototype._addPropertyEffect(iprop,
775
+ klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,
776
+ {fn: createNotifyInstancePropEffect(iprop, userNotifyInstanceProp)});
777
+ }
778
+ }
779
+ if (options.forwardHostProp && template.__dataHost) {
780
+ for (let hprop in hostProps) {
781
+ // As we're iterating hostProps in this function, note whether
782
+ // there were any, for an optimization in addPropagateEffects
783
+ if (!templateInfo.hasHostProps) {
784
+ templateInfo.hasHostProps = true;
785
+ }
786
+ klass.prototype._addPropertyEffect(hprop,
787
+ klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,
788
+ {fn: createNotifyHostPropEffect()});
789
+ }
790
+ }
791
+ }
792
+
793
+ function createNotifyInstancePropEffect(instProp, userNotifyInstanceProp) {
794
+ return function notifyInstanceProp(inst, prop, props) {
795
+ userNotifyInstanceProp.call(inst.__templatizeOwner,
796
+ inst, prop, props[prop]);
797
+ };
798
+ }
799
+
800
+ function createNotifyHostPropEffect() {
801
+ return function notifyHostProp(inst, prop, props) {
802
+ inst.__dataHost._setPendingPropertyOrPath('_host_' + prop, props[prop], true, true);
803
+ };
804
+ }
805
+
806
+
807
+ /**
808
+ * Returns an anonymous `PropertyEffects` class bound to the
809
+ * `<template>` provided. Instancing the class will result in the
810
+ * template being stamped into a document fragment stored as the instance's
811
+ * `root` property, after which it can be appended to the DOM.
812
+ *
813
+ * Templates may utilize all Polymer data-binding features as well as
814
+ * declarative event listeners. Event listeners and inline computing
815
+ * functions in the template will be called on the host of the template.
816
+ *
817
+ * The constructor returned takes a single argument dictionary of initial
818
+ * property values to propagate into template bindings. Additionally
819
+ * host properties can be forwarded in, and instance properties can be
820
+ * notified out by providing optional callbacks in the `options` dictionary.
821
+ *
822
+ * Valid configuration in `options` are as follows:
823
+ *
824
+ * - `forwardHostProp(property, value)`: Called when a property referenced
825
+ * in the template changed on the template's host. As this library does
826
+ * not retain references to templates instanced by the user, it is the
827
+ * templatize owner's responsibility to forward host property changes into
828
+ * user-stamped instances. The `instance.forwardHostProp(property, value)`
829
+ * method on the generated class should be called to forward host
830
+ * properties into the template to prevent unnecessary property-changed
831
+ * notifications. Any properties referenced in the template that are not
832
+ * defined in `instanceProps` will be notified up to the template's host
833
+ * automatically.
834
+ * - `instanceProps`: Dictionary of property names that will be added
835
+ * to the instance by the templatize owner. These properties shadow any
836
+ * host properties, and changes within the template to these properties
837
+ * will result in `notifyInstanceProp` being called.
838
+ * - `mutableData`: When `true`, the generated class will skip strict
839
+ * dirty-checking for objects and arrays (always consider them to be
840
+ * "dirty").
841
+ * - `notifyInstanceProp(instance, property, value)`: Called when
842
+ * an instance property changes. Users may choose to call `notifyPath`
843
+ * on e.g. the owner to notify the change.
844
+ * - `parentModel`: When `true`, events handled by declarative event listeners
845
+ * (`on-event="handler"`) will be decorated with a `model` property pointing
846
+ * to the template instance that stamped it. It will also be returned
847
+ * from `instance.parentModel` in cases where template instance nesting
848
+ * causes an inner model to shadow an outer model.
849
+ *
850
+ * All callbacks are called bound to the `owner`. Any context
851
+ * needed for the callbacks (such as references to `instances` stamped)
852
+ * should be stored on the `owner` such that they can be retrieved via
853
+ * `this`.
854
+ *
855
+ * When `options.forwardHostProp` is declared as an option, any properties
856
+ * referenced in the template will be automatically forwarded from the host of
857
+ * the `<template>` to instances, with the exception of any properties listed in
858
+ * the `options.instanceProps` object. `instanceProps` are assumed to be
859
+ * managed by the owner of the instances, either passed into the constructor
860
+ * or set after the fact. Note, any properties passed into the constructor will
861
+ * always be set to the instance (regardless of whether they would normally
862
+ * be forwarded from the host).
863
+ *
864
+ * Note that `templatize()` can be run only once for a given `<template>`.
865
+ * Further calls will result in an error. Also, there is a special
866
+ * behavior if the template was duplicated through a mechanism such as
867
+ * `<dom-repeat>` or `<test-fixture>`. In this case, all calls to
868
+ * `templatize()` return the same class for all duplicates of a template.
869
+ * The class returned from `templatize()` is generated only once using
870
+ * the `options` from the first call. This means that any `options`
871
+ * provided to subsequent calls will be ignored. Therefore, it is very
872
+ * important not to close over any variables inside the callbacks. Also,
873
+ * arrow functions must be avoided because they bind the outer `this`.
874
+ * Inside the callbacks, any contextual information can be accessed
875
+ * through `this`, which points to the `owner`.
876
+ *
877
+ * @param {!HTMLTemplateElement} template Template to templatize
878
+ * @param {Polymer_PropertyEffects=} owner Owner of the template instances;
879
+ * any optional callbacks will be bound to this owner.
880
+ * @param {Object=} options Options dictionary (see summary for details)
881
+ * @return {function(new:TemplateInstanceBase, Object=)} Generated class bound
882
+ * to the template provided
883
+ * @suppress {invalidCasts}
884
+ */
885
+ function templatize(template, owner, options) {
886
+ // Under strictTemplatePolicy, the templatized element must be owned
887
+ // by a (trusted) Polymer element, indicated by existence of _methodHost;
888
+ // e.g. for dom-if & dom-repeat in main document, _methodHost is null
889
+ if (strictTemplatePolicy && !findMethodHost(template)) {
890
+ throw new Error('strictTemplatePolicy: template owner not trusted');
891
+ }
892
+ options = /** @type {!TemplatizeOptions} */(options || {});
893
+ if (template.__templatizeOwner) {
894
+ throw new Error('A <template> can only be templatized once');
895
+ }
896
+ template.__templatizeOwner = owner;
897
+ const ctor = owner ? owner.constructor : TemplateInstanceBase;
898
+ let templateInfo = ctor._parseTemplate(template);
899
+ // Get memoized base class for the prototypical template, which
900
+ // includes property effects for binding template & forwarding
901
+ /**
902
+ * @constructor
903
+ * @extends {TemplateInstanceBase}
904
+ */
905
+ let baseClass = templateInfo.templatizeInstanceClass;
906
+ if (!baseClass) {
907
+ baseClass = createTemplatizerClass(template, templateInfo, options);
908
+ templateInfo.templatizeInstanceClass = baseClass;
909
+ }
910
+ const methodHost = findMethodHost(template);
911
+ // Host property forwarding must be installed onto template instance
912
+ addPropagateEffects(template, templateInfo, options, methodHost);
913
+ // Subclass base class and add reference for this specific template
914
+ /** @private */
915
+ let klass = class TemplateInstance extends baseClass {};
916
+ /** @override */
917
+ klass.prototype._methodHost = methodHost;
918
+ /** @override */
919
+ klass.prototype.__dataHost = /** @type {!DataTemplate} */ (template);
920
+ /** @override */
921
+ klass.prototype.__templatizeOwner = /** @type {!Object} */ (owner);
922
+ /** @override */
923
+ klass.prototype.__hostProps = templateInfo.hostProps;
924
+ klass = /** @type {function(new:TemplateInstanceBase)} */(klass); //eslint-disable-line no-self-assign
925
+ return klass;
926
+ }
927
+
928
+ function warnOnUndeclaredProperties(templateInfo, options, methodHost) {
929
+ const declaredProps = methodHost.constructor._properties;
930
+ const {propertyEffects} = templateInfo;
931
+ const {instanceProps} = options;
932
+ for (let prop in propertyEffects) {
933
+ // Ensure properties with template effects are declared on the outermost
934
+ // host (`methodHost`), unless they are instance props or static functions
935
+ if (!declaredProps[prop] && !(instanceProps && instanceProps[prop])) {
936
+ const effects = propertyEffects[prop];
937
+ for (let i=0; i<effects.length; i++) {
938
+ const {part} = effects[i].info;
939
+ if (!(part.signature && part.signature.static)) {
940
+ console.warn(`Property '${prop}' used in template but not ` +
941
+ `declared in 'properties'; attribute will not be observed.`);
942
+ break;
943
+ }
944
+ }
945
+ }
946
+ }
947
+ }
948
+
949
+ /**
950
+ * Returns the template "model" associated with a given element, which
951
+ * serves as the binding scope for the template instance the element is
952
+ * contained in. A template model is an instance of
953
+ * `TemplateInstanceBase`, and should be used to manipulate data
954
+ * associated with this template instance.
955
+ *
956
+ * Example:
957
+ *
958
+ * let model = modelForElement(el);
959
+ * if (model.index < 10) {
960
+ * model.set('item.checked', true);
961
+ * }
962
+ *
963
+ * @param {HTMLElement} template The model will be returned for
964
+ * elements stamped from this template (accepts either an HTMLTemplateElement)
965
+ * or a `<dom-if>`/`<dom-repeat>` element when using `removeNestedTemplates`
966
+ * optimization.
967
+ * @param {Node=} node Node for which to return a template model.
968
+ * @return {TemplateInstanceBase} Template instance representing the
969
+ * binding scope for the element
970
+ */
971
+ function modelForElement(template, node) {
972
+ let model;
973
+ while (node) {
974
+ // An element with a __templatizeInstance marks the top boundary
975
+ // of a scope; walk up until we find one, and then ensure that
976
+ // its __dataHost matches `this`, meaning this dom-repeat stamped it
977
+ if ((model = node.__dataHost ? node : node.__templatizeInstance)) {
978
+ // Found an element stamped by another template; keep walking up
979
+ // from its __dataHost
980
+ if (model.__dataHost != template) {
981
+ node = model.__dataHost;
982
+ } else {
983
+ return model;
984
+ }
985
+ } else {
986
+ // Still in a template scope, keep going up until
987
+ // a __templatizeInstance is found
988
+ node = wrap(node).parentNode;
989
+ }
990
+ }
991
+ return null;
992
+ }
993
+
994
+ /**
995
+ * @license
996
+ * Copyright (c) 2021 - 2022 Vaadin Ltd.
997
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
998
+ */
999
+
1000
+ const testUserAgent = (regexp) => regexp.test(navigator.userAgent);
1001
+
1002
+ const testPlatform = (regexp) => regexp.test(navigator.platform);
1003
+
1004
+ const testVendor = (regexp) => regexp.test(navigator.vendor);
1005
+
1006
+ testUserAgent(/Android/);
1007
+
1008
+ testUserAgent(/Chrome/) && testVendor(/Google Inc/);
1009
+
1010
+ const isFirefox = testUserAgent(/Firefox/);
1011
+
1012
+ // IPadOS 13 lies and says it's a Mac, but we can distinguish by detecting touch support.
1013
+ const isIPad = testPlatform(/^iPad/) || (testPlatform(/^Mac/) && navigator.maxTouchPoints > 1);
1014
+
1015
+ const isIPhone = testPlatform(/^iPhone/);
1016
+
1017
+ const isIOS = isIPhone || isIPad;
1018
+
1019
+ const isSafari = testUserAgent(/^((?!chrome|android).)*safari/i);
1020
+
1021
+ const isTouch = (() => {
1022
+ try {
1023
+ document.createEvent('TouchEvent');
1024
+ return true;
1025
+ } catch (e) {
1026
+ return false;
1027
+ }
1028
+ })();
1029
+
1030
+ /**
1031
+ * @license
1032
+ * Copyright (c) 2021 - 2022 Vaadin Ltd.
1033
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
1034
+ */
1035
+
1036
+ const instances = [];
1037
+
1038
+ /**
1039
+ * A controller for trapping focus within a DOM node.
1040
+ */
1041
+ class FocusTrapController {
1042
+ /**
1043
+ * @param {HTMLElement} host
1044
+ */
1045
+ constructor(host) {
1046
+ /**
1047
+ * The controller host element.
1048
+ *
1049
+ * @type {HTMLElement}
1050
+ */
1051
+ this.host = host;
1052
+
1053
+ /**
1054
+ * A node for trapping focus in.
1055
+ *
1056
+ * @type {HTMLElement | null}
1057
+ * @private
1058
+ */
1059
+ this.__trapNode = null;
1060
+
1061
+ this.__onKeyDown = this.__onKeyDown.bind(this);
1062
+ }
1063
+
1064
+ hostConnected() {
1065
+ document.addEventListener('keydown', this.__onKeyDown);
1066
+ }
1067
+
1068
+ hostDisconnected() {
1069
+ document.removeEventListener('keydown', this.__onKeyDown);
1070
+ }
1071
+
1072
+ /**
1073
+ * Activates a focus trap for a DOM node that will prevent focus from escaping the node.
1074
+ * The trap can be deactivated with the `.releaseFocus()` method.
1075
+ *
1076
+ * If focus is initially outside the trap, the method will move focus inside,
1077
+ * on the first focusable element of the trap in the tab order.
1078
+ * The first focusable element can be the trap node itself if it is focusable
1079
+ * and comes first in the tab order.
1080
+ *
1081
+ * If there are no focusable elements, the method will throw an exception
1082
+ * and the trap will not be set.
1083
+ *
1084
+ * @param {HTMLElement} trapNode
1085
+ */
1086
+ trapFocus(trapNode) {
1087
+ this.__trapNode = trapNode;
1088
+
1089
+ if (this.__focusableElements.length === 0) {
1090
+ this.__trapNode = null;
1091
+ throw new Error('The trap node should have at least one focusable descendant or be focusable itself.');
1092
+ }
1093
+
1094
+ instances.push(this);
1095
+
1096
+ if (this.__focusedElementIndex === -1) {
1097
+ this.__focusableElements[0].focus();
1098
+ }
1099
+ }
1100
+
1101
+ /**
1102
+ * Deactivates the focus trap set with the `.trapFocus()` method
1103
+ * so that it becomes possible to tab outside the trap node.
1104
+ */
1105
+ releaseFocus() {
1106
+ this.__trapNode = null;
1107
+
1108
+ instances.pop();
1109
+ }
1110
+
1111
+ /**
1112
+ * A `keydown` event handler that manages tabbing navigation when the trap is enabled.
1113
+ *
1114
+ * - Moves focus to the next focusable element of the trap on `Tab` press.
1115
+ * When no next element to focus, the method moves focus to the first focusable element.
1116
+ * - Moves focus to the prev focusable element of the trap on `Shift+Tab` press.
1117
+ * When no prev element to focus, the method moves focus to the last focusable element.
1118
+ *
1119
+ * @param {KeyboardEvent} event
1120
+ * @private
1121
+ */
1122
+ __onKeyDown(event) {
1123
+ if (!this.__trapNode) {
1124
+ return;
1125
+ }
1126
+
1127
+ // Only handle events for the last instance
1128
+ if (this !== Array.from(instances).pop()) {
1129
+ return;
1130
+ }
1131
+
1132
+ if (event.key === 'Tab') {
1133
+ event.preventDefault();
1134
+
1135
+ const backward = event.shiftKey;
1136
+ this.__focusNextElement(backward);
1137
+ }
1138
+ }
1139
+
1140
+ /**
1141
+ * - Moves focus to the next focusable element if `backward === false`.
1142
+ * When no next element to focus, the method moves focus to the first focusable element.
1143
+ * - Moves focus to the prev focusable element if `backward === true`.
1144
+ * When no prev element to focus the method moves focus to the last focusable element.
1145
+ *
1146
+ * If no focusable elements, the method returns immediately.
1147
+ *
1148
+ * @param {boolean} backward
1149
+ * @private
1150
+ */
1151
+ __focusNextElement(backward = false) {
1152
+ const focusableElements = this.__focusableElements;
1153
+ const step = backward ? -1 : 1;
1154
+ const currentIndex = this.__focusedElementIndex;
1155
+ const nextIndex = (focusableElements.length + currentIndex + step) % focusableElements.length;
1156
+ const element = focusableElements[nextIndex];
1157
+ element.focus();
1158
+ if (element.localName === 'input') {
1159
+ element.select();
1160
+ }
1161
+ }
1162
+
1163
+ /**
1164
+ * An array of tab-ordered focusable elements inside the trap node.
1165
+ *
1166
+ * @return {HTMLElement[]}
1167
+ * @private
1168
+ */
1169
+ get __focusableElements() {
1170
+ return getFocusableElements(this.__trapNode);
1171
+ }
1172
+
1173
+ /**
1174
+ * The index of the element inside the trap node that currently has focus.
1175
+ *
1176
+ * @return {HTMLElement | undefined}
1177
+ * @private
1178
+ */
1179
+ get __focusedElementIndex() {
1180
+ const focusableElements = this.__focusableElements;
1181
+ return focusableElements.indexOf(focusableElements.filter(isElementFocused).pop());
1182
+ }
1183
+ }
1184
+
1185
+ /**
1186
+ * @license
1187
+ * Copyright (c) 2017 - 2022 Vaadin Ltd.
1188
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
1189
+ */
1190
+
1191
+ /**
1192
+ *
1193
+ * `<vaadin-overlay>` is a Web Component for creating overlays. The content of the overlay
1194
+ * can be populated in two ways: imperatively by using renderer callback function and
1195
+ * declaratively by using Polymer's Templates.
1196
+ *
1197
+ * ### Rendering
1198
+ *
1199
+ * By default, the overlay uses the content provided by using the renderer callback function.
1200
+ *
1201
+ * The renderer function provides `root`, `owner`, `model` arguments when applicable.
1202
+ * Generate DOM content by using `model` object properties if needed, append it to the `root`
1203
+ * element and control the state of the host element by accessing `owner`. Before generating new
1204
+ * content, users are able to check if there is already content in `root` for reusing it.
1205
+ *
1206
+ * ```html
1207
+ * <vaadin-overlay id="overlay"></vaadin-overlay>
1208
+ * ```
1209
+ * ```js
1210
+ * const overlay = document.querySelector('#overlay');
1211
+ * overlay.renderer = function(root) {
1212
+ * root.textContent = "Overlay content";
1213
+ * };
1214
+ * ```
1215
+ *
1216
+ * Renderer is called on the opening of the overlay and each time the related model is updated.
1217
+ * DOM generated during the renderer call can be reused
1218
+ * in the next renderer call and will be provided with the `root` argument.
1219
+ * On first call it will be empty.
1220
+ *
1221
+ * **NOTE:** when the renderer property is defined, the `<template>` content is not used.
1222
+ *
1223
+ * ### Templating
1224
+ *
1225
+ * Alternatively, the content can be provided with Polymer Template.
1226
+ * Overlay finds the first child template and uses that in case renderer callback function
1227
+ * is not provided. You can also set a custom template using the `template` property.
1228
+ *
1229
+ * After the content from the template is stamped, the `content` property
1230
+ * points to the content container.
1231
+ *
1232
+ * The overlay provides `forwardHostProp` when calling
1233
+ * `Polymer.Templatize.templatize` for the template, so that the bindings
1234
+ * from the parent scope propagate to the content.
1235
+ *
1236
+ * ### Styling
1237
+ *
1238
+ * To style the overlay content, use styles in the parent scope:
1239
+ *
1240
+ * - If the overlay is used in a component, then the component styles
1241
+ * apply the overlay content.
1242
+ * - If the overlay is used in the global DOM scope, then global styles
1243
+ * apply to the overlay content.
1244
+ *
1245
+ * See examples for styling the overlay content in the live demos.
1246
+ *
1247
+ * The following Shadow DOM parts are available for styling the overlay component itself:
1248
+ *
1249
+ * Part name | Description
1250
+ * -----------|---------------------------------------------------------|
1251
+ * `backdrop` | Backdrop of the overlay
1252
+ * `overlay` | Container for position/sizing/alignment of the content
1253
+ * `content` | Content of the overlay
1254
+ *
1255
+ * The following state attributes are available for styling:
1256
+ *
1257
+ * Attribute | Description | Part
1258
+ * ---|---|---
1259
+ * `opening` | Applied just after the overlay is attached to the DOM. You can apply a CSS @keyframe animation for this state. | `:host`
1260
+ * `closing` | Applied just before the overlay is detached from the DOM. You can apply a CSS @keyframe animation for this state. | `:host`
1261
+ *
1262
+ * The following custom CSS properties are available for styling:
1263
+ *
1264
+ * Custom CSS property | Description | Default value
1265
+ * ---|---|---
1266
+ * `--vaadin-overlay-viewport-bottom` | Bottom offset of the visible viewport area | `0` or detected offset
1267
+ *
1268
+ * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
1269
+ *
1270
+ * @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
1271
+ * @fires {CustomEvent} vaadin-overlay-open - Fired after the overlay is opened.
1272
+ * @fires {CustomEvent} vaadin-overlay-close - Fired before the overlay will be closed. If canceled the closing of the overlay is canceled as well.
1273
+ * @fires {CustomEvent} vaadin-overlay-closing - Fired when the overlay will be closed.
1274
+ * @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.
1275
+ * @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.
1276
+ *
1277
+ * @extends HTMLElement
1278
+ * @mixes ThemableMixin
1279
+ * @mixes DirMixin
1280
+ * @mixes ControllerMixin
1281
+ */
1282
+ class Overlay extends ThemableMixin(DirMixin(ControllerMixin(PolymerElement))) {
1283
+ static get template() {
1284
+ return html`
1285
+ <style>
1286
+ :host {
1287
+ z-index: 200;
1288
+ position: fixed;
1289
+
1290
+ /* Despite of what the names say, <vaadin-overlay> is just a container
1291
+ for position/sizing/alignment. The actual overlay is the overlay part. */
1292
+
1293
+ /* Default position constraints: the entire viewport. Note: themes can
1294
+ override this to introduce gaps between the overlay and the viewport. */
1295
+ top: 0;
1296
+ right: 0;
1297
+ bottom: var(--vaadin-overlay-viewport-bottom);
1298
+ left: 0;
1299
+
1300
+ /* Use flexbox alignment for the overlay part. */
1301
+ display: flex;
1302
+ flex-direction: column; /* makes dropdowns sizing easier */
1303
+ /* Align to center by default. */
1304
+ align-items: center;
1305
+ justify-content: center;
1306
+
1307
+ /* Allow centering when max-width/max-height applies. */
1308
+ margin: auto;
1309
+
1310
+ /* The host is not clickable, only the overlay part is. */
1311
+ pointer-events: none;
1312
+
1313
+ /* Remove tap highlight on touch devices. */
1314
+ -webkit-tap-highlight-color: transparent;
1315
+
1316
+ /* CSS API for host */
1317
+ --vaadin-overlay-viewport-bottom: 0;
1318
+ }
1319
+
1320
+ :host([hidden]),
1321
+ :host(:not([opened]):not([closing])) {
1322
+ display: none !important;
1323
+ }
1324
+
1325
+ [part='overlay'] {
1326
+ -webkit-overflow-scrolling: touch;
1327
+ overflow: auto;
1328
+ pointer-events: auto;
1329
+
1330
+ /* Prevent overflowing the host in MSIE 11 */
1331
+ max-width: 100%;
1332
+ box-sizing: border-box;
1333
+
1334
+ -webkit-tap-highlight-color: initial; /* reenable tap highlight inside */
1335
+ }
1336
+
1337
+ [part='backdrop'] {
1338
+ z-index: -1;
1339
+ content: '';
1340
+ background: rgba(0, 0, 0, 0.5);
1341
+ position: fixed;
1342
+ top: 0;
1343
+ left: 0;
1344
+ bottom: 0;
1345
+ right: 0;
1346
+ pointer-events: auto;
1347
+ }
1348
+ </style>
1349
+
1350
+ <div id="backdrop" part="backdrop" hidden$="[[!withBackdrop]]"></div>
1351
+ <div part="overlay" id="overlay" tabindex="0">
1352
+ <div part="content" id="content">
1353
+ <slot></slot>
1354
+ </div>
1355
+ </div>
1356
+ `;
1357
+ }
1358
+
1359
+ static get is() {
1360
+ return 'vaadin-overlay';
1361
+ }
1362
+
1363
+ static get properties() {
1364
+ return {
1365
+ /**
1366
+ * When true, the overlay is visible and attached to body.
1367
+ */
1368
+ opened: {
1369
+ type: Boolean,
1370
+ notify: true,
1371
+ observer: '_openedChanged',
1372
+ reflectToAttribute: true,
1373
+ },
1374
+
1375
+ /**
1376
+ * Owner element passed with renderer function
1377
+ * @type {HTMLElement}
1378
+ */
1379
+ owner: Element,
1380
+
1381
+ /**
1382
+ * Custom function for rendering the content of the overlay.
1383
+ * Receives three arguments:
1384
+ *
1385
+ * - `root` The root container DOM element. Append your content to it.
1386
+ * - `owner` The host element of the renderer function.
1387
+ * - `model` The object with the properties related with rendering.
1388
+ * @type {OverlayRenderer | null | undefined}
1389
+ */
1390
+ renderer: Function,
1391
+
1392
+ /**
1393
+ * The template of the overlay content.
1394
+ * @type {HTMLTemplateElement | null | undefined}
1395
+ */
1396
+ template: {
1397
+ type: Object,
1398
+ notify: true,
1399
+ },
1400
+
1401
+ /**
1402
+ * References the content container after the template is stamped.
1403
+ * @type {!HTMLElement | undefined}
1404
+ */
1405
+ content: {
1406
+ type: Object,
1407
+ notify: true,
1408
+ },
1409
+
1410
+ /**
1411
+ * When true the overlay has backdrop on top of content when opened.
1412
+ * @type {boolean}
1413
+ */
1414
+ withBackdrop: {
1415
+ type: Boolean,
1416
+ value: false,
1417
+ reflectToAttribute: true,
1418
+ },
1419
+
1420
+ /**
1421
+ * Object with properties that is passed to `renderer` function
1422
+ */
1423
+ model: Object,
1424
+
1425
+ /**
1426
+ * When true the overlay won't disable the main content, showing
1427
+ * it doesn’t change the functionality of the user interface.
1428
+ * @type {boolean}
1429
+ */
1430
+ modeless: {
1431
+ type: Boolean,
1432
+ value: false,
1433
+ reflectToAttribute: true,
1434
+ observer: '_modelessChanged',
1435
+ },
1436
+
1437
+ /**
1438
+ * When set to true, the overlay is hidden. This also closes the overlay
1439
+ * immediately in case there is a closing animation in progress.
1440
+ * @type {boolean}
1441
+ */
1442
+ hidden: {
1443
+ type: Boolean,
1444
+ reflectToAttribute: true,
1445
+ observer: '_hiddenChanged',
1446
+ },
1447
+
1448
+ /**
1449
+ * When true move focus to the first focusable element in the overlay,
1450
+ * or to the overlay if there are no focusable elements.
1451
+ * @type {boolean}
1452
+ */
1453
+ focusTrap: {
1454
+ type: Boolean,
1455
+ value: false,
1456
+ },
1457
+
1458
+ /**
1459
+ * Set to true to enable restoring of focus when overlay is closed.
1460
+ * @type {boolean}
1461
+ */
1462
+ restoreFocusOnClose: {
1463
+ type: Boolean,
1464
+ value: false,
1465
+ },
1466
+
1467
+ /**
1468
+ * Set to specify the element which should be focused on overlay close,
1469
+ * if `restoreFocusOnClose` is set to true.
1470
+ * @type {HTMLElement}
1471
+ */
1472
+ restoreFocusNode: {
1473
+ type: HTMLElement,
1474
+ },
1475
+
1476
+ /** @private */
1477
+ _mouseDownInside: {
1478
+ type: Boolean,
1479
+ },
1480
+
1481
+ /** @private */
1482
+ _mouseUpInside: {
1483
+ type: Boolean,
1484
+ },
1485
+
1486
+ /** @private */
1487
+ _instance: {
1488
+ type: Object,
1489
+ },
1490
+
1491
+ /** @private */
1492
+ _originalContentPart: Object,
1493
+
1494
+ /** @private */
1495
+ _contentNodes: Array,
1496
+
1497
+ /** @private */
1498
+ _oldOwner: Element,
1499
+
1500
+ /** @private */
1501
+ _oldModel: Object,
1502
+
1503
+ /** @private */
1504
+ _oldTemplate: Object,
1505
+
1506
+ /** @private */
1507
+ _oldRenderer: Object,
1508
+
1509
+ /** @private */
1510
+ _oldOpened: Boolean,
1511
+ };
1512
+ }
1513
+
1514
+ static get observers() {
1515
+ return ['_templateOrRendererChanged(template, renderer, owner, model, opened)'];
1516
+ }
1517
+
1518
+ constructor() {
1519
+ super();
1520
+ this._boundMouseDownListener = this._mouseDownListener.bind(this);
1521
+ this._boundMouseUpListener = this._mouseUpListener.bind(this);
1522
+ this._boundOutsideClickListener = this._outsideClickListener.bind(this);
1523
+ this._boundKeydownListener = this._keydownListener.bind(this);
1524
+
1525
+ this._observer = new FlattenedNodesObserver(this, (info) => {
1526
+ this._setTemplateFromNodes(info.addedNodes);
1527
+ });
1528
+
1529
+ // Listener for preventing closing of the paper-dialog and all components extending `iron-overlay-behavior`.
1530
+ this._boundIronOverlayCanceledListener = this._ironOverlayCanceled.bind(this);
1531
+
1532
+ /* c8 ignore next 3 */
1533
+ if (isIOS) {
1534
+ this._boundIosResizeListener = () => this._detectIosNavbar();
1535
+ }
1536
+
1537
+ this.__focusTrapController = new FocusTrapController(this);
1538
+ }
1539
+
1540
+ /** @protected */
1541
+ ready() {
1542
+ super.ready();
1543
+
1544
+ this._observer.flush();
1545
+
1546
+ // Need to add dummy click listeners to this and the backdrop or else
1547
+ // the document click event listener (_outsideClickListener) may never
1548
+ // get invoked on iOS Safari (reproducible in <vaadin-dialog>
1549
+ // and <vaadin-context-menu>).
1550
+ this.addEventListener('click', () => {});
1551
+ this.$.backdrop.addEventListener('click', () => {});
1552
+
1553
+ this.addController(this.__focusTrapController);
1554
+ }
1555
+
1556
+ /** @private */
1557
+ _detectIosNavbar() {
1558
+ /* c8 ignore next 15 */
1559
+ if (!this.opened) {
1560
+ return;
1561
+ }
1562
+
1563
+ const innerHeight = window.innerHeight;
1564
+ const innerWidth = window.innerWidth;
1565
+
1566
+ const landscape = innerWidth > innerHeight;
1567
+
1568
+ const clientHeight = document.documentElement.clientHeight;
1569
+
1570
+ if (landscape && clientHeight > innerHeight) {
1571
+ this.style.setProperty('--vaadin-overlay-viewport-bottom', `${clientHeight - innerHeight}px`);
1572
+ } else {
1573
+ this.style.setProperty('--vaadin-overlay-viewport-bottom', '0');
1574
+ }
1575
+ }
1576
+
1577
+ /**
1578
+ * @param {!Array<!Element>} nodes
1579
+ * @protected
1580
+ */
1581
+ _setTemplateFromNodes(nodes) {
1582
+ this.template = nodes.find((node) => node.localName && node.localName === 'template') || this.template;
1583
+ }
1584
+
1585
+ /**
1586
+ * @param {Event=} sourceEvent
1587
+ * @event vaadin-overlay-close
1588
+ * fired before the `vaadin-overlay` will be closed. If canceled the closing of the overlay is canceled as well.
1589
+ */
1590
+ close(sourceEvent) {
1591
+ const evt = new CustomEvent('vaadin-overlay-close', {
1592
+ bubbles: true,
1593
+ cancelable: true,
1594
+ detail: { sourceEvent },
1595
+ });
1596
+ this.dispatchEvent(evt);
1597
+ if (!evt.defaultPrevented) {
1598
+ this.opened = false;
1599
+ }
1600
+ }
1601
+
1602
+ /** @protected */
1603
+ connectedCallback() {
1604
+ super.connectedCallback();
1605
+
1606
+ /* c8 ignore next 3 */
1607
+ if (this._boundIosResizeListener) {
1608
+ this._detectIosNavbar();
1609
+ window.addEventListener('resize', this._boundIosResizeListener);
1610
+ }
1611
+ }
1612
+
1613
+ /** @protected */
1614
+ disconnectedCallback() {
1615
+ super.disconnectedCallback();
1616
+
1617
+ /* c8 ignore next 3 */
1618
+ if (this._boundIosResizeListener) {
1619
+ window.removeEventListener('resize', this._boundIosResizeListener);
1620
+ }
1621
+ }
1622
+
1623
+ /**
1624
+ * Requests an update for the content of the overlay.
1625
+ * While performing the update, it invokes the renderer passed in the `renderer` property.
1626
+ *
1627
+ * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
1628
+ */
1629
+ requestContentUpdate() {
1630
+ if (this.renderer) {
1631
+ this.renderer.call(this.owner, this.content, this.owner, this.model);
1632
+ }
1633
+ }
1634
+
1635
+ /** @private */
1636
+ _ironOverlayCanceled(event) {
1637
+ event.preventDefault();
1638
+ }
1639
+
1640
+ /** @private */
1641
+ _mouseDownListener(event) {
1642
+ this._mouseDownInside = event.composedPath().indexOf(this.$.overlay) >= 0;
1643
+ }
1644
+
1645
+ /** @private */
1646
+ _mouseUpListener(event) {
1647
+ this._mouseUpInside = event.composedPath().indexOf(this.$.overlay) >= 0;
1648
+ }
1649
+
1650
+ /**
1651
+ * We need to listen on 'click' / 'tap' event and capture it and close the overlay before
1652
+ * propagating the event to the listener in the button. Otherwise, if the clicked button would call
1653
+ * open(), this would happen: https://www.youtube.com/watch?v=Z86V_ICUCD4
1654
+ *
1655
+ * @event vaadin-overlay-outside-click
1656
+ * fired before the `vaadin-overlay` will be closed on outside click. If canceled the closing of the overlay is canceled as well.
1657
+ *
1658
+ * @private
1659
+ */
1660
+ _outsideClickListener(event) {
1661
+ if (event.composedPath().includes(this.$.overlay) || this._mouseDownInside || this._mouseUpInside) {
1662
+ this._mouseDownInside = false;
1663
+ this._mouseUpInside = false;
1664
+ return;
1665
+ }
1666
+ if (!this._last) {
1667
+ return;
1668
+ }
1669
+
1670
+ const evt = new CustomEvent('vaadin-overlay-outside-click', {
1671
+ bubbles: true,
1672
+ cancelable: true,
1673
+ detail: { sourceEvent: event },
1674
+ });
1675
+ this.dispatchEvent(evt);
1676
+
1677
+ if (this.opened && !evt.defaultPrevented) {
1678
+ this.close(event);
1679
+ }
1680
+ }
1681
+
1682
+ /**
1683
+ * @event vaadin-overlay-escape-press
1684
+ * fired before the `vaadin-overlay` will be closed on ESC button press. If canceled the closing of the overlay is canceled as well.
1685
+ *
1686
+ * @private
1687
+ */
1688
+ _keydownListener(event) {
1689
+ if (!this._last) {
1690
+ return;
1691
+ }
1692
+
1693
+ // Only close modeless overlay on Esc press when it contains focus
1694
+ if (this.modeless && !event.composedPath().includes(this.$.overlay)) {
1695
+ return;
1696
+ }
1697
+
1698
+ if (event.key === 'Escape') {
1699
+ const evt = new CustomEvent('vaadin-overlay-escape-press', {
1700
+ bubbles: true,
1701
+ cancelable: true,
1702
+ detail: { sourceEvent: event },
1703
+ });
1704
+ this.dispatchEvent(evt);
1705
+
1706
+ if (this.opened && !evt.defaultPrevented) {
1707
+ this.close(event);
1708
+ }
1709
+ }
1710
+ }
1711
+
1712
+ /** @protected */
1713
+ _ensureTemplatized() {
1714
+ this._setTemplateFromNodes(Array.from(this.children));
1715
+ }
1716
+
1717
+ /**
1718
+ * @event vaadin-overlay-open
1719
+ * fired after the `vaadin-overlay` is opened.
1720
+ *
1721
+ * @private
1722
+ */
1723
+ _openedChanged(opened, wasOpened) {
1724
+ if (!this._instance) {
1725
+ this._ensureTemplatized();
1726
+ }
1727
+
1728
+ if (opened) {
1729
+ // Store focused node.
1730
+ this.__restoreFocusNode = this._getActiveElement();
1731
+ this._animatedOpening();
1732
+
1733
+ afterNextRender(this, () => {
1734
+ if (this.focusTrap) {
1735
+ this.__focusTrapController.trapFocus(this.$.overlay);
1736
+ }
1737
+
1738
+ const evt = new CustomEvent('vaadin-overlay-open', { bubbles: true });
1739
+ this.dispatchEvent(evt);
1740
+ });
1741
+
1742
+ document.addEventListener('keydown', this._boundKeydownListener);
1743
+
1744
+ if (!this.modeless) {
1745
+ this._addGlobalListeners();
1746
+ }
1747
+ } else if (wasOpened) {
1748
+ if (this.focusTrap) {
1749
+ this.__focusTrapController.releaseFocus();
1750
+ }
1751
+
1752
+ this._animatedClosing();
1753
+
1754
+ document.removeEventListener('keydown', this._boundKeydownListener);
1755
+
1756
+ if (!this.modeless) {
1757
+ this._removeGlobalListeners();
1758
+ }
1759
+ }
1760
+ }
1761
+
1762
+ /** @private */
1763
+ _hiddenChanged(hidden) {
1764
+ if (hidden && this.hasAttribute('closing')) {
1765
+ this._flushAnimation('closing');
1766
+ }
1767
+ }
1768
+
1769
+ /**
1770
+ * @return {boolean}
1771
+ * @protected
1772
+ */
1773
+ _shouldAnimate() {
1774
+ const name = getComputedStyle(this).getPropertyValue('animation-name');
1775
+ const hidden = getComputedStyle(this).getPropertyValue('display') === 'none';
1776
+ return !hidden && name && name !== 'none';
1777
+ }
1778
+
1779
+ /**
1780
+ * @param {string} type
1781
+ * @param {Function} callback
1782
+ * @protected
1783
+ */
1784
+ _enqueueAnimation(type, callback) {
1785
+ const handler = `__${type}Handler`;
1786
+ const listener = (event) => {
1787
+ if (event && event.target !== this) {
1788
+ return;
1789
+ }
1790
+ callback();
1791
+ this.removeEventListener('animationend', listener);
1792
+ delete this[handler];
1793
+ };
1794
+ this[handler] = listener;
1795
+ this.addEventListener('animationend', listener);
1796
+ }
1797
+
1798
+ /**
1799
+ * @param {string} type
1800
+ * @protected
1801
+ */
1802
+ _flushAnimation(type) {
1803
+ const handler = `__${type}Handler`;
1804
+ if (typeof this[handler] === 'function') {
1805
+ this[handler]();
1806
+ }
1807
+ }
1808
+
1809
+ /** @protected */
1810
+ _animatedOpening() {
1811
+ if (this.parentNode === document.body && this.hasAttribute('closing')) {
1812
+ this._flushAnimation('closing');
1813
+ }
1814
+ this._attachOverlay();
1815
+ if (!this.modeless) {
1816
+ this._enterModalState();
1817
+ }
1818
+ this.setAttribute('opening', '');
1819
+
1820
+ if (this._shouldAnimate()) {
1821
+ this._enqueueAnimation('opening', () => {
1822
+ this._finishOpening();
1823
+ });
1824
+ } else {
1825
+ this._finishOpening();
1826
+ }
1827
+ }
1828
+
1829
+ /** @protected */
1830
+ _attachOverlay() {
1831
+ this._placeholder = document.createComment('vaadin-overlay-placeholder');
1832
+ this.parentNode.insertBefore(this._placeholder, this);
1833
+ document.body.appendChild(this);
1834
+ this.bringToFront();
1835
+ }
1836
+
1837
+ /** @protected */
1838
+ _finishOpening() {
1839
+ document.addEventListener('iron-overlay-canceled', this._boundIronOverlayCanceledListener);
1840
+ this.removeAttribute('opening');
1841
+ }
1842
+
1843
+ /** @protected */
1844
+ _finishClosing() {
1845
+ document.removeEventListener('iron-overlay-canceled', this._boundIronOverlayCanceledListener);
1846
+ this._detachOverlay();
1847
+ this.$.overlay.style.removeProperty('pointer-events');
1848
+ this.removeAttribute('closing');
1849
+ }
1850
+
1851
+ /**
1852
+ * @event vaadin-overlay-closing
1853
+ * Fired when the overlay will be closed.
1854
+ *
1855
+ * @protected
1856
+ */
1857
+ _animatedClosing() {
1858
+ if (this.hasAttribute('opening')) {
1859
+ this._flushAnimation('opening');
1860
+ }
1861
+ if (this._placeholder) {
1862
+ this._exitModalState();
1863
+
1864
+ // Use this.restoreFocusNode if specified, otherwise fallback to the node
1865
+ // which was focused before opening the overlay.
1866
+ const restoreFocusNode = this.restoreFocusNode || this.__restoreFocusNode;
1867
+
1868
+ if (this.restoreFocusOnClose && restoreFocusNode) {
1869
+ // If the activeElement is `<body>` or inside the overlay,
1870
+ // we are allowed to restore the focus. In all the other
1871
+ // cases focus might have been moved elsewhere by another
1872
+ // component or by the user interaction (e.g. click on a
1873
+ // button outside the overlay).
1874
+ const activeElement = this._getActiveElement();
1875
+
1876
+ if (activeElement === document.body || this._deepContains(activeElement)) {
1877
+ // Focusing the restoreFocusNode doesn't always work synchronously on Firefox and Safari
1878
+ // (e.g. combo-box overlay close on outside click).
1879
+ setTimeout(() => restoreFocusNode.focus());
1880
+ }
1881
+ this.__restoreFocusNode = null;
1882
+ }
1883
+
1884
+ this.setAttribute('closing', '');
1885
+ this.dispatchEvent(new CustomEvent('vaadin-overlay-closing'));
1886
+
1887
+ if (this._shouldAnimate()) {
1888
+ this._enqueueAnimation('closing', () => {
1889
+ this._finishClosing();
1890
+ });
1891
+ } else {
1892
+ this._finishClosing();
1893
+ }
1894
+ }
1895
+ }
1896
+
1897
+ /** @protected */
1898
+ _detachOverlay() {
1899
+ this._placeholder.parentNode.insertBefore(this, this._placeholder);
1900
+ this._placeholder.parentNode.removeChild(this._placeholder);
1901
+ }
1902
+
1903
+ /**
1904
+ * Returns all attached overlays in visual stacking order.
1905
+ * @private
1906
+ */
1907
+ static get __attachedInstances() {
1908
+ return Array.from(document.body.children)
1909
+ .filter((el) => el instanceof Overlay && !el.hasAttribute('closing'))
1910
+ .sort((a, b) => a.__zIndex - b.__zIndex || 0);
1911
+ }
1912
+
1913
+ /**
1914
+ * Returns true if this is the last one in the opened overlays stack
1915
+ * @return {boolean}
1916
+ * @protected
1917
+ */
1918
+ get _last() {
1919
+ return this === Overlay.__attachedInstances.pop();
1920
+ }
1921
+
1922
+ /** @private */
1923
+ _modelessChanged(modeless) {
1924
+ if (!modeless) {
1925
+ if (this.opened) {
1926
+ this._addGlobalListeners();
1927
+ this._enterModalState();
1928
+ }
1929
+ } else {
1930
+ this._removeGlobalListeners();
1931
+ this._exitModalState();
1932
+ }
1933
+ }
1934
+
1935
+ /** @protected */
1936
+ _addGlobalListeners() {
1937
+ document.addEventListener('mousedown', this._boundMouseDownListener);
1938
+ document.addEventListener('mouseup', this._boundMouseUpListener);
1939
+ // Firefox leaks click to document on contextmenu even if prevented
1940
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=990614
1941
+ document.documentElement.addEventListener('click', this._boundOutsideClickListener, true);
1942
+ }
1943
+
1944
+ /** @protected */
1945
+ _enterModalState() {
1946
+ if (document.body.style.pointerEvents !== 'none') {
1947
+ // Set body pointer-events to 'none' to disable mouse interactions with
1948
+ // other document nodes.
1949
+ this._previousDocumentPointerEvents = document.body.style.pointerEvents;
1950
+ document.body.style.pointerEvents = 'none';
1951
+ }
1952
+
1953
+ // Disable pointer events in other attached overlays
1954
+ Overlay.__attachedInstances.forEach((el) => {
1955
+ if (el !== this) {
1956
+ el.shadowRoot.querySelector('[part="overlay"]').style.pointerEvents = 'none';
1957
+ }
1958
+ });
1959
+ }
1960
+
1961
+ /** @protected */
1962
+ _removeGlobalListeners() {
1963
+ document.removeEventListener('mousedown', this._boundMouseDownListener);
1964
+ document.removeEventListener('mouseup', this._boundMouseUpListener);
1965
+ document.documentElement.removeEventListener('click', this._boundOutsideClickListener, true);
1966
+ }
1967
+
1968
+ /** @protected */
1969
+ _exitModalState() {
1970
+ if (this._previousDocumentPointerEvents !== undefined) {
1971
+ // Restore body pointer-events
1972
+ document.body.style.pointerEvents = this._previousDocumentPointerEvents;
1973
+ delete this._previousDocumentPointerEvents;
1974
+ }
1975
+
1976
+ // Restore pointer events in the previous overlay(s)
1977
+ const instances = Overlay.__attachedInstances;
1978
+ let el;
1979
+ // Use instances.pop() to ensure the reverse order
1980
+ while ((el = instances.pop())) {
1981
+ if (el === this) {
1982
+ // Skip the current instance
1983
+ continue;
1984
+ }
1985
+ el.shadowRoot.querySelector('[part="overlay"]').style.removeProperty('pointer-events');
1986
+ if (!el.modeless) {
1987
+ // Stop after the last modal
1988
+ break;
1989
+ }
1990
+ }
1991
+ }
1992
+
1993
+ /** @protected */
1994
+ _removeOldContent() {
1995
+ if (!this.content || !this._contentNodes) {
1996
+ return;
1997
+ }
1998
+
1999
+ this._observer.disconnect();
2000
+
2001
+ this._contentNodes.forEach((node) => {
2002
+ if (node.parentNode === this.content) {
2003
+ this.content.removeChild(node);
2004
+ }
2005
+ });
2006
+
2007
+ if (this._originalContentPart) {
2008
+ // Restore the original <div part="content">
2009
+ this.$.content.parentNode.replaceChild(this._originalContentPart, this.$.content);
2010
+ this.$.content = this._originalContentPart;
2011
+ this._originalContentPart = undefined;
2012
+ }
2013
+
2014
+ this._observer.connect();
2015
+
2016
+ this._contentNodes = undefined;
2017
+ this.content = undefined;
2018
+ }
2019
+
2020
+ /**
2021
+ * @param {!HTMLTemplateElement} template
2022
+ * @protected
2023
+ */
2024
+ _stampOverlayTemplate(template) {
2025
+ this._removeOldContent();
2026
+
2027
+ if (!template._Templatizer) {
2028
+ template._Templatizer = templatize(template, this, {
2029
+ forwardHostProp(prop, value) {
2030
+ if (this._instance) {
2031
+ this._instance.forwardHostProp(prop, value);
2032
+ }
2033
+ },
2034
+ });
2035
+ }
2036
+
2037
+ this._instance = new template._Templatizer({});
2038
+ this._contentNodes = Array.from(this._instance.root.childNodes);
2039
+
2040
+ const templateRoot = template._templateRoot || (template._templateRoot = template.getRootNode());
2041
+
2042
+ if (templateRoot !== document) {
2043
+ if (!this.$.content.shadowRoot) {
2044
+ this.$.content.attachShadow({ mode: 'open' });
2045
+ }
2046
+
2047
+ let scopeCssText = Array.from(templateRoot.querySelectorAll('style')).reduce(
2048
+ (result, style) => result + style.textContent,
2049
+ '',
2050
+ );
2051
+
2052
+ // The overlay root’s :host styles should not apply inside the overlay
2053
+ scopeCssText = scopeCssText.replace(/:host/g, ':host-nomatch');
2054
+
2055
+ if (scopeCssText) {
2056
+ // Append a style to the content shadowRoot
2057
+ const style = document.createElement('style');
2058
+ style.textContent = scopeCssText;
2059
+ this.$.content.shadowRoot.appendChild(style);
2060
+ this._contentNodes.unshift(style);
2061
+ }
2062
+
2063
+ this.$.content.shadowRoot.appendChild(this._instance.root);
2064
+ this.content = this.$.content.shadowRoot;
2065
+ } else {
2066
+ this.appendChild(this._instance.root);
2067
+ this.content = this;
2068
+ }
2069
+ }
2070
+
2071
+ /** @private */
2072
+ _removeNewRendererOrTemplate(template, oldTemplate, renderer, oldRenderer) {
2073
+ if (template !== oldTemplate) {
2074
+ this.template = undefined;
2075
+ } else if (renderer !== oldRenderer) {
2076
+ this.renderer = undefined;
2077
+ }
2078
+ }
2079
+
2080
+ /** @private */
2081
+ // eslint-disable-next-line max-params
2082
+ _templateOrRendererChanged(template, renderer, owner, model, opened) {
2083
+ if (template && renderer) {
2084
+ this._removeNewRendererOrTemplate(template, this._oldTemplate, renderer, this._oldRenderer);
2085
+ throw new Error('You should only use either a renderer or a template for overlay content');
2086
+ }
2087
+
2088
+ const ownerOrModelChanged = this._oldOwner !== owner || this._oldModel !== model;
2089
+ this._oldModel = model;
2090
+ this._oldOwner = owner;
2091
+
2092
+ const templateChanged = this._oldTemplate !== template;
2093
+ this._oldTemplate = template;
2094
+
2095
+ const rendererChanged = this._oldRenderer !== renderer;
2096
+ this._oldRenderer = renderer;
2097
+
2098
+ const openedChanged = this._oldOpened !== opened;
2099
+ this._oldOpened = opened;
2100
+
2101
+ if (rendererChanged) {
2102
+ this.content = this;
2103
+ this.content.innerHTML = '';
2104
+ // Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
2105
+ // When clearing the rendered content, this part needs to be manually disposed of.
2106
+ // Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
2107
+ delete this.content._$litPart$;
2108
+ }
2109
+
2110
+ if (template && templateChanged) {
2111
+ this._stampOverlayTemplate(template);
2112
+ } else if (renderer && (rendererChanged || openedChanged || ownerOrModelChanged)) {
2113
+ if (opened) {
2114
+ this.requestContentUpdate();
2115
+ }
2116
+ }
2117
+ }
2118
+
2119
+ /**
2120
+ * @return {!Element}
2121
+ * @protected
2122
+ */
2123
+ _getActiveElement() {
2124
+ // Document.activeElement can be null
2125
+ // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement
2126
+ let active = document.activeElement || document.body;
2127
+ while (active.shadowRoot && active.shadowRoot.activeElement) {
2128
+ active = active.shadowRoot.activeElement;
2129
+ }
2130
+ return active;
2131
+ }
2132
+
2133
+ /**
2134
+ * @param {!Node} node
2135
+ * @return {boolean}
2136
+ * @protected
2137
+ */
2138
+ _deepContains(node) {
2139
+ if (this.contains(node)) {
2140
+ return true;
2141
+ }
2142
+ let n = node;
2143
+ const doc = node.ownerDocument;
2144
+ // Walk from node to `this` or `document`
2145
+ while (n && n !== doc && n !== this) {
2146
+ n = n.parentNode || n.host;
2147
+ }
2148
+ return n === this;
2149
+ }
2150
+
2151
+ /**
2152
+ * Brings the overlay as visually the frontmost one
2153
+ */
2154
+ bringToFront() {
2155
+ let zIndex = '';
2156
+ const frontmost = Overlay.__attachedInstances.filter((o) => o !== this).pop();
2157
+ if (frontmost) {
2158
+ const frontmostZIndex = frontmost.__zIndex;
2159
+ zIndex = frontmostZIndex + 1;
2160
+ }
2161
+ this.style.zIndex = zIndex;
2162
+ this.__zIndex = zIndex || parseFloat(getComputedStyle(this).zIndex);
2163
+ }
2164
+ }
2165
+
2166
+ customElements.define(Overlay.is, Overlay);
2167
+
2168
+ /**
2169
+ * @license
2170
+ * Copyright (c) 2017 - 2022 Vaadin Ltd.
2171
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
2172
+ */
2173
+
2174
+ const menuOverlayCore = i`
2175
+ :host([opening]),
2176
+ :host([closing]) {
2177
+ animation: 0.14s lumo-overlay-dummy-animation;
2178
+ }
2179
+
2180
+ [part='overlay'] {
2181
+ will-change: opacity, transform;
2182
+ }
2183
+
2184
+ :host([opening]) [part='overlay'] {
2185
+ animation: 0.1s lumo-menu-overlay-enter ease-out both;
2186
+ }
2187
+
2188
+ @keyframes lumo-menu-overlay-enter {
2189
+ 0% {
2190
+ opacity: 0;
2191
+ transform: translateY(-4px);
2192
+ }
2193
+ }
2194
+
2195
+ :host([closing]) [part='overlay'] {
2196
+ animation: 0.1s lumo-menu-overlay-exit both;
2197
+ }
2198
+
2199
+ @keyframes lumo-menu-overlay-exit {
2200
+ 100% {
2201
+ opacity: 0;
2202
+ }
2203
+ }
2204
+ `;
2205
+
2206
+ registerStyles('', menuOverlayCore, { moduleId: 'lumo-menu-overlay-core' });
2207
+
2208
+ const menuOverlayExt = i`
2209
+ /* Small viewport (bottom sheet) styles */
2210
+ /* Use direct media queries instead of the state attributes ([phone] and [fullscreen]) provided by the elements */
2211
+ @media (max-width: 420px), (max-height: 420px) {
2212
+ :host {
2213
+ top: 0 !important;
2214
+ right: 0 !important;
2215
+ bottom: var(--vaadin-overlay-viewport-bottom, 0) !important;
2216
+ left: 0 !important;
2217
+ align-items: stretch !important;
2218
+ justify-content: flex-end !important;
2219
+ }
2220
+
2221
+ [part='overlay'] {
2222
+ max-height: 50vh;
2223
+ width: 100vw;
2224
+ border-radius: 0;
2225
+ box-shadow: var(--lumo-box-shadow-xl);
2226
+ }
2227
+
2228
+ /* The content part scrolls instead of the overlay part, because of the gradient fade-out */
2229
+ [part='content'] {
2230
+ padding: 30px var(--lumo-space-m);
2231
+ max-height: inherit;
2232
+ box-sizing: border-box;
2233
+ -webkit-overflow-scrolling: touch;
2234
+ overflow: auto;
2235
+ -webkit-mask-image: linear-gradient(transparent, #000 40px, #000 calc(100% - 40px), transparent);
2236
+ mask-image: linear-gradient(transparent, #000 40px, #000 calc(100% - 40px), transparent);
2237
+ }
2238
+
2239
+ [part='backdrop'] {
2240
+ display: block;
2241
+ }
2242
+
2243
+ /* Animations */
2244
+
2245
+ :host([opening]) [part='overlay'] {
2246
+ animation: 0.2s lumo-mobile-menu-overlay-enter cubic-bezier(0.215, 0.61, 0.355, 1) both;
2247
+ }
2248
+
2249
+ :host([closing]),
2250
+ :host([closing]) [part='backdrop'] {
2251
+ animation-delay: 0.14s;
2252
+ }
2253
+
2254
+ :host([closing]) [part='overlay'] {
2255
+ animation: 0.14s 0.14s lumo-mobile-menu-overlay-exit cubic-bezier(0.55, 0.055, 0.675, 0.19) both;
2256
+ }
2257
+ }
2258
+
2259
+ @keyframes lumo-mobile-menu-overlay-enter {
2260
+ 0% {
2261
+ transform: translateY(150%);
2262
+ }
2263
+ }
2264
+
2265
+ @keyframes lumo-mobile-menu-overlay-exit {
2266
+ 100% {
2267
+ transform: translateY(150%);
2268
+ }
2269
+ }
2270
+ `;
2271
+
2272
+ const menuOverlay = [overlay, menuOverlayCore, menuOverlayExt];
2273
+
2274
+ registerStyles('', menuOverlay, { moduleId: 'lumo-menu-overlay' });
2275
+
2276
+ /**
2277
+ * @license
2278
+ * Copyright (c) 2017 - 2022 Vaadin Ltd.
2279
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
2280
+ */
2281
+
2282
+ const PROP_NAMES_VERTICAL = {
2283
+ start: 'top',
2284
+ end: 'bottom',
2285
+ };
2286
+
2287
+ const PROP_NAMES_HORIZONTAL = {
2288
+ start: 'left',
2289
+ end: 'right',
2290
+ };
2291
+
2292
+ const targetResizeObserver = new ResizeObserver((entries) => {
2293
+ setTimeout(() => {
2294
+ entries.forEach((entry) => {
2295
+ if (entry.target.__overlay) {
2296
+ entry.target.__overlay._updatePosition();
2297
+ }
2298
+ });
2299
+ });
2300
+ });
2301
+
2302
+ /**
2303
+ * @polymerMixin
2304
+ */
2305
+ const PositionMixin = (superClass) =>
2306
+ class PositionMixin extends superClass {
2307
+ static get properties() {
2308
+ return {
2309
+ /**
2310
+ * The element next to which this overlay should be aligned.
2311
+ * The position of the overlay relative to the positionTarget can be adjusted
2312
+ * with properties `horizontalAlign`, `verticalAlign`, `noHorizontalOverlap`
2313
+ * and `noVerticalOverlap`.
2314
+ */
2315
+ positionTarget: {
2316
+ type: Object,
2317
+ value: null,
2318
+ },
2319
+
2320
+ /**
2321
+ * When `positionTarget` is set, this property defines whether to align the overlay's
2322
+ * left or right side to the target element by default.
2323
+ * Possible values are `start` and `end`.
2324
+ * RTL is taken into account when interpreting the value.
2325
+ * The overlay is automatically flipped to the opposite side when it doesn't fit into
2326
+ * the default side defined by this property.
2327
+ *
2328
+ * @attr {start|end} horizontal-align
2329
+ */
2330
+ horizontalAlign: {
2331
+ type: String,
2332
+ value: 'start',
2333
+ },
2334
+
2335
+ /**
2336
+ * When `positionTarget` is set, this property defines whether to align the overlay's
2337
+ * top or bottom side to the target element by default.
2338
+ * Possible values are `top` and `bottom`.
2339
+ * The overlay is automatically flipped to the opposite side when it doesn't fit into
2340
+ * the default side defined by this property.
2341
+ *
2342
+ * @attr {top|bottom} vertical-align
2343
+ */
2344
+ verticalAlign: {
2345
+ type: String,
2346
+ value: 'top',
2347
+ },
2348
+
2349
+ /**
2350
+ * When `positionTarget` is set, this property defines whether the overlay should overlap
2351
+ * the target element in the x-axis, or be positioned right next to it.
2352
+ *
2353
+ * @attr {boolean} no-horizontal-overlap
2354
+ */
2355
+ noHorizontalOverlap: {
2356
+ type: Boolean,
2357
+ value: false,
2358
+ },
2359
+
2360
+ /**
2361
+ * When `positionTarget` is set, this property defines whether the overlay should overlap
2362
+ * the target element in the y-axis, or be positioned right above/below it.
2363
+ *
2364
+ * @attr {boolean} no-vertical-overlap
2365
+ */
2366
+ noVerticalOverlap: {
2367
+ type: Boolean,
2368
+ value: false,
2369
+ },
2370
+
2371
+ /**
2372
+ * If the overlay content has no intrinsic height, this property can be used to set
2373
+ * the minimum vertical space (in pixels) required by the overlay. Setting a value to
2374
+ * the property effectively disables the content measurement in favor of using this
2375
+ * fixed value for determining the open direction.
2376
+ *
2377
+ * @attr {number} required-vertical-space
2378
+ */
2379
+ requiredVerticalSpace: {
2380
+ type: Number,
2381
+ value: 0,
2382
+ },
2383
+ };
2384
+ }
2385
+
2386
+ static get observers() {
2387
+ return [
2388
+ '__positionSettingsChanged(horizontalAlign, verticalAlign, noHorizontalOverlap, noVerticalOverlap, requiredVerticalSpace)',
2389
+ '__overlayOpenedChanged(opened, positionTarget)',
2390
+ ];
2391
+ }
2392
+
2393
+ constructor() {
2394
+ super();
2395
+
2396
+ this.__onScroll = this.__onScroll.bind(this);
2397
+ this._updatePosition = this._updatePosition.bind(this);
2398
+ }
2399
+
2400
+ /** @protected */
2401
+ connectedCallback() {
2402
+ super.connectedCallback();
2403
+
2404
+ if (this.opened) {
2405
+ this.__addUpdatePositionEventListeners();
2406
+ }
2407
+ }
2408
+
2409
+ /** @protected */
2410
+ disconnectedCallback() {
2411
+ super.disconnectedCallback();
2412
+ this.__removeUpdatePositionEventListeners();
2413
+ }
2414
+
2415
+ /** @private */
2416
+ __addUpdatePositionEventListeners() {
2417
+ window.addEventListener('resize', this._updatePosition);
2418
+
2419
+ this.__positionTargetAncestorRootNodes = getAncestorRootNodes(this.positionTarget);
2420
+ this.__positionTargetAncestorRootNodes.forEach((node) => {
2421
+ node.addEventListener('scroll', this.__onScroll, true);
2422
+ });
2423
+ }
2424
+
2425
+ /** @private */
2426
+ __removeUpdatePositionEventListeners() {
2427
+ window.removeEventListener('resize', this._updatePosition);
2428
+
2429
+ if (this.__positionTargetAncestorRootNodes) {
2430
+ this.__positionTargetAncestorRootNodes.forEach((node) => {
2431
+ node.removeEventListener('scroll', this.__onScroll, true);
2432
+ });
2433
+ this.__positionTargetAncestorRootNodes = null;
2434
+ }
2435
+ }
2436
+
2437
+ /** @private */
2438
+ __overlayOpenedChanged(opened, positionTarget) {
2439
+ this.__removeUpdatePositionEventListeners();
2440
+
2441
+ if (positionTarget) {
2442
+ positionTarget.__overlay = null;
2443
+ targetResizeObserver.unobserve(positionTarget);
2444
+
2445
+ if (opened) {
2446
+ this.__addUpdatePositionEventListeners();
2447
+ positionTarget.__overlay = this;
2448
+ targetResizeObserver.observe(positionTarget);
2449
+ }
2450
+ }
2451
+
2452
+ if (opened) {
2453
+ const computedStyle = getComputedStyle(this);
2454
+ if (!this.__margins) {
2455
+ this.__margins = {};
2456
+ ['top', 'bottom', 'left', 'right'].forEach((propName) => {
2457
+ this.__margins[propName] = parseInt(computedStyle[propName], 10);
2458
+ });
2459
+ }
2460
+ this.setAttribute('dir', computedStyle.direction);
2461
+
2462
+ this._updatePosition();
2463
+ // Schedule another position update (to cover virtual keyboard opening for example)
2464
+ requestAnimationFrame(() => this._updatePosition());
2465
+ }
2466
+ }
2467
+
2468
+ get __isRTL() {
2469
+ return this.getAttribute('dir') === 'rtl';
2470
+ }
2471
+
2472
+ __positionSettingsChanged() {
2473
+ this._updatePosition();
2474
+ }
2475
+
2476
+ /** @private */
2477
+ __onScroll(e) {
2478
+ // If the scroll event occurred inside the overlay, ignore it.
2479
+ if (!this.contains(e.target)) {
2480
+ this._updatePosition();
2481
+ }
2482
+ }
2483
+
2484
+ _updatePosition() {
2485
+ if (!this.positionTarget || !this.opened) {
2486
+ return;
2487
+ }
2488
+
2489
+ const targetRect = this.positionTarget.getBoundingClientRect();
2490
+
2491
+ // Detect the desired alignment and update the layout accordingly
2492
+ const shouldAlignStartVertically = this.__shouldAlignStartVertically(targetRect);
2493
+ this.style.justifyContent = shouldAlignStartVertically ? 'flex-start' : 'flex-end';
2494
+
2495
+ const shouldAlignStartHorizontally = this.__shouldAlignStartHorizontally(targetRect, this.__isRTL);
2496
+ const flexStart =
2497
+ (!this.__isRTL && shouldAlignStartHorizontally) || (this.__isRTL && !shouldAlignStartHorizontally);
2498
+ this.style.alignItems = flexStart ? 'flex-start' : 'flex-end';
2499
+
2500
+ // Get the overlay rect after possible overlay alignment changes
2501
+ const overlayRect = this.getBoundingClientRect();
2502
+
2503
+ // Obtain vertical positioning properties
2504
+ const verticalProps = this.__calculatePositionInOneDimension(
2505
+ targetRect,
2506
+ overlayRect,
2507
+ this.noVerticalOverlap,
2508
+ PROP_NAMES_VERTICAL,
2509
+ this,
2510
+ shouldAlignStartVertically,
2511
+ );
2512
+
2513
+ // Obtain horizontal positioning properties
2514
+ const horizontalProps = this.__calculatePositionInOneDimension(
2515
+ targetRect,
2516
+ overlayRect,
2517
+ this.noHorizontalOverlap,
2518
+ PROP_NAMES_HORIZONTAL,
2519
+ this,
2520
+ shouldAlignStartHorizontally,
2521
+ );
2522
+
2523
+ // Apply the positioning properties to the overlay
2524
+ Object.assign(this.style, verticalProps, horizontalProps);
2525
+
2526
+ this.toggleAttribute('bottom-aligned', !shouldAlignStartVertically);
2527
+ this.toggleAttribute('top-aligned', shouldAlignStartVertically);
2528
+
2529
+ this.toggleAttribute('end-aligned', !flexStart);
2530
+ this.toggleAttribute('start-aligned', flexStart);
2531
+ }
2532
+
2533
+ __shouldAlignStartHorizontally(targetRect, rtl) {
2534
+ // Using previous size to fix a case where window resize may cause the overlay to be squeezed
2535
+ // smaller than its current space before the fit-calculations.
2536
+ const contentWidth = Math.max(this.__oldContentWidth || 0, this.$.overlay.offsetWidth);
2537
+ this.__oldContentWidth = this.$.overlay.offsetWidth;
2538
+
2539
+ const viewportWidth = Math.min(window.innerWidth, document.documentElement.clientWidth);
2540
+ const defaultAlignLeft = (!rtl && this.horizontalAlign === 'start') || (rtl && this.horizontalAlign === 'end');
2541
+
2542
+ return this.__shouldAlignStart(
2543
+ targetRect,
2544
+ contentWidth,
2545
+ viewportWidth,
2546
+ this.__margins,
2547
+ defaultAlignLeft,
2548
+ this.noHorizontalOverlap,
2549
+ PROP_NAMES_HORIZONTAL,
2550
+ );
2551
+ }
2552
+
2553
+ __shouldAlignStartVertically(targetRect) {
2554
+ // Using previous size to fix a case where window resize may cause the overlay to be squeezed
2555
+ // smaller than its current space before the fit-calculations.
2556
+ const contentHeight =
2557
+ this.requiredVerticalSpace || Math.max(this.__oldContentHeight || 0, this.$.overlay.offsetHeight);
2558
+ this.__oldContentHeight = this.$.overlay.offsetHeight;
2559
+
2560
+ const viewportHeight = Math.min(window.innerHeight, document.documentElement.clientHeight);
2561
+ const defaultAlignTop = this.verticalAlign === 'top';
2562
+
2563
+ return this.__shouldAlignStart(
2564
+ targetRect,
2565
+ contentHeight,
2566
+ viewportHeight,
2567
+ this.__margins,
2568
+ defaultAlignTop,
2569
+ this.noVerticalOverlap,
2570
+ PROP_NAMES_VERTICAL,
2571
+ );
2572
+ }
2573
+
2574
+ // eslint-disable-next-line max-params
2575
+ __shouldAlignStart(targetRect, contentSize, viewportSize, margins, defaultAlignStart, noOverlap, propNames) {
2576
+ const spaceForStartAlignment =
2577
+ viewportSize - targetRect[noOverlap ? propNames.end : propNames.start] - margins[propNames.end];
2578
+ const spaceForEndAlignment = targetRect[noOverlap ? propNames.start : propNames.end] - margins[propNames.start];
2579
+
2580
+ const spaceForDefaultAlignment = defaultAlignStart ? spaceForStartAlignment : spaceForEndAlignment;
2581
+ const spaceForOtherAlignment = defaultAlignStart ? spaceForEndAlignment : spaceForStartAlignment;
2582
+
2583
+ const shouldGoToDefaultSide =
2584
+ spaceForDefaultAlignment > spaceForOtherAlignment || spaceForDefaultAlignment > contentSize;
2585
+
2586
+ return defaultAlignStart === shouldGoToDefaultSide;
2587
+ }
2588
+
2589
+ /**
2590
+ * Returns an adjusted value after resizing the browser window,
2591
+ * to avoid wrong calculations when e.g. previously set `bottom`
2592
+ * CSS property value is larger than the updated viewport height.
2593
+ * See https://github.com/vaadin/web-components/issues/4604
2594
+ */
2595
+ __adjustBottomProperty(cssPropNameToSet, propNames, currentValue) {
2596
+ let adjustedProp;
2597
+
2598
+ if (cssPropNameToSet === propNames.end) {
2599
+ // Adjust horizontally
2600
+ if (propNames.end === PROP_NAMES_VERTICAL.end) {
2601
+ const viewportHeight = Math.min(window.innerHeight, document.documentElement.clientHeight);
2602
+
2603
+ if (currentValue > viewportHeight && this.__oldViewportHeight) {
2604
+ const heightDiff = this.__oldViewportHeight - viewportHeight;
2605
+ adjustedProp = currentValue - heightDiff;
2606
+ }
2607
+
2608
+ this.__oldViewportHeight = viewportHeight;
2609
+ }
2610
+
2611
+ // Adjust vertically
2612
+ if (propNames.end === PROP_NAMES_HORIZONTAL.end) {
2613
+ const viewportWidth = Math.min(window.innerWidth, document.documentElement.clientWidth);
2614
+
2615
+ if (currentValue > viewportWidth && this.__oldViewportWidth) {
2616
+ const widthDiff = this.__oldViewportWidth - viewportWidth;
2617
+ adjustedProp = currentValue - widthDiff;
2618
+ }
2619
+
2620
+ this.__oldViewportWidth = viewportWidth;
2621
+ }
2622
+ }
2623
+
2624
+ return adjustedProp;
2625
+ }
2626
+
2627
+ /**
2628
+ * Returns an object with CSS position properties to set,
2629
+ * e.g. { top: "100px" }
2630
+ */
2631
+ // eslint-disable-next-line max-params
2632
+ __calculatePositionInOneDimension(targetRect, overlayRect, noOverlap, propNames, overlay, shouldAlignStart) {
2633
+ const cssPropNameToSet = shouldAlignStart ? propNames.start : propNames.end;
2634
+ const cssPropNameToClear = shouldAlignStart ? propNames.end : propNames.start;
2635
+
2636
+ const currentValue = parseFloat(overlay.style[cssPropNameToSet] || getComputedStyle(overlay)[cssPropNameToSet]);
2637
+ const adjustedValue = this.__adjustBottomProperty(cssPropNameToSet, propNames, currentValue);
2638
+
2639
+ const diff =
2640
+ overlayRect[shouldAlignStart ? propNames.start : propNames.end] -
2641
+ targetRect[noOverlap === shouldAlignStart ? propNames.end : propNames.start];
2642
+
2643
+ const valueToSet = adjustedValue
2644
+ ? `${adjustedValue}px`
2645
+ : `${currentValue + diff * (shouldAlignStart ? -1 : 1)}px`;
2646
+
2647
+ return {
2648
+ [cssPropNameToSet]: valueToSet,
2649
+ [cssPropNameToClear]: '',
2650
+ };
2651
+ }
2652
+ };
2653
+
2654
+ /**
2655
+ * @license
2656
+ * Copyright (c) 2021 - 2022 Vaadin Ltd.
2657
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
2658
+ */
2659
+
2660
+ /**
2661
+ * A controller which prevents the virtual keyboard from showing up on mobile devices
2662
+ * when the field's overlay is closed.
2663
+ */
2664
+ class VirtualKeyboardController {
2665
+ /**
2666
+ * @param {{ inputElement?: HTMLElement; opened: boolean } & HTMLElement} host
2667
+ */
2668
+ constructor(host) {
2669
+ this.host = host;
2670
+
2671
+ host.addEventListener('opened-changed', () => {
2672
+ if (!host.opened) {
2673
+ // Prevent opening the virtual keyboard when the input gets re-focused on dropdown close
2674
+ this.__setVirtualKeyboardEnabled(false);
2675
+ }
2676
+ });
2677
+
2678
+ // Re-enable virtual keyboard on blur, so it gets opened when the field is focused again
2679
+ host.addEventListener('blur', () => this.__setVirtualKeyboardEnabled(true));
2680
+
2681
+ // Re-enable the virtual keyboard whenever the field is touched
2682
+ host.addEventListener('touchstart', () => this.__setVirtualKeyboardEnabled(true));
2683
+ }
2684
+
2685
+ /** @private */
2686
+ __setVirtualKeyboardEnabled(value) {
2687
+ if (this.host.inputElement) {
2688
+ this.host.inputElement.inputMode = value ? '' : 'none';
2689
+ }
2690
+ }
2691
+ }
2692
+
2693
+ export { Overlay as O, PositionMixin as P, VirtualKeyboardController as V, OptionalMutableData as a, modelForElement as b, afterNextRender as c, isIOS as d, menuOverlayCore as e, isSafari as f, isTouch as g, isFirefox as i, menuOverlay as m, overlay as o, templatize as t };