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: flex; 23 position: absolute; 24 top: 0; 25 bottom: 0; 26 left: 0; 27 right: 0; 28 width: 100%; 29 height: 100%; 30 overflow: hidden; 31 z-index: 2001; 32 pointer-events: none; 33 } 34 :host([mask]) .bg{ 35 position:absolute; 36 top: 0; 37 right: 0; 38 left: 0; 39 bottom: 0; 40 width: 100%; 41 height: 100%; 42 background-color: #00000055; 43 } 44 :host(:not([mask])) .bg{ 45 display: none; 46 } 47 .title{ 48 display: flex; 49 justify-content: space-between; 50 align-items: center; 51 padding: 12px 20px; 52 border-bottom: var(--dark-border1,#f0f0f0) 1px solid; 53 font-size: 18px; 54 font-weight: bold; 55 color: var(--dark-color1,#262626); 56 } 57 slot{ 58 padding: {1}; 59 display: block; 60 } 61 :host([visible]) .bg{ 62 transition: all .3s; 63 opacity: 1; 64 visibility: visible; 65 } 66 :host(:not([visible])) .bg{ 67 transition: all .3s; 68 opacity: 0; 69 visibility: hidden; 70 } 71 /* 72 right(默认) 73 */ 74 :host(:not([placement])) .drawer, 75 :host([placement='right']) .drawer{ 76 width: {2}; 77 box-sizing: border-box; 78 position: absolute; 79 display: flex; 80 flex-direction: column; 81 right: 0px; 82 top: 0px; 83 bottom: 0px; 84 height: 100%; 85 overflow: auto; 86 background-color: var(--dark-background,#FFFFFF); 87 -webkit-transform: translateZ(0); 88 -moz-transform: translateZ(0); 89 -ms-transform: translateZ(0); 90 -o-transform: translateZ(0); 91 transform: translateZ(0); 92 transform: translateX(100%); 93 transition: transform .3s; 94 } 95 :host(:not([placement])[visible]) .drawer, 96 :host([placement='right'][visible]) .drawer{ 97 transform: translateX(0%); 98 box-shadow: 0px 0 20px 0px #00000055; 99 } 100 :host(:not([visible]):not([placement])) .drawer, 101 :host(:not([visible])[placement='right']) .drawer{ 102 transform: translateX(100%); 103 } 104 /* 105 左边 106 */ 107 :host([placement='left']) .drawer{ 108 width: {2}; 109 box-sizing: border-box; 110 position: absolute; 111 display: flex; 112 flex-direction: column; 113 left: 0px; 114 top: 0px; 115 bottom: 0px; 116 right: auto; 117 height: 100%; 118 background-color: var(--dark-background,#FFFFFF); 119 webkit-transform: translate3d(0,0,0); 120 -moz-transform: translate3d(0,0,0); 121 -ms-transform: translate3d(0,0,0); 122 -o-transform: translate3d(0,0,0); 123 transform: translate3d(0,0,0); 124 transform: translate(0%,0%); 125 } 126 :host([placement='left']) .drawer{ 127 transition: transform .3s; 128 } 129 :host([placement='left'][visible]) .drawer{ 130 box-shadow: 0px 0 60px 0px #00000055; 131 transform: translate(0%,0%); 132 } 133 :host(:not([visible])[placement='left']) .drawer{ 134 transform: translate(-100%,0%); 135 } 136 137 /* 138 top 139 */ 140 :host([placement='top']) .drawer{ 141 box-sizing: border-box; 142 position: absolute; 143 display: flex; 144 flex-direction: column; 145 left: 0px; 146 top: 0px; 147 right: 0px; 148 width: 100%; 149 background-color: var(--dark-background,#FFFFFF); 150 webkit-transform: translate3d(0,0,0); 151 -moz-transform: translate3d(0,0,0); 152 -ms-transform: translate3d(0,0,0); 153 -o-transform: translate3d(0,0,0); 154 transform: translate3d(0,0,0); 155 } 156 :host([placement='top']) .drawer{ 157 transform: translateY(-100%); 158 transition: transform .3s; 159 } 160 :host([placement='top'][visible]) .drawer{ 161 box-shadow: 0px 0 60px 0px #00000055; 162 transform: translateY(0%); 163 } 164 :host(:not([visible])[placement='top']) .drawer{ 165 transform: translateY(-100%); 166 } 167 168 /* 169 bottom 170 */ 171 :host([placement='bottom']) .drawer{ 172 box-sizing: border-box; 173 position: absolute; 174 display: flex; 175 flex-direction: column; 176 left: 0px; 177 bottom: 0px; 178 right: 0px; 179 top: auto; 180 width: 100%; 181 background-color: var(--dark-background,#FFFFFF); 182 webkit-transform: translate3d(0,0,0); 183 -moz-transform: translate3d(0,0,0); 184 -ms-transform: translate3d(0,0,0); 185 -o-transform: translate3d(0,0,0); 186 transform: translate3d(0,0,0); 187 transform: translate(0%,0%); 188 transition: transform .3s; 189 } 190 :host([placement='bottom'][visible]) .drawer{ 191 box-shadow: 0px 0 60px 0px #00000055; 192 transform: translate(0%,0%); 193 } 194 :host(:not([visible])[placement='bottom']) .drawer{ 195 transform: translate(0%,100%); 196 } 197 198 :host([closeable]) .close-icon{ 199 display: flex; 200 color: #8c8c8c; 201 padding: 5px; 202 } 203 :host([closeable]) .close-icon:hover{ 204 color: #414141; 205 } 206 :host(:not([closeable])) .close-icon{ 207 display: none; 208 } 209 </style> 210`; 211 212const initHtmlStyle = (padding: string, width: string): string => { 213 return replacePlaceholders(css, padding, width); 214}; 215 216@element('lit-drawer') 217export class LitDrawer extends BaseElement { 218 static get observedAttributes(): string[] { 219 return [ 220 'drawer-title', 221 'visible', 222 'placement', 223 'mask', 224 'mask-closable', 225 'closeable', 226 'content-padding', 227 'content-width', 228 ]; 229 } 230 231 initHtml(): string { 232 return ` 233 ${initHtmlStyle(this.contentPadding, this.contentWidth)} 234 <div class="bg"></div> 235 <div class="drawer"> 236 <div class="title"> 237 <label id="drawer-tittle-text">${this.title}</label> 238 <lit-icon class="close-icon" name="close"></lit-icon> 239 </div> 240 <div style="overflow-x: hidden;overflow-y: auto"> 241 <slot></slot> 242 </div> 243 </div> 244 `; 245 } 246 get contentWidth(): string { 247 return this.getAttribute('content-width') || '400px'; 248 } 249 set contentWidth(value) { 250 this.shadowRoot!.querySelector<HTMLDivElement>('.drawer')!.style.width = value; 251 this.setAttribute('content-width', value); 252 } 253 get contentPadding(): string { 254 return this.getAttribute('content-padding') || '20px'; 255 } 256 set contentPadding(value) { 257 this.shadowRoot!.querySelector('slot')!.style.padding = value; 258 this.setAttribute('content-padding', value); 259 } 260 get placement(): string | null { 261 return this.getAttribute('placement'); 262 } 263 set placement(value: unknown) { 264 // @ts-ignore 265 this.setAttribute('placement', value); 266 } 267 get drawerTitle(): string { 268 return this.getAttribute('drawer-title') || ''; 269 } 270 set drawerTitle(value) { 271 this.shadowRoot!.querySelector('#drawer-tittle-text')!.textContent = value; 272 this.setAttribute('drawer-title', value); 273 } 274 get visible(): boolean { 275 return this.getAttribute('visible') !== null; 276 } 277 set visible(value: unknown) { 278 if (value) { 279 // @ts-ignore 280 this.setAttribute('visible', value); 281 } else { 282 this.removeAttribute('visible'); 283 } 284 } 285 get mask(): boolean { 286 return this.getAttribute('mask') !== null; 287 } 288 set mask(value) { 289 if (value) { 290 this.setAttribute('mask', ''); 291 } else { 292 this.removeAttribute('mask'); 293 } 294 } 295 get maskCloseable(): boolean { 296 return this.getAttribute('mask-closeable') !== null; 297 } 298 set maskCloseable(value) { 299 if (value) { 300 this.setAttribute('mask-closeable', ''); 301 } else { 302 this.removeAttribute('mask-closeable'); 303 } 304 } 305 get closeable(): boolean { 306 return this.getAttribute('closeable') !== null; 307 } 308 309 set closeable(value) { 310 if (value) { 311 this.setAttribute('closeable', ''); 312 } else { 313 this.removeAttribute('closeable'); 314 } 315 } 316 317 //当 custom element首次被插入文档DOM时,被调用。 318 initElements(): void { 319 let bg: HTMLDivElement | null = this.shadowRoot!.querySelector('.bg'); 320 if (this.maskCloseable) { 321 bg!.onclick = (e: unknown): void => { 322 // @ts-ignore 323 e.stopPropagation(); 324 this.visible = false; // @ts-ignore 325 this.dispatchEvent(new CustomEvent('onClose', e)); 326 }; 327 } 328 if (this.closeable) { 329 // @ts-ignore 330 (this.shadowRoot!.querySelector('.close-icon') as unknown).onclick = (e: unknown): void => { 331 this.visible = false; // @ts-ignore 332 this.dispatchEvent(new CustomEvent('onClose', e)); 333 }; 334 } 335 } 336 set onClose(fn: unknown) { 337 // @ts-ignore 338 this.addEventListener('onClose', fn); 339 } 340 //当 custom element从文档DOM中删除时,被调用。 341 disconnectedCallback(): void {} 342 343 //当 custom element被移动到新的文档时,被调用。 344 adoptedCallback(): void {} 345 346 //当 custom element增加、删除、修改自身属性时,被调用。 347 attributeChangedCallback(name: string, oldValue: string, newValue: string): void { 348 if (this.mask) { 349 if (name === 'visible') { 350 if (newValue !== null) { 351 this.style.pointerEvents = 'all'; 352 } else { 353 this.style.pointerEvents = 'none'; 354 } 355 } else if (name === 'placement') { 356 if (newValue === 'bottom') { 357 let el = this.shadowRoot!.querySelector('.drawer'); 358 } 359 } 360 } 361 } 362} 363 364if (!customElements.get('lit-drawer')) { 365 customElements.define('lit-drawer', LitDrawer); 366} 367