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