• 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 { BaseElement, element } from '../../../../base-ui/BaseElement';
17import { LitIcon } from '../../../../base-ui/icon/LitIcon';
18import { SearchHtml } from './Search.html';
19import '../../../../base-ui/select/LitSelect';
20import '../../../../base-ui/select/LitSelectOption';
21import { LitSelect } from '../../../../base-ui/select/LitSelect';
22import { Utils } from '../base/Utils';
23import { SpSystemTrace } from '../../SpSystemTrace';
24
25const LOCAL_STORAGE_SEARCH_KEY = 'search_key';
26let timerId: unknown = null;
27@element('lit-search')
28export class LitSearch extends BaseElement {
29  valueChangeHandler: ((str: string, id?: number) => void) | undefined | null;
30  private search: HTMLInputElement | undefined | null;
31  private _total: number = 0;
32  private _index: number = 0;
33  private _list: Array<unknown> = [];
34  private _value: boolean = false;
35  private totalEL: HTMLSpanElement | null | undefined;
36  private indexEL: HTMLSpanElement | null | undefined;
37  private searchHistoryListEL: HTMLUListElement | null | undefined;
38  private historyMaxCount = 100;
39  private lastSearch = '';
40  private searchList: Array<SearchInfo> = [];
41  private searchELList: Array<HTMLElement> = [];
42  //定义翻页index
43  private retarget_index: number = 0;
44  private _retarge_index: HTMLInputElement | null | undefined;
45  private traceSelector: LitSelect | null | undefined;
46  public currenSearchValue: string | undefined | null;
47
48  get list(): Array<unknown> {
49    return this._list;
50  }
51
52  set list(value: Array<unknown>) {
53    this._list = value;
54    this.total = value.length;
55  }
56
57  get index(): number {
58    return this._index;
59  }
60
61  set index(value: number) {
62    this._index = value;
63    this.indexEL!.textContent = `${value + 1}`;
64  }
65
66  get searchValue(): string {
67    return this.search?.value || '';
68  }
69
70  get total(): number {
71    return this._total;
72  }
73
74  set total(value: number) {
75    if (value > 0) {
76      this.setAttribute('show-search-info', '');
77    } else {
78      this.removeAttribute('show-search-info');
79    }
80    this._total = value;
81    this.totalEL!.textContent = value.toString();
82  }
83
84  get isLoading(): boolean {
85    return this.hasAttribute('isLoading');
86  }
87
88  set isLoading(va) {
89    if (va) {
90      this.setAttribute('isLoading', '');
91    } else {
92      this.removeAttribute('isLoading');
93      window.localStorage.setItem(LOCAL_STORAGE_SEARCH_KEY, '');
94    }
95  }
96
97  set isClearValue(value: boolean) {
98    this._value = value;
99  }
100
101  get isClearValue(): boolean {
102    return this._value;
103  }
104
105  setPercent(name: string = '', value: number): void {
106    let searchHide = this.shadowRoot!.querySelector<HTMLElement>('.root');
107    let searchIcon = this.shadowRoot!.querySelector<HTMLElement>('#search-icon');
108    if (this.hasAttribute('textRoll')) {
109      this.removeAttribute('textRoll');
110    }
111    this.isLoading = false;
112    if (value > 0 && value <= 100) {
113      searchHide!.style.display = 'flex';
114      searchHide!.style.backgroundColor = 'var(--dark-background5,#e3e3e3)';
115      searchIcon?.setAttribute('name', 'cloud-sync');
116      this.search!.setAttribute('placeholder', `${name}${value}%`);
117      this.search!.setAttribute('readonly', '');
118      this.search!.className = 'readonly';
119      this.isLoading = true;
120    } else if (value > 100) {
121      searchHide!.style.display = 'flex';
122      searchHide!.style.backgroundColor = 'var(--dark-background5,#fff)';
123      searchIcon?.setAttribute('name', 'search');
124      this.search?.setAttribute('placeholder', 'search');
125      this.search?.removeAttribute('readonly');
126      this.search!.className = 'write';
127    } else if (value === -1) {
128      searchHide!.style.display = 'flex';
129      searchHide!.style.backgroundColor = 'var(--dark-background5,#e3e3e3)';
130      searchIcon?.setAttribute('name', 'cloud-sync');
131      this.search!.setAttribute('placeholder', `${name}`);
132      this.search!.setAttribute('readonly', '');
133      this.search!.className = 'readonly';
134    } else if (value === -2) {
135      searchHide!.style.display = 'flex';
136      searchHide!.style.backgroundColor = 'var(--dark-background5,#e3e3e3)';
137      searchIcon?.setAttribute('name', 'cloud-sync');
138      this.search!.setAttribute('placeholder', `${name}`);
139      this.search!.setAttribute('readonly', '');
140      this.search!.className = 'text-Roll';
141      setTimeout((): void => {
142        this.setAttribute('textRoll', '');
143      }, 200);
144    } else {
145      searchHide!.style.display = 'none';
146    }
147  }
148
149  clear(): void {
150    this.search = this.shadowRoot!.querySelector<HTMLInputElement>('input');
151    this.search!.value = '';
152    if (!Utils.isDistributedMode()) {
153      this.removeAttribute('distributed');
154    }
155    this.list = [];
156  }
157
158  blur(): void {
159    this.search?.blur();
160  }
161
162  updateSearchList(searchStr: string | null): void {
163    if (searchStr === null || searchStr.length === 0 || searchStr.trim().length === 0) {
164      return;
165    }
166    let searchInfo = this.searchList.find((searchInfo): boolean => searchInfo.searchContent === searchStr);
167    if (searchInfo !== undefined) {
168      let index = this.searchList.indexOf(searchInfo);
169      this.searchList.splice(index, 1);
170      this.searchList.unshift({ searchContent: searchStr, useCount: 1 });
171    } else {
172      this.searchList.unshift({ searchContent: searchStr, useCount: 1 });
173    }
174  }
175
176  getSearchHistory(): Array<SearchInfo> {
177    let searchString = window.localStorage.getItem(LOCAL_STORAGE_SEARCH_KEY);
178    if (searchString) {
179      let searHistory = JSON.parse(searchString);
180      if (Array.isArray(searHistory)) {
181        this.searchList = searHistory;
182        return searHistory;
183      }
184    }
185    return [];
186  }
187
188  private searchFocusListener(): void {
189    if (!this.search?.hasAttribute('readonly')) {
190      this.showSearchHistoryList();
191    }
192  }
193
194  private searchBlurListener(): void {
195    setTimeout((): void => {
196      this.hideSearchHistoryList();
197    }, 200);
198  }
199
200  private searchKeyupListener(e: unknown): void {
201    timerId = null;
202    // @ts-ignore
203    if (e.keyCode === 13) {
204      this.updateSearchList(this.search!.value);
205      // @ts-ignore
206      if (e.shiftKey) {
207        this.dispatchEvent(
208          new CustomEvent('previous-data', {
209            detail: {
210              value: this.search!.value,
211            },
212            composed: false,
213          })
214        );
215      } else {
216        this.dispatchEvent(
217          new CustomEvent('next-data', {
218            detail: {
219              value: this.search!.value,
220            },
221            composed: false,
222          })
223        );
224      }
225    } else {
226      this.updateSearchHistoryList(this.search!.value);
227      this.valueChangeHandler?.(this.trimSideSpace(this.search!.value));
228    }
229    // @ts-ignore
230    e.stopPropagation();
231  }
232
233  trimSideSpace(str: string): string {
234    return str.replace(/(^\s*)|(\s*$)/g, '');
235  }
236
237  initElements(): void {
238    this.initTraceSelectHandler();
239    this.search = this.shadowRoot!.querySelector<HTMLInputElement>('input');
240    this.totalEL = this.shadowRoot!.querySelector<HTMLSpanElement>('#total');
241    this.indexEL = this.shadowRoot!.querySelector<HTMLSpanElement>('#index');
242    this.searchHistoryListEL = this.shadowRoot!.querySelector<HTMLUListElement>('.search-history-list');
243    this._retarge_index = this.shadowRoot!.querySelector<HTMLInputElement>("input[name='retarge_index']");
244    this.search!.addEventListener('focus', (): void => {
245      this.searchFocusListener();
246    });
247    this.search!.addEventListener('blur', (): void => {
248      this.searchBlurListener();
249    });
250    this.search!.addEventListener('keyup', (e: KeyboardEvent) => {
251      this._retarge_index!.value = '';
252      this.searchKeyupListener(e);
253    });
254    //阻止事件冒泡
255    this.search!.addEventListener('keydown', (e: KeyboardEvent) => {
256      e.stopPropagation();
257    });
258
259    this.search!.addEventListener('keypress', (e: KeyboardEvent) => {
260      e.stopPropagation();
261    });
262    this.shadowRoot?.querySelector('#arrow-left')?.addEventListener('click', (): void => {
263      this.dispatchEvent(
264        new CustomEvent('previous-data', {
265          detail: {
266            value: this.search!.value,
267          },
268        })
269      );
270    });
271    this.shadowRoot?.querySelector('#arrow-right')?.addEventListener('click', (): void => {
272      this.dispatchEvent(
273        new CustomEvent('next-data', {
274          detail: {
275            value: this.search!.value,
276          },
277        })
278      );
279    });
280    this.keyUpListener();
281    //阻止事件冒泡
282    this.shadowRoot?.querySelector("input[name='retarge_index']")?.addEventListener('keydown', (e: unknown) => {
283      // @ts-ignore
284      e.stopPropagation();
285    });
286    this.shadowRoot?.querySelector("input[name='retarge_index']")?.addEventListener('keypress', (e: unknown) => {
287      // @ts-ignore
288      e.stopPropagation();
289    });
290  }
291
292  private initTraceSelectHandler(): void {
293    this.traceSelector = this.shadowRoot!.querySelector<LitSelect>('#trace_selector');
294    let selectorBody = this.traceSelector?.shadowRoot!.querySelector<HTMLDivElement>('.body');
295    if (selectorBody) {
296      selectorBody.style.width = '200px';
297      selectorBody.style.overflow = 'hidden';
298    }
299    this.traceSelector?.addEventListener('change', (): void => {
300      if (Utils.currentSelectTrace !== this.traceSelector!.value) {
301        Utils.currentSelectTrace = this.traceSelector!.value;
302        this.clear();
303        this.dispatchEvent(new CustomEvent('trace-change', {
304          detail: {
305            value: this.traceSelector?.value,
306          },
307        }));
308      }
309    });
310    this.traceSelector?.addEventListener('focus', (e): void => {
311      e.stopPropagation();
312    });
313  }
314
315  setTraceSelectOptions(): void {
316    this.traceSelector!.dataSource = Utils.distributedTrace.map((trace, index) => ({
317      value: `${index + 1}`,
318      name: trace
319    }));
320  }
321
322  getSearchTraceId(): string | null | undefined {
323    if (this.hasAttribute('distributed')) {
324      return this.traceSelector?.value;
325    }
326    return null;
327  }
328
329  private keyUpListener(): void {
330    let _root = this.shadowRoot!.querySelector<HTMLInputElement>('.root');
331    let _prompt = this.shadowRoot!.querySelector<HTMLInputElement>('#prompt');
332    // 添加翻页监听事件
333    this.shadowRoot?.querySelector("input[name='retarge_index']")?.addEventListener('keyup', (e: unknown): void => {
334      // @ts-ignore
335      if (e.keyCode === 13) {
336        this.retarget_index = Number(this._retarge_index!.value);
337        if (this.retarget_index <= this._list.length && this.retarget_index !== 0) {
338          this.dispatchEvent(
339            new CustomEvent('retarget-data', {
340              detail: {
341                value: this.retarget_index,
342              },
343            })
344          );
345        } else if (this.retarget_index === 0) {
346          return;
347        } else {
348          _prompt!.style.display = 'block';
349          _root!.style.display = 'none';
350          _prompt!.innerHTML = `${this._list.length} pages in total, please re-enter`;
351          setTimeout(() => {
352            _prompt!.style.display = 'none';
353            _root!.style.display = 'flex';
354            this._retarge_index!.value = '';
355          }, 2000);
356        }
357        // @ts-ignore
358        e.target.blur();
359      }
360      // @ts-ignore
361      e.stopPropagation();
362    });
363  }
364
365  initHtml(): string {
366    return SearchHtml;
367  }
368
369  showSearchHistoryList(): void {
370    this.searchHistoryListEL!.innerHTML = '';
371    let historyInfos = this.getSearchHistory();
372    let fragment = document.createElement('div');
373    historyInfos.forEach((historyInfo) => {
374      let searchVessel = document.createElement('div');
375      searchVessel.className = 'search-list';
376      let searchInfoOption = document.createElement('li');
377      let closeOption = document.createElement('lit-icon');
378      closeOption.setAttribute('name', 'close');
379      closeOption.className = 'close-option';
380      closeOption.setAttribute('size', '20');
381      searchInfoOption.className = 'search-history-list-item';
382      searchInfoOption.textContent = historyInfo.searchContent;
383      searchInfoOption.addEventListener('click', (): void => {
384        if (searchInfoOption.textContent) {
385          let flag = this.search!.value;
386          this.search!.value = searchInfoOption.textContent;
387          this.valueChangeHandler?.(this.search!.value);
388          if (flag !== searchInfoOption.textContent) {
389            this._retarge_index!.value = '';
390            this.index = -1;
391          }
392        }
393      });
394      searchVessel.append(searchInfoOption);
395      searchVessel.append(closeOption);
396      this.searchELList.push(searchInfoOption);
397      this.searchELList.push(closeOption);
398      fragment.append(searchVessel);
399    });
400    this.searchHistoryListEL?.append(fragment);
401    if (this.searchList.length > 0) {
402      this.searchHistoryListEL!.style.display = 'block';
403    }
404    let closeOptionList = this.searchHistoryListEL!.querySelectorAll<LitIcon>('.close-option');
405    closeOptionList.forEach((item): void => {
406      item.addEventListener('click', (): void => {
407        let currentHistory = item.previousSibling!.textContent;
408        let index = this.searchList.findIndex((element): boolean => element.searchContent === currentHistory);
409        if (index !== -1) {
410          this.searchList.splice(index, 1);
411        }
412        let historyStr = JSON.stringify(this.searchList);
413        window.localStorage.setItem(LOCAL_STORAGE_SEARCH_KEY, historyStr);
414      });
415    });
416  }
417
418  hideSearchHistoryList(): void {
419    this.searchHistoryListEL!.style.display = 'none';
420    if (this.searchList.length > this.historyMaxCount) {
421      this.searchList = this.searchList.slice(0, this.historyMaxCount);
422    }
423    if (this.searchList.length === 0) {
424      return;
425    }
426    let historyStr = JSON.stringify(this.searchList);
427    window.localStorage.setItem(LOCAL_STORAGE_SEARCH_KEY, historyStr);
428    this.searchList = [];
429    this.searchELList = [];
430  }
431
432  updateSearchHistoryList(searchValue: string): void {
433    const keyword = searchValue.toLowerCase();
434    this.searchELList.forEach((item) => {
435      if (item.textContent!.toLowerCase().includes(keyword)) {
436        item.style.display = 'block';
437      } else {
438        item.style.display = 'none';
439      }
440    });
441  }
442}
443
444export interface SearchInfo {
445  searchContent: string;
446  useCount: number;
447}
448