phoenix-ui-components 4.2.0 → 4.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,7 +18,7 @@ import * as i1$1 from '@angular/material/dialog';
18
18
  import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
19
19
  import * as i2$2 from '@angular/material/icon';
20
20
  import { MatIconModule } from '@angular/material/icon';
21
- import { ActiveVariable, EventDisplay, SceneManager, PrettySymbols, Edm4hepJsonLoader, JiveXMLLoader, PHYSLITELoader, ATLASESDLoader, CMSLoader, readZipFile, VRManager, XRSessionType, ARManager, phoenixURLOptions, getAvailableColumns, DEFAULT_ETA_PHI_CONFIG, ATLAS_KINEMATICS, ATLAS_MASTERCLASS_CONFIG, invariantMass, fourMomentumFromCluster, fourMomentumFromTrack } from 'phoenix-event-display';
21
+ import { ActiveVariable, EventDisplay, SceneManager, PrettySymbols, Edm4hepJsonLoader, JiveXMLLoader, PHYSLITELoader, ATLASESDLoader, CMSLoader, readZipFile, VRManager, XRSessionType, ARManager, phoenixURLOptions, getAvailableColumns, DEFAULT_ETA_PHI_CONFIG, ATLAS_KINEMATICS, ATLAS_MASS_HISTOGRAM, ATLAS_MASTERCLASS_CONFIG, invariantMass, fourMomentumFromCluster, fourMomentumFromTrack } from 'phoenix-event-display';
22
22
  import * as i2$3 from '@angular/material/snack-bar';
23
23
  import { MatSnackBarModule } from '@angular/material/snack-bar';
24
24
  import RecordRTC from 'recordrtc';
@@ -29,7 +29,6 @@ import * as i3$1 from '@angular/material/tooltip';
29
29
  import { MatTooltipModule } from '@angular/material/tooltip';
30
30
  import * as i4$2 from '@angular/material/menu';
31
31
  import { MatMenuTrigger, MatMenuModule } from '@angular/material/menu';
32
- import { ResizeSensor } from 'css-element-queries';
33
32
  import * as i3$2 from '@angular/cdk/drag-drop';
34
33
  import { DragDropModule } from '@angular/cdk/drag-drop';
35
34
  import { ComponentPortal } from '@angular/cdk/portal';
@@ -46,6 +45,8 @@ import { MatTreeNestedDataSource } from '@angular/material/tree';
46
45
  import JSZip from 'jszip';
47
46
  import * as i6 from '@angular/material/tabs';
48
47
  import { MatTabsModule } from '@angular/material/tabs';
48
+ import { createHistogram, gStyle } from 'jsroot';
49
+ import { draw, redraw } from 'jsroot/draw';
49
50
 
50
51
  class NavComponent {
51
52
  constructor() { }
@@ -141,7 +142,7 @@ class ConfigSliderComponent {
141
142
  }] }); })();
142
143
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ConfigSliderComponent, { className: "ConfigSliderComponent", filePath: "components/phoenix-menu/config/config-slider/config-slider.component.ts", lineNumber: 11 }); })();
143
144
 
144
- const _c0$e = ["phoenixMenuItem"];
145
+ const _c0$f = ["phoenixMenuItem"];
145
146
  const _c1$7 = a0 => ({ expanded: a0 });
146
147
  const _c2$3 = a0 => ({ "top.px": a0 });
147
148
  const _c3$2 = a0 => ({ "range-slider-row": a0 });
@@ -517,7 +518,7 @@ class PhoenixMenuItemComponent {
517
518
  }
518
519
  static ɵfac = function PhoenixMenuItemComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PhoenixMenuItemComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
519
520
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PhoenixMenuItemComponent, selectors: [["app-phoenix-menu-item"]], viewQuery: function PhoenixMenuItemComponent_Query(rf, ctx) { if (rf & 1) {
520
- i0.ɵɵviewQuery(_c0$e, 5);
521
+ i0.ɵɵviewQuery(_c0$f, 5);
521
522
  } if (rf & 2) {
522
523
  let _t;
523
524
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.phoenixMenuItem = _t.first);
@@ -528,11 +529,11 @@ class PhoenixMenuItemComponent {
528
529
  i0.ɵɵproperty("ngIf", ctx.isCompact)("ngIfElse", fullItem_r19);
529
530
  i0.ɵɵadvance(3);
530
531
  i0.ɵɵproperty("ngIf", ctx.currentNode.childrenActive);
531
- } }, dependencies: [i2.NgClass, i2.NgForOf, i2.NgIf, i2.NgTemplateOutlet, i2.NgStyle, i2.NgSwitch, i2.NgSwitchCase, i3.MatButton, i4.NgSelectOption, i4.ɵNgSelectMultipleOption, i4.DefaultValueAccessor, i4.NumberValueAccessor, i4.NgControlStatus, i4.NgModel, i4$1.MatSlideToggle, i5.MatCheckbox, PhoenixMenuItemComponent, ConfigSliderComponent], styles: [".phoenix-menu-item{width:100%;padding:.6rem;background:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color);font-size:.8rem;transition:all .5s;display:flex;justify-content:space-between;align-items:center}.phoenix-menu-item-left{display:flex;justify-content:flex-start;min-width:0;padding-right:.5rem;overflow:hidden}.phoenix-menu-item-left .item-name{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.phoenix-menu-item-left .item-icon{margin-right:.2rem}.phoenix-menu-item-right{display:flex;justify-content:flex-end}.phoenix-menu-item-right .item-expand.expanded{transform:scaleY(-1)}.phoenix-menu-item-right .item-settings{margin-right:.2rem}@media screen and (max-width:768px){.phoenix-menu-item{font-size:.75rem;padding:.5rem}}.phoenix-menu-item-compact{flex:1;min-width:0;color:var(--phoenix-text-color)}.phoenix-menu-item-compact .compact-option-button{width:100%;display:flex;flex-direction:column;align-items:center;gap:.15rem;padding:.3rem .2rem;color:inherit;border-radius:.3rem;transition:all .4s}.phoenix-menu-item-compact .compact-option-button:hover{background:var(--phoenix-options-icon-bg);cursor:pointer}.phoenix-menu-item-compact .compact-option-button .compact-option-label{font-size:.65rem;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}.phoenix-menu-options-bar{display:flex;align-items:stretch;gap:.25rem;padding:.25rem .4rem;background:var(--phoenix-background-color-secondary)}.phoenix-menu-options-bar app-phoenix-menu-item{display:flex;flex:1;min-width:0}.phoenix-menu-item .item-config-backdrop,.phoenix-menu-item-compact .item-config-backdrop{position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:110}.phoenix-menu-item .item-config,.phoenix-menu-item-compact .item-config{position:absolute;right:100%;width:14rem;padding:.5rem;background:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color-secondary);box-shadow:var(--phoenix-box-shadow);display:flex;flex-direction:column;z-index:120;max-height:60vh;overflow-y:auto;font-size:.8rem}.phoenix-menu-item .item-config::-webkit-scrollbar,.phoenix-menu-item-compact .item-config::-webkit-scrollbar{width:4px}.phoenix-menu-item .item-config::-webkit-scrollbar-thumb,.phoenix-menu-item-compact .item-config::-webkit-scrollbar-thumb{background:var(--phoenix-text-color-secondary, #999);border-radius:4px}.phoenix-menu-item .item-config>*,.phoenix-menu-item-compact .item-config>*{margin-bottom:.4rem}.phoenix-menu-item .item-config>*:last-child,.phoenix-menu-item-compact .item-config>*:last-child{margin-bottom:0}.phoenix-menu-item .item-config .item-config-single,.phoenix-menu-item-compact .item-config .item-config-single{display:flex;flex-direction:row}.phoenix-menu-item .item-config .item-config-single .item-config-label,.phoenix-menu-item-compact .item-config .item-config-single .item-config-label{width:30%}.phoenix-menu-item .item-config .item-config-single .item-config-data,.phoenix-menu-item-compact .item-config .item-config-single .item-config-data{flex-grow:1;padding-left:.5rem;min-width:0}.phoenix-menu-item .item-config .item-config-single.range-slider-row,.phoenix-menu-item-compact .item-config .item-config-single.range-slider-row{align-items:center}.phoenix-menu-item .item-config .item-config-single.range-slider-row .item-config-label,.phoenix-menu-item-compact .item-config .item-config-single.range-slider-row .item-config-label{width:auto;min-width:1.6rem;flex-shrink:0;margin-bottom:0}.phoenix-menu-item .icon-wrapper,.phoenix-menu-item-compact .icon-wrapper{display:inline-block;width:1.3rem;height:1.3rem;padding:.23rem;transition:all .4s}.phoenix-menu-item .icon-wrapper.icon-button:hover,.phoenix-menu-item-compact .icon-wrapper.icon-button:hover{background:var(--phoenix-options-icon-bg);border-radius:40%;cursor:pointer}.phoenix-menu-item .icon-wrapper svg,.phoenix-menu-item-compact .icon-wrapper svg{width:100%;height:100%;vertical-align:top}.phoenix-menu-item .mat-slide-toggle-bar,.phoenix-menu-item-compact .mat-slide-toggle-bar{width:30px!important}.phoenix-menu-item .mat-slide-toggle-thumb,.phoenix-menu-item-compact .mat-slide-toggle-thumb{width:15px!important;height:15px!important;position:absolute;top:2px}.phoenix-menu-item .mat-slider-horizontal,.phoenix-menu-item-compact .mat-slider-horizontal{min-width:0;width:100%;height:20px}.phoenix-menu-item .mat-slider-horizontal .mat-slider-wrapper,.phoenix-menu-item-compact .mat-slider-horizontal .mat-slider-wrapper{top:10px}.phoenix-menu-item label.mat-checkbox-layout,.phoenix-menu-item-compact label.mat-checkbox-layout{margin:0}@media screen and (max-width:768px){.phoenix-menu-item .icon-wrapper,.phoenix-menu-item-compact .icon-wrapper{width:1rem;height:1rem;padding:.15rem}}.phoenix-menu-children{margin-left:.5rem;border-left:1px solid var(--phoenix-accent);max-height:35vh;overflow-y:auto}.phoenix-menu-children::-webkit-scrollbar{width:4px}.phoenix-menu-children::-webkit-scrollbar-thumb{background:var(--phoenix-text-color-secondary, #999);border-radius:4px}.range-slider .range-slider-inputs{gap:4px}.range-slider .range-slider-side{display:flex;align-items:center;flex:1;gap:3px;min-width:0}.range-slider .range-slider-side mat-checkbox{flex-shrink:0;--mdc-checkbox-state-layer-size: 24px}.range-slider .range-slider-side input[type=number]{min-width:0;width:100%;padding:.15rem .25rem;font-size:.75rem;appearance:textfield;-moz-appearance:textfield}.range-slider .range-slider-side input[type=number]::-webkit-outer-spin-button,.range-slider .range-slider-side input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}\n"], encapsulation: 2 });
532
+ } }, dependencies: [i2.NgClass, i2.NgForOf, i2.NgIf, i2.NgTemplateOutlet, i2.NgStyle, i2.NgSwitch, i2.NgSwitchCase, i3.MatButton, i4.NgSelectOption, i4.ɵNgSelectMultipleOption, i4.DefaultValueAccessor, i4.NumberValueAccessor, i4.NgControlStatus, i4.NgModel, i4$1.MatSlideToggle, i5.MatCheckbox, PhoenixMenuItemComponent, ConfigSliderComponent], styles: [".phoenix-menu-item{width:100%;padding:.6rem;background:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color);font-size:.8rem;transition:all .5s;display:flex;justify-content:space-between;align-items:center}.phoenix-menu-item-left{display:flex;justify-content:flex-start;min-width:0;padding-right:.5rem;overflow:hidden}.phoenix-menu-item-left .item-name{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.phoenix-menu-item-left .item-icon{margin-right:.2rem}.phoenix-menu-item-right{display:flex;justify-content:flex-end}.phoenix-menu-item-right .item-expand.expanded{transform:scaleY(-1)}.phoenix-menu-item-right .item-settings{margin-right:.2rem}@media screen and (max-width:768px){.phoenix-menu-item{font-size:.75rem;padding:.5rem}}.phoenix-menu-item-compact{flex:1;min-width:0;color:var(--phoenix-text-color)}.phoenix-menu-item-compact .compact-option-button{width:100%;display:flex;flex-direction:column;align-items:center;gap:.15rem;padding:.3rem .2rem;color:inherit;border-radius:.3rem;transition:all .4s}.phoenix-menu-item-compact .compact-option-button:hover{background:var(--phoenix-options-icon-bg);cursor:pointer}.phoenix-menu-item-compact .compact-option-button .compact-option-label{font-size:.65rem;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}.phoenix-menu-options-bar{display:flex;align-items:stretch;gap:.25rem;padding:.25rem .4rem;background:var(--phoenix-background-color-secondary)}.phoenix-menu-options-bar app-phoenix-menu-item{display:flex;flex:1;min-width:0}.phoenix-menu-item .item-config-backdrop,.phoenix-menu-item-compact .item-config-backdrop{position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:110}.phoenix-menu-item .item-config,.phoenix-menu-item-compact .item-config{position:absolute;right:100%;width:14rem;padding:.5rem;background:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color-secondary);box-shadow:var(--phoenix-box-shadow);display:flex;flex-direction:column;z-index:120;max-height:60vh;overflow-y:auto;font-size:.8rem}.phoenix-menu-item .item-config::-webkit-scrollbar,.phoenix-menu-item-compact .item-config::-webkit-scrollbar{width:4px}.phoenix-menu-item .item-config::-webkit-scrollbar-thumb,.phoenix-menu-item-compact .item-config::-webkit-scrollbar-thumb{background:var(--phoenix-text-color-secondary, #999);border-radius:4px}.phoenix-menu-item .item-config>*,.phoenix-menu-item-compact .item-config>*{margin-bottom:.4rem}.phoenix-menu-item .item-config>*:last-child,.phoenix-menu-item-compact .item-config>*:last-child{margin-bottom:0}.phoenix-menu-item .item-config .item-config-single,.phoenix-menu-item-compact .item-config .item-config-single{display:flex;flex-direction:row}.phoenix-menu-item .item-config .item-config-single .item-config-label,.phoenix-menu-item-compact .item-config .item-config-single .item-config-label{width:30%}.phoenix-menu-item .item-config .item-config-single .item-config-data,.phoenix-menu-item-compact .item-config .item-config-single .item-config-data{flex-grow:1;padding-left:.5rem;min-width:0}.phoenix-menu-item .item-config .item-config-single.range-slider-row,.phoenix-menu-item-compact .item-config .item-config-single.range-slider-row{align-items:center}.phoenix-menu-item .item-config .item-config-single.range-slider-row .item-config-label,.phoenix-menu-item-compact .item-config .item-config-single.range-slider-row .item-config-label{width:auto;min-width:1.6rem;flex-shrink:0;margin-bottom:0}.phoenix-menu-item .icon-wrapper,.phoenix-menu-item-compact .icon-wrapper{display:inline-block;width:1.3rem;height:1.3rem;padding:.23rem;transition:all .4s}.phoenix-menu-item .icon-wrapper.icon-button:hover,.phoenix-menu-item-compact .icon-wrapper.icon-button:hover{background:var(--phoenix-options-icon-bg);border-radius:40%;cursor:pointer}.phoenix-menu-item .icon-wrapper svg,.phoenix-menu-item-compact .icon-wrapper svg{width:100%;height:100%;vertical-align:top}.phoenix-menu-item .mat-slide-toggle-bar,.phoenix-menu-item-compact .mat-slide-toggle-bar{width:30px!important}.phoenix-menu-item .mat-slide-toggle-thumb,.phoenix-menu-item-compact .mat-slide-toggle-thumb{width:15px!important;height:15px!important;position:absolute;top:2px}.phoenix-menu-item .mat-slider-horizontal,.phoenix-menu-item-compact .mat-slider-horizontal{min-width:0;width:100%;height:20px}.phoenix-menu-item .mat-slider-horizontal .mat-slider-wrapper,.phoenix-menu-item-compact .mat-slider-horizontal .mat-slider-wrapper{top:10px}.phoenix-menu-item label.mat-checkbox-layout,.phoenix-menu-item-compact label.mat-checkbox-layout{margin:0}@media screen and (max-width:768px){.phoenix-menu-item .icon-wrapper,.phoenix-menu-item-compact .icon-wrapper{width:1rem;height:1rem;padding:.15rem}}.phoenix-menu-children{margin-left:.5rem;border-left:1px solid var(--phoenix-accent)}.range-slider .range-slider-inputs{gap:4px}.range-slider .range-slider-side{display:flex;align-items:center;flex:1;gap:3px;min-width:0}.range-slider .range-slider-side mat-checkbox{flex-shrink:0;--mdc-checkbox-state-layer-size: 24px}.range-slider .range-slider-side input[type=number]{min-width:0;width:100%;padding:.15rem .25rem;font-size:.75rem;appearance:textfield;-moz-appearance:textfield}.range-slider .range-slider-side input[type=number]::-webkit-outer-spin-button,.range-slider .range-slider-side input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}\n"], encapsulation: 2 });
532
533
  }
