• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 '../icon/LitIcon';
17import { BaseElement, element } from '../BaseElement';
18import { type LitIcon } from '../icon/LitIcon';
19import { type TreeItemData } from './LitTree';
20import {LitTreeNodeHtmlStyle} from "./LitTreeNode.html";
21
22@element('lit-tree-node')
23export class LitTreeNode extends BaseElement {
24  private arrowElement: HTMLSpanElement | null | undefined;
25  private itemElement: HTMLDivElement | null | undefined;
26  private checkboxElement: HTMLInputElement | null | undefined;
27  private iconElement: LitIcon | null | undefined;
28  private _data: TreeItemData | null | undefined;
29
30  static get observedAttributes(): string[] {
31    return [
32      'icon-name',
33      'icon-size',
34      'color',
35      'path',
36      'title',
37      'arrow',
38      'checkable',
39      'selected',
40      'checked',
41      'missing',
42      'multiple',
43      'top-depth',
44    ];
45  }
46
47  get checkable(): string {
48    return this.getAttribute('checkable') || 'false';
49  }
50
51  set data(value: TreeItemData | null | undefined) {
52    this._data = value;
53  }
54
55  get data(): TreeItemData | null | undefined {
56    return this._data;
57  }
58
59  set checkable(value) {
60    if (value === 'true') {
61      this.setAttribute('checkable', 'true');
62    } else {
63      this.setAttribute('checkable', 'false');
64    }
65  }
66
67  set multiple(value: boolean) {
68    if (value) {
69      this.setAttribute('multiple', '');
70    } else {
71      this.removeAttribute('multiple');
72    }
73  }
74
75  get multiple(): boolean {
76    return this.hasAttribute('multiple');
77  }
78
79  get iconName(): string {
80    return this.getAttribute('icon-name') || '';
81  }
82
83  set iconName(value) {
84    this.setAttribute('icon-name', value);
85  }
86
87  get topDepth(): boolean {
88    return this.hasAttribute('top-depth');
89  }
90
91  set topDepth(value) {
92    if (value) {
93      this.setAttribute('top-depth', '');
94    } else {
95      this.removeAttribute('top-depth');
96    }
97  }
98
99  get arrow(): boolean {
100    return this.hasAttribute('arrow');
101  }
102
103  set arrow(value) {
104    if (value) {
105      this.setAttribute('arrow', 'true');
106    } else {
107      this.removeAttribute('arrow');
108    }
109  }
110
111  get open(): string {
112    return this.getAttribute('open') || 'true';
113  }
114
115  set open(value) {
116    this.setAttribute('open', value);
117  }
118
119  get selected(): boolean {
120    return this.hasAttribute('selected');
121  }
122
123  set selected(value) {
124    if (value) {
125      this.setAttribute('selected', '');
126    } else {
127      this.removeAttribute('selected');
128    }
129  }
130
131  get checked(): boolean {
132    return this.hasAttribute('checked');
133  }
134
135  set checked(value) {
136    if (value === null || !value) {
137      this.removeAttribute('checked');
138    } else {
139      this.setAttribute('checked', '');
140    }
141  }
142
143  initElements(): void {
144    this.arrowElement = this.shadowRoot!.querySelector<HTMLSpanElement>('#arrow');
145    this.iconElement = this.shadowRoot!.querySelector<LitIcon>('#icon');
146    this.itemElement = this.shadowRoot!.querySelector<HTMLDivElement>('#item');
147    this.checkboxElement = this.shadowRoot!.querySelector<HTMLInputElement>('#checkbox');
148    this.arrowElement!.onclick = (e): void => {
149      e.stopPropagation();
150      this.autoExpand();
151    };
152    this.itemElement!.onclick = (e): void => {
153      e.stopPropagation();
154      if (this._data && this._data.disable === true) {
155        return;
156      }
157      this.onChange(!this.data?.checked);
158    };
159  }
160
161  onChange(checked: boolean): void {
162    this.checked = checked;
163    this.data!.checked = checked;
164    this.checkHandler();
165    this.dispatchEvent(new CustomEvent('change', { detail: checked }));
166  }
167
168  initHtml(): string {
169    return `
170         ${LitTreeNodeHtmlStyle}
171        </style>
172        <span id="arrow" style="margin-right: 2px"></span>
173        <div id="item" style="display: flex;align-items: center;padding-left: 2px">
174<!--            <lit-check-box id="checkbox"></lit-check-box>-->
175            <input id="checkbox" type="radio" style="cursor: pointer; pointer-events: none"/>
176            <lit-icon id="icon" name="${this.iconName}"></lit-icon>
177            <span id="title">${this.title}</span>
178        </div>
179        `;
180  }
181
182  //当 custom element首次被插入文档DOM时,被调用。
183  connectedCallback(): void {
184    if (this.hasAttribute('checked')) {
185      this.checkboxElement!.checked = true;
186    }
187    this.checkHandler();
188  }
189
190  checkHandler(): void {
191    if (this.checked) {
192      this.removeAttribute('missing');
193    }
194    if (this.hasAttribute('multiple')) {
195      if (this.nextElementSibling) {
196        if (this.checked) {
197          this.nextElementSibling.querySelectorAll('lit-tree-node').forEach((a: any) => {
198            a.checked = true;
199            a.removeAttribute('missing');
200          });
201        } else {
202          this.nextElementSibling.querySelectorAll('lit-tree-node').forEach((a: any) => (a.checked = false));
203        }
204      }
205      let setCheckStatus = (element: any): void => {
206        if (
207          element.parentElement.parentElement.previousElementSibling &&
208          element.parentElement.parentElement.previousElementSibling.tagName === 'LIT-TREE-NODE'
209        ) {
210          let allChecked = Array.from(element.parentElement.parentElement.querySelectorAll('lit-tree-node')).every(
211            (item: any) => item.checked
212          );
213          let someChecked = Array.from(element.parentElement.parentElement.querySelectorAll('lit-tree-node')).some(
214            (item: any, index, array) => item.checked
215          );
216          if (allChecked === true) {
217            element.parentElement.parentElement.previousElementSibling.checked = true;
218            element.parentElement.parentElement.previousElementSibling.removeAttribute('missing');
219          } else if (someChecked) {
220            element.parentElement.parentElement.previousElementSibling.setAttribute('missing', '');
221            element.parentElement.parentElement.previousElementSibling.removeAttribute('checked');
222          } else {
223            element.parentElement.parentElement.previousElementSibling.removeAttribute('missing');
224            element.parentElement.parentElement.previousElementSibling.removeAttribute('checked');
225          }
226          setCheckStatus(element.parentElement.parentElement.previousElementSibling);
227        }
228      };
229      setCheckStatus(this);
230    }
231  }
232
233  expand(): void {
234    if (this.open === 'true') {
235      return;
236    }
237    let uul = this.parentElement!.querySelector('ul');
238    this.expandSection(uul);
239    this.arrowElement!.style.transform = 'translateX(-50%) rotateZ(0deg)';
240  }
241
242  collapse(): void {
243    if (this.open === 'false') {
244      return;
245    }
246    let uul = this.parentElement!.querySelector('ul');
247    this.collapseSection(uul);
248    this.arrowElement!.style.transform = 'translateX(-50%) rotateZ(-90deg)';
249  }
250
251  autoExpand(): void {
252    let uul = this.parentElement!.querySelector('ul');
253    if (this.open === 'true') {
254      this.collapseSection(uul);
255      this.arrowElement!.style.transform = 'translateX(-50%) rotateZ(-90deg)';
256    } else {
257      this.expandSection(uul);
258      this.arrowElement!.style.transform = 'translateX(-50%) rotateZ(0deg)';
259    }
260  }
261
262  //收起
263  collapseSection(element: any): void {
264    if (!element) {
265      return;
266    }
267    let sectionHeight = element.scrollHeight;
268    let elementTransition = element.style.transition;
269    element.style.transition = '';
270    requestAnimationFrame(function () {
271      element.style.height = sectionHeight + 'px';
272      element.style.transition = elementTransition;
273      requestAnimationFrame(function () {
274        element.style.height = 0 + 'px';
275      });
276    });
277    this.open = 'false';
278  }
279
280  //展开
281  expandSection(element: any): void {
282    if (!element) {
283      return;
284    }
285    let sectionHeight = element.scrollHeight;
286    element.style.height = sectionHeight + 'px';
287    element.ontransitionend = (e: any): void => {
288      element.ontransitionend = null;
289      element.style.height = null;
290      this.open = 'true';
291    };
292  }
293
294  //当 custom element从文档DOM中删除时,被调用。
295  disconnectedCallback(): void {}
296
297  //当 custom element被移动到新的文档时,被调用。
298  adoptedCallback(): void {}
299
300  //当 custom element增加、删除、修改自身属性时,被调用。
301  attributeChangedCallback(name: string, oldValue: any, newValue: any): void {
302    if (name === 'title') {
303      this.shadowRoot!.querySelector('#title')!.textContent = newValue;
304    } else if (name === 'icon-name') {
305      if (this.iconElement) {
306        if (newValue !== null && newValue !== '' && newValue !== 'null') {
307          this.iconElement!.setAttribute('name', newValue);
308          this.iconElement!.style.display = 'flex';
309        } else {
310          this.iconElement!.style.display = 'none';
311        }
312      }
313    } else if (name === 'checkable') {
314      if (this.checkboxElement) {
315        if (newValue === 'true' && this._data!.disable !== true) {
316          this.checkboxElement!.style.display = 'inline-block';
317        } else {
318          this.checkboxElement!.style.display = 'none';
319        }
320      }
321    } else if (name === 'checked') {
322      if (this.checkboxElement) {
323        this.checkboxElement.checked = this.hasAttribute('checked');
324      }
325    }
326  }
327
328  //在node top  top-right  bottom bottom-right 画线条
329  drawLine(direction: string /*string[top|bottom|top-right|bottom-right]*/): void {
330    let item = this.shadowRoot!.querySelector('#item');
331    if (!item) {
332      return;
333    }
334    item.removeAttribute('line-top');
335    item.removeAttribute('line-top-right');
336    item.removeAttribute('line-bottom');
337    item.removeAttribute('line-bottom-right');
338    switch (direction) {
339      case 'top':
340        item.setAttribute('line-top', '');
341        break;
342      case 'bottom':
343        item.setAttribute('line-bottom', '');
344        break;
345      case 'top-right':
346        item.setAttribute('line-top-right', '');
347        break;
348      case 'bottom-right':
349        item.setAttribute('line-bottom-right', '');
350        break;
351    }
352  }
353}
354
355if (!customElements.get('lit-tree-node')) {
356  customElements.define('lit-tree-node', LitTreeNode);
357}
358