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 { replacePlaceholders } from '../utils/Template'; 18 19let css = ` 20<style> 21 :host{ 22 display: inline-flex; 23 overflow: visible; 24 cursor: pointer; 25 position: relative; 26 border-radius: 16px; 27 outline: none; 28 user-select:none; 29 width: 75%; 30 -webkit-user-select:none ; 31 -moz-user-select:none; 32 } 33 :host(:not([border])), 34 :host([border='true']){ 35 border: 1px solid var(--bark-prompt,#dcdcdc); 36 } 37 .multipleSelect{ 38 display: flex; 39 width: 100%; 40 z-index: 98; 41 position: relative; 42 padding: 3px 6px; 43 font-size: 1rem; 44 transition: all .3s; 45 outline: none; 46 user-select:none; 47 align-items: center; 48 justify-content: space-between; 49 -webkit-user-select:none ; 50 -moz-user-select:none; 51 } 52 input{ 53 display: inline-flex; 54 width:100%; 55 z-index: 8999; 56 color: var(--dark-color2,rgba(0,0,0,0.9)); 57 background-color: transparent; 58 border: 0; 59 user-select:none; 60 outline: none; 61 cursor: pointer; 62 -webkit-user-select:none ; 63 -moz-user-select:none; 64 } 65 .body{ 66 max-height: {1}; 67 width: 100%; 68 display: block; 69 overflow: auto; 70 position: absolute; 71 bottom: 100%; 72 padding-top: 5px; 73 margin-top: 2px; 74 transition: all 0.2s; 75 flex-direction: column; 76 transform-origin: bottom center; 77 box-shadow: 0 5px 15px 0px #00000033; 78 background-color: var(--dark-background4,#fff); 79 border-radius: 2px; 80 opacity: 0; 81 z-index: 99; 82 visibility: hidden; 83 } 84 :host([placement="bottom"]) .body{ 85 bottom:unset; 86 top: 100%; 87 transition: none; 88 transform: none; 89 } 90 .body-bottom{ 91 top: 100%; 92 transform-origin: top center; 93 bottom: auto; 94 } 95 .multipleRoot input::-webkit-input-placeholder { 96 color: var(--dark-color,#aab2bd); 97 } 98 :host([disabled]) { 99 pointer-events: none; 100 cursor: not-allowed; 101 background-color: var(--dark-background1,#f5f5f5); 102 } 103 </style> 104`; 105const initHtmlStyle = (height: string): string => { 106 return replacePlaceholders(css, height); 107}; 108 109@element('lit-allocation-select') 110export class LitAllocationSelect extends BaseElement { 111 private selectAllocationInputEl: HTMLInputElement | null | undefined; 112 private selectAllocationInputContent: HTMLDivElement | undefined; 113 private selectAllocationOptions: unknown; 114 private processDataList: Array<string> = []; 115 116 static get observedAttributes(): string[] { 117 return ['value', 'disabled', 'placeholder']; 118 } 119 120 get defaultPlaceholder(): string { 121 return this.getAttribute('placeholder') || ''; 122 } 123 124 get placeholder(): string { 125 return this.getAttribute('placeholder') || this.defaultPlaceholder; 126 } 127 128 set placeholder(selectAllocationValue) { 129 this.setAttribute('placeholder', selectAllocationValue); 130 } 131 132 get value(): string { 133 return this.getAttribute('value') || ''; 134 } 135 136 set value(selectAllocationValue: string) { 137 this.setAttribute('value', selectAllocationValue); 138 } 139 140 set processData(value: Array<string>) { 141 this.processDataList = value; // @ts-ignore 142 this.selectAllocationOptions.innerHTML = ''; 143 value.forEach((item) => { 144 let option = document.createElement('div'); 145 option.className = 'option'; 146 option.innerHTML = item; 147 option.style.padding = '8px 10px'; // @ts-ignore 148 this.selectAllocationOptions.appendChild(option); 149 this.selectAllocationInputEl?.focus(); 150 }); 151 } 152 153 get placement(): string { 154 return this.getAttribute('placement') || ''; 155 } 156 157 set placement(selectAllocationValuePlacement: string) { 158 if (selectAllocationValuePlacement) { 159 this.setAttribute('placement', selectAllocationValuePlacement); 160 } else { 161 this.removeAttribute('placement'); 162 } 163 } 164 165 get listHeight(): string { 166 return this.getAttribute('list-height') || '256px'; 167 } 168 169 set listHeight(value) { 170 this.setAttribute('list-height', value); 171 } 172 173 attributeChangedCallback(name: unknown, oldValue: unknown, newValue: unknown): void { 174 switch (name) { 175 case 'value': // @ts-ignore 176 this.selectAllocationInputEl!.value = newValue; 177 break; 178 case 'placeholder': // @ts-ignore 179 this.selectAllocationInputEl!.placeholder = newValue; 180 break; 181 } 182 } 183 184 initElements(): void { 185 this.selectAllocationInputContent = this.shadowRoot!.querySelector('.multipleSelect') as HTMLDivElement; 186 this.addEventListener('click', () => { 187 // @ts-ignore 188 if (this.selectAllocationOptions.style.visibility === 'visible') { 189 // @ts-ignore 190 this.selectAllocationOptions.style.visibility = 'hidden'; // @ts-ignore 191 this.selectAllocationOptions.style.opacity = '0'; 192 } else { 193 this.showProcessList(); 194 } 195 this.selectAllocationInputContent!.dispatchEvent(new CustomEvent('inputClick', {})); 196 }); 197 this.addEventListener('focusout', (e) => { 198 // @ts-ignore 199 this.selectAllocationOptions.style.visibility = 'hidden'; // @ts-ignore 200 this.selectAllocationOptions.style.opacity = '0'; 201 }); 202 this.initData(); 203 } 204 205 showProcessList(): void { 206 setTimeout(() => { 207 if (this.processDataList.length > 0) { 208 // @ts-ignore 209 this.selectAllocationOptions.style.visibility = 'visible'; // @ts-ignore 210 this.selectAllocationOptions.style.opacity = '1'; 211 } 212 }, 200); 213 } 214 215 initHtml(): string { 216 return ` 217 ${initHtmlStyle(this.listHeight)} 218 <div class="multipleSelect" tabindex="0"> 219 <div class="multipleRoot" id="select" style="width:100%"> 220 <input id="singleInput" placeholder="${this.placeholder}"/> 221 </div> 222 <lit-icon class="icon" name='down' color="#c3c3c3"></lit-icon> 223 </div> 224 <div class="body body-bottom"> 225 <slot></slot> 226 <slot name="footer"></slot> 227 </div> 228 `; 229 } 230 231 connectedCallback(): void { 232 this.selectAllocationInputEl!.onkeydown = (ev): void => { 233 // @ts-ignore 234 if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') { 235 ev.preventDefault(); 236 } 237 }; 238 } 239 240 initData(): void { 241 this.selectAllocationInputEl = this.shadowRoot!.querySelector('input'); 242 this.selectAllocationOptions = this.shadowRoot!.querySelector('.body') as HTMLDivElement; 243 this.selectAllocationInputEl?.addEventListener('input', () => { 244 let filter = [...this.shadowRoot!.querySelectorAll<HTMLDivElement>('.option')].filter((a: HTMLDivElement) => { 245 if (a.textContent!.indexOf(this.selectAllocationInputEl!.value) <= -1) { 246 a.style.display = 'none'; 247 } else { 248 a.style.display = 'block'; 249 } 250 }); 251 this.value = this.selectAllocationInputEl!.value; 252 this.selectAllocationInputContent!.dispatchEvent(new CustomEvent('valuable', {})); 253 }); 254 this.shadowRoot?.querySelectorAll('.option').forEach((a) => { 255 a.addEventListener('mousedown', (e) => { 256 a.dispatchEvent( 257 new CustomEvent('onSelected', { 258 detail: { 259 selected: true, 260 text: a.textContent, 261 }, 262 }) 263 ); 264 }); 265 a.addEventListener('onSelected', (e: unknown) => { 266 // @ts-ignore 267 this.selectAllocationInputEl!.value = e.detail.text; // @ts-ignore 268 this.value = e.detail.text; 269 this.selectAllocationInputContent!.dispatchEvent(new CustomEvent('valuable', {})); 270 }); 271 }); 272 } 273} 274