533
534
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PhoenixMenuItemComponent, [{
534
535
  type: Component,
535
- args: [{ standalone: false, selector: 'app-phoenix-menu-item', encapsulation: ViewEncapsulation.None, template: "<!-- Leaf nodes holding only configs (Draw/Cut/Color Options) are shown as a\n compact icon button; everything else as a full menu row. -->\n<div\n *ngIf=\"isCompact; else fullItem\"\n class=\"phoenix-menu-item-compact\"\n #phoenixMenuItem\n>\n <button\n class=\"btn-blank compact-option-button\"\n [title]=\"currentNode.name\"\n (click)=\"\n calculateConfigTop(); currentNode.configActive = !currentNode.configActive\n \"\n >\n <span class=\"icon-wrapper\">\n <svg>\n <use\n attr.href=\"assets/icons/{{ currentNode.icon }}.svg#{{\n currentNode.icon\n }}\"\n ></use>\n </svg>\n </span>\n <span class=\"compact-option-label\">{{ compactLabel }}</span>\n </button>\n <ng-container *ngTemplateOutlet=\"configPopup\"></ng-container>\n</div>\n\n<ng-template #fullItem>\n <div class=\"phoenix-menu-item\" #phoenixMenuItem>\n <div class=\"phoenix-menu-item-left align-items-center\">\n <!-- If the item has some toggle function -->\n <ng-container *ngIf=\"currentNode.onToggle; else nodeName\">\n <mat-slide-toggle\n [checked]=\"currentNode.toggleState\"\n (change)=\"currentNode.toggleSelfAndDescendants($event.checked)\"\n >\n <ng-container *ngTemplateOutlet=\"nodeName\"></ng-container>\n </mat-slide-toggle>\n </ng-container>\n <!-- If the item has no toggle function -->\n <ng-template #nodeName>\n <span class=\"d-flex w-100 align-items-center\">\n <span *ngIf=\"currentNode.icon\" class=\"icon-wrapper item-icon\">\n <svg>\n <use\n attr.href=\"assets/icons/{{ currentNode.icon }}.svg#{{\n currentNode.icon\n }}\"\n ></use>\n </svg>\n </span>\n <span class=\"item-name\" [title]=\"currentNode.name\">{{\n currentNode.name\n }}</span>\n </span>\n </ng-template>\n </div>\n <div class=\"phoenix-menu-item-right\">\n <div *ngIf=\"currentNode.configs.length > 0\">\n <button\n class=\"icon-wrapper icon-button btn-blank item-settings\"\n (click)=\"\n calculateConfigTop();\n currentNode.configActive = !currentNode.configActive\n \"\n >\n <svg>\n <use href=\"assets/icons/gear.svg#gear\"></use>\n </svg>\n </button>\n <ng-container *ngTemplateOutlet=\"configPopup\"></ng-container>\n </div>\n <button\n class=\"icon-wrapper icon-button btn-blank item-expand\"\n *ngIf=\"currentNode.children.length > 0\"\n (click)=\"currentNode.childrenActive = !currentNode.childrenActive\"\n [ngClass]=\"{ expanded: currentNode.childrenActive }\"\n data-testid=\"dropdown\"\n >\n <svg>\n <use href=\"assets/icons/expand.svg#expand\"></use>\n </svg>\n </button>\n </div>\n </div>\n</ng-template>\n\n<div class=\"phoenix-menu-children\" *ngIf=\"currentNode.childrenActive\">\n <div class=\"phoenix-menu-options-bar\" *ngIf=\"compactChildren.length > 0\">\n <app-phoenix-menu-item\n *ngFor=\"let childNode of compactChildren\"\n [currentNode]=\"childNode\"\n >\n </app-phoenix-menu-item>\n </div>\n <app-phoenix-menu-item\n *ngFor=\"let childNode of regularChildren\"\n [currentNode]=\"childNode\"\n >\n </app-phoenix-menu-item>\n</div>\n\n<!-- Config popup shared by the full row (gear button) and the compact icon button -->\n<ng-template #configPopup>\n <div\n class=\"item-config-backdrop\"\n *ngIf=\"currentNode.configActive\"\n (click)=\"currentNode.configActive = false\"\n ></div>\n <div\n class=\"item-config\"\n [hidden]=\"!currentNode.configActive\"\n [ngStyle]=\"{ 'top.px': configTop }\"\n >\n <ng-container\n *ngFor=\"let config of castConfigsToAny(currentNode.configs)\"\n [ngSwitch]=\"config.type\"\n >\n <!-- Config types -->\n <div\n class=\"item-config-single\"\n [ngClass]=\"{ 'range-slider-row': config.type === 'rangeSlider' }\"\n *ngIf=\"!config.hidden\"\n >\n <label\n class=\"item-config-label\"\n for=\"item-config-field\"\n *ngIf=\"config.type !== 'button'\"\n [ngClass]=\"{ 'font-weight-bold w-100': config.type === 'label' }\"\n >\n {{ config.label }}\n </label>\n <div\n class=\"item-config-data\"\n [ngClass]=\"{ 'p-0': config.type === 'button' }\"\n *ngIf=\"config.type !== 'label'\"\n >\n <mat-checkbox\n *ngSwitchCase=\"'checkbox'\"\n [checked]=\"config.isChecked\"\n (change)=\"\n config.onChange($event.checked); config.isChecked = $event.checked\n \"\n ></mat-checkbox>\n\n <app-config-slider\n *ngSwitchCase=\"'slider'\"\n [value]=\"config.value\"\n (onChange)=\"config.onChange($event); config.value = $event\"\n [min]=\"config.min\"\n [max]=\"config.max\"\n [step]=\"config.step\"\n [allowCustomValue]=\"config.allowCustomValue\"\n >\n </app-config-slider>\n\n <button\n *ngSwitchCase=\"'button'\"\n class=\"w-100\"\n (click)=\"config.onClick()\"\n mat-button\n mat-stroked-button\n >\n {{ config.label }}\n </button>\n\n <input\n *ngSwitchCase=\"'color'\"\n id=\"item-config-field\"\n type=\"color\"\n [value]=\"config.color\"\n (input)=\"\n config.onChange($event.target.value);\n config.color = $event.target.value\n \"\n />\n\n <div *ngSwitchCase=\"'rangeSlider'\" class=\"range-slider\">\n <div class=\"range-slider-inputs d-flex align-items-center\">\n <div class=\"range-slider-side\">\n <mat-checkbox\n [(ngModel)]=\"config.enableMin\"\n (change)=\"config.setEnableMin($event.checked)\"\n ></mat-checkbox>\n <input\n type=\"number\"\n placeholder=\"Min\"\n class=\"form-control form-control-sm\"\n [(ngModel)]=\"config.value\"\n (input)=\"\n config.value = $event.target.value; config.onChange(config)\n \"\n [step]=\"config.step\"\n />\n </div>\n <div class=\"range-slider-side\">\n <mat-checkbox\n [(ngModel)]=\"config.enableMax\"\n (change)=\"config.setEnableMax($event.checked)\"\n ></mat-checkbox>\n <input\n type=\"number\"\n placeholder=\"Max\"\n class=\"form-control form-control-sm\"\n [(ngModel)]=\"config.highValue\"\n (input)=\"\n config.highValue = $event.target.value;\n config.onChange(config)\n \"\n [step]=\"config.step\"\n />\n </div>\n </div>\n </div>\n\n <select\n *ngSwitchCase=\"'select'\"\n id=\"item-config-field\"\n class=\"w-100\"\n [value]=\"config.value ?? config.options[0]\"\n (change)=\"config.onChange($event.target.value)\"\n >\n <option\n *ngFor=\"let singleOption of config.options\"\n [value]=\"singleOption\"\n >\n {{ singleOption }}\n </option>\n </select>\n </div>\n </div>\n <!-- Config types END -->\n </ng-container>\n </div>\n</ng-template>\n", styles: [".phoenix-menu-item{width:100%;padding:.6rem;background:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color);font-size:.8rem;transition:all .5s;display:flex;justify-content:space-between;align-items:center}.phoenix-menu-item-left{display:flex;justify-content:flex-start;min-width:0;padding-right:.5rem;overflow:hidden}.phoenix-menu-item-left .item-name{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.phoenix-menu-item-left .item-icon{margin-right:.2rem}.phoenix-menu-item-right{display:flex;justify-content:flex-end}.phoenix-menu-item-right .item-expand.expanded{transform:scaleY(-1)}.phoenix-menu-item-right .item-settings{margin-right:.2rem}@media screen and (max-width:768px){.phoenix-menu-item{font-size:.75rem;padding:.5rem}}.phoenix-menu-item-compact{flex:1;min-width:0;color:var(--phoenix-text-color)}.phoenix-menu-item-compact .compact-option-button{width:100%;display:flex;flex-direction:column;align-items:center;gap:.15rem;padding:.3rem .2rem;color:inherit;border-radius:.3rem;transition:all .4s}.phoenix-menu-item-compact .compact-option-button:hover{background:var(--phoenix-options-icon-bg);cursor:pointer}.phoenix-menu-item-compact .compact-option-button .compact-option-label{font-size:.65rem;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}.phoenix-menu-options-bar{display:flex;align-items:stretch;gap:.25rem;padding:.25rem .4rem;background:var(--phoenix-background-color-secondary)}.phoenix-menu-options-bar app-phoenix-menu-item{display:flex;flex:1;min-width:0}.phoenix-menu-item .item-config-backdrop,.phoenix-menu-item-compact .item-config-backdrop{position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:110}.phoenix-menu-item .item-config,.phoenix-menu-item-compact .item-config{position:absolute;right:100%;width:14rem;padding:.5rem;background:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color-secondary);box-shadow:var(--phoenix-box-shadow);display:flex;flex-direction:column;z-index:120;max-height:60vh;overflow-y:auto;font-size:.8rem}.phoenix-menu-item .item-config::-webkit-scrollbar,.phoenix-menu-item-compact .item-config::-webkit-scrollbar{width:4px}.phoenix-menu-item .item-config::-webkit-scrollbar-thumb,.phoenix-menu-item-compact .item-config::-webkit-scrollbar-thumb{background:var(--phoenix-text-color-secondary, #999);border-radius:4px}.phoenix-menu-item .item-config>*,.phoenix-menu-item-compact .item-config>*{margin-bottom:.4rem}.phoenix-menu-item .item-config>*:last-child,.phoenix-menu-item-compact .item-config>*:last-child{margin-bottom:0}.phoenix-menu-item .item-config .item-config-single,.phoenix-menu-item-compact .item-config .item-config-single{display:flex;flex-direction:row}.phoenix-menu-item .item-config .item-config-single .item-config-label,.phoenix-menu-item-compact .item-config .item-config-single .item-config-label{width:30%}.phoenix-menu-item .item-config .item-config-single .item-config-data,.phoenix-menu-item-compact .item-config .item-config-single .item-config-data{flex-grow:1;padding-left:.5rem;min-width:0}.phoenix-menu-item .item-config .item-config-single.range-slider-row,.phoenix-menu-item-compact .item-config .item-config-single.range-slider-row{align-items:center}.phoenix-menu-item .item-config .item-config-single.range-slider-row .item-config-label,.phoenix-menu-item-compact .item-config .item-config-single.range-slider-row .item-config-label{width:auto;min-width:1.6rem;flex-shrink:0;margin-bottom:0}.phoenix-menu-item .icon-wrapper,.phoenix-menu-item-compact .icon-wrapper{display:inline-block;width:1.3rem;height:1.3rem;padding:.23rem;transition:all .4s}.phoenix-menu-item .icon-wrapper.icon-button:hover,.phoenix-menu-item-compact .icon-wrapper.icon-button:hover{background:var(--phoenix-options-icon-bg);border-radius:40%;cursor:pointer}.phoenix-menu-item .icon-wrapper svg,.phoenix-menu-item-compact .icon-wrapper svg{width:100%;height:100%;vertical-align:top}.phoenix-menu-item .mat-slide-toggle-bar,.phoenix-menu-item-compact .mat-slide-toggle-bar{width:30px!important}.phoenix-menu-item .mat-slide-toggle-thumb,.phoenix-menu-item-compact .mat-slide-toggle-thumb{width:15px!important;height:15px!important;position:absolute;top:2px}.phoenix-menu-item .mat-slider-horizontal,.phoenix-menu-item-compact .mat-slider-horizontal{min-width:0;width:100%;height:20px}.phoenix-menu-item .mat-slider-horizontal .mat-slider-wrapper,.phoenix-menu-item-compact .mat-slider-horizontal .mat-slider-wrapper{top:10px}.phoenix-menu-item label.mat-checkbox-layout,.phoenix-menu-item-compact label.mat-checkbox-layout{margin:0}@media screen and (max-width:768px){.phoenix-menu-item .icon-wrapper,.phoenix-menu-item-compact .icon-wrapper{width:1rem;height:1rem;padding:.15rem}}.phoenix-menu-children{margin-left:.5rem;border-left:1px solid var(--phoenix-accent);max-height:35vh;overflow-y:auto}.phoenix-menu-children::-webkit-scrollbar{width:4px}.phoenix-menu-children::-webkit-scrollbar-thumb{background:var(--phoenix-text-color-secondary, #999);border-radius:4px}.range-slider .range-slider-inputs{gap:4px}.range-slider .range-slider-side{display:flex;align-items:center;flex:1;gap:3px;min-width:0}.range-slider .range-slider-side mat-checkbox{flex-shrink:0;--mdc-checkbox-state-layer-size: 24px}.range-slider .range-slider-side input[type=number]{min-width:0;width:100%;padding:.15rem .25rem;font-size:.75rem;appearance:textfield;-moz-appearance:textfield}.range-slider .range-slider-side input[type=number]::-webkit-outer-spin-button,.range-slider .range-slider-side input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}\n"] }]
536
+ args: [{ standalone: false, selector: 'app-phoenix-menu-item', encapsulation: ViewEncapsulation.None, template: "<!-- Leaf nodes holding only configs (Draw/Cut/Color Options) are shown as a\n compact icon button; everything else as a full menu row. -->\n<div\n *ngIf=\"isCompact; else fullItem\"\n class=\"phoenix-menu-item-compact\"\n #phoenixMenuItem\n>\n <button\n class=\"btn-blank compact-option-button\"\n [title]=\"currentNode.name\"\n (click)=\"\n calculateConfigTop(); currentNode.configActive = !currentNode.configActive\n \"\n >\n <span class=\"icon-wrapper\">\n <svg>\n <use\n attr.href=\"assets/icons/{{ currentNode.icon }}.svg#{{\n currentNode.icon\n }}\"\n ></use>\n </svg>\n </span>\n <span class=\"compact-option-label\">{{ compactLabel }}</span>\n </button>\n <ng-container *ngTemplateOutlet=\"configPopup\"></ng-container>\n</div>\n\n<ng-template #fullItem>\n <div class=\"phoenix-menu-item\" #phoenixMenuItem>\n <div class=\"phoenix-menu-item-left align-items-center\">\n <!-- If the item has some toggle function -->\n <ng-container *ngIf=\"currentNode.onToggle; else nodeName\">\n <mat-slide-toggle\n [checked]=\"currentNode.toggleState\"\n (change)=\"currentNode.toggleSelfAndDescendants($event.checked)\"\n >\n <ng-container *ngTemplateOutlet=\"nodeName\"></ng-container>\n </mat-slide-toggle>\n </ng-container>\n <!-- If the item has no toggle function -->\n <ng-template #nodeName>\n <span class=\"d-flex w-100 align-items-center\">\n <span *ngIf=\"currentNode.icon\" class=\"icon-wrapper item-icon\">\n <svg>\n <use\n attr.href=\"assets/icons/{{ currentNode.icon }}.svg#{{\n currentNode.icon\n }}\"\n ></use>\n </svg>\n </span>\n <span class=\"item-name\" [title]=\"currentNode.name\">{{\n currentNode.name\n }}</span>\n </span>\n </ng-template>\n </div>\n <div class=\"phoenix-menu-item-right\">\n <div *ngIf=\"currentNode.configs.length > 0\">\n <button\n class=\"icon-wrapper icon-button btn-blank item-settings\"\n (click)=\"\n calculateConfigTop();\n currentNode.configActive = !currentNode.configActive\n \"\n >\n <svg>\n <use href=\"assets/icons/gear.svg#gear\"></use>\n </svg>\n </button>\n <ng-container *ngTemplateOutlet=\"configPopup\"></ng-container>\n </div>\n <button\n class=\"icon-wrapper icon-button btn-blank item-expand\"\n *ngIf=\"currentNode.children.length > 0\"\n (click)=\"currentNode.childrenActive = !currentNode.childrenActive\"\n [ngClass]=\"{ expanded: currentNode.childrenActive }\"\n data-testid=\"dropdown\"\n >\n <svg>\n <use href=\"assets/icons/expand.svg#expand\"></use>\n </svg>\n </button>\n </div>\n </div>\n</ng-template>\n\n<div class=\"phoenix-menu-children\" *ngIf=\"currentNode.childrenActive\">\n <div class=\"phoenix-menu-options-bar\" *ngIf=\"compactChildren.length > 0\">\n <app-phoenix-menu-item\n *ngFor=\"let childNode of compactChildren\"\n [currentNode]=\"childNode\"\n >\n </app-phoenix-menu-item>\n </div>\n <app-phoenix-menu-item\n *ngFor=\"let childNode of regularChildren\"\n [currentNode]=\"childNode\"\n >\n </app-phoenix-menu-item>\n</div>\n\n<!-- Config popup shared by the full row (gear button) and the compact icon button -->\n<ng-template #configPopup>\n <div\n class=\"item-config-backdrop\"\n *ngIf=\"currentNode.configActive\"\n (click)=\"currentNode.configActive = false\"\n ></div>\n <div\n class=\"item-config\"\n [hidden]=\"!currentNode.configActive\"\n [ngStyle]=\"{ 'top.px': configTop }\"\n >\n <ng-container\n *ngFor=\"let config of castConfigsToAny(currentNode.configs)\"\n [ngSwitch]=\"config.type\"\n >\n <!-- Config types -->\n <div\n class=\"item-config-single\"\n [ngClass]=\"{ 'range-slider-row': config.type === 'rangeSlider' }\"\n *ngIf=\"!config.hidden\"\n >\n <label\n class=\"item-config-label\"\n for=\"item-config-field\"\n *ngIf=\"config.type !== 'button'\"\n [ngClass]=\"{ 'font-weight-bold w-100': config.type === 'label' }\"\n >\n {{ config.label }}\n </label>\n <div\n class=\"item-config-data\"\n [ngClass]=\"{ 'p-0': config.type === 'button' }\"\n *ngIf=\"config.type !== 'label'\"\n >\n <mat-checkbox\n *ngSwitchCase=\"'checkbox'\"\n [checked]=\"config.isChecked\"\n (change)=\"\n config.onChange($event.checked); config.isChecked = $event.checked\n \"\n ></mat-checkbox>\n\n <app-config-slider\n *ngSwitchCase=\"'slider'\"\n [value]=\"config.value\"\n (onChange)=\"config.onChange($event); config.value = $event\"\n [min]=\"config.min\"\n [max]=\"config.max\"\n [step]=\"config.step\"\n [allowCustomValue]=\"config.allowCustomValue\"\n >\n </app-config-slider>\n\n <button\n *ngSwitchCase=\"'button'\"\n class=\"w-100\"\n (click)=\"config.onClick()\"\n mat-button\n mat-stroked-button\n >\n {{ config.label }}\n </button>\n\n <input\n *ngSwitchCase=\"'color'\"\n id=\"item-config-field\"\n type=\"color\"\n [value]=\"config.color\"\n (input)=\"\n config.onChange($event.target.value);\n config.color = $event.target.value\n \"\n />\n\n <div *ngSwitchCase=\"'rangeSlider'\" class=\"range-slider\">\n <div class=\"range-slider-inputs d-flex align-items-center\">\n <div class=\"range-slider-side\">\n <mat-checkbox\n [(ngModel)]=\"config.enableMin\"\n (change)=\"config.setEnableMin($event.checked)\"\n ></mat-checkbox>\n <input\n type=\"number\"\n placeholder=\"Min\"\n class=\"form-control form-control-sm\"\n [(ngModel)]=\"config.value\"\n (input)=\"\n config.value = $event.target.value; config.onChange(config)\n \"\n [step]=\"config.step\"\n />\n </div>\n <div class=\"range-slider-side\">\n <mat-checkbox\n [(ngModel)]=\"config.enableMax\"\n (change)=\"config.setEnableMax($event.checked)\"\n ></mat-checkbox>\n <input\n type=\"number\"\n placeholder=\"Max\"\n class=\"form-control form-control-sm\"\n [(ngModel)]=\"config.highValue\"\n (input)=\"\n config.highValue = $event.target.value;\n config.onChange(config)\n \"\n [step]=\"config.step\"\n />\n </div>\n </div>\n </div>\n\n <select\n *ngSwitchCase=\"'select'\"\n id=\"item-config-field\"\n class=\"w-100\"\n [value]=\"config.value ?? config.options[0]\"\n (change)=\"config.onChange($event.target.value)\"\n >\n <option\n *ngFor=\"let singleOption of config.options\"\n [value]=\"singleOption\"\n >\n {{ singleOption }}\n </option>\n </select>\n </div>\n </div>\n <!-- Config types END -->\n </ng-container>\n </div>\n</ng-template>\n", styles: [".phoenix-menu-item{width:100%;padding:.6rem;background:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color);font-size:.8rem;transition:all .5s;display:flex;justify-content:space-between;align-items:center}.phoenix-menu-item-left{display:flex;justify-content:flex-start;min-width:0;padding-right:.5rem;overflow:hidden}.phoenix-menu-item-left .item-name{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.phoenix-menu-item-left .item-icon{margin-right:.2rem}.phoenix-menu-item-right{display:flex;justify-content:flex-end}.phoenix-menu-item-right .item-expand.expanded{transform:scaleY(-1)}.phoenix-menu-item-right .item-settings{margin-right:.2rem}@media screen and (max-width:768px){.phoenix-menu-item{font-size:.75rem;padding:.5rem}}.phoenix-menu-item-compact{flex:1;min-width:0;color:var(--phoenix-text-color)}.phoenix-menu-item-compact .compact-option-button{width:100%;display:flex;flex-direction:column;align-items:center;gap:.15rem;padding:.3rem .2rem;color:inherit;border-radius:.3rem;transition:all .4s}.phoenix-menu-item-compact .compact-option-button:hover{background:var(--phoenix-options-icon-bg);cursor:pointer}.phoenix-menu-item-compact .compact-option-button .compact-option-label{font-size:.65rem;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}.phoenix-menu-options-bar{display:flex;align-items:stretch;gap:.25rem;padding:.25rem .4rem;background:var(--phoenix-background-color-secondary)}.phoenix-menu-options-bar app-phoenix-menu-item{display:flex;flex:1;min-width:0}.phoenix-menu-item .item-config-backdrop,.phoenix-menu-item-compact .item-config-backdrop{position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:110}.phoenix-menu-item .item-config,.phoenix-menu-item-compact .item-config{position:absolute;right:100%;width:14rem;padding:.5rem;background:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color-secondary);box-shadow:var(--phoenix-box-shadow);display:flex;flex-direction:column;z-index:120;max-height:60vh;overflow-y:auto;font-size:.8rem}.phoenix-menu-item .item-config::-webkit-scrollbar,.phoenix-menu-item-compact .item-config::-webkit-scrollbar{width:4px}.phoenix-menu-item .item-config::-webkit-scrollbar-thumb,.phoenix-menu-item-compact .item-config::-webkit-scrollbar-thumb{background:var(--phoenix-text-color-secondary, #999);border-radius:4px}.phoenix-menu-item .item-config>*,.phoenix-menu-item-compact .item-config>*{margin-bottom:.4rem}.phoenix-menu-item .item-config>*:last-child,.phoenix-menu-item-compact .item-config>*:last-child{margin-bottom:0}.phoenix-menu-item .item-config .item-config-single,.phoenix-menu-item-compact .item-config .item-config-single{display:flex;flex-direction:row}.phoenix-menu-item .item-config .item-config-single .item-config-label,.phoenix-menu-item-compact .item-config .item-config-single .item-config-label{width:30%}.phoenix-menu-item .item-config .item-config-single .item-config-data,.phoenix-menu-item-compact .item-config .item-config-single .item-config-data{flex-grow:1;padding-left:.5rem;min-width:0}.phoenix-menu-item .item-config .item-config-single.range-slider-row,.phoenix-menu-item-compact .item-config .item-config-single.range-slider-row{align-items:center}.phoenix-menu-item .item-config .item-config-single.range-slider-row .item-config-label,.phoenix-menu-item-compact .item-config .item-config-single.range-slider-row .item-config-label{width:auto;min-width:1.6rem;flex-shrink:0;margin-bottom:0}.phoenix-menu-item .icon-wrapper,.phoenix-menu-item-compact .icon-wrapper{display:inline-block;width:1.3rem;height:1.3rem;padding:.23rem;transition:all .4s}.phoenix-menu-item .icon-wrapper.icon-button:hover,.phoenix-menu-item-compact .icon-wrapper.icon-button:hover{background:var(--phoenix-options-icon-bg);border-radius:40%;cursor:pointer}.phoenix-menu-item .icon-wrapper svg,.phoenix-menu-item-compact .icon-wrapper svg{width:100%;height:100%;vertical-align:top}.phoenix-menu-item .mat-slide-toggle-bar,.phoenix-menu-item-compact .mat-slide-toggle-bar{width:30px!important}.phoenix-menu-item .mat-slide-toggle-thumb,.phoenix-menu-item-compact .mat-slide-toggle-thumb{width:15px!important;height:15px!important;position:absolute;top:2px}.phoenix-menu-item .mat-slider-horizontal,.phoenix-menu-item-compact .mat-slider-horizontal{min-width:0;width:100%;height:20px}.phoenix-menu-item .mat-slider-horizontal .mat-slider-wrapper,.phoenix-menu-item-compact .mat-slider-horizontal .mat-slider-wrapper{top:10px}.phoenix-menu-item label.mat-checkbox-layout,.phoenix-menu-item-compact label.mat-checkbox-layout{margin:0}@media screen and (max-width:768px){.phoenix-menu-item .icon-wrapper,.phoenix-menu-item-compact .icon-wrapper{width:1rem;height:1rem;padding:.15rem}}.phoenix-menu-children{margin-left:.5rem;border-left:1px solid var(--phoenix-accent)}.range-slider .range-slider-inputs{gap:4px}.range-slider .range-slider-side{display:flex;align-items:center;flex:1;gap:3px;min-width:0}.range-slider .range-slider-side mat-checkbox{flex-shrink:0;--mdc-checkbox-state-layer-size: 24px}.range-slider .range-slider-side input[type=number]{min-width:0;width:100%;padding:.15rem .25rem;font-size:.75rem;appearance:textfield;-moz-appearance:textfield}.range-slider .range-slider-side input[type=number]::-webkit-outer-spin-button,.range-slider .range-slider-side input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}\n"] }]
536
537
  }], () => [{ type: i0.ChangeDetectorRef }], { currentNode: [{
537
538
  type: Input
538
539
  }], phoenixMenuItem: [{
@@ -664,11 +665,11 @@ class PhoenixMenuComponent {
664
665
  } if (rf & 2) {
665
666
  i0.ɵɵadvance(2);
666
667
  i0.ɵɵproperty("currentNode", ctx.rootNode);
667
- } }, dependencies: [i2$2.MatIcon, PhoenixMenuItemComponent], styles: ["[_nghost-%COMP%]{width:15rem;max-width:45%;position:absolute;top:5rem;right:1rem}#phoenixMenu[_ngcontent-%COMP%]{background:var(--phoenix-background-color-secondary);max-height:75vh;overflow-y:auto;box-shadow:var(--phoenix-box-shadow);z-index:100}@media screen and (max-width:768px){#phoenixMenu[_ngcontent-%COMP%]{top:4rem}}.shortcuts-button-wrapper[_ngcontent-%COMP%]{text-align:right;pointer-events:auto}.shortcuts-btn[_ngcontent-%COMP%]{background-color:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color);border:none;padding:5px 10px;border-radius:3px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;box-shadow:var(--phoenix-box-shadow);transition:background-color .2s ease;font-family:inherit;font-size:.8rem}.shortcuts-btn[_ngcontent-%COMP%]:hover{background-color:var(--phoenix-menu-item-hover-color, #444)}"] });
668
+ } }, dependencies: [i2$2.MatIcon, PhoenixMenuItemComponent], styles: ["[_nghost-%COMP%]{width:15rem;max-width:45%;position:absolute;top:5rem;right:1rem}#phoenixMenu[_ngcontent-%COMP%]{background:var(--phoenix-background-color-secondary);max-height:calc(100vh - 8.5rem);overflow-y:auto;box-shadow:var(--phoenix-box-shadow);z-index:100}#phoenixMenu[_ngcontent-%COMP%]::-webkit-scrollbar{width:4px}#phoenixMenu[_ngcontent-%COMP%]::-webkit-scrollbar-thumb{background:var(--phoenix-text-color-secondary, #999);border-radius:4px}@media screen and (max-width:768px){#phoenixMenu[_ngcontent-%COMP%]{top:4rem}}.shortcuts-button-wrapper[_ngcontent-%COMP%]{text-align:right;pointer-events:auto}.shortcuts-btn[_ngcontent-%COMP%]{background-color:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color);border:none;padding:5px 10px;border-radius:3px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;box-shadow:var(--phoenix-box-shadow);transition:background-color .2s ease;font-family:inherit;font-size:.8rem}.shortcuts-btn[_ngcontent-%COMP%]:hover{background-color:var(--phoenix-menu-item-hover-color, #444)}"] });
668
669
  }
