@vscode-elements/elements 1.16.1 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/custom-elements.json +1178 -565
  2. package/dist/bundled.js +731 -638
  3. package/dist/includes/VscElement.d.ts.map +1 -1
  4. package/dist/includes/VscElement.js +3 -1
  5. package/dist/includes/VscElement.js.map +1 -1
  6. package/dist/includes/vscode-select/OptionListController.d.ts +61 -0
  7. package/dist/includes/vscode-select/OptionListController.d.ts.map +1 -0
  8. package/dist/includes/vscode-select/OptionListController.js +373 -0
  9. package/dist/includes/vscode-select/OptionListController.js.map +1 -0
  10. package/dist/includes/vscode-select/helpers.d.ts +2 -2
  11. package/dist/includes/vscode-select/helpers.js.map +1 -1
  12. package/dist/includes/vscode-select/styles.d.ts.map +1 -1
  13. package/dist/includes/vscode-select/styles.js +28 -26
  14. package/dist/includes/vscode-select/styles.js.map +1 -1
  15. package/dist/includes/vscode-select/template-elements.d.ts +1 -0
  16. package/dist/includes/vscode-select/template-elements.d.ts.map +1 -1
  17. package/dist/includes/vscode-select/template-elements.js +14 -1
  18. package/dist/includes/vscode-select/template-elements.js.map +1 -1
  19. package/dist/includes/vscode-select/types.d.ts +11 -7
  20. package/dist/includes/vscode-select/types.d.ts.map +1 -1
  21. package/dist/includes/vscode-select/types.js.map +1 -1
  22. package/dist/includes/vscode-select/vscode-select-base.d.ts +26 -31
  23. package/dist/includes/vscode-select/vscode-select-base.d.ts.map +1 -1
  24. package/dist/includes/vscode-select/vscode-select-base.js +228 -325
  25. package/dist/includes/vscode-select/vscode-select-base.js.map +1 -1
  26. package/dist/vscode-button/vscode-button.styles.js +1 -1
  27. package/dist/vscode-button/vscode-button.styles.js.map +1 -1
  28. package/dist/vscode-button-group/vscode-button-group.styles.d.ts.map +1 -1
  29. package/dist/vscode-button-group/vscode-button-group.styles.js +2 -0
  30. package/dist/vscode-button-group/vscode-button-group.styles.js.map +1 -1
  31. package/dist/vscode-icon/vscode-icon.d.ts.map +1 -1
  32. package/dist/vscode-icon/vscode-icon.js +1 -0
  33. package/dist/vscode-icon/vscode-icon.js.map +1 -1
  34. package/dist/vscode-label/vscode-label.d.ts.map +1 -1
  35. package/dist/vscode-label/vscode-label.js +9 -7
  36. package/dist/vscode-label/vscode-label.js.map +1 -1
  37. package/dist/vscode-multi-select/vscode-multi-select.d.ts +9 -1
  38. package/dist/vscode-multi-select/vscode-multi-select.d.ts.map +1 -1
  39. package/dist/vscode-multi-select/vscode-multi-select.js +126 -66
  40. package/dist/vscode-multi-select/vscode-multi-select.js.map +1 -1
  41. package/dist/vscode-scrollable/vscode-scrollable.d.ts +57 -13
  42. package/dist/vscode-scrollable/vscode-scrollable.d.ts.map +1 -1
  43. package/dist/vscode-scrollable/vscode-scrollable.js +187 -82
  44. package/dist/vscode-scrollable/vscode-scrollable.js.map +1 -1
  45. package/dist/vscode-single-select/vscode-single-select.d.ts +4 -4
  46. package/dist/vscode-single-select/vscode-single-select.d.ts.map +1 -1
  47. package/dist/vscode-single-select/vscode-single-select.js +140 -74
  48. package/dist/vscode-single-select/vscode-single-select.js.map +1 -1
  49. package/package.json +7 -5
  50. package/vscode.css-custom-data.json +1 -5
  51. package/vscode.html-custom-data.json +51 -5
@@ -5,20 +5,34 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { html, render, nothing } from 'lit';
8
- import { property, query, queryAssignedElements, state } from 'lit/decorators.js';
8
+ import { property, queryAssignedElements, state } from 'lit/decorators.js';
9
9
  import { classMap } from 'lit/directives/class-map.js';
10
+ import { ifDefined } from 'lit/directives/if-defined.js';
10
11
  import { repeat } from 'lit/directives/repeat.js';
12
+ import { when } from 'lit/directives/when.js';
11
13
  import '../../vscode-button/index.js';
12
14
  import '../../vscode-option/index.js';
13
- import { filterOptionsByPattern, findNextSelectableOptionIndex, findPrevSelectableOptionIndex, highlightRanges, } from './helpers.js';
15
+ import { stylePropertyMap } from '../style-property-map.js';
14
16
  import { VscElement } from '../VscElement.js';
