1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import './LitTreeNode';
17import { BaseElement, element } from '../BaseElement';
18import { type LitTreeNode } from './LitTreeNode';
19
20export interface TreeItemData {
21  key: string;
22  title: string;
23  icon?: string; //节点的自定义图标  设置show-icon才会生效
24  selected?: boolean; //控制是否选择该节点
25  checked?: boolean;
26  disable?: boolean; //控制是否显示checkbox
27  children?: Array<TreeItemData> | null | undefined;
28}
29
30@element('lit-tree')
31export class LitTree extends BaseElement {
32  private _treeData: Array<TreeItemData> = [];
33  private currentSelectedNode: unknown;
34  private currentSelectedData: unknown;
35  private proxyData: unknown;
36  private nodeList: Array<LitTreeNode> = [];
37  private contextMenu: HTMLDivElement | null | undefined;
38  private srcDragElement: unknown;
39  private dragDirection: string | null | undefined;
40
41  static get observedAttributes(): string[] {
42    return ['show-line', 'show-icon', 'checkable', 'foldable', 'dragable', 'multiple']; //foldable 表示点击目录是否可以折叠
43  }
44
45  set treeData(value: Array<TreeItemData>) {
46    this._treeData = value;
47    this.shadowRoot!.querySelector('#root')!.innerHTML = '';
48    this.nodeList = [];
49    this.drawTree(this.shadowRoot!.querySelector('#root'), value, true);
50
51    /*双向绑定*/
52    const handler = {
53      get: (target: unknown, propkey: unknown): unknown => {
54        //@ts-ignore
55        return target[propkey];
56      },
57      set: (target: unknown, propkey: unknown, value: unknown, receiver: unknown): boolean => {
58        //@ts-ignore
59        if (target[propkey] !== value) {
60          //@ts-ignore
61          if (!value.children) {
62            //@ts-ignore
63            value.children = new Proxy([], handler);
64          } else {
65            //@ts-ignore
66            value.children = new Proxy(value.children, handler);
67          } //@ts-ignore
68          target[propkey] = value;
69          if (!this.currentSelectedNode) {
70            this._insertNode(null, value);
71          } else {
72            //@ts-ignore
73            if (this.currentSelectedNode.nextElementSibling) {
74              //@ts-ignore
75              this._insertNode(this.currentSelectedNode.nextElementSibling, value);
76            } else {
77              //@ts-ignore
78              this.currentSelectedNode.setAttribute('show-arrow', 'true');
79              let ul = document.createElement('ul');
80              // @ts-ignore
81              ul.open = 'true';
82              ul.style.transition = '.3s all'; //@ts-ignore
83              this.currentSelectedNode.parentElement.append(ul); //@ts-ignore
84              this.currentSelectedNode.arrow = true;
85              this._insertNode(ul, value);
86            }
87          }
88        }
89        return true;
90      },
91    };
92    let setProxy = (v: Array<TreeItemData>): void => {
93      v.forEach((a) => {
94        if (!a.children) {
95          //@ts-ignore
96          a.children = new Proxy([], handler);
97        } else {
98          //@ts-ignore
99          a.children = new Proxy(a.children, handler);
100          setProxy(a.children || []);
101        }
102      });
103    };
104    setProxy(this._treeData);
105    this.proxyData = new Proxy(this._treeData, handler);
106  }
107
108  get multiple(): boolean {
109    return this.hasAttribute('multiple');
110  }
111
112  set multiple(value: boolean) {
113    if (value) {
114      this.setAttribute('multiple', '');
115    } else {
116      this.removeAttribute('multiple');
117    }
118  }
119
120  get treeData(): TreeItemData[] {
121    //@ts-ignore
122    return this.proxyData;
123  }
124
125  set onSelect(fn: unknown) {
126    //@ts-ignore
127    this.addEventListener('onSelect', fn);
128  }
129
130  set onChange(fn: unknown) {
131    //@ts-ignore
132    this.addEventListener('onChange', fn);
133  }
134
135  set foldable(value: unknown) {
136    if (value) {
137      this.setAttribute('foldable', '');
138    } else {
139      this.removeAttribute('foldable');
140    }
141  }
142
143  //当 custom element首次被插入文档DOM时,被调用。
144  connectedCallback(): void {
145    this.onclick = (ev): void => {
146      this.contextMenu!.style.display = 'none';
147      this.currentSelectedData = null;
148      this.currentSelectedNode = null;
149      this.selectedNode(null);
150    };
151  }
152
153  getCheckdKeys(): unknown[] {
154    //@ts-ignore
155    return Array.from(this.shadowRoot!.querySelectorAll('lit-tree-node[checked]')).map((a: unknown) => a.data.key);
156  }
157
158  getCheckdNodes(): unknown[] {
159    //@ts-ignore
160    return Array.from(this.shadowRoot!.querySelectorAll('lit-tree-node[checked]')).map((a: unknown) => a.data);
161  }
162
163  //展开所有节点
164  expandKeys(keys: Array<string>): void {
165    keys.forEach((k) =>
166      //@ts-ignore
167      this.shadowRoot!.querySelectorAll(`lit-tree-node[key='${k}']`).forEach((b: unknown) => b.expand())
168    );
169  }
170
171  collapseKeys(keys: Array<string>): void {
172    keys.forEach((k) =>
173      //@ts-ignore
174      this.shadowRoot!.querySelectorAll(`lit-tree-node[key='${k}']`).forEach((b: unknown) => b.collapse())
175    );
176  }
177
178  checkedKeys(keys: Array<string>): void {
179    keys.forEach((k) =>
180      this.shadowRoot!.querySelectorAll(`lit-tree-node[key='${k}']`).forEach((b: unknown) => {
181        //@ts-ignore
182        b.setAttribute('checked', 'true');
183        //@ts-ignore
184        b.checkHandler();
185      })
186    );
187  }
188
189  uncheckedKeys(keys: Array<string>): void {
190    keys.forEach((k) =>
191      this.shadowRoot!.querySelectorAll(`lit-tree-node[key='${k}']`).forEach((b: unknown) => {
192        //@ts-ignore
193        b.removeAttribute('checked'); //@ts-ignore
194        b.removeAttribute('missing'); //@ts-ignore
195        b.checkHandler();
196      })
197    );
198  }
199
200  drawTree(parent: unknown, array: Array<TreeItemData>, topDepth: boolean = false): void {
201    array.forEach((a: TreeItemData) => {
202      let li: HTMLLIElement = document.createElement('li');
203      let node: LitTreeNode = document.createElement('lit-tree-node') as LitTreeNode;
204      node.title = a.title;
205      node.setAttribute('key', a.key);
206      node.topDepth = topDepth;
207      this.treeNodeDragable(node, a);
208      // @ts-ignore
209      li.data = a;
210      li.append(node); //@ts-ignore
211      parent.append(li);
212      let ul: HTMLUListElement = document.createElement('ul');
213      // @ts-ignore
214      ul.open = 'true';
215      ul.style.transition = '.3s all';
216      this.addEvent(a, node, li, ul);
217      // node 添加右键菜单功能
218      node.oncontextmenu = (ev): void => {
219        ev.preventDefault();
220        this.selectedNode(node);
221        this.currentSelectedNode = node;
222        this.currentSelectedData = node.data;
223        this.contextMenu!.style.display = 'block';
224        this.contextMenu!.style.left = ev.pageX + 'px';
225        this.contextMenu!.style.top = ev.pageY + 'px';
226      };
227    });
228    this.oncontextmenu = (ev): void => {
229      ev.preventDefault();
230      this.contextMenu!.style.display = 'block';
231      this.contextMenu!.style.left = ev.pageX + 'px';
232      this.contextMenu!.style.top = ev.pageY + 'px';
233    };
234  }
235
236  treeNodeDragable(node: LitTreeNode, a: TreeItemData): void {
237    let that = this;
238    if (this.hasAttribute('dragable')) {
239      node.draggable = true;
240      document.ondragover = function (e) {
241        e.preventDefault();
242      };
243      //在拖动目标上触发事件 (源元素)
244      node.ondrag = (ev) => this.onDrag(ev); //元素正在拖动时触发
245      node.ondragstart = (ev) => this.onDragStart(ev); //用户开始拖动元素时触发
246      node.ondragend = (ev) => this.onDragEnd(ev); // 用户完成元素拖动后触发
247      //释放目标时触发的事件:
248      node.ondragenter = (ev) => this.onDragEnter(ev); //当被鼠标拖动的对象进入其容器范围内时触发此事件
249      node.ondragover = (ev) => this.onDragOver(ev); //当某被拖动的对象在另一对象容器范围内拖动时触发此事件
250      node.ondragleave = (ev) => this.onDragLeave(ev); //当被鼠标拖动的对象离开其容器范围内时触发此事件
251      node.ondrop = (ev) => this.onDrop(ev); //在一个拖动过程中,释放鼠标键时触发此事件
252    }
253    node.selected = a.selected || false; //是否选中行
254    node.checked = a.checked || false; // 是否勾选
255    node.data = a;
256    node.addEventListener('change', (e: unknown): void => {
257      //@ts-ignore
258      if (e.detail && !this.multiple) {
259        this.nodeList.forEach((item) => {
260          item.checked = item.data!.key === node.data!.key;
261          item.data!.checked = item.checked;
262        });
263      }
264      let litTreeNodes = this.nodeList.filter((it) => it.checked);
265      if (litTreeNodes.length === 0) {
266        node.checked = true;
267        node.data!.checked = true;
268      } //@ts-ignore
269      that.dispatchEvent(new CustomEvent('onChange', { detail: { data: (node as unknown).data, checked: e.detail } }));
270    });
271    node.multiple = this.hasAttribute('multiple');
272    node.checkable = this.getAttribute('checkable') || 'false';
273    this.nodeList.push(node);
274  }
275
276  addEvent(a: TreeItemData, node: LitTreeNode, li: HTMLLIElement, ul: HTMLUListElement): void {
277    if (a.children && a.children.length > 0) {
278      if (this.hasAttribute('show-icon')) {
279        if (a.icon) {
280          //@ts-ignore
281          (node as unknown).iconName = a.icon;
282        } else {
283          //@ts-ignore
284          (node as unknown).iconName = 'folder';
285        }
286      } else {
287        node.iconName = '';
288      }
289      node.arrow = true;
290      li.append(ul);
291      this.drawTree(ul, a.children);
292    } else {
293      if (this.hasAttribute('show-icon')) {
294        if (a.icon) {
295          node.iconName = a.icon;
296        } else {
297          node.iconName = 'file';
298        }
299      } else {
300        node.iconName = '';
301      }
302      node.arrow = false;
303    }
304    li.onclick = (e): void => {
305      e.stopPropagation();
306      if (this.hasAttribute('foldable')) {
307        // @ts-ignore
308        if (li.data.children && li.data.children.length > 0) {
309          node.autoExpand();
310        } else {
311          // @ts-ignore
312          this.dispatchEvent(new CustomEvent('onSelect', { detail: li.data }));
313          this.selectedNode(node);
314        }
315      } else {
316        // @ts-ignore
317        this.dispatchEvent(new CustomEvent('onSelect', { detail: li.data }));
318        this.selectedNode(node);
319      }
320    };
321  }
322
323  //取消所有节点的选中状态 然后选中当前node节点
324  selectedNode(node: LitTreeNode | null | undefined): void {
325    this.shadowRoot!.querySelectorAll<LitTreeNode>('lit-tree-node').forEach((a) => {
326      a.selected = false;
327    });
328    if (node) {
329      node.selected = true;
330    }
331  }
332
333  closeContextMenu(): void {
334    this.contextMenu!.style.display = 'none';
335  }
336
337  onDrag(e: MouseEvent): void { }
338
339  onDragStart(ev: MouseEvent): undefined {
340    this.srcDragElement = ev.target;
341    (ev.target! as LitTreeNode).open = 'true';
342    (ev.target! as LitTreeNode).autoExpand();
343    return undefined;
344  }
345
346  onDragEnd(ev: MouseEvent): undefined {
347    this.srcDragElement = null;
348    return undefined;
349  }
350
351  onDragEnter(ev: MouseEvent): undefined {
352    (ev.target as LitTreeNode).style.backgroundColor = '#42b98333';
353    return undefined;
354  }
355
356  onDragOver(ev: MouseEvent): undefined {
357    let node = ev.target as LitTreeNode; //@ts-ignore
358    if (this.srcDragElement.data.key === node.data!.key) {
359      return undefined;
360    } //@ts-ignore
361    let rect = (ev.currentTarget! as unknown).getBoundingClientRect();
362    if (ev.clientX >= rect.left + rect.width / 3 && ev.clientX < rect.left + rect.width) {
363      //bottom-right
364      this.dragDirection = 'bottom-right';
365      node.drawLine('bottom-right');
366    } else if (ev.clientY >= rect.top && ev.clientY < rect.top + rect.height / 2) {
367      //上面
368      this.dragDirection = 'top';
369      node.drawLine('top');
370    } else if (ev.clientY <= rect.bottom && ev.clientY > rect.top + rect.height / 2) {
371      //下面
372      this.dragDirection = 'bottom';
373      node.drawLine('bottom');
374    }
375    return undefined;
376  }
377
378  onDragLeave(ev: MouseEvent): undefined {
379    (ev.target as LitTreeNode).style.backgroundColor = '#ffffff00';
380    (ev.target as LitTreeNode).drawLine('');
381    return undefined;
382  }
383
384  onDrop(ev: MouseEvent): undefined {
385    (ev.target as LitTreeNode).style.backgroundColor = '#ffffff00';
386    (ev.target as LitTreeNode).drawLine('');
387    //移动的不是node节点 而是上层的li节点
388    //@ts-ignore
389    let srcData = this.srcDragElement.data; //获取原节点的data数据
390    let dstData = (ev.target as LitTreeNode).data; //获取目标节点的data数据
391    if (srcData.key === dstData!.key) {
392      return undefined;
393    } //同一个节点不用处理
394    //@ts-ignore
395    let srcElement = this.srcDragElement.parentElement;
396    let srcParentElement = srcElement.parentElement;
397    let dstElement = (ev.target as LitTreeNode).parentElement;
398    srcElement.parentElement.removeChild(srcElement); //node li ul  从 ul 中移除 li
399    if (this.dragDirection === 'top') {
400      dstElement!.parentElement!.insertBefore(srcElement, dstElement);
401    } else if (this.dragDirection === 'bottom') {
402      dstElement!.parentElement!.insertBefore(srcElement, dstElement!.nextSibling);
403    } else if (this.dragDirection === 'bottom-right') {
404      let ul = dstElement!.querySelector('ul');
405      if (!ul) {
406        ul = document.createElement('ul');
407        ul.style.cssText = 'transition: all 0.3s ease 0s;';
408        dstElement!.appendChild(ul);
409      }
410      dstElement!.querySelector<LitTreeNode>('lit-tree-node')!.arrow = true; //拖动进入子节点,需要开启箭头
411      ul.appendChild(srcElement);
412    }
413    let ul1 = dstElement!.querySelector('ul'); //如果拖动后目标节点的子节点没有记录,需要关闭arrow箭头
414    if (ul1) {
415      if (ul1.childElementCount === 0) {
416        (ul1.previousElementSibling! as LitTreeNode).arrow = false;
417      }
418    }
419    if (srcParentElement.childElementCount === 0) {
420      srcParentElement.previousElementSibling.arrow = false;
421    } //如果拖动的原节点的父节点没有子节点需要 关闭arrow箭头
422    //拖动调整结构后修改 data树形数据结构
423    this.removeDataNode(this._treeData, srcData);
424    this.addDataNode(this._treeData, srcData, dstData!.key, this.dragDirection!);
425    this.dispatchEvent(
426      new CustomEvent('drop', {
427        detail: {
428          treeData: this._treeData,
429          srcData: srcData,
430          dstData: dstData,
431          type: this.dragDirection,
432        },
433      })
434    );
435    ev.stopPropagation();
436    return undefined;
437  }
438
439  //移除treeData中指定的节点 通过key匹配
440  removeDataNode(arr: Array<TreeItemData>, d: TreeItemData): void {
441    let delIndex = arr.findIndex((v) => v.key === d.key);
442    if (delIndex > -1) {
443      arr.splice(delIndex, 1);
444      return;
445    }
446    for (let i = 0; i < arr.length; i++) {
447      if (arr[i].children && arr[i].children!.length > 0) {
448        this.removeDataNode(arr[i].children!, d);
449      }
450    }
451  }
452
453  //中array中匹配到key为k的节点, t='bottom-right' 把d加入到该节点的children中去 t='top' 添加到找到的节点前面 t='bottom' 添加到找到的节点后面
454  addDataNode(arr: Array<TreeItemData>, d: TreeItemData, k: string, t: string): void {
455    for (let i = 0; i < arr.length; i++) {
456      if (arr[i].key === k) {
457        if (t === 'bottom-right') {
458          if (!arr[i].children) {
459            arr[i].children = [];
460          }
461          arr[i].children!.push(d);
462        } else if (t === 'top') {
463          arr.splice(i, 0, d);
464        } else if (t === 'bottom') {
465          arr.splice(i + 1, 0, d);
466        }
467        return;
468      } else {
469        if (arr[i].children) {
470          this.addDataNode(arr[i].children || [], d, k, t);
471        }
472      }
473    }
474  }
475
476  insert(obj: TreeItemData): void {
477    if (this.currentSelectedData) {
478      //@ts-ignore
479      this.currentSelectedData.children.push(obj);
480    } else {
481      this.treeData.push(obj);
482    }
483  }
484  insertNodeDragEvent(insertNode: LitTreeNode) {
485    if (this.hasAttribute('dragable')) {
486      insertNode.draggable = true;
487      document.ondragover = function (e): void {
488        e.preventDefault();
489      };
490      //在拖动目标上触发事件 (源元素)
491      insertNode.ondrag = (ev): void => this.onDrag(ev); //元素正在拖动时触发
492      insertNode.ondragstart = (ev): undefined => this.onDragStart(ev); //用户开始拖动元素时触发
493      insertNode.ondragend = (ev): undefined => this.onDragEnd(ev); // 用户完成元素拖动后触发
494      //释放目标时触发的事件:
495      insertNode.ondragenter = (ev): undefined => this.onDragEnter(ev); //当被鼠标拖动的对象进入其容器范围内时触发此事件
496      insertNode.ondragover = (ev): undefined => this.onDragOver(ev); //当某被拖动的对象在另一对象容器范围内拖动时触发此事件
497      insertNode.ondragleave = (ev): undefined => this.onDragLeave(ev); //当被鼠标拖动的对象离开其容器范围内时触发此事件
498      insertNode.ondrop = (ev): undefined => this.onDrop(ev); //在一个拖动过程中,释放鼠标键时触发此事件
499    }
500  }
501  _insertNode(parent: unknown, a: unknown): void {
502    if (!parent) {
503      parent = this.shadowRoot!.querySelector('#root');
504    }
505    let li: HTMLLIElement = document.createElement('li');
506    let insertNode: LitTreeNode = document.createElement('lit-tree-node') as LitTreeNode; //@ts-ignore
507    insertNode.title = a.title; //@ts-ignore
508    insertNode.setAttribute('key', a.key);
509    this.setDragableOfEvent(insertNode);
510    if (this.hasAttribute('dragable')) {
511      insertNode.draggable = true;
512      document.ondragover = function (e): void {
513        e.preventDefault();
514      };
515      //在拖动目标上触发事件 (源元素)
516      insertNode.ondrag = (ev): void => this.onDrag(ev); //元素正在拖动时触发
517      insertNode.ondragstart = (ev): undefined => this.onDragStart(ev); //用户开始拖动元素时触发
518      insertNode.ondragend = (ev): undefined => this.onDragEnd(ev); // 用户完成元素拖动后触发
519      //释放目标时触发的事件:
520      insertNode.ondragenter = (ev): undefined => this.onDragEnter(ev); //当被鼠标拖动的对象进入其容器范围内时触发此事件
521      insertNode.ondragover = (ev): undefined => this.onDragOver(ev); //当某被拖动的对象在另一对象容器范围内拖动时触发此事件
522      insertNode.ondragleave = (ev): undefined => this.onDragLeave(ev); //当被鼠标拖动的对象离开其容器范围内时触发此事件
523      insertNode.ondrop = (ev): undefined => this.onDrop(ev); //在一个拖动过程中,释放鼠标键时触发此事件
524    } //@ts-ignore
525    insertNode.selected = a.selected || false; //是否选中行
526    //@ts-ignore
527    insertNode.checked = a.checked || false; // 是否勾选
528    //@ts-ignore
529    insertNode.data = a;
530    insertNode.addEventListener('change', (e: unknown) => {
531      //@ts-ignore
532      if (e.detail && !this.multiple) {
533        this.nodeList.forEach((node) => {
534          node.checked = node.data!.key === insertNode.data!.key;
535        });
536      } //@ts-ignore
537      this.dispatchEvent(new CustomEvent('onChange', { detail: { data: insertNode.data, checked: e.detail } }));
538    });
539    this.nodeList.push(insertNode);
540    insertNode.checkable = this.getAttribute('checkable') || 'false';
541    insertNode.multiple = this.hasAttribute('multiple');
542    // @ts-ignore
543    li.data = a;
544    li.append(insertNode); //@ts-ignore
545    parent.append(li);
546    let ul: HTMLUListElement = document.createElement('ul');
547    // @ts-ignore
548    ul.open = 'true';
549    ul.style.transition = '.3s all';
550    this.setChildren(a, insertNode, li, ul);
551    // node 添加右键菜单功能
552    this.addedRightClickMenuFunction(insertNode);
553  }
554
555  addedRightClickMenuFunction(insertNode: LitTreeNode): void {
556    insertNode.oncontextmenu = (ev): void => {
557      ev.preventDefault();
558      this.selectedNode(insertNode);
559      this.currentSelectedNode = insertNode;
560      this.currentSelectedData = insertNode.data;
561      this.contextMenu!.style.display = 'block';
562      this.contextMenu!.style.left = ev.pageX + 'px';
563      this.contextMenu!.style.top = ev.pageY + 'px';
564    };
565  }
566
567  setDragableOfEvent(insertNode: LitTreeNode): void {
568    if (this.hasAttribute('dragable')) {
569      insertNode.draggable = true;
570      document.ondragover = function (e): void {
571        e.preventDefault();
572      };
573      //在拖动目标上触发事件 (源元素)
574      insertNode.ondrag = (ev): void => this.onDrag(ev); //元素正在拖动时触发
575      insertNode.ondragstart = (ev): undefined => this.onDragStart(ev); //用户开始拖动元素时触发
576      insertNode.ondragend = (ev): undefined => this.onDragEnd(ev); // 用户完成元素拖动后触发
577      //释放目标时触发的事件:
578      insertNode.ondragenter = (ev): undefined => this.onDragEnter(ev); //当被鼠标拖动的对象进入其容器范围内时触发此事件
579      insertNode.ondragover = (ev): undefined => this.onDragOver(ev); //当某被拖动的对象在另一对象容器范围内拖动时触发此事件
580      insertNode.ondragleave = (ev): undefined => this.onDragLeave(ev); //当被鼠标拖动的对象离开其容器范围内时触发此事件
581      insertNode.ondrop = (ev): undefined => this.onDrop(ev); //在一个拖动过程中,释放鼠标键时触发此事件
582    }
583  }
584
585  setChildren(a: unknown, insertNode: LitTreeNode, li: HTMLLIElement, ul: HTMLUListElement): void {
586    //@ts-ignore
587    if (a.children && a.children.length > 0) {
588      if (this.hasAttribute('show-icon')) {
589        //@ts-ignore
590        if (a.icon) {
591          //@ts-ignore
592          insertNode.iconName = a.icon;
593        } else {
594          insertNode.iconName = 'folder';
595        }
596      } else {
597        insertNode.iconName = '';
598      }
599      insertNode.arrow = true;
600      li.append(ul); //@ts-ignore
601      this.drawTree(ul, a.children);
602    } else {
603      if (this.hasAttribute('show-icon')) {
604        //@ts-ignore
605        if (a.icon) {
606          //@ts-ignore
607          insertNode.iconName = a.icon;
608        } else {
609          insertNode.iconName = 'file';
610        }
611      } else {
612        insertNode.iconName = '';
613      }
614      insertNode.arrow = false;
615    }
616    li.onclick = (e): void => {
617      e.stopPropagation();
618      if (this.hasAttribute('foldable')) {
619        // @ts-ignore
620        if (li.data.children && li.data.children.length > 0) {
621          insertNode.autoExpand();
622        } else {
623          // @ts-ignore
624          this.dispatchEvent(new CustomEvent('onSelect', { detail: li.data }));
625          this.selectedNode(insertNode);
626        }
627      } else {
628        // @ts-ignore
629        this.dispatchEvent(new CustomEvent('onSelect', { detail: li.data }));
630        this.selectedNode(insertNode);
631      }
632    };
633  }
634
635  initElements(): void {
636    this.contextMenu = this.shadowRoot!.querySelector<HTMLDivElement>('#contextMenu');
637  }
638
639  initHtml(): string {
640    return `
641        <style>
642        :host{ 
643            display: flex;
644            color:#333;
645            width: 100%;
646            height: 100%;
647            overflow: auto;
648        }
649        :host *{
650            box-sizing: border-box;
651        }
652        ul,li{
653            list-style-type: none;
654            position:relative;
655            cursor:pointer;
656            overflow: hidden;
657        }
658        :host([show-line]) ul{
659            padding-left:10px;
660            border-left: 1px solid #d9d9d9;
661            overflow: hidden;
662        }
663        :host(:not([show-line])) ul{
664            padding-left: 10px;
665            overflow: hidden;
666        }
667        /*ul>li:after{content:' ';position:absolute;top:50%;left:-45px;width:45px;border:none;border-top:1px solid #d9d9d9;}*/
668        #root{
669            width: 100%;
670            height: 100%;
671        }
672        .context-menu { 
673            position: absolute; 
674            box-shadow: 0 0 10px #00000077;
675            pointer-events: auto;
676            z-index: 999;
677            transition: all .3s;
678        } 
679        </style>
680        <ul id="root" style="margin: 0;overflow: hidden"></ul>
681        <div id="contextMenu" class="context-menu" style="display:none"> 
682            <slot name="contextMenu"></slot>
683        </div> 
684        `;
685  }
686}
687
688if (!customElements.get('lit-tree')) {
689  customElements.define('lit-tree', LitTree);
690}
691