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