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 18const initHtmlStyle:string = ` 19 <style> 20 :host{ 21 display:inline-block; 22 -webkit-tap-highlight-color: transparent; 23 } 24 #name{ 25 transition:0.31s width,0.31s height,0.31s background-color; 26 width:2.41em; 27 height:1.21em; 28 background: #3391FF; 29 display:flex; 30 padding:0.124em; 31 border-radius:1.21em; 32 cursor:pointer; 33 } 34 35 :host(:not([checked])) #name { 36 background: #999999; 37 } 38 39 #name::before{ 40 content:''; 41 flex:0; 42 transition:.2s cubic-bezier(.12, .4, .29, 1.46) flex; 43 } 44 #name::after{ 45 content:''; 46 width:.4em; 47 height:.4em; 48 border-radius:1.2em; 49 border:.4em solid #fff; 50 background-color:#ffffff; 51 transition:.3s background,.3s padding,.3s width,.3s height,.3s border-radius,.3s border; 52 box-shadow: 0 2px 4px 0 rgba(0,35,11,0.2); 53 } 54 #name:active::after{ 55 padding:0 .3em; 56 } 57 #switch:checked+#name{ 58 background:#42b983); 59 } 60 #switch:checked+#name::before{ 61 flex:1; 62 } 63 #switch{ 64 position:absolute; 65 clip:rect(0,0px,0px,0); 66 } 67 :host(:focus-within) #name::after,:host(:active) ::after{ 68 background:#42b983; 69 } 70 :host(:focus-within) #name{ 71 box-shadow: 0 0 10px rgba(0,0,0,0.1); 72 } 73 :host(:focus-within) #switch,:host(:active) #switch{ 74 z-index:2 75 } 76 :host([disabled]){ 77 pointer-events: none; 78 opacity:.5; 79 } 80 :host([disabled]) #name{ 81 pointer-events: all; 82 cursor: not-allowed; 83 } 84 </style> 85 `; 86 87@element('lit-switch') 88export default class LitSwitch extends BaseElement { 89 private switch: HTMLInputElement | null | undefined; 90 private isfocus: boolean | undefined; 91 92 static get observedAttributes() { 93 return ['disabled', 'checked']; 94 } 95 96 get disabled():boolean { 97 return this.getAttribute('disabled') !== null; 98 } 99 100 get checked() { 101 return this.getAttribute('checked') !== null; 102 } 103 104 set checked(value) { 105 if (value === null || value === false) { 106 this.removeAttribute('checked'); 107 } else { 108 this.setAttribute('checked', ''); 109 } 110 } 111 112 set disabled(value:boolean) { 113 if (value === null || value === false) { 114 this.removeAttribute('disabled'); 115 } else { 116 this.setAttribute('disabled', ''); 117 } 118 } 119 120 get name() { 121 return this.getAttribute('name'); 122 } 123 124 initElements(): void {} 125 126 initHtml(): string { 127 return ` 128 ${initHtmlStyle} 129 <input type="checkbox" id="switch"><label id="name" for="switch"></label> 130 `; 131 } 132 133 connectedCallback() { 134 this.switch = this.shadowRoot?.getElementById('switch') as HTMLInputElement; 135 this.disabled = this.disabled; 136 this.checked = this.checked; 137 this.switch!.onchange = (ev) => { 138 this.checked = this.switch!.checked; 139 let changeEvent: CustomEventInit<LitSwitchChangeEvent> = { 140 detail: { 141 checked: this.checked, 142 }, 143 }; 144 this.dispatchEvent(new CustomEvent('change', changeEvent)); 145 }; 146 this.switch.onkeydown = (ev) => { 147 switch (ev.keyCode) { 148 case 13: //enter 149 this.checked = !this.checked; 150 let changeEvent: CustomEventInit<LitSwitchChangeEvent> = { 151 detail: { 152 checked: this.checked, 153 }, 154 }; 155 this.dispatchEvent(new CustomEvent('change', changeEvent)); 156 break; 157 default: 158 break; 159 } 160 }; 161 this.setEvent(); 162 } 163 164 setEvent():void{ 165 this.switch!.onfocus = (ev) => { 166 ev.stopPropagation(); 167 if (!this.isfocus) { 168 this.dispatchEvent( 169 new CustomEvent('focus', { 170 detail: { value: this.switch!.value }, 171 }) 172 ); 173 } 174 }; 175 this.switch!.onblur = (ev) => { 176 ev.stopPropagation(); 177 if (getComputedStyle(this.switch!).zIndex == '2') { 178 this.isfocus = true; 179 } else { 180 this.isfocus = false; 181 this.dispatchEvent( 182 new CustomEvent('blur', { 183 detail: { value: this.switch!.value }, 184 }) 185 ); 186 } 187 }; 188 } 189 190 attributeChangedCallback(name: string, oldValue: string, newValue: string) { 191 if (name === 'disabled' && this.switch) { 192 if (newValue !== null) { 193 this.switch.setAttribute('disabled', ''); 194 } else { 195 this.switch.removeAttribute('disabled'); 196 } 197 } 198 if (name === 'checked' && this.switch) { 199 if (newValue !== null) { 200 this.switch.checked = true; 201 } else { 202 this.switch.checked = false; 203 } 204 } 205 } 206} 207 208export interface LitSwitchChangeEvent { 209 checked: boolean; 210} 211