669
670
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PhoenixMenuComponent, [{
670
671
  type: Component,
671
- args: [{ standalone: false, selector: 'app-phoenix-menu', template: "<div class=\"phoenix-menu-drag-container\">\n <div id=\"phoenixMenu\">\n <app-phoenix-menu-item [currentNode]=\"rootNode\"></app-phoenix-menu-item>\n </div>\n</div>\n<div class=\"shortcuts-button-wrapper mt-2\">\n <button class=\"shortcuts-btn\" (click)=\"openShortcutsDialog()\">\n <mat-icon\n style=\"font-size: 16px; width: 16px; height: 16px; vertical-align: middle\"\n >keyboard</mat-icon\n >\n <span style=\"vertical-align: middle\">Shortcuts</span>\n </button>\n</div>\n", styles: [":host{width:15rem;max-width:45%;position:absolute;top:5rem;right:1rem}#phoenixMenu{background:var(--phoenix-background-color-secondary);max-height:75vh;overflow-y:auto;box-shadow:var(--phoenix-box-shadow);z-index:100}@media screen and (max-width:768px){#phoenixMenu{top:4rem}}.shortcuts-button-wrapper{text-align:right;pointer-events:auto}.shortcuts-btn{background-color:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color);border:none;padding:5px 10px;border-radius:3px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;box-shadow:var(--phoenix-box-shadow);transition:background-color .2s ease;font-family:inherit;font-size:.8rem}.shortcuts-btn:hover{background-color:var(--phoenix-menu-item-hover-color, #444)}\n"] }]
672
+ args: [{ standalone: false, selector: 'app-phoenix-menu', template: "<div class=\"phoenix-menu-drag-container\">\n <div id=\"phoenixMenu\">\n <app-phoenix-menu-item [currentNode]=\"rootNode\"></app-phoenix-menu-item>\n </div>\n</div>\n<div class=\"shortcuts-button-wrapper mt-2\">\n <button class=\"shortcuts-btn\" (click)=\"openShortcutsDialog()\">\n <mat-icon\n style=\"font-size: 16px; width: 16px; height: 16px; vertical-align: middle\"\n >keyboard</mat-icon\n >\n <span style=\"vertical-align: middle\">Shortcuts</span>\n </button>\n</div>\n", styles: [":host{width:15rem;max-width:45%;position:absolute;top:5rem;right:1rem}#phoenixMenu{background:var(--phoenix-background-color-secondary);max-height:calc(100vh - 8.5rem);overflow-y:auto;box-shadow:var(--phoenix-box-shadow);z-index:100}#phoenixMenu::-webkit-scrollbar{width:4px}#phoenixMenu::-webkit-scrollbar-thumb{background:var(--phoenix-text-color-secondary, #999);border-radius:4px}@media screen and (max-width:768px){#phoenixMenu{top:4rem}}.shortcuts-button-wrapper{text-align:right;pointer-events:auto}.shortcuts-btn{background-color:var(--phoenix-background-color-secondary);color:var(--phoenix-text-color);border:none;padding:5px 10px;border-radius:3px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;box-shadow:var(--phoenix-box-shadow);transition:background-color .2s ease;font-family:inherit;font-size:.8rem}.shortcuts-btn:hover{background-color:var(--phoenix-menu-item-hover-color, #444)}\n"] }]
672
673
  }], () => [{ type: i1$1.MatDialog }], { rootNode: [{
673
674
  type: Input
674
675
  }] }); })();
@@ -893,7 +894,7 @@ class EventDisplayService extends EventDisplay {
893
894
  }]
894
895
  }], () => [{ type: i0.NgZone }], null); })();
895
896
 
896
- const _c0$d = a0 => ({ disabled: a0 });
897
+ const _c0$e = a0 => ({ disabled: a0 });
897
898
  const _c1$6 = a0 => ({ "active-icon": a0 });
898
899
  class MenuToggleComponent {
899
900
  icon;
@@ -908,7 +909,7 @@ class MenuToggleComponent {
908
909
  i0.ɵɵelement(2, "use");
909
910
  i0.ɵɵelementEnd()();
910
911
  } if (rf & 2) {
911
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(6, _c0$d, ctx.disabled))("matTooltip", ctx.tooltip);
912
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(6, _c0$e, ctx.disabled))("matTooltip", ctx.tooltip);
912
913
  i0.ɵɵadvance();
913
914
  i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(8, _c1$6, ctx.active));
914
915
  i0.ɵɵadvance();
@@ -1185,7 +1186,7 @@ class AutoRotateComponent {
1185
1186
  }], () => [{ type: EventDisplayService }], null); })();
1186
1187
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AutoRotateComponent, { className: "AutoRotateComponent", filePath: "components/ui-menu/auto-rotate/auto-rotate.component.ts", lineNumber: 10 }); })();
1187
1188
 
1188
- const _c0$c = ["overlayWindow"];
1189
+ const _c0$d = ["overlayWindow"];
1189
1190
  const _c1$5 = ["overlayCard"];
1190
1191
  const _c2$2 = ["resizeHandleCorner"];
1191
1192
  const _c3$1 = ["*"];
@@ -1237,7 +1238,7 @@ function OverlayComponent_div_3_Template(rf, ctx) { if (rf & 1) {
1237
1238
  function OverlayComponent_span_4_Template(rf, ctx) { if (rf & 1) {
1238
1239
  const _r4 = i0.ɵɵgetCurrentView();
1239
1240
  i0.ɵɵelementStart(0, "span", 13, 1);
1240
- i0.ɵɵlistener("cdkDragMoved", function OverlayComponent_span_4_Template_span_cdkDragMoved_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onResize()); });
1241
+ i0.ɵɵlistener("pointerdown", function OverlayComponent_span_4_Template_span_pointerdown_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onResizeStart($event)); })("mousedown", function OverlayComponent_span_4_Template_span_mousedown_0_listener($event) { i0.ɵɵrestoreView(_r4); return i0.ɵɵresetView($event.stopPropagation()); })("touchstart", function OverlayComponent_span_4_Template_span_touchstart_0_listener($event) { i0.ɵɵrestoreView(_r4); return i0.ɵɵresetView($event.stopPropagation()); });
1241
1242
  i0.ɵɵelementEnd();
