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