15
- import { chevronDownIcon } from './template-elements.js';
16
- const VISIBLE_OPTS = 10;
17
- const OPT_HEIGHT = 22;
17
+ import { filterOptionsByPattern, highlightRanges } from './helpers.js';
18
+ import { OptionListController } from './OptionListController.js';
19
+ import { checkIcon } from './template-elements.js';
20
+ import '../../vscode-scrollable/vscode-scrollable.js';
21
+ export const VISIBLE_OPTS = 10;
22
+ export const OPT_HEIGHT = 22;
18
23
  /**
19
24
  * @cssprop --dropdown-z-index - workaround for dropdown z-index issues
20
25
  */
21
26
  export class VscodeSelectBase extends VscElement {
27
+ /**
28
+ * Options can be filtered by typing into a text input field.
29
+ */
30
+ set combobox(enabled) {
31
+ this._opts.comboboxMode = enabled;
32
+ }
33
+ get combobox() {
34
+ return this._opts.comboboxMode;
35
+ }
22
36
  /**
23
37
  * The element cannot be used and is not focusable.
24
38
  */
@@ -55,26 +69,31 @@ export class VscodeSelectBase extends VscElement {
55
69
  'startsWith',
56
70
  'startsWithPerTerm',
57
71
  ];
72
+ let fm;
58
73
  if (validValues.includes(val)) {
59
- this._filter = val;
74
+ // this._filter = val as FilterMethod;
75
+ fm = val;
60
76
  }
61
77
  else {
62
- this._filter = 'fuzzy';
78
+ // this._filter = 'fuzzy';
79
+ // eslint-disable-next-line no-console
63
80
  console.warn(`[VSCode Webview Elements] Invalid filter: "${val}", fallback to default. Valid values are: "contains", "fuzzy", "startsWith", "startsWithPerm".`, this);
81
+ fm = 'fuzzy';
64
82
  }
83
+ this._opts.filterMethod = fm;
65
84
  }
66
85
  get filter() {
67
- return this._filter;
86
+ return this._opts.filterMethod;
68
87
  }
69
88
  /**
70
89
  * @attr [options=[]]
71
90
  * @type {Option[]}
72
91
  */
73
92
  set options(opts) {
74
- this._options = opts.map((op, index) => ({ ...op, index }));
93
+ this._opts.populate(opts);
75
94
  }