1242
1243
  } if (rf & 2) {
1243
1244
  const ctx_r2 = i0.ɵɵnextContext();
@@ -1250,6 +1251,7 @@ let maxZIndex = 100;
1250
1251
  class OverlayComponent {
1251
1252
  eventDisplay;
1252
1253
  elementRef;
1254
+ ngZone;
1253
1255
  /** Title of the overlay. */
1254
1256
  overlayTitle;
1255
1257
  /** If the overlay is open or not. */
@@ -1275,8 +1277,6 @@ class OverlayComponent {
1275
1277
  showBody = true;
1276
1278
  /** Aspect ratio of the overlay view. */
1277
1279
  aspectRatio = window.innerWidth / window.innerHeight;
1278
- /** Bound resize handler for cleanup. */
1279
- resizeHandler = () => this.resetHandlePosition();
1280
1280
  // ********************************************************************************
1281
1281
  // * Below code is specific to the overlay resize feature. (LOOK INTO CSS RESIZE) *
1282
1282
  // ********************************************************************************
@@ -1290,9 +1290,12 @@ class OverlayComponent {
1290
1290
  MIN_RES_WIDTH = 300;
1291
1291
  /** Minimum resizable height */
1292
1292
  MIN_RES_HEIGHT = 100;
1293
- constructor(eventDisplay, elementRef) {
1293
+ /** The resize currently being dragged, or `null` if there is none. */
1294
+ resizeSession = null;
1295
+ constructor(eventDisplay, elementRef, ngZone) {
1294
1296
  this.eventDisplay = eventDisplay;
1295
1297
  this.elementRef = elementRef;
1298
+ this.ngZone = ngZone;
1296
1299
  }
1297
1300
  ngOnInit() { }
1298
1301
  toggleBodyAndEmit() {
@@ -1310,95 +1313,166 @@ class OverlayComponent {
1310
1313
  pane.style.zIndex = maxZIndex.toString();
1311
1314
  }
1312
1315
  }
1313
- /**
1314
- * Move the resizable handle to the bottom right after the component is created.
1315
- */
1316
1316
  ngAfterViewInit() {
1317
1317
  this.bringToFront();
1318
- if (this.resizable) {
1319
- const resizeHandleElement = this.resizeHandleCorner.nativeElement;
1320
- resizeHandleElement.style.bottom = '0';
1321
- resizeHandleElement.style.right = '0';
1322
- new ResizeSensor(this.overlayCard.nativeElement, () => {
1323
- this.resetHandlePosition();
1324
- });
1325
- window.addEventListener('resize', this.resizeHandler);
1326
- }
1327
1318
  }
1328
1319
  /**
1329
- * Clean up event listeners when the component is destroyed.
1320
+ * Clean up any resize still in progress when the component is destroyed.
1330
1321
  */
1331
1322
  ngOnDestroy() {
1332
- window.removeEventListener('resize', this.resizeHandler);
1323
+ this.stopResize();
1333
1324
  }
1334
1325
  /**
1335
- * Resize the overlay card when the resize handle is dragged.
1326
+ * Start resizing the overlay card.
1327
+ *
1328
+ * The pointer is captured by the handle so that every subsequent move and,
1329
+ * crucially, the closing `pointerup` are delivered to us even if the cursor
1330
+ * leaves the handle or the window. Without the capture a fast drag can end
1331
+ * outside the handle and leave the overlay glued to the cursor.
1332
+ * @param event Pointer event that started the resize.
1336
1333
  */
1337
- onResize() {
1338
- const resizeHandleElement = this.resizeHandleCorner.nativeElement;
1339
- const overlayCardElement = this.overlayCard.nativeElement;
1340
- const dragRect = resizeHandleElement.getBoundingClientRect();
1341
- const overlayRect = overlayCardElement.getBoundingClientRect();
1342
- const width = dragRect.left - overlayRect.left + dragRect.width;
1343
- let height = dragRect.top - overlayRect.top + dragRect.height;
1344
- this.setHandleTransform(overlayRect, dragRect);
1345
- if (width > this.MIN_RES_WIDTH && height > this.MIN_RES_HEIGHT) {
1346
- if (this.keepAspectRatioFixed) {
1347
- height = width / this.aspectRatio;
1348
- }
1349
- if (this.overlayWindow?.nativeElement) {
1350
- const oldratioW = width / this.overlayWindow.nativeElement.width;
1351
- const oldratioH = height / this.overlayWindow.nativeElement.height;
1352
- this.eventDisplay
1353
- .getThreeManager()
1354
- .getOverlayRenderer()
1355
- .setSize(width, height);
1356
- this.eventDisplay
1357
- .getThreeManager()
1358
- .syncOverlayViewPort(oldratioW, oldratioH);
1359
- }
1360
- else {
1361
- // Fallback for generic content that does not have a 3D canvas
1362
- this.overlayCard.nativeElement.style.width = width + 'px';
1363
- this.overlayCard.nativeElement.style.height = height + 'px';
1364
- const contentWrapper = this.overlayCard.nativeElement.querySelector('.overlay-card-content');
1365
- if (contentWrapper) {
1366
- contentWrapper.style.flex = '1';
1367
- contentWrapper.style.overflow = 'hidden';
1368
- contentWrapper.style.maxHeight = 'none';
1369
- const contentBody = contentWrapper.firstElementChild;
1370
- if (contentBody) {
1371
- contentBody.style.width = '100%';
1372
- contentBody.style.height = '100%';
1373
- contentBody.style.maxWidth = 'none';
1374
- contentBody.style.maxHeight = 'none';
1375
- }
1376
- }
1377
- }
1334
+ onResizeStart(event) {
1335
+ if (!this.resizable || this.resizeSession) {
1336
+ return;
1378
1337
  }
1338
+ event.preventDefault();
1339
+ this.bringToFront();
1340
+ // The 3D overlay sizes its canvas, everything else sizes the whole card,
1341
+ // so measure whichever one the drag is going to act on.
1342
+ const startRect = (this.overlayWindow?.nativeElement ?? this.overlayCard.nativeElement).getBoundingClientRect();
1343
+ this.resizeSession = {
1344
+ pointerId: event.pointerId,
1345
+ startX: event.clientX,
1346
+ startY: event.clientY,
1347
+ startWidth: startRect.width,
1348
+ startHeight: startRect.height,
1349
+ };
1350
+ const handle = this.resizeHandleCorner.nativeElement;
1351
+ try {
1352
+ handle.setPointerCapture?.(event.pointerId);
1353
+ }
1354
+ catch {
1355
+ // Capturing is best effort; the listeners below still end the resize.
1356
+ }
1357
+ // Resizing only writes to the DOM and the renderer, so it does not need to
1358
+ // trigger change detection on every pointer move.
1359
+ this.ngZone.runOutsideAngular(() => {
1360
+ handle.addEventListener('pointermove', this.onResizeMove);
1361
+ handle.addEventListener('pointerup', this.onResizeEnd);
1362
+ handle.addEventListener('pointercancel', this.onResizeEnd);
1363
+ handle.addEventListener('lostpointercapture', this.onResizeEnd);
1364
+ document.addEventListener('keydown', this.onResizeKeyDown);
1365
+ });
1379
1366
  }
1367
+ /** Resize the overlay to follow the pointer. */
1368
+ onResizeMove = (event) => {
1369
+ const session = this.resizeSession;
1370
+ if (!session || event.pointerId !== session.pointerId) {
1371
+ return;
1372
+ }
1373
+ // The button can be released where the page never sees it, e.g. outside the
1374
+ // window. Noticing that the button is gone stops the overlay from staying
1375
+ // glued to the cursor.
1376
+ if (event.pointerType === 'mouse' && event.buttons === 0) {
1377
+ this.stopResize();
1378
+ return;
1379
+ }
1380
+ event.preventDefault();
1381
+ const width = Math.max(this.MIN_RES_WIDTH, session.startWidth + (event.clientX - session.startX));
1382
+ const height = Math.max(this.MIN_RES_HEIGHT, session.startHeight + (event.clientY - session.startY));
1383
+ this.applySize(width, this.keepAspectRatioFixed ? width / this.aspectRatio : height);
1384
+ };
1385
+ /** End the resize once the pointer is released, cancelled or lost. */
1386
+ onResizeEnd = (event) => {
1387
+ if (this.resizeSession &&
1388
+ event.pointerId !== this.resizeSession.pointerId) {
1389
+ return;
1390
+ }
1391
+ this.stopResize();
1392
+ };
1393
+ /** Abandon the resize and restore the original size on Escape. */
1394
+ onResizeKeyDown = (event) => {
1395
+ if (event.key !== 'Escape' || !this.resizeSession) {
1396
+ return;
1397
+ }
1398
+ const { startWidth, startHeight } = this.resizeSession;
1399
+ this.stopResize();
1400
+ if (startWidth > 0 && startHeight > 0) {
1401
+ this.applySize(startWidth, startHeight);
1402
+ }
1403
+ };
1380
1404
  /**
1381
- * Reset resize handle position.
1405
+ * Stop the resize in progress, if any, releasing the pointer and detaching
1406
+ * every listener added when it started.
1382
1407
  */
1383
- resetHandlePosition() {
1384
- const resizeHandleElement = this.resizeHandleCorner.nativeElement;
1385
- this.setHandleTransform(this.overlayCard.nativeElement.getBoundingClientRect(), resizeHandleElement.getBoundingClientRect());
1386
- resizeHandleElement.style.bottom = null;
1387
- resizeHandleElement.style.right = null;
1408
+ stopResize() {
1409
+ const session = this.resizeSession;
1410
+ if (!session) {
1411
+ return;
1412
+ }
1413
+ this.resizeSession = null;
1414
+ document.removeEventListener('keydown', this.onResizeKeyDown);
1415
+ const handle = this.resizeHandleCorner?.nativeElement;
1416
+ if (!handle) {
1417
+ return;
1418
+ }
1419
+ handle.removeEventListener('pointermove', this.onResizeMove);
1420
+ handle.removeEventListener('pointerup', this.onResizeEnd);
1421
+ handle.removeEventListener('pointercancel', this.onResizeEnd);
1422
+ handle.removeEventListener('lostpointercapture', this.onResizeEnd);
1423
+ try {
1424
+ if (handle.hasPointerCapture?.(session.pointerId)) {
1425
+ handle.releasePointerCapture(session.pointerId);
1426
+ }
1427
+ }
1428
+ catch {
1429
+ // The pointer may already be gone; nothing left to release.
1430
+ }
1388
1431
  }
1389
1432
  /**
1390
- * Set the position of the resize handle using transform3d.
1391
- * @param overlayRect Bounding client rectangle of the overlay card.
1392
- * @param dragRect Bounding client rectangle of the resize handle.
1433
+ * Resize the overlay content to the given size.
1434
+ * @param width New width of the overlay content in pixels.
1435
+ * @param height New height of the overlay content in pixels.
1393
1436
  */
1394
- setHandleTransform(overlayRect, dragRect) {
1395
- const translateX = overlayRect.width - dragRect.width;
1396
- const translateY = overlayRect.height - dragRect.height;
1397
- this.resizeHandleCorner.nativeElement.style.transform = `translate3d(${translateX}px, ${translateY}px, 0)`;
1437
+ applySize(width, height) {
1438
+ const canvas = this.overlayWindow?.nativeElement;
1439
+ if (canvas) {
1440
+ const oldratioW = width / canvas.width;
1441
+ const oldratioH = height / canvas.height;
1442
+ this.eventDisplay
1443
+ .getThreeManager()
1444
+ .getOverlayRenderer()
1445
+ .setSize(width, height);
1446
+ this.eventDisplay
1447
+ .getThreeManager()
1448
+ .syncOverlayViewPort(oldratioW, oldratioH);
1449
+ return;
1450
+ }
1451
+ // Fallback for generic content that does not have a 3D canvas
1452
+ const overlayCardElement = this.overlayCard.nativeElement;
1453
+ overlayCardElement.style.width = width + 'px';
1454
+ overlayCardElement.style.height = height + 'px';
1455
+ const contentWrapper = overlayCardElement.querySelector('.overlay-card-content');
1456
+ if (contentWrapper) {
1457
+ contentWrapper.style.flex = '1';
1458
+ contentWrapper.style.overflow = 'auto';
1459
+ contentWrapper.style.maxHeight = 'none';
1460
+ const contentBody = contentWrapper.firstElementChild;
1461
+ if (contentBody) {
1462
+ // Fill the card when there is room to spare, but keep the natural
1463
+ // height when there is not so the wrapper can scroll instead of
1464
+ // clipping the content.
1465
+ contentBody.style.width = '100%';
1466
+ contentBody.style.height = 'auto';
1467
+ contentBody.style.minHeight = '100%';
1468
+ contentBody.style.maxWidth = 'none';
1469
+ contentBody.style.maxHeight = 'none';
1470
+ }
1471
+ }
1398
1472
  }
1399
- static ɵfac = function OverlayComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || OverlayComponent)(i0.ɵɵdirectiveInject(EventDisplayService), i0.ɵɵdirectiveInject(i0.ElementRef)); };
1473
+ static ɵfac = function OverlayComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || OverlayComponent)(i0.ɵɵdirectiveInject(EventDisplayService), i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.NgZone)); };
1400
1474
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: OverlayComponent, selectors: [["app-overlay"]], contentQueries: function OverlayComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) {
1401
- i0.ɵɵcontentQuery(dirIndex, _c0$c, 5);
1475
+ i0.ɵɵcontentQuery(dirIndex, _c0$d, 5);
1402
1476
  } if (rf & 2) {
1403
1477
  let _t;
1404
1478
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.overlayWindow = _t.first);
@@ -1409,7 +1483,7 @@ class OverlayComponent {
1409
1483
  let _t;
1410
1484
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.overlayCard = _t.first);
1411
1485
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.resizeHandleCorner = _t.first);
1412
- } }, inputs: { overlayTitle: "overlayTitle", active: "active", icon: "icon", resizable: "resizable", transparentBody: "transparentBody", keepAspectRatioFixed: "keepAspectRatioFixed" }, standalone: false, ngContentSelectors: _c3$1, decls: 5, vars: 4, consts: [["overlayCard", ""], ["resizeHandleCorner", ""], ["cdkDrag", "", "cdkDragRootElement", ".cdk-overlay-pane", "cdkDragBoundary", "body", 1, "overlay-card", "card", 3, "mousedown", "touchstart", "hidden"], ["class", "card-header d-flex justify-content-between", "cdkDragHandle", "", 3, "ngClass", 4, "ngIf"], ["class", "overlay-card-content", 3, "ngClass", 4, "ngIf"], ["class", "resize-handle corner", "cdkDrag", "", 3, "hidden", "cdkDragMoved", 4, "ngIf"], ["cdkDragHandle", "", 1, "card-header", "d-flex", "justify-content-between", 3, "ngClass"], ["class", "align-self-center card-icon", 4, "ngIf"], [1, "align-self-center", "m-0", "px-2"], ["matTooltip", "Expand / Collapse", "matTooltipPosition", "right", 1, "align-self-center", "card-icon", "btn-blank", "expandCollapse", 3, "click", "ngClass"], ["href", "assets/icons/expand.svg#expand"], [1, "align-self-center", "card-icon"], [1, "overlay-card-content", 3, "ngClass"], ["cdkDrag", "", 1, "resize-handle", "corner", 3, "cdkDragMoved", "hidden"]], template: function OverlayComponent_Template(rf, ctx) { if (rf & 1) {
1486
+ } }, inputs: { overlayTitle: "overlayTitle", active: "active", icon: "icon", resizable: "resizable", transparentBody: "transparentBody", keepAspectRatioFixed: "keepAspectRatioFixed" }, standalone: false, ngContentSelectors: _c3$1, decls: 5, vars: 4, consts: [["overlayCard", ""], ["resizeHandleCorner", ""], ["cdkDrag", "", "cdkDragRootElement", ".cdk-overlay-pane", "cdkDragBoundary", "body", 1, "overlay-card", "card", 3, "mousedown", "touchstart", "hidden"], ["class", "card-header d-flex justify-content-between", "cdkDragHandle", "", 3, "ngClass", 4, "ngIf"], ["class", "overlay-card-content", 3, "ngClass", 4, "ngIf"], ["class", "resize-handle corner", 3, "hidden", "pointerdown", "mousedown", "touchstart", 4, "ngIf"], ["cdkDragHandle", "", 1, "card-header", "d-flex", "justify-content-between", 3, "ngClass"], ["class", "align-self-center card-icon", 4, "ngIf"], [1, "align-self-center", "m-0", "px-2"], ["matTooltip", "Expand / Collapse", "matTooltipPosition", "right", 1, "align-self-center", "card-icon", "btn-blank", "expandCollapse", 3, "click", "ngClass"], ["href", "assets/icons/expand.svg#expand"], [1, "align-self-center", "card-icon"], [1, "overlay-card-content", 3, "ngClass"], [1, "resize-handle", "corner", 3, "pointerdown", "mousedown", "touchstart", "hidden"]], template: function OverlayComponent_Template(rf, ctx) { if (rf & 1) {
1413
1487
  const _r1 = i0.ɵɵgetCurrentView();
1414
1488
  i0.ɵɵprojectionDef();
1415
1489
  i0.ɵɵelementStart(0, "div", 2, 0);
@@ -1424,12 +1498,12 @@ class OverlayComponent {
1424
1498
  i0.ɵɵproperty("ngIf", ctx.showBody);
1425
1499
  i0.ɵɵadvance();
1426
1500
  i0.ɵɵproperty("ngIf", ctx.resizable);
1427
- } }, dependencies: [i2.NgClass, i2.NgIf, i3$2.CdkDrag, i3$2.CdkDragHandle, i3$1.MatTooltip], styles: [".overlay-card.card{color:var(--phoenix-text-color);background:none;max-width:95vw;font-size:12px;min-width:300px;height:auto;display:flex;flex-direction:column;box-shadow:none;border:none;position:relative;z-index:200}.overlay-card.card .card-header{width:12rem;background:var(--phoenix-background-color-secondary);box-shadow:var(--phoenix-box-shadow);padding:.4rem .8rem;border-radius:10px;border:none;cursor:move;text-align:center;transition:all .2s}.overlay-card.card .card-header.with-body{border-bottom-left-radius:0;border-bottom-right-radius:0}.overlay-card.card .card-header .card-icon{display:flex;height:.7rem;width:.7rem}.overlay-card.card .card-header h5{font-size:.9rem}.overlay-card.card .card-header .expandCollapse{cursor:pointer;transition:all .2s}.overlay-card.card .card-header .expandCollapse.expand{transform:scaleY(-1)}.overlay-card.card .card-header .expandCollapse svg{width:100%;height:100%}.overlay-card.card .overlay-card-content{background:var(--phoenix-background-color-secondary);box-shadow:var(--phoenix-box-shadow);border-radius:0 10px 10px;overflow:auto;height:100%;max-height:40vh}.overlay-card.card .overlay-card-content.transparent-bg{background:none}.overlay-card.card .list-group{color:var(--phoenix-text-color-secondary);background:var(--phoenix-background-color-secondary);max-height:40vh;overflow:auto}.overlay-card.card .list-group .list-group-item{background-color:var(--phoenix-background-color-secondary);border-color:var(--phoenix-border)}.overlay-card.card .resize-handle{width:1rem;height:1rem;cursor:nwse-resize;position:absolute}.overlay-card.card .resize-handle.corner{border-right:.2rem solid var(--phoenix-text-color);border-bottom:.2rem solid var(--phoenix-text-color)}\n"], encapsulation: 2 });
1501
+ } }, dependencies: [i2.NgClass, i2.NgIf, i3$2.CdkDrag, i3$2.CdkDragHandle, i3$1.MatTooltip], styles: [".overlay-card.card{color:var(--phoenix-text-color);background:none;max-width:95vw;font-size:12px;min-width:300px;height:auto;display:flex;flex-direction:column;box-shadow:none;border:none;position:relative;z-index:200}.overlay-card.card .card-header{width:12rem;background:var(--phoenix-background-color-secondary);box-shadow:var(--phoenix-box-shadow);padding:.4rem .8rem;border-radius:10px;border:none;cursor:move;text-align:center;transition:all .2s}.overlay-card.card .card-header.with-body{border-bottom-left-radius:0;border-bottom-right-radius:0}.overlay-card.card .card-header .card-icon{display:flex;height:.7rem;width:.7rem}.overlay-card.card .card-header h5{font-size:.9rem}.overlay-card.card .card-header .expandCollapse{cursor:pointer;transition:all .2s}.overlay-card.card .card-header .expandCollapse.expand{transform:scaleY(-1)}.overlay-card.card .card-header .expandCollapse svg{width:100%;height:100%}.overlay-card.card .overlay-card-content{background:var(--phoenix-background-color-secondary);box-shadow:var(--phoenix-box-shadow);border-radius:0 10px 10px;overflow:auto;height:100%;max-height:40vh}.overlay-card.card .overlay-card-content.transparent-bg{background:none}.overlay-card.card .list-group{color:var(--phoenix-text-color-secondary);background:var(--phoenix-background-color-secondary);max-height:40vh;overflow:auto}.overlay-card.card .list-group .list-group-item{background-color:var(--phoenix-background-color-secondary);border-color:var(--phoenix-border)}.overlay-card.card .resize-handle{width:1rem;height:1rem;cursor:nwse-resize;position:absolute;bottom:0;right:0;touch-action:none}.overlay-card.card .resize-handle.corner{border-right:.2rem solid var(--phoenix-text-color);border-bottom:.2rem solid var(--phoenix-text-color)}\n"], encapsulation: 2 });
1428
1502
  }
