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'; 18let css = ` 19<style> 20 :host{ 21 outline: none; 22 display:inline-block; 23 position: relative; 24 overflow: visible; 25 } 26 27 .title{ 28 padding: 6px 15px; 29 font-weight: bold; 30 font-size: 0.9rem; 31 border-bottom: 1px solid #f0f0f0; 32 } 33 .content{ 34 padding: 10px; 35 } 36 .trigger-click { 37 position: absolute; 38 visibility: hidden; 39 z-index: -100; 40 width: 100%; 41 height: 100%; 42 } 43 /*通用*/ 44 .popover{ 45 width: {1}; 46 min-width: 160px; 47 display: flex; 48 flex-direction: column; 49 visibility: hidden; 50 opacity: 0; 51 transition: all 0.3s; 52 z-index: 1; 53 position: absolute; 54 border-radius: 2px; 55 background-color: var(--dark-background3,#fff); 56 box-shadow: 0 0 20px #00000044; 57 } 58 .popover:after{ 59 content: ''; 60 display: flex; 61 position: absolute; 62 width: 12px; 63 height: 12px; 64 background: linear-gradient(45deg, var(--dark-background3,#FFFFFF), 65 var(--dark-background3,#FFFFFF) 50%, transparent 50%, transparent 100%); 66 } 67 :host(:not([placement])) .popover, 68 :host([placement='top']) .popover{ 69 top: 0; 70 left: 50%; 71 right: 0; 72 transform: translate(-50%,calc(-100% - 12px)) scale(0.5); 73 transform-origin: bottom; 74 } 75 :host(:not([placement])) .popover:after, 76 :host([placement='top']) .popover:after{ 77 border-top: 6px solid #fff; 78 left: 0; 79 top: calc(100%); 80 transform: translate(-50%,0%); 81 left: 50%; 82 } 83 :host(:not([placement])[trigger='hover']:hover) .popover, 84 :host(:not([placement]):not([trigger]):hover) .popover, 85 :host([placement='top'][trigger='hover']:hover) .popover, 86 :host([placement='top']:not([trigger]):hover) .popover { 87 visibility: visible; 88 opacity: 1; 89 transform: translate(-50%,calc(-100% - 12px)) scale(1); 90 } 91 :host(:not([placement])[trigger='click']) input[type=checkbox]:checked ~ .popover, 92 :host([placement='top'][trigger='click']) input[type=checkbox]:checked ~ .popover { 93 visibility: visible; 94 opacity: 1; 95 transform: translate(-50%,calc(-100% - 12px)) scale(1); 96 } 97 :host([placement='topLeft']) .popover{ 98 top: 0; 99 left: 50%; 100 right: 0; 101 transform: translate(0,calc(-100% - 12px)) scale(0.5); 102 transform-origin: left bottom; 103 } 104 :host([placement='topLeft']) .popover:after{ 105 top: 99%; 106 transform: rotateX(180deg); 107 } 108 :host([placement='topLeft']:not([trigger]):hover) .popover, 109 :host([placement='topLeft'][trigger='hover']:hover) .popover { 110 visibility: visible; 111 opacity: 1; 112 transform: translate(0%,calc(-100% - 12px)) scale(1); 113 } 114 :host([placement='topLeft'][trigger='click']) input[type=checkbox]:checked ~ .popover { 115 visibility: visible; 116 opacity: 1; 117 transform: translate(0%,calc(-100% - 12px)) scale(1); 118 } 119 :host([placement='topRight']) .popover{ 120 top: 0; 121 right: 0; 122 transform: translate(0,calc(-100% - 12px)) scale(0.5); 123 transform-origin: right bottom; 124 } 125 :host([placement='topRight']) .popover:after{ 126 border-top: 6px solid #fff; 127 top: calc(100%); 128 transform: translate(0%,0%); 129 right: 20px; 130 } 131 :host([placement='topRight']:not([trigger]):hover) .popover, 132 :host([placement='topRight'][trigger='hover']:hover) .popover { 133 visibility: visible; 134 opacity: 1; 135 transform: translate(0%,calc(-100% - 12px)) scale(1); 136 } 137 :host([placement='topRight'][trigger='click']) input[type=checkbox]:checked ~ .popover { 138 visibility: visible; 139 opacity: 1; 140 transform: translate(0%,calc(-100% - 12px)) scale(1); 141 } 142 :host([placement='leftTop']) .popover{ 143 top: 0; 144 right: 100%; 145 transform: translate(-12px,0) scale(0.5); 146 transform-origin: right top; 147 } 148 :host([placement='leftTop']) .popover:after{ 149 border-left: 6px solid #fff; 150 top: 10px; 151 right: -12px; 152 transform: translate(0px,0%); 153 } 154 :host([placement='leftTop']:not([trigger]):hover) .popover, 155 :host([placement='leftTop'][trigger='hover']:hover) .popover { 156 visibility: visible; 157 opacity: 1; 158 right: 100%; 159 transform: translate(-12px,0) scale(1); 160 } 161 :host([placement='leftTop'][trigger='click']) input[type=checkbox]:checked ~ .popover { 162 visibility: visible; 163 opacity: 1; 164 transform: translate(-12px,0) scale(1); 165 } 166 :host([placement='left']) .popover{ 167 right: 100%; 168 top: 50%; 169 transform: translate(-12px,-50%) scale(0.5); 170 transform-origin: right center; 171 } 172 :host([placement='left']) .popover:after{ 173 border-left: 6px solid #fff; 174 top: 50%; 175 right: -12px; 176 transform: translate(0px,-50%); 177 } 178 :host([placement='left']:not([trigger]):hover) .popover, 179 :host([placement='left'][trigger='hover']:hover) .popover { 180 visibility: visible; 181 opacity: 1; 182 right: 100%; 183 transform: translate(-12px,-50%) scale(1); 184 } 185 :host([placement='left'][trigger='click']) input[type=checkbox]:checked ~ .popover { 186 visibility: visible; 187 opacity: 1; 188 transform: translate(-12px,-50%) scale(1); 189 } 190 :host([placement='leftBottom']) .popover{ 191 right: 100%; 192 bottom: 0; 193 transform: translate(-12px,0%) scale(0.5); 194 transform-origin: right bottom; 195 } 196 :host([placement='leftBottom']) .popover:after{ 197 border-left: 6px solid #fff; 198 bottom: 10px; 199 right: -12px; 200 transform: translate(0px,-50%); 201 } 202 :host([placement='leftBottom']:not([trigger]):hover) .popover, 203 :host([placement='leftBottom'][trigger='hover']:hover) .popover { 204 visibility: visible; 205 opacity: 1; 206 right: 100%; 207 transform: translate(-12px,0%) scale(1); 208 } 209 :host([placement='leftBottom'][trigger='click']) input[type=checkbox]:checked ~ .popover { 210 visibility: visible; 211 opacity: 1; 212 transform: translate(-12px,0%) scale(1); 213 } 214 :host([placement='rightTop']) .popover{ 215 top: 0; 216 left: 100%; 217 transform: translate(12px,0) scale(0.5); 218 transform-origin: left top; 219 } 220 :host([placement='rightTop']) .popover:after{ 221 border-right: 6px solid #fff; 222 top: 10px; 223 left: -12px; 224 transform: translate(0px,0%); 225 } 226 :host([placement='rightTop']:not([trigger]):hover) .popover, 227 :host([placement='rightTop'][trigger='hover']:hover) .popover { 228 visibility: visible; 229 opacity: 1; 230 left: 100%; 231 transform: translate(12px,0) scale(1); 232 } 233 :host([placement='rightTop'][trigger='click']) input[type=checkbox]:checked ~ .popover { 234 visibility: visible; 235 opacity: 1; 236 transform: translate(12px,0) scale(1); 237 } 238 :host([placement='right']) .popover{ 239 top: 50%; 240 left: 100%; 241 transform: translate(12px,-50%) scale(0.5); 242 transform-origin: left center; 243 } 244 :host([placement='right']) .popover:after{ 245 border-right: 6px solid #fff; 246 top: 50%; 247 left: -12px; 248 transform: translate(0px,-50%); 249 } 250 :host([placement='right']:not([trigger]):hover) .popover, 251 :host([placement='right'][trigger='hover']:hover) .popover { 252 visibility: visible; 253 opacity: 1; 254 left: 100%; 255 transform: translate(12px,-50%) scale(1); 256 } 257 :host([placement='right'][trigger='click']) input[type=checkbox]:checked ~ .popover { 258 visibility: visible; 259 opacity: 1; 260 transform: translate(12px,-50%) scale(1); 261 } 262 :host([placement='rightBottom']) .popover{ 263 bottom: 0; 264 left: 100%; 265 transform: translate(12px,0%) scale(0.5); 266 transform-origin: left bottom; 267 } 268 :host([placement='rightBottom']) .popover:after{ 269 border-right: 6px solid #fff; 270 left: -12px; 271 bottom: 10px; 272 transform: translate(0px,0); 273 } 274 :host([placement='rightBottom']:not([trigger]):hover) .popover, 275 :host([placement='rightBottom'][trigger='hover']:hover) .popover { 276 visibility: visible; 277 opacity: 1; 278 bottom: 0; 279 transform: translate(12px,0%) scale(1); 280 } 281 :host([placement='rightBottom'][trigger='click']) input[type=checkbox]:checked ~ .popover { 282 visibility: visible; 283 opacity: 1; 284 transform: translate(12px,0%) scale(1); 285 } 286 287 :host([placement='bottomLeft']) .popover{ 288 bottom: 0; 289 /*left: 0;*/ 290 left: 8px; 291 right: 0; 292 transform: translate(0,calc(100% + 12px)) scale(0.5); 293 transform-origin: top left; 294 } 295 :host([placement='bottomLeft']) .popover:after{ 296 bottom: calc(100%); 297 transform: translate(0%,0%); 298 left: 0; 299 } 300 :host([placement='bottomLeft']:not([trigger]):hover) .popover, 301 :host([placement='bottomLeft'][trigger='hover']:hover) .popover { 302 visibility: visible; 303 opacity: 1; 304 transform: translate(0%,calc(100% + 12px)) scale(1); 305 } 306 :host([placement='bottomLeft'][trigger='click']) input[type=checkbox]:checked ~ .popover { 307 visibility: visible; 308 opacity: 1; 309 transform: translate(0%,calc(100% + 12px)) scale(1); 310 } 311 :host([placement='bottom']) .popover{ 312 bottom: 0; 313 left: 50%; 314 right: 0; 315 transform: translate(-50%,calc(100% + 12px)) scale(0.5); 316 transform-origin: top center; 317 } 318 :host([placement='bottom']) .popover:after{ 319 border-bottom: 6px solid #fff; 320 bottom: calc(100%); 321 transform: translate(-50%,0%); 322 left: 50%; 323 } 324 :host([placement='bottom']:not([trigger]):hover) .popover, 325 :host([placement='bottom'][trigger='hover']:hover) .popover { 326 visibility: visible; 327 opacity: 1; 328 transform: translate(-50%,calc(100% + 12px)) scale(1); 329 } 330 :host([placement='bottom'][trigger='click']) input[type=checkbox]:checked ~ .popover { 331 visibility: visible; 332 opacity: 1; 333 transform: translate(-50%,calc(100% + 12px)) scale(1); 334 } 335 /*bottomRight*/ 336 :host([placement='bottomRight']) .popover{ 337 bottom: 0; 338 right: 0; 339 transform: translate(0%,calc(100% + 12px)) scale(0.5); 340 transform-origin: top right; 341 } 342 :host([placement='bottomRight']) .popover:after{ 343 border-bottom: 6px solid #fff; 344 bottom: calc(100%); 345 transform: translate(-50%,0%); 346 right: 10px; 347 } 348 :host([placement='bottomRight']:not([trigger]):hover) .popover, 349 :host([placement='bottomRight'][trigger='hover']:hover) .popover { 350 visibility: visible; 351 opacity: 1; 352 transform: translate(0,calc(100% + 12px)) scale(1); 353 } 354 :host([placement='bottomRight'][trigger='click']) input[type=checkbox]:checked ~ .popover { 355 visibility: visible; 356 opacity: 1; 357 transform: translate(0%,calc(100% + 12px)) scale(1); 358 } 359 :host(:not([title])) .title{ 360 display: none; 361 } 362 </style> 363`; 364const initHtmlStyle = (wid: string): string => { 365 return replacePlaceholders(css, wid); 366}; 367 368@element('lit-popover') 369export class LitPopover extends BaseElement { 370 static get observedAttributes(): string[] { 371 return ['title', 'trigger', 'width', 'placement', 'visible']; 372 } 373 374 get visible(): string { 375 return this.getAttribute('visible') || 'false'; 376 } 377 378 set visible(value) { 379 if (value) { 380 this.setAttribute('visible', 'true'); 381 } else { 382 this.setAttribute('visible', 'false'); 383 } 384 } 385 386 set placement(value) { 387 this.setAttribute('placement', value || 'bottomLeft'); 388 } 389 390 get placement(): string | null { 391 return this.getAttribute('placement'); 392 } 393 394 get trigger(): string { 395 return this.getAttribute('trigger') || 'hover'; 396 } 397 398 set trigger(value) { 399 this.setAttribute('trigger', value); 400 } 401 402 get title(): string { 403 return this.getAttribute('title') || ''; 404 } 405 406 set title(value: string) { 407 this.setAttribute('title', value); 408 } 409 410 get width(): string { 411 return this.getAttribute('width') || 'max-content'; 412 } 413 414 set width(value) { 415 this.setAttribute('width', value); 416 } 417 418 get haveRadio(): string | null { 419 return this.getAttribute('haveRadio'); 420 } 421 422 initElements(): void {} 423 424 initHtml(): string { 425 return ` 426 ${initHtmlStyle(this.width)} 427 <input class="trigger-click" type="checkbox"> 428 <div class="popover" title=""> 429 <div class="title">${this.title}</div> 430 <div class="content"><slot name="content" ></slot></div> 431 </div> 432 <slot></slot> 433 `; 434 } 435 436 connectedCallback(): void { 437 let popover: unknown = this.shadowRoot!.querySelector('.popover'); 438 let checkbox: unknown = this.shadowRoot!.querySelector('.trigger-click'); 439 this.setAttribute('tabindex', '1'); // @ts-ignore 440 popover.onclick = (e: unknown): void => { 441 // @ts-ignore 442 e.stopPropagation(); 443 }; // @ts-ignore 444 popover.addEventListener('mousemove', (e: unknown) => { 445 // @ts-ignore 446 e.stopPropagation(); 447 }); 448 this.onclick = (e: unknown): void => { 449 // @ts-ignore 450 e.stopPropagation(); // @ts-ignore 451 if (e.relatedTarget?.hasAttribute('not-close')) { 452 this.focus(); 453 } // @ts-ignore 454 checkbox.checked = !checkbox.checked; // @ts-ignore 455 this.visible = checkbox.checked; 456 }; // @ts-ignore 457 popover.onmouseleave = (): void => { 458 this.focus(); 459 }; 460 this.onblur = (ev: unknown): void => { 461 // @ts-ignore 462 if (ev.relatedTarget && this.haveRadio) { 463 // @ts-ignore 464 if (ev.relatedTarget.hasAttribute('not-close')) { 465 // @ts-ignore 466 } else if (ev.relatedTarget.type === 'radio') { 467 this.focus(); 468 } else { 469 // @ts-ignore 470 this.visible = false; 471 } 472 } else { 473 // @ts-ignore 474 this.visible = false; 475 } 476 }; 477 } 478 479 disconnectedCallback(): void {} 480 481 adoptedCallback(): void {} 482 483 attributeChangedCallback(name: unknown, oldValue: unknown, newValue: unknown): void { 484 if (name === 'visible') { 485 if (newValue === 'false') { 486 // @ts-ignore 487 this.shadowRoot!.querySelector('.trigger-click')!.checked = false; 488 } else { 489 // @ts-ignore 490 this.shadowRoot!.querySelector('.trigger-click')!.checked = true; 491 } 492 } 493 } 494} 495