76
95
  get options() {
77
- return this._options.map(({ label, value, description, selected, disabled }) => ({
96
+ return this._opts.options.map(({ label, value, description, selected, disabled }) => ({
78
97
  label,
79
98
  value,
80
99
  description,
@@ -84,13 +103,12 @@ export class VscodeSelectBase extends VscElement {
84
103
  }
85
104
  constructor() {
86
105
  super();
87
- /** @internal */
88
- this.ariaExpanded = 'false';
89
106
  this.creatable = false;
90
107
  /**
91
- * Options can be filtered by typing into a text input field.
108
+ * Accessible label for screen readers. When a `<vscode-label>` is connected
109
+ * to the component, it will be filled automatically.
92
110
  */
93
- this.combobox = false;
111
+ this.label = '';
94
112
  /**
95
113
  * Sets the invalid state manually.
96
114
  */
@@ -107,29 +125,17 @@ export class VscodeSelectBase extends VscElement {
107
125
  * Position of the options list when visible.
108
126
  */
109
127
  this.position = 'below';
110
- /** @internal */
111
- this.tabIndex = 0;
128
+ this._opts = new OptionListController(this);
112
129
  this._firstUpdateCompleted = false;
113
- this._activeIndex = -1;
114
130
  this._currentDescription = '';
115
131
  this._filter = 'fuzzy';
116
- this._filterPattern = '';
117
- this._selectedIndex = -1;
118
132
  this._selectedIndexes = [];
119
133
  this._options = [];
120
134
  this._value = '';
121
135
  this._values = [];
122
- this._listScrollTop = 0;
123
136
  this._isPlaceholderOptionActive = false;
124
137
  this._isBeingFiltered = false;
125
- /** @internal */
126
- this._multiple = false;
127
- /**
128
- * @internal
129
- * Quick-searchable map for searching a value in the options list.
130
- * Keys are the options values, values are the option indexes.
131
- */
132
- this._valueOptionIndexMap = {};
138
+ this._optionListScrollPos = 0;
133
139
  this._isHoverForbidden = false;
134
140
  this._disabled = false;
135
141
  this._originalTabIndex = undefined;
@@ -137,14 +143,16 @@ export class VscodeSelectBase extends VscElement {
137
143
  const path = event.composedPath();
138
144
  const found = path.findIndex((et) => et === this);
139
145
  if (found === -1) {
140
- this._toggleDropdown(false);
141
- window.removeEventListener('click', this._onClickOutside);
146
+ this.open = false;
142
147
  }
143
148
  };
144
149
  this._onMouseMove = () => {
145
150
  this._isHoverForbidden = false;
146
151
  window.removeEventListener('mousemove', this._onMouseMove);
147
152
  };
153
+ this._onOptionListScroll = (ev) => {
154
+ this._optionListScrollPos = ev.detail;
155
+ };
148
156
  this._onComponentKeyDown = (event) => {
149
157
  if ([' ', 'ArrowUp', 'ArrowDown', 'Escape'].includes(event.key)) {
150
158
  event.stopPropagation();
@@ -157,7 +165,7 @@ export class VscodeSelectBase extends VscElement {
157
165
  this._onSpaceKeyDown();
158
166
  }
159
167
  if (event.key === 'Escape') {
160
- this._toggleDropdown(false);
168
+ this._onEscapeKeyDown();
161
169
  }
162
170
  if (event.key === 'ArrowUp') {
163
171
  this._onArrowUpKeyDown();
@@ -183,6 +191,7 @@ export class VscodeSelectBase extends VscElement {
183
191
  this.addEventListener('keydown', this._onComponentKeyDown);
184
192
  this.addEventListener('focus', this._onComponentFocus);
185
193
  this.addEventListener('blur', this._onComponentBlur);
194
+ this._setAutoFocus();
186
195
  }
187
196
  disconnectedCallback() {
188
197
  super.disconnectedCallback();
@@ -198,32 +207,59 @@ export class VscodeSelectBase extends VscElement {
198
207
  this._manageRequired();
199
208
  }
200
209
  }
210
+ update(changedProperties) {
211
+ super.update(changedProperties);
212
+ if (changedProperties.has('open')) {
213
+ if (this.open) {
214
+ this._opts.activateDefault();
215
+ this._scrollActiveElementToTop();
216
+ window.addEventListener('click', this._onClickOutside);
217
+ }
218
+ else {
219
+ window.removeEventListener('click', this._onClickOutside);
220
+ }
221
+ }
222
+ }
201
223
  get _filteredOptions() {
202
- if (!this.combobox || this._filterPattern === '') {
224
+ if (!this.combobox || this._opts.filterPattern === '') {
203
225
  return this._options;
204
226
  }
205
- return filterOptionsByPattern(this._options, this._filterPattern, this._filter);
227
+ return filterOptionsByPattern(this._options, this._opts.filterPattern, this._filter);
206
228
  }
207
- get _currentOptions() {
208
- return this.combobox ? this._filteredOptions : this._options;
229
+ _setAutoFocus() {
230
+ if (this.hasAttribute('autofocus')) {
231
+ if (this.tabIndex < 0) {
232
+ this.tabIndex = 0;
233
+ }
234
+ if (this.combobox) {
235
+ this.updateComplete.then(() => {
236
+ this.shadowRoot
237
+ ?.querySelector('.combobox-input')
238
+ .focus();
239
+ });
240
+ }
241
+ else {
242
+ this.updateComplete.then(() => {
243
+ this.shadowRoot
244
+ ?.querySelector('.select-face')
245
+ .focus();
246
+ });
247
+ }
248
+ }
209
249
  }
210
250
  get _isSuggestedOptionVisible() {
211
251
  if (!(this.combobox && this.creatable)) {
212
252
  return false;
213
253
  }
214
- const filterPatternExistsAsOption = typeof this._valueOptionIndexMap[this._filterPattern] !== 'undefined';
215
- const filtered = this._filterPattern.length > 0;
254
+ const filterPatternExistsAsOption = this._opts.getOptionByValue(this._opts.filterPattern) !== null;
255
+ const filtered = this._opts.filterPattern.length > 0;
216
256
  return !filterPatternExistsAsOption && filtered;
217
257
  }
218
258
  _manageRequired() { }
219
259
  _setStateFromSlottedElements() {
220
- const options = [];
221
- let nextIndex = 0;
222
260
  const optionElements = this._assignedOptions ?? [];
223
- const selectedIndexes = [];
224
- const values = [];
225
- this._valueOptionIndexMap = {};
226
- optionElements.forEach((el, i) => {
261
+ this._opts.clear();
262
+ optionElements.forEach((el) => {
227
263
  const { innerText, description, disabled } = el;
228
264
  const value = typeof el.value === 'string' ? el.value : innerText.trim();
229
265
  const selected = el.selected ?? false;
@@ -232,93 +268,61 @@ export class VscodeSelectBase extends VscElement {
232
268
  value,
233
269
  description,
234
270
  selected,
235
- index: nextIndex,
236
271
  disabled,
237
272
  };
238
- nextIndex = options.push(op);
239
- if (selected && !this._multiple) {
240
- this._activeIndex = i;
241
- }
242
- if (selected) {
243
- selectedIndexes.push(options.length - 1);
244
- values.push(value);
245
- }
246
- this._valueOptionIndexMap[op.value] = op.index;
273
+ this._opts.add(op);
247
274
  });
248
- this._options = options;
249
- if (selectedIndexes.length > 0) {
250
- this._selectedIndex = selectedIndexes[0];
251
- this._selectedIndexes = selectedIndexes;
252
- this._value = values[0];
253
- this._values = values;
254
- }
255
- if (!this._multiple && !this.combobox && selectedIndexes.length === 0) {
256
- this._selectedIndex = this._options.length > 0 ? 0 : -1;
257
- }
258
- }
259
- async _toggleDropdown(visible) {
260
- this.open = visible;
261
- this.ariaExpanded = String(visible);
262
- if (visible && !this._multiple) {
263
- this._activeIndex = this._selectedIndex;
264
- }
265
- if (visible && !this._multiple && !this.combobox) {
266
- this._activeIndex = this._selectedIndex;
267
- if (this._activeIndex > VISIBLE_OPTS - 1) {
268
- await this.updateComplete;
269
- this._listElement.scrollTop = Math.floor(this._activeIndex * OPT_HEIGHT);
270
- }
271
- }
272
- if (visible) {
273
- window.addEventListener('click', this._onClickOutside);
274
- }
275
- else {
276
- window.removeEventListener('click', this._onClickOutside);
277
- }
278
275
  }
279
276
  _createSuggestedOption() {
280
- const nextSelectedIndex = this._options.length;
277
+ const nextSelectedIndex = this._opts.numOptions;
281
278
  const op = document.createElement('vscode-option');
282
- op.value = this._filterPattern;
283
- render(this._filterPattern, op);
279
+ op.value = this._opts.filterPattern;
280
+ render(this._opts.filterPattern, op);
284
281
  this.appendChild(op);
285
282
  return nextSelectedIndex;
286
283
  }
287
284
  _dispatchChangeEvent() {
288
- if (!this._multiple) {
289
- /** @deprecated */
290
- this.dispatchEvent(new CustomEvent('vsc-change', {
291
- detail: {
292
- selectedIndex: this._selectedIndex,
293
- value: this._value,
294
- },
295
- }));
296
- }
297
- else {
298
- /** @deprecated */
299
- this.dispatchEvent(new CustomEvent('vsc-change', {
300
- detail: {
301
- selectedIndexes: this._selectedIndexes,
302
- value: this._values,
303
- },
304
- }));
305
- }
306
285
  this.dispatchEvent(new Event('change'));
307
286
  this.dispatchEvent(new Event('input'));
308
287
  }
309
288
  async _createAndSelectSuggestedOption() { }
310
- _onFaceClick() {
311
- this._toggleDropdown(!this.open);
312
- if (this._multiple) {
313
- this._activeIndex = 0;
314
- }
315
- }
316
289
  _toggleComboboxDropdown() {
317
- this._filterPattern = '';
318
- this._toggleDropdown(!this.open);
319
- if (this._multiple) {
320
- this._activeIndex = -1;
290
+ this._opts.filterPattern = '';
291
+ this.open = !this.open;
292
+ }
293
+ _scrollActiveElementToTop() {
294
+ this._optionListScrollPos = Math.floor(this._opts.relativeActiveIndex * OPT_HEIGHT);
295
+ }
296
+ async _adjustOptionListScrollPos(direction, optionIndex) {
297
+ let numOpts = this._opts.numOfVisibleOptions;
298
+ const suggestedOptionVisible = this._isSuggestedOptionVisible;
299
+ if (suggestedOptionVisible) {
300
+ numOpts += 1;
301
+ }
302
+ if (numOpts <= VISIBLE_OPTS) {
303
+ return;
304
+ }
305
+ this._isHoverForbidden = true;
306
+ window.addEventListener('mousemove', this._onMouseMove);
307
+ const ulScrollTop = this._optionListScrollPos;
308
+ const liPosY = optionIndex * OPT_HEIGHT;
309
+ const fullyVisible = liPosY >= ulScrollTop &&
310
+ liPosY <= ulScrollTop + VISIBLE_OPTS * OPT_HEIGHT - OPT_HEIGHT;
311
+ if (direction === 'down') {
312
+ if (!fullyVisible) {
313
+ this._optionListScrollPos =
314
+ optionIndex * OPT_HEIGHT - (VISIBLE_OPTS - 1) * OPT_HEIGHT;
315
+ }
321
316
  }
317
+ if (direction === 'up') {
318
+ if (!fullyVisible) {
319
+ this._optionListScrollPos = Math.floor(this._opts.relativeActiveIndex * OPT_HEIGHT);
320
+ }
321
+ }
322
+ }
323
+ //#region event handlers
324
+ _onFaceClick() {
325
+ this.open = !this.open;
322
326
  }
323
327
  _onComboboxButtonClick() {
324
328
  this._toggleComboboxDropdown();
@@ -338,11 +342,11 @@ export class VscodeSelectBase extends VscElement {
338
342
  }
339
343
  if (el.matches('.placeholder')) {
340
344
  this._isPlaceholderOptionActive = true;
341
- this._activeIndex = -1;
345
+ this._opts.activeIndex = -1;
342
346
  }
343
347
  else {
344
348
  this._isPlaceholderOptionActive = false;
345
- this._activeIndex = Number(this.combobox ? el.dataset.filteredIndex : el.dataset.index);
349
+ this._opts.activeIndex = +el.dataset.index;
346
350
  }
347
351
  }
348
352
  _onPlaceholderOptionMouseOut() {
@@ -363,143 +367,70 @@ export class VscodeSelectBase extends VscElement {
363
367
  if (clickedOnAcceptButton) {
364
368
  return;
365
369
  }
366
- const list = this.combobox ? this._filteredOptions : this._options;
367
- const showDropdownNext = !this.open;
368
- this._toggleDropdown(showDropdownNext);
369
- if (!this._multiple &&
370
- !showDropdownNext &&
371
- this._selectedIndex !== this._activeIndex) {
372
- this._selectedIndex =
373
- this._activeIndex > -1 ? list[this._activeIndex].index : -1;
374
- this._value =
375
- this._selectedIndex > -1
376
- ? this._options[this._selectedIndex].value
377
- : '';
378
- this._dispatchChangeEvent();
379
- }
380
- if (this.combobox) {
381
- if (this._isPlaceholderOptionActive) {
382
- this._createAndSelectSuggestedOption();
383
- }
384
- else {
385
- if (!this._multiple && !showDropdownNext) {
386
- this._selectedIndex =
387
- this._activeIndex > -1
388
- ? this._filteredOptions[this._activeIndex].index
389
- : -1;
390
- }
391
- if (!this._multiple && showDropdownNext) {
392
- this.updateComplete.then(() => {
393
- this._scrollActiveElementToTop();
394
- });
395
- }
396
- }
397
- }
398
- if (this._multiple && showDropdownNext) {
399
- this._activeIndex = 0;
400
- }
401
370
  }
402
371
  _onSpaceKeyDown() {
403
372
  if (!this.open) {
404
- this._toggleDropdown(true);
373
+ this.open = true;
405
374
  return;
406
375
  }
407
- if (this.open && this._multiple && this._activeIndex > -1) {
408
- const opts = this.combobox ? this._filteredOptions : this._options;
409
- const selectedOption = opts[this._activeIndex];
410
- const nextSelectedIndexes = [];
411
- this._options[selectedOption.index].selected = !selectedOption.selected;
412
- opts.forEach(({ index }) => {
413
- const { selected } = this._options[index];
414
- if (selected) {
415
- nextSelectedIndexes.push(index);
416
- }
417
- });
418
- this._selectedIndexes = nextSelectedIndexes;
419
- }
420
- }
421
- _scrollActiveElementToTop() {
422
- this._listElement.scrollTop = Math.floor(this._activeIndex * OPT_HEIGHT);
423
- }
424
- async _adjustOptionListScrollPos(direction, optionIndex) {
425
- let numOpts = this.combobox
426
- ? this._filteredOptions.length
427
- : this._options.length;
428
- const suggestedOptionVisible = this._isSuggestedOptionVisible;
429
- if (suggestedOptionVisible) {
430
- numOpts += 1;
431
- }
432
- if (numOpts <= VISIBLE_OPTS) {
433
- return;
434
- }
435
- this._isHoverForbidden = true;
436
- window.addEventListener('mousemove', this._onMouseMove);
437
- const ulScrollTop = this._listElement.scrollTop;
438
- const liPosY = optionIndex * OPT_HEIGHT;
439
- const fullyVisible = liPosY >= ulScrollTop &&
440
- liPosY <= ulScrollTop + VISIBLE_OPTS * OPT_HEIGHT - OPT_HEIGHT;
441
- if (direction === 'down') {
442
- if (!fullyVisible) {
443
- this._listElement.scrollTop =
444
- optionIndex * OPT_HEIGHT - (VISIBLE_OPTS - 1) * OPT_HEIGHT;
445
- }
446
- }
447
- if (direction === 'up') {
448
- if (!fullyVisible) {
449
- this._listElement.scrollTop = Math.floor(this._activeIndex * OPT_HEIGHT);
450
- }
451
- }
452
376
  }
453
377
  _onArrowUpKeyDown() {
454
378
  if (this.open) {
455
- if (this._activeIndex <= 0 && !(this.combobox && this.creatable)) {
379
+ if (this._opts.activeIndex <= 0 && !(this.combobox && this.creatable)) {
456
380
  return;
457
381
  }
458
382
  if (this._isPlaceholderOptionActive) {
459
- const optionIndex = this._currentOptions.length - 1;
460
- this._activeIndex = optionIndex;
383
+ const optionIndex = this._opts.numOfVisibleOptions - 1;
384
+ this._opts.activeIndex = optionIndex;
461
385
  this._isPlaceholderOptionActive = false;
462
386
  }
463
387
  else {
464
- const currentOptions = this.combobox
465
- ? this._filteredOptions
466
- : this._options;
467
- const prevSelectable = findPrevSelectableOptionIndex(currentOptions, this._activeIndex);
468
- if (prevSelectable > -1) {
469
- this._activeIndex = prevSelectable;
470
- this._adjustOptionListScrollPos('up', prevSelectable);
388
+ const prevOp = this._opts.prev();
389
+ if (prevOp !== null) {
390
+ this._opts.activeIndex = prevOp?.index ?? -1;
391
+ const prevSelectableIndex = prevOp?.filteredIndex ?? -1;
392
+ if (prevSelectableIndex > -1) {
393
+ this._adjustOptionListScrollPos('up', prevSelectableIndex);
394
+ }
471
395
  }
472
396
  }
473
397
  }
398
+ else {
399
+ this.open = true;
400
+ this._opts.activateDefault();
401
+ }
474
402
  }
475
403
  _onArrowDownKeyDown() {
476
- let numOpts = this.combobox
477
- ? this._filteredOptions.length
478
- : this._options.length;
479
- const currentOptions = this.combobox
480
- ? this._filteredOptions
481
- : this._options;
404
+ let numOpts = this._opts.numOfVisibleOptions;
482
405
  const suggestedOptionVisible = this._isSuggestedOptionVisible;
483
406
  if (suggestedOptionVisible) {
484
407
  numOpts += 1;
485
408
  }
486
409
  if (this.open) {
487
- if (this._isPlaceholderOptionActive && this._activeIndex === -1) {
410
+ if (this._isPlaceholderOptionActive && this._opts.activeIndex === -1) {
488
411
  return;
489
412
  }
490
- if (suggestedOptionVisible && this._activeIndex === numOpts - 2) {
413
+ const nextOp = this._opts.next();
414
+ if (suggestedOptionVisible && nextOp === null) {
491
415
  this._isPlaceholderOptionActive = true;
492
416
  this._adjustOptionListScrollPos('down', numOpts - 1);
493
- this._activeIndex = -1;
417
+ this._opts.activeIndex = -1;
494
418
  }
495
- else if (this._activeIndex < numOpts - 1) {
496
- const nextSelectable = findNextSelectableOptionIndex(currentOptions, this._activeIndex);
497
- if (nextSelectable > -1) {
498
- this._activeIndex = nextSelectable;
499
- this._adjustOptionListScrollPos('down', nextSelectable);
419
+ else if (nextOp !== null) {
420
+ const nextSelectableIndex = nextOp?.filteredIndex ?? -1;
421
+ this._opts.activeIndex = nextOp?.index ?? -1;
422
+ if (nextSelectableIndex > -1) {
423
+ this._adjustOptionListScrollPos('down', nextSelectableIndex);
500
424
  }
501
425
  }
502
426
  }
427
+ else {
428
+ this.open = true;
429
+ this._opts.activateDefault();
430
+ }
431
+ }
432
+ _onEscapeKeyDown() {
433
+ this.open = false;
503
434
  }
504
435
  _onSlotChange() {
505
436
  this._setStateFromSlottedElements();
@@ -508,61 +439,83 @@ export class VscodeSelectBase extends VscElement {
508
439
  _onComboboxInputFocus(ev) {
509
440
  ev.target.select();
510
441
  this._isBeingFiltered = false;
511
- this._filterPattern = '';
442
+ this._opts.filterPattern = '';
512
443
  }
513
444
  _onComboboxInputBlur() {
514
445
  this._isBeingFiltered = false;
515
446
  }
516
447
  _onComboboxInputInput(ev) {
517
448
  this._isBeingFiltered = true;
518
- this._filterPattern = ev.target.value;
519
- this._activeIndex = -1;
520
- this._toggleDropdown(true);
449
+ this._opts.filterPattern = ev.target.value;
450
+ this._opts.activeIndex = -1;
451
+ this.open = true;
521
452
  }
522
453
  _onComboboxInputClick() {
523
- this._isBeingFiltered = this._filterPattern !== '';
524
- this._toggleDropdown(true);
454
+ this._isBeingFiltered = this._opts.filterPattern !== '';
455
+ this.open = true;
456
+ }
457
+ _onComboboxInputSpaceKeyDown(ev) {
458
+ if (ev.key === ' ') {
459
+ ev.stopPropagation();
460
+ }
525
461
  }
526
462
  _onOptionClick(_ev) {
527
463
  this._isBeingFiltered = false;
528
464
  return;
529
465
  }
466
+ //#endregion
467
+ //#region render functions
468
+ _renderCheckbox(checked, label) {
469
+ const checkboxClasses = {
470
+ 'checkbox-icon': true,
471
+ checked,
472
+ };
473
+ return html `<span class=${classMap(checkboxClasses)}>${checkIcon}</span
474
+ ><span class="option-label">${label}</span>`;
475
+ }
530
476
  _renderOptions() {
531
- const list = this.combobox ? this._filteredOptions : this._options;
477
+ const list = this._opts.options;
532
478
  return html `
533
479
  <ul
480
+ aria-label=${ifDefined(this.label ?? undefined)}
481
+ aria-multiselectable=${ifDefined(this._opts.multiSelect ? 'true' : undefined)}
534
482
  class="options"
483
+ id="select-listbox"
484
+ role="listbox"
485
+ tabindex="-1"
535
486
  @click=${this._onOptionClick}
536
487
  @mouseover=${this._onOptionMouseOver}
537
488
  >
538
489
  ${repeat(list, (op) => op.index, (op, index) => {
490
+ if (!op.visible) {
491
+ return nothing;
492
+ }
493
+ const active = op.index === this._opts.activeIndex && !op.disabled;
494
+ const selected = this._opts.getIsIndexSelected(op.index);
539
495
  const optionClasses = {
540
- active: index === this._activeIndex && !op.disabled,
496
+ active,
541
497
  disabled: op.disabled,
542
498
  option: true,
543
- selected: op.selected,
544
- };
545
- const checkboxClasses = {
546
- 'checkbox-icon': true,
547
- checked: op.selected,
499
+ selected,
548
500
  };
549
501
  const labelText = (op.ranges?.length ?? 0 > 0)
550
502
  ? highlightRanges(op.label, op.ranges ?? [])
551
503
  : op.label;
552
504
  return html `
553
505
  <li
506
+ aria-selected=${selected ? 'true' : 'false'}
554
507
  class=${classMap(optionClasses)}
555
508
  data-index=${op.index}
556
509
  data-filtered-index=${index}
510
+ id=${`op-${op.index}`}
511
+ role="option"
512
+ tabindex="-1"
557
513
  >
558
- ${this._multiple
559
- ? html `<span class=${classMap(checkboxClasses)}></span
560
- ><span class="option-label">${labelText}</span>`
561
- : labelText}
514
+ ${when(this._opts.multiSelect, () => this._renderCheckbox(selected, labelText), () => labelText)}
562
515
  </li>
563
516
  `;
564
517
  })}
565
- ${this._renderPlaceholderOption(list.length < 1)}
518
+ ${this._renderPlaceholderOption(this._opts.numOfVisibleOptions < 1)}
566
519
  </ul>
567
520
  `;
568
521
  }
@@ -570,10 +523,11 @@ export class VscodeSelectBase extends VscElement {
570
523
  if (!this.combobox) {
571
524
  return nothing;
572
525
  }
573
- if (this._valueOptionIndexMap[this._filterPattern]) {
526
+ const foundOption = this._opts.getOptionByLabel(this._opts.filterPattern);
527
+ if (foundOption) {
574
528
  return nothing;
575
529
  }
576
- if (this.creatable && this._filterPattern.length > 0) {
530
+ if (this.creatable && this._opts.filterPattern.length > 0) {
577
531
  return html `<li
578
532
  class=${classMap({
579
533
  option: true,
@@ -582,7 +536,7 @@ export class VscodeSelectBase extends VscElement {
582
536
  })}
583
537
  @mouseout=${this._onPlaceholderOptionMouseOut}
584
538
  >
585
- Add "${this._filterPattern}"
539
+ Add "${this._opts.filterPattern}"
586
540
  </li>`;
587
541
  }
588
542
  else {
@@ -594,10 +548,11 @@ export class VscodeSelectBase extends VscElement {
594
548
  }
595
549
  }
596
550
  _renderDescription() {
597
- if (!this._options[this._activeIndex]) {
551
+ const op = this._opts.getActiveOption();
552
+ if (!op) {
598
553
  return nothing;
599
554
  }
600
- const { description } = this._options[this._activeIndex];
555
+ const { description } = op;
601
556
  return description
602
557
  ? html `<div class="description">${description}</div>`
603
558
  : nothing;
@@ -605,89 +560,52 @@ export class VscodeSelectBase extends VscElement {
605
560
  _renderSelectFace() {
606
561
  return html `${nothing}`;
607
562
  }
608
- _renderMultiSelectLabel() {
609
- switch (this._selectedIndexes.length) {
610
- case 0:
611
- return html `<span class="select-face-badge no-item"
612
- >No items selected</span
613
- >`;
614
- case 1:
615
- return html `<span class="select-face-badge">1 item selected</span>`;
616
- default:
617
- return html `<span class="select-face-badge"
618
- >${this._selectedIndexes.length} items selected</span
619
- >`;
620
- }
621
- }
622
563
  _renderComboboxFace() {
623
- let inputVal = '';
624
- if (this._isBeingFiltered) {
625
- inputVal = this._filterPattern;
626
- }
627
- else {
628
- inputVal =
629
- this._selectedIndex > -1
630
- ? (this._options[this._selectedIndex]?.label ?? '')
631
- : '';
632
- }
633
- return html `
634
- <div class="combobox-face face">
635
- ${this._multiple ? this._renderMultiSelectLabel() : nothing}
636
- <input
637
- class="combobox-input"
638
- spellcheck="false"
639
- type="text"
640
- autocomplete="off"
641
- .value=${inputVal}
642
- @focus=${this._onComboboxInputFocus}
643
- @blur=${this._onComboboxInputBlur}
644
- @input=${this._onComboboxInputInput}
645
- @click=${this._onComboboxInputClick}
646
- >
647
- <button
648
- class="combobox-button"
649
- type="button"
650
- @click=${this._onComboboxButtonClick}
651
- @keydown=${this._onComboboxButtonKeyDown}
652
- >
653
- ${chevronDownIcon}
654
- </button>
655
- </div>
656
- `;
564
+ return html `${nothing}`;
657
565
  }
658
566
  _renderDropdownControls() {
659
567
  return html `${nothing}`;
660
568
  }
661
569
  _renderDropdown() {
662
- const classes = classMap({
570
+ const classes = {
663
571
  dropdown: true,
664
- multiple: this._multiple,
665
- });
572
+ multiple: this._opts.multiSelect,
573
+ open: this.open,
574
+ };
575
+ const visibleOptions = this._isSuggestedOptionVisible || this._opts.numOfVisibleOptions === 0
576
+ ? this._opts.numOfVisibleOptions + 1
577
+ : this._opts.numOfVisibleOptions;
578
+ const scrollPaneHeight = Math.min(visibleOptions * OPT_HEIGHT, VISIBLE_OPTS * OPT_HEIGHT);
666
579
  return html `
667
- <div class=${classes}>
580
+ <div class=${classMap(classes)}>
668
581
  ${this.position === 'above' ? this._renderDescription() : nothing}
669
- ${this._renderOptions()} ${this._renderDropdownControls()}
582
+ <vscode-scrollable
583
+ always-visible
584
+ class="scrollable"
585
+ min-thumb-size="40"
586
+ tabindex="-1"
587
+ @vsc-scrollable-scroll=${this._onOptionListScroll}
588
+ .scrollPos=${this._optionListScrollPos}
589
+ .style=${stylePropertyMap({
590
+ height: `${scrollPaneHeight}px`,
591
+ })}
592
+ >
593
+ ${this._renderOptions()} ${this._renderDropdownControls()}
594
+ </vscode-scrollable>
670
595
  ${this.position === 'below' ? this._renderDescription() : nothing}
671
596
  </div>
672
597
  `;
673
598
  }
674
- render() {
675
- return html `
676
- <slot class="main-slot" @slotchange=${this._onSlotChange}></slot>
677
- ${this.combobox ? this._renderComboboxFace() : this._renderSelectFace()}
678
- ${this.open ? this._renderDropdown() : nothing}
679
- `;
680
- }
681
599
  }
682
- __decorate([
683
- property({ type: String, reflect: true, attribute: 'aria-expanded' })
684
- ], VscodeSelectBase.prototype, "ariaExpanded", void 0);
685
600
  __decorate([
686
601
  property({ type: Boolean, reflect: true })
687
602
  ], VscodeSelectBase.prototype, "creatable", void 0);
688
603
  __decorate([
689
604
  property({ type: Boolean, reflect: true })
690
- ], VscodeSelectBase.prototype, "combobox", void 0);
605
+ ], VscodeSelectBase.prototype, "combobox", null);
606
+ __decorate([
607
+ property({ reflect: true })
608
+ ], VscodeSelectBase.prototype, "label", void 0);
691
609
  __decorate([
692
610
  property({ type: Boolean, reflect: true })
693
611
  ], VscodeSelectBase.prototype, "disabled", null);
@@ -709,18 +627,12 @@ __decorate([
709
627
  __decorate([
710
628
  property({ reflect: true })
711
629
  ], VscodeSelectBase.prototype, "position", void 0);
712
- __decorate([
713
- property({ type: Number, attribute: true, reflect: true })
714
- ], VscodeSelectBase.prototype, "tabIndex", void 0);
715
630
  __decorate([
716
631
  queryAssignedElements({
717
632
  flatten: true,
718
633
  selector: 'vscode-option',
719
634
  })
720
635
  ], VscodeSelectBase.prototype, "_assignedOptions", void 0);
721
- __decorate([
722
- state()
723
- ], VscodeSelectBase.prototype, "_activeIndex", void 0);
724
636
  __decorate([
725
637
  state()
726
638
  ], VscodeSelectBase.prototype, "_currentDescription", void 0);
@@ -730,12 +642,6 @@ __decorate([
730
642
  __decorate([
731
643
  state()
732
644
  ], VscodeSelectBase.prototype, "_filteredOptions", null);
733
- __decorate([
734
- state()
735
- ], VscodeSelectBase.prototype, "_filterPattern", void 0);
736
- __decorate([
737
- state()
738
- ], VscodeSelectBase.prototype, "_selectedIndex", void 0);
739
645
  __decorate([
740
646
  state()
741
647
  ], VscodeSelectBase.prototype, "_selectedIndexes", void 0);
@@ -748,9 +654,6 @@ __decorate([
748
654
  __decorate([
749
655
  state()
750
656
  ], VscodeSelectBase.prototype, "_values", void 0);
751
- __decorate([
752
- state()
753
- ], VscodeSelectBase.prototype, "_listScrollTop", void 0);
754
657
  __decorate([
755
658
  state()
756
659
  ], VscodeSelectBase.prototype, "_isPlaceholderOptionActive", void 0);
@@ -758,6 +661,6 @@ __decorate([
758
661
  state()
759
662
  ], VscodeSelectBase.prototype, "_isBeingFiltered", void 0);
760
663
  __decorate([
761
- query('.options')
762
- ], VscodeSelectBase.prototype, "_listElement", void 0);
664
+ state()
665
+ ], VscodeSelectBase.prototype, "_optionListScrollPos", void 0);
763
666
  //# sourceMappingURL=vscode-select-base.js.map