1429
1503
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(OverlayComponent, [{
1430
1504
  type: Component,
1431
- args: [{ standalone: false, selector: 'app-overlay', encapsulation: ViewEncapsulation.None, template: "<div\n #overlayCard\n class=\"overlay-card card\"\n [hidden]=\"!active\"\n (mousedown)=\"bringToFront()\"\n (touchstart)=\"bringToFront()\"\n cdkDrag\n cdkDragRootElement=\".cdk-overlay-pane\"\n cdkDragBoundary=\"body\"\n>\n <div\n class=\"card-header d-flex justify-content-between\"\n [ngClass]=\"{ 'with-body': showBody }\"\n cdkDragHandle\n *ngIf=\"overlayTitle\"\n >\n <div class=\"align-self-center card-icon\" *ngIf=\"icon\">\n <svg>\n <use attr.href=\"assets/icons/{{ icon }}.svg#{{ icon }}\"></use>\n </svg>\n </div>\n <h5 class=\"align-self-center m-0 px-2\">{{ overlayTitle }}</h5>\n <button\n class=\"align-self-center card-icon btn-blank expandCollapse\"\n matTooltip=\"Expand / Collapse\"\n matTooltipPosition=\"right\"\n [ngClass]=\"{ expand: showBody }\"\n (click)=\"toggleBodyAndEmit()\"\n >\n <svg>\n <use href=\"assets/icons/expand.svg#expand\"></use>\n </svg>\n </button>\n </div>\n <div\n class=\"overlay-card-content\"\n *ngIf=\"showBody\"\n [ngClass]=\"{ 'transparent-bg': transparentBody }\"\n >\n <ng-content></ng-content>\n </div>\n <!-- Making the overlay resizable -->\n <span\n #resizeHandleCorner\n class=\"resize-handle corner\"\n *ngIf=\"resizable\"\n [hidden]=\"!showBody\"\n cdkDrag\n (cdkDragMoved)=\"onResize()\"\n ></span>\n</div>\n", styles: [".overlay-card.card{color:var(--phoenix-text-color);background:none;max-width:95vw;font-size:12px;min-width:300px;height:auto;display:flex;flex-direction:column;box-shadow:none;border:none;position:relative;z-index:200}.overlay-card.card .card-header{width:12rem;background:var(--phoenix-background-color-secondary);box-shadow:var(--phoenix-box-shadow);padding:.4rem .8rem;border-radius:10px;border:none;cursor:move;text-align:center;transition:all .2s}.overlay-card.card .card-header.with-body{border-bottom-left-radius:0;border-bottom-right-radius:0}.overlay-card.card .card-header .card-icon{display:flex;height:.7rem;width:.7rem}.overlay-card.card .card-header h5{font-size:.9rem}.overlay-card.card .card-header .expandCollapse{cursor:pointer;transition:all .2s}.overlay-card.card .card-header .expandCollapse.expand{transform:scaleY(-1)}.overlay-card.card .card-header .expandCollapse svg{width:100%;height:100%}.overlay-card.card .overlay-card-content{background:var(--phoenix-background-color-secondary);box-shadow:var(--phoenix-box-shadow);border-radius:0 10px 10px;overflow:auto;height:100%;max-height:40vh}.overlay-card.card .overlay-card-content.transparent-bg{background:none}.overlay-card.card .list-group{color:var(--phoenix-text-color-secondary);background:var(--phoenix-background-color-secondary);max-height:40vh;overflow:auto}.overlay-card.card .list-group .list-group-item{background-color:var(--phoenix-background-color-secondary);border-color:var(--phoenix-border)}.overlay-card.card .resize-handle{width:1rem;height:1rem;cursor:nwse-resize;position:absolute}.overlay-card.card .resize-handle.corner{border-right:.2rem solid var(--phoenix-text-color);border-bottom:.2rem solid var(--phoenix-text-color)}\n"] }]
1432
- }], () => [{ type: EventDisplayService }, { type: i0.ElementRef }], { overlayTitle: [{
1505
+ args: [{ standalone: false, selector: 'app-overlay', encapsulation: ViewEncapsulation.None, template: "<div\n #overlayCard\n class=\"overlay-card card\"\n [hidden]=\"!active\"\n (mousedown)=\"bringToFront()\"\n (touchstart)=\"bringToFront()\"\n cdkDrag\n cdkDragRootElement=\".cdk-overlay-pane\"\n cdkDragBoundary=\"body\"\n>\n <div\n class=\"card-header d-flex justify-content-between\"\n [ngClass]=\"{ 'with-body': showBody }\"\n cdkDragHandle\n *ngIf=\"overlayTitle\"\n >\n <div class=\"align-self-center card-icon\" *ngIf=\"icon\">\n <svg>\n <use attr.href=\"assets/icons/{{ icon }}.svg#{{ icon }}\"></use>\n </svg>\n </div>\n <h5 class=\"align-self-center m-0 px-2\">{{ overlayTitle }}</h5>\n <button\n class=\"align-self-center card-icon btn-blank expandCollapse\"\n matTooltip=\"Expand / Collapse\"\n matTooltipPosition=\"right\"\n [ngClass]=\"{ expand: showBody }\"\n (click)=\"toggleBodyAndEmit()\"\n >\n <svg>\n <use href=\"assets/icons/expand.svg#expand\"></use>\n </svg>\n </button>\n </div>\n <div\n class=\"overlay-card-content\"\n *ngIf=\"showBody\"\n [ngClass]=\"{ 'transparent-bg': transparentBody }\"\n >\n <ng-content></ng-content>\n </div>\n <!-- Making the overlay resizable -->\n <span\n #resizeHandleCorner\n class=\"resize-handle corner\"\n *ngIf=\"resizable\"\n [hidden]=\"!showBody\"\n (pointerdown)=\"onResizeStart($event)\"\n (mousedown)=\"$event.stopPropagation()\"\n (touchstart)=\"$event.stopPropagation()\"\n ></span>\n</div>\n", styles: [".overlay-card.card{color:var(--phoenix-text-color);background:none;max-width:95vw;font-size:12px;min-width:300px;height:auto;display:flex;flex-direction:column;box-shadow:none;border:none;position:relative;z-index:200}.overlay-card.card .card-header{width:12rem;background:var(--phoenix-background-color-secondary);box-shadow:var(--phoenix-box-shadow);padding:.4rem .8rem;border-radius:10px;border:none;cursor:move;text-align:center;transition:all .2s}.overlay-card.card .card-header.with-body{border-bottom-left-radius:0;border-bottom-right-radius:0}.overlay-card.card .card-header .card-icon{display:flex;height:.7rem;width:.7rem}.overlay-card.card .card-header h5{font-size:.9rem}.overlay-card.card .card-header .expandCollapse{cursor:pointer;transition:all .2s}.overlay-card.card .card-header .expandCollapse.expand{transform:scaleY(-1)}.overlay-card.card .card-header .expandCollapse svg{width:100%;height:100%}.overlay-card.card .overlay-card-content{background:var(--phoenix-background-color-secondary);box-shadow:var(--phoenix-box-shadow);border-radius:0 10px 10px;overflow:auto;height:100%;max-height:40vh}.overlay-card.card .overlay-card-content.transparent-bg{background:none}.overlay-card.card .list-group{color:var(--phoenix-text-color-secondary);background:var(--phoenix-background-color-secondary);max-height:40vh;overflow:auto}.overlay-card.card .list-group .list-group-item{background-color:var(--phoenix-background-color-secondary);border-color:var(--phoenix-border)}.overlay-card.card .resize-handle{width:1rem;height:1rem;cursor:nwse-resize;position:absolute;bottom:0;right:0;touch-action:none}.overlay-card.card .resize-handle.corner{border-right:.2rem solid var(--phoenix-text-color);border-bottom:.2rem solid var(--phoenix-text-color)}\n"] }]
1506
+ }], () => [{ type: EventDisplayService }, { type: i0.ElementRef }, { type: i0.NgZone }], { overlayTitle: [{
1433
1507
  type: Input
1434
1508
  }], active: [{
1435
1509
  type: Input
@@ -1451,9 +1525,9 @@ class OverlayComponent {
1451
1525
  type: ContentChild,
1452
1526
  args: ['overlayWindow']
1453
1527
  }] }); })();
1454
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(OverlayComponent, { className: "OverlayComponent", filePath: "components/ui-menu/overlay/overlay.component.ts", lineNumber: 28 }); })();
1528
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(OverlayComponent, { className: "OverlayComponent", filePath: "components/ui-menu/overlay/overlay.component.ts", lineNumber: 37 }); })();
1455
1529
 
1456
- const _c0$b = (a0, a1, a2) => ({ "active-object": a0, "is-cut": a1, "d-none": a2 });
1530
+ const _c0$c = (a0, a1, a2) => ({ "active-object": a0, "is-cut": a1, "d-none": a2 });
1457
1531
  function CollectionsInfoOverlayComponent_div_1_div_1_optgroup_10_option_1_Template(rf, ctx) { if (rf & 1) {
1458
1532
  i0.ɵɵelementStart(0, "option", 17);
1459
1533
  i0.ɵɵtext(1);
@@ -1580,7 +1654,7 @@ function CollectionsInfoOverlayComponent_div_1_table_3_tr_11_Template(rf, ctx) {
1580
1654
  const object_r10 = ctx.$implicit;
1581
1655
  const i_r12 = ctx.index;
1582
1656
  const ctx_r1 = i0.ɵɵnextContext(3);
1583
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction3(8, _c0$b, ctx_r1.activeObject && ctx_r1.activeObject.value === object_r10.uuid, object_r10.isCut, object_r10.isCut && ctx_r1.hideInvisible));
1657
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction3(8, _c0$c, ctx_r1.activeObject && ctx_r1.activeObject.value === object_r10.uuid, object_r10.isCut, object_r10.isCut && ctx_r1.hideInvisible));
1584
1658
  i0.ɵɵattribute("id", object_r10.uuid);
1585
1659
  i0.ɵɵadvance(2);
1586
1660
  i0.ɵɵtextInterpolate1("#", i_r12);
@@ -1841,7 +1915,7 @@ class CollectionsInfoComponent {
1841
1915
  }], () => [{ type: i1$3.Overlay }], null); })();
1842
1916
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(CollectionsInfoComponent, { className: "CollectionsInfoComponent", filePath: "components/ui-menu/collections-info/collections-info.component.ts", lineNumber: 17 }); })();
1843
1917
 
1844
- const _c0$a = a0 => ({ "active-object": a0 });
1918
+ const _c0$b = a0 => ({ "active-object": a0 });
1845
1919
  function GeometryBrowserOverlayComponent_div_1_div_1_option_8_Template(rf, ctx) { if (rf & 1) {
1846
1920
  i0.ɵɵelementStart(0, "option", 13);
1847
1921
  i0.ɵɵtext(1);
@@ -1896,7 +1970,7 @@ function GeometryBrowserOverlayComponent_div_1_table_3_tr_8_Template(rf, ctx) {
1896
1970
  const object_r5 = ctx.$implicit;
1897
1971
  const i_r6 = ctx.index;
1898
1972
  const ctx_r1 = i0.ɵɵnextContext(3);
1899
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(4, _c0$a, ctx_r1.activeObject && ctx_r1.activeObject.value === object_r5.uuid));
1973
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(4, _c0$b, ctx_r1.activeObject && ctx_r1.activeObject.value === object_r5.uuid));
1900
1974
  i0.ɵɵattribute("id", object_r5.uuid);
1901
1975
  i0.ɵɵadvance(2);
1902
1976
  i0.ɵɵtextInterpolate1("#", i_r6);
@@ -3074,7 +3148,7 @@ class ObjectSelectionComponent {
3074
3148
  }], () => [{ type: i1$3.Overlay }, { type: EventDisplayService }], null); })();
3075
3149
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ObjectSelectionComponent, { className: "ObjectSelectionComponent", filePath: "components/ui-menu/object-selection/object-selection.component.ts", lineNumber: 18 }); })();
3076
3150
 
3077
- const _c0$9 = ["overlayWindow"];
3151
+ const _c0$a = ["overlayWindow"];
3078
3152
  class OverlayViewWindowComponent {
3079
3153
  eventDisplay;
3080
3154
  showOverlay = true;
@@ -3126,7 +3200,7 @@ class OverlayViewWindowComponent {
3126
3200
  }
3127
3201
  static ɵfac = function OverlayViewWindowComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || OverlayViewWindowComponent)(i0.ɵɵdirectiveInject(EventDisplayService)); };
3128
3202
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: OverlayViewWindowComponent, selectors: [["app-overlay-view-window"]], viewQuery: function OverlayViewWindowComponent_Query(rf, ctx) { if (rf & 1) {
3129
- i0.ɵɵviewQuery(_c0$9, 5);
3203
+ i0.ɵɵviewQuery(_c0$a, 5);
3130
3204
  } if (rf & 2) {
3131
3205
  let _t;
3132
3206
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.overlayWindow = _t.first);
@@ -3214,7 +3288,7 @@ class OverlayViewComponent {
3214
3288
  }], () => [{ type: i1$3.Overlay }], null); })();
3215
3289
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(OverlayViewComponent, { className: "OverlayViewComponent", filePath: "components/ui-menu/overlay-view/overlay-view.component.ts", lineNumber: 17 }); })();
3216
3290
 
3217
- const _c0$8 = a0 => ({ visible: a0 });
3291
+ const _c0$9 = a0 => ({ visible: a0 });
3218
3292
  function TreeMenuItemComponent_ng_container_0_button_2_Template(rf, ctx) { if (rf & 1) {
3219
3293
  const _r2 = i0.ɵɵgetCurrentView();
3220
3294
  i0.ɵɵelementStart(0, "button", 7);
@@ -3252,7 +3326,7 @@ function TreeMenuItemComponent_ng_container_0_Template(rf, ctx) { if (rf & 1) {
3252
3326
  } if (rf & 2) {
3253
3327
  const ctx_r2 = i0.ɵɵnextContext();
3254
3328
  i0.ɵɵadvance();
3255
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(5, _c0$8, ctx_r2.visible));
3329
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(5, _c0$9, ctx_r2.visible));
3256
3330
  i0.ɵɵadvance();
3257
3331
  i0.ɵɵproperty("ngIf", ctx_r2.node.children);
3258
3332
  i0.ɵɵadvance(2);
@@ -4241,7 +4315,7 @@ class PerformanceToggleComponent {
4241
4315
  }], () => [{ type: EventDisplayService }], null); })();
4242
4316
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PerformanceToggleComponent, { className: "PerformanceToggleComponent", filePath: "components/ui-menu/performance-toggle/performance-toggle.component.ts", lineNumber: 10 }); })();
4243
4317
 
4244
- const _c0$7 = ["*"];
4318
+ const _c0$8 = ["*"];
4245
4319
  function UiMenuComponent_app_object_clipping_6_Template(rf, ctx) { if (rf & 1) {
4246
4320
  i0.ɵɵelement(0, "app-object-clipping");
4247
4321
  } }
