@everymatrix/general-input 1.22.0 → 1.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/checkbox-group-input_10.cjs.entry.js +12282 -11782
- package/dist/components/active-mixin.js +5 -5
- package/dist/components/checkbox-group-input2.js +248 -303
- package/dist/components/date-input2.js +3461 -2218
- package/dist/components/field-mixin.js +2188 -2474
- package/dist/components/input-field-shared-styles.js +308 -211
- package/dist/components/password-input2.js +119 -72
- package/dist/components/vaadin-button.js +102 -73
- package/dist/components/vaadin-combo-box.js +964 -803
- package/dist/components/virtual-keyboard-controller.js +1114 -1771
- package/dist/esm/checkbox-group-input_10.entry.js +12282 -11782
- package/dist/general-input/general-input.esm.js +1 -1
- package/dist/general-input/p-bcde6ed8.entry.js +3646 -0
- package/package.json +9 -6
- package/dist/components/pattern-mixin.js +0 -85
- package/dist/general-input/p-553c91f3.entry.js +0 -3583
- /package/dist/types/Users/{catalin.poclid/Documents/work → adrian.pripon/Documents/Work}/widgets-stencil/packages/general-input/.stencil/packages/general-input/stencil.config.d.ts +0 -0
|
@@ -1,11 +1,111 @@
|
|
|
1
|
-
import { i, r as registerStyles,
|
|
2
|
-
import {
|
|
3
|
-
import { i as inputFieldShared, e as isSafari, f as isTouch, c as InputControlMixin, d as inputFieldShared$1 } from './input-field-shared-styles.js';
|
|
4
|
-
|
|
1
|
+
import { i, r as registerStyles, g as defineCustomElement, h as html, j as ThemableMixin, o as DirMixin, P as PolymerElement, n as microTask, R as idlePeriod, U as animationFrame, W as flush, p as Debouncer, X as enqueueDebouncer, t as timeOut, q as generateUniqueId, J as ControllerMixin, V as ValidateMixin, m as FocusMixin, K as KeyboardMixin, I as InputMixin, a as DisabledMixin, b as isElementFocused, e as InputController, f as LabelledInputController, T as TooltipController, E as ElementMixin } from './field-mixin.js';
|
|
2
|
+
import { c as overlay, d as menuOverlayCore, P as PositionMixin, O as OverlayMixin, o as overlayStyles, b as OverlayClassMixin, V as VirtualKeyboardController } from './virtual-keyboard-controller.js';
|
|
3
|
+
import { i as inputFieldShared, e as isSafari, I as InputConstraintsMixin, f as isTouch, c as InputControlMixin, d as inputFieldShared$1 } from './input-field-shared-styles.js';
|
|
4
|
+
|
|
5
|
+
const item = i`
|
|
6
|
+
:host {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
font-family: var(--lumo-font-family);
|
|
11
|
+
font-size: var(--lumo-font-size-m);
|
|
12
|
+
line-height: var(--lumo-line-height-xs);
|
|
13
|
+
padding: 0.5em calc(var(--lumo-space-l) + var(--lumo-border-radius-m) / 4) 0.5em
|
|
14
|
+
var(--_lumo-list-box-item-padding-left, calc(var(--lumo-border-radius-m) / 4));
|
|
15
|
+
min-height: var(--lumo-size-m);
|
|
16
|
+
outline: none;
|
|
17
|
+
border-radius: var(--lumo-border-radius-m);
|
|
18
|
+
cursor: var(--lumo-clickable-cursor);
|
|
19
|
+
-webkit-font-smoothing: antialiased;
|
|
20
|
+
-moz-osx-font-smoothing: grayscale;
|
|
21
|
+
-webkit-tap-highlight-color: var(--lumo-primary-color-10pct);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Checkmark */
|
|
25
|
+
[part='checkmark']::before {
|
|
26
|
+
display: var(--_lumo-item-selected-icon-display, none);
|
|
27
|
+
content: var(--lumo-icons-checkmark);
|
|
28
|
+
font-family: lumo-icons;
|
|
29
|
+
font-size: var(--lumo-icon-size-m);
|
|
30
|
+
line-height: 1;
|
|
31
|
+
font-weight: normal;
|
|
32
|
+
width: 1em;
|
|
33
|
+
height: 1em;
|
|
34
|
+
margin: calc((1 - var(--lumo-line-height-xs)) * var(--lumo-font-size-m) / 2) 0;
|
|
35
|
+
color: var(--lumo-primary-text-color);
|
|
36
|
+
flex: none;
|
|
37
|
+
opacity: 0;
|
|
38
|
+
transition: transform 0.2s cubic-bezier(0.12, 0.32, 0.54, 2), opacity 0.1s;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:host([selected]) [part='checkmark']::before {
|
|
42
|
+
opacity: 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
:host([active]:not([selected])) [part='checkmark']::before {
|
|
46
|
+
transform: scale(0.8);
|
|
47
|
+
opacity: 0;
|
|
48
|
+
transition-duration: 0s;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
[part='content'] {
|
|
52
|
+
flex: auto;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Disabled */
|
|
56
|
+
:host([disabled]) {
|
|
57
|
+
color: var(--lumo-disabled-text-color);
|
|
58
|
+
cursor: default;
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* TODO a workaround until we have "focus-follows-mouse". After that, use the hover style for focus-ring as well */
|
|
63
|
+
@media (any-hover: hover) {
|
|
64
|
+
:host(:hover:not([disabled])) {
|
|
65
|
+
background-color: var(--lumo-primary-color-10pct);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
:host([focus-ring]:not([disabled])) {
|
|
69
|
+
box-shadow: inset 0 0 0 2px var(--lumo-primary-color-50pct);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* RTL specific styles */
|
|
74
|
+
:host([dir='rtl']) {
|
|
75
|
+
padding-left: calc(var(--lumo-space-l) + var(--lumo-border-radius-m) / 4);
|
|
76
|
+
padding-right: var(--_lumo-list-box-item-padding-left, calc(var(--lumo-border-radius-m) / 4));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Slotted icons */
|
|
80
|
+
:host ::slotted(vaadin-icon) {
|
|
81
|
+
width: var(--lumo-icon-size-m);
|
|
82
|
+
height: var(--lumo-icon-size-m);
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
registerStyles('vaadin-item', item, { moduleId: 'lumo-item' });
|
|
87
|
+
|
|
88
|
+
const comboBoxItem = i`
|
|
89
|
+
:host {
|
|
90
|
+
transition: background-color 100ms;
|
|
91
|
+
overflow: hidden;
|
|
92
|
+
--_lumo-item-selected-icon-display: block;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@media (any-hover: hover) {
|
|
96
|
+
:host([focused]:not([disabled])) {
|
|
97
|
+
box-shadow: inset 0 0 0 2px var(--lumo-primary-color-50pct);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
registerStyles('vaadin-combo-box-item', [item, comboBoxItem], {
|
|
103
|
+
moduleId: 'lumo-combo-box-item',
|
|
104
|
+
});
|
|
5
105
|
|
|
6
106
|
/**
|
|
7
107
|
* @license
|
|
8
|
-
* Copyright (c) 2022 Vaadin Ltd.
|
|
108
|
+
* Copyright (c) 2022 - 2023 Vaadin Ltd.
|
|
9
109
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
10
110
|
*/
|
|
11
111
|
|
|
@@ -56,14 +156,6 @@ const comboBoxOverlay = i`
|
|
|
56
156
|
padding: 0;
|
|
57
157
|
}
|
|
58
158
|
|
|
59
|
-
:host {
|
|
60
|
-
--_vaadin-combo-box-items-container-border-width: var(--lumo-space-xs);
|
|
61
|
-
--_vaadin-combo-box-items-container-border-style: solid;
|
|
62
|
-
--_vaadin-combo-box-items-container-border-color: transparent;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/* Loading state */
|
|
66
|
-
|
|
67
159
|
/* When items are empty, the spinner needs some room */
|
|
68
160
|
:host(:not([closing])) [part~='content'] {
|
|
69
161
|
min-height: calc(2 * var(--lumo-space-s) + var(--lumo-icon-size-s));
|
|
@@ -80,7 +172,9 @@ const comboBoxOverlay = i`
|
|
|
80
172
|
:host([bottom-aligned]) [part~='overlay'] {
|
|
81
173
|
margin-bottom: var(--lumo-space-xs);
|
|
82
174
|
}
|
|
175
|
+
`;
|
|
83
176
|
|
|
177
|
+
const comboBoxLoader = i`
|
|
84
178
|
[part~='loader'] {
|
|
85
179
|
position: absolute;
|
|
86
180
|
z-index: 1;
|
|
@@ -92,8 +186,6 @@ const comboBoxOverlay = i`
|
|
|
92
186
|
margin-inline-end: 0;
|
|
93
187
|
}
|
|
94
188
|
|
|
95
|
-
/* RTL specific styles */
|
|
96
|
-
|
|
97
189
|
:host([dir='rtl']) [part~='loader'] {
|
|
98
190
|
left: auto;
|
|
99
191
|
margin-left: 0;
|
|
@@ -103,127 +195,182 @@ const comboBoxOverlay = i`
|
|
|
103
195
|
}
|
|
104
196
|
`;
|
|
105
197
|
|
|
106
|
-
registerStyles(
|
|
107
|
-
|
|
108
|
-
|
|
198
|
+
registerStyles(
|
|
199
|
+
'vaadin-combo-box-overlay',
|
|
200
|
+
[
|
|
201
|
+
overlay,
|
|
202
|
+
menuOverlayCore,
|
|
203
|
+
comboBoxOverlay,
|
|
204
|
+
loader,
|
|
205
|
+
comboBoxLoader,
|
|
206
|
+
i`
|
|
207
|
+
:host {
|
|
208
|
+
--_vaadin-combo-box-items-container-border-width: var(--lumo-space-xs);
|
|
209
|
+
--_vaadin-combo-box-items-container-border-style: solid;
|
|
210
|
+
}
|
|
211
|
+
`,
|
|
212
|
+
],
|
|
213
|
+
{ moduleId: 'lumo-combo-box-overlay' },
|
|
214
|
+
);
|
|
109
215
|
|
|
110
|
-
const
|
|
216
|
+
const comboBox = i`
|
|
111
217
|
:host {
|
|
112
|
-
display: flex;
|
|
113
|
-
align-items: center;
|
|
114
|
-
box-sizing: border-box;
|
|
115
|
-
font-family: var(--lumo-font-family);
|
|
116
|
-
font-size: var(--lumo-font-size-m);
|
|
117
|
-
line-height: var(--lumo-line-height-xs);
|
|
118
|
-
padding: 0.5em calc(var(--lumo-space-l) + var(--lumo-border-radius-m) / 4) 0.5em
|
|
119
|
-
var(--_lumo-list-box-item-padding-left, calc(var(--lumo-border-radius-m) / 4));
|
|
120
|
-
min-height: var(--lumo-size-m);
|
|
121
218
|
outline: none;
|
|
122
|
-
border-radius: var(--lumo-border-radius-m);
|
|
123
|
-
cursor: var(--lumo-clickable-cursor);
|
|
124
|
-
-webkit-font-smoothing: antialiased;
|
|
125
|
-
-moz-osx-font-smoothing: grayscale;
|
|
126
|
-
-webkit-tap-highlight-color: var(--lumo-primary-color-10pct);
|
|
127
219
|
}
|
|
128
220
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
display: var(--_lumo-item-selected-icon-display, none);
|
|
132
|
-
content: var(--lumo-icons-checkmark);
|
|
133
|
-
font-family: lumo-icons;
|
|
134
|
-
font-size: var(--lumo-icon-size-m);
|
|
135
|
-
line-height: 1;
|
|
136
|
-
font-weight: normal;
|
|
137
|
-
width: 1em;
|
|
138
|
-
height: 1em;
|
|
139
|
-
margin: calc((1 - var(--lumo-line-height-xs)) * var(--lumo-font-size-m) / 2) 0;
|
|
140
|
-
color: var(--lumo-primary-text-color);
|
|
141
|
-
flex: none;
|
|
142
|
-
opacity: 0;
|
|
143
|
-
transition: transform 0.2s cubic-bezier(0.12, 0.32, 0.54, 2), opacity 0.1s;
|
|
221
|
+
[part='toggle-button']::before {
|
|
222
|
+
content: var(--lumo-icons-dropdown);
|
|
144
223
|
}
|
|
224
|
+
`;
|
|
145
225
|
|
|
146
|
-
|
|
147
|
-
opacity: 1;
|
|
148
|
-
}
|
|
226
|
+
registerStyles('vaadin-combo-box', [inputFieldShared, comboBox], { moduleId: 'lumo-combo-box' });
|
|
149
227
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
228
|
+
/**
|
|
229
|
+
* @license
|
|
230
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
231
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
232
|
+
*/
|
|
155
233
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
234
|
+
/**
|
|
235
|
+
* @polymerMixin
|
|
236
|
+
*/
|
|
237
|
+
const ComboBoxItemMixin = (superClass) =>
|
|
238
|
+
class ComboBoxItemMixinClass extends superClass {
|
|
239
|
+
static get properties() {
|
|
240
|
+
return {
|
|
241
|
+
/**
|
|
242
|
+
* The index of the item.
|
|
243
|
+
*/
|
|
244
|
+
index: {
|
|
245
|
+
type: Number,
|
|
246
|
+
},
|
|
159
247
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
248
|
+
/**
|
|
249
|
+
* The item to render.
|
|
250
|
+
*/
|
|
251
|
+
item: {
|
|
252
|
+
type: Object,
|
|
253
|
+
},
|
|
166
254
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
255
|
+
/**
|
|
256
|
+
* The text to render in the item.
|
|
257
|
+
*/
|
|
258
|
+
label: {
|
|
259
|
+
type: String,
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* True when item is selected.
|
|
264
|
+
*/
|
|
265
|
+
selected: {
|
|
266
|
+
type: Boolean,
|
|
267
|
+
value: false,
|
|
268
|
+
reflectToAttribute: true,
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* True when item is focused.
|
|
273
|
+
*/
|
|
274
|
+
focused: {
|
|
275
|
+
type: Boolean,
|
|
276
|
+
value: false,
|
|
277
|
+
reflectToAttribute: true,
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Custom function for rendering the item content.
|
|
282
|
+
*/
|
|
283
|
+
renderer: {
|
|
284
|
+
type: Function,
|
|
285
|
+
},
|
|
286
|
+
};
|
|
171
287
|
}
|
|
172
288
|
|
|
173
|
-
|
|
174
|
-
|
|
289
|
+
static get observers() {
|
|
290
|
+
return ['__rendererOrItemChanged(renderer, index, item.*, selected, focused)', '__updateLabel(label, renderer)'];
|
|
175
291
|
}
|
|
176
|
-
}
|
|
177
292
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
padding-right: var(--_lumo-list-box-item-padding-left, calc(var(--lumo-border-radius-m) / 4));
|
|
182
|
-
}
|
|
293
|
+
static get observedAttributes() {
|
|
294
|
+
return [...super.observedAttributes, 'hidden'];
|
|
295
|
+
}
|
|
183
296
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
297
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
298
|
+
if (name === 'hidden' && newValue !== null) {
|
|
299
|
+
// The element is being hidden (by virtualizer). Mark one of the __rendererOrItemChanged
|
|
300
|
+
// dependencies as undefined to make sure it's called when the element is shown again
|
|
301
|
+
// and assigned properties with possibly identical values as before hiding.
|
|
302
|
+
this.index = undefined;
|
|
303
|
+
} else {
|
|
304
|
+
super.attributeChangedCallback(name, oldValue, newValue);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
191
307
|
|
|
192
|
-
|
|
308
|
+
/** @protected */
|
|
309
|
+
connectedCallback() {
|
|
310
|
+
super.connectedCallback();
|
|
193
311
|
|
|
194
|
-
|
|
195
|
-
:host {
|
|
196
|
-
transition: background-color 100ms;
|
|
197
|
-
overflow: hidden;
|
|
198
|
-
--_lumo-item-selected-icon-display: block;
|
|
199
|
-
}
|
|
312
|
+
this._owner = this.parentNode.owner;
|
|
200
313
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
314
|
+
const hostDir = this._owner.getAttribute('dir');
|
|
315
|
+
if (hostDir) {
|
|
316
|
+
this.setAttribute('dir', hostDir);
|
|
317
|
+
}
|
|
204
318
|
}
|
|
205
|
-
}
|
|
206
|
-
`;
|
|
207
319
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
320
|
+
/**
|
|
321
|
+
* Requests an update for the content of the item.
|
|
322
|
+
* While performing the update, it invokes the renderer passed in the `renderer` property.
|
|
323
|
+
*
|
|
324
|
+
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
325
|
+
*/
|
|
326
|
+
requestContentUpdate() {
|
|
327
|
+
if (!this.renderer) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
211
330
|
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
331
|
+
const model = {
|
|
332
|
+
index: this.index,
|
|
333
|
+
item: this.item,
|
|
334
|
+
focused: this.focused,
|
|
335
|
+
selected: this.selected,
|
|
336
|
+
};
|
|
216
337
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
`;
|
|
338
|
+
this.renderer(this, this._owner, model);
|
|
339
|
+
}
|
|
221
340
|
|
|
222
|
-
|
|
341
|
+
/** @private */
|
|
342
|
+
__rendererOrItemChanged(renderer, index, item) {
|
|
343
|
+
if (item === undefined || index === undefined) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (this._oldRenderer !== renderer) {
|
|
348
|
+
this.innerHTML = '';
|
|
349
|
+
// Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
|
|
350
|
+
// When clearing the rendered content, this part needs to be manually disposed of.
|
|
351
|
+
// Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
|
|
352
|
+
delete this._$litPart$;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (renderer) {
|
|
356
|
+
this._oldRenderer = renderer;
|
|
357
|
+
this.requestContentUpdate();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** @private */
|
|
362
|
+
__updateLabel(label, renderer) {
|
|
363
|
+
if (renderer) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
this.textContent = label;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
223
370
|
|
|
224
371
|
/**
|
|
225
372
|
* @license
|
|
226
|
-
* Copyright (c) 2015 -
|
|
373
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
227
374
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
228
375
|
*/
|
|
229
376
|
|
|
@@ -246,13 +393,15 @@ registerStyles('vaadin-combo-box', [inputFieldShared, comboBox], { moduleId: 'lu
|
|
|
246
393
|
* `selected` | Set when the item is selected
|
|
247
394
|
* `focused` | Set when the item is focused
|
|
248
395
|
*
|
|
249
|
-
* See [Styling Components](https://vaadin.com/docs/latest/styling/
|
|
396
|
+
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
250
397
|
*
|
|
398
|
+
* @customElement
|
|
399
|
+
* @mixes ComboBoxItemMixin
|
|
251
400
|
* @mixes ThemableMixin
|
|
252
401
|
* @mixes DirMixin
|
|
253
402
|
* @private
|
|
254
403
|
*/
|
|
255
|
-
class ComboBoxItem extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
404
|
+
class ComboBoxItem extends ComboBoxItemMixin(ThemableMixin(DirMixin(PolymerElement))) {
|
|
256
405
|
static get template() {
|
|
257
406
|
return html`
|
|
258
407
|
<style>
|
|
@@ -274,218 +423,143 @@ class ComboBoxItem extends ThemableMixin(DirMixin(PolymerElement)) {
|
|
|
274
423
|
static get is() {
|
|
275
424
|
return 'vaadin-combo-box-item';
|
|
276
425
|
}
|
|
426
|
+
}
|
|
277
427
|
|
|
278
|
-
|
|
279
|
-
return {
|
|
280
|
-
/**
|
|
281
|
-
* The index of the item
|
|
282
|
-
*/
|
|
283
|
-
index: Number,
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* The item to render
|
|
287
|
-
* @type {(String|Object)}
|
|
288
|
-
*/
|
|
289
|
-
item: Object,
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* The text label corresponding to the item
|
|
293
|
-
*/
|
|
294
|
-
label: String,
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* True when item is selected
|
|
298
|
-
*/
|
|
299
|
-
selected: {
|
|
300
|
-
type: Boolean,
|
|
301
|
-
value: false,
|
|
302
|
-
reflectToAttribute: true,
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* True when item is focused
|
|
307
|
-
*/
|
|
308
|
-
focused: {
|
|
309
|
-
type: Boolean,
|
|
310
|
-
value: false,
|
|
311
|
-
reflectToAttribute: true,
|
|
312
|
-
},
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Custom function for rendering the content of the `<vaadin-combo-box-item>` propagated from the combo box element.
|
|
316
|
-
*/
|
|
317
|
-
renderer: Function,
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Saved instance of a custom renderer function.
|
|
321
|
-
*/
|
|
322
|
-
_oldRenderer: Function,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
static get observers() {
|
|
327
|
-
return ['__rendererOrItemChanged(renderer, index, item.*, selected, focused)', '__updateLabel(label, renderer)'];
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
connectedCallback() {
|
|
331
|
-
super.connectedCallback();
|
|
428
|
+
defineCustomElement(ComboBoxItem);
|
|
332
429
|
|
|
333
|
-
|
|
430
|
+
/**
|
|
431
|
+
* @license
|
|
432
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
433
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
434
|
+
*/
|
|
334
435
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
436
|
+
/**
|
|
437
|
+
* @polymerMixin
|
|
438
|
+
* @mixes PositionMixin
|
|
439
|
+
*/
|
|
440
|
+
const ComboBoxOverlayMixin = (superClass) =>
|
|
441
|
+
class ComboBoxOverlayMixin extends PositionMixin(superClass) {
|
|
442
|
+
static get observers() {
|
|
443
|
+
return ['_setOverlayWidth(positionTarget, opened)'];
|
|
338
444
|
}
|
|
339
|
-
}
|
|
340
445
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
346
|
-
*/
|
|
347
|
-
requestContentUpdate() {
|
|
348
|
-
if (!this.renderer) {
|
|
349
|
-
return;
|
|
446
|
+
constructor() {
|
|
447
|
+
super();
|
|
448
|
+
|
|
449
|
+
this.requiredVerticalSpace = 200;
|
|
350
450
|
}
|
|
351
451
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
focused: this.focused,
|
|
356
|
-
selected: this.selected,
|
|
357
|
-
};
|
|
452
|
+
/** @protected */
|
|
453
|
+
connectedCallback() {
|
|
454
|
+
super.connectedCallback();
|
|
358
455
|
|
|
359
|
-
|
|
360
|
-
}
|
|
456
|
+
const comboBox = this._comboBox;
|
|
361
457
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
458
|
+
const hostDir = comboBox && comboBox.getAttribute('dir');
|
|
459
|
+
if (hostDir) {
|
|
460
|
+
this.setAttribute('dir', hostDir);
|
|
461
|
+
}
|
|
366
462
|
}
|
|
367
463
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
464
|
+
/**
|
|
465
|
+
* Override method inherited from `Overlay`
|
|
466
|
+
* to not close on position target click.
|
|
467
|
+
*
|
|
468
|
+
* @param {Event} event
|
|
469
|
+
* @return {boolean}
|
|
470
|
+
* @protected
|
|
471
|
+
*/
|
|
472
|
+
_shouldCloseOnOutsideClick(event) {
|
|
473
|
+
const eventPath = event.composedPath();
|
|
474
|
+
return !eventPath.includes(this.positionTarget) && !eventPath.includes(this);
|
|
374
475
|
}
|
|
375
476
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
477
|
+
/** @private */
|
|
478
|
+
_setOverlayWidth(positionTarget, opened) {
|
|
479
|
+
if (positionTarget && opened) {
|
|
480
|
+
const propPrefix = this.localName;
|
|
481
|
+
this.style.setProperty(`--_${propPrefix}-default-width`, `${positionTarget.clientWidth}px`);
|
|
381
482
|
|
|
382
|
-
|
|
383
|
-
__updateLabel(label, renderer) {
|
|
384
|
-
if (renderer) {
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
483
|
+
const customWidth = getComputedStyle(this._comboBox).getPropertyValue(`--${propPrefix}-width`);
|
|
387
484
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}
|
|
485
|
+
if (customWidth === '') {
|
|
486
|
+
this.style.removeProperty(`--${propPrefix}-width`);
|
|
487
|
+
} else {
|
|
488
|
+
this.style.setProperty(`--${propPrefix}-width`, customWidth);
|
|
489
|
+
}
|
|
391
490
|
|
|
392
|
-
|
|
491
|
+
this._updatePosition();
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
};
|
|
393
495
|
|
|
394
496
|
/**
|
|
395
497
|
* @license
|
|
396
|
-
* Copyright (c) 2015 -
|
|
498
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
397
499
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
398
500
|
*/
|
|
399
501
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
width: var(--vaadin-combo-box-overlay-width, var(--_vaadin-combo-box-overlay-default-width, auto));
|
|
405
|
-
}
|
|
502
|
+
const comboBoxOverlayStyles = i`
|
|
503
|
+
#overlay {
|
|
504
|
+
width: var(--vaadin-combo-box-overlay-width, var(--_vaadin-combo-box-overlay-default-width, auto));
|
|
505
|
+
}
|
|
406
506
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
{ moduleId: 'vaadin-combo-box-overlay-styles' },
|
|
414
|
-
);
|
|
507
|
+
[part='content'] {
|
|
508
|
+
display: flex;
|
|
509
|
+
flex-direction: column;
|
|
510
|
+
height: 100%;
|
|
511
|
+
}
|
|
512
|
+
`;
|
|
415
513
|
|
|
416
|
-
|
|
514
|
+
registerStyles('vaadin-combo-box-overlay', [overlayStyles, comboBoxOverlayStyles], {
|
|
515
|
+
moduleId: 'vaadin-combo-box-overlay-styles',
|
|
516
|
+
});
|
|
417
517
|
|
|
418
518
|
/**
|
|
419
519
|
* An element used internally by `<vaadin-combo-box>`. Not intended to be used separately.
|
|
420
520
|
*
|
|
421
|
-
* @
|
|
422
|
-
* @
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
memoizedTemplate.content.querySelector('[part~="overlay"]').removeAttribute('tabindex');
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
return memoizedTemplate;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
static get observers() {
|
|
439
|
-
return ['_setOverlayWidth(positionTarget, opened)'];
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
connectedCallback() {
|
|
443
|
-
super.connectedCallback();
|
|
444
|
-
|
|
445
|
-
const comboBox = this._comboBox;
|
|
446
|
-
|
|
447
|
-
const hostDir = comboBox && comboBox.getAttribute('dir');
|
|
448
|
-
if (hostDir) {
|
|
449
|
-
this.setAttribute('dir', hostDir);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
ready() {
|
|
454
|
-
super.ready();
|
|
455
|
-
const loader = document.createElement('div');
|
|
456
|
-
loader.setAttribute('part', 'loader');
|
|
457
|
-
const content = this.shadowRoot.querySelector('[part~="content"]');
|
|
458
|
-
content.parentNode.insertBefore(loader, content);
|
|
459
|
-
this.requiredVerticalSpace = 200;
|
|
521
|
+
* @customElement
|
|
522
|
+
* @extends HTMLElement
|
|
523
|
+
* @mixes ComboBoxOverlayMixin
|
|
524
|
+
* @mixes DirMixin
|
|
525
|
+
* @mixes OverlayMixin
|
|
526
|
+
* @mixes ThemableMixin
|
|
527
|
+
* @private
|
|
528
|
+
*/
|
|
529
|
+
class ComboBoxOverlay extends ComboBoxOverlayMixin(OverlayMixin(DirMixin(ThemableMixin(PolymerElement)))) {
|
|
530
|
+
static get is() {
|
|
531
|
+
return 'vaadin-combo-box-overlay';
|
|
460
532
|
}
|
|
461
533
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
534
|
+
static get template() {
|
|
535
|
+
return html`
|
|
536
|
+
<div id="backdrop" part="backdrop" hidden></div>
|
|
537
|
+
<div part="overlay" id="overlay">
|
|
538
|
+
<div part="loader"></div>
|
|
539
|
+
<div part="content" id="content"><slot></slot></div>
|
|
540
|
+
</div>
|
|
541
|
+
`;
|
|
467
542
|
}
|
|
543
|
+
}
|
|
468
544
|
|
|
469
|
-
|
|
470
|
-
if (positionTarget && opened) {
|
|
471
|
-
const propPrefix = this.localName;
|
|
472
|
-
this.style.setProperty(`--_${propPrefix}-default-width`, `${positionTarget.clientWidth}px`);
|
|
473
|
-
|
|
474
|
-
const customWidth = getComputedStyle(this._comboBox).getPropertyValue(`--${propPrefix}-width`);
|
|
545
|
+
defineCustomElement(ComboBoxOverlay);
|
|
475
546
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
547
|
+
/**
|
|
548
|
+
* @license
|
|
549
|
+
* Copyright (c) 2023 Vaadin Ltd.
|
|
550
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
551
|
+
*/
|
|
481
552
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
553
|
+
/**
|
|
554
|
+
* Convenience method for reading a value from a path.
|
|
555
|
+
*
|
|
556
|
+
* @param {string} path
|
|
557
|
+
* @param {object} object
|
|
558
|
+
*/
|
|
559
|
+
function get(path, object) {
|
|
560
|
+
return path.split('.').reduce((obj, property) => (obj ? obj[property] : undefined), object);
|
|
485
561
|
}
|
|
486
562
|
|
|
487
|
-
customElements.define(ComboBoxOverlay.is, ComboBoxOverlay);
|
|
488
|
-
|
|
489
563
|
/**
|
|
490
564
|
* @license
|
|
491
565
|
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
|
@@ -496,7 +570,7 @@ customElements.define(ComboBoxOverlay.is, ComboBoxOverlay);
|
|
|
496
570
|
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
|
497
571
|
*/
|
|
498
572
|
|
|
499
|
-
const IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/);
|
|
573
|
+
const IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/u);
|
|
500
574
|
const IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8;
|
|
501
575
|
const DEFAULT_PHYSICAL_COUNT = 3;
|
|
502
576
|
|
|
@@ -986,9 +1060,12 @@ const ironList = {
|
|
|
986
1060
|
this._physicalIndexForKey = {};
|
|
987
1061
|
this._firstVisibleIndexVal = null;
|
|
988
1062
|
this._lastVisibleIndexVal = null;
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1063
|
+
if (!this._physicalItems) {
|
|
1064
|
+
this._physicalItems = [];
|
|
1065
|
+
}
|
|
1066
|
+
if (!this._physicalSizes) {
|
|
1067
|
+
this._physicalSizes = [];
|
|
1068
|
+
}
|
|
992
1069
|
this._physicalStart = 0;
|
|
993
1070
|
if (this._scrollTop > this._scrollOffset) {
|
|
994
1071
|
this._resetScrollPosition(0);
|
|
@@ -1097,16 +1174,21 @@ const ironList = {
|
|
|
1097
1174
|
* @param {boolean=} forceUpdate If true, updates the height no matter what.
|
|
1098
1175
|
*/
|
|
1099
1176
|
_updateScrollerSize(forceUpdate) {
|
|
1100
|
-
|
|
1177
|
+
const estScrollHeight =
|
|
1101
1178
|
this._physicalBottom +
|
|
1102
1179
|
Math.max(this._virtualCount - this._physicalCount - this._virtualStart, 0) * this._physicalAverage;
|
|
1103
1180
|
|
|
1104
|
-
|
|
1105
|
-
|
|
1181
|
+
this._estScrollHeight = estScrollHeight;
|
|
1182
|
+
|
|
1106
1183
|
// Amortize height adjustment, so it won't trigger large repaints too often.
|
|
1107
|
-
if (
|
|
1108
|
-
|
|
1109
|
-
this._scrollHeight
|
|
1184
|
+
if (
|
|
1185
|
+
forceUpdate ||
|
|
1186
|
+
this._scrollHeight === 0 ||
|
|
1187
|
+
this._scrollPosition >= estScrollHeight - this._physicalSize ||
|
|
1188
|
+
Math.abs(estScrollHeight - this._scrollHeight) >= this._viewportHeight
|
|
1189
|
+
) {
|
|
1190
|
+
this.$.items.style.height = `${estScrollHeight}px`;
|
|
1191
|
+
this._scrollHeight = estScrollHeight;
|
|
1110
1192
|
}
|
|
1111
1193
|
},
|
|
1112
1194
|
|
|
@@ -1202,7 +1284,9 @@ const ironList = {
|
|
|
1202
1284
|
},
|
|
1203
1285
|
|
|
1204
1286
|
_debounce(name, cb, asyncModule) {
|
|
1205
|
-
|
|
1287
|
+
if (!this._debouncers) {
|
|
1288
|
+
this._debouncers = {};
|
|
1289
|
+
}
|
|
1206
1290
|
this._debouncers[name] = Debouncer.debounce(this._debouncers[name], asyncModule, cb.bind(this));
|
|
1207
1291
|
enqueueDebouncer(this._debouncers[name]);
|
|
1208
1292
|
},
|
|
@@ -1210,7 +1294,7 @@ const ironList = {
|
|
|
1210
1294
|
|
|
1211
1295
|
/**
|
|
1212
1296
|
* @license
|
|
1213
|
-
* Copyright (c) 2021 -
|
|
1297
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
1214
1298
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
1215
1299
|
*/
|
|
1216
1300
|
|
|
@@ -1340,11 +1424,15 @@ class IronListAdapter {
|
|
|
1340
1424
|
}
|
|
1341
1425
|
|
|
1342
1426
|
update(startIndex = 0, endIndex = this.size - 1) {
|
|
1427
|
+
const updatedElements = [];
|
|
1343
1428
|
this.__getVisibleElements().forEach((el) => {
|
|
1344
1429
|
if (el.__virtualIndex >= startIndex && el.__virtualIndex <= endIndex) {
|
|
1345
1430
|
this.__updateElement(el, el.__virtualIndex, true);
|
|
1431
|
+
updatedElements.push(el);
|
|
1346
1432
|
}
|
|
1347
1433
|
});
|
|
1434
|
+
|
|
1435
|
+
this.__afterElementsUpdated(updatedElements);
|
|
1348
1436
|
}
|
|
1349
1437
|
|
|
1350
1438
|
/**
|
|
@@ -1407,28 +1495,40 @@ class IronListAdapter {
|
|
|
1407
1495
|
this.updateElement(el, index);
|
|
1408
1496
|
el.__lastUpdatedIndex = index;
|
|
1409
1497
|
}
|
|
1498
|
+
}
|
|
1410
1499
|
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1500
|
+
/**
|
|
1501
|
+
* Called synchronously right after elements have been updated.
|
|
1502
|
+
* This is a good place to do any post-update work.
|
|
1503
|
+
*
|
|
1504
|
+
* @param {!Array<!HTMLElement>} updatedElements
|
|
1505
|
+
*/
|
|
1506
|
+
__afterElementsUpdated(updatedElements) {
|
|
1507
|
+
updatedElements.forEach((el) => {
|
|
1508
|
+
const elementHeight = el.offsetHeight;
|
|
1509
|
+
if (elementHeight === 0) {
|
|
1510
|
+
// If the elements have 0 height after update (for example due to lazy rendering),
|
|
1511
|
+
// it results in iron-list requesting to create an unlimited count of elements.
|
|
1512
|
+
// Assign a temporary placeholder sizing to elements that would otherwise end up having
|
|
1513
|
+
// no height.
|
|
1514
|
+
el.style.paddingTop = `${this.__placeholderHeight}px`;
|
|
1515
|
+
|
|
1516
|
+
// Manually schedule the resize handler to make sure the placeholder padding is
|
|
1517
|
+
// cleared in case the resize observer never triggers.
|
|
1518
|
+
this.__placeholderClearDebouncer = Debouncer.debounce(this.__placeholderClearDebouncer, animationFrame, () =>
|
|
1519
|
+
this._resizeHandler(),
|
|
1520
|
+
);
|
|
1521
|
+
} else {
|
|
1522
|
+
// Add element height to the queue
|
|
1523
|
+
this.__elementHeightQueue.push(elementHeight);
|
|
1524
|
+
this.__elementHeightQueue.shift();
|
|
1525
|
+
|
|
1526
|
+
// Calculate new placeholder height based on the average of the defined values in the
|
|
1527
|
+
// element height queue
|
|
1528
|
+
const filteredHeights = this.__elementHeightQueue.filter((h) => h !== undefined);
|
|
1529
|
+
this.__placeholderHeight = Math.round(filteredHeights.reduce((a, b) => a + b, 0) / filteredHeights.length);
|
|
1530
|
+
}
|
|
1531
|
+
});
|
|
1432
1532
|
}
|
|
1433
1533
|
|
|
1434
1534
|
__getIndexScrollOffset(index) {
|
|
@@ -1453,42 +1553,35 @@ class IronListAdapter {
|
|
|
1453
1553
|
this._debouncers._increasePoolIfNeeded.cancel();
|
|
1454
1554
|
}
|
|
1455
1555
|
|
|
1456
|
-
// Prevent element update while the scroll position is being restored
|
|
1457
|
-
this.__preventElementUpdates = true;
|
|
1458
|
-
|
|
1459
|
-
// Record the scroll position before changing the size
|
|
1460
|
-
let fvi; // First visible index
|
|
1461
|
-
let fviOffsetBefore; // Scroll offset of the first visible index
|
|
1462
|
-
if (size > 0) {
|
|
1463
|
-
fvi = this.adjustedFirstVisibleIndex;
|
|
1464
|
-
fviOffsetBefore = this.__getIndexScrollOffset(fvi);
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
1556
|
// Change the size
|
|
1468
1557
|
this.__size = size;
|
|
1469
1558
|
|
|
1470
|
-
this.
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1559
|
+
if (!this._physicalItems) {
|
|
1560
|
+
// Not initialized yet
|
|
1561
|
+
this._itemsChanged({
|
|
1562
|
+
path: 'items',
|
|
1563
|
+
});
|
|
1564
|
+
this.__preventElementUpdates = true;
|
|
1565
|
+
flush();
|
|
1566
|
+
this.__preventElementUpdates = false;
|
|
1567
|
+
} else {
|
|
1568
|
+
// Already initialized, just update _virtualCount
|
|
1569
|
+
this._virtualCount = this.items.length;
|
|
1570
|
+
}
|
|
1479
1571
|
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1572
|
+
// When reducing size while invisible, iron-list does not update items, so
|
|
1573
|
+
// their hidden state is not updated and their __lastUpdatedIndex is not
|
|
1574
|
+
// reset. In that case force an update here.
|
|
1575
|
+
if (!this._isVisible) {
|
|
1576
|
+
this._assignModels();
|
|
1484
1577
|
}
|
|
1485
1578
|
|
|
1486
1579
|
if (!this.elementsContainer.children.length) {
|
|
1487
1580
|
requestAnimationFrame(() => this._resizeHandler());
|
|
1488
1581
|
}
|
|
1489
1582
|
|
|
1490
|
-
|
|
1491
|
-
//
|
|
1583
|
+
// Schedule and flush a resize handler. This will cause a
|
|
1584
|
+
// re-render for the elements.
|
|
1492
1585
|
this._resizeHandler();
|
|
1493
1586
|
flush();
|
|
1494
1587
|
}
|
|
@@ -1553,16 +1646,20 @@ class IronListAdapter {
|
|
|
1553
1646
|
|
|
1554
1647
|
/** @private */
|
|
1555
1648
|
_assignModels(itemSet) {
|
|
1649
|
+
const updatedElements = [];
|
|
1556
1650
|
this._iterateItems((pidx, vidx) => {
|
|
1557
1651
|
const el = this._physicalItems[pidx];
|
|
1558
1652
|
el.hidden = vidx >= this.size;
|
|
1559
1653
|
if (!el.hidden) {
|
|
1560
1654
|
el.__virtualIndex = vidx + (this._vidxOffset || 0);
|
|
1561
1655
|
this.__updateElement(el, el.__virtualIndex);
|
|
1656
|
+
updatedElements.push(el);
|
|
1562
1657
|
} else {
|
|
1563
1658
|
delete el.__lastUpdatedIndex;
|
|
1564
1659
|
}
|
|
1565
1660
|
}, itemSet);
|
|
1661
|
+
|
|
1662
|
+
this.__afterElementsUpdated(updatedElements);
|
|
1566
1663
|
}
|
|
1567
1664
|
|
|
1568
1665
|
/** @private */
|
|
@@ -1691,7 +1788,9 @@ class IronListAdapter {
|
|
|
1691
1788
|
deltaY *= this._scrollPageHeight;
|
|
1692
1789
|
}
|
|
1693
1790
|
|
|
1694
|
-
|
|
1791
|
+
if (!this._deltaYAcc) {
|
|
1792
|
+
this._deltaYAcc = 0;
|
|
1793
|
+
}
|
|
1695
1794
|
|
|
1696
1795
|
if (this._wheelAnimationFrame) {
|
|
1697
1796
|
// Accumulate wheel delta while a frame is being processed
|
|
@@ -1764,6 +1863,29 @@ class IronListAdapter {
|
|
|
1764
1863
|
);
|
|
1765
1864
|
}
|
|
1766
1865
|
|
|
1866
|
+
/**
|
|
1867
|
+
* Increases the pool size.
|
|
1868
|
+
* @override
|
|
1869
|
+
*/
|
|
1870
|
+
_increasePoolIfNeeded(count) {
|
|
1871
|
+
if (this._physicalCount > 2 && count) {
|
|
1872
|
+
// The iron-list logic has already created some physical items and
|
|
1873
|
+
// has decided to create more. Since each item creation round is
|
|
1874
|
+
// expensive, let's try to create the remaining items in one go.
|
|
1875
|
+
|
|
1876
|
+
// Calculate the total item count that would be needed to fill the viewport
|
|
1877
|
+
// plus the buffer assuming rest of the items to be of the average size
|
|
1878
|
+
// of the items already created.
|
|
1879
|
+
const totalItemCount = Math.ceil(this._optPhysicalSize / this._physicalAverage);
|
|
1880
|
+
const missingItemCount = totalItemCount - this._physicalCount;
|
|
1881
|
+
// Create the remaining items in one go. Use a maximum of 100 items
|
|
1882
|
+
// as a safety measure.
|
|
1883
|
+
super._increasePoolIfNeeded(Math.max(count, Math.min(100, missingItemCount)));
|
|
1884
|
+
} else {
|
|
1885
|
+
super._increasePoolIfNeeded(count);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1767
1889
|
/**
|
|
1768
1890
|
* @returns {Number|undefined} - The browser's default font-size in pixels
|
|
1769
1891
|
* @private
|
|
@@ -1893,6 +2015,24 @@ class Virtualizer {
|
|
|
1893
2015
|
this.__adapter = new IronListAdapter(config);
|
|
1894
2016
|
}
|
|
1895
2017
|
|
|
2018
|
+
/**
|
|
2019
|
+
* Gets the index of the first visible item in the viewport.
|
|
2020
|
+
*
|
|
2021
|
+
* @return {number}
|
|
2022
|
+
*/
|
|
2023
|
+
get firstVisibleIndex() {
|
|
2024
|
+
return this.__adapter.adjustedFirstVisibleIndex;
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
/**
|
|
2028
|
+
* Gets the index of the last visible item in the viewport.
|
|
2029
|
+
*
|
|
2030
|
+
* @return {number}
|
|
2031
|
+
*/
|
|
2032
|
+
get lastVisibleIndex() {
|
|
2033
|
+
return this.__adapter.adjustedLastVisibleIndex;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
1896
2036
|
/**
|
|
1897
2037
|
* The size of the virtualizer
|
|
1898
2038
|
* @return {number | undefined} The size of the virtualizer
|
|
@@ -1940,29 +2080,11 @@ class Virtualizer {
|
|
|
1940
2080
|
flush() {
|
|
1941
2081
|
this.__adapter.flush();
|
|
1942
2082
|
}
|
|
1943
|
-
|
|
1944
|
-
/**
|
|
1945
|
-
* Gets the index of the first visible item in the viewport.
|
|
1946
|
-
*
|
|
1947
|
-
* @return {number}
|
|
1948
|
-
*/
|
|
1949
|
-
get firstVisibleIndex() {
|
|
1950
|
-
return this.__adapter.adjustedFirstVisibleIndex;
|
|
1951
|
-
}
|
|
1952
|
-
|
|
1953
|
-
/**
|
|
1954
|
-
* Gets the index of the last visible item in the viewport.
|
|
1955
|
-
*
|
|
1956
|
-
* @return {number}
|
|
1957
|
-
*/
|
|
1958
|
-
get lastVisibleIndex() {
|
|
1959
|
-
return this.__adapter.adjustedLastVisibleIndex;
|
|
1960
|
-
}
|
|
1961
2083
|
}
|
|
1962
2084
|
|
|
1963
2085
|
/**
|
|
1964
2086
|
* @license
|
|
1965
|
-
* Copyright (c) 2015 -
|
|
2087
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
1966
2088
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
1967
2089
|
*/
|
|
1968
2090
|
|
|
@@ -1979,394 +2101,454 @@ const ComboBoxPlaceholder = class ComboBoxPlaceholder {
|
|
|
1979
2101
|
|
|
1980
2102
|
/**
|
|
1981
2103
|
* @license
|
|
1982
|
-
* Copyright (c) 2015 -
|
|
2104
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
1983
2105
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
1984
2106
|
*/
|
|
1985
2107
|
|
|
1986
2108
|
/**
|
|
1987
|
-
*
|
|
1988
|
-
*
|
|
1989
|
-
* @extends HTMLElement
|
|
1990
|
-
* @private
|
|
2109
|
+
* @polymerMixin
|
|
1991
2110
|
*/
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
:
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
/* Fixes item background from getting on top of scrollbars on Safari */
|
|
2006
|
-
transform: translate3d(0, 0, 0);
|
|
2007
|
-
|
|
2008
|
-
/* Enable momentum scrolling on iOS */
|
|
2009
|
-
-webkit-overflow-scrolling: touch;
|
|
2010
|
-
|
|
2011
|
-
/* Fixes scrollbar disappearing when 'Show scroll bars: Always' enabled in Safari */
|
|
2012
|
-
box-shadow: 0 0 0 white;
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
#selector {
|
|
2016
|
-
border-width: var(--_vaadin-combo-box-items-container-border-width);
|
|
2017
|
-
border-style: var(--_vaadin-combo-box-items-container-border-style);
|
|
2018
|
-
border-color: var(--_vaadin-combo-box-items-container-border-color);
|
|
2019
|
-
position: relative;
|
|
2020
|
-
}
|
|
2021
|
-
</style>
|
|
2022
|
-
<div id="selector">
|
|
2023
|
-
<slot></slot>
|
|
2024
|
-
</div>
|
|
2025
|
-
`;
|
|
2026
|
-
}
|
|
2111
|
+
const ComboBoxScrollerMixin = (superClass) =>
|
|
2112
|
+
class ComboBoxScrollerMixin extends superClass {
|
|
2113
|
+
static get properties() {
|
|
2114
|
+
return {
|
|
2115
|
+
/**
|
|
2116
|
+
* A full set of items to filter the visible options from.
|
|
2117
|
+
* Set to an empty array when combo-box is not opened.
|
|
2118
|
+
*/
|
|
2119
|
+
items: {
|
|
2120
|
+
type: Array,
|
|
2121
|
+
observer: '__itemsChanged',
|
|
2122
|
+
},
|
|
2027
2123
|
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
observer: '__itemsChanged',
|
|
2037
|
-
},
|
|
2124
|
+
/**
|
|
2125
|
+
* Index of an item that has focus outline and is scrolled into view.
|
|
2126
|
+
* The actual focus still remains in the input field.
|
|
2127
|
+
*/
|
|
2128
|
+
focusedIndex: {
|
|
2129
|
+
type: Number,
|
|
2130
|
+
observer: '__focusedIndexChanged',
|
|
2131
|
+
},
|
|
2038
2132
|
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
},
|
|
2133
|
+
/**
|
|
2134
|
+
* Set to true while combo-box fetches new page from the data provider.
|
|
2135
|
+
*/
|
|
2136
|
+
loading: {
|
|
2137
|
+
type: Boolean,
|
|
2138
|
+
observer: '__loadingChanged',
|
|
2139
|
+
},
|
|
2047
2140
|
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2141
|
+
/**
|
|
2142
|
+
* Whether the combo-box is currently opened or not. If set to false,
|
|
2143
|
+
* calling `scrollIntoView` does not have any effect.
|
|
2144
|
+
*/
|
|
2145
|
+
opened: {
|
|
2146
|
+
type: Boolean,
|
|
2147
|
+
observer: '__openedChanged',
|
|
2148
|
+
},
|
|
2055
2149
|
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
},
|
|
2150
|
+
/**
|
|
2151
|
+
* The selected item from the `items` array.
|
|
2152
|
+
*/
|
|
2153
|
+
selectedItem: {
|
|
2154
|
+
type: Object,
|
|
2155
|
+
observer: '__selectedItemChanged',
|
|
2156
|
+
},
|
|
2064
2157
|
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
},
|
|
2158
|
+
/**
|
|
2159
|
+
* Path for the id of the item, used to detect whether the item is selected.
|
|
2160
|
+
*/
|
|
2161
|
+
itemIdPath: {
|
|
2162
|
+
type: String,
|
|
2163
|
+
},
|
|
2072
2164
|
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2165
|
+
/**
|
|
2166
|
+
* Reference to the owner (combo-box owner), used by the item elements.
|
|
2167
|
+
*/
|
|
2168
|
+
owner: {
|
|
2169
|
+
type: Object,
|
|
2170
|
+
},
|
|
2079
2171
|
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2172
|
+
/**
|
|
2173
|
+
* Function used to set a label for every combo-box item.
|
|
2174
|
+
*/
|
|
2175
|
+
getItemLabel: {
|
|
2176
|
+
type: Object,
|
|
2177
|
+
},
|
|
2086
2178
|
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2179
|
+
/**
|
|
2180
|
+
* Function used to render the content of every combo-box item.
|
|
2181
|
+
*/
|
|
2182
|
+
renderer: {
|
|
2183
|
+
type: Object,
|
|
2184
|
+
observer: '__rendererChanged',
|
|
2185
|
+
},
|
|
2093
2186
|
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
}
|
|
2187
|
+
/**
|
|
2188
|
+
* Used to propagate the `theme` attribute from the host element.
|
|
2189
|
+
*/
|
|
2190
|
+
theme: {
|
|
2191
|
+
type: String,
|
|
2192
|
+
},
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2101
2195
|
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
type: String,
|
|
2107
|
-
},
|
|
2108
|
-
};
|
|
2109
|
-
}
|
|
2196
|
+
constructor() {
|
|
2197
|
+
super();
|
|
2198
|
+
this.__boundOnItemClick = this.__onItemClick.bind(this);
|
|
2199
|
+
}
|
|
2110
2200
|
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2201
|
+
/** @private */
|
|
2202
|
+
get _viewportTotalPaddingBottom() {
|
|
2203
|
+
if (this._cachedViewportTotalPaddingBottom === undefined) {
|
|
2204
|
+
const itemsStyle = window.getComputedStyle(this.$.selector);
|
|
2205
|
+
this._cachedViewportTotalPaddingBottom = [itemsStyle.paddingBottom, itemsStyle.borderBottomWidth]
|
|
2206
|
+
.map((v) => {
|
|
2207
|
+
return parseInt(v, 10);
|
|
2208
|
+
})
|
|
2209
|
+
.reduce((sum, v) => {
|
|
2210
|
+
return sum + v;
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2115
2213
|
|
|
2116
|
-
|
|
2117
|
-
if (opened) {
|
|
2118
|
-
this.requestContentUpdate();
|
|
2214
|
+
return this._cachedViewportTotalPaddingBottom;
|
|
2119
2215
|
}
|
|
2120
|
-
}
|
|
2121
|
-
|
|
2122
|
-
/** @protected */
|
|
2123
|
-
ready() {
|
|
2124
|
-
super.ready();
|
|
2125
2216
|
|
|
2126
|
-
|
|
2127
|
-
|
|
2217
|
+
/** @protected */
|
|
2218
|
+
ready() {
|
|
2219
|
+
super.ready();
|
|
2128
2220
|
|
|
2129
|
-
|
|
2130
|
-
this.__hostTagName = this.constructor.is.replace('-scroller', '');
|
|
2221
|
+
this.setAttribute('role', 'listbox');
|
|
2131
2222
|
|
|
2132
|
-
|
|
2223
|
+
// Ensure every instance has unique ID
|
|
2224
|
+
this.id = `${this.localName}-${generateUniqueId()}`;
|
|
2133
2225
|
|
|
2134
|
-
|
|
2226
|
+
// Allow extensions to customize tag name for the items
|
|
2227
|
+
this.__hostTagName = this.constructor.is.replace('-scroller', '');
|
|
2135
2228
|
|
|
2136
|
-
|
|
2229
|
+
this.addEventListener('click', (e) => e.stopPropagation());
|
|
2137
2230
|
|
|
2138
|
-
|
|
2139
|
-
createElements: this.__createElements.bind(this),
|
|
2140
|
-
updateElement: this.__updateElement.bind(this),
|
|
2141
|
-
elementsContainer: this,
|
|
2142
|
-
scrollTarget: this,
|
|
2143
|
-
scrollContainer: this.$.selector,
|
|
2144
|
-
});
|
|
2145
|
-
}
|
|
2231
|
+
this.__patchWheelOverScrolling();
|
|
2146
2232
|
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2233
|
+
this.__virtualizer = new Virtualizer({
|
|
2234
|
+
createElements: this.__createElements.bind(this),
|
|
2235
|
+
updateElement: this._updateElement.bind(this),
|
|
2236
|
+
elementsContainer: this,
|
|
2237
|
+
scrollTarget: this,
|
|
2238
|
+
scrollContainer: this.$.selector,
|
|
2239
|
+
});
|
|
2150
2240
|
}
|
|
2151
|
-
}
|
|
2152
2241
|
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2242
|
+
/**
|
|
2243
|
+
* Requests an update for the virtualizer to re-render items.
|
|
2244
|
+
*/
|
|
2245
|
+
requestContentUpdate() {
|
|
2246
|
+
if (this.__virtualizer) {
|
|
2247
|
+
this.__virtualizer.update();
|
|
2248
|
+
}
|
|
2156
2249
|
}
|
|
2157
2250
|
|
|
2158
|
-
|
|
2251
|
+
/**
|
|
2252
|
+
* Scrolls an item at given index into view and adjusts `scrollTop`
|
|
2253
|
+
* so that the element gets fully visible on Arrow Down key press.
|
|
2254
|
+
* @param {number} index
|
|
2255
|
+
*/
|
|
2256
|
+
scrollIntoView(index) {
|
|
2257
|
+
if (!(this.opened && index >= 0)) {
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
const visibleItemsCount = this._visibleItemsCount();
|
|
2159
2262
|
|
|
2160
|
-
|
|
2263
|
+
let targetIndex = index;
|
|
2161
2264
|
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2265
|
+
if (index > this.__virtualizer.lastVisibleIndex - 1) {
|
|
2266
|
+
// Index is below the bottom, scrolling down. Make the item appear at the bottom.
|
|
2267
|
+
// First scroll to target (will be at the top of the scroller) to make sure it's rendered.
|
|
2268
|
+
this.__virtualizer.scrollToIndex(index);
|
|
2269
|
+
// Then calculate the index for the following scroll (to get the target to bottom of the scroller).
|
|
2270
|
+
targetIndex = index - visibleItemsCount + 1;
|
|
2271
|
+
} else if (index > this.__virtualizer.firstVisibleIndex) {
|
|
2272
|
+
// The item is already visible, scrolling is unnecessary per se. But we need to trigger iron-list to set
|
|
2273
|
+
// the correct scrollTop on the scrollTarget. Scrolling to firstVisibleIndex.
|
|
2274
|
+
targetIndex = this.__virtualizer.firstVisibleIndex;
|
|
2275
|
+
}
|
|
2276
|
+
this.__virtualizer.scrollToIndex(Math.max(0, targetIndex));
|
|
2174
2277
|
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2278
|
+
// Sometimes the item is partly below the bottom edge, detect and adjust.
|
|
2279
|
+
const lastPhysicalItem = [...this.children].find(
|
|
2280
|
+
(el) => !el.hidden && el.index === this.__virtualizer.lastVisibleIndex,
|
|
2281
|
+
);
|
|
2282
|
+
if (!lastPhysicalItem || index !== lastPhysicalItem.index) {
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
const lastPhysicalItemRect = lastPhysicalItem.getBoundingClientRect();
|
|
2286
|
+
const scrollerRect = this.getBoundingClientRect();
|
|
2287
|
+
const scrollTopAdjust = lastPhysicalItemRect.bottom - scrollerRect.bottom + this._viewportTotalPaddingBottom;
|
|
2288
|
+
if (scrollTopAdjust > 0) {
|
|
2289
|
+
this.scrollTop += scrollTopAdjust;
|
|
2290
|
+
}
|
|
2181
2291
|
}
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2292
|
+
|
|
2293
|
+
/**
|
|
2294
|
+
* @param {string | object} item
|
|
2295
|
+
* @param {string | object} selectedItem
|
|
2296
|
+
* @param {string} itemIdPath
|
|
2297
|
+
* @protected
|
|
2298
|
+
*/
|
|
2299
|
+
_isItemSelected(item, selectedItem, itemIdPath) {
|
|
2300
|
+
if (item instanceof ComboBoxPlaceholder) {
|
|
2301
|
+
return false;
|
|
2302
|
+
} else if (itemIdPath && item !== undefined && selectedItem !== undefined) {
|
|
2303
|
+
return get(itemIdPath, item) === get(itemIdPath, selectedItem);
|
|
2304
|
+
}
|
|
2305
|
+
return item === selectedItem;
|
|
2187
2306
|
}
|
|
2188
|
-
}
|
|
2189
2307
|
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2308
|
+
/** @private */
|
|
2309
|
+
__itemsChanged(items) {
|
|
2310
|
+
if (this.__virtualizer && items) {
|
|
2311
|
+
this.__virtualizer.size = items.length;
|
|
2312
|
+
this.__virtualizer.flush();
|
|
2313
|
+
this.requestContentUpdate();
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2194
2316
|
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2317
|
+
/** @private */
|
|
2318
|
+
__loadingChanged() {
|
|
2319
|
+
this.requestContentUpdate();
|
|
2320
|
+
}
|
|
2199
2321
|
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
return this.get(itemIdPath, item) === this.get(itemIdPath, selectedItem);
|
|
2322
|
+
/** @private */
|
|
2323
|
+
__openedChanged(opened) {
|
|
2324
|
+
if (opened) {
|
|
2325
|
+
this.requestContentUpdate();
|
|
2326
|
+
}
|
|
2206
2327
|
}
|
|
2207
|
-
return item === selectedItem;
|
|
2208
|
-
}
|
|
2209
2328
|
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
if (this.__virtualizer && items) {
|
|
2213
|
-
this.__virtualizer.size = items.length;
|
|
2214
|
-
this.__virtualizer.flush();
|
|
2329
|
+
/** @private */
|
|
2330
|
+
__selectedItemChanged() {
|
|
2215
2331
|
this.requestContentUpdate();
|
|
2216
2332
|
}
|
|
2217
|
-
}
|
|
2218
|
-
|
|
2219
|
-
/** @private */
|
|
2220
|
-
__loadingChanged() {
|
|
2221
|
-
this.requestContentUpdate();
|
|
2222
|
-
}
|
|
2223
2333
|
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2334
|
+
/** @private */
|
|
2335
|
+
__focusedIndexChanged(index, oldIndex) {
|
|
2336
|
+
if (index !== oldIndex) {
|
|
2337
|
+
this.requestContentUpdate();
|
|
2338
|
+
}
|
|
2228
2339
|
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2340
|
+
// Do not jump back to the previously focused item while loading
|
|
2341
|
+
// when requesting next page from the data provider on scroll.
|
|
2342
|
+
if (index >= 0 && !this.loading) {
|
|
2343
|
+
this.scrollIntoView(index);
|
|
2344
|
+
}
|
|
2233
2345
|
}
|
|
2234
2346
|
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2347
|
+
/** @private */
|
|
2348
|
+
__rendererChanged(renderer, oldRenderer) {
|
|
2349
|
+
if (renderer || oldRenderer) {
|
|
2350
|
+
this.requestContentUpdate();
|
|
2351
|
+
}
|
|
2239
2352
|
}
|
|
2240
|
-
}
|
|
2241
2353
|
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2354
|
+
/** @private */
|
|
2355
|
+
__createElements(count) {
|
|
2356
|
+
return [...Array(count)].map(() => {
|
|
2357
|
+
const item = document.createElement(`${this.__hostTagName}-item`);
|
|
2358
|
+
item.addEventListener('click', this.__boundOnItemClick);
|
|
2359
|
+
// Negative tabindex prevents the item content from being focused.
|
|
2360
|
+
item.tabIndex = '-1';
|
|
2361
|
+
item.style.width = '100%';
|
|
2362
|
+
return item;
|
|
2363
|
+
});
|
|
2246
2364
|
}
|
|
2247
|
-
}
|
|
2248
2365
|
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
item
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2366
|
+
/**
|
|
2367
|
+
* @param {HTMLElement} el
|
|
2368
|
+
* @param {number} index
|
|
2369
|
+
* @protected
|
|
2370
|
+
*/
|
|
2371
|
+
_updateElement(el, index) {
|
|
2372
|
+
const item = this.items[index];
|
|
2373
|
+
const focusedIndex = this.focusedIndex;
|
|
2374
|
+
const selected = this._isItemSelected(item, this.selectedItem, this.itemIdPath);
|
|
2375
|
+
|
|
2376
|
+
el.setProperties({
|
|
2377
|
+
item,
|
|
2378
|
+
index,
|
|
2379
|
+
label: this.getItemLabel(item),
|
|
2380
|
+
selected,
|
|
2381
|
+
renderer: this.renderer,
|
|
2382
|
+
focused: !this.loading && focusedIndex === index,
|
|
2383
|
+
});
|
|
2260
2384
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
el.setProperties({
|
|
2268
|
-
item,
|
|
2269
|
-
index,
|
|
2270
|
-
label: this.getItemLabel(item),
|
|
2271
|
-
selected,
|
|
2272
|
-
renderer: this.renderer,
|
|
2273
|
-
focused: this.__isItemFocused(focusedIndex, index),
|
|
2274
|
-
});
|
|
2385
|
+
el.id = `${this.__hostTagName}-item-${index}`;
|
|
2386
|
+
el.setAttribute('role', index !== undefined ? 'option' : false);
|
|
2387
|
+
el.setAttribute('aria-selected', selected.toString());
|
|
2388
|
+
el.setAttribute('aria-posinset', index + 1);
|
|
2389
|
+
el.setAttribute('aria-setsize', this.items.length);
|
|
2275
2390
|
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2391
|
+
if (this.theme) {
|
|
2392
|
+
el.setAttribute('theme', this.theme);
|
|
2393
|
+
} else {
|
|
2394
|
+
el.removeAttribute('theme');
|
|
2395
|
+
}
|
|
2281
2396
|
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
el.removeAttribute('theme');
|
|
2397
|
+
if (item instanceof ComboBoxPlaceholder) {
|
|
2398
|
+
this.__requestItemByIndex(index);
|
|
2399
|
+
}
|
|
2286
2400
|
}
|
|
2287
2401
|
|
|
2288
|
-
|
|
2289
|
-
|
|
2402
|
+
/** @private */
|
|
2403
|
+
__onItemClick(e) {
|
|
2404
|
+
this.dispatchEvent(new CustomEvent('selection-changed', { detail: { item: e.currentTarget.item } }));
|
|
2290
2405
|
}
|
|
2291
|
-
}
|
|
2292
2406
|
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2407
|
+
/**
|
|
2408
|
+
* We want to prevent the kinetic scrolling energy from being transferred from the overlay contents over to the parent.
|
|
2409
|
+
* Further improvement ideas: after the contents have been scrolled to the top or bottom and scrolling has stopped, it could allow
|
|
2410
|
+
* scrolling the parent similarly to touch scrolling.
|
|
2411
|
+
* @private
|
|
2412
|
+
*/
|
|
2413
|
+
__patchWheelOverScrolling() {
|
|
2414
|
+
this.$.selector.addEventListener('wheel', (e) => {
|
|
2415
|
+
const scrolledToTop = this.scrollTop === 0;
|
|
2416
|
+
const scrolledToBottom = this.scrollHeight - this.scrollTop - this.clientHeight <= 1;
|
|
2417
|
+
if (scrolledToTop && e.deltaY < 0) {
|
|
2418
|
+
e.preventDefault();
|
|
2419
|
+
} else if (scrolledToBottom && e.deltaY > 0) {
|
|
2420
|
+
e.preventDefault();
|
|
2421
|
+
}
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2297
2424
|
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2425
|
+
/**
|
|
2426
|
+
* Dispatches an `index-requested` event for the given index to notify
|
|
2427
|
+
* the data provider that it should start loading the page containing the requested index.
|
|
2428
|
+
*
|
|
2429
|
+
* The event is dispatched asynchronously to prevent an immediate page request and therefore
|
|
2430
|
+
* a possible infinite recursion in case the data provider implements page request cancelation logic
|
|
2431
|
+
* by invoking data provider page callbacks with an empty array.
|
|
2432
|
+
* The infinite recursion may occur otherwise since invoking a data provider page callback with an empty array
|
|
2433
|
+
* triggers a synchronous scroller update and, if the callback corresponds to the currently visible page,
|
|
2434
|
+
* the scroller will synchronously request the page again which may lead to looping in the end.
|
|
2435
|
+
* That was the case for the Flow counterpart:
|
|
2436
|
+
* https://github.com/vaadin/flow-components/issues/3553#issuecomment-1239344828
|
|
2437
|
+
* @private
|
|
2438
|
+
*/
|
|
2439
|
+
__requestItemByIndex(index) {
|
|
2440
|
+
requestAnimationFrame(() => {
|
|
2441
|
+
this.dispatchEvent(
|
|
2442
|
+
new CustomEvent('index-requested', {
|
|
2443
|
+
detail: {
|
|
2444
|
+
index,
|
|
2445
|
+
currentScrollerPos: this._oldScrollerPosition,
|
|
2446
|
+
},
|
|
2447
|
+
}),
|
|
2448
|
+
);
|
|
2449
|
+
});
|
|
2450
|
+
}
|
|
2314
2451
|
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
this.
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
})
|
|
2322
|
-
.reduce((sum, v) => {
|
|
2323
|
-
return sum + v;
|
|
2324
|
-
});
|
|
2452
|
+
/** @private */
|
|
2453
|
+
_visibleItemsCount() {
|
|
2454
|
+
// Ensure items are positioned
|
|
2455
|
+
this.__virtualizer.scrollToIndex(this.__virtualizer.firstVisibleIndex);
|
|
2456
|
+
const hasItems = this.__virtualizer.size > 0;
|
|
2457
|
+
return hasItems ? this.__virtualizer.lastVisibleIndex - this.__virtualizer.firstVisibleIndex + 1 : 0;
|
|
2325
2458
|
}
|
|
2459
|
+
};
|
|
2326
2460
|
|
|
2327
|
-
|
|
2328
|
-
|
|
2461
|
+
/**
|
|
2462
|
+
* @license
|
|
2463
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
2464
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2465
|
+
*/
|
|
2329
2466
|
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
* https://github.com/vaadin/flow-components/issues/3553#issuecomment-1239344828
|
|
2342
|
-
*/
|
|
2343
|
-
__requestItemByIndex(index) {
|
|
2344
|
-
requestAnimationFrame(() => {
|
|
2345
|
-
this.dispatchEvent(
|
|
2346
|
-
new CustomEvent('index-requested', {
|
|
2347
|
-
detail: {
|
|
2348
|
-
index,
|
|
2349
|
-
currentScrollerPos: this._oldScrollerPosition,
|
|
2350
|
-
},
|
|
2351
|
-
}),
|
|
2352
|
-
);
|
|
2353
|
-
});
|
|
2467
|
+
/**
|
|
2468
|
+
* An element used internally by `<vaadin-combo-box>`. Not intended to be used separately.
|
|
2469
|
+
*
|
|
2470
|
+
* @customElement
|
|
2471
|
+
* @extends HTMLElement
|
|
2472
|
+
* @mixes ComboBoxScrollerMixin
|
|
2473
|
+
* @private
|
|
2474
|
+
*/
|
|
2475
|
+
class ComboBoxScroller extends ComboBoxScrollerMixin(PolymerElement) {
|
|
2476
|
+
static get is() {
|
|
2477
|
+
return 'vaadin-combo-box-scroller';
|
|
2354
2478
|
}
|
|
2355
2479
|
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2480
|
+
static get template() {
|
|
2481
|
+
return html`
|
|
2482
|
+
<style>
|
|
2483
|
+
:host {
|
|
2484
|
+
display: block;
|
|
2485
|
+
min-height: 1px;
|
|
2486
|
+
overflow: auto;
|
|
2487
|
+
|
|
2488
|
+
/* Fixes item background from getting on top of scrollbars on Safari */
|
|
2489
|
+
transform: translate3d(0, 0, 0);
|
|
2490
|
+
|
|
2491
|
+
/* Enable momentum scrolling on iOS */
|
|
2492
|
+
-webkit-overflow-scrolling: touch;
|
|
2493
|
+
|
|
2494
|
+
/* Fixes scrollbar disappearing when 'Show scroll bars: Always' enabled in Safari */
|
|
2495
|
+
box-shadow: 0 0 0 white;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
#selector {
|
|
2499
|
+
border-width: var(--_vaadin-combo-box-items-container-border-width);
|
|
2500
|
+
border-style: var(--_vaadin-combo-box-items-container-border-style);
|
|
2501
|
+
border-color: var(--_vaadin-combo-box-items-container-border-color, transparent);
|
|
2502
|
+
position: relative;
|
|
2503
|
+
}
|
|
2504
|
+
</style>
|
|
2505
|
+
<div id="selector">
|
|
2506
|
+
<slot></slot>
|
|
2507
|
+
</div>
|
|
2508
|
+
`;
|
|
2362
2509
|
}
|
|
2363
2510
|
}
|
|
2364
2511
|
|
|
2365
|
-
|
|
2512
|
+
defineCustomElement(ComboBoxScroller);
|
|
2513
|
+
|
|
2514
|
+
/**
|
|
2515
|
+
* @license
|
|
2516
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
2517
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2518
|
+
*/
|
|
2519
|
+
|
|
2520
|
+
/**
|
|
2521
|
+
* A mixin to provide `pattern` property.
|
|
2522
|
+
*
|
|
2523
|
+
* @polymerMixin
|
|
2524
|
+
* @mixes InputConstraintsMixin
|
|
2525
|
+
*/
|
|
2526
|
+
const PatternMixin = (superclass) =>
|
|
2527
|
+
class PatternMixinClass extends InputConstraintsMixin(superclass) {
|
|
2528
|
+
static get properties() {
|
|
2529
|
+
return {
|
|
2530
|
+
/**
|
|
2531
|
+
* A regular expression that the value is checked against.
|
|
2532
|
+
* The pattern must match the entire value, not just some subset.
|
|
2533
|
+
*/
|
|
2534
|
+
pattern: {
|
|
2535
|
+
type: String,
|
|
2536
|
+
},
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
static get delegateAttrs() {
|
|
2541
|
+
return [...super.delegateAttrs, 'pattern'];
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
static get constraints() {
|
|
2545
|
+
return [...super.constraints, 'pattern'];
|
|
2546
|
+
}
|
|
2547
|
+
};
|
|
2366
2548
|
|
|
2367
2549
|
/**
|
|
2368
2550
|
* @license
|
|
2369
|
-
* Copyright (c) 2015 -
|
|
2551
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
2370
2552
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2371
2553
|
*/
|
|
2372
2554
|
|
|
@@ -2676,20 +2858,18 @@ const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
2676
2858
|
_flushPendingRequests(size) {
|
|
2677
2859
|
if (this._pendingRequests) {
|
|
2678
2860
|
const lastPage = Math.ceil(size / this.pageSize);
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
if (page >= lastPage) {
|
|
2683
|
-
this._pendingRequests[page]([], size);
|
|
2861
|
+
Object.entries(this._pendingRequests).forEach(([page, callback]) => {
|
|
2862
|
+
if (parseInt(page) >= lastPage) {
|
|
2863
|
+
callback([], size);
|
|
2684
2864
|
}
|
|
2685
|
-
}
|
|
2865
|
+
});
|
|
2686
2866
|
}
|
|
2687
2867
|
}
|
|
2688
2868
|
};
|
|
2689
2869
|
|
|
2690
2870
|
/**
|
|
2691
2871
|
* @license
|
|
2692
|
-
* Copyright (c) 2021 -
|
|
2872
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
2693
2873
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2694
2874
|
*/
|
|
2695
2875
|
|
|
@@ -2714,7 +2894,7 @@ function processTemplates(component) {
|
|
|
2714
2894
|
|
|
2715
2895
|
/**
|
|
2716
2896
|
* @license
|
|
2717
|
-
* Copyright (c) 2015 -
|
|
2897
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
2718
2898
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2719
2899
|
*/
|
|
2720
2900
|
|
|
@@ -2748,10 +2928,19 @@ function findItemIndex(items, callback) {
|
|
|
2748
2928
|
|
|
2749
2929
|
/**
|
|
2750
2930
|
* @polymerMixin
|
|
2931
|
+
* @mixes ControllerMixin
|
|
2932
|
+
* @mixes ValidateMixin
|
|
2933
|
+
* @mixes DisabledMixin
|
|
2934
|
+
* @mixes InputMixin
|
|
2935
|
+
* @mixes KeyboardMixin
|
|
2936
|
+
* @mixes FocusMixin
|
|
2937
|
+
* @mixes OverlayClassMixin
|
|
2751
2938
|
* @param {function(new:HTMLElement)} subclass
|
|
2752
2939
|
*/
|
|
2753
2940
|
const ComboBoxMixin = (subclass) =>
|
|
2754
|
-
class
|
|
2941
|
+
class ComboBoxMixinClass extends OverlayClassMixin(
|
|
2942
|
+
ControllerMixin(ValidateMixin(FocusMixin(KeyboardMixin(InputMixin(DisabledMixin(subclass)))))),
|
|
2943
|
+
) {
|
|
2755
2944
|
static get properties() {
|
|
2756
2945
|
return {
|
|
2757
2946
|
/**
|
|
@@ -2950,7 +3139,6 @@ const ComboBoxMixin = (subclass) =>
|
|
|
2950
3139
|
|
|
2951
3140
|
constructor() {
|
|
2952
3141
|
super();
|
|
2953
|
-
this._boundOnFocusout = this._onFocusout.bind(this);
|
|
2954
3142
|
this._boundOverlaySelectedItemChanged = this._overlaySelectedItemChanged.bind(this);
|
|
2955
3143
|
this._boundOnClearButtonMouseDown = this.__onClearButtonMouseDown.bind(this);
|
|
2956
3144
|
this._boundOnClick = this._onClick.bind(this);
|
|
@@ -2967,24 +3155,6 @@ const ComboBoxMixin = (subclass) =>
|
|
|
2967
3155
|
return 'vaadin-combo-box';
|
|
2968
3156
|
}
|
|
2969
3157
|
|
|
2970
|
-
/**
|
|
2971
|
-
* @return {string | undefined}
|
|
2972
|
-
* @protected
|
|
2973
|
-
*/
|
|
2974
|
-
get _inputElementValue() {
|
|
2975
|
-
return this.inputElement ? this.inputElement[this._propertyForValue] : undefined;
|
|
2976
|
-
}
|
|
2977
|
-
|
|
2978
|
-
/**
|
|
2979
|
-
* @param {string} value
|
|
2980
|
-
* @protected
|
|
2981
|
-
*/
|
|
2982
|
-
set _inputElementValue(value) {
|
|
2983
|
-
if (this.inputElement) {
|
|
2984
|
-
this.inputElement[this._propertyForValue] = value;
|
|
2985
|
-
}
|
|
2986
|
-
}
|
|
2987
|
-
|
|
2988
3158
|
/**
|
|
2989
3159
|
* Get a reference to the native `<input>` element.
|
|
2990
3160
|
* Override to provide a custom input.
|
|
@@ -3035,8 +3205,6 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3035
3205
|
this._initOverlay();
|
|
3036
3206
|
this._initScroller();
|
|
3037
3207
|
|
|
3038
|
-
this.addEventListener('focusout', this._boundOnFocusout);
|
|
3039
|
-
|
|
3040
3208
|
this._lastCommittedValue = this.value;
|
|
3041
3209
|
|
|
3042
3210
|
this.addEventListener('click', this._boundOnClick);
|
|
@@ -3044,7 +3212,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3044
3212
|
|
|
3045
3213
|
const bringToFrontListener = () => {
|
|
3046
3214
|
requestAnimationFrame(() => {
|
|
3047
|
-
this
|
|
3215
|
+
this._overlayElement.bringToFront();
|
|
3048
3216
|
});
|
|
3049
3217
|
};
|
|
3050
3218
|
|
|
@@ -3135,6 +3303,8 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3135
3303
|
overlay.addEventListener('opened-changed', (e) => {
|
|
3136
3304
|
this._overlayOpened = e.detail.value;
|
|
3137
3305
|
});
|
|
3306
|
+
|
|
3307
|
+
this._overlayElement = overlay;
|
|
3138
3308
|
}
|
|
3139
3309
|
|
|
3140
3310
|
/**
|
|
@@ -3146,7 +3316,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3146
3316
|
_initScroller(host) {
|
|
3147
3317
|
const scrollerTag = `${this._tagNamePrefix}-scroller`;
|
|
3148
3318
|
|
|
3149
|
-
const overlay = this
|
|
3319
|
+
const overlay = this._overlayElement;
|
|
3150
3320
|
|
|
3151
3321
|
overlay.renderer = (root) => {
|
|
3152
3322
|
if (!root.firstChild) {
|
|
@@ -3159,7 +3329,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3159
3329
|
|
|
3160
3330
|
const scroller = overlay.querySelector(scrollerTag);
|
|
3161
3331
|
|
|
3162
|
-
scroller.
|
|
3332
|
+
scroller.owner = host || this;
|
|
3163
3333
|
scroller.getItemLabel = this._getItemLabel.bind(this);
|
|
3164
3334
|
scroller.addEventListener('selection-changed', this._boundOverlaySelectedItemChanged);
|
|
3165
3335
|
|
|
@@ -3254,7 +3424,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3254
3424
|
}
|
|
3255
3425
|
}
|
|
3256
3426
|
|
|
3257
|
-
this
|
|
3427
|
+
this._overlayElement.restoreFocusOnClose = true;
|
|
3258
3428
|
} else {
|
|
3259
3429
|
this._onClosed();
|
|
3260
3430
|
if (this._openedWithFocusRing && this._isInputFocused()) {
|
|
@@ -3288,13 +3458,19 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3288
3458
|
return event.composedPath()[0] === this.clearElement;
|
|
3289
3459
|
}
|
|
3290
3460
|
|
|
3461
|
+
/** @private */
|
|
3462
|
+
__onClearButtonMouseDown(event) {
|
|
3463
|
+
event.preventDefault(); // Prevent native focusout event
|
|
3464
|
+
this.inputElement.focus();
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3291
3467
|
/**
|
|
3292
3468
|
* @param {Event} event
|
|
3293
3469
|
* @protected
|
|
3294
3470
|
*/
|
|
3295
|
-
|
|
3471
|
+
_onClearButtonClick(event) {
|
|
3296
3472
|
event.preventDefault();
|
|
3297
|
-
this.
|
|
3473
|
+
this._onClearAction();
|
|
3298
3474
|
|
|
3299
3475
|
// De-select dropdown item
|
|
3300
3476
|
if (this.opened) {
|
|
@@ -3330,15 +3506,13 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3330
3506
|
}
|
|
3331
3507
|
|
|
3332
3508
|
/** @private */
|
|
3333
|
-
_onClick(
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
if (
|
|
3337
|
-
this.
|
|
3338
|
-
} else if (path.indexOf(this._toggleElement) > -1) {
|
|
3339
|
-
this._onToggleButtonClick(e);
|
|
3509
|
+
_onClick(event) {
|
|
3510
|
+
if (this._isClearButton(event)) {
|
|
3511
|
+
this._onClearButtonClick(event);
|
|
3512
|
+
} else if (event.composedPath().includes(this._toggleElement)) {
|
|
3513
|
+
this._onToggleButtonClick(event);
|
|
3340
3514
|
} else {
|
|
3341
|
-
this._onHostClick(
|
|
3515
|
+
this._onHostClick(event);
|
|
3342
3516
|
}
|
|
3343
3517
|
}
|
|
3344
3518
|
|
|
@@ -3353,7 +3527,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3353
3527
|
super._onKeyDown(e);
|
|
3354
3528
|
|
|
3355
3529
|
if (e.key === 'Tab') {
|
|
3356
|
-
this
|
|
3530
|
+
this._overlayElement.restoreFocusOnClose = false;
|
|
3357
3531
|
} else if (e.key === 'ArrowDown') {
|
|
3358
3532
|
this._onArrowDown();
|
|
3359
3533
|
|
|
@@ -3369,7 +3543,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3369
3543
|
|
|
3370
3544
|
/** @private */
|
|
3371
3545
|
_getItemLabel(item) {
|
|
3372
|
-
let label = item && this.itemLabelPath ?
|
|
3546
|
+
let label = item && this.itemLabelPath ? get(this.itemLabelPath, item) : undefined;
|
|
3373
3547
|
if (label === undefined || label === null) {
|
|
3374
3548
|
label = item ? item.toString() : '';
|
|
3375
3549
|
}
|
|
@@ -3378,7 +3552,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3378
3552
|
|
|
3379
3553
|
/** @private */
|
|
3380
3554
|
_getItemValue(item) {
|
|
3381
|
-
let value = item && this.itemValuePath ?
|
|
3555
|
+
let value = item && this.itemValuePath ? get(this.itemValuePath, item) : undefined;
|
|
3382
3556
|
if (value === undefined) {
|
|
3383
3557
|
value = item ? item.toString() : '';
|
|
3384
3558
|
}
|
|
@@ -3516,7 +3690,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3516
3690
|
} else if (this.clearButtonVisible && !this.opened && !!this.value) {
|
|
3517
3691
|
e.stopPropagation();
|
|
3518
3692
|
// The clear button is visible and the overlay is closed, so clear the value.
|
|
3519
|
-
this.
|
|
3693
|
+
this._onClearAction();
|
|
3520
3694
|
}
|
|
3521
3695
|
} else if (this.opened) {
|
|
3522
3696
|
// Auto-open is enabled
|
|
@@ -3534,7 +3708,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3534
3708
|
} else if (this.clearButtonVisible && !!this.value) {
|
|
3535
3709
|
e.stopPropagation();
|
|
3536
3710
|
// The clear button is visible and the overlay is closed, so clear the value.
|
|
3537
|
-
this.
|
|
3711
|
+
this._onClearAction();
|
|
3538
3712
|
}
|
|
3539
3713
|
}
|
|
3540
3714
|
|
|
@@ -3556,7 +3730,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3556
3730
|
* Clears the current value.
|
|
3557
3731
|
* @protected
|
|
3558
3732
|
*/
|
|
3559
|
-
|
|
3733
|
+
_onClearAction() {
|
|
3560
3734
|
this.selectedItem = null;
|
|
3561
3735
|
|
|
3562
3736
|
if (this.allowCustomValue) {
|
|
@@ -3607,7 +3781,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3607
3781
|
}
|
|
3608
3782
|
} else {
|
|
3609
3783
|
// Try to find an item which label matches the input value.
|
|
3610
|
-
const items = [...(this.filteredItems || [])
|
|
3784
|
+
const items = [this.selectedItem, ...(this.filteredItems || [])];
|
|
3611
3785
|
const itemMatchingInputValue = items[this.__getItemIndexByLabel(items, this._inputElementValue)];
|
|
3612
3786
|
|
|
3613
3787
|
if (
|
|
@@ -3648,14 +3822,6 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3648
3822
|
this.filter = '';
|
|
3649
3823
|
}
|
|
3650
3824
|
|
|
3651
|
-
/**
|
|
3652
|
-
* @return {string}
|
|
3653
|
-
* @protected
|
|
3654
|
-
*/
|
|
3655
|
-
get _propertyForValue() {
|
|
3656
|
-
return 'value';
|
|
3657
|
-
}
|
|
3658
|
-
|
|
3659
3825
|
/**
|
|
3660
3826
|
* Override an event listener from `InputMixin`.
|
|
3661
3827
|
* @param {!Event} event
|
|
@@ -3802,6 +3968,12 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3802
3968
|
|
|
3803
3969
|
/** @private */
|
|
3804
3970
|
_detectAndDispatchChange() {
|
|
3971
|
+
// Do not validate when focusout is caused by document
|
|
3972
|
+
// losing focus, which happens on browser tab switch.
|
|
3973
|
+
if (document.hasFocus()) {
|
|
3974
|
+
this.validate();
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3805
3977
|
if (this.value !== this._lastCommittedValue) {
|
|
3806
3978
|
this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
|
|
3807
3979
|
this._lastCommittedValue = this.value;
|
|
@@ -3944,26 +4116,18 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3944
4116
|
}
|
|
3945
4117
|
}
|
|
3946
4118
|
|
|
3947
|
-
/**
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
if (event.relatedTarget && event.relatedTarget.localName === `${this._tagNamePrefix}-item`) {
|
|
3958
|
-
return;
|
|
3959
|
-
}
|
|
4119
|
+
/**
|
|
4120
|
+
* Override method inherited from `FocusMixin`
|
|
4121
|
+
* to close the overlay on blur and commit the value.
|
|
4122
|
+
*
|
|
4123
|
+
* @param {boolean} focused
|
|
4124
|
+
* @protected
|
|
4125
|
+
* @override
|
|
4126
|
+
*/
|
|
4127
|
+
_setFocused(focused) {
|
|
4128
|
+
super._setFocused(focused);
|
|
3960
4129
|
|
|
3961
|
-
|
|
3962
|
-
if (event.relatedTarget === this.$.overlay) {
|
|
3963
|
-
event.composedPath()[0].focus();
|
|
3964
|
-
return;
|
|
3965
|
-
}
|
|
3966
|
-
if (!this.readonly && !this._closeOnBlurIsPrevented) {
|
|
4130
|
+
if (!focused && !this.readonly && !this._closeOnBlurIsPrevented) {
|
|
3967
4131
|
// User's logic in `custom-value-set` event listener might cause input to blur,
|
|
3968
4132
|
// which will result in attempting to commit the same custom value once again.
|
|
3969
4133
|
if (!this.opened && this.allowCustomValue && this._inputElementValue === this._lastCustomValue) {
|
|
@@ -3975,6 +4139,32 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3975
4139
|
}
|
|
3976
4140
|
}
|
|
3977
4141
|
|
|
4142
|
+
/**
|
|
4143
|
+
* Override method inherited from `FocusMixin` to not remove focused
|
|
4144
|
+
* state when focus moves to the overlay.
|
|
4145
|
+
*
|
|
4146
|
+
* @param {FocusEvent} event
|
|
4147
|
+
* @return {boolean}
|
|
4148
|
+
* @protected
|
|
4149
|
+
* @override
|
|
4150
|
+
*/
|
|
4151
|
+
_shouldRemoveFocus(event) {
|
|
4152
|
+
// VoiceOver on iOS fires `focusout` event when moving focus to the item in the dropdown.
|
|
4153
|
+
// Do not focus the input in this case, because it would break announcement for the item.
|
|
4154
|
+
if (event.relatedTarget && event.relatedTarget.localName === `${this._tagNamePrefix}-item`) {
|
|
4155
|
+
return false;
|
|
4156
|
+
}
|
|
4157
|
+
|
|
4158
|
+
// Do not blur when focus moves to the overlay
|
|
4159
|
+
// Also, fixes the problem with `focusout` happening when clicking on the scroll bar on Edge
|
|
4160
|
+
if (event.relatedTarget === this._overlayElement) {
|
|
4161
|
+
event.composedPath()[0].focus();
|
|
4162
|
+
return false;
|
|
4163
|
+
}
|
|
4164
|
+
|
|
4165
|
+
return true;
|
|
4166
|
+
}
|
|
4167
|
+
|
|
3978
4168
|
/** @private */
|
|
3979
4169
|
_onTouchend(event) {
|
|
3980
4170
|
if (!this.clearElement || event.composedPath()[0] !== this.clearElement) {
|
|
@@ -3982,7 +4172,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3982
4172
|
}
|
|
3983
4173
|
|
|
3984
4174
|
event.preventDefault();
|
|
3985
|
-
this.
|
|
4175
|
+
this._onClearAction();
|
|
3986
4176
|
}
|
|
3987
4177
|
|
|
3988
4178
|
/**
|
|
@@ -4028,7 +4218,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
4028
4218
|
|
|
4029
4219
|
/**
|
|
4030
4220
|
* @license
|
|
4031
|
-
* Copyright (c) 2015 -
|
|
4221
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
4032
4222
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
4033
4223
|
*/
|
|
4034
4224
|
|
|
@@ -4148,7 +4338,7 @@ registerStyles('vaadin-combo-box', inputFieldShared$1, { moduleId: 'vaadin-combo
|
|
|
4148
4338
|
* Note: the `theme` attribute value set on `<vaadin-combo-box>` is
|
|
4149
4339
|
* propagated to the internal components listed above.
|
|
4150
4340
|
*
|
|
4151
|
-
* See [Styling Components](https://vaadin.com/docs/latest/styling/
|
|
4341
|
+
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
4152
4342
|
*
|
|
4153
4343
|
* @fires {Event} change - Fired when the user commits a value change.
|
|
4154
4344
|
* @fires {CustomEvent} custom-value-set - Fired when the user sets a custom value.
|
|
@@ -4159,6 +4349,7 @@ registerStyles('vaadin-combo-box', inputFieldShared$1, { moduleId: 'vaadin-combo
|
|
|
4159
4349
|
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
|
|
4160
4350
|
* @fires {CustomEvent} validated - Fired whenever the field is validated.
|
|
4161
4351
|
*
|
|
4352
|
+
* @customElement
|
|
4162
4353
|
* @extends HTMLElement
|
|
4163
4354
|
* @mixes ElementMixin
|
|
4164
4355
|
* @mixes ThemableMixin
|
|
@@ -4261,6 +4452,7 @@ class ComboBox extends ComboBoxDataProviderMixin(
|
|
|
4261
4452
|
this._tooltipController = new TooltipController(this);
|
|
4262
4453
|
this.addController(this._tooltipController);
|
|
4263
4454
|
this._tooltipController.setPosition('top');
|
|
4455
|
+
this._tooltipController.setAriaTarget(this.inputElement);
|
|
4264
4456
|
this._tooltipController.setShouldShow((target) => !target.opened);
|
|
4265
4457
|
|
|
4266
4458
|
this._positionTarget = this.shadowRoot.querySelector('[part="input-field"]');
|
|
@@ -4268,48 +4460,17 @@ class ComboBox extends ComboBoxDataProviderMixin(
|
|
|
4268
4460
|
}
|
|
4269
4461
|
|
|
4270
4462
|
/**
|
|
4271
|
-
* Override method
|
|
4272
|
-
*
|
|
4273
|
-
*
|
|
4274
|
-
*
|
|
4275
|
-
*/
|
|
4276
|
-
_setFocused(focused) {
|
|
4277
|
-
super._setFocused(focused);
|
|
4278
|
-
|
|
4279
|
-
if (!focused) {
|
|
4280
|
-
this.validate();
|
|
4281
|
-
}
|
|
4282
|
-
}
|
|
4283
|
-
|
|
4284
|
-
/**
|
|
4285
|
-
* Override method inherited from `FocusMixin` to not remove focused
|
|
4286
|
-
* state when focus moves to the overlay.
|
|
4287
|
-
* @param {FocusEvent} event
|
|
4288
|
-
* @return {boolean}
|
|
4289
|
-
* @protected
|
|
4290
|
-
* @override
|
|
4291
|
-
*/
|
|
4292
|
-
_shouldRemoveFocus(event) {
|
|
4293
|
-
// Do not blur when focus moves to the overlay
|
|
4294
|
-
if (event.relatedTarget === this.$.overlay) {
|
|
4295
|
-
event.composedPath()[0].focus();
|
|
4296
|
-
return false;
|
|
4297
|
-
}
|
|
4298
|
-
|
|
4299
|
-
return true;
|
|
4300
|
-
}
|
|
4301
|
-
|
|
4302
|
-
/**
|
|
4303
|
-
* Override method inherited from `InputControlMixin` to handle clear
|
|
4304
|
-
* button click and stop event from propagating to the host element.
|
|
4463
|
+
* Override the method from `InputControlMixin`
|
|
4464
|
+
* to stop event propagation to prevent `ComboBoxMixin`
|
|
4465
|
+
* from handling this click event also on its own.
|
|
4466
|
+
*
|
|
4305
4467
|
* @param {Event} event
|
|
4306
4468
|
* @protected
|
|
4307
4469
|
* @override
|
|
4308
4470
|
*/
|
|
4309
4471
|
_onClearButtonClick(event) {
|
|
4310
4472
|
event.stopPropagation();
|
|
4311
|
-
|
|
4312
|
-
this._handleClearButtonClick(event);
|
|
4473
|
+
super._onClearButtonClick(event);
|
|
4313
4474
|
}
|
|
4314
4475
|
|
|
4315
4476
|
/**
|
|
@@ -4326,4 +4487,4 @@ class ComboBox extends ComboBoxDataProviderMixin(
|
|
|
4326
4487
|
}
|
|
4327
4488
|
}
|
|
4328
4489
|
|
|
4329
|
-
|
|
4490
|
+
defineCustomElement(ComboBox);
|