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 { LitPopContent } from './LitPopContent'; 18import { LitPopoverTitle } from './LitPopoverTitle'; 19import { LitRadioGroup } from '../radiobox/LitRadioGroup'; 20import { LitRadioBox } from '../radiobox/LitRadioBox'; 21import { LitCheckBox } from '../checkbox/LitCheckBox'; 22import { LitCheckGroup } from '../checkbox/LitCheckGroup'; 23import { LitCheckBoxWithText } from '../checkbox/LitCheckBoxWithText'; 24 25const initHtmlStyle = ` 26 <style> 27 :host { 28 display:inline-block; 29 position:relative; 30 overflow:visible; 31 } 32 :host([direction="top"]) ::slotted(lit-pop-content){ 33 bottom:100%; 34 left:50%; 35 transform:translate(-50%,-10px) scale(0); 36 transform-origin: center bottom; 37 } 38 :host([direction="top"]) ::slotted(lit-pop-content)::after{ 39 content: ''; 40 position: absolute; 41 top: 100%; 42 left: 50%; 43 border-top: 10px solid #FFF; 44 border-right: 10px solid transparent; 45 border-left: 10px solid transparent; 46 47 } 48 :host([direction="top"]) ::slotted(lit-pop-content[open]), 49 :host([direction="top"][trigger="hover"]:hover) ::slotted(lit-pop-content), 50 :host([direction="top"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 51 transform:translate(-50%,-10px) scale(1); 52 } 53 54 :host([direction="bottom"]) ::slotted(lit-pop-content){ 55 top:100%; 56 left:50%; 57 transform:translate(-50%,10px) scale(0); 58 transform-origin: center top; 59 } 60 :host([direction="bottom"]) ::slotted(lit-pop-content)::after{ 61 content: ''; 62 position: absolute; 63 bottom: 100%; 64 left: 50%; 65 border-bottom: 10px solid #FFF; 66 border-right: 10px solid transparent; 67 border-left: 10px solid transparent; 68 } 69 :host([direction="bottom"]) ::slotted(lit-pop-content[open]), 70 :host([direction="bottom"][trigger="hover"]:hover) ::slotted(lit-pop-content), 71 :host([direction="bottom"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 72 transform:translate(-50%,10px) scale(1); 73 } 74 75 76 :host([direction="left"]) ::slotted(lit-pop-content){ 77 right:100%; 78 top:50%; 79 transform:translate(-10px,-50%) scale(0); 80 transform-origin: right; 81 } 82 :host([direction="left"]) ::slotted(lit-pop-content)::after{ 83 content: ''; 84 position: absolute; 85 bottom: 40%; 86 left: 100%; 87 border-left: 10px solid #FFF; 88 border-bottom: 10px solid transparent; 89 border-top: 10px solid transparent; 90 } 91 :host([direction="left"]) ::slotted(lit-pop-content[open]), 92 :host([direction="left"][trigger="hover"]:hover) ::slotted(lit-pop-content), 93 :host([direction="left"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 94 transform:translate(-10px,-50%) scale(1); 95 } 96 :host([direction="right"]) ::slotted(lit-pop-content){ 97 left:100%; 98 top:50%; 99 transform:translate(10px,-50%) scale(0); 100 transform-origin: left; 101 } 102 :host([direction="right"]) ::slotted(lit-pop-content)::after{ 103 content: ''; 104 position: absolute; 105 bottom: 40%; 106 right: 100%; 107 border-right: 10px solid #FFF; 108 border-bottom: 10px solid transparent; 109 border-top: 10px solid transparent; 110 } 111 :host([direction="right"]) ::slotted(lit-pop-content[open]), 112 :host([direction="right"][trigger="hover"]:hover) ::slotted(lit-pop-content), 113 :host([direction="right"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 114 transform:translate(10px,-50%) scale(1); 115 } 116 117 118 119 :host([direction="leftbottom"]) ::slotted(lit-pop-content){ 120 right:100%; 121 top:0; 122 transform:translate(-10px) scale(0); 123 } 124 :host([direction="leftbottom"]) ::slotted(lit-pop-content[open]), 125 :host([direction="leftbottom"][trigger="hover"]:hover) ::slotted(lit-pop-content), 126 :host([direction="leftbottom"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 127 transform:translate(-10px) scale(1); 128 } 129 130 :host([direction="leftbottom"]) ::slotted(lit-pop-content)::after{ 131 content: ''; 132 position: absolute; 133 top: 10%; 134 left: 100%; 135 border-left: 10px solid #FFF; 136 border-bottom: 10px solid transparent; 137 } 138 139 :host([direction="lefttop"]) ::slotted(lit-pop-content){ 140 right:100%; 141 bottom:0; 142 transform:translate(-10px) scale(0); 143 transform-origin: right bottom; 144 } 145 :host([direction="lefttop"]) ::slotted(lit-pop-content)::after{ 146 content: ''; 147 position: absolute; 148 bottom: 10%; 149 left: 100%; 150 border-left: 10px solid #FFF; 151 border-top: 10px solid transparent; 152 } 153 :host([direction="lefttop"]) ::slotted(lit-pop-content[open]), 154 :host([direction="lefttop"][trigger="hover"]:hover) ::slotted(lit-pop-content), 155 :host([direction="lefttop"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 156 transform:translate(-10px) scale(1); 157 } 158 :host([direction="topright"]) ::slotted(lit-pop-content){ 159 bottom:100%; 160 left:50%; 161 transform:translate(0,-10px) scale(0); 162 transform-origin: left bottom; 163 } 164 :host([direction="topright"]) ::slotted(lit-pop-content)::after{ 165 content: ''; 166 position: absolute; 167 top: 100%; 168 left: 0%; 169 border-top: 10px solid #FFF; 170 border-right: 10px solid transparent; 171 } 172 :host([direction="topright"]) ::slotted(lit-pop-content[open]), 173 :host([direction="topright"][trigger="hover"]:hover) ::slotted(lit-pop-content), 174 :host([direction="topright"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 175 transform:translate(0,-10px) scale(1); 176 } 177 178 179 :host([direction="topleft"]) ::slotted(lit-pop-content){ 180 bottom:100%; 181 right:50%; 182 transform:translate(0,-10px) scale(0); 183 transform-origin: right bottom; 184 } 185 :host([direction="topleft"]) ::slotted(lit-pop-content)::after{ 186 content: ''; 187 position: absolute; 188 top: 100%; 189 right: 0%; 190 border-top: 10px solid #FFF; 191 border-left: 10px solid transparent; 192 } 193 :host([direction="topleft"]) ::slotted(lit-pop-content[open]), 194 :host([direction="topleft"][trigger="hover"]:hover) ::slotted(lit-pop-content), 195 :host([direction="topleft"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 196 transform:translate(0,-10px) scale(1); 197 } 198 199 200 :host([direction="rightbottom"]) ::slotted(lit-pop-content){ 201 left:100%; 202 top:0; 203 transform:translate(10px) scale(0); 204 transform-origin: left top; 205 } 206 :host([direction="rightbottom"]) ::slotted(lit-pop-content)::after{ 207 content: ''; 208 position: absolute; 209 top: 10%; 210 right: 100%; 211 border-top: 10px solid #FFF; 212 border-left: 10px solid transparent; 213 } 214 :host([direction="rightbottom"]) ::slotted(lit-pop-content[open]), 215 :host([direction="rightbottom"][trigger="hover"]:hover) ::slotted(lit-pop-content), 216 :host([direction="rightbottom"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 217 transform:translate(10px) scale(1); 218 } 219 :host([direction="righttop"]) ::slotted(lit-pop-content){ 220 left:100%; 221 bottom:0; 222 transform:translate(10px) scale(0); 223 transform-origin: left bottom; 224 } 225 :host([direction="righttop"]) ::slotted(lit-pop-content[open]), 226 :host([direction="righttop"][trigger="hover"]:hover) ::slotted(lit-pop-content), 227 :host([direction="righttop"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 228 transform:translate(10px) scale(1); 229 } 230 :host([direction="righttop"]) ::slotted(lit-pop-content)::after{ 231 content: ''; 232 position: absolute; 233 bottom: 10%; 234 right: 100%; 235 border-bottom: 10px solid #FFF; 236 border-left: 10px solid transparent; 237 } 238 239 :host([direction="bottomright"]) ::slotted(lit-pop-content), 240 :host(:not([direction])) ::slotted(lit-pop-content){ 241 left:0; 242 top:100%; 243 transform:translate(0,10px) scale(0); 244 transform-origin: left top; 245 } 246 :host([direction="bottomright"]) ::slotted(lit-pop-content)::after, 247 :host(:not([direction])) ::slotted(lit-pop-content)::after{ 248 content: ''; 249 position: absolute; 250 left: 10%; 251 bottom: 100%; 252 border-bottom: 10px solid #FFF; 253 border-right: 10px solid transparent; 254 } 255 :host(:not([direction])) ::slotted(lit-pop-content[open]), 256 :host(:not([direction])[trigger="hover"]:hover) ::slotted(lit-pop-content), 257 :host(:not([direction])[trigger="focus"]:focus-within) ::slotted(lit-pop-content), 258 :host([direction="bottomright"]) ::slotted(lit-pop-content[open]), 259 :host([direction="bottomright"][trigger="hover"]:hover) ::slotted(lit-pop-content), 260 :host([direction="bottomright"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 261 transform:translate(0,10px) scale(1); 262 } 263 264 :host([direction="bottomleft"]) ::slotted(lit-pop-content){ 265 right:0; 266 top:100%; 267 transform:translate(0,10px) scale(0); 268 transform-origin: right top; 269 } 270 :host([direction="bottomleft"]) ::slotted(lit-pop-content)::after, 271 :host(:not([direction])) ::slotted(lit-pop-content)::after{ 272 content: ''; 273 position: absolute; 274 right: 10%; 275 bottom: 100%; 276 border-bottom: 10px solid #FFF; 277 border-left: 10px solid transparent; 278 } 279 :host([direction="bottomleft"]) ::slotted(lit-pop-content[open]), 280 :host([direction="bottomleft"][trigger="hover"]:hover) ::slotted(lit-pop-content), 281 :host([direction="bottomleft"][trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 282 transform:translate(0,10px) scale(1); 283 } 284 285 :host ::slotted(lit-pop-content[open]), 286 :host([trigger="hover"]:hover) ::slotted(lit-pop-content), 287 :host([trigger="focus"]:focus-within) ::slotted(lit-pop-content){ 288 opacity:1; 289 visibility:visible; 290 } 291 slot{ 292 } 293 </style> 294 `; 295 296@element('lit-popover') 297export class LitPopover extends BaseElement { 298 private popContent: LitPopContent | null | undefined; 299 private litGroup: LitRadioGroup | LitCheckGroup | undefined; 300 private _texBox: LitCheckBoxWithText | undefined; 301 302 static get observedAttributes(): string[] { 303 return []; 304 } 305 306 get type(): string { 307 return this.getAttribute('type') || ''; 308 } 309 310 set type(type: string) { 311 this.setAttribute('type', type); 312 } 313 314 get title(): string { 315 return this.getAttribute('title') || ''; 316 } 317 318 set title(title: string) { 319 this.setAttribute('title', title); 320 } 321 322 get limit(): LimitText { 323 if (this._texBox?.checked) { 324 return { 325 textLowerLimit: this._texBox.lowerLimit, 326 textUpperLimit: this._texBox.upLimit, 327 }; 328 } 329 return { textLowerLimit: '', textUpperLimit: '' }; 330 } 331 332 set dataSource(dataSource: Array<SelectBean>) { 333 this.popContent = this.querySelector<LitPopContent>('lit-pop-content'); 334 if (!this.popContent) { 335 this.popContent = new LitPopContent(); 336 this.appendChild(this.popContent); 337 } 338 switch (this.type) { 339 case 'multiple': 340 this.setMultiple(dataSource); 341 break; 342 case 'radio': 343 this.litGroup = new LitRadioGroup(); 344 if (this.title !== '') { 345 let title = new LitPopoverTitle(); 346 title.setAttribute('title', this.title || ''); 347 this.popContent!.appendChild(title); 348 this.litGroup.setAttribute('layout', 'compact'); 349 } else { 350 this.litGroup.setAttribute('layout', 'dispersion'); 351 } 352 this.popContent!.appendChild(this.litGroup); 353 dataSource.forEach((data) => { 354 let litRadioBox = new LitRadioBox(); 355 if (this.title === '') { 356 litRadioBox.setAttribute('dis', 'round'); 357 } else { 358 litRadioBox.setAttribute('dis', 'check'); 359 } 360 if (data.isSelected) { 361 litRadioBox.setAttribute('checked', 'true'); 362 } 363 this.litGroup?.appendChild(litRadioBox); 364 litRadioBox.setAttribute('value', data.text); 365 }); 366 break; 367 case 'multiple-text': 368 dataSource.forEach((data) => { 369 this._texBox = new LitCheckBoxWithText(); 370 this._texBox.setAttribute('text', data.text); 371 this._texBox.setAttribute('checked', ''); 372 this.popContent!.appendChild(this._texBox); 373 }); 374 break; 375 case 'data-ming': 376 break; 377 } 378 } 379 380 setMultiple(dataSource: Array<SelectBean>): void { 381 this.litGroup = new LitCheckGroup(); 382 this.litGroup.setAttribute('layout', 'dispersion'); 383 this.popContent!.appendChild(this.litGroup); 384 dataSource.forEach((data) => { 385 let litCheckBox = new LitCheckBox(); 386 this.litGroup?.appendChild(litCheckBox); 387 if (data.isSelected) { 388 litCheckBox.setAttribute('checked', 'true'); 389 } 390 litCheckBox.setAttribute('value', data.text); 391 }); 392 } 393 394 get select(): Array<string> | undefined { 395 if (this._texBox?.checked) { 396 return [this._texBox!.text]; 397 } 398 return this.litGroup?.value; 399 } 400 401 get trigger(): string | null { 402 return this.getAttribute('trigger'); 403 } 404 405 get direction(): string { 406 return this.getAttribute('direction') || 'topright'; 407 } 408 409 set direction(value: string) { 410 this.setAttribute('direction', value); 411 } 412 413 get open(): boolean { 414 return this.getAttribute('open') !== null; 415 } 416 417 set open(value: boolean) { 418 if (value === null || value === false) { 419 this.removeAttribute('open'); 420 } else { 421 this.setAttribute('open', ''); 422 } 423 } 424 425 initElements(): void {} 426 427 initHtml(): string { 428 return ` 429 ${initHtmlStyle} 430 <slot></slot> 431 `; 432 } 433 434 connectedCallback(): void { 435 if (!(this.trigger && this.trigger !== 'click')) { 436 this.addEventListener('click', () => { 437 this.popContent = this.querySelector<LitPopContent>('lit-pop-content'); 438 if (!this.popContent) { 439 this.popContent = new LitPopContent(); 440 this.appendChild(this.popContent); 441 } 442 this.popContent?.setAttribute('open', 'true'); 443 }); 444 } 445 document.addEventListener('mousedown', (ev) => { 446 const path = ev.composedPath && ev.composedPath(); 447 if ( 448 // @ts-ignore 449 this.popContent && 450 !path.includes(this.popContent) && 451 !path.includes(this.children[0]) && 452 !path.includes(this.popContent) 453 ) { 454 this.popContent!.open = false; 455 } 456 }); 457 } 458} 459 460export interface SelectBean { 461 text: string; 462 isSelected: boolean; 463 limitText?: LimitText; 464} 465 466export interface LimitText { 467 textUpperLimit: string; 468 textLowerLimit: string; 469} 470 471export interface Charge { 472 text: string; 473 isSelected: boolean; 474} 475