@@ -4281,6 +4355,12 @@ function UiMenuComponent_app_make_picture_17_Template(rf, ctx) { if (rf & 1) {
4281
4355
  function UiMenuComponent_app_share_link_19_Template(rf, ctx) { if (rf & 1) {
4282
4356
  i0.ɵɵelement(0, "app-share-link");
4283
4357
  } }
4358
+ function UiMenuComponent_app_histogram_panel_20_Template(rf, ctx) { if (rf & 1) {
4359
+ i0.ɵɵelement(0, "app-histogram-panel", 6);
4360
+ } if (rf & 2) {
4361
+ const ctx_r0 = i0.ɵɵnextContext();
4362
+ i0.ɵɵproperty("config", ctx_r0.histogramConfig);
4363
+ } }
4284
4364
  const defaultUIMenuConfig = {
4285
4365
  showVRToggle: true,
4286
4366
  showARToggle: true,
@@ -4299,15 +4379,17 @@ const defaultUIMenuConfig = {
4299
4379
  showEtaPhiPanel: true,
4300
4380
  showKinematicsPanel: true,
4301
4381
  showMasterclassPanel: false,
4382
+ showHistogramPanel: false,
4302
4383
  };
4303
4384
  class UiMenuComponent {
4304
4385
  eventDataImportOptions = Object.values(EventDataFormat);
4305
4386
  animationPresets = defaultAnimationPresets;
4387
+ histogramConfig;
4306
4388
  uiConfig = { ...defaultUIMenuConfig };
4307
4389
  kinematicsConfig;
4308
4390
  masterclassConfig;
4309
4391
  static ɵfac = function UiMenuComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || UiMenuComponent)(); };
4310
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UiMenuComponent, selectors: [["app-ui-menu"]], inputs: { eventDataImportOptions: "eventDataImportOptions", animationPresets: "animationPresets", uiConfig: "uiConfig", kinematicsConfig: "kinematicsConfig", masterclassConfig: "masterclassConfig" }, standalone: false, ngContentSelectors: _c0$7, decls: 21, vars: 14, consts: [[4, "ngIf"], [3, "animationPresets", 4, "ngIf"], [3, "uiConfig", "kinematicsConfig"], [3, "config", 4, "ngIf"], [3, "eventDataImportOptions"], [3, "animationPresets"], [3, "config"]], template: function UiMenuComponent_Template(rf, ctx) { if (rf & 1) {
4392
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UiMenuComponent, selectors: [["app-ui-menu"]], inputs: { eventDataImportOptions: "eventDataImportOptions", animationPresets: "animationPresets", histogramConfig: "histogramConfig", uiConfig: "uiConfig", kinematicsConfig: "kinematicsConfig", masterclassConfig: "masterclassConfig" }, standalone: false, ngContentSelectors: _c0$8, decls: 22, vars: 15, consts: [[4, "ngIf"], [3, "animationPresets", 4, "ngIf"], [3, "uiConfig", "kinematicsConfig"], [3, "config", 4, "ngIf"], [3, "eventDataImportOptions"], [3, "animationPresets"], [3, "config"]], template: function UiMenuComponent_Template(rf, ctx) { if (rf & 1) {
4311
4393
  i0.ɵɵprojectionDef();
4312
4394
  i0.ɵɵelementStart(0, "app-ui-menu-wrapper");
4313
4395
  i0.ɵɵelement(1, "app-event-selector")(2, "app-zoom-controls")(3, "app-view-options")(4, "app-auto-rotate")(5, "app-dark-theme");
@@ -4317,8 +4399,8 @@ class UiMenuComponent {
4317
4399
  i0.ɵɵelement(12, "app-more-info", 2);
4318
4400
  i0.ɵɵtemplate(13, UiMenuComponent_app_masterclass_panel_13_Template, 1, 1, "app-masterclass-panel", 3)(14, UiMenuComponent_app_performance_toggle_14_Template, 1, 0, "app-performance-toggle", 0)(15, UiMenuComponent_app_vr_toggle_15_Template, 1, 0, "app-vr-toggle", 0)(16, UiMenuComponent_app_ar_toggle_16_Template, 1, 0, "app-ar-toggle", 0)(17, UiMenuComponent_app_make_picture_17_Template, 1, 0, "app-make-picture", 0);
4319
4401
  i0.ɵɵelement(18, "app-io-options", 4);
4320
- i0.ɵɵtemplate(19, UiMenuComponent_app_share_link_19_Template, 1, 0, "app-share-link", 0);
4321
- i0.ɵɵprojection(20);
4402
+ i0.ɵɵtemplate(19, UiMenuComponent_app_share_link_19_Template, 1, 0, "app-share-link", 0)(20, UiMenuComponent_app_histogram_panel_20_Template, 1, 1, "app-histogram-panel", 3);
4403
+ i0.ɵɵprojection(21);
4322
4404
  i0.ɵɵelementEnd();
4323
4405
  } if (rf & 2) {
4324
4406
  i0.ɵɵadvance(6);
@@ -4347,15 +4429,19 @@ class UiMenuComponent {
4347
4429
  i0.ɵɵproperty("eventDataImportOptions", ctx.eventDataImportOptions);
4348
4430
  i0.ɵɵadvance();
4349
4431
  i0.ɵɵproperty("ngIf", ctx.uiConfig.showShareLink);
4432
+ i0.ɵɵadvance();
4433
+ i0.ɵɵproperty("ngIf", ctx.uiConfig == null ? null : ctx.uiConfig.showHistogramPanel);
4350
4434
  } }, encapsulation: 2 });
4351
4435
  }
4352
4436
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UiMenuComponent, [{
4353
4437
  type: Component,
4354
- args: [{ standalone: false, selector: 'app-ui-menu', template: "<app-ui-menu-wrapper>\n <!-- Event selector -->\n <app-event-selector></app-event-selector>\n\n <!-- Zoom in and zoom out controls -->\n <app-zoom-controls></app-zoom-controls>\n\n <app-view-options></app-view-options>\n\n <app-auto-rotate></app-auto-rotate>\n\n <!-- Dark theme toggle -->\n <app-dark-theme></app-dark-theme>\n\n <!-- Toggle for clipping geometries -->\n <app-object-clipping\n *ngIf=\"uiConfig.showObjectClipping\"\n ></app-object-clipping>\n\n <!-- Main view toggle -->\n <app-main-view-toggle></app-main-view-toggle>\n\n <!-- Toggle for overlay panel -->\n <app-overlay-view *ngIf=\"uiConfig.showOverlayView\"></app-overlay-view>\n\n <!-- Toggle for selected object panel -->\n <app-object-selection\n *ngIf=\"uiConfig.showObjectSelection\"\n ></app-object-selection>\n\n <!-- Toggle for animating the event data -->\n <app-animate-event *ngIf=\"uiConfig.showAnimateEvent\"></app-animate-event>\n\n <!-- Toggle for animating camera through event -->\n <app-animate-camera\n *ngIf=\"uiConfig.showAnimateCamera\"\n [animationPresets]=\"animationPresets\"\n ></app-animate-camera>\n\n <!-- More information panels -->\n <app-more-info\n [uiConfig]=\"uiConfig\"\n [kinematicsConfig]=\"kinematicsConfig\"\n ></app-more-info>\n\n <!-- Masterclass: tag particles and calculate invariant mass -->\n <app-masterclass-panel\n *ngIf=\"uiConfig?.showMasterclassPanel\"\n [config]=\"masterclassConfig\"\n ></app-masterclass-panel>\n\n <!-- Toggle for performance -->\n <app-performance-toggle\n *ngIf=\"uiConfig.showPerformanceToggle\"\n ></app-performance-toggle>\n\n <!-- Toggle for VR -->\n <app-vr-toggle *ngIf=\"uiConfig.showVRToggle\"></app-vr-toggle>\n\n <!-- Toggle for AR -->\n <app-ar-toggle *ngIf=\"uiConfig.showARToggle\"></app-ar-toggle>\n\n <!-- Make pictures from scene -->\n <app-make-picture *ngIf=\"uiConfig.showMakePicture\"></app-make-picture>\n\n <!-- Toggle for loading geometries modal-->\n <app-io-options\n [eventDataImportOptions]=\"eventDataImportOptions\"\n ></app-io-options>\n\n <!-- Toggle for shareable link constructor modal-->\n <app-share-link *ngIf=\"uiConfig.showShareLink\"></app-share-link>\n\n <!-- Extra options -->\n <ng-content></ng-content>\n</app-ui-menu-wrapper>\n" }]
4438
+ args: [{ standalone: false, selector: 'app-ui-menu', template: "<app-ui-menu-wrapper>\n <!-- Event selector -->\n <app-event-selector></app-event-selector>\n\n <!-- Zoom in and zoom out controls -->\n <app-zoom-controls></app-zoom-controls>\n\n <app-view-options></app-view-options>\n\n <app-auto-rotate></app-auto-rotate>\n\n <!-- Dark theme toggle -->\n <app-dark-theme></app-dark-theme>\n\n <!-- Toggle for clipping geometries -->\n <app-object-clipping\n *ngIf=\"uiConfig.showObjectClipping\"\n ></app-object-clipping>\n\n <!-- Main view toggle -->\n <app-main-view-toggle></app-main-view-toggle>\n\n <!-- Toggle for overlay panel -->\n <app-overlay-view *ngIf=\"uiConfig.showOverlayView\"></app-overlay-view>\n\n <!-- Toggle for selected object panel -->\n <app-object-selection\n *ngIf=\"uiConfig.showObjectSelection\"\n ></app-object-selection>\n\n <!-- Toggle for animating the event data -->\n <app-animate-event *ngIf=\"uiConfig.showAnimateEvent\"></app-animate-event>\n\n <!-- Toggle for animating camera through event -->\n <app-animate-camera\n *ngIf=\"uiConfig.showAnimateCamera\"\n [animationPresets]=\"animationPresets\"\n ></app-animate-camera>\n\n <!-- More information panels -->\n <app-more-info\n [uiConfig]=\"uiConfig\"\n [kinematicsConfig]=\"kinematicsConfig\"\n ></app-more-info>\n\n <!-- Masterclass: tag particles and calculate invariant mass -->\n <app-masterclass-panel\n *ngIf=\"uiConfig?.showMasterclassPanel\"\n [config]=\"masterclassConfig\"\n ></app-masterclass-panel>\n\n <!-- Toggle for performance -->\n <app-performance-toggle\n *ngIf=\"uiConfig.showPerformanceToggle\"\n ></app-performance-toggle>\n\n <!-- Toggle for VR -->\n <app-vr-toggle *ngIf=\"uiConfig.showVRToggle\"></app-vr-toggle>\n\n <!-- Toggle for AR -->\n <app-ar-toggle *ngIf=\"uiConfig.showARToggle\"></app-ar-toggle>\n\n <!-- Make pictures from scene -->\n <app-make-picture *ngIf=\"uiConfig.showMakePicture\"></app-make-picture>\n\n <!-- Toggle for loading geometries modal-->\n <app-io-options\n [eventDataImportOptions]=\"eventDataImportOptions\"\n ></app-io-options>\n\n <!-- Toggle for shareable link constructor modal-->\n <app-share-link *ngIf=\"uiConfig.showShareLink\"></app-share-link>\n\n <!-- Histogram panel -->\n <app-histogram-panel\n *ngIf=\"uiConfig?.showHistogramPanel\"\n [config]=\"histogramConfig\"\n ></app-histogram-panel>\n\n <!-- Extra options -->\n <ng-content></ng-content>\n</app-ui-menu-wrapper>\n" }]
4355
4439
  }], null, { eventDataImportOptions: [{
4356
4440
  type: Input
4357
4441
  }], animationPresets: [{
4358
4442
  type: Input
4443
+ }], histogramConfig: [{
4444
+ type: Input
4359
4445
  }], uiConfig: [{
4360
4446
  type: Input
4361
4447
  }], kinematicsConfig: [{
@@ -4363,9 +4449,9 @@ class UiMenuComponent {
4363
4449
  }], masterclassConfig: [{
4364
4450
  type: Input
4365
4451
  }] }); })();
4366
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UiMenuComponent, { className: "UiMenuComponent", filePath: "components/ui-menu/ui-menu.component.ts", lineNumber: 58 }); })();
4452
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UiMenuComponent, { className: "UiMenuComponent", filePath: "components/ui-menu/ui-menu.component.ts", lineNumber: 61 }); })();
4367
4453
 
4368
- const _c0$6 = ["qrcodeCanvas"];
4454
+ const _c0$7 = ["qrcodeCanvas"];
4369
4455
  class ShareLinkDialogComponent {
4370
4456
  dialogRef;
4371
4457
  eventDisplay;
@@ -4455,7 +4541,7 @@ class ShareLinkDialogComponent {
4455
4541
  }
4456
4542
  static ɵfac = function ShareLinkDialogComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ShareLinkDialogComponent)(i0.ɵɵdirectiveInject(i1$1.MatDialogRef), i0.ɵɵdirectiveInject(EventDisplayService)); };
4457
4543
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ShareLinkDialogComponent, selectors: [["app-share-link-dialog"]], viewQuery: function ShareLinkDialogComponent_Query(rf, ctx) { if (rf & 1) {
4458
- i0.ɵɵviewQuery(_c0$6, 5);
4544
+ i0.ɵɵviewQuery(_c0$7, 5);
4459
4545
  } if (rf & 2) {
4460
4546
  let _t;
4461
4547
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.qrcodeCanvas = _t.first);
@@ -4936,7 +5022,7 @@ class EventDataExplorerComponent {
4936
5022
  }] }); })();
4937
5023
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EventDataExplorerComponent, { className: "EventDataExplorerComponent", filePath: "components/ui-menu/event-data-explorer/event-data-explorer.component.ts", lineNumber: 15 }); })();
4938
5024
 
4939
- const _c0$5 = (a0, a1) => ({ "active-icon": a0, "reload-icon": a1 });
5025
+ const _c0$6 = (a0, a1) => ({ "active-icon": a0, "reload-icon": a1 });
4940
5026
  class CycleEventsComponent {
4941
5027
  eventDisplay;
4942
5028
  fileLoader;
@@ -5027,7 +5113,7 @@ class CycleEventsComponent {
5027
5113
  i0.ɵɵelementEnd()();
5028
5114
  } if (rf & 2) {
5029
5115
  i0.ɵɵadvance();
5030
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction2(1, _c0$5, ctx.active, ctx.reloading));
5116
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction2(1, _c0$6, ctx.active, ctx.reloading));
5031
5117
  } }, dependencies: [i2.NgClass, i3$1.MatTooltip], styles: ["[_nghost-%COMP%]{display:flex;margin:0 .6rem}[_nghost-%COMP%] .cycle-events[_ngcontent-%COMP%]{display:flex;background:unset;border:none;height:2.5rem;width:2.5rem;min-height:2.5rem;min-width:2.5rem;padding:.65rem;cursor:pointer;align-self:center;transition:all .4s}[_nghost-%COMP%] .cycle-events-icon[_ngcontent-%COMP%]{width:100%;height:100%}[_nghost-%COMP%] .cycle-events-icon.active-icon[_ngcontent-%COMP%]{--phoenix-options-icon-path: #00bcd4}[_nghost-%COMP%] .cycle-events-icon.reload-icon[_ngcontent-%COMP%]{--phoenix-options-icon-path: #77dd77}[_nghost-%COMP%] .cycle-events[_ngcontent-%COMP%]:hover{background-color:var(--phoenix-options-icon-bg);border-radius:40%;transition:all .4s}[_nghost-%COMP%] .cycle-events.disabled[_ngcontent-%COMP%]{cursor:not-allowed;opacity:.4}"] });
5032
5118
  }
5033
5119
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(CycleEventsComponent, [{
@@ -5042,7 +5128,7 @@ class CycleEventsComponent {
5042
5128
  }] }); })();
5043
5129
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(CycleEventsComponent, { className: "CycleEventsComponent", filePath: "components/ui-menu/cycle-events/cycle-events.component.ts", lineNumber: 11 }); })();
5044
5130
 
5045
- const _c0$4 = a0 => ({ "sort-active": a0 });
5131
+ const _c0$5 = a0 => ({ "sort-active": a0 });
5046
5132
  const _c1$4 = (a0, a1) => ({ "selected-event": a0, "loaded-event": a1 });
5047
5133
  const _c2$1 = a0 => ({ "dim-zero": a0 });
5048
5134
  function EventBrowserOverlayComponent_div_1_div_1_span_2_Template(rf, ctx) { if (rf & 1) {
@@ -5195,9 +5281,9 @@ function EventBrowserOverlayComponent_div_1_table_5_th_5_Template(rf, ctx) { if
5195
5281
  i0.ɵɵadvance(3);
5196
5282
  i0.ɵɵtextInterpolate(col_r12);
5197
5283
  i0.ɵɵadvance(2);
5198
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(3, _c0$4, ctx_r2.isSortActive(col_r12, "desc")));
5284
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(3, _c0$5, ctx_r2.isSortActive(col_r12, "desc")));
5199
5285
  i0.ɵɵadvance(3);
5200
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(5, _c0$4, ctx_r2.isSortActive(col_r12, "asc")));
5286
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(5, _c0$5, ctx_r2.isSortActive(col_r12, "asc")));
5201
5287
  } }
5202
5288
  function EventBrowserOverlayComponent_div_1_table_5_tr_30_div_4_Template(rf, ctx) { if (rf & 1) {
5203
5289
  i0.ɵɵelementStart(0, "div", 53);
@@ -5323,13 +5409,13 @@ function EventBrowserOverlayComponent_div_1_table_5_Template(rf, ctx) { if (rf &
5323
5409
  i0.ɵɵadvance(5);
5324
5410
  i0.ɵɵproperty("ngForOf", ctx_r2.columns);
5325
5411
  i0.ɵɵadvance(6);
5326
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(6, _c0$4, ctx_r2.isSortActive("_reco", "desc")));
5412
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(6, _c0$5, ctx_r2.isSortActive("_reco", "desc")));
5327
5413
  i0.ɵɵadvance(3);
5328
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(8, _c0$4, ctx_r2.isSortActive("_reco", "asc")));
5414
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(8, _c0$5, ctx_r2.isSortActive("_reco", "asc")));
5329
5415
  i0.ɵɵadvance(8);
5330
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(10, _c0$4, ctx_r2.isSortActive("_met", "desc")));
5416
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(10, _c0$5, ctx_r2.isSortActive("_met", "desc")));
5331
5417
  i0.ɵɵadvance(3);
5332
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(12, _c0$4, ctx_r2.isSortActive("_met", "asc")));
5418
+ i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(12, _c0$5, ctx_r2.isSortActive("_met", "asc")));
5333
5419
  i0.ɵɵadvance(5);
5334
5420
  i0.ɵɵproperty("ngForOf", ctx_r2.displayedSummaries);
5335
5421
  } }
@@ -5719,7 +5805,7 @@ class EventBrowserComponent {
5719
5805
  }], () => [{ type: i1$3.Overlay }], null); })();
5720
5806
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EventBrowserComponent, { className: "EventBrowserComponent", filePath: "components/ui-menu/event-browser/event-browser.component.ts", lineNumber: 17 }); })();
5721
5807
 
5722
- const _c0$3 = ["etaPhiCanvas"];
5808
+ const _c0$4 = ["etaPhiCanvas"];
5723
5809
  const _c1$3 = ["colorBarCanvas"];
5724
5810
  class EtaPhiPanelOverlayComponent {
5725
5811
  eventDisplay;
@@ -6345,7 +6431,7 @@ class EtaPhiPanelOverlayComponent {
6345
6431
  }
6346
6432
  static ɵfac = function EtaPhiPanelOverlayComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || EtaPhiPanelOverlayComponent)(i0.ɵɵdirectiveInject(EventDisplayService)); };
6347
6433
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EtaPhiPanelOverlayComponent, selectors: [["app-eta-phi-panel-overlay"]], viewQuery: function EtaPhiPanelOverlayComponent_Query(rf, ctx) { if (rf & 1) {
6348
- i0.ɵɵviewQuery(_c0$3, 5);
6434
+ i0.ɵɵviewQuery(_c0$4, 5);
6349
6435
  i0.ɵɵviewQuery(_c1$3, 5);
6350
6436
  } if (rf & 2) {
6351
6437
  let _t;
@@ -7023,7 +7109,7 @@ class KinematicsPanelComponent {
7023
7109
  }] }); })();
7024
7110
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(KinematicsPanelComponent, { className: "KinematicsPanelComponent", filePath: "components/ui-menu/kinematics-panel/kinematics-panel.component.ts", lineNumber: 22 }); })();
7025
7111
 
7026
- const _c0$2 = ["infoPanel"];
7112
+ const _c0$3 = ["infoPanel"];
7027
7113
  const _c1$2 = ["eventBrowser"];
7028
7114
  const _c2 = ["collectionsInfo"];
7029
7115
  const _c3 = ["etaPhiPanel"];
@@ -7187,7 +7273,7 @@ class MoreInfoComponent {
7187
7273
  }
7188
7274
  static ɵfac = function MoreInfoComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MoreInfoComponent)(); };
7189
7275
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MoreInfoComponent, selectors: [["app-more-info"]], viewQuery: function MoreInfoComponent_Query(rf, ctx) { if (rf & 1) {
7190
- i0.ɵɵviewQuery(_c0$2, 5);
7276
+ i0.ɵɵviewQuery(_c0$3, 5);
7191
7277
  i0.ɵɵviewQuery(_c1$2, 5);
7192
7278
  i0.ɵɵviewQuery(_c2, 5);
7193
7279
  i0.ɵɵviewQuery(_c3, 5);
@@ -7724,12 +7810,12 @@ function pad(n) {
7724
7810
  return n < 10 ? `0${n}` : String(n);
7725
7811
  }
7726
7812
 
7727
- const _c0$1 = ["*"];
7813
+ const _c0$2 = ["*"];
7728
7814
  const _c1$1 = a0 => ({ "ui-menu-hidden": a0 });
7729
7815
  class UiMenuWrapperComponent {
7730
7816
  hideUIMenu = false;
7731
7817
  static ɵfac = function UiMenuWrapperComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || UiMenuWrapperComponent)(); };
7732
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UiMenuWrapperComponent, selectors: [["app-ui-menu-wrapper"]], standalone: false, ngContentSelectors: _c0$1, decls: 7, vars: 5, consts: [["id", "uiMenu"], ["id", "hideUIMenu", "type", "button", "aria-label", "Toggle toolbar visibility", "matTooltipPosition", "above", 3, "click", "matTooltip", "ngClass"], ["href", "assets/icons/expand.svg#expand"], ["id", "optionsPanel", 3, "hidden"]], template: function UiMenuWrapperComponent_Template(rf, ctx) { if (rf & 1) {
7818
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UiMenuWrapperComponent, selectors: [["app-ui-menu-wrapper"]], standalone: false, ngContentSelectors: _c0$2, decls: 7, vars: 5, consts: [["id", "uiMenu"], ["id", "hideUIMenu", "type", "button", "aria-label", "Toggle toolbar visibility", "matTooltipPosition", "above", 3, "click", "matTooltip", "ngClass"], ["href", "assets/icons/expand.svg#expand"], ["id", "optionsPanel", 3, "hidden"]], template: function UiMenuWrapperComponent_Template(rf, ctx) { if (rf & 1) {
7733
7819
  i0.ɵɵprojectionDef();
7734
7820
  i0.ɵɵelementStart(0, "div", 0)(1, "button", 1);
7735
7821
  i0.ɵɵlistener("click", function UiMenuWrapperComponent_Template_button_click_1_listener() { return ctx.hideUIMenu = !ctx.hideUIMenu; });
@@ -7755,6 +7841,430 @@ class UiMenuWrapperComponent {
7755
7841
  }], null, null); })();
7756
7842
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UiMenuWrapperComponent, { className: "UiMenuWrapperComponent", filePath: "components/ui-menu/ui-menu-wrapper/ui-menu-wrapper.component.ts", lineNumber: 9 }); })();
7757
7843
 
