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