• 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.js';
17import { selectHtmlStr } from './LitSelectHtml.js';
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        <style>
160        ${selectHtmlStr()}
161        :host(:not([mode]))  input{
162            width: 100%;
163        }
164        .body{
165            max-height: ${this.listHeight};
166            overflow: auto;
167            border-radius: 2px;
168            box-shadow: 0 5px 15px 0px #00000033;
169        }
170        .multipleRoot input::-webkit-input-placeholder {
171            color: var(--dark-color,#aab2bd);
172        }
173        :host(:not([loading])) .loading{
174            display: none;
175        }
176        :host([loading]) .loading{
177            display: flex;
178        }
179        :host(:not([allow-clear])) .clear{
180            display: none;
181        }
182        :host([loading]) .icon{
183            display: none;
184        }
185        :host(:not([loading])) .icon{
186            display: flex;
187        }
188        .clear:hover{
189            color: #8c8c8c;
190        }
191        .clear{
192            color: #bfbfbf;
193            display: none;
194        }
195        .multipleRoot{
196            display: flex;
197            align-items: center;
198            flex-flow: wrap;
199            flex-wrap: wrap;
200            flex-direction: column;
201        }
202        .search{
203            color: #bfbfbf;
204            display: none;
205        }
206        .tag{
207            overflow: auto;
208            height: auto;
209            display: inline-flex;
210            position: relative;
211            align-items: center;
212            font-size: .75rem;
213            font-weight: bold;
214            padding: 1px 4px;
215            margin-right: 4px;
216            margin-top: 1px;
217            margin-bottom: 1px;
218            color: #242424;
219            background-color: #f5f5f5;
220        }
221        .tag-close:hover{
222            color: #333;
223        }
224        .tag-close{
225            padding: 2px;
226            font-size: .8rem;
227            color: #999999;
228            margin-left: 0px;
229        }
230        </style>
231        <div class="root noSelect" tabindex="0" hidefocus="true">
232            <div class="multipleRoot">
233            <input placeholder="${this.placeholder}" autocomplete="off" ${
234      this.showSearch || this.canInsert ? '' : 'readonly'
235    } tabindex="0"></div>
236            <lit-loading class="loading" size="12"></lit-loading>
237            <lit-icon class="icon" name='down' color="#c3c3c3"></lit-icon>
238            <lit-icon class="clear" name='close-circle-fill'></lit-icon>
239            <lit-icon class="search" name='search'></lit-icon>
240        </div>
241        <div class="body">
242            <slot></slot>
243            <slot name="footer"></slot>
244        </div>
245        `;
246  }
247
248  isMultiple() {
249    return this.hasAttribute('mode') && this.getAttribute('mode') === 'multiple';
250  }
251
252  newTag(value: any, text: any) {
253    let tag: any = document.createElement('div');
254    let icon: any = document.createElement('lit-icon');
255    icon.classList.add('tag-close');
256    icon.name = 'close';
257    let span = document.createElement('span');
258    tag.classList.add('tag');
259    span.dataset['value'] = value;
260    span.textContent = text;
261    tag.append(span);
262    tag.append(icon);
263    icon.onclick = (ev: any) => {
264      tag.parentElement.removeChild(tag);
265      this.querySelector(`lit-select-option[value=${value}]`)!.removeAttribute('selected');
266      if (this.shadowRoot!.querySelectorAll('.tag').length == 0) {
267        this.selectInputEl.style.width = 'auto';
268        this.selectInputEl.placeholder = this.defaultPlaceholder;
269      }
270      ev.stopPropagation();
271    };
272    tag.value = value;
273    tag.dataset['value'] = value;
274    tag.text = text;
275    tag.dataset['text'] = text;
276    return tag;
277  }
278
279  connectedCallback() {
280    this.tabIndex = 0;
281    this.focused = false;
282    this.bodyEl = this.shadowRoot!.querySelector('.body');
283    this.selectInputEl = this.shadowRoot!.querySelector('input');
284    this.selectClearEl = this.shadowRoot!.querySelector('.clear');
285    this.selectIconEl = this.shadowRoot!.querySelector('.icon');
286    this.selectSearchEl = this.shadowRoot!.querySelector('.search');
287    this.selectMultipleRootEl = this.shadowRoot!.querySelector('.multipleRoot');
288    this.selectClearEl.onclick = (ev: any) => {
289      if (this.isMultiple()) {
290        let delNodes: Array<any> = [];
291        this.selectMultipleRootEl.childNodes.forEach((a: any) => {
292          if (a.tagName === 'DIV') {
293            delNodes.push(a);
294          }
295        });
296        for (let i = 0; i < delNodes.length; i++) {
297          delNodes[i].remove();
298        }
299        if (this.shadowRoot!.querySelectorAll('.tag').length == 0) {
300          this.selectInputEl.style.width = 'auto';
301          this.selectInputEl.placeholder = this.defaultPlaceholder;
302        }
303      }
304      this.querySelectorAll('lit-select-option').forEach((a) => a.removeAttribute('selected'));
305      this.selectInputEl.value = '';
306      this.selectClearEl.style.display = 'none';
307      this.selectIconEl.style.display = 'flex';
308      this.blur();
309      ev.stopPropagation();
310      this.dispatchEvent(new CustomEvent('onClear', { detail: ev }));
311    };
312    this.initOptions();
313    this.onclick = (ev: any) => {
314      if (ev.target.tagName === 'LIT-SELECT') {
315        if (this.focused === false) {
316          this.selectInputEl.focus();
317          this.focused = true;
318          this.bodyEl!.style.display = 'block';
319        } else {
320          this.blur();
321          this.bodyEl!.style.display = 'none';
322          this.focused = false;
323        }
324      }
325    };
326    this.onmouseover = this.onfocus = (ev) => {
327      if (this.focused === false && this.hasAttribute('adaptive-expansion')) {
328        if (this.parentElement!.offsetTop < this.bodyEl!.clientHeight) {
329          this.bodyEl!.classList.add('body-bottom');
330        } else {
331          this.bodyEl!.classList.remove('body-bottom');
332        }
333      }
334      if (this.hasAttribute('allow-clear')) {
335        if (this.selectInputEl.value.length > 0 || this.selectInputEl.placeholder !== this.defaultPlaceholder) {
336          this.selectClearEl.style.display = 'flex';
337          this.selectIconEl.style.display = 'none';
338        } else {
339          this.selectClearEl.style.display = 'none';
340          this.selectIconEl.style.display = 'flex';
341        }
342      }
343    };
344    this.onmouseout = this.onblur = (ev) => {
345      if (this.hasAttribute('allow-clear')) {
346        this.selectClearEl.style.display = 'none';
347        this.selectIconEl.style.display = 'flex';
348      }
349      this.focused = false;
350    };
351    this.selectInputEl.onfocus = (ev: any) => {
352      if (this.hasAttribute('disabled')) return;
353      if (this.selectInputEl.value.length > 0) {
354        this.selectInputEl.placeholder = this.selectInputEl.value;
355        this.selectInputEl.value = '';
356      }
357      if (this.hasAttribute('show-search')) {
358        this.selectSearchEl.style.display = 'flex';
359        this.selectIconEl.style.display = 'none';
360      }
361      this.querySelectorAll('lit-select-option').forEach((a) => {
362        // @ts-ignore
363        a.style.display = 'flex';
364      });
365    };
366    this.selectInputEl.onblur = (ev: any) => {
367      if (this.hasAttribute('disabled')) return;
368      if (this.isMultiple()) {
369        if (this.hasAttribute('show-search')) {
370          this.selectSearchEl.style.display = 'none';
371          this.selectIconEl.style.display = 'flex';
372        }
373      } else {
374        if (this.selectInputEl.placeholder !== this.defaultPlaceholder) {
375          this.selectInputEl.value = this.selectInputEl.placeholder;
376          this.selectInputEl.placeholder = this.defaultPlaceholder;
377        }
378        if (this.hasAttribute('show-search')) {
379          this.selectSearchEl.style.display = 'none';
380          this.selectIconEl.style.display = 'flex';
381        }
382      }
383    };
384    this.selectInputEl.oninput = (ev: any) => {
385      let els = [...this.querySelectorAll('lit-select-option')];
386      if (this.hasAttribute('show-search')) {
387        if (!ev.target.value) {
388          els.forEach((a: any) => (a.style.display = 'flex'));
389        } else {
390          els.forEach((a: any) => {
391            let value = a.getAttribute('value');
392            if (
393              value.toLowerCase().indexOf(ev.target.value.toLowerCase()) !== -1 ||
394              a.textContent.toLowerCase().indexOf(ev.target.value.toLowerCase()) !== -1
395            ) {
396              a.style.display = 'flex';
397            } else {
398              a.style.display = 'none';
399            }
400          });
401        }
402      } else {
403        this.value = ev.target.value;
404      }
405    };
406    this.selectInputEl.onkeydown = (ev: any) => {
407      if (ev.key === 'Backspace') {
408        if (this.isMultiple()) {
409          let tag = this.selectMultipleRootEl.lastElementChild.previousElementSibling;
410          if (tag) {
411            this.querySelector(`lit-select-option[value=${tag.value}]`)?.removeAttribute('selected');
412            tag.remove();
413            if (this.shadowRoot!.querySelectorAll('.tag').length == 0) {
414              this.selectInputEl.style.width = 'auto';
415              this.selectInputEl.placeholder = this.defaultPlaceholder;
416            }
417          }
418        } else {
419          this.clear();
420          this.dispatchEvent(new CustomEvent('onClear', { detail: ev })); //向外派发清理事件
421        }
422      } else if (ev.key === 'Enter') {
423        if (!this.canInsert) {
424          let filter = [...this.querySelectorAll('lit-select-option')].filter((a: any) => a.style.display !== 'none');
425          if (filter.length > 0) {
426            this.selectInputEl.value = filter[0].textContent;
427            this.selectInputEl.placeholder = filter[0].textContent;
428            this.blur();
429            // @ts-ignore
430            this.value = filter[0].getAttribute('value');
431            this.dispatchEvent(
432              new CustomEvent('change', {
433                detail: {
434                  selected: true,
435                  value: filter[0].getAttribute('value'),
436                  text: filter[0].textContent,
437                },
438              })
439            );
440          }
441        }
442      }
443    };
444  }
445
446  initOptions() {
447    this.querySelectorAll('lit-select-option').forEach((a) => {
448      if (this.isMultiple()) {
449        a.setAttribute('check', '');
450        if (a.getAttribute('value') === this.defaultValue) {
451          let tag = this.newTag(a.getAttribute('value'), a.textContent);
452          this.selectMultipleRootEl.insertBefore(tag, this.selectInputEl);
453          this.selectInputEl.placeholder = '';
454          this.selectInputEl.value = '';
455          this.selectInputEl.style.width = '1px';
456          a.setAttribute('selected', '');
457        }
458      } else {
459        if (a.getAttribute('value') === this.defaultValue) {
460          this.selectInputEl.value = a.textContent;
461          a.setAttribute('selected', '');
462        }
463      }
464      a.addEventListener('mouseup', (e) => {
465        e.stopPropagation();
466      });
467      a.addEventListener('mousedown', (e) => {
468        e.stopPropagation();
469      });
470      a.addEventListener('onSelected', (e: any) => {
471        if (this.isMultiple()) {
472          if (a.hasAttribute('selected')) {
473            let tag = this.shadowRoot!.querySelector(`div[data-value=${e.detail.value}]`) as HTMLElement;
474            if (tag) {
475              tag.parentElement!.removeChild(tag);
476            }
477            e.detail.selected = false;
478          } else {
479            let tag = this.newTag(e.detail.value, e.detail.text);
480            this.selectMultipleRootEl.insertBefore(tag, this.selectInputEl);
481            this.selectInputEl.placeholder = '';
482            this.selectInputEl.value = '';
483            this.selectInputEl.style.width = '1px';
484          }
485          if (this.shadowRoot!.querySelectorAll('.tag').length == 0) {
486            this.selectInputEl.style.width = 'auto';
487            this.selectInputEl.placeholder = this.defaultPlaceholder;
488          }
489          this.selectInputEl.focus();
490        } else {
491          [...this.querySelectorAll('lit-select-option')].forEach((a) => a.removeAttribute('selected'));
492          this.blur();
493          this.bodyEl!.style.display = 'none';
494          // @ts-ignore
495          this.selectInputEl.value = e.detail.text;
496        }
497        if (a.hasAttribute('selected')) {
498          a.removeAttribute('selected');
499        } else {
500          a.setAttribute('selected', '');
501        }
502        // @ts-ignore
503        this.value = e.detail.value;
504        this.dispatchEvent(new CustomEvent('change', { detail: e.detail })); //向外层派发change事件,返回当前选中项
505      });
506    });
507  }
508
509  clear() {
510    this.selectInputEl.value = '';
511    this.selectInputEl.placeholder = this.defaultPlaceholder;
512  }
513
514  reset() {
515    this.querySelectorAll('lit-select-option').forEach((a) => {
516      [...this.querySelectorAll('lit-select-option')].forEach((a) => a.removeAttribute('selected'));
517      if (a.getAttribute('value') === this.defaultValue) {
518        this.selectInputEl.value = a.textContent;
519        a.setAttribute('selected', '');
520      }
521    });
522  }
523
524  disconnectedCallback() {}
525
526  adoptedCallback() {}
527
528  attributeChangedCallback(name: any, oldValue: any, newValue: any) {
529    if (name === 'value' && this.selectInputEl) {
530      if (newValue) {
531        [...this.querySelectorAll('lit-select-option')].forEach((a) => {
532          if (a.getAttribute('value') === newValue) {
533            a.setAttribute('selected', '');
534            this.selectInputEl.value = a.textContent;
535          } else {
536            a.removeAttribute('selected');
537          }
538        });
539      } else {
540        this.clear();
541      }
542    }
543  }
544}
545