7844
+ const _c0$1 = ["histogramDiv"];
7845
+ function HistogramPanelOverlayComponent_div_1_div_1_Template(rf, ctx) { if (rf & 1) {
7846
+ i0.ɵɵelementStart(0, "div", 16)(1, "span", 17);
7847
+ i0.ɵɵtext(2);
7848
+ i0.ɵɵelementEnd()();
7849
+ } if (rf & 2) {
7850
+ const ctx_r1 = i0.ɵɵnextContext(2);
7851
+ i0.ɵɵadvance(2);
7852
+ i0.ɵɵtextInterpolate(ctx_r1.config.emptyText || "Waiting for data...");
7853
+ } }
7854
+ function HistogramPanelOverlayComponent_div_1_div_5_Template(rf, ctx) { if (rf & 1) {
7855
+ i0.ɵɵelementStart(0, "div", 18)(1, "span", 19);
7856
+ i0.ɵɵtext(2);
7857
+ i0.ɵɵelementEnd()();
7858
+ } if (rf & 2) {
7859
+ const hint_r3 = ctx.$implicit;
7860
+ const ctx_r1 = i0.ɵɵnextContext(2);
7861
+ i0.ɵɵstyleProp("left", ctx_r1.getHintPosition(hint_r3.mass), "%")("border-color", hint_r3.color || "#ffffff55");
7862
+ i0.ɵɵadvance();
7863
+ i0.ɵɵstyleProp("color", hint_r3.color || "#ffffff88");
7864
+ i0.ɵɵadvance();
7865
+ i0.ɵɵtextInterpolate(ctx_r1.getHintLabel(hint_r3));
7866
+ } }
7867
+ function HistogramPanelOverlayComponent_div_1_Template(rf, ctx) { if (rf & 1) {
7868
+ const _r1 = i0.ɵɵgetCurrentView();
7869
+ i0.ɵɵelementStart(0, "div", 3);
7870
+ i0.ɵɵtemplate(1, HistogramPanelOverlayComponent_div_1_div_1_Template, 3, 1, "div", 4);
7871
+ i0.ɵɵelementStart(2, "div", 5);
7872
+ i0.ɵɵelement(3, "div", 6, 0);
7873
+ i0.ɵɵtemplate(5, HistogramPanelOverlayComponent_div_1_div_5_Template, 3, 7, "div", 7);
7874
+ i0.ɵɵelementEnd();
7875
+ i0.ɵɵelementStart(6, "div", 8)(7, "div", 9)(8, "span", 10);
7876
+ i0.ɵɵtext(9, "Entries:");
7877
+ i0.ɵɵelementEnd();
7878
+ i0.ɵɵelementStart(10, "span", 11);
7879
+ i0.ɵɵtext(11);
7880
+ i0.ɵɵelementEnd();
7881
+ i0.ɵɵelementStart(12, "span", 12);
7882
+ i0.ɵɵtext(13, "|");
7883
+ i0.ɵɵelementEnd();
7884
+ i0.ɵɵelementStart(14, "span", 10);
7885
+ i0.ɵɵtext(15, "Mean:");
7886
+ i0.ɵɵelementEnd();
7887
+ i0.ɵɵelementStart(16, "span", 11);
7888
+ i0.ɵɵtext(17);
7889
+ i0.ɵɵpipe(18, "number");
7890
+ i0.ɵɵelementEnd()();
7891
+ i0.ɵɵelementStart(19, "div", 13)(20, "button", 14);
7892
+ i0.ɵɵlistener("click", function HistogramPanelOverlayComponent_div_1_Template_button_click_20_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.resetHistogram()); });
7893
+ i0.ɵɵtext(21, " Reset ");
7894
+ i0.ɵɵelementEnd();
7895
+ i0.ɵɵelementStart(22, "button", 15);
7896
+ i0.ɵɵlistener("click", function HistogramPanelOverlayComponent_div_1_Template_button_click_22_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.exportCSV()); });
7897
+ i0.ɵɵtext(23, " Export ");
7898
+ i0.ɵɵelementEnd()()()();
7899
+ } if (rf & 2) {
7900
+ const ctx_r1 = i0.ɵɵnextContext();
7901
+ i0.ɵɵadvance();
7902
+ i0.ɵɵproperty("ngIf", ctx_r1.entries === 0);
7903
+ i0.ɵɵadvance(4);
7904
+ i0.ɵɵproperty("ngForOf", ctx_r1.config.hints);
7905
+ i0.ɵɵadvance(6);
7906
+ i0.ɵɵtextInterpolate(ctx_r1.entries);
7907
+ i0.ɵɵadvance(6);
7908
+ i0.ɵɵtextInterpolate(ctx_r1.entries > 0 ? i0.ɵɵpipeBind2(18, 4, ctx_r1.mean, "1.1-1") + " GeV" : "\u2014");
7909
+ } }
7910
+ /** localStorage key prefix for histogram persistence. */
7911
+ const STORAGE_PREFIX = 'phoenix-histogram-';
7912
+ class HistogramPanelOverlayComponent {
7913
+ eventDisplay;
7914
+ /** Whether the histogram panel is visible. Uses setter to trigger draw on show. */
7915
+ _showHistogram = false;
7916
+ set showHistogram(val) {
7917
+ const wasHidden = !this._showHistogram;
7918
+ this._showHistogram = val;
7919
+ if (val && wasHidden) {
7920
+ setTimeout(() => this.drawHistogram(), 0);
7921
+ }
7922
+ else if (!val) {
7923
+ this.drawn = false;
7924
+ }
7925
+ }
7926
+ get showHistogram() {
7927
+ return this._showHistogram;
7928
+ }
7929
+ config = ATLAS_MASS_HISTOGRAM;
7930
+ histogramDiv;
7931
+ entries = 0;
7932
+ mean = 0;
7933
+ sumValues = 0;
7934
+ rawValues = [];
7935
+ histogram;
7936
+ drawn = false;
7937
+ unsubscribe;
7938
+ redrawTimer = null;
7939
+ constructor(eventDisplay) {
7940
+ this.eventDisplay = eventDisplay;
7941
+ }
7942
+ ngOnInit() {
7943
+ const c = this.config;
7944
+ this.histogram = createHistogram('TH1F', c.nbins);
7945
+ this.histogram.fName = 'hmass';
7946
+ this.histogram.fTitle = '';
7947
+ this.histogram.fXaxis.fXmin = c.xmin;
7948
+ this.histogram.fXaxis.fXmax = c.xmax;
7949
+ this.histogram.fXaxis.fTitle = c.xlabel;
7950
+ this.histogram.fYaxis.fTitle = c.ylabel;
7951
+ this.histogram.fLineColor = c.lineColor;
7952
+ this.histogram.fLineWidth = 0;
7953
+ this.histogram.fFillColor = c.fillColor;
7954
+ this.histogram.fFillStyle = c.fillStyle;
7955
+ // Disable stat box — BIT(9) = 512
7956
+ this.histogram.fBits = this.histogram.fBits | 512;
7957
+ // Restore any saved state from a previous session
7958
+ this.restoreFromStorage();
7959
+ const eventName = c.eventName ?? 'result-recorded';
7960
+ this.unsubscribe = this.eventDisplay.on(eventName, (data) => {
7961
+ if (data?.mass != null) {
7962
+ this.addValue(data.mass);
7963
+ }
7964
+ });
7965
+ }
7966
+ ngOnDestroy() {
7967
+ this.unsubscribe?.();
7968
+ if (this.redrawTimer) {
7969
+ clearTimeout(this.redrawTimer);
7970
+ }
7971
+ }
7972
+ /** Save gStyle, apply dark theme values, await callback, then restore gStyle. */
7973
+ async withDarkGStyle(fn) {
7974
+ const saved = {
7975
+ fCanvasColor: gStyle.fCanvasColor,
7976
+ fPadColor: gStyle.fPadColor,
7977
+ fFrameFillColor: gStyle.fFrameFillColor,
7978
+ fFrameLineColor: gStyle.fFrameLineColor,
7979
+ };
7980
+ // Dark theme: 1=black bg, 0=white text/lines
7981
+ gStyle.fCanvasColor = 1;
7982
+ gStyle.fPadColor = 1;
7983
+ gStyle.fFrameFillColor = 1;
7984
+ gStyle.fFrameLineColor = 0;
7985
+ try {
7986
+ return await fn();
7987
+ }
7988
+ finally {
7989
+ gStyle.fCanvasColor = saved.fCanvasColor;
7990
+ gStyle.fPadColor = saved.fPadColor;
7991
+ gStyle.fFrameFillColor = saved.fFrameFillColor;
7992
+ gStyle.fFrameLineColor = saved.fFrameLineColor;
7993
+ }
7994
+ }
7995
+ async drawHistogram() {
7996
+ if (this.histogramDiv?.nativeElement && this.histogram) {
7997
+ // Set axis colors for dark theme directly on histogram object (not gStyle)
7998
+ this.histogram.fXaxis.fAxisColor = 0;
7999
+ this.histogram.fXaxis.fLabelColor = 0;
8000
+ this.histogram.fXaxis.fTitleColor = 0;
8001
+ this.histogram.fYaxis.fAxisColor = 0;
8002
+ this.histogram.fYaxis.fLabelColor = 0;
8003
+ this.histogram.fYaxis.fTitleColor = 0;
8004
+ await this.withDarkGStyle(() => draw(this.histogramDiv.nativeElement, this.histogram, 'hist,no_stat'));
8005
+ this.applyDarkTheme();
8006
+ this.drawn = true;
8007
+ }
8008
+ }
8009
+ /** Apply dark theme to jsroot SVG via direct DOM manipulation. */
8010
+ applyDarkTheme() {
8011
+ const el = this.histogramDiv?.nativeElement;
8012
+ if (!el)
8013
+ return;
8014
+ const svg = el.querySelector('svg');
8015
+ if (svg) {
8016
+ svg.style.background = '#1a1a2e';
8017
+ }
8018
+ el.querySelectorAll('rect').forEach((rect) => {
8019
+ const fill = rect.getAttribute('fill');
8020
+ if (fill === 'white' ||
8021
+ fill === 'rgb(255, 255, 255)' ||
8022
+ fill === '#ffffff' ||
8023
+ fill === 'rgb(255,255,255)') {
8024
+ rect.setAttribute('fill', '#1a1a2e');
8025
+ rect.setAttribute('stroke', 'rgba(255,255,255,0.15)');
8026
+ }
8027
+ });
8028
+ el.querySelectorAll('line').forEach((line) => {
8029
+ const stroke = line.getAttribute('stroke');
8030
+ if (stroke === 'black' ||
8031
+ stroke === 'rgb(0, 0, 0)' ||
8032
+ stroke === '#000000' ||
8033
+ stroke === 'rgb(0,0,0)') {
8034
+ line.setAttribute('stroke', '#aaaaaa');
8035
+ }
8036
+ });
8037
+ el.querySelectorAll('path').forEach((path) => {
8038
+ const stroke = path.getAttribute('stroke');
8039
+ if (stroke === 'black' ||
8040
+ stroke === 'rgb(0, 0, 0)' ||
8041
+ stroke === '#000000' ||
8042
+ stroke === 'rgb(0,0,0)') {
8043
+ path.setAttribute('stroke', '#aaaaaa');
8044
+ }
8045
+ });
8046
+ el.querySelectorAll('text').forEach((text) => {
8047
+ const fill = text.getAttribute('fill');
8048
+ if (fill === 'black' ||
8049
+ fill === 'rgb(0, 0, 0)' ||
8050
+ fill === '#000000' ||
8051
+ fill === 'rgb(0,0,0)' ||
8052
+ !fill) {
8053
+ text.setAttribute('fill', '#cccccc');
8054
+ }
8055
+ });
8056
+ }
8057
+ /** Add a value and schedule a debounced redraw. */
8058
+ addValue(value) {
8059
+ if (!this.histogram)
8060
+ return;
8061
+ this.histogram.Fill(value);
8062
+ this.rawValues.push(value);
8063
+ this.entries++;
8064
+ this.sumValues += value;
8065
+ this.mean = this.sumValues / this.entries;
8066
+ this.saveToStorage();
8067
+ if (this.drawn && this.histogramDiv?.nativeElement) {
8068
+ this.scheduleRedraw();
8069
+ }
8070
+ }
8071
+ /** Debounced redraw — coalesces rapid events (e.g. bulk load) into one repaint. */
8072
+ scheduleRedraw() {
8073
+ if (this.redrawTimer)
8074
+ return;
8075
+ this.redrawTimer = setTimeout(async () => {
8076
+ this.redrawTimer = null;
8077
+ if (this.drawn && this.histogramDiv?.nativeElement) {
8078
+ await this.withDarkGStyle(() => redraw(this.histogramDiv.nativeElement, this.histogram, 'hist,no_stat'));
8079
+ this.applyDarkTheme();
8080
+ }
8081
+ }, 50);
8082
+ }
8083
+ /** Reset histogram after user confirmation. */
8084
+ resetHistogram() {
8085
+ if (this.entries === 0)
8086
+ return;
8087
+ if (!confirm(`Clear all ${this.entries} entries? This cannot be undone.`)) {
8088
+ return;
8089
+ }
8090
+ this.clearHistogram();
8091
+ }
8092
+ /** Clear all histogram data and redraw. */
8093
+ async clearHistogram() {
8094
+ if (!this.histogram)
8095
+ return;
8096
+ for (let i = 0; i < this.histogram.fArray.length; i++) {
8097
+ this.histogram.fArray[i] = 0;
8098
+ }
8099
+ this.histogram.fEntries = 0;
8100
+ this.entries = 0;
8101
+ this.mean = 0;
8102
+ this.sumValues = 0;
8103
+ this.rawValues = [];
8104
+ this.clearStorage();
8105
+ if (this.drawn && this.histogramDiv?.nativeElement) {
8106
+ await this.withDarkGStyle(() => redraw(this.histogramDiv.nativeElement, this.histogram, 'hist,no_stat'));
8107
+ this.applyDarkTheme();
8108
+ }
8109
+ }
8110
+ /** Get hint line position as percentage of the wrapper, accounting for jsroot pad margins. */
8111
+ getHintPosition(mass) {
8112
+ const c = this.config;
8113
+ const padLeft = 0.1; // jsroot default pad left margin
8114
+ const padRight = 0.1; // jsroot default pad right margin
8115
+ const fraction = (mass - c.xmin) / (c.xmax - c.xmin);
8116
+ return (padLeft + fraction * (1 - padLeft - padRight)) * 100;
8117
+ }
8118
+ /** Format hint label with mass value and unit for readability. */
8119
+ getHintLabel(hint) {
8120
+ const massStr = hint.mass >= 10
8121
+ ? hint.mass.toFixed(1)
8122
+ : hint.mass.toFixed(hint.mass < 1 ? 2 : 1);
8123
+ return `${hint.label} (${massStr} GeV)`;
8124
+ }
8125
+ /** Export histogram data as TSV. */
8126
+ exportCSV() {
8127
+ if (!this.histogram)
8128
+ return;
8129
+ const c = this.config;
8130
+ const binWidth = (c.xmax - c.xmin) / c.nbins;
8131
+ let tsv = 'bin_low\tbin_high\tentries\n';
8132
+ for (let i = 1; i <= c.nbins; i++) {
8133
+ const low = c.xmin + (i - 1) * binWidth;
8134
+ const high = low + binWidth;
8135
+ const content = this.histogram.fArray[i] || 0;
8136
+ tsv += `${low.toFixed(2)}\t${high.toFixed(2)}\t${content}\n`;
8137
+ }
8138
+ const filename = c.title.toLowerCase().replace(/\s+/g, '_') + '_data.tsv';
8139
+ const blob = new Blob([tsv], { type: 'text/tab-separated-values' });
8140
+ const url = URL.createObjectURL(blob);
8141
+ const a = document.createElement('a');
8142
+ a.href = url;
8143
+ a.download = filename;
8144
+ a.click();
8145
+ URL.revokeObjectURL(url);
8146
+ }
8147
+ // --- localStorage persistence ---
8148
+ /** Storage key derived from config title to avoid collisions between configs. */
8149
+ get storageKey() {
8150
+ return (STORAGE_PREFIX + this.config.title.toLowerCase().replace(/\s+/g, '-'));
8151
+ }
8152
+ /** Save raw values to localStorage for crash recovery. */
8153
+ saveToStorage() {
8154
+ try {
8155
+ localStorage.setItem(this.storageKey, JSON.stringify(this.rawValues));
8156
+ }
8157
+ catch {
8158
+ // localStorage full or unavailable — silently ignore
8159
+ }
8160
+ }
8161
+ /** Restore histogram state from localStorage. */
8162
+ restoreFromStorage() {
8163
+ try {
8164
+ const saved = localStorage.getItem(this.storageKey);
8165
+ if (!saved)
8166
+ return;
8167
+ const values = JSON.parse(saved);
8168
+ if (!Array.isArray(values) || values.length === 0)
8169
+ return;
8170
+ for (const v of values) {
8171
+ this.histogram.Fill(v);
8172
+ this.rawValues.push(v);
8173
+ this.entries++;
8174
+ this.sumValues += v;
8175
+ }
8176
+ this.mean = this.sumValues / this.entries;
8177
+ }
8178
+ catch {
8179
+ // Corrupted data — start fresh
8180
+ }
8181
+ }
8182
+ /** Clear saved state from localStorage. */
8183
+ clearStorage() {
8184
+ try {
8185
+ localStorage.removeItem(this.storageKey);
8186
+ }
8187
+ catch {
8188
+ // Ignore
8189
+ }
8190
+ }
8191
+ static ɵfac = function HistogramPanelOverlayComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || HistogramPanelOverlayComponent)(i0.ɵɵdirectiveInject(EventDisplayService)); };
8192
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: HistogramPanelOverlayComponent, selectors: [["app-histogram-panel-overlay"]], viewQuery: function HistogramPanelOverlayComponent_Query(rf, ctx) { if (rf & 1) {
8193
+ i0.ɵɵviewQuery(_c0$1, 5);
8194
+ } if (rf & 2) {
8195
+ let _t;
8196
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.histogramDiv = _t.first);
8197
+ } }, inputs: { showHistogram: "showHistogram", config: "config" }, standalone: false, decls: 2, vars: 4, consts: [["histogramDiv", ""], ["icon", "histogram", 3, "overlayTitle", "active", "resizable"], ["class", "histogram-container", 4, "ngIf"], [1, "histogram-container"], ["class", "histogram-empty", 4, "ngIf"], [1, "histogram-wrapper"], [1, "histogram-canvas"], ["class", "hint-line", 3, "left", "borderColor", 4, "ngFor", "ngForOf"], [1, "histogram-footer"], [1, "histogram-stats"], [1, "stat-label"], [1, "stat-value"], [1, "stat-sep"], [1, "histogram-actions"], ["matTooltip", "Clear all entries", 1, "btn-action", 3, "click"], ["matTooltip", "Download bin data as TSV", 1, "btn-action", 3, "click"], [1, "histogram-empty"], [1, "empty-hint"], [1, "hint-line"], [1, "hint-label"]], template: function HistogramPanelOverlayComponent_Template(rf, ctx) { if (rf & 1) {
8198
+ i0.ɵɵelementStart(0, "app-overlay", 1);
8199
+ i0.ɵɵtemplate(1, HistogramPanelOverlayComponent_div_1_Template, 24, 7, "div", 2);
8200
+ i0.ɵɵelementEnd();
8201
+ } if (rf & 2) {
8202
+ i0.ɵɵproperty("overlayTitle", ctx.config.title)("active", ctx.showHistogram)("resizable", false);
8203
+ i0.ɵɵadvance();
8204
+ i0.ɵɵproperty("ngIf", ctx.showHistogram);
8205
+ } }, dependencies: [i2.NgForOf, i2.NgIf, i3$1.MatTooltip, OverlayComponent, i2.DecimalPipe], styles: [".histogram-container[_ngcontent-%COMP%]{padding:.5rem;width:450px;display:flex;flex-direction:column;gap:.4rem}.histogram-wrapper[_ngcontent-%COMP%]{position:relative;min-height:280px;border-radius:6px;overflow:hidden;background:#1a1a2e}.histogram-canvas[_ngcontent-%COMP%]{width:100%;height:280px;border-radius:6px;overflow:hidden}.histogram-canvas[_ngcontent-%COMP%] svg{background:#1a1a2e!important}.hint-line[_ngcontent-%COMP%]{position:absolute;top:10%;bottom:10%;border-left:1.5px dashed;opacity:.8;pointer-events:none;z-index:10}.hint-label[_ngcontent-%COMP%]{position:absolute;top:-2px;left:4px;font-size:.75rem;font-weight:600;letter-spacing:.02em;white-space:nowrap;opacity:.95;text-shadow:0 1px 3px rgba(0,0,0,.6)}.histogram-empty[_ngcontent-%COMP%]{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:5;pointer-events:none}.histogram-empty[_ngcontent-%COMP%] .empty-hint[_ngcontent-%COMP%]{font-size:.75rem;color:var(--phoenix-text-color-secondary);opacity:.5;text-align:center;max-width:250px;line-height:1.4}.histogram-footer[_ngcontent-%COMP%]{display:flex;justify-content:space-between;align-items:center;padding:.3rem .2rem 0}.histogram-stats[_ngcontent-%COMP%]{display:flex;align-items:baseline;gap:.35rem}.histogram-stats[_ngcontent-%COMP%] .stat-label[_ngcontent-%COMP%]{font-size:.7rem;color:var(--phoenix-text-color-secondary);opacity:.6}.histogram-stats[_ngcontent-%COMP%] .stat-value[_ngcontent-%COMP%]{font-size:.8rem;color:var(--phoenix-text-color);font-weight:500}.histogram-stats[_ngcontent-%COMP%] .stat-sep[_ngcontent-%COMP%]{font-size:.7rem;color:var(--phoenix-text-color-secondary);opacity:.3;margin:0 .1rem}.histogram-actions[_ngcontent-%COMP%]{display:flex;gap:.3rem}.btn-action[_ngcontent-%COMP%]{font-size:.7rem;padding:.2rem .5rem;border:1px solid rgba(255,255,255,.08);border-radius:5px;background:#ffffff0a;color:var(--phoenix-text-color-secondary);cursor:pointer;transition:all .15s}.btn-action[_ngcontent-%COMP%]:hover{background:#ffffff1a;border-color:#ffffff26}"] });
8206
+ }
8207
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(HistogramPanelOverlayComponent, [{
8208
+ type: Component,
8209
+ args: [{ standalone: false, selector: 'app-histogram-panel-overlay', template: "<app-overlay\n [overlayTitle]=\"config.title\"\n icon=\"histogram\"\n [active]=\"showHistogram\"\n [resizable]=\"false\"\n>\n <div class=\"histogram-container\" *ngIf=\"showHistogram\">\n <!-- Empty state guidance -->\n <div class=\"histogram-empty\" *ngIf=\"entries === 0\">\n <span class=\"empty-hint\">{{\n config.emptyText || 'Waiting for data...'\n }}</span>\n </div>\n\n <!-- Histogram canvas with hint overlays -->\n <div class=\"histogram-wrapper\">\n <div #histogramDiv class=\"histogram-canvas\"></div>\n\n <!-- Reference lines for known resonances -->\n <div\n class=\"hint-line\"\n *ngFor=\"let hint of config.hints\"\n [style.left.%]=\"getHintPosition(hint.mass)\"\n [style.borderColor]=\"hint.color || '#ffffff55'\"\n >\n <span class=\"hint-label\" [style.color]=\"hint.color || '#ffffff88'\">{{\n getHintLabel(hint)\n }}</span>\n </div>\n </div>\n\n <!-- Footer: stats + actions -->\n <div class=\"histogram-footer\">\n <div class=\"histogram-stats\">\n <span class=\"stat-label\">Entries:</span>\n <span class=\"stat-value\">{{ entries }}</span>\n <span class=\"stat-sep\">|</span>\n <span class=\"stat-label\">Mean:</span>\n <span class=\"stat-value\">{{\n entries > 0 ? (mean | number: '1.1-1') + ' GeV' : '\u2014'\n }}</span>\n </div>\n <div class=\"histogram-actions\">\n <button\n class=\"btn-action\"\n (click)=\"resetHistogram()\"\n matTooltip=\"Clear all entries\"\n >\n Reset\n </button>\n <button\n class=\"btn-action\"\n (click)=\"exportCSV()\"\n matTooltip=\"Download bin data as TSV\"\n >\n Export\n </button>\n </div>\n </div>\n </div>\n</app-overlay>\n", styles: [".histogram-container{padding:.5rem;width:450px;display:flex;flex-direction:column;gap:.4rem}.histogram-wrapper{position:relative;min-height:280px;border-radius:6px;overflow:hidden;background:#1a1a2e}.histogram-canvas{width:100%;height:280px;border-radius:6px;overflow:hidden}.histogram-canvas ::ng-deep svg{background:#1a1a2e!important}.hint-line{position:absolute;top:10%;bottom:10%;border-left:1.5px dashed;opacity:.8;pointer-events:none;z-index:10}.hint-label{position:absolute;top:-2px;left:4px;font-size:.75rem;font-weight:600;letter-spacing:.02em;white-space:nowrap;opacity:.95;text-shadow:0 1px 3px rgba(0,0,0,.6)}.histogram-empty{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:5;pointer-events:none}.histogram-empty .empty-hint{font-size:.75rem;color:var(--phoenix-text-color-secondary);opacity:.5;text-align:center;max-width:250px;line-height:1.4}.histogram-footer{display:flex;justify-content:space-between;align-items:center;padding:.3rem .2rem 0}.histogram-stats{display:flex;align-items:baseline;gap:.35rem}.histogram-stats .stat-label{font-size:.7rem;color:var(--phoenix-text-color-secondary);opacity:.6}.histogram-stats .stat-value{font-size:.8rem;color:var(--phoenix-text-color);font-weight:500}.histogram-stats .stat-sep{font-size:.7rem;color:var(--phoenix-text-color-secondary);opacity:.3;margin:0 .1rem}.histogram-actions{display:flex;gap:.3rem}.btn-action{font-size:.7rem;padding:.2rem .5rem;border:1px solid rgba(255,255,255,.08);border-radius:5px;background:#ffffff0a;color:var(--phoenix-text-color-secondary);cursor:pointer;transition:all .15s}.btn-action:hover{background:#ffffff1a;border-color:#ffffff26}\n"] }]
8210
+ }], () => [{ type: EventDisplayService }], { showHistogram: [{
8211
+ type: Input
8212
+ }], config: [{
8213
+ type: Input
8214
+ }], histogramDiv: [{
8215
+ type: ViewChild,
8216
+ args: ['histogramDiv']
8217
+ }] }); })();
8218
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(HistogramPanelOverlayComponent, { className: "HistogramPanelOverlayComponent", filePath: "components/ui-menu/histogram-panel/histogram-panel-overlay/histogram-panel-overlay.component.ts", lineNumber: 26 }); })();
8219
+
8220
+ class HistogramPanelComponent {
8221
+ overlay;
8222
+ showHistogram = false;
8223
+ overlayWindow;
8224
+ config;
8225
+ constructor(overlay) {
8226
+ this.overlay = overlay;
8227
+ }
8228
+ ngOnInit() {
8229
+ const overlayRef = this.overlay.create();
8230
+ const overlayPortal = new ComponentPortal(HistogramPanelOverlayComponent);
8231
+ this.overlayWindow = overlayRef.attach(overlayPortal);
8232
+ this.overlayWindow.instance.showHistogram = this.showHistogram;
8233
+ if (this.config) {
8234
+ this.overlayWindow.instance.config = this.config;
8235
+ }
8236
+ }
8237
+ ngOnChanges(changes) {
8238
+ if (changes['config'] && this.overlayWindow) {
8239
+ if (this.config) {
8240
+ this.overlayWindow.instance.config = this.config;
8241
+ }
8242
+ }
8243
+ }
8244
+ ngOnDestroy() {
8245
+ this.overlayWindow.destroy();
8246
+ }
8247
+ toggleOverlay() {
8248
+ this.showHistogram = !this.showHistogram;
8249
+ this.overlayWindow.instance.showHistogram = this.showHistogram;
8250
+ }
8251
+ static ɵfac = function HistogramPanelComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || HistogramPanelComponent)(i0.ɵɵdirectiveInject(i1$3.Overlay)); };
8252
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: HistogramPanelComponent, selectors: [["app-histogram-panel"]], inputs: { config: "config" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 1, vars: 1, consts: [["tooltip", "Histogram panel", "icon", "histogram", 3, "click", "active"]], template: function HistogramPanelComponent_Template(rf, ctx) { if (rf & 1) {
8253
+ i0.ɵɵelementStart(0, "app-menu-toggle", 0);
8254
+ i0.ɵɵlistener("click", function HistogramPanelComponent_Template_app_menu_toggle_click_0_listener() { return ctx.toggleOverlay(); });
8255
+ i0.ɵɵelementEnd();
8256
+ } if (rf & 2) {
8257
+ i0.ɵɵproperty("active", ctx.showHistogram);
8258
+ } }, dependencies: [MenuToggleComponent], encapsulation: 2 });
8259
+ }
8260
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(HistogramPanelComponent, [{
8261
+ type: Component,
8262
+ args: [{ standalone: false, selector: 'app-histogram-panel', template: "<app-menu-toggle\n tooltip=\"Histogram panel\"\n icon=\"histogram\"\n [active]=\"showHistogram\"\n (click)=\"toggleOverlay()\"\n>\n</app-menu-toggle>\n" }]
8263
+ }], () => [{ type: i1$3.Overlay }], { config: [{
8264
+ type: Input
8265
+ }] }); })();
8266
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(HistogramPanelComponent, { className: "HistogramPanelComponent", filePath: "components/ui-menu/histogram-panel/histogram-panel.component.ts", lineNumber: 21 }); })();
8267
+
7758
8268
  function MasterclassPanelOverlayComponent_div_1_option_8_Template(rf, ctx) { if (rf & 1) {
7759
8269
  i0.ɵɵelementStart(0, "option", 14);
7760
8270
  i0.ɵɵtext(1);
@@ -8598,6 +9108,8 @@ const PHOENIX_COMPONENTS = [
8598
9108
  FileExplorerComponent,
8599
9109
  RingLoaderComponent,
8600
9110
  CycleEventsComponent,
9111
+ HistogramPanelComponent,
9112
+ HistogramPanelOverlayComponent,
8601
9113
  EventBrowserComponent,
8602
9114
  EventBrowserOverlayComponent,
8603
9115
  EtaPhiPanelComponent,
@@ -8732,6 +9244,8 @@ class PhoenixUIModule {
8732
9244
  FileExplorerComponent,
8733
9245
  RingLoaderComponent,
8734
9246
  CycleEventsComponent,
9247
+ HistogramPanelComponent,
9248
+ HistogramPanelOverlayComponent,
8735
9249
  EventBrowserComponent,
8736
9250
  EventBrowserOverlayComponent,
8737
9251
  EtaPhiPanelComponent,
@@ -8811,6 +9325,8 @@ class PhoenixUIModule {
8811
9325
  FileExplorerComponent,
8812
9326
  RingLoaderComponent,
8813
9327
  CycleEventsComponent,
9328
+ HistogramPanelComponent,
9329
+ HistogramPanelOverlayComponent,
8814
9330
  EventBrowserComponent,
8815
9331
  EventBrowserOverlayComponent,
8816
9332
  EtaPhiPanelComponent,
@@ -8840,6 +9356,7 @@ i0.ɵɵsetComponentScope(UiMenuComponent, [i2.NgIf, UiMenuWrapperComponent,
8840
9356
  MakePictureComponent,
8841
9357
  PerformanceToggleComponent,
8842
9358
  ShareLinkComponent,
9359
+ HistogramPanelComponent,
8843
9360
  MasterclassPanelComponent], []);
8844
9361
 
8845
9362
  // Components
@@ -8848,5 +9365,5 @@ i0.ɵɵsetComponentScope(UiMenuComponent, [i2.NgIf, UiMenuWrapperComponent,
8848
9365
  * Generated bundle index. Do not edit.
8849
9366
  */
8850
9367
 
8851
- export { AnimateCameraComponent, AnimateEventComponent, ArToggleComponent, AttributePipe, AutoRotateComponent, CartesianGridConfigComponent, CollectionsInfoComponent, CollectionsInfoOverlayComponent, ConfigSliderComponent, CycleEventsComponent, DarkThemeComponent, DownloadAnimationDialogComponent, EmbedMenuComponent, EtaPhiPanelComponent, EtaPhiPanelOverlayComponent, EventBrowserComponent, EventBrowserOverlayComponent, EventDataExplorerComponent, EventDataExplorerDialogComponent, EventDataFormat, EventDisplayService, EventSelectorComponent, ExperimentInfoComponent, ExperimentLinkComponent, FileEvent, FileExplorerComponent, FileNode, GeometryBrowserComponent, GeometryBrowserOverlayComponent, IOOptionsDialogComponent, ImportOption, InfoPanelComponent, InfoPanelOverlayComponent, IoOptionsComponent, KinematicsPanelComponent, KinematicsPanelOverlayComponent, LoaderComponent, MainViewToggleComponent, MakePictureComponent, MasterclassPanelComponent, MasterclassPanelOverlayComponent, MenuToggleComponent, MoreInfoComponent, NavComponent, NotificationService, NotificationToastComponent, ObjectClippingComponent, ObjectSelectionComponent, ObjectSelectionOverlayComponent, OverlayCascadeService, OverlayComponent, OverlayViewComponent, OverlayViewWindowComponent, PerformanceToggleComponent, PhoenixMenuComponent, PhoenixMenuItemComponent, PhoenixUIModule, RingLoaderComponent, SSModeComponent, SessionPillComponent, ShareLinkComponent, ShareLinkDialogComponent, ShortcutsDialogComponent, TreeMenuComponent, TreeMenuItemComponent, UiMenuComponent, UiMenuWrapperComponent, ViewOptionsComponent, VrToggleComponent, ZoomControlsComponent, defaultAnimationPresets, defaultUIMenuConfig };
9368
+ export { AnimateCameraComponent, AnimateEventComponent, ArToggleComponent, AttributePipe, AutoRotateComponent, CartesianGridConfigComponent, CollectionsInfoComponent, CollectionsInfoOverlayComponent, ConfigSliderComponent, CycleEventsComponent, DarkThemeComponent, DownloadAnimationDialogComponent, EmbedMenuComponent, EtaPhiPanelComponent, EtaPhiPanelOverlayComponent, EventBrowserComponent, EventBrowserOverlayComponent, EventDataExplorerComponent, EventDataExplorerDialogComponent, EventDataFormat, EventDisplayService, EventSelectorComponent, ExperimentInfoComponent, ExperimentLinkComponent, FileEvent, FileExplorerComponent, FileNode, GeometryBrowserComponent, GeometryBrowserOverlayComponent, HistogramPanelComponent, HistogramPanelOverlayComponent, IOOptionsDialogComponent, ImportOption, InfoPanelComponent, InfoPanelOverlayComponent, IoOptionsComponent, KinematicsPanelComponent, KinematicsPanelOverlayComponent, LoaderComponent, MainViewToggleComponent, MakePictureComponent, MasterclassPanelComponent, MasterclassPanelOverlayComponent, MenuToggleComponent, MoreInfoComponent, NavComponent, NotificationService, NotificationToastComponent, ObjectClippingComponent, ObjectSelectionComponent, ObjectSelectionOverlayComponent, OverlayCascadeService, OverlayComponent, OverlayViewComponent, OverlayViewWindowComponent, PerformanceToggleComponent, PhoenixMenuComponent, PhoenixMenuItemComponent, PhoenixUIModule, RingLoaderComponent, SSModeComponent, SessionPillComponent, ShareLinkComponent, ShareLinkDialogComponent, ShortcutsDialogComponent, TreeMenuComponent, TreeMenuItemComponent, UiMenuComponent, UiMenuWrapperComponent, ViewOptionsComponent, VrToggleComponent, ZoomControlsComponent, defaultAnimationPresets, defaultUIMenuConfig };
8852
9369
  //# sourceMappingURL=phoenix-ui-components.mjs.map