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