@everymatrix/general-input 1.27.6 → 1.27.8
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 +12152 -11622
- package/dist/components/active-mixin.js +6 -6
- package/dist/components/checkbox-group-input2.js +260 -307
- 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 +988 -805
- package/dist/components/virtual-keyboard-controller.js +1114 -1771
- package/dist/esm/checkbox-group-input_10.entry.js +12152 -11622
- package/dist/general-input/general-input.esm.js +1 -1
- package/dist/general-input/p-e322bf39.entry.js +3646 -0
- package/dist/types/Users/dragos.bodea/Documents/everymatrix-prjs/emfe-widgets/widgets-stencil/packages/general-input/.stencil/packages/general-input/stencil.config.d.ts +2 -0
- package/package.json +1 -1
- package/dist/components/pattern-mixin.js +0 -85
- package/dist/general-input/p-de2b2f7b.entry.js +0 -3581
- package/dist/types/Users/sebastian.strulea/Documents/work/widgets-stencil/packages/general-input/.stencil/packages/general-input/stencil.config.d.ts +0 -2
|
@@ -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, f as defineCustomElement, h as html, g as ThemableMixin, n as DirMixin, P as PolymerElement, m as microTask, R as idlePeriod, U as animationFrame, W as flush, o as Debouncer, X as enqueueDebouncer, t as timeOut, p as generateUniqueId, H as ControllerMixin, V as ValidateMixin, l as FocusMixin, K as KeyboardMixin, I as InputMixin, a as DisabledMixin, N as isElementFocused, c as InputController, e 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
|
-
* @
|
|
521
|
+
* @customElement
|
|
522
|
+
* @extends HTMLElement
|
|
523
|
+
* @mixes ComboBoxOverlayMixin
|
|
524
|
+
* @mixes DirMixin
|
|
525
|
+
* @mixes OverlayMixin
|
|
526
|
+
* @mixes ThemableMixin
|
|
422
527
|
* @private
|
|
423
528
|
*/
|
|
424
|
-
class ComboBoxOverlay extends
|
|
529
|
+
class ComboBoxOverlay extends ComboBoxOverlayMixin(OverlayMixin(DirMixin(ThemableMixin(PolymerElement)))) {
|
|
425
530
|
static get is() {
|
|
426
531
|
return 'vaadin-combo-box-overlay';
|
|
427
532
|
}
|
|
428
533
|
|
|
429
534
|
static get template() {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
_outsideClickListener(event) {
|
|
463
|
-
const eventPath = event.composedPath();
|
|
464
|
-
if (!eventPath.includes(this.positionTarget) && !eventPath.includes(this)) {
|
|
465
|
-
this.close();
|
|
466
|
-
}
|
|
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,37 @@ 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._updateScrollerSize();
|
|
1570
|
+
this._virtualCount = this.items.length;
|
|
1571
|
+
this._render();
|
|
1572
|
+
}
|
|
1479
1573
|
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1574
|
+
// When reducing size while invisible, iron-list does not update items, so
|
|
1575
|
+
// their hidden state is not updated and their __lastUpdatedIndex is not
|
|
1576
|
+
// reset. In that case force an update here.
|
|
1577
|
+
if (!this._isVisible) {
|
|
1578
|
+
this._assignModels();
|
|
1484
1579
|
}
|
|
1485
1580
|
|
|
1486
1581
|
if (!this.elementsContainer.children.length) {
|
|
1487
1582
|
requestAnimationFrame(() => this._resizeHandler());
|
|
1488
1583
|
}
|
|
1489
1584
|
|
|
1490
|
-
|
|
1491
|
-
//
|
|
1585
|
+
// Schedule and flush a resize handler. This will cause a
|
|
1586
|
+
// re-render for the elements.
|
|
1492
1587
|
this._resizeHandler();
|
|
1493
1588
|
flush();
|
|
1494
1589
|
}
|
|
@@ -1553,16 +1648,20 @@ class IronListAdapter {
|
|
|
1553
1648
|
|
|
1554
1649
|
/** @private */
|
|
1555
1650
|
_assignModels(itemSet) {
|
|
1651
|
+
const updatedElements = [];
|
|
1556
1652
|
this._iterateItems((pidx, vidx) => {
|
|
1557
1653
|
const el = this._physicalItems[pidx];
|
|
1558
1654
|
el.hidden = vidx >= this.size;
|
|
1559
1655
|
if (!el.hidden) {
|
|
1560
1656
|
el.__virtualIndex = vidx + (this._vidxOffset || 0);
|
|
1561
1657
|
this.__updateElement(el, el.__virtualIndex);
|
|
1658
|
+
updatedElements.push(el);
|
|
1562
1659
|
} else {
|
|
1563
1660
|
delete el.__lastUpdatedIndex;
|
|
1564
1661
|
}
|
|
1565
1662
|
}, itemSet);
|
|
1663
|
+
|
|
1664
|
+
this.__afterElementsUpdated(updatedElements);
|
|
1566
1665
|
}
|
|
1567
1666
|
|
|
1568
1667
|
/** @private */
|
|
@@ -1691,7 +1790,9 @@ class IronListAdapter {
|
|
|
1691
1790
|
deltaY *= this._scrollPageHeight;
|
|
1692
1791
|
}
|
|
1693
1792
|
|
|
1694
|
-
|
|
1793
|
+
if (!this._deltaYAcc) {
|
|
1794
|
+
this._deltaYAcc = 0;
|
|
1795
|
+
}
|
|
1695
1796
|
|
|
1696
1797
|
if (this._wheelAnimationFrame) {
|
|
1697
1798
|
// Accumulate wheel delta while a frame is being processed
|
|
@@ -1764,6 +1865,29 @@ class IronListAdapter {
|
|
|
1764
1865
|
);
|
|
1765
1866
|
}
|
|
1766
1867
|
|
|
1868
|
+
/**
|
|
1869
|
+
* Increases the pool size.
|
|
1870
|
+
* @override
|
|
1871
|
+
*/
|
|
1872
|
+
_increasePoolIfNeeded(count) {
|
|
1873
|
+
if (this._physicalCount > 2 && count) {
|
|
1874
|
+
// The iron-list logic has already created some physical items and
|
|
1875
|
+
// has decided to create more. Since each item creation round is
|
|
1876
|
+
// expensive, let's try to create the remaining items in one go.
|
|
1877
|
+
|
|
1878
|
+
// Calculate the total item count that would be needed to fill the viewport
|
|
1879
|
+
// plus the buffer assuming rest of the items to be of the average size
|
|
1880
|
+
// of the items already created.
|
|
1881
|
+
const totalItemCount = Math.ceil(this._optPhysicalSize / this._physicalAverage);
|
|
1882
|
+
const missingItemCount = totalItemCount - this._physicalCount;
|
|
1883
|
+
// Create the remaining items in one go. Use a maximum of 100 items
|
|
1884
|
+
// as a safety measure.
|
|
1885
|
+
super._increasePoolIfNeeded(Math.max(count, Math.min(100, missingItemCount)));
|
|
1886
|
+
} else {
|
|
1887
|
+
super._increasePoolIfNeeded(count);
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1767
1891
|
/**
|
|
1768
1892
|
* @returns {Number|undefined} - The browser's default font-size in pixels
|
|
1769
1893
|
* @private
|
|
@@ -1893,6 +2017,24 @@ class Virtualizer {
|
|
|
1893
2017
|
this.__adapter = new IronListAdapter(config);
|
|
1894
2018
|
}
|
|
1895
2019
|
|
|
2020
|
+
/**
|
|
2021
|
+
* Gets the index of the first visible item in the viewport.
|
|
2022
|
+
*
|
|
2023
|
+
* @return {number}
|
|
2024
|
+
*/
|
|
2025
|
+
get firstVisibleIndex() {
|
|
2026
|
+
return this.__adapter.adjustedFirstVisibleIndex;
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
/**
|
|
2030
|
+
* Gets the index of the last visible item in the viewport.
|
|
2031
|
+
*
|
|
2032
|
+
* @return {number}
|
|
2033
|
+
*/
|
|
2034
|
+
get lastVisibleIndex() {
|
|
2035
|
+
return this.__adapter.adjustedLastVisibleIndex;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
1896
2038
|
/**
|
|
1897
2039
|
* The size of the virtualizer
|
|
1898
2040
|
* @return {number | undefined} The size of the virtualizer
|
|
@@ -1940,29 +2082,11 @@ class Virtualizer {
|
|
|
1940
2082
|
flush() {
|
|
1941
2083
|
this.__adapter.flush();
|
|
1942
2084
|
}
|
|
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
2085
|
}
|
|
1962
2086
|
|
|
1963
2087
|
/**
|
|
1964
2088
|
* @license
|
|
1965
|
-
* Copyright (c) 2015 -
|
|
2089
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
1966
2090
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
1967
2091
|
*/
|
|
1968
2092
|
|
|
@@ -1979,394 +2103,454 @@ const ComboBoxPlaceholder = class ComboBoxPlaceholder {
|
|
|
1979
2103
|
|
|
1980
2104
|
/**
|
|
1981
2105
|
* @license
|
|
1982
|
-
* Copyright (c) 2015 -
|
|
2106
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
1983
2107
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
1984
2108
|
*/
|
|
1985
2109
|
|
|
1986
2110
|
/**
|
|
1987
|
-
*
|
|
1988
|
-
*
|
|
1989
|
-
* @extends HTMLElement
|
|
1990
|
-
* @private
|
|
2111
|
+
* @polymerMixin
|
|
1991
2112
|
*/
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
2113
|
+
const ComboBoxScrollerMixin = (superClass) =>
|
|
2114
|
+
class ComboBoxScrollerMixin extends superClass {
|
|
2115
|
+
static get properties() {
|
|
2116
|
+
return {
|
|
2117
|
+
/**
|
|
2118
|
+
* A full set of items to filter the visible options from.
|
|
2119
|
+
* Set to an empty array when combo-box is not opened.
|
|
2120
|
+
*/
|
|
2121
|
+
items: {
|
|
2122
|
+
type: Array,
|
|
2123
|
+
observer: '__itemsChanged',
|
|
2124
|
+
},
|
|
1996
2125
|
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2126
|
+
/**
|
|
2127
|
+
* Index of an item that has focus outline and is scrolled into view.
|
|
2128
|
+
* The actual focus still remains in the input field.
|
|
2129
|
+
*/
|
|
2130
|
+
focusedIndex: {
|
|
2131
|
+
type: Number,
|
|
2132
|
+
observer: '__focusedIndexChanged',
|
|
2133
|
+
},
|
|
2004
2134
|
|
|
2005
|
-
|
|
2006
|
-
|
|
2135
|
+
/**
|
|
2136
|
+
* Set to true while combo-box fetches new page from the data provider.
|
|
2137
|
+
*/
|
|
2138
|
+
loading: {
|
|
2139
|
+
type: Boolean,
|
|
2140
|
+
observer: '__loadingChanged',
|
|
2141
|
+
},
|
|
2007
2142
|
|
|
2008
|
-
|
|
2009
|
-
|
|
2143
|
+
/**
|
|
2144
|
+
* Whether the combo-box is currently opened or not. If set to false,
|
|
2145
|
+
* calling `scrollIntoView` does not have any effect.
|
|
2146
|
+
*/
|
|
2147
|
+
opened: {
|
|
2148
|
+
type: Boolean,
|
|
2149
|
+
observer: '__openedChanged',
|
|
2150
|
+
},
|
|
2010
2151
|
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2152
|
+
/**
|
|
2153
|
+
* The selected item from the `items` array.
|
|
2154
|
+
*/
|
|
2155
|
+
selectedItem: {
|
|
2156
|
+
type: Object,
|
|
2157
|
+
observer: '__selectedItemChanged',
|
|
2158
|
+
},
|
|
2014
2159
|
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
}
|
|
2021
|
-
</style>
|
|
2022
|
-
<div id="selector">
|
|
2023
|
-
<slot></slot>
|
|
2024
|
-
</div>
|
|
2025
|
-
`;
|
|
2026
|
-
}
|
|
2160
|
+
/**
|
|
2161
|
+
* Path for the id of the item, used to detect whether the item is selected.
|
|
2162
|
+
*/
|
|
2163
|
+
itemIdPath: {
|
|
2164
|
+
type: String,
|
|
2165
|
+
},
|
|
2027
2166
|
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
items: {
|
|
2035
|
-
type: Array,
|
|
2036
|
-
observer: '__itemsChanged',
|
|
2037
|
-
},
|
|
2167
|
+
/**
|
|
2168
|
+
* Reference to the owner (combo-box owner), used by the item elements.
|
|
2169
|
+
*/
|
|
2170
|
+
owner: {
|
|
2171
|
+
type: Object,
|
|
2172
|
+
},
|
|
2038
2173
|
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
observer: '__focusedIndexChanged',
|
|
2046
|
-
},
|
|
2174
|
+
/**
|
|
2175
|
+
* Function used to set a label for every combo-box item.
|
|
2176
|
+
*/
|
|
2177
|
+
getItemLabel: {
|
|
2178
|
+
type: Object,
|
|
2179
|
+
},
|
|
2047
2180
|
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2181
|
+
/**
|
|
2182
|
+
* Function used to render the content of every combo-box item.
|
|
2183
|
+
*/
|
|
2184
|
+
renderer: {
|
|
2185
|
+
type: Object,
|
|
2186
|
+
observer: '__rendererChanged',
|
|
2187
|
+
},
|
|
2055
2188
|
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2189
|
+
/**
|
|
2190
|
+
* Used to propagate the `theme` attribute from the host element.
|
|
2191
|
+
*/
|
|
2192
|
+
theme: {
|
|
2193
|
+
type: String,
|
|
2194
|
+
},
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2064
2197
|
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
type: Object,
|
|
2070
|
-
observer: '__selectedItemChanged',
|
|
2071
|
-
},
|
|
2198
|
+
constructor() {
|
|
2199
|
+
super();
|
|
2200
|
+
this.__boundOnItemClick = this.__onItemClick.bind(this);
|
|
2201
|
+
}
|
|
2072
2202
|
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2203
|
+
/** @private */
|
|
2204
|
+
get _viewportTotalPaddingBottom() {
|
|
2205
|
+
if (this._cachedViewportTotalPaddingBottom === undefined) {
|
|
2206
|
+
const itemsStyle = window.getComputedStyle(this.$.selector);
|
|
2207
|
+
this._cachedViewportTotalPaddingBottom = [itemsStyle.paddingBottom, itemsStyle.borderBottomWidth]
|
|
2208
|
+
.map((v) => {
|
|
2209
|
+
return parseInt(v, 10);
|
|
2210
|
+
})
|
|
2211
|
+
.reduce((sum, v) => {
|
|
2212
|
+
return sum + v;
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2079
2215
|
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
*/
|
|
2083
|
-
comboBox: {
|
|
2084
|
-
type: Object,
|
|
2085
|
-
},
|
|
2216
|
+
return this._cachedViewportTotalPaddingBottom;
|
|
2217
|
+
}
|
|
2086
2218
|
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
getItemLabel: {
|
|
2091
|
-
type: Object,
|
|
2092
|
-
},
|
|
2219
|
+
/** @protected */
|
|
2220
|
+
ready() {
|
|
2221
|
+
super.ready();
|
|
2093
2222
|
|
|
2094
|
-
|
|
2095
|
-
* Function used to render the content of every combo-box item.
|
|
2096
|
-
*/
|
|
2097
|
-
renderer: {
|
|
2098
|
-
type: Object,
|
|
2099
|
-
observer: '__rendererChanged',
|
|
2100
|
-
},
|
|
2223
|
+
this.setAttribute('role', 'listbox');
|
|
2101
2224
|
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
*/
|
|
2105
|
-
theme: {
|
|
2106
|
-
type: String,
|
|
2107
|
-
},
|
|
2108
|
-
};
|
|
2109
|
-
}
|
|
2225
|
+
// Ensure every instance has unique ID
|
|
2226
|
+
this.id = `${this.localName}-${generateUniqueId()}`;
|
|
2110
2227
|
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
this.__boundOnItemClick = this.__onItemClick.bind(this);
|
|
2114
|
-
}
|
|
2228
|
+
// Allow extensions to customize tag name for the items
|
|
2229
|
+
this.__hostTagName = this.constructor.is.replace('-scroller', '');
|
|
2115
2230
|
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
this.
|
|
2231
|
+
this.addEventListener('click', (e) => e.stopPropagation());
|
|
2232
|
+
|
|
2233
|
+
this.__patchWheelOverScrolling();
|
|
2234
|
+
|
|
2235
|
+
this.__virtualizer = new Virtualizer({
|
|
2236
|
+
createElements: this.__createElements.bind(this),
|
|
2237
|
+
updateElement: this._updateElement.bind(this),
|
|
2238
|
+
elementsContainer: this,
|
|
2239
|
+
scrollTarget: this,
|
|
2240
|
+
scrollContainer: this.$.selector,
|
|
2241
|
+
});
|
|
2119
2242
|
}
|
|
2120
|
-
}
|
|
2121
2243
|
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2244
|
+
/**
|
|
2245
|
+
* Requests an update for the virtualizer to re-render items.
|
|
2246
|
+
*/
|
|
2247
|
+
requestContentUpdate() {
|
|
2248
|
+
if (this.__virtualizer) {
|
|
2249
|
+
this.__virtualizer.update();
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2125
2252
|
|
|
2126
|
-
|
|
2127
|
-
|
|
2253
|
+
/**
|
|
2254
|
+
* Scrolls an item at given index into view and adjusts `scrollTop`
|
|
2255
|
+
* so that the element gets fully visible on Arrow Down key press.
|
|
2256
|
+
* @param {number} index
|
|
2257
|
+
*/
|
|
2258
|
+
scrollIntoView(index) {
|
|
2259
|
+
if (!(this.opened && index >= 0)) {
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2128
2262
|
|
|
2129
|
-
|
|
2130
|
-
this.__hostTagName = this.constructor.is.replace('-scroller', '');
|
|
2263
|
+
const visibleItemsCount = this._visibleItemsCount();
|
|
2131
2264
|
|
|
2132
|
-
|
|
2265
|
+
let targetIndex = index;
|
|
2133
2266
|
|
|
2134
|
-
|
|
2267
|
+
if (index > this.__virtualizer.lastVisibleIndex - 1) {
|
|
2268
|
+
// Index is below the bottom, scrolling down. Make the item appear at the bottom.
|
|
2269
|
+
// First scroll to target (will be at the top of the scroller) to make sure it's rendered.
|
|
2270
|
+
this.__virtualizer.scrollToIndex(index);
|
|
2271
|
+
// Then calculate the index for the following scroll (to get the target to bottom of the scroller).
|
|
2272
|
+
targetIndex = index - visibleItemsCount + 1;
|
|
2273
|
+
} else if (index > this.__virtualizer.firstVisibleIndex) {
|
|
2274
|
+
// The item is already visible, scrolling is unnecessary per se. But we need to trigger iron-list to set
|
|
2275
|
+
// the correct scrollTop on the scrollTarget. Scrolling to firstVisibleIndex.
|
|
2276
|
+
targetIndex = this.__virtualizer.firstVisibleIndex;
|
|
2277
|
+
}
|
|
2278
|
+
this.__virtualizer.scrollToIndex(Math.max(0, targetIndex));
|
|
2279
|
+
|
|
2280
|
+
// Sometimes the item is partly below the bottom edge, detect and adjust.
|
|
2281
|
+
const lastPhysicalItem = [...this.children].find(
|
|
2282
|
+
(el) => !el.hidden && el.index === this.__virtualizer.lastVisibleIndex,
|
|
2283
|
+
);
|
|
2284
|
+
if (!lastPhysicalItem || index !== lastPhysicalItem.index) {
|
|
2285
|
+
return;
|
|
2286
|
+
}
|
|
2287
|
+
const lastPhysicalItemRect = lastPhysicalItem.getBoundingClientRect();
|
|
2288
|
+
const scrollerRect = this.getBoundingClientRect();
|
|
2289
|
+
const scrollTopAdjust = lastPhysicalItemRect.bottom - scrollerRect.bottom + this._viewportTotalPaddingBottom;
|
|
2290
|
+
if (scrollTopAdjust > 0) {
|
|
2291
|
+
this.scrollTop += scrollTopAdjust;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2135
2294
|
|
|
2136
|
-
|
|
2295
|
+
/**
|
|
2296
|
+
* @param {string | object} item
|
|
2297
|
+
* @param {string | object} selectedItem
|
|
2298
|
+
* @param {string} itemIdPath
|
|
2299
|
+
* @protected
|
|
2300
|
+
*/
|
|
2301
|
+
_isItemSelected(item, selectedItem, itemIdPath) {
|
|
2302
|
+
if (item instanceof ComboBoxPlaceholder) {
|
|
2303
|
+
return false;
|
|
2304
|
+
} else if (itemIdPath && item !== undefined && selectedItem !== undefined) {
|
|
2305
|
+
return get(itemIdPath, item) === get(itemIdPath, selectedItem);
|
|
2306
|
+
}
|
|
2307
|
+
return item === selectedItem;
|
|
2308
|
+
}
|
|
2137
2309
|
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2310
|
+
/** @private */
|
|
2311
|
+
__itemsChanged(items) {
|
|
2312
|
+
if (this.__virtualizer && items) {
|
|
2313
|
+
this.__virtualizer.size = items.length;
|
|
2314
|
+
this.__virtualizer.flush();
|
|
2315
|
+
this.requestContentUpdate();
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2146
2318
|
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
this.
|
|
2319
|
+
/** @private */
|
|
2320
|
+
__loadingChanged() {
|
|
2321
|
+
this.requestContentUpdate();
|
|
2150
2322
|
}
|
|
2151
|
-
}
|
|
2152
2323
|
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2324
|
+
/** @private */
|
|
2325
|
+
__openedChanged(opened) {
|
|
2326
|
+
if (opened) {
|
|
2327
|
+
this.requestContentUpdate();
|
|
2328
|
+
}
|
|
2156
2329
|
}
|
|
2157
2330
|
|
|
2158
|
-
|
|
2331
|
+
/** @private */
|
|
2332
|
+
__selectedItemChanged() {
|
|
2333
|
+
this.requestContentUpdate();
|
|
2334
|
+
}
|
|
2159
2335
|
|
|
2160
|
-
|
|
2336
|
+
/** @private */
|
|
2337
|
+
__focusedIndexChanged(index, oldIndex) {
|
|
2338
|
+
if (index !== oldIndex) {
|
|
2339
|
+
this.requestContentUpdate();
|
|
2340
|
+
}
|
|
2161
2341
|
|
|
2162
|
-
|
|
2163
|
-
//
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
targetIndex = index - visibleItemsCount + 1;
|
|
2168
|
-
} else if (index > this.__virtualizer.firstVisibleIndex) {
|
|
2169
|
-
// The item is already visible, scrolling is unnecessary per se. But we need to trigger iron-list to set
|
|
2170
|
-
// the correct scrollTop on the scrollTarget. Scrolling to firstVisibleIndex.
|
|
2171
|
-
targetIndex = this.__virtualizer.firstVisibleIndex;
|
|
2342
|
+
// Do not jump back to the previously focused item while loading
|
|
2343
|
+
// when requesting next page from the data provider on scroll.
|
|
2344
|
+
if (index >= 0 && !this.loading) {
|
|
2345
|
+
this.scrollIntoView(index);
|
|
2346
|
+
}
|
|
2172
2347
|
}
|
|
2173
|
-
this.__virtualizer.scrollToIndex(Math.max(0, targetIndex));
|
|
2174
2348
|
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
(
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
return;
|
|
2349
|
+
/** @private */
|
|
2350
|
+
__rendererChanged(renderer, oldRenderer) {
|
|
2351
|
+
if (renderer || oldRenderer) {
|
|
2352
|
+
this.requestContentUpdate();
|
|
2353
|
+
}
|
|
2181
2354
|
}
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2355
|
+
|
|
2356
|
+
/** @private */
|
|
2357
|
+
__createElements(count) {
|
|
2358
|
+
return [...Array(count)].map(() => {
|
|
2359
|
+
const item = document.createElement(`${this.__hostTagName}-item`);
|
|
2360
|
+
item.addEventListener('click', this.__boundOnItemClick);
|
|
2361
|
+
// Negative tabindex prevents the item content from being focused.
|
|
2362
|
+
item.tabIndex = '-1';
|
|
2363
|
+
item.style.width = '100%';
|
|
2364
|
+
return item;
|
|
2365
|
+
});
|
|
2187
2366
|
}
|
|
2188
|
-
}
|
|
2189
2367
|
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2368
|
+
/**
|
|
2369
|
+
* @param {HTMLElement} el
|
|
2370
|
+
* @param {number} index
|
|
2371
|
+
* @protected
|
|
2372
|
+
*/
|
|
2373
|
+
_updateElement(el, index) {
|
|
2374
|
+
const item = this.items[index];
|
|
2375
|
+
const focusedIndex = this.focusedIndex;
|
|
2376
|
+
const selected = this._isItemSelected(item, this.selectedItem, this.itemIdPath);
|
|
2377
|
+
|
|
2378
|
+
el.setProperties({
|
|
2379
|
+
item,
|
|
2380
|
+
index,
|
|
2381
|
+
label: this.getItemLabel(item),
|
|
2382
|
+
selected,
|
|
2383
|
+
renderer: this.renderer,
|
|
2384
|
+
focused: !this.loading && focusedIndex === index,
|
|
2385
|
+
});
|
|
2194
2386
|
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2387
|
+
el.id = `${this.__hostTagName}-item-${index}`;
|
|
2388
|
+
el.setAttribute('role', index !== undefined ? 'option' : false);
|
|
2389
|
+
el.setAttribute('aria-selected', selected.toString());
|
|
2390
|
+
el.setAttribute('aria-posinset', index + 1);
|
|
2391
|
+
el.setAttribute('aria-setsize', this.items.length);
|
|
2199
2392
|
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
return this.get(itemIdPath, item) === this.get(itemIdPath, selectedItem);
|
|
2206
|
-
}
|
|
2207
|
-
return item === selectedItem;
|
|
2208
|
-
}
|
|
2393
|
+
if (this.theme) {
|
|
2394
|
+
el.setAttribute('theme', this.theme);
|
|
2395
|
+
} else {
|
|
2396
|
+
el.removeAttribute('theme');
|
|
2397
|
+
}
|
|
2209
2398
|
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
this.__virtualizer.size = items.length;
|
|
2214
|
-
this.__virtualizer.flush();
|
|
2215
|
-
this.requestContentUpdate();
|
|
2399
|
+
if (item instanceof ComboBoxPlaceholder) {
|
|
2400
|
+
this.__requestItemByIndex(index);
|
|
2401
|
+
}
|
|
2216
2402
|
}
|
|
2217
|
-
}
|
|
2218
|
-
|
|
2219
|
-
/** @private */
|
|
2220
|
-
__loadingChanged() {
|
|
2221
|
-
this.requestContentUpdate();
|
|
2222
|
-
}
|
|
2223
2403
|
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2404
|
+
/** @private */
|
|
2405
|
+
__onItemClick(e) {
|
|
2406
|
+
this.dispatchEvent(new CustomEvent('selection-changed', { detail: { item: e.currentTarget.item } }));
|
|
2407
|
+
}
|
|
2228
2408
|
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2409
|
+
/**
|
|
2410
|
+
* We want to prevent the kinetic scrolling energy from being transferred from the overlay contents over to the parent.
|
|
2411
|
+
* Further improvement ideas: after the contents have been scrolled to the top or bottom and scrolling has stopped, it could allow
|
|
2412
|
+
* scrolling the parent similarly to touch scrolling.
|
|
2413
|
+
* @private
|
|
2414
|
+
*/
|
|
2415
|
+
__patchWheelOverScrolling() {
|
|
2416
|
+
this.$.selector.addEventListener('wheel', (e) => {
|
|
2417
|
+
const scrolledToTop = this.scrollTop === 0;
|
|
2418
|
+
const scrolledToBottom = this.scrollHeight - this.scrollTop - this.clientHeight <= 1;
|
|
2419
|
+
if (scrolledToTop && e.deltaY < 0) {
|
|
2420
|
+
e.preventDefault();
|
|
2421
|
+
} else if (scrolledToBottom && e.deltaY > 0) {
|
|
2422
|
+
e.preventDefault();
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2233
2425
|
}
|
|
2234
2426
|
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2427
|
+
/**
|
|
2428
|
+
* Dispatches an `index-requested` event for the given index to notify
|
|
2429
|
+
* the data provider that it should start loading the page containing the requested index.
|
|
2430
|
+
*
|
|
2431
|
+
* The event is dispatched asynchronously to prevent an immediate page request and therefore
|
|
2432
|
+
* a possible infinite recursion in case the data provider implements page request cancelation logic
|
|
2433
|
+
* by invoking data provider page callbacks with an empty array.
|
|
2434
|
+
* The infinite recursion may occur otherwise since invoking a data provider page callback with an empty array
|
|
2435
|
+
* triggers a synchronous scroller update and, if the callback corresponds to the currently visible page,
|
|
2436
|
+
* the scroller will synchronously request the page again which may lead to looping in the end.
|
|
2437
|
+
* That was the case for the Flow counterpart:
|
|
2438
|
+
* https://github.com/vaadin/flow-components/issues/3553#issuecomment-1239344828
|
|
2439
|
+
* @private
|
|
2440
|
+
*/
|
|
2441
|
+
__requestItemByIndex(index) {
|
|
2442
|
+
requestAnimationFrame(() => {
|
|
2443
|
+
this.dispatchEvent(
|
|
2444
|
+
new CustomEvent('index-requested', {
|
|
2445
|
+
detail: {
|
|
2446
|
+
index,
|
|
2447
|
+
currentScrollerPos: this._oldScrollerPosition,
|
|
2448
|
+
},
|
|
2449
|
+
}),
|
|
2450
|
+
);
|
|
2451
|
+
});
|
|
2239
2452
|
}
|
|
2240
|
-
}
|
|
2241
2453
|
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
this.
|
|
2454
|
+
/** @private */
|
|
2455
|
+
_visibleItemsCount() {
|
|
2456
|
+
// Ensure items are positioned
|
|
2457
|
+
this.__virtualizer.scrollToIndex(this.__virtualizer.firstVisibleIndex);
|
|
2458
|
+
const hasItems = this.__virtualizer.size > 0;
|
|
2459
|
+
return hasItems ? this.__virtualizer.lastVisibleIndex - this.__virtualizer.firstVisibleIndex + 1 : 0;
|
|
2246
2460
|
}
|
|
2247
|
-
}
|
|
2461
|
+
};
|
|
2248
2462
|
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
// Negative tabindex prevents the item content from being focused.
|
|
2255
|
-
item.tabIndex = '-1';
|
|
2256
|
-
item.style.width = '100%';
|
|
2257
|
-
return item;
|
|
2258
|
-
});
|
|
2259
|
-
}
|
|
2463
|
+
/**
|
|
2464
|
+
* @license
|
|
2465
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
2466
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2467
|
+
*/
|
|
2260
2468
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
focused: this.__isItemFocused(focusedIndex, index),
|
|
2274
|
-
});
|
|
2469
|
+
/**
|
|
2470
|
+
* An element used internally by `<vaadin-combo-box>`. Not intended to be used separately.
|
|
2471
|
+
*
|
|
2472
|
+
* @customElement
|
|
2473
|
+
* @extends HTMLElement
|
|
2474
|
+
* @mixes ComboBoxScrollerMixin
|
|
2475
|
+
* @private
|
|
2476
|
+
*/
|
|
2477
|
+
class ComboBoxScroller extends ComboBoxScrollerMixin(PolymerElement) {
|
|
2478
|
+
static get is() {
|
|
2479
|
+
return 'vaadin-combo-box-scroller';
|
|
2480
|
+
}
|
|
2275
2481
|
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2482
|
+
static get template() {
|
|
2483
|
+
return html`
|
|
2484
|
+
<style>
|
|
2485
|
+
:host {
|
|
2486
|
+
display: block;
|
|
2487
|
+
min-height: 1px;
|
|
2488
|
+
overflow: auto;
|
|
2281
2489
|
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
} else {
|
|
2285
|
-
el.removeAttribute('theme');
|
|
2286
|
-
}
|
|
2490
|
+
/* Fixes item background from getting on top of scrollbars on Safari */
|
|
2491
|
+
transform: translate3d(0, 0, 0);
|
|
2287
2492
|
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2493
|
+
/* Enable momentum scrolling on iOS */
|
|
2494
|
+
-webkit-overflow-scrolling: touch;
|
|
2292
2495
|
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
}
|
|
2496
|
+
/* Fixes scrollbar disappearing when 'Show scroll bars: Always' enabled in Safari */
|
|
2497
|
+
box-shadow: 0 0 0 white;
|
|
2498
|
+
}
|
|
2297
2499
|
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
} else if (scrolledToBottom && e.deltaY > 0) {
|
|
2310
|
-
e.preventDefault();
|
|
2311
|
-
}
|
|
2312
|
-
});
|
|
2500
|
+
#selector {
|
|
2501
|
+
border-width: var(--_vaadin-combo-box-items-container-border-width);
|
|
2502
|
+
border-style: var(--_vaadin-combo-box-items-container-border-style);
|
|
2503
|
+
border-color: var(--_vaadin-combo-box-items-container-border-color, transparent);
|
|
2504
|
+
position: relative;
|
|
2505
|
+
}
|
|
2506
|
+
</style>
|
|
2507
|
+
<div id="selector">
|
|
2508
|
+
<slot></slot>
|
|
2509
|
+
</div>
|
|
2510
|
+
`;
|
|
2313
2511
|
}
|
|
2512
|
+
}
|
|
2314
2513
|
|
|
2315
|
-
|
|
2316
|
-
if (this._cachedViewportTotalPaddingBottom === undefined) {
|
|
2317
|
-
const itemsStyle = window.getComputedStyle(this.$.selector);
|
|
2318
|
-
this._cachedViewportTotalPaddingBottom = [itemsStyle.paddingBottom, itemsStyle.borderBottomWidth]
|
|
2319
|
-
.map((v) => {
|
|
2320
|
-
return parseInt(v, 10);
|
|
2321
|
-
})
|
|
2322
|
-
.reduce((sum, v) => {
|
|
2323
|
-
return sum + v;
|
|
2324
|
-
});
|
|
2325
|
-
}
|
|
2514
|
+
defineCustomElement(ComboBoxScroller);
|
|
2326
2515
|
|
|
2327
|
-
|
|
2328
|
-
|
|
2516
|
+
/**
|
|
2517
|
+
* @license
|
|
2518
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
2519
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2520
|
+
*/
|
|
2329
2521
|
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
currentScrollerPos: this._oldScrollerPosition,
|
|
2350
|
-
},
|
|
2351
|
-
}),
|
|
2352
|
-
);
|
|
2353
|
-
});
|
|
2354
|
-
}
|
|
2522
|
+
/**
|
|
2523
|
+
* A mixin to provide `pattern` property.
|
|
2524
|
+
*
|
|
2525
|
+
* @polymerMixin
|
|
2526
|
+
* @mixes InputConstraintsMixin
|
|
2527
|
+
*/
|
|
2528
|
+
const PatternMixin = (superclass) =>
|
|
2529
|
+
class PatternMixinClass extends InputConstraintsMixin(superclass) {
|
|
2530
|
+
static get properties() {
|
|
2531
|
+
return {
|
|
2532
|
+
/**
|
|
2533
|
+
* A regular expression that the value is checked against.
|
|
2534
|
+
* The pattern must match the entire value, not just some subset.
|
|
2535
|
+
*/
|
|
2536
|
+
pattern: {
|
|
2537
|
+
type: String,
|
|
2538
|
+
},
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2355
2541
|
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
this.__virtualizer.scrollToIndex(this.__virtualizer.firstVisibleIndex);
|
|
2360
|
-
const hasItems = this.__virtualizer.size > 0;
|
|
2361
|
-
return hasItems ? this.__virtualizer.lastVisibleIndex - this.__virtualizer.firstVisibleIndex + 1 : 0;
|
|
2362
|
-
}
|
|
2363
|
-
}
|
|
2542
|
+
static get delegateAttrs() {
|
|
2543
|
+
return [...super.delegateAttrs, 'pattern'];
|
|
2544
|
+
}
|
|
2364
2545
|
|
|
2365
|
-
|
|
2546
|
+
static get constraints() {
|
|
2547
|
+
return [...super.constraints, 'pattern'];
|
|
2548
|
+
}
|
|
2549
|
+
};
|
|
2366
2550
|
|
|
2367
2551
|
/**
|
|
2368
2552
|
* @license
|
|
2369
|
-
* Copyright (c) 2015 -
|
|
2553
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
2370
2554
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2371
2555
|
*/
|
|
2372
2556
|
|
|
@@ -2676,20 +2860,18 @@ const ComboBoxDataProviderMixin = (superClass) =>
|
|
|
2676
2860
|
_flushPendingRequests(size) {
|
|
2677
2861
|
if (this._pendingRequests) {
|
|
2678
2862
|
const lastPage = Math.ceil(size / this.pageSize);
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
if (page >= lastPage) {
|
|
2683
|
-
this._pendingRequests[page]([], size);
|
|
2863
|
+
Object.entries(this._pendingRequests).forEach(([page, callback]) => {
|
|
2864
|
+
if (parseInt(page) >= lastPage) {
|
|
2865
|
+
callback([], size);
|
|
2684
2866
|
}
|
|
2685
|
-
}
|
|
2867
|
+
});
|
|
2686
2868
|
}
|
|
2687
2869
|
}
|
|
2688
2870
|
};
|
|
2689
2871
|
|
|
2690
2872
|
/**
|
|
2691
2873
|
* @license
|
|
2692
|
-
* Copyright (c) 2021 -
|
|
2874
|
+
* Copyright (c) 2021 - 2023 Vaadin Ltd.
|
|
2693
2875
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2694
2876
|
*/
|
|
2695
2877
|
|
|
@@ -2714,7 +2896,7 @@ function processTemplates(component) {
|
|
|
2714
2896
|
|
|
2715
2897
|
/**
|
|
2716
2898
|
* @license
|
|
2717
|
-
* Copyright (c) 2015 -
|
|
2899
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
2718
2900
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
2719
2901
|
*/
|
|
2720
2902
|
|
|
@@ -2748,10 +2930,19 @@ function findItemIndex(items, callback) {
|
|
|
2748
2930
|
|
|
2749
2931
|
/**
|
|
2750
2932
|
* @polymerMixin
|
|
2933
|
+
* @mixes ControllerMixin
|
|
2934
|
+
* @mixes ValidateMixin
|
|
2935
|
+
* @mixes DisabledMixin
|
|
2936
|
+
* @mixes InputMixin
|
|
2937
|
+
* @mixes KeyboardMixin
|
|
2938
|
+
* @mixes FocusMixin
|
|
2939
|
+
* @mixes OverlayClassMixin
|
|
2751
2940
|
* @param {function(new:HTMLElement)} subclass
|
|
2752
2941
|
*/
|
|
2753
2942
|
const ComboBoxMixin = (subclass) =>
|
|
2754
|
-
class
|
|
2943
|
+
class ComboBoxMixinClass extends OverlayClassMixin(
|
|
2944
|
+
ControllerMixin(ValidateMixin(FocusMixin(KeyboardMixin(InputMixin(DisabledMixin(subclass)))))),
|
|
2945
|
+
) {
|
|
2755
2946
|
static get properties() {
|
|
2756
2947
|
return {
|
|
2757
2948
|
/**
|
|
@@ -2926,6 +3117,14 @@ const ComboBoxMixin = (subclass) =>
|
|
|
2926
3117
|
observer: '_toggleElementChanged',
|
|
2927
3118
|
},
|
|
2928
3119
|
|
|
3120
|
+
/**
|
|
3121
|
+
* Set of items to be rendered in the dropdown.
|
|
3122
|
+
* @protected
|
|
3123
|
+
*/
|
|
3124
|
+
_dropdownItems: {
|
|
3125
|
+
type: Array,
|
|
3126
|
+
},
|
|
3127
|
+
|
|
2929
3128
|
/** @private */
|
|
2930
3129
|
_closeOnBlurIsPrevented: Boolean,
|
|
2931
3130
|
|
|
@@ -2943,14 +3142,13 @@ const ComboBoxMixin = (subclass) =>
|
|
|
2943
3142
|
static get observers() {
|
|
2944
3143
|
return [
|
|
2945
3144
|
'_selectedItemChanged(selectedItem, itemValuePath, itemLabelPath)',
|
|
2946
|
-
'_openedOrItemsChanged(opened,
|
|
2947
|
-
'_updateScroller(_scroller,
|
|
3145
|
+
'_openedOrItemsChanged(opened, _dropdownItems, loading)',
|
|
3146
|
+
'_updateScroller(_scroller, _dropdownItems, opened, loading, selectedItem, itemIdPath, _focusedIndex, renderer, theme)',
|
|
2948
3147
|
];
|
|
2949
3148
|
}
|
|
2950
3149
|
|
|
2951
3150
|
constructor() {
|
|
2952
3151
|
super();
|
|
2953
|
-
this._boundOnFocusout = this._onFocusout.bind(this);
|
|
2954
3152
|
this._boundOverlaySelectedItemChanged = this._overlaySelectedItemChanged.bind(this);
|
|
2955
3153
|
this._boundOnClearButtonMouseDown = this.__onClearButtonMouseDown.bind(this);
|
|
2956
3154
|
this._boundOnClick = this._onClick.bind(this);
|
|
@@ -2967,24 +3165,6 @@ const ComboBoxMixin = (subclass) =>
|
|
|
2967
3165
|
return 'vaadin-combo-box';
|
|
2968
3166
|
}
|
|
2969
3167
|
|
|
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
3168
|
/**
|
|
2989
3169
|
* Get a reference to the native `<input>` element.
|
|
2990
3170
|
* Override to provide a custom input.
|
|
@@ -3035,8 +3215,6 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3035
3215
|
this._initOverlay();
|
|
3036
3216
|
this._initScroller();
|
|
3037
3217
|
|
|
3038
|
-
this.addEventListener('focusout', this._boundOnFocusout);
|
|
3039
|
-
|
|
3040
3218
|
this._lastCommittedValue = this.value;
|
|
3041
3219
|
|
|
3042
3220
|
this.addEventListener('click', this._boundOnClick);
|
|
@@ -3044,7 +3222,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3044
3222
|
|
|
3045
3223
|
const bringToFrontListener = () => {
|
|
3046
3224
|
requestAnimationFrame(() => {
|
|
3047
|
-
this
|
|
3225
|
+
this._overlayElement.bringToFront();
|
|
3048
3226
|
});
|
|
3049
3227
|
};
|
|
3050
3228
|
|
|
@@ -3135,6 +3313,8 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3135
3313
|
overlay.addEventListener('opened-changed', (e) => {
|
|
3136
3314
|
this._overlayOpened = e.detail.value;
|
|
3137
3315
|
});
|
|
3316
|
+
|
|
3317
|
+
this._overlayElement = overlay;
|
|
3138
3318
|
}
|
|
3139
3319
|
|
|
3140
3320
|
/**
|
|
@@ -3146,7 +3326,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3146
3326
|
_initScroller(host) {
|
|
3147
3327
|
const scrollerTag = `${this._tagNamePrefix}-scroller`;
|
|
3148
3328
|
|
|
3149
|
-
const overlay = this
|
|
3329
|
+
const overlay = this._overlayElement;
|
|
3150
3330
|
|
|
3151
3331
|
overlay.renderer = (root) => {
|
|
3152
3332
|
if (!root.firstChild) {
|
|
@@ -3159,7 +3339,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3159
3339
|
|
|
3160
3340
|
const scroller = overlay.querySelector(scrollerTag);
|
|
3161
3341
|
|
|
3162
|
-
scroller.
|
|
3342
|
+
scroller.owner = host || this;
|
|
3163
3343
|
scroller.getItemLabel = this._getItemLabel.bind(this);
|
|
3164
3344
|
scroller.addEventListener('selection-changed', this._boundOverlaySelectedItemChanged);
|
|
3165
3345
|
|
|
@@ -3202,7 +3382,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3202
3382
|
this.dispatchEvent(new CustomEvent('vaadin-combo-box-dropdown-opened', { bubbles: true, composed: true }));
|
|
3203
3383
|
|
|
3204
3384
|
this._onOpened();
|
|
3205
|
-
} else if (wasOpened && this.
|
|
3385
|
+
} else if (wasOpened && this._dropdownItems && this._dropdownItems.length) {
|
|
3206
3386
|
this.close();
|
|
3207
3387
|
|
|
3208
3388
|
this.dispatchEvent(new CustomEvent('vaadin-combo-box-dropdown-closed', { bubbles: true, composed: true }));
|
|
@@ -3254,7 +3434,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3254
3434
|
}
|
|
3255
3435
|
}
|
|
3256
3436
|
|
|
3257
|
-
this
|
|
3437
|
+
this._overlayElement.restoreFocusOnClose = true;
|
|
3258
3438
|
} else {
|
|
3259
3439
|
this._onClosed();
|
|
3260
3440
|
if (this._openedWithFocusRing && this._isInputFocused()) {
|
|
@@ -3288,13 +3468,19 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3288
3468
|
return event.composedPath()[0] === this.clearElement;
|
|
3289
3469
|
}
|
|
3290
3470
|
|
|
3471
|
+
/** @private */
|
|
3472
|
+
__onClearButtonMouseDown(event) {
|
|
3473
|
+
event.preventDefault(); // Prevent native focusout event
|
|
3474
|
+
this.inputElement.focus();
|
|
3475
|
+
}
|
|
3476
|
+
|
|
3291
3477
|
/**
|
|
3292
3478
|
* @param {Event} event
|
|
3293
3479
|
* @protected
|
|
3294
3480
|
*/
|
|
3295
|
-
|
|
3481
|
+
_onClearButtonClick(event) {
|
|
3296
3482
|
event.preventDefault();
|
|
3297
|
-
this.
|
|
3483
|
+
this._onClearAction();
|
|
3298
3484
|
|
|
3299
3485
|
// De-select dropdown item
|
|
3300
3486
|
if (this.opened) {
|
|
@@ -3330,15 +3516,13 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3330
3516
|
}
|
|
3331
3517
|
|
|
3332
3518
|
/** @private */
|
|
3333
|
-
_onClick(
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
if (
|
|
3337
|
-
this.
|
|
3338
|
-
} else if (path.indexOf(this._toggleElement) > -1) {
|
|
3339
|
-
this._onToggleButtonClick(e);
|
|
3519
|
+
_onClick(event) {
|
|
3520
|
+
if (this._isClearButton(event)) {
|
|
3521
|
+
this._onClearButtonClick(event);
|
|
3522
|
+
} else if (event.composedPath().includes(this._toggleElement)) {
|
|
3523
|
+
this._onToggleButtonClick(event);
|
|
3340
3524
|
} else {
|
|
3341
|
-
this._onHostClick(
|
|
3525
|
+
this._onHostClick(event);
|
|
3342
3526
|
}
|
|
3343
3527
|
}
|
|
3344
3528
|
|
|
@@ -3353,7 +3537,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3353
3537
|
super._onKeyDown(e);
|
|
3354
3538
|
|
|
3355
3539
|
if (e.key === 'Tab') {
|
|
3356
|
-
this
|
|
3540
|
+
this._overlayElement.restoreFocusOnClose = false;
|
|
3357
3541
|
} else if (e.key === 'ArrowDown') {
|
|
3358
3542
|
this._onArrowDown();
|
|
3359
3543
|
|
|
@@ -3369,7 +3553,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3369
3553
|
|
|
3370
3554
|
/** @private */
|
|
3371
3555
|
_getItemLabel(item) {
|
|
3372
|
-
let label = item && this.itemLabelPath ?
|
|
3556
|
+
let label = item && this.itemLabelPath ? get(this.itemLabelPath, item) : undefined;
|
|
3373
3557
|
if (label === undefined || label === null) {
|
|
3374
3558
|
label = item ? item.toString() : '';
|
|
3375
3559
|
}
|
|
@@ -3378,7 +3562,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3378
3562
|
|
|
3379
3563
|
/** @private */
|
|
3380
3564
|
_getItemValue(item) {
|
|
3381
|
-
let value = item && this.itemValuePath ?
|
|
3565
|
+
let value = item && this.itemValuePath ? get(this.itemValuePath, item) : undefined;
|
|
3382
3566
|
if (value === undefined) {
|
|
3383
3567
|
value = item ? item.toString() : '';
|
|
3384
3568
|
}
|
|
@@ -3388,7 +3572,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3388
3572
|
/** @private */
|
|
3389
3573
|
_onArrowDown() {
|
|
3390
3574
|
if (this.opened) {
|
|
3391
|
-
const items = this.
|
|
3575
|
+
const items = this._dropdownItems;
|
|
3392
3576
|
if (items) {
|
|
3393
3577
|
this._focusedIndex = Math.min(items.length - 1, this._focusedIndex + 1);
|
|
3394
3578
|
this._prefillFocusedItemLabel();
|
|
@@ -3404,7 +3588,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3404
3588
|
if (this._focusedIndex > -1) {
|
|
3405
3589
|
this._focusedIndex = Math.max(0, this._focusedIndex - 1);
|
|
3406
3590
|
} else {
|
|
3407
|
-
const items = this.
|
|
3591
|
+
const items = this._dropdownItems;
|
|
3408
3592
|
if (items) {
|
|
3409
3593
|
this._focusedIndex = items.length - 1;
|
|
3410
3594
|
}
|
|
@@ -3419,7 +3603,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3419
3603
|
/** @private */
|
|
3420
3604
|
_prefillFocusedItemLabel() {
|
|
3421
3605
|
if (this._focusedIndex > -1) {
|
|
3422
|
-
const focusedItem = this.
|
|
3606
|
+
const focusedItem = this._dropdownItems[this._focusedIndex];
|
|
3423
3607
|
this._inputElementValue = this._getItemLabel(focusedItem);
|
|
3424
3608
|
this._markAllSelectionRange();
|
|
3425
3609
|
}
|
|
@@ -3516,7 +3700,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3516
3700
|
} else if (this.clearButtonVisible && !this.opened && !!this.value) {
|
|
3517
3701
|
e.stopPropagation();
|
|
3518
3702
|
// The clear button is visible and the overlay is closed, so clear the value.
|
|
3519
|
-
this.
|
|
3703
|
+
this._onClearAction();
|
|
3520
3704
|
}
|
|
3521
3705
|
} else if (this.opened) {
|
|
3522
3706
|
// Auto-open is enabled
|
|
@@ -3534,7 +3718,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3534
3718
|
} else if (this.clearButtonVisible && !!this.value) {
|
|
3535
3719
|
e.stopPropagation();
|
|
3536
3720
|
// The clear button is visible and the overlay is closed, so clear the value.
|
|
3537
|
-
this.
|
|
3721
|
+
this._onClearAction();
|
|
3538
3722
|
}
|
|
3539
3723
|
}
|
|
3540
3724
|
|
|
@@ -3556,7 +3740,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3556
3740
|
* Clears the current value.
|
|
3557
3741
|
* @protected
|
|
3558
3742
|
*/
|
|
3559
|
-
|
|
3743
|
+
_onClearAction() {
|
|
3560
3744
|
this.selectedItem = null;
|
|
3561
3745
|
|
|
3562
3746
|
if (this.allowCustomValue) {
|
|
@@ -3592,7 +3776,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3592
3776
|
/** @private */
|
|
3593
3777
|
_commitValue() {
|
|
3594
3778
|
if (this._focusedIndex > -1) {
|
|
3595
|
-
const focusedItem = this.
|
|
3779
|
+
const focusedItem = this._dropdownItems[this._focusedIndex];
|
|
3596
3780
|
if (this.selectedItem !== focusedItem) {
|
|
3597
3781
|
this.selectedItem = focusedItem;
|
|
3598
3782
|
}
|
|
@@ -3607,7 +3791,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3607
3791
|
}
|
|
3608
3792
|
} else {
|
|
3609
3793
|
// Try to find an item which label matches the input value.
|
|
3610
|
-
const items = [...(this.
|
|
3794
|
+
const items = [this.selectedItem, ...(this._dropdownItems || [])];
|
|
3611
3795
|
const itemMatchingInputValue = items[this.__getItemIndexByLabel(items, this._inputElementValue)];
|
|
3612
3796
|
|
|
3613
3797
|
if (
|
|
@@ -3648,14 +3832,6 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3648
3832
|
this.filter = '';
|
|
3649
3833
|
}
|
|
3650
3834
|
|
|
3651
|
-
/**
|
|
3652
|
-
* @return {string}
|
|
3653
|
-
* @protected
|
|
3654
|
-
*/
|
|
3655
|
-
get _propertyForValue() {
|
|
3656
|
-
return 'value';
|
|
3657
|
-
}
|
|
3658
|
-
|
|
3659
3835
|
/**
|
|
3660
3836
|
* Override an event listener from `InputMixin`.
|
|
3661
3837
|
* @param {!Event} event
|
|
@@ -3802,6 +3978,12 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3802
3978
|
|
|
3803
3979
|
/** @private */
|
|
3804
3980
|
_detectAndDispatchChange() {
|
|
3981
|
+
// Do not validate when focusout is caused by document
|
|
3982
|
+
// losing focus, which happens on browser tab switch.
|
|
3983
|
+
if (document.hasFocus()) {
|
|
3984
|
+
this.validate();
|
|
3985
|
+
}
|
|
3986
|
+
|
|
3805
3987
|
if (this.value !== this._lastCommittedValue) {
|
|
3806
3988
|
this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
|
|
3807
3989
|
this._lastCommittedValue = this.value;
|
|
@@ -3824,6 +4006,8 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3824
4006
|
|
|
3825
4007
|
/** @private */
|
|
3826
4008
|
_filteredItemsChanged(filteredItems, oldFilteredItems) {
|
|
4009
|
+
this._setDropdownItems(filteredItems);
|
|
4010
|
+
|
|
3827
4011
|
// Store the currently focused item if any. The focused index preserves
|
|
3828
4012
|
// in the case when more filtered items are loading but it is reset
|
|
3829
4013
|
// when the user types in a filter query.
|
|
@@ -3884,6 +4068,16 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3884
4068
|
}
|
|
3885
4069
|
}
|
|
3886
4070
|
|
|
4071
|
+
/**
|
|
4072
|
+
* Provide items to be rendered in the dropdown.
|
|
4073
|
+
* Override this method to show custom items.
|
|
4074
|
+
*
|
|
4075
|
+
* @protected
|
|
4076
|
+
*/
|
|
4077
|
+
_setDropdownItems(items) {
|
|
4078
|
+
this._dropdownItems = items;
|
|
4079
|
+
}
|
|
4080
|
+
|
|
3887
4081
|
/** @private */
|
|
3888
4082
|
_getItemElements() {
|
|
3889
4083
|
return Array.from(this._scroller.querySelectorAll(`${this._tagNamePrefix}-item`));
|
|
@@ -3944,26 +4138,18 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3944
4138
|
}
|
|
3945
4139
|
}
|
|
3946
4140
|
|
|
3947
|
-
/**
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
if (event.relatedTarget && event.relatedTarget.localName === `${this._tagNamePrefix}-item`) {
|
|
3958
|
-
return;
|
|
3959
|
-
}
|
|
4141
|
+
/**
|
|
4142
|
+
* Override method inherited from `FocusMixin`
|
|
4143
|
+
* to close the overlay on blur and commit the value.
|
|
4144
|
+
*
|
|
4145
|
+
* @param {boolean} focused
|
|
4146
|
+
* @protected
|
|
4147
|
+
* @override
|
|
4148
|
+
*/
|
|
4149
|
+
_setFocused(focused) {
|
|
4150
|
+
super._setFocused(focused);
|
|
3960
4151
|
|
|
3961
|
-
|
|
3962
|
-
if (event.relatedTarget === this.$.overlay) {
|
|
3963
|
-
event.composedPath()[0].focus();
|
|
3964
|
-
return;
|
|
3965
|
-
}
|
|
3966
|
-
if (!this.readonly && !this._closeOnBlurIsPrevented) {
|
|
4152
|
+
if (!focused && !this.readonly && !this._closeOnBlurIsPrevented) {
|
|
3967
4153
|
// User's logic in `custom-value-set` event listener might cause input to blur,
|
|
3968
4154
|
// which will result in attempting to commit the same custom value once again.
|
|
3969
4155
|
if (!this.opened && this.allowCustomValue && this._inputElementValue === this._lastCustomValue) {
|
|
@@ -3975,6 +4161,32 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3975
4161
|
}
|
|
3976
4162
|
}
|
|
3977
4163
|
|
|
4164
|
+
/**
|
|
4165
|
+
* Override method inherited from `FocusMixin` to not remove focused
|
|
4166
|
+
* state when focus moves to the overlay.
|
|
4167
|
+
*
|
|
4168
|
+
* @param {FocusEvent} event
|
|
4169
|
+
* @return {boolean}
|
|
4170
|
+
* @protected
|
|
4171
|
+
* @override
|
|
4172
|
+
*/
|
|
4173
|
+
_shouldRemoveFocus(event) {
|
|
4174
|
+
// VoiceOver on iOS fires `focusout` event when moving focus to the item in the dropdown.
|
|
4175
|
+
// Do not focus the input in this case, because it would break announcement for the item.
|
|
4176
|
+
if (event.relatedTarget && event.relatedTarget.localName === `${this._tagNamePrefix}-item`) {
|
|
4177
|
+
return false;
|
|
4178
|
+
}
|
|
4179
|
+
|
|
4180
|
+
// Do not blur when focus moves to the overlay
|
|
4181
|
+
// Also, fixes the problem with `focusout` happening when clicking on the scroll bar on Edge
|
|
4182
|
+
if (event.relatedTarget === this._overlayElement) {
|
|
4183
|
+
event.composedPath()[0].focus();
|
|
4184
|
+
return false;
|
|
4185
|
+
}
|
|
4186
|
+
|
|
4187
|
+
return true;
|
|
4188
|
+
}
|
|
4189
|
+
|
|
3978
4190
|
/** @private */
|
|
3979
4191
|
_onTouchend(event) {
|
|
3980
4192
|
if (!this.clearElement || event.composedPath()[0] !== this.clearElement) {
|
|
@@ -3982,7 +4194,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
3982
4194
|
}
|
|
3983
4195
|
|
|
3984
4196
|
event.preventDefault();
|
|
3985
|
-
this.
|
|
4197
|
+
this._onClearAction();
|
|
3986
4198
|
}
|
|
3987
4199
|
|
|
3988
4200
|
/**
|
|
@@ -4028,7 +4240,7 @@ const ComboBoxMixin = (subclass) =>
|
|
|
4028
4240
|
|
|
4029
4241
|
/**
|
|
4030
4242
|
* @license
|
|
4031
|
-
* Copyright (c) 2015 -
|
|
4243
|
+
* Copyright (c) 2015 - 2023 Vaadin Ltd.
|
|
4032
4244
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
4033
4245
|
*/
|
|
4034
4246
|
|
|
@@ -4148,7 +4360,7 @@ registerStyles('vaadin-combo-box', inputFieldShared$1, { moduleId: 'vaadin-combo
|
|
|
4148
4360
|
* Note: the `theme` attribute value set on `<vaadin-combo-box>` is
|
|
4149
4361
|
* propagated to the internal components listed above.
|
|
4150
4362
|
*
|
|
4151
|
-
* See [Styling Components](https://vaadin.com/docs/latest/styling/
|
|
4363
|
+
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
4152
4364
|
*
|
|
4153
4365
|
* @fires {Event} change - Fired when the user commits a value change.
|
|
4154
4366
|
* @fires {CustomEvent} custom-value-set - Fired when the user sets a custom value.
|
|
@@ -4159,6 +4371,7 @@ registerStyles('vaadin-combo-box', inputFieldShared$1, { moduleId: 'vaadin-combo
|
|
|
4159
4371
|
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
|
|
4160
4372
|
* @fires {CustomEvent} validated - Fired whenever the field is validated.
|
|
4161
4373
|
*
|
|
4374
|
+
* @customElement
|
|
4162
4375
|
* @extends HTMLElement
|
|
4163
4376
|
* @mixes ElementMixin
|
|
4164
4377
|
* @mixes ThemableMixin
|
|
@@ -4261,6 +4474,7 @@ class ComboBox extends ComboBoxDataProviderMixin(
|
|
|
4261
4474
|
this._tooltipController = new TooltipController(this);
|
|
4262
4475
|
this.addController(this._tooltipController);
|
|
4263
4476
|
this._tooltipController.setPosition('top');
|
|
4477
|
+
this._tooltipController.setAriaTarget(this.inputElement);
|
|
4264
4478
|
this._tooltipController.setShouldShow((target) => !target.opened);
|
|
4265
4479
|
|
|
4266
4480
|
this._positionTarget = this.shadowRoot.querySelector('[part="input-field"]');
|
|
@@ -4268,48 +4482,17 @@ class ComboBox extends ComboBoxDataProviderMixin(
|
|
|
4268
4482
|
}
|
|
4269
4483
|
|
|
4270
4484
|
/**
|
|
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.
|
|
4485
|
+
* Override the method from `InputControlMixin`
|
|
4486
|
+
* to stop event propagation to prevent `ComboBoxMixin`
|
|
4487
|
+
* from handling this click event also on its own.
|
|
4488
|
+
*
|
|
4305
4489
|
* @param {Event} event
|
|
4306
4490
|
* @protected
|
|
4307
4491
|
* @override
|
|
4308
4492
|
*/
|
|
4309
4493
|
_onClearButtonClick(event) {
|
|
4310
4494
|
event.stopPropagation();
|
|
4311
|
-
|
|
4312
|
-
this._handleClearButtonClick(event);
|
|
4495
|
+
super._onClearButtonClick(event);
|
|
4313
4496
|
}
|
|
4314
4497
|
|
|
4315
4498
|
/**
|
|
@@ -4326,4 +4509,4 @@ class ComboBox extends ComboBoxDataProviderMixin(
|
|
|
4326
4509
|
}
|
|
4327
4510
|
}
|
|
4328
4511
|
|
|
4329
|
-
|
|
4512
|
+
defineCustomElement(ComboBox);
|