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