@vscode-elements/elements 1.17.0 → 2.0.0-pre.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/custom-elements.json +2547 -2262
  2. package/dist/bundled.js +275 -359
  3. package/dist/includes/VscElement.js +1 -1
  4. package/dist/includes/VscElement.js.map +1 -1
  5. package/dist/main.d.ts +1 -0
  6. package/dist/main.d.ts.map +1 -1
  7. package/dist/main.js +1 -0
  8. package/dist/main.js.map +1 -1
  9. package/dist/vscode-tree/helpers.d.ts +11 -0
  10. package/dist/vscode-tree/helpers.d.ts.map +1 -0
  11. package/dist/vscode-tree/helpers.js +139 -0
  12. package/dist/vscode-tree/helpers.js.map +1 -0
  13. package/dist/vscode-tree/tree-context.d.ts +33 -0
  14. package/dist/vscode-tree/tree-context.d.ts.map +1 -0
  15. package/dist/vscode-tree/tree-context.js +4 -0
  16. package/dist/vscode-tree/tree-context.js.map +1 -0
  17. package/dist/vscode-tree/vscode-tree.d.ts +35 -150
  18. package/dist/vscode-tree/vscode-tree.d.ts.map +1 -1
  19. package/dist/vscode-tree/vscode-tree.js +263 -627
  20. package/dist/vscode-tree/vscode-tree.js.map +1 -1
  21. package/dist/vscode-tree/vscode-tree.styles.d.ts.map +1 -1
  22. package/dist/vscode-tree/vscode-tree.styles.js +4 -240
  23. package/dist/vscode-tree/vscode-tree.styles.js.map +1 -1
  24. package/dist/vscode-tree-item/index.d.ts +2 -0
  25. package/dist/vscode-tree-item/index.d.ts.map +1 -0
  26. package/dist/vscode-tree-item/index.js +2 -0
  27. package/dist/vscode-tree-item/index.js.map +1 -0
  28. package/dist/vscode-tree-item/vscode-tree-item.d.ts +45 -0
  29. package/dist/vscode-tree-item/vscode-tree-item.d.ts.map +1 -0
  30. package/dist/vscode-tree-item/vscode-tree-item.js +377 -0
  31. package/dist/vscode-tree-item/vscode-tree-item.js.map +1 -0
  32. package/dist/vscode-tree-item/vscode-tree-item.styles.d.ts +4 -0
  33. package/dist/vscode-tree-item/vscode-tree-item.styles.d.ts.map +1 -0
  34. package/dist/vscode-tree-item/vscode-tree-item.styles.js +165 -0
  35. package/dist/vscode-tree-item/vscode-tree-item.styles.js.map +1 -0
  36. package/package.json +2 -1
  37. package/vscode.css-custom-data.json +32 -54
  38. package/vscode.html-custom-data.json +83 -70
