• 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 '../BaseElement';
17import { selectHtmlStr } from './LitSelectHtml';
18
19@element('lit-select')
20export class LitSelect extends BaseElement {
21  private focused: any;
22  private selectInputEl: any;
23  private selectClearEl: any;
24  private selectIconEl: any;
25  private bodyEl: any;
26  private selectSearchEl: any;
27  private selectMultipleRootEl: any;
28
29  static get observedAttributes() {
30    return [
31      'value',
32      'default-value',
33      'placeholder',
34      'disabled',
35      'loading',
36      'allow-clear',
37      'show-search',
38      'list-height',
39      'border',
40      'mode',
41    ];
42  }
43
44  get value() {
45    return this.getAttribute('value') || this.defaultValue;
46  }
47
48  set value(selectValue) {
49    this.setAttribute('value', selectValue);
50  }
51
52  get rounded() {
53    return this.hasAttribute('rounded');
54  }
55
56  set rounded(selectRounded: boolean) {
57    if (selectRounded) {
58      this.setAttribute('rounded', '');
59    } else {
60      this.removeAttribute('rounded');
61    }
62  }
63
64  get placement(): string {
65    return this.getAttribute('placement') || '';
66  }
67
68  set placement(selectPlacement: string) {
69    if (selectPlacement) {
70      this.setAttribute('placement', selectPlacement);
71    } else {
72      this.removeAttribute('placement');
73    }
74  }
75
76  get border() {
77    return this.getAttribute('border') || 'true';
78  }
79
80  set border(selectBorder) {
81    if (selectBorder) {
82      this.setAttribute('border', 'true');
83    } else {
84      this.setAttribute('border', 'false');
85    }
86  }
87
88  get listHeight() {
89    return this.getAttribute('list-height') || '256px';
90  }
91
92  set listHeight(selectListHeight) {
93    this.setAttribute('list-height', selectListHeight);
94  }
95
96  get defaultPlaceholder() {
97    return this.getAttribute('placeholder') || '请选择';
98  }
99
100  set canInsert(can: boolean) {
101    if (can) {
102      this.setAttribute('canInsert', '');
103    } else {
104      this.removeAttribute('canInsert');
105    }
106  }
107
108  get canInsert() {
109    return this.hasAttribute('canInsert');
110  }
111  get showSearch() {
112    return this.hasAttribute('show-search');
113  }
114
115  get defaultValue() {
116    return this.getAttribute('default-value') || '';
117  }
118
119  set defaultValue(selectDefaultValue) {
120    this.setAttribute('default-value', selectDefaultValue);
121  }
122
123  get placeholder() {
124    return this.getAttribute('placeholder') || this.defaultPlaceholder;
125  }
126
127  set placeholder(selectPlaceHolder) {
128    this.setAttribute('placeholder', selectPlaceHolder);
129  }
130
131  get loading() {
132    return this.hasAttribute('loading');
133  }
134
135  set loading(selectLoading) {
136    if (selectLoading) {
137      this.setAttribute('loading', '');
138    } else {
139      this.removeAttribute('loading');
140    }
141  }
142
143  set dataSource(selectDataSource: any) {
144    selectDataSource.forEach((dateSourceBean: any) => {
145      let selectOption = document.createElement('lit-select-option');
146      if (dateSourceBean.name) {
147        selectOption.textContent = dateSourceBean.name;
148        selectOption.setAttribute('value', dateSourceBean.name);
149      }
150      this.append(selectOption);
151    });
152    this.initOptions();
153  }
154
155  initElements(): void {}
156
157  initHtml() {
158    return `
159        ${selectHtmlStr(this.listHeight)}
160        <div class="root noSelect" tabindex="0" hidefocus="true">
161            <div class="multipleRoot">
162            <input placeholder="${this.placeholder}" autocomplete="off" ${
163      this.showSearch || this.canInsert ? '' : 'readonly'
164    } tabindex="0"></div>
165            <lit-loading class="loading" size="12"></lit-loading>
166            <lit-icon class="icon" name='down' color="#c3c3c3"></lit-icon>
167            <lit-icon class="clear" name='close-circle-fill'></lit-icon>
168            <lit-icon class="search" name='search'></lit-icon>
169        </div>
170        <div class="body">
171            <slot></slot>
172            <slot name="footer"></slot>
173        </div>
174        `;
175  }
176
177  isMultiple() {
178    return this.hasAttribute('mode') && this.getAttribute('mode') === 'multiple';
179  }
180
181  newTag(value: any, text: any) {
182    let tag: any = document.createElement('div');
183    let icon: any = document.createElement('lit-icon');
184    icon.classList.add('tag-close');
185    icon.name = 'close';
186    let span = document.createElement('span');
187    tag.classList.add('tag');
188    span.dataset['value'] = value;
189    span.textContent = text;
190    tag.append(span);
191    tag.append(icon);
192    icon.onclick = (ev: any) => {
193      tag.parentElement.removeChild(tag);
194      this.querySelector(`lit-select-option[value=${value}]`)!.removeAttribute('selected');
195      if (this.shadowRoot!.querySelectorAll('.tag').length == 0) {
196        this.selectInputEl.style.width = 'auto';
197        this.selectInputEl.placeholder = this.defaultPlaceholder;
198      }
199      ev.stopPropagation();
200    };
201    tag.value = value;
202    tag.dataset['value'] = value;
203    tag.text = text;
204    tag.dataset['text'] = text;
205    return tag;
206  }
207
208  connectedCallback() {
209    this.tabIndex = 0;
210    this.focused = false;
211    this.bodyEl = this.shadowRoot!.querySelector('.body');
212    this.selectInputEl = this.shadowRoot!.querySelector('input');
213    this.selectClearEl = this.shadowRoot!.querySelector('.clear');
214    this.selectIconEl = this.shadowRoot!.querySelector('.icon');
215    this.selectSearchEl = this.shadowRoot!.querySelector('.search');
216    this.selectMultipleRootEl = this.shadowRoot!.querySelector('.multipleRoot');
217    this.setEventClick();
218    this.setEvent();
219    this.selectInputEl.onblur = (ev: any) => {
220      if (this.hasAttribute('disabled')) return;
221      if (this.isMultiple()) {
222        if (this.hasAttribute('show-search')) {
223          this.selectSearchEl.style.display = 'none';
224          this.selectIconEl.style.display = 'flex';
225        }
226      } else {
227        if (this.selectInputEl.placeholder !== this.defaultPlaceholder) {
228          this.selectInputEl.value = this.selectInputEl.placeholder;
229          this.selectInputEl.placeholder = this.defaultPlaceholder;
230        }
231        if (this.hasAttribute('show-search')) {
232          this.selectSearchEl.style.display = 'none';
233          this.selectIconEl.style.display = 'flex';
234        }
235      }
236    };
237    this.setOninput();
238    this.setOnkeydown();
239  }
240
241  setOninput():void{
242    this.selectInputEl.oninput = (ev: any) => {
243      let els: Element[] = [...this.querySelectorAll('lit-select-option')];
244      if (this.hasAttribute('show-search')) {
245        if (!ev.target.value) {
246          els.forEach((a: any) => (a.style.display = 'flex'));
247        } else {
248          this.setSelectItem(els,ev)
249        }
250      } else {
251        this.value = ev.target.value;
252      }
253    };
254  }
255
256  setSelectItem(els:Element[],ev:any):void{
257    els.forEach((a: any) => {
258      let value = a.getAttribute('value');
259      if (
260        value.toLowerCase().indexOf(ev.target.value.toLowerCase()) !== -1 ||
261        a.textContent.toLowerCase().indexOf(ev.target.value.toLowerCase()) !== -1
262      ) {
263        a.style.display = 'flex';
264      } else {
265        a.style.display = 'none';
266      }
267    });
268  }
269
270  setEventClick():void{
271    this.selectClearEl.onclick = (ev: any) => {
272      if (this.isMultiple()) {
273        let delNodes: Array<any> = [];
274        this.selectMultipleRootEl.childNodes.forEach((a: any) => {
275          if (a.tagName === 'DIV') {
276            delNodes.push(a);
277          }
278        });
279        for (let i = 0; i < delNodes.length; i++) {
280          delNodes[i].remove();
281        }
282        if (this.shadowRoot!.querySelectorAll('.tag').length == 0) {
283          this.selectInputEl.style.width = 'auto';
284          this.selectInputEl.placeholder = this.defaultPlaceholder;
285        }
286      }
287      this.querySelectorAll('lit-select-option').forEach((a) => a.removeAttribute('selected'));
288      this.selectInputEl.value = '';
289      this.selectClearEl.style.display = 'none';
290      this.selectIconEl.style.display = 'flex';
291      this.blur();
292      ev.stopPropagation();
293      this.dispatchEvent(new CustomEvent('onClear', { detail: ev }));
294    };
295    this.initOptions();
296    this.onclick = (ev: any) => {
297      if (ev.target.tagName === 'LIT-SELECT') {
298        if (this.focused === false) {
299          this.selectInputEl.focus();
300          this.focused = true;
301          this.bodyEl!.style.display = 'block';
302        } else {
303          this.blur();
304          this.bodyEl!.style.display = 'none';
305          this.focused = false;
306        }
307      }
308    };
309  }
310
311  setEvent():void{
312    this.onmouseover = this.onfocus = (ev) => {
313      if (this.focused === false && this.hasAttribute('adaptive-expansion')) {
314        if (this.parentElement!.offsetTop < this.bodyEl!.clientHeight) {
315          this.bodyEl!.classList.add('body-bottom');
316        } else {
317          this.bodyEl!.classList.remove('body-bottom');
318        }
319      }
320      if (this.hasAttribute('allow-clear')) {
321        if (this.selectInputEl.value.length > 0 || this.selectInputEl.placeholder !== this.defaultPlaceholder) {
322          this.selectClearEl.style.display = 'flex';
323          this.selectIconEl.style.display = 'none';
324        } else {
325          this.selectClearEl.style.display = 'none';
326          this.selectIconEl.style.display = 'flex';
327        }
328      }
329    };
330    this.onmouseout = this.onblur = (ev) => {
331      if (this.hasAttribute('allow-clear')) {
332        this.selectClearEl.style.display = 'none';
333        this.selectIconEl.style.display = 'flex';
334      }
335      this.focused = false;
336    };
337    this.selectInputEl.onfocus = (ev: any) => {
338      if (this.hasAttribute('disabled')) return;
339      if (this.selectInputEl.value.length > 0) {
340        this.selectInputEl.placeholder = this.selectInputEl.value;
341        this.selectInputEl.value = '';
342      }
343      if (this.hasAttribute('show-search')) {
344        this.selectSearchEl.style.display = 'flex';
345        this.selectIconEl.style.display = 'none';
346      }
347      this.querySelectorAll('lit-select-option').forEach((a) => {
348        // @ts-ignore
349        a.style.display = 'flex';
350      });
351    };
352  }
353
354  setOnkeydown():void{
355    this.selectInputEl.onkeydown = (ev: any) => {
356      if (ev.key === 'Backspace') {
357        if (this.isMultiple()) {
358          let tag = this.selectMultipleRootEl.lastElementChild.previousElementSibling;
359          if (tag) {
360            this.querySelector(`lit-select-option[value=${tag.value}]`)?.removeAttribute('selected');
361            tag.remove();
362            if (this.shadowRoot!.querySelectorAll('.tag').length == 0) {
363              this.selectInputEl.style.width = 'auto';
364              this.selectInputEl.placeholder = this.defaultPlaceholder;
365            }
366          }
367        } else {
368          this.clear();
369          this.dispatchEvent(new CustomEvent('onClear', { detail: ev })); //向外派发清理事件
370        }
371      } else if (ev.key === 'Enter') {
372        if (!this.canInsert) {
373          let filter = [...this.querySelectorAll('lit-select-option')].filter((a: any) => a.style.display !== 'none');
374          if (filter.length > 0) {
375            this.selectInputEl.value = filter[0].textContent;
376            this.selectInputEl.placeholder = filter[0].textContent;
377            this.blur();
378            // @ts-ignore
379            this.value = filter[0].getAttribute('value');
380            this.dispatchEvent(
381              new CustomEvent('change', {
382                detail: {
383                  selected: true,
384                  value: filter[0].getAttribute('value'),
385                  text: filter[0].textContent,
386                },
387              })
388            );
389          }
390        }
391      } else if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') {
392        ev.preventDefault();
393      }
394    };
395  }
396
397  initOptions() {
398    this.querySelectorAll('lit-select-option').forEach((a) => {
399      if (this.isMultiple()) {
400        a.setAttribute('check', '');
401        if (a.getAttribute('value') === this.defaultValue) {
402          let tag = this.newTag(a.getAttribute('value'), a.textContent);
403          this.selectMultipleRootEl.insertBefore(tag, this.selectInputEl);
404          this.selectInputEl.placeholder = '';
405          this.selectInputEl.value = '';
406          this.selectInputEl.style.width = '1px';
407          a.setAttribute('selected', '');
408        }
409      } else {
410        if (a.getAttribute('value') === this.defaultValue) {
411          this.selectInputEl.value = a.textContent;
412          a.setAttribute('selected', '');
413        }
414      }
415      a.addEventListener('mouseup', (e) => {
416        e.stopPropagation();
417      });
418      a.addEventListener('mousedown', (e) => {
419        e.stopPropagation();
420      });
421      this.onSelectedEvent(a);
422    });
423  }
424
425  onSelectedEvent(a:Element):void{
426    a.addEventListener('onSelected', (e: any) => {
427      if (this.isMultiple()) {
428        if (a.hasAttribute('selected')) {
429          let tag = this.shadowRoot!.querySelector(`div[data-value=${e.detail.value}]`) as HTMLElement;
430          if (tag) {
431            tag.parentElement!.removeChild(tag);
432          }
433          e.detail.selected = false;
434        } else {
435          let tag = this.newTag(e.detail.value, e.detail.text);
436          this.selectMultipleRootEl.insertBefore(tag, this.selectInputEl);
437          this.selectInputEl.placeholder = '';
438          this.selectInputEl.value = '';
439          this.selectInputEl.style.width = '1px';
440        }
441        if (this.shadowRoot!.querySelectorAll('.tag').length == 0) {
442          this.selectInputEl.style.width = 'auto';
443          this.selectInputEl.placeholder = this.defaultPlaceholder;
444        }
445        this.selectInputEl.focus();
446      } else {
447        [...this.querySelectorAll('lit-select-option')].forEach((a) => a.removeAttribute('selected'));
448        this.blur();
449        this.bodyEl!.style.display = 'none';
450        // @ts-ignore
451        this.selectInputEl.value = e.detail.text;
452      }
453      if (a.hasAttribute('selected')) {
454        a.removeAttribute('selected');
455      } else {
456        a.setAttribute('selected', '');
457      }
458      // @ts-ignore
459      this.value = e.detail.value;
460      this.dispatchEvent(new CustomEvent('change', { detail: e.detail })); //向外层派发change事件,返回当前选中项
461    });
462  }
463
464  clear() {
465    this.selectInputEl.value = '';
466    this.selectInputEl.placeholder = this.defaultPlaceholder;
467  }
468
469  reset() {
470    this.querySelectorAll('lit-select-option').forEach((a) => {
471      [...this.querySelectorAll('lit-select-option')].forEach((a) => a.removeAttribute('selected'));
472      if (a.getAttribute('value') === this.defaultValue) {
473        this.selectInputEl.value = a.textContent;
474        a.setAttribute('selected', '');
475      }
476    });
477  }
478
479  disconnectedCallback() {}
480
481  adoptedCallback() {}
482
483  attributeChangedCallback(name: any, oldValue: any, newValue: any) {
484    if (name === 'value' && this.selectInputEl) {
485      if (newValue) {
486        [...this.querySelectorAll('lit-select-option')].forEach((a) => {
487          if (a.getAttribute('value') === newValue) {
488            a.setAttribute('selected', '');
489            this.selectInputEl.value = a.textContent;
490          } else {
491            a.removeAttribute('selected');
492          }
493        });
494      } else {
495        this.clear();
496      }
497    }
498  }
499}
500