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'; 17 18let colorStr: string | null = ''; 19let colorText: string | null = ''; 20let css = ` 21<style> 22 /* 23 * Outer box style 24 */ 25 :host{ 26 box-sizing:border-box; 27 display:flex; 28 29 } 30 /* 31 * The mouse is missing 32 */ 33 :host([disabled]){ 34 opacity:0.8; 35 cursor:not-allowed; 36 } 37 /* 38 * Disable sliding 39 */ 40 :host([disabled]) input[type="range"]{ 41 pointer-events:none; 42 } 43 /* 44 * Currently the entire sliding vessel is controlled 45 */ 46 #slider-con{ 47 cursor:pointer; 48 display:flex; 49 align-items:center; 50 width:95%; 51 grid-auto-flow: row dense; 52 position: relative; 53 } 54 /* 55 * Display prompt information 56 */ 57 :host([showtips]){ 58 pointer-events:all; 59 } 60 61 #slider{ 62 background-color: var(--dark-background7,#D8D8D8); 63 z-index: 5; 64 } 65 66 /* 67 * Slider basic style 68 */ 69 input[type="range"]{ 70 pointer-events:all; 71 margin:0 -5px; 72 width: 100%; 73 -webkit-appearance: none; 74 outline : 0; 75 background: rgba(0,0,0,0.1); 76 height: 10px; 77 border-radius:2px; 78 background: -webkit-linear-gradient(right, ${colorStr}, ${colorText}) no-repeat; 79 } 80 81 /* 82 * Slider-line slidedAble area component 83 */ 84 input[type="range"]::-webkit-slider-runnable-track{ 85 display: flex; 86 align-items: center; 87 position: relative; 88 height: 10px; 89 border-radius:5px; 90 } 91 92 /* 93 * Slider slider component 94 */ 95 input[type="range"]::-webkit-slider-thumb{ 96 -webkit-appearance: none; 97 position: relative; 98 width:20px; 99 height:20px; 100 margin-top: -4px; 101 border-radius: 5px; 102 background:#999999; 103 transition:0.2s cubic-bezier(.12, .4, .29, 1.46); 104 } 105 106 input[type="range"]:focus{ 107 z-index:2; 108 } 109 110 :host(:focus-within) #slider-con,:host(:hover) #slider-con{ 111 z-index:10 112 } 113 114 :host([disabled]) #slider{ 115 opacity:0.6; 116 } 117 </style> 118`; 119const initHtmlStyle = (str: string | null, text: string | null) => { 120 colorStr = str; 121 colorText = text; 122 return css; 123}; 124 125@element('lit-slider') 126export class LitSlider extends BaseElement { 127 private litSliderStyle: LitSliderStyle | undefined | null; 128 private litSlider: HTMLInputElement | undefined | null; 129 private litSliderCon: HTMLDivElement | undefined | null; 130 private litResult: HTMLInputElement | undefined | null; 131 private slotEl: HTMLSlotElement | undefined | null; 132 private currentValue: number = 0; 133 private defaultTimeText: string | undefined | null; 134 135 static get observedAttributes() { 136 return ['percent', 'disabled-X', 'custom-slider', 'custom-line', 'custom-button', 'disabled']; 137 } 138 139 get sliderStyle(): LitSliderStyle { 140 if (this.litSliderStyle) { 141 return this.litSliderStyle; 142 } else { 143 return { 144 minRange: 0, 145 maxRange: 100, 146 defaultValue: '0', 147 resultUnit: '', 148 stepSize: 1, 149 lineColor: 'var(--dark-color3,#46B1E3)', 150 buttonColor: '#999999', 151 }; 152 } 153 } 154 155 set disabled(value) { 156 if (value === null || value === false) { 157 this.removeAttribute('disabled'); 158 } else { 159 this.setAttribute('disabled', ''); 160 } 161 } 162 163 get disabled() { 164 return this.getAttribute('disabled') !== null; 165 } 166 167 set sliderStyle(value: LitSliderStyle) { 168 this.litSliderStyle = value; 169 this.currentValue = Number(value.defaultValue); 170 this.litSliderStyle.defaultValue = value.defaultValue; 171 if (this.litSliderStyle.resultUnit === 'h:m:s') { 172 let timeData = this.litSliderStyle.defaultValue.split(':'); 173 let timeSize = Number(timeData[0]) * 3600 + Number(timeData[1]) * 60 + Number(timeData[2]); 174 this.defaultTimeText = timeSize.toString(); 175 let defaultSize = 176 ((timeSize - this.litSliderStyle.minRange) * 100) / 177 (this.litSliderStyle.maxRange - this.litSliderStyle.minRange); 178 this.litSlider!.style.backgroundSize = defaultSize + '%'; 179 } else { 180 this.defaultTimeText = this.litSliderStyle.defaultValue; 181 this.litSlider!.style.backgroundSize = '0%'; 182 if (Number(this.litSliderStyle.defaultValue)) { 183 let defaultSize = 184 ((Number(this.litSliderStyle.defaultValue) - this.litSliderStyle.minRange) / 185 (this.litSliderStyle.maxRange - this.litSliderStyle.minRange)) * 186 100; 187 this.litSlider!.style.backgroundSize = defaultSize + '%'; 188 } 189 } 190 let htmlInputElement = this.shadowRoot?.querySelector('#slider') as HTMLInputElement; 191 let attribute = htmlInputElement.getAttribute('type'); 192 if (attribute === 'range') { 193 htmlInputElement!.setAttribute('value', this.defaultTimeText!); 194 htmlInputElement!.setAttribute('min', this.litSliderStyle!.minRange.toString()); 195 htmlInputElement!.setAttribute('max', this.litSliderStyle!.maxRange.toString()); 196 htmlInputElement!.setAttribute('step', this.litSliderStyle!.stepSize.toString()); 197 } 198 } 199 200 get disabledX() { 201 return this.getAttribute('disabled-X') || ''; 202 } 203 204 set disabledX(value: string) { 205 if (value) { 206 this.setAttribute('disabled-X', ''); 207 } else { 208 this.removeAttribute('disabled-X'); 209 } 210 } 211 212 get customSlider() { 213 return this.getAttribute('custom-slider') || ''; 214 } 215 216 set customSlider(value: string) { 217 if (value) { 218 this.setAttribute('custom-slider', ''); 219 } else { 220 this.removeAttribute('custom-slider'); 221 } 222 } 223 224 get customLine() { 225 return this.getAttribute('custom-line') || ''; 226 } 227 228 set customLine(value: string) { 229 this.setAttribute('custom-line', value); 230 } 231 232 get customButton() { 233 return this.getAttribute('custom-button') || ''; 234 } 235 236 set customButton(value: string) { 237 this.setAttribute('custom-button', value); 238 } 239 240 get percent() { 241 return this.getAttribute('percent') || ''; 242 } 243 244 set percent(value: string) { 245 this.setAttribute('percent', value); 246 let resultNumber = 247 ((Number(value) - this.sliderStyle!.minRange) * 100) / (this.sliderStyle!.maxRange - this.sliderStyle!.minRange); 248 this.litSlider!.style.backgroundSize = resultNumber + '%'; 249 } 250 251 get resultUnit() { 252 return this.getAttribute('resultUnit') || ''; 253 } 254 255 set resultUnit(value: string) { 256 this.setAttribute('resultUnit', value); 257 } 258 259 initElements(): void { 260 this.litSlider = this.shadowRoot?.querySelector('#slider') as HTMLInputElement; 261 } 262 263 initHtml(): string { 264 return ` 265 ${initHtmlStyle( 266 this.getAttribute('defaultColor') ? this.getAttribute('defaultColor') : '#46B1E3', 267 this.getAttribute('defaultColor') ? this.getAttribute('defaultColor') : '#46B1E3' 268 )} 269 <slot id="slot"></slot> 270 <div id='slider-con' dir="right"> 271 <input id="slider" type="range" max="10000000"> 272 </div> 273 `; 274 } 275 276 // It is called when the custom element is first inserted into the document DOM. 277 connectedCallback() { 278 this.slotEl = this.shadowRoot?.querySelector('#slot'); 279 this.litSliderCon = this.shadowRoot?.querySelector('#slider-con'); 280 // Add a slider for input event listeners 281 this.litSlider?.addEventListener('input', this.inputChangeEvent); 282 this.litSlider?.addEventListener('change', this.inputChangeEvent); 283 this.litSlider?.addEventListener('keydown', this.inputKeyDownEvent); 284 this.litSliderStyle = this.sliderStyle; 285 } 286 287 // @ts-ignore 288 inputKeyDownEvent = (ev) => { 289 if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') { 290 ev.preventDefault(); 291 } 292 }; 293 294 inputChangeEvent = (event: any) => { 295 if (this.litSlider) { 296 this.currentValue = parseInt(this.litSlider?.value); 297 let resultNumber = 298 ((this.currentValue - this.litSliderStyle!.minRange) * 100) / 299 (this.litSliderStyle!.maxRange - this.litSliderStyle!.minRange); 300 this.percent = Number(resultNumber) + '%'; 301 this.litSliderCon?.style.setProperty('percent', this.currentValue + '%'); 302 let parentElement = this.parentNode as Element; 303 parentElement.setAttribute('percent', this.currentValue + ''); 304 if (this.sliderStyle.resultUnit === 'h:m:s') { 305 this.litSlider!.style.backgroundSize = this.percent; 306 } else { 307 this.litSlider!.style.backgroundSize = this.percent; 308 } 309 this.parentElement!.setAttribute('percent', this.litSlider?.value); 310 } 311 }; 312 313 disconnectedCallback() { 314 this.litSlider?.removeEventListener('input', this.inputChangeEvent); 315 this.litSlider?.removeEventListener('change', this.inputChangeEvent); 316 this.litSlider?.removeEventListener('change', this.inputKeyDownEvent); 317 } 318 319 adoptedCallback() {} 320 321 attributeChangedCallback(name: string, oldValue: string, newValue: string) { 322 switch (name) { 323 case 'percent': 324 if (newValue === null || newValue === '0%') { 325 let parentElement = this.parentNode as Element; 326 parentElement?.removeAttribute('percent'); 327 } else { 328 let parentElement = this.parentNode as Element; 329 } 330 break; 331 default: 332 break; 333 } 334 } 335 336 formatSeconds(value: string) { 337 let result = parseInt(value); 338 let hours = Math.floor(result / 3600) < 10 ? '0' + Math.floor(result / 3600) : Math.floor(result / 3600); 339 let minute = 340 Math.floor((result / 60) % 60) < 10 ? '0' + Math.floor((result / 60) % 60) : Math.floor((result / 60) % 60); 341 let second = Math.floor(result % 60) < 10 ? '0' + Math.floor(result % 60) : Math.floor(result % 60); 342 let resultTime = ''; 343 if (hours === '00') { 344 resultTime += `00:`; 345 } else { 346 resultTime += `${hours}:`; 347 } 348 if (minute === '00') { 349 resultTime += `00:`; 350 } else { 351 resultTime += `${minute}:`; 352 } 353 resultTime += `${second}`; 354 return resultTime; 355 } 356} 357 358export interface LitSliderStyle { 359 minRange: number; 360 maxRange: number; 361 defaultValue: string; 362 resultUnit: string; 363 stepSize: number; 364 lineColor?: string; 365 buttonColor?: string; 366} 367 368export interface LitSliderLineStyle { 369 lineWith: number; 370 lineHeight: number; 371 border?: string; 372 borderRadiusValue?: number; 373 lineChangeColor?: string; 374} 375 376export interface LitSliderButtonStyle { 377 buttonWith: number; 378 buttonHeight: number; 379 border?: string; 380 borderRadiusValue?: number; 381 buttonChangeColor?: string; 382} 383