@@ -4,713 +4,349 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { html, nothing } from 'lit';
8
- import { property, state } from 'lit/decorators.js';
9
- import { classMap } from 'lit/directives/class-map.js';
10
- import { ifDefined } from 'lit/directives/if-defined.js';
11
- import { customElement, VscElement } from '../includes/VscElement.js';
12
- import { stylePropertyMap } from '../includes/style-property-map.js';
13
- import '../vscode-badge/index.js';
14
- import '../vscode-icon/index.js';
15
- import styles from './vscode-tree.styles.js';
16
- const ARROW_OUTER_WIDTH = 30;
17
- const ARROW_ICON_WIDTH = 16;
18
- const CONTENT_PADDING = 3;
19
- const addPath = (tree, prevPath = []) => {
20
- const nextTree = [];
21
- tree.forEach((item, index) => {
22
- const path = [...prevPath, index];
23
- const nextItem = {
24
- ...item,
25
- path,
26
- };
27
- if (item.subItems) {
28
- nextItem.subItems = addPath(item.subItems, path);
29
- }
30
- nextTree.push(nextItem);
31
- });
32
- return nextTree;
7
+ import { html } from 'lit';
8
+ import { provide } from '@lit/context';
9
+ import { customElement, property, queryAssignedElements, } from 'lit/decorators.js';
10
+ import { VscElement } from '../includes/VscElement';
11
+ import styles from './vscode-tree.styles';
12
+ import { configContext, treeContext, } from './tree-context';
13
+ import { findNextItem, findPrevItem, getParentItem, initPathTrackerProps, } from './helpers.js';
14
+ export const EXPAND_MODE = {
15
+ SINGLE_CLICK: 'singleClick',
16
+ DOUBLE_CLICK: 'doubleClick',
33
17
  };
34
- const isBranch = (item) => {
35
- if (item.subItems &&
36
- Array.isArray(item.subItems) &&
37
- item?.subItems?.length > 0) {
38
- return true;
39
- }
40
- return false;
41
- };
42
- /**
43
- * @tag vscode-tree
44
- *
45
- * @fires vsc-select Dispatched when an item is selected.
46
- * @fires {VscTreeSelectEvent} vsc-tree-select Dispatched when an item is selected.
47
- * @fires vsc-run-action Dispatched when an action icon is clicked.
48
- * @fires {VscTreeActionEvent} vsc-tree-action Dispatched when an action icon is clicked.
49
- *
50
- * @cssprop --vscode-focusBorder
51
- * @cssprop --vscode-font-family
52
- * @cssprop --vscode-font-size
53
- * @cssprop --vscode-font-weight
54
- * @cssprop --vscode-list-hoverForeground
55
- * @cssprop --vscode-list-hoverBackground
56
- * @cssprop --vscode-list-inactiveSelectionBackground
57
- * @cssprop --vscode-list-inactiveSelectionForeground
58
- * @cssprop --vscode-list-activeSelectionBackground
59
- * @cssprop --vscode-list-activeSelectionForeground
60
- * @cssprop --vscode-list-inactiveSelectionIconForeground
61
- * @cssprop --vscode-list-inactiveFocusBackground
62
- * @cssprop --vscode-list-inactiveFocusOutline
63
- * @cssprop --vscode-list-focusOutline
64
- * @cssprop --vscode-tree-inactiveIndentGuidesStroke
65
- * @cssprop --vscode-tree-indentGuidesStroke
66
- *
67
- * @csspart text-content
68
- * @csspart description
69
- * @csspart counter-badge-decoration
70
- * @csspart filled-circle-decoration
71
- * @csspart caption-decoration
72
- * @csspart decorations Container of decorations
73
- */
18
+ const listenedKeys = [
19
+ ' ',
20
+ 'ArrowDown',
21
+ 'ArrowUp',
22
+ 'ArrowLeft',
23
+ 'ArrowRight',
24
+ 'Enter',
25
+ 'Escape',
26
+ 'Shift',
27
+ ];
28
+ const DEFAULT_ARROWS = false;
29
+ const DEFAULT_INDENT = 8;
30
+ const DEFAULT_INDENT_GUIDES = false;
31
+ const DEFAULT_MULTI_SELECT = false;
32
+ const DEFAULT_EXPAND_MODE = EXPAND_MODE.SINGLE_CLICK;
74
33
  let VscodeTree = class VscodeTree extends VscElement {
34
+ //#region lifecycle methods
75
35
  constructor() {
76
- super(...arguments);
77
- this.indent = 8;
78
- this.arrows = false;
79
- this.multiline = false;
80
- this.tabindex = 0;
81
- this.indentGuides = false;
82
- this._data = [];
83
- this._selectedItem = null;
84
- this._focusedItem = null;
85
- this._selectedBranch = null;
86
- this._focusedBranch = null;
87
- this._handleComponentKeyDownBound = this._handleComponentKeyDown.bind(this);
88
- }
89
- set data(val) {
90
- const oldVal = this._data;
91
- this._data = addPath(val);
92
- this.requestUpdate('data', oldVal);
93
- }
94
- get data() {
95
- return this._data;
96
- }
97
- /**
98
- * Closes all opened tree items recursively.
99
- */
100
- closeAll() {
101
- this._closeSubTreeRecursively(this.data);
102
- this.requestUpdate();
103
- }
104
- /**
105
- * Deselects all selected items.
106
- */
107
- deselectAll() {
108
- this._deselectItemsRecursively(this.data);
109
- this.requestUpdate();
110
- }
111
- /**
112
- * Returns a reference to a TreeItem object by path.
113
- * @param path
114
- * @returns
115
- */
116
- getItemByPath(path) {
117
- return this._getItemByPath(path);
118
- }
119
- connectedCallback() {
120
- super.connectedCallback();
121
- this.addEventListener('keydown', this._handleComponentKeyDownBound);
122
- }
123
- disconnectedCallback() {
124
- super.disconnectedCallback();
125
- this.removeEventListener('keydown', this._handleComponentKeyDownBound);
126
- }
127
- _getItemByPath(path) {
128
- let current = this._data;
129
- let item = null;
130
- path.forEach((el, i) => {
131
- if (i === path.length - 1) {
132
- item = current[el];
133
- }
134
- else {
135
- current = current[el].subItems;
136
- }
137
- });
138
- return item;
139
- }
140
- _handleActionClick(ev) {
141
- ev.stopPropagation();
142
- const el = ev.target;
143
- const itemPath = el.dataset.itemPath;
144
- const actionIndex = el.dataset.index;
145
- let item = null;
146
- let actionId = '';
147
- let value = '';
148
- if (itemPath) {
149
- const path = itemPath.split('/').map((p) => Number(p));
150
- item = this._getItemByPath(path);
151
- if (item?.actions) {
152
- const index = Number(actionIndex);
153
- if (item.actions[index]) {
154
- actionId = item.actions[index].actionId;
155
- }
156
- }
157
- if (item?.value) {
158
- value = item.value;
159
- }
160
- }
161
- /** @deprecated Renamed to `vsc-tree-action` */
162
- this.dispatchEvent(new CustomEvent('vsc-run-action', {
163
- detail: {
164
- actionId,
165
- item,
166
- value,
36
+ super();
37
+ //#region properties
38
+ this.arrows = DEFAULT_ARROWS;
39
+ this.expandMode = DEFAULT_EXPAND_MODE;
40
+ this.indent = DEFAULT_INDENT;
41
+ this.indentGuides = DEFAULT_INDENT_GUIDES;
42
+ this.multiSelect = DEFAULT_MULTI_SELECT;
43
+ //#endregion
44
+ //#region private variables
45
+ this._treeContextState = {
46
+ isShiftPressed: false,
47
+ activeItem: null,
48
+ selectedItems: new Set(),
49
+ allItems: null,
50
+ itemListUpToDate: false,
51
+ focusedItem: null,
52
+ prevFocusedItem: null,
53
+ hasBranchItem: false,
54
+ rootElement: this,
55
+ highlightedItems: [],
56
+ highlightGuides: () => {
57
+ this._highlightGuides();
167
58
  },
168
- }));
169
- /**
170
- * Dispatched when an action icon is clicked.
171
- */
172
- this.dispatchEvent(new CustomEvent('vsc-tree-action', {
173
- detail: {
174
- actionId,
175
- item,
176
- value,
177
- },
178
- }));
179
- }
180
- _renderIconVariant(variant) {
181
- const { type, value } = variant;
182
- if (type === 'themeicon') {
183
- return html `<vscode-icon name=${value} class="theme-icon"></vscode-icon>`;
184
- }
185
- else {
186
- return html `<span
187
- class="image-icon"
188
- .style=${stylePropertyMap({ backgroundImage: `url(${value})` })}
189
- ></span>`;
190
- }
191
- }
192
- _renderIcon(item) {
193
- const iconVariants = {
194
- branch: {
195
- value: 'folder',
196
- type: 'themeicon',
197
- },
198
- open: {
199
- value: 'folder-opened',
200
- type: 'themeicon',
201
- },
202
- leaf: {
203
- value: 'file',
204
- type: 'themeicon',
59
+ emitSelectEvent: () => {
60
+ this._emitSelectEvent();
205
61
  },
206
62
  };
207
- if (item.iconUrls) {
208
- if (item.iconUrls.branch) {
209
- iconVariants.branch = {
210
- value: item.iconUrls.branch,
211
- type: 'image',
212
- };
213
- }
214
- if (item.iconUrls.leaf) {
215
- iconVariants.leaf = {
216
- value: item.iconUrls.leaf,
217
- type: 'image',
218
- };
219
- }
220
- if (item.iconUrls.open) {
221
- iconVariants.open = {
222
- value: item.iconUrls.open,
223
- type: 'image',
224
- };
225
- }
226
- }
227
- else if (typeof item.icons === 'object') {
228
- if (item.icons.branch) {
229
- iconVariants.branch = {
230
- value: item.icons.branch,
231
- type: 'themeicon',
232
- };
233
- }
234
- if (item.icons.leaf) {
235
- iconVariants.leaf = {
236
- value: item.icons.leaf,
237
- type: 'themeicon',
238
- };
239
- }
240
- if (item.icons.open) {
241
- iconVariants.open = {
242
- value: item.icons.open,
243
- type: 'themeicon',
244
- };
63
+ this._configContext = {
64
+ arrows: DEFAULT_ARROWS,
65
+ expandMode: DEFAULT_EXPAND_MODE,
66
+ indent: DEFAULT_INDENT,
67
+ indentGuides: DEFAULT_INDENT_GUIDES,
68
+ multiSelect: DEFAULT_MULTI_SELECT,
69
+ };
70
+ this._handleComponentKeyDown = (ev) => {
71
+ const key = ev.key;
72
+ if (listenedKeys.includes(key)) {
73
+ ev.stopPropagation();
74
+ ev.preventDefault();
245
75
  }
246
- }
247
- else if (!item.icons) {
248
- return html `${nothing}`;
249
- }
250
- if (isBranch(item)) {
251
- if (item.open) {
252
- return this._renderIconVariant(iconVariants.open);
76
+ switch (key) {
77
+ case ' ':
78
+ case 'Enter':
79
+ this._handleEnterPress();
80
+ break;
81
+ case 'ArrowDown':
82
+ this._handleArrowDownPress();
83
+ break;
84
+ case 'ArrowLeft':
85
+ this._handleArrowLeftPress(ev);
86
+ break;
87
+ case 'ArrowRight':
88
+ this._handleArrowRightPress();
89
+ break;
90
+ case 'ArrowUp':
91
+ this._handleArrowUpPress();
92
+ break;
93
+ case 'Shift':
94
+ this._handleShiftPress();
95
+ break;
96
+ default:
253
97
  }
254
- else {
255
- return this._renderIconVariant(iconVariants.branch);
98
+ };
99
+ this._handleComponentKeyUp = (ev) => {
100
+ if (ev.key === 'Shift') {
101
+ this._treeContextState.isShiftPressed = false;
256
102
  }
257
- }
258
- else {
259
- return this._renderIconVariant(iconVariants.leaf);
260
- }
261
- }
262
- _renderArrow(item) {
263
- if (!this.arrows || !isBranch(item)) {
264
- return html `${nothing}`;
265
- }
266
- const { open = false } = item;
267
- const arrowIconName = open ? 'chevron-down' : 'chevron-right';
268
- return html `
269
- <div class="arrow-container">
270
- <vscode-icon name=${arrowIconName} class="icon-arrow"></vscode-icon>
271
- </div>
272
- `;
273
- }
274
- _renderActions(item) {
275
- const actionButtons = [];
276
- if (item.actions && Array.isArray(item.actions)) {
277
- item.actions.forEach((action, index) => {
278
- if (action.icon) {
279
- const icon = html `<vscode-icon
280
- name=${action.icon}
281
- action-icon
282
- title=${ifDefined(action.tooltip)}
283
- data-item-path=${ifDefined(item.path?.join('/'))}
284
- data-index=${index}
285
- class="action-icon"
286
- @click=${this._handleActionClick}
287
- ></vscode-icon>`;
288
- actionButtons.push(icon);
103
+ };
104
+ this._handleSlotChange = () => {
105
+ this._treeContextState.itemListUpToDate = false;
106
+ initPathTrackerProps(this, this._assignedTreeItems);
107
+ this.updateComplete.then(() => {
108
+ if (this._treeContextState.activeItem === null) {
109
+ const firstChild = this.querySelector(':scope > vscode-tree-item');
110
+ if (firstChild) {
111
+ firstChild.active = true;
112
+ }
289
113
  }
114
+ this._highlightGuides();
290
115
  });
291
- }
292
- if (actionButtons.length > 0) {
293
- return html `<div class="actions">${actionButtons}</div>`;
294
- }
295
- else {
296
- return html `${nothing}`;
297
- }
116
+ };
117
+ this.addEventListener('keyup', this._handleComponentKeyUp);
118
+ this.addEventListener('keydown', this._handleComponentKeyDown);
298
119
  }
299
- _renderDecorations(item) {
300
- const decorations = [];
301
- if (item.decorations && Array.isArray(item.decorations)) {
302
- item.decorations.forEach((decoration) => {
303
- const { appearance = 'text', visibleWhen = 'always', content = '', color = '', focusedColor = '', hoverColor = '', selectedColor = '', } = decoration;
304
- const visibleWhenClass = `visible-when-${visibleWhen}`;
305
- const inlineStyles = {};
306
- if (color) {
307
- inlineStyles['--color'] = color;
308
- }
309
- if (focusedColor) {
310
- inlineStyles['--focused-color'] = focusedColor;
311
- }
312
- if (hoverColor) {
313
- inlineStyles['--hover-color'] = hoverColor;
314
- }
315
- if (selectedColor) {
316
- inlineStyles['--selected-color'] = selectedColor;
317
- }
318
- switch (appearance) {
319
- case 'counter-badge':
320
- decorations.push(html `<vscode-badge
321
- variant="counter"
322
- class=${['counter-badge', visibleWhenClass].join(' ')}
323
- part="counter-badge-decoration"
324
- >${content}</vscode-badge
325
- >`);
326
- break;
327
- case 'filled-circle':
328
- decorations.push(html `<vscode-icon
329
- name="circle-filled"
330
- size="14"
331
- class=${['filled-circle', visibleWhenClass].join(' ')}
332
- part="filled-circle-decoration"
333
- .style=${stylePropertyMap(inlineStyles)}
334
- ></vscode-icon>`);
335
- break;
336
- case 'text':
337
- decorations.push(html `<div
338
- class=${['decoration-text', visibleWhenClass].join(' ')}
339
- part="caption-decoration"
340
- .style=${stylePropertyMap(inlineStyles)}
341
- >
342
- ${content}
343
- </div>`);
344
- break;
345
- default:
346
- }
347
- });
348
- }
349
- if (decorations.length > 0) {
350
- return html `<div class="decorations" part="decorations">
351
- ${decorations}
352
- </div>`;
353
- }
354
- else {
355
- return html `${nothing}`;
356
- }
120
+ connectedCallback() {
121
+ super.connectedCallback();
122
+ this.role = 'tree';
357
123
  }
358
- _renderTreeItem(item, additionalOptions) {
359
- const { open = false, label, description = '', tooltip, selected = false, focused = false, subItems = [], } = item;
360
- const { path, itemType, hasFocusedItem = false, hasSelectedItem = false, } = additionalOptions;
361
- const indentLevel = path.length - 1;
362
- const contentsClasses = ['contents'];
363
- const liClasses = open ? ['open'] : [];
364
- const indentSize = indentLevel * this.indent;
365
- const padLeft = this.arrows && itemType === 'leaf'
366
- ? ARROW_OUTER_WIDTH + indentSize
367
- : indentSize;
368
- const arrowMarkup = this._renderArrow(item);
369
- const iconMarkup = this._renderIcon(item);
370
- const indentGuidePos = this.arrows
371
- ? indentSize + ARROW_ICON_WIDTH
372
- : indentSize + CONTENT_PADDING;
373
- const subTreeMarkup = open && itemType === 'branch'
374
- ? html `<ul
375
- .style=${stylePropertyMap({
376
- '--indent-guide-pos': `${indentGuidePos}px`,
377
- })}
378
- class=${classMap({
379
- 'has-active-item': hasFocusedItem || hasSelectedItem,
380
- })}
381
- >
382
- ${this._renderTree(subItems, path)}
383
- </ul>`
384
- : nothing;
385
- const descriptionMarkup = description
386
- ? html `<span class="description" part="description">${description}</span>`
387
- : nothing;
388
- const actionsMarkup = this._renderActions(item);
389
- const decorationsMarkup = this._renderDecorations(item);
390
- liClasses.push(itemType);
391
- if (selected) {
392
- contentsClasses.push('selected');
124
+ willUpdate(changedProperties) {
125
+ this._updateConfigContext(changedProperties);
126
+ if (changedProperties.has('multiSelect')) {
127
+ this.ariaMultiSelectable = this.multiSelect ? 'true' : 'false';
393
128
  }
394
- if (focused) {
395
- contentsClasses.push('focused');
396
- }
397
- return html `
398
- <li data-path=${path.join('/')} class=${liClasses.join(' ')}>
399
- <div
400
- class=${contentsClasses.join(' ')}
401
- .style=${stylePropertyMap({
402
- paddingLeft: `${padLeft + CONTENT_PADDING}px`,
403
- })}
404
- >
405
- ${arrowMarkup}${iconMarkup}<span
406
- class="text-content"
407
- part="text-content"
408
- title=${ifDefined(tooltip)}
409
- >${label}${descriptionMarkup}</span
410
- >
411
- ${actionsMarkup} ${decorationsMarkup}
412
- </div>
413
- ${subTreeMarkup}
414
- </li>
415
- `;
416
129
  }
417
- _renderTree(tree, oldPath = []) {
418
- const ret = [];
419
- if (!tree) {
420
- return nothing;
421
- }
422
- tree.forEach((item, index) => {
423
- const path = [...oldPath, index];
424
- const itemType = isBranch(item) ? 'branch' : 'leaf';
425
- const { selected = false, focused = false, hasFocusedItem = false, hasSelectedItem = false, } = item;
426
- if (selected) {
427
- this._selectedItem = item;
130
+ //#endregion
131
+ //#region public methods
132
+ expandAll() {
133
+ const children = this.querySelectorAll('vscode-tree-item');
134
+ children.forEach((item) => {
135
+ if (item.branch) {
136
+ item.open = true;
428
137
  }
429
- if (focused) {
430
- this._focusedItem = item;
138
+ });
139
+ }
140
+ collapseAll() {
141
+ const children = this.querySelectorAll('vscode-tree-item');
142
+ children.forEach((item) => {
143
+ if (item.branch) {
144
+ item.open = false;
431
145
  }
432
- ret.push(this._renderTreeItem(item, {
433
- path,
434
- itemType,
435
- hasFocusedItem,
436
- hasSelectedItem,
437
- }));
438
146
  });
439
- return ret;
440
147
  }
441
- _selectItem(item) {
442
- if (this._selectedItem) {
443
- this._selectedItem.selected = false;
444
- }
445
- if (this._focusedItem) {
446
- this._focusedItem.focused = false;
447
- }
448
- this._selectedItem = item;
449
- item.selected = true;
450
- this._focusedItem = item;
451
- item.focused = true;
452
- if (this._selectedBranch) {
453
- this._selectedBranch.hasSelectedItem = false;
454
- }
455
- let parentBranch = null;
456
- if (item.path?.length && item.path.length > 1) {
457
- parentBranch = this._getItemByPath(item.path.slice(0, -1));
458
- }
459
- if (isBranch(item)) {
460
- this._selectedBranch = item;
461
- item.hasSelectedItem = true;
462
- item.open = !item.open;
463
- if (!item.open) {
464
- if (parentBranch) {
465
- this._selectedBranch = parentBranch;
466
- parentBranch.hasSelectedItem = true;
467
- }
148
+ /**
149
+ * @internal
150
+ * Updates `hasBranchItem` property in the context state in order to removing
151
+ * extra padding before the leaf elements, if it is required.
152
+ */
153
+ updateHasBranchItemFlag() {
154
+ const hasBranchItem = this._assignedTreeItems.some((li) => li.branch);
155
+ this._treeContextState = { ...this._treeContextState, hasBranchItem };
156
+ }
157
+ //#endregion
158
+ //#region private methods
159
+ _emitSelectEvent() {
160
+ const ev = new CustomEvent('vsc-tree-select', {
161
+ detail: Array.from(this._treeContextState.selectedItems),
162
+ });
163
+ this.dispatchEvent(ev);
164
+ }
165
+ _highlightGuides() {
166
+ const { activeItem, highlightedItems, selectedItems } = this._treeContextState;
167
+ highlightedItems.forEach((i) => (i.highlightedGuides = false));
168
+ if (activeItem) {
169
+ this._treeContextState.highlightedItems = [];
170
+ if (activeItem.branch && activeItem.open) {
171
+ activeItem.highlightedGuides = true;
172
+ this._treeContextState.highlightedItems.push(activeItem);
468
173
  }
469
174
  else {
470
- this._selectedBranch = item;
471
- item.hasSelectedItem = true;
175
+ const parent = getParentItem(activeItem);
176
+ if (parent && parent.branch) {
177
+ parent.highlightedGuides = true;
178
+ this._treeContextState.highlightedItems.push(parent);
179
+ }
472
180
  }
473
181
  }
474
- else {
475
- if (item.path?.length && item.path.length > 1) {
476
- const parentBranch = this._getItemByPath(item.path.slice(0, -1));
477
- if (parentBranch) {
478
- this._selectedBranch = parentBranch;
479
- parentBranch.hasSelectedItem = true;
182
+ if (selectedItems) {
183
+ selectedItems.forEach((item) => {
184
+ if (item.branch && item.open) {
185
+ item.highlightedGuides = true;
186
+ this._treeContextState.highlightedItems.push(item);
480
187
  }
481
- }
482
- else {
483
- this._selectedBranch = item;
484
- item.hasSelectedItem = true;
485
- }
188
+ else {
189
+ const parent = getParentItem(item);
190
+ if (parent && parent.branch) {
191
+ parent.highlightedGuides = true;
192
+ this._treeContextState.highlightedItems.push(item);
193
+ }
194
+ }
195
+ });
486
196
  }
487
- this._emitSelectEvent(this._selectedItem, this._selectedItem.path.join('/'));
488
- this.requestUpdate();
489
197
  }
490
- _focusItem(item) {
491
- if (this._focusedItem) {
492
- this._focusedItem.focused = false;
198
+ _updateConfigContext(changedProperties) {
199
+ const { arrows, expandMode, indent, indentGuides, multiSelect } = this;
200
+ if (changedProperties.has('arrows')) {
201
+ this._configContext = { ...this._configContext, arrows };
493
202
  }
494
- this._focusedItem = item;
495
- item.focused = true;
496
- const isBranch = !!item?.subItems?.length;
497
- if (this._focusedBranch) {
498
- this._focusedBranch.hasFocusedItem = false;
203
+ if (changedProperties.has('expandMode')) {
204
+ this._configContext = { ...this._configContext, expandMode };
499
205
  }
500
- let parentBranch = null;
501
- if (item.path?.length && item.path.length > 1) {
502
- parentBranch = this._getItemByPath(item.path.slice(0, -1));
206
+ if (changedProperties.has('indent')) {
207
+ this._configContext = { ...this._configContext, indent };
503
208
  }
504
- if (!isBranch) {
505
- if (parentBranch) {
506
- this._focusedBranch = parentBranch;
507
- parentBranch.hasFocusedItem = true;
508
- }
209
+ if (changedProperties.has('indentGuides')) {
210
+ this._configContext = { ...this._configContext, indentGuides };
509
211
  }
510
- else {
511
- if (item.open) {
512
- this._focusedBranch = item;
513
- item.hasFocusedItem = true;
514
- }
515
- else if (!item.open && parentBranch) {
516
- this._focusedBranch = parentBranch;
517
- parentBranch.hasFocusedItem = true;
518
- }
212
+ if (changedProperties.has('multiSelect')) {
213
+ this._configContext = { ...this._configContext, multiSelect };
519
214
  }
520
215
  }
521
- _closeSubTreeRecursively(tree) {
522
- tree.forEach((item) => {
523
- item.open = false;
524
- if (item.subItems && item.subItems.length > 0) {
525
- this._closeSubTreeRecursively(item.subItems);
526
- }
216
+ _focusItem(item) {
217
+ item.active = true;
218
+ item.updateComplete.then(() => {
219
+ item.focus();
527
220
  });
528
221
  }
529
- _deselectItemsRecursively(tree) {
530
- tree.forEach((item) => {
531
- if (item.selected) {
532
- item.selected = false;
533
- }
534
- if (item.subItems && item.subItems.length > 0) {
535
- this._deselectItemsRecursively(item.subItems);
222
+ _focusPrevItem() {
223
+ if (this._treeContextState.focusedItem) {
224
+ const item = findPrevItem(this._treeContextState.focusedItem);
225
+ if (item) {
226
+ this._focusItem(item);
227
+ if (this._treeContextState.isShiftPressed && this.multiSelect) {
228
+ item.selected = !item.selected;
229
+ this._emitSelectEvent();
230
+ }
536
231
  }
537
- });
232
+ }
538
233
  }
539
- _emitSelectEvent(item, path) {
540
- const { icons, label, open, value } = item;
541
- const detail = {
542
- icons,
543
- itemType: isBranch(item) ? 'branch' : 'leaf',
544
- label,
545
- open: open || false,
546
- value: value || label,
547
- path,
548
- };
549
- /** @deprecated Renamed to `vsc-tree-select` */
550
- this.dispatchEvent(new CustomEvent('vsc-select', {
551
- bubbles: true,
552
- composed: true,
553
- detail,
554
- }));
555
- this.dispatchEvent(new CustomEvent('vsc-tree-select', {
556
- detail,
557
- }));
234
+ _focusNextItem() {
235
+ if (this._treeContextState.focusedItem) {
236
+ const item = findNextItem(this._treeContextState.focusedItem);
237
+ if (item) {
238
+ this._focusItem(item);
239
+ if (this._treeContextState.isShiftPressed && this.multiSelect) {
240
+ item.selected = !item.selected;
241
+ this._emitSelectEvent();
242
+ }
243
+ }
244
+ }
558
245
  }
559
- _focusPrevItem() {
560
- if (!this._focusedItem) {
561
- this._focusItem(this._data[0]);
246
+ //#endregion
247
+ //#region event handlers
248
+ _handleArrowRightPress() {
249
+ if (!this._treeContextState.focusedItem) {
562
250
  return;
563
251
  }
564
- const { path } = this._focusedItem;
565
- if (path && path?.length > 0) {
566
- const currentItemIndex = path[path.length - 1];
567
- const hasParent = path.length > 1;
568
- if (currentItemIndex > 0) {
569
- const newPath = [...path];
570
- newPath[newPath.length - 1] = currentItemIndex - 1;
571
- const prevSibling = this._getItemByPath(newPath);
572
- let newFocusedItem = prevSibling;
573
- if (prevSibling?.open && prevSibling.subItems?.length) {
574
- const { subItems } = prevSibling;
575
- newFocusedItem = subItems[subItems.length - 1];
576
- }
577
- this._focusItem(newFocusedItem);
252
+ const { focusedItem } = this._treeContextState;
253
+ if (focusedItem.branch) {
254
+ if (focusedItem.open) {
255
+ this._focusNextItem();
578
256
  }
579
257
  else {
580
- if (hasParent) {
581
- const newPath = [...path];
582
- newPath.pop();
583
- this._focusItem(this._getItemByPath(newPath));
584
- }
258
+ focusedItem.open = true;
585
259
  }
586
260
  }
587
- else {
588
- this._focusItem(this._data[0]);
589
- }
590
261
  }
591
- _focusNextItem() {
592
- if (!this._focusedItem) {
593
- this._focusItem(this._data[0]);
262
+ _handleArrowLeftPress(ev) {
263
+ if (ev.ctrlKey) {
264
+ this.collapseAll();
594
265
  return;
595
266
  }
596
- const { path, open } = this._focusedItem;
597
- if (open &&
598
- Array.isArray(this._focusedItem.subItems) &&
599
- this._focusedItem.subItems.length > 0) {
600
- this._focusItem(this._focusedItem.subItems[0]);
267
+ if (!this._treeContextState.focusedItem) {
601
268
  return;
602
269
  }
603
- const nextPath = [...path];
604
- nextPath[nextPath.length - 1] += 1;
605
- let nextFocusedItem = this._getItemByPath(nextPath);
606
- if (nextFocusedItem) {
607
- this._focusItem(nextFocusedItem);
270
+ const { focusedItem } = this._treeContextState;
271
+ const parent = getParentItem(focusedItem);
272
+ if (!focusedItem.branch) {
273
+ if (parent && parent.branch) {
274
+ this._focusItem(parent);
275
+ }
608
276
  }
609
277
  else {
610
- nextPath.pop();
611
- if (nextPath.length > 0) {
612
- nextPath[nextPath.length - 1] += 1;
613
- nextFocusedItem = this._getItemByPath(nextPath);
614
- if (nextFocusedItem) {
615
- this._focusItem(nextFocusedItem);
278
+ if (focusedItem.open) {
279
+ focusedItem.open = false;
280
+ }
281
+ else {
282
+ if (parent && parent.branch) {
283
+ this._focusItem(parent);
616
284
  }
617
285
  }
618
286
  }
619
287
  }
620
- _handleClick(event) {
621
- const composedPath = event.composedPath();
622
- const targetElement = composedPath.find((el) => el.tagName &&
623
- el.tagName.toUpperCase() === 'LI');
624
- if (targetElement) {
625
- const pathStr = targetElement.dataset.path || '';
626
- const path = pathStr.split('/').map((el) => Number(el));
627
- const item = this._getItemByPath(path);
628
- this._selectItem(item);
288
+ _handleArrowDownPress() {
289
+ if (this._treeContextState.focusedItem) {
290
+ this._focusNextItem();
629
291
  }
630
292
  else {
631
- if (this._focusedItem) {
632
- this._focusedItem.focused = false;
633
- }
634
- this._focusedItem = null;
293
+ this._focusItem(this._assignedTreeItems[0]);
635
294
  }
636
295
  }
637
- _handleComponentKeyDown(ev) {
638
- const keys = [
639
- ' ',
640
- 'ArrowDown',
641
- 'ArrowUp',
642
- 'Enter',
643
- 'Escape',
644
- ];
645
- const key = ev.key;
646
- if (keys.includes(ev.key)) {
647
- ev.stopPropagation();
648
- ev.preventDefault();
649
- }
650
- if (key === 'Escape') {
651
- this._focusedItem = null;
652
- }
653
- if (key === 'ArrowUp') {
296
+ _handleArrowUpPress() {
297
+ if (this._treeContextState.focusedItem) {
654
298
  this._focusPrevItem();
655
299
  }
656
- if (key === 'ArrowDown') {
657
- this._focusNextItem();
300
+ else {
301
+ this._focusItem(this._assignedTreeItems[0]);
658
302
  }
659
- if (key === 'Enter' || key === ' ') {
660
- if (this._focusedItem) {
661
- this._selectItem(this._focusedItem);
303
+ }
304
+ _handleEnterPress() {
305
+ const { focusedItem } = this._treeContextState;
306
+ if (focusedItem) {
307
+ this._treeContextState.selectedItems.forEach((li) => (li.selected = false));
308
+ focusedItem.selected = true;
309
+ this._emitSelectEvent();
310
+ if (focusedItem.branch) {
311
+ focusedItem.open = !focusedItem.open;
662
312
  }
663
313
  }
664
314
  }
315
+ _handleShiftPress() {
316
+ this._treeContextState.isShiftPressed = true;
317
+ }
318
+ //#endregion
665
319
  render() {
666
- const classes = classMap({
667
- multi: this.multiline,
668
- single: !this.multiline,
669
- wrapper: true,
670
- 'has-not-focused-item': !this._focusedItem,
671
- 'selection-none': !this._selectedItem,
672
- 'selection-single': this._selectedItem !== null,
673
- });
674
- return html `
675
- <div @click=${this._handleClick} class=${classes}>
676
- <ul>
677
- ${this._renderTree(this._data)}
678
- </ul>
679
- </div>
680
- `;
320
+ return html `<div>
321
+ <slot @slotchange=${this._handleSlotChange}></slot>
322
+ </div>`;
681
323
  }
682
324
  };
683
325
  VscodeTree.styles = styles;
684
- __decorate([
685
- property({ type: Array, reflect: false })
686
- ], VscodeTree.prototype, "data", null);
687
- __decorate([
688
- property({ type: Number })
689
- ], VscodeTree.prototype, "indent", void 0);
690
326
  __decorate([
691
327
  property({ type: Boolean, reflect: true })
692
328
  ], VscodeTree.prototype, "arrows", void 0);
693
329
  __decorate([
694
- property({ type: Boolean, reflect: true })
695
- ], VscodeTree.prototype, "multiline", void 0);
330
+ property({ type: String, attribute: 'expand-mode' })
331
+ ], VscodeTree.prototype, "expandMode", void 0);
696
332
  __decorate([
697
333
  property({ type: Number, reflect: true })
698
- ], VscodeTree.prototype, "tabindex", void 0);
334
+ ], VscodeTree.prototype, "indent", void 0);
699
335
  __decorate([
700
- property({ type: Boolean, reflect: true, attribute: 'indent-guides' })
336
+ property({ type: Boolean, attribute: 'indent-guides', reflect: true })
701
337
  ], VscodeTree.prototype, "indentGuides", void 0);
702
338
  __decorate([
703
- state()
704
- ], VscodeTree.prototype, "_selectedItem", void 0);
339
+ property({ type: Boolean, reflect: true, attribute: 'multi-select' })
340
+ ], VscodeTree.prototype, "multiSelect", void 0);
705
341
  __decorate([
706
- state()
707
- ], VscodeTree.prototype, "_focusedItem", void 0);
342
+ provide({ context: treeContext })
343
+ ], VscodeTree.prototype, "_treeContextState", void 0);
708
344
  __decorate([
709
- state()
710
- ], VscodeTree.prototype, "_selectedBranch", void 0);
345
+ provide({ context: configContext })
346
+ ], VscodeTree.prototype, "_configContext", void 0);
711
347
  __decorate([
712
- state()
713
- ], VscodeTree.prototype, "_focusedBranch", void 0);
348
+ queryAssignedElements({ selector: 'vscode-tree-item' })
349
+ ], VscodeTree.prototype, "_assignedTreeItems", void 0);
714
350
  VscodeTree = __decorate([
715
351
  customElement('vscode-tree')
716
352
  ], VscodeTree);