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 "../../../../base-ui/BaseElement.js"; 17import "../../../../base-ui/select/LitSelect.js"; 18import "../../../../base-ui/select/LitSelectOption.js"; 19import '../../../../base-ui/icon/LitIcon.js' 20import {LitIcon} from "../../../../base-ui/icon/LitIcon.js"; 21import "../../../../base-ui/popover/LitPopoverV.js" 22import {LitCheckBox} from "../../../../base-ui/checkbox/LitCheckBox.js"; 23 24export interface FilterData { 25 inputValue: string, 26 firstSelect: string | null | undefined, 27 secondSelect: string | null | undefined, 28 mark: boolean | null | undefined, 29 icon: string | null, 30 type: string, 31} 32 33export interface MiningData { 34 type: string, 35 item: any | null | undefined, 36 remove?: Array<any> | null | undefined, 37} 38 39@element('tab-pane-filter') 40export class TabPaneFilter extends BaseElement { 41 private filterInputEL: HTMLInputElement | null | undefined; 42 private firstSelectEL: HTMLSelectElement | null | undefined; 43 private secondSelectEL: HTMLSelectElement | null | undefined; 44 private markButtonEL: HTMLButtonElement | null | undefined; 45 private iconEL: LitIcon | null | undefined; 46 private statisticsName: HTMLDivElement | null | undefined; 47 private getFilter: ((e: FilterData) => void) | undefined; 48 private getMining: ((e: MiningData) => void) | undefined; 49 private getLibrary: ((e: MiningData) => void) | undefined; 50 private getCallTree: ((e: any) => void) | undefined; 51 private getCallTreeConstraints: ((e: any) => void) | undefined; 52 private getStatisticsType: ((e: any) => void) | undefined; 53 54 private cutList: Array<any> | undefined; 55 private libraryList: Array<any> | undefined; 56 57 filterData(type:string,data:object = {}){ 58 return { 59 type: type, 60 inputValue: this.filterInputEL!.value, 61 firstSelect: this.firstSelectEL?.value, 62 secondSelect: this.secondSelectEL?.value, 63 mark: false, 64 icon: this.icon, 65 ...data 66 } 67 } 68 69 initElements(): void { 70 this.cutList = []; 71 this.libraryList = []; 72 this.filterInputEL = this.shadowRoot?.querySelector("#filter-input") 73 this.markButtonEL = this.shadowRoot?.querySelector("#mark") 74 this.iconEL = this.shadowRoot?.querySelector<LitIcon>("#icon") 75 this.statisticsName = this.shadowRoot?.querySelector<HTMLDivElement>(".statistics-name"); 76 this.iconEL!.onclick = (e) => { 77 if (this.iconEL!.name == "statistics") { 78 this.iconEL!.name = "menu"; 79 this.iconEL!.size = 18; 80 if (this.getFilter) { 81 this.getFilter(this.filterData("icon")) 82 } 83 } else if (this.iconEL!.name == "menu") { 84 this.iconEL!.name = "statistics"; 85 this.iconEL!.size = 16; 86 if (this.getFilter) { 87 this.getFilter(this.filterData("icon")) 88 } 89 } 90 } 91 92 this.markButtonEL!.onclick = (e) => { 93 if (this.getFilter) { 94 this.getFilter(this.filterData("mark",{mark: true})) 95 } 96 } 97 98 this.filterInputEL?.addEventListener("keyup", (event: any) => { 99 if (event.keyCode == 13) { 100 if (this.getFilter) { 101 this.getFilter(this.filterData("inputValue",{inputValue: event.target.value})) 102 } 103 } 104 event.stopPropagation(); 105 }); 106 107 this.filterInputEL?.addEventListener("keypress", (event: any) => { 108 event.stopPropagation(); 109 }); 110 111 this.setSelectList() 112 113 this.initializeCallTree() 114 115 this.initializeTreeConstraints() 116 117 this.initializeMining() 118 119 this.initializeLibrary() 120 121 this.shadowRoot!.querySelectorAll<HTMLDivElement>(".mining-button").forEach((e, idx) => { 122 e!.onclick = (ev) => { 123 if (idx == 0) { 124 const restoreList = this.cutList!.filter(item => item.highlight === true) 125 const list = this.cutList!.filter(item => item.highlight === false) 126 this.cutList = list; 127 if (this.getMining) { 128 this.getMining({type: "button", item: "restore", remove: restoreList}); 129 } 130 this.initializeMining(); 131 } 132 } 133 }) 134 this.shadowRoot!.querySelector<HTMLDivElement>(".library-button")!.onclick = (ev)=>{ 135 const restoreList = this.libraryList!.filter(item => item.highlight === true) 136 const list = this.libraryList!.filter(item => item.highlight === false) 137 this.libraryList = list; 138 if (this.getLibrary) { 139 this.getLibrary({type: "button", item: "restore", remove: restoreList}); 140 } 141 this.initializeLibrary(); 142 } 143 144 this.shadowRoot!.querySelector<HTMLDivElement>("#data-mining")!.onclick = (e)=>{ 145 if (this.getMining) { 146 this.getMining({type: "button", item: "symbol"}); 147 } 148 } 149 this.shadowRoot!.querySelector<HTMLDivElement>("#data-library")!.onclick = (e)=>{ 150 if (this.getLibrary) { 151 this.getLibrary({type: "button", item: "library"}); 152 } 153 } 154 this.shadowRoot!.querySelector<HTMLDivElement>(".sort")!.onclick =(e)=>{ 155 let statisticsType = this.statisticsName!.textContent == "Statistics by Operation" 156 this.statisticsName!.textContent = statisticsType?"Statistics by Thread":"Statistics by Operation"; 157 if (this.getStatisticsType) { 158 this.getStatisticsType(statisticsType?"thread":"operation"); 159 } 160 } 161 } 162 163 set firstSelect(value: string) { 164 this.firstSelectEL!.value = value; 165 } 166 167 get firstSelect() { 168 return this.firstSelectEL?.value || "" 169 } 170 171 set secondSelect(value: string) { 172 this.secondSelectEL!.value = value; 173 } 174 175 get secondSelect() { 176 return this.secondSelectEL?.value || "" 177 } 178 179 set filterValue(value: string) { 180 this.filterInputEL!.value = value; 181 } 182 183 get filterValue() { 184 return this.filterInputEL!.value 185 } 186 187 get inputPlaceholder() { 188 return this.getAttribute("inputPlaceholder") || "Detail Filter"; 189 } 190 191 get icon() { 192 if (this.getAttribute("icon") != "false") { 193 if (this.iconEL!.name == "statistics") { 194 return "tree" 195 } else if (this.iconEL!.name == "menu") { 196 return "block" 197 } else { 198 return "" 199 } 200 } else { 201 return ""; 202 } 203 } 204 205 set icon(value: string) { 206 if (value == "block") { 207 this.iconEL!.name = "menu"; 208 this.iconEL!.size = 18; 209 } else if (value == "tree") { 210 this.iconEL!.name = "statistics"; 211 this.iconEL!.size = 16; 212 } 213 } 214 215 get disabledMining(){ 216 return this.hasAttribute("disabledMining") 217 } 218 219 set disabledMining(value:boolean){ 220 if (value) { 221 this.setAttribute("disabledMining","") 222 }else { 223 this.removeAttribute("disabledMining") 224 } 225 } 226 227 getCallTreeData(getCallTree: (v: any) => void) { 228 this.getCallTree = getCallTree 229 } 230 231 getCallTreeConstraintsData(getCallTreeConstraints: (v: any) => void) { 232 this.getCallTreeConstraints = getCallTreeConstraints 233 } 234 235 getFilterData(getFilter: (v: FilterData) => void) { 236 this.getFilter = getFilter 237 } 238 239 getStatisticsTypeData(getStatisticsType: (v: any) => void) { 240 this.getStatisticsType = getStatisticsType 241 } 242 243 setSelectList(firstList: Array<any> | null | undefined = ["All Allocations", "Created & Existing", "Created & Destroyed"], 244 secondList: Array<any> | null | undefined = ["All Heap & Anonymous VM", "All Heap", "All Anonymous VM"], 245 firstTitle = "Allocation Lifespan",secondTitle = "Allocation Type") { 246 if (!firstList && !secondList) return; 247 let sLE = this.shadowRoot?.querySelector("#load") 248 let html = ``; 249 if (firstList) { 250 html += `<lit-select default-value="" id="first-select" class="spacing" placeholder="please choose">` 251 if(firstTitle != ""){ 252 html += `<lit-select-option value="${firstTitle}" disabled>${firstTitle}</lit-select-option>` 253 } 254 firstList!.forEach((a, b) => { 255 html += `<lit-select-option value="${b}">${a}</lit-select-option>` 256 }) 257 html += `</lit-select>` 258 } 259 if (secondList) { 260 html += `<lit-select default-value="" id="second-select" class="spacing" placeholder="please choose">` 261 if(secondTitle != ""){ 262 html += `<lit-select-option value="${secondTitle}" disabled>${secondTitle}</lit-select-option>` 263 } 264 secondList!.forEach((a, b) => { 265 html += `<lit-select-option value="${b}">${a}</lit-select-option>` 266 }) 267 html += `</lit-select>` 268 } 269 if (!firstList) { 270 this.secondSelectEL!.outerHTML = html; 271 } else if (!secondList) { 272 this.firstSelectEL!.outerHTML = html; 273 } else { 274 sLE!.innerHTML = html; 275 } 276 277 this.firstSelectEL = this.shadowRoot?.querySelector("#first-select") 278 this.secondSelectEL = this.shadowRoot?.querySelector("#second-select") 279 280 this.firstSelectEL!.onchange = (e) => { 281 if (this.getFilter) { 282 this.getFilter(this.filterData("firstSelect")) 283 } 284 } 285 this.secondSelectEL!.onchange = (e) => { 286 if (this.getFilter) { 287 this.getFilter(this.filterData("secondSelect")) 288 } 289 } 290 } 291 292 initializeCallTree() { 293 let row = this.shadowRoot!.querySelectorAll(".tree-check"); 294 row.forEach((e, idx) => { 295 let check = e.querySelector<LitCheckBox>("lit-check-box") 296 e.querySelector("div")!.onclick = (ev) => { 297 if (this.getCallTree) { 298 if (idx == 0) { 299 this.getCallTree({ 300 checks: [!check!.checked, row[1].querySelector<LitCheckBox>("lit-check-box")!.checked], 301 value: idx 302 }); 303 } else { 304 this.getCallTree({ 305 checks: [row[0].querySelector<LitCheckBox>("lit-check-box")!.checked, !check!.checked], 306 value: idx 307 }); 308 } 309 } 310 check!.checked = !check!.checked; 311 } 312 check!.onchange = (ev: any) => { 313 if (this.getCallTree) { 314 if (idx == 0) { 315 this.getCallTree({ 316 checks: [ev.target.checked, row[1].querySelector<LitCheckBox>("lit-check-box")!.checked], 317 value: idx 318 }); 319 } else { 320 this.getCallTree({ 321 checks: [row[0].querySelector<LitCheckBox>("lit-check-box")!.checked, ev.target.checked], 322 value: idx 323 }); 324 } 325 } 326 } 327 }) 328 } 329 330 initializeTreeConstraints() { 331 let inputs = this.shadowRoot!.querySelectorAll<HTMLInputElement>(".constraints-input") 332 let check = this.shadowRoot!.querySelector<LitCheckBox>("#constraints-check") 333 check!.onchange = (ev: any) => { 334 inputs.forEach((e: any, idx) => { 335 if (inputs[idx].value == "") { 336 inputs[idx].value = idx == 0 ? "0" : "∞" 337 } 338 ev.target.checked ? e.removeAttribute("disabled") : e.setAttribute("disabled", '') 339 }) 340 if (this.getCallTreeConstraints) { 341 this.getCallTreeConstraints({ 342 checked: ev.target.checked, 343 min: inputs[0].value, 344 max: inputs[1].value 345 }); 346 } 347 } 348 inputs.forEach((e, idx) => { 349 e.oninput = function () { 350 // @ts-ignore 351 this.value = this.value.replace(/\D/g, ''); 352 } 353 e.addEventListener("keyup", (event: any) => { 354 event.stopPropagation(); 355 if (event.keyCode == "13") { 356 if (event?.target.value == "") { 357 inputs[idx].value = idx == 0 ? "0" : "∞" 358 } 359 if (this.getCallTreeConstraints) { 360 this.getCallTreeConstraints({ 361 checked: check!.checked, 362 min: idx == 0 ? event?.target.value : inputs[0].value, 363 max: idx == 1 ? event?.target.value : inputs[1].value 364 }); 365 } 366 } 367 }) 368 }) 369 } 370 371 initializeMining() { 372 let html = ``; 373 this.cutList!.forEach((a, b) => { 374 html += `<div style="display: flex;padding: 4px 7px;" class="mining-checked" ${a.highlight ? "highlight" : ""}> 375 <lit-check-box class="lit-check-box" not-close ${a.checked ? "checked" : ""} style="display: flex"></lit-check-box> 376 <div id="title" title="${a.name}">${a.name}</div></div>`; 377 }) 378 379 this.shadowRoot!.querySelector<HTMLDivElement>("#mining-row")!.innerHTML = html; 380 381 let row = this.shadowRoot!.querySelector("#mining-row")!.childNodes; 382 row!.forEach((e: any, idx) => { 383 e!.querySelector("#title")!.onclick = (ev: any) => { 384 if (e.getAttribute("highlight") == "") { 385 e.removeAttribute("highlight") 386 this.cutList![idx].highlight = false; 387 } else { 388 e.setAttribute("highlight", ""); 389 this.cutList![idx].highlight = true; 390 } 391 } 392 // @ts-ignore 393 e!.querySelector<LitCheckBox>("lit-check-box")!.onchange = (ev) => { 394 // @ts-ignore 395 this.cutList[idx].checked = e!.querySelector<LitCheckBox>("lit-check-box")!.checked; 396 if (this.getMining) { 397 this.getMining({type: "check", item: this.cutList![idx]}); 398 } 399 } 400 }) 401 } 402 403 initializeLibrary() { 404 let html = ``; 405 this.libraryList!.forEach((a, b) => { 406 html += `<div style="display: flex;padding: 4px 7px;" class="library-checked" ${a.highlight ? "highlight" : ""}> 407 <lit-check-box class="lit-check-box" not-close ${a.checked ? "checked" : ""} style="display: flex"></lit-check-box> 408 <div id="title" title="${a.name}">${a.name}</div></div>`; 409 }) 410 411 this.shadowRoot!.querySelector<HTMLDivElement>("#library-row")!.innerHTML = html; 412 413 let row = this.shadowRoot!.querySelector("#library-row")!.childNodes; 414 row!.forEach((e: any, idx) => { 415 e!.querySelector("#title")!.onclick = (ev: any) => { 416 if (e.getAttribute("highlight") == "") { 417 e.removeAttribute("highlight") 418 this.libraryList![idx].highlight = false; 419 } else { 420 e.setAttribute("highlight", ""); 421 this.libraryList![idx].highlight = true; 422 } 423 } 424 425 // @ts-ignore 426 e!.querySelector<LitCheckBox>("lit-check-box")!.onchange = (ev) => { 427 // @ts-ignore 428 this.libraryList[idx].checked = e!.querySelector<LitCheckBox>("lit-check-box")!.checked; 429 if (this.getLibrary) { 430 this.getLibrary({type: "check", item: this.libraryList![idx]}); 431 } 432 } 433 }) 434 } 435 436 getDataMining(getMining: (v: MiningData) => void) { 437 this.getMining = getMining 438 } 439 440 getDataLibrary(getLibrary: (v: MiningData) => void) { 441 this.getLibrary = getLibrary 442 } 443 444 addDataMining(data: any, type: string) { 445 let list:Array<any> = (type=="symbol"?this.cutList:this.libraryList) ||[]; 446 let idx = list!.findIndex((e) => e.name == data.name) 447 if (idx == -1) { 448 list!.push({type: type, name: data.name, checked: true, select: "1", data: data, highlight: false}); 449 } else { 450 list![idx] = {type: type, name: data.name, checked: true, select: "1", data: data, highlight: false} 451 } 452 this.initializeMining(); 453 this.initializeLibrary(); 454 return idx; 455 } 456 457 getFilterTreeData() { 458 let row = this.shadowRoot!.querySelectorAll<LitCheckBox>(".tree-check lit-check-box"); 459 let inputs = this.shadowRoot!.querySelectorAll<HTMLInputElement>(".constraints-input") 460 let check = this.shadowRoot!.querySelector<LitCheckBox>("#constraints-check") 461 let data = { 462 callTree: [row[0]!.checked, row[1]!.checked], 463 callTreeConstraints: { 464 checked: check!.checked, 465 inputs: [inputs[0].value == "" ? "0" : inputs[0].value, inputs[1].value == "" ? "∞" : inputs[1].value] 466 }, 467 dataMining: this.cutList, 468 dataLibrary: this.libraryList, 469 } 470 return data; 471 } 472 473 initializeFilterTree(callTree: boolean = true, treeConstraints: boolean = true, mining: boolean = true) { 474 if (callTree) { 475 let row = this.shadowRoot!.querySelectorAll(".tree-check"); 476 row.forEach((e, idx) => { 477 let check = e.querySelector<LitCheckBox>("lit-check-box") 478 check!.checked = false 479 }) 480 } 481 if (treeConstraints) { 482 let inputs = this.shadowRoot!.querySelectorAll<HTMLInputElement>(".constraints-input") 483 if (inputs.length > 0) { 484 inputs[0].value = "0"; 485 inputs[1].value = "∞"; 486 } 487 let check = this.shadowRoot!.querySelector<LitCheckBox>("#constraints-check") 488 check!.checked = false 489 } 490 if (mining) { 491 this.cutList = []; 492 this.libraryList = []; 493 this.initializeMining(); 494 this.initializeLibrary(); 495 } 496 } 497 498 initHtml(): string { 499 return ` 500 <style> 501 :host{ 502 height: 30px; 503 background: var(--dark-background4,#F2F2F2); 504 border-top: 1px solid var(--dark-border1,#c9d0da);display: flex;align-items: center;z-index: 2; 505 margin-left: -10px; 506 width: calc(100% + 20px); 507 } 508 509 .chosen-single { 510 position: relative; 511 display: block; 512 overflow: hidden; 513 text-decoration: none; 514 white-space: nowrap; 515 height: 34px; 516 padding: 3px 6px; 517 font-size: 14px; 518 line-height: 1.42857143; 519 color: #555; 520 background-color: #fff; 521 background-image: none; 522 border: 1px solid #ccc; 523 border-radius: 4px; 524 transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; 525 box-shadow: inset 0 1px 1px rgba(0,0,0,.075); 526 } 527 .disabled{ 528 color: rgba(0,0,0,0.4); 529 } 530 #filter-input{ 531 background: var(--dark-background4,#FFFFFF); 532 border: 1px solid var(--dark-border,rgba(0,0,0,0.60)); 533 color: var(--dark-color2,#000000); 534 border-radius: 8px; 535 width: 200px; 536 } 537 #filter-input:focus{ 538 outline: none; 539 box-shadow: 1px 1px 1px var(--dark-color,#bebebe); 540 } 541 #filter-input::-webkit-input-placeholder { 542 color: var(--dark-color,#aab2bd); 543 } 544 .describe{ 545 font-size: 0.8rem; 546 } 547 548 #mark{ 549 border: 1px solid var(--bark-prompt,#999999); 550 border-radius: 1px; 551 background: var(--dark-background4,#F2F2F2); 552 color: var(--dark-color2,rgba(0,0,0,0.9)); 553 transition: all 0.1s; 554 } 555 #mark:hover{ 556 background: var(--dark-background1,#dfdfdf); 557 } 558 #mark:active{ 559 background: var(--dark-background4,#F2F2F2); 560 transition: all 0.05s; 561 } 562 #first-select{ 563 width: 200px; 564 } 565 #second-select{ 566 width: 200px; 567 } 568 .spacing{ 569 margin-left: 10px; 570 } 571 .max-spacing{ 572 margin-left: 15px; 573 } 574 575 :host(:not([inputLeftText])) .left-text{ 576 display: none; 577 } 578 :host(:not([input])) #filter-input{ 579 display: none; 580 } 581 :host(:not([mark])) #mark{ 582 display: none; 583 } 584 :host(:not([first])) #first-select{ 585 display: none; 586 } 587 :host(:not([second])) #second-select{ 588 display: none; 589 } 590 :host(:not([tree])) .tree{ 591 display: none; 592 } 593 :host([disabledMining]) #data-mining{ 594 display: none; 595 } 596 :host([disabledMining]) #data-library{ 597 display: none; 598 } 599 :host(:not([icon])) #icon{ 600 display: none; 601 } 602 #icon[name="statistics"]{ 603 margin-left: 12px; 604 } 605 606 .constraints-input{ 607 background: var(--dark-border,#ffffff); 608 color: var(--dark-color1,rgba(0,0,0,0.86)); 609 border: 1px solid var(--dark-border,rgba(0,0,0,0.60)); 610 border-radius: 10px; 611 width: 40px; 612 margin-left: 10px; 613 outline: none; 614 } 615 .constraints-input[disabled]{ 616 background: var(--dark-background5,#ededed); 617 } 618 .mining-button{ 619 opacity: 0.9; 620 font-size: 13px; 621 color: #0A59F7; 622 text-align: center; 623 line-height: 16px; 624 background: var(--dark-background3,#F4F3F4); 625 border: 1px solid var(--dark-background8,#F4F3F4); 626 border-radius: 16px; 627 padding: 2px 18px; 628 } 629 .library-button{ 630 opacity: 0.9; 631 font-size: 13px; 632 color: #0A59F7; 633 text-align: center; 634 line-height: 16px; 635 background: var(--dark-background3,#F4F3F4); 636 border: 1px solid var(--dark-background8,#F4F3F4); 637 border-radius: 16px; 638 padding: 2px 18px; 639 } 640 641 #call-tree-popover[visible="true"] #call-tree{ 642 color: #0A59F7; 643 } 644 #tree-constraints-popover[visible="true"] #tree-constraints{ 645 color: #0A59F7; 646 } 647 #data-mining-popover[visible="true"] #data-mining{ 648 color: #0A59F7; 649 } 650 651 .mining-checked[highlight]{ 652 color: #FFFFFF; 653 background: #0C65D1; 654 } 655 #data-library-popover[visible="true"] #data-library{ 656 color: #0A59F7; 657 } 658 .library-checked[highlight]{ 659 color: #FFFFFF; 660 background: #0C65D1; 661 } 662 #title{ 663 overflow: hidden; 664 white-space: nowrap; 665 text-overflow: ellipsis; 666 flex: 1; 667 text-align: left; 668 } 669 #mining-row{ 670 background: var(--dark-background4,#F2F2F2); 671 border-radius: 2px; 672 height: 135px; 673 width: 250px; 674 overflow-y: auto; 675 } 676 #library-row{ 677 background: var(--dark-background4,#F2F2F2); 678 border-radius: 2px; 679 height: 135px; 680 width: 250px; 681 overflow-y: auto; 682 } 683 .tree-check{ 684 margin-bottom: 5px; 685 display: flex; 686 align-content: center; 687 } 688 .sort{ 689 display: flex; 690 align-items: center; 691 cursor: pointer; 692 } 693 :host(:not([sort])) .sort{ 694 display: none; 695 } 696</style> 697 <lit-icon name="statistics" class="spacing" id="icon" size="16"></lit-icon> 698 <span class="describe left-text spacing">Input Filter</span> 699 <input id="filter-input" class="spacing" placeholder="${this.inputPlaceholder}"/> 700 <button id="mark" class="spacing">Mark Snapshot</button> 701 <div id="load" style="display: flex"> 702 703 </div> 704 <lit-popover placement="topLeft" class="popover" haveRadio="true" trigger="click" id="call-tree-popover"> 705 <div slot="content"> 706 <div class="tree-check"><lit-check-box class="lit-check-box" not-close></lit-check-box><div>Invert</div></div> 707 <div class="tree-check"><lit-check-box class="lit-check-box" not-close></lit-check-box><div>Hide System so</div></div> 708 </div> 709 <span class="describe tree max-spacing" id="call-tree">Options</span> 710 </lit-popover> 711 <lit-popover placement="topLeft" class="popover" haveRadio="true" trigger="click" id="tree-constraints-popover"> 712 <div slot="content" style="display: flex; align-items: flex-end"> 713 <lit-check-box id="constraints-check" not-close></lit-check-box> 714 <input class="constraints-input" disabled value="0" not-close/> 715 <lit-popover placement="topLeft" class="popover" haveRadio="true" not-close> 716 <div slot="content"> 717 <div style="font-size: 0.7rem">Constraints:Only enabled with data and while stopped;</div> 718 <div style="font-size: 0.7rem">filters data to thresholds. </div> 719 </div> 720 <input class="constraints-input" disabled value="∞" not-close/> 721 </lit-popover> 722 </div> 723 <span class="describe tree max-spacing" id="tree-constraints">Sample Count Filter</span> 724 </lit-popover> 725 <lit-popover placement="topLeft" class="popover" haveRadio="true" trigger="click" id="data-mining-popover"> 726 <div slot="content"> 727 <div id="mining-row"> 728 729 </div> 730 <div style="display: flex;justify-content: space-around; margin-top: 8px"> 731 <div class="mining-button">Reset</div> 732 </div> 733 </div> 734 <span class="describe tree max-spacing" id="data-mining">Symbol Filter</span> 735 </lit-popover> 736 <lit-popover placement="topLeft" class="popover" haveRadio="true" trigger="click" id="data-library-popover"> 737 <div slot="content"> 738 <div id="library-row"> 739 740 </div> 741 <div style="display: flex;justify-content: space-around; margin-top: 8px"> 742 <div class="library-button">Reset</div> 743 </div> 744 </div> 745 <span class="describe tree max-spacing" id="data-library">Library Filter</span> 746 </lit-popover> 747 <div class="sort"> 748 <lit-icon name="swap" class="spacing" size="16"></lit-icon> 749 <div style="margin-left: 5px" class="statistics-name">Statistics by Thread</div> 750 </div> 751 `; 752 } 753} 754