Warning, /firebird/firebird-ng/src/app/components/shell/shell.component.ts is written in an unsupported language. File is not indexed.
0001 import {
0002 Component,
0003 HostListener,
0004 ViewChild,
0005 ViewContainerRef,
0006 ComponentRef,
0007 Type,
0008 EventEmitter,
0009 Output, ElementRef, Input,
0010 } from '@angular/core';
0011 import { CommonModule } from '@angular/common';
0012 import {Router} from '@angular/router';
0013 // Angular Material imports for the top bar
0014 import { MatToolbarModule } from '@angular/material/toolbar';
0015 import { MatButtonModule } from '@angular/material/button';
0016 import { MatIconModule } from '@angular/material/icon';
0017 import { MatMenuModule } from '@angular/material/menu';
0018 import {ThemeSwitcherComponent} from "../theme-switcher/theme-switcher.component";
0019 import {MatTooltip} from "@angular/material/tooltip";
0020
0021 interface NavItem {
0022 label: string;
0023 route: string;
0024 external?: boolean;
0025 icon?: string;
0026 /** If true, shows up on wide screens directly. Otherwise in hamburger. */
0027 alwaysVisible?: boolean;
0028 }
0029
0030 @Component({
0031 standalone: true,
0032 selector: 'app-shell',
0033 templateUrl: './shell.component.html',
0034 styleUrls: ['./shell.component.scss'],
0035 imports: [
0036 CommonModule,
0037 // Material modules needed for the top bar
0038 MatToolbarModule,
0039 MatButtonModule,
0040 MatIconModule,
0041 MatMenuModule,
0042 ThemeSwitcherComponent,
0043 MatTooltip
0044 ],
0045 })
0046 export class ShellComponent {
0047
0048 /** The reference to the left container, for programmatic component creation */
0049 @ViewChild('leftPaneContainer', { read: ViewContainerRef, static: true })
0050 leftPaneContainer!: ViewContainerRef;
0051
0052 /** The reference to the right container, for programmatic component creation */
0053 @ViewChild('rightPaneContainer', { read: ViewContainerRef, static: true })
0054 rightPaneContainer!: ViewContainerRef;
0055
0056 /** The reference to main content container */
0057 @ViewChild('mainContent', { static: true }) mainContent!: ElementRef<HTMLElement>;
0058
0059 /** Event emitted when the resizing of the left pane ends. Emits the new width. */
0060 @Output() onEndResizeLeft = new EventEmitter<number>();
0061
0062 /** Event emitted when the resizing of the right pane ends. Emits the new width. */
0063 @Output() onEndResizeRight = new EventEmitter<number>();
0064
0065 /** Event emitted when the visibility of the right panel is changed. */
0066 @Output() onVisibilityChangeRight = new EventEmitter<boolean>();
0067
0068 /** Event emitted when the visibility of the left panel is changed. */
0069 @Output() onVisibilityChangeLeft = new EventEmitter<boolean>();
0070
0071 /** Is left Pane visible by default */
0072 @Input()
0073 isLeftPaneVisible = false;
0074
0075 /** Is right Pane visible by default */
0076 @Input()
0077 isRightPaneVisible = false;
0078
0079 /** Shell resizing logic */
0080 private isResizingLeft = false;
0081 private isResizingRight = false;
0082 leftPaneWidth = 250;
0083 rightPaneWidth = 250;
0084
0085
0086
0087 /** Top bar: whether the mobile menu is open */
0088 navOpen = false;
0089 /** Track current theme (light or dark) */
0090 isDarkTheme = false;
0091
0092 /** Single place for nav items */
0093 navItems: NavItem[] = [
0094 { label: 'Doc', route: '/help', icon: 'menu_book', alwaysVisible: true },
0095 { label: 'Display', route: '/display', icon: 'monitor', alwaysVisible: true },
0096 { label: 'Configure', route: '/config', icon: 'tune', alwaysVisible: true },
0097 { label: 'GitHub Repo', route: 'https://github.com/eic/firebird', external: true, icon: 'code' },
0098 { label: 'Submit Ideas', route: 'https://github.com/eic/firebird/issues', external: true, icon: 'feedback' },
0099 ];
0100
0101 constructor(
0102 private router: Router
0103 ) {}
0104
0105 /** Resizing logic for left pane */
0106 onMouseDownLeft(event: MouseEvent) {
0107 this.isResizingLeft = true;
0108 event.preventDefault();
0109 }
0110
0111 /** Resizing logic for right pane */
0112 onMouseDownRight(event: MouseEvent) {
0113 this.isResizingRight = true;
0114 event.preventDefault();
0115 }
0116
0117 @HostListener('document:mousemove', ['$event'])
0118 onMouseMove(event: MouseEvent) {
0119 if (this.isResizingLeft) {
0120 const minWidth = 100;
0121 const maxWidth = window.innerWidth - this.rightPaneWidth - 100;
0122 const newWidth = event.clientX;
0123 this.leftPaneWidth = Math.max(minWidth, Math.min(newWidth, maxWidth));
0124 event.preventDefault();
0125 } else if (this.isResizingRight) {
0126 const minWidth = 100;
0127 const maxWidth = window.innerWidth - this.leftPaneWidth - 100;
0128 const newWidth = window.innerWidth - event.clientX;
0129 this.rightPaneWidth = Math.max(minWidth, Math.min(newWidth, maxWidth));
0130 event.preventDefault();
0131 }
0132 }
0133
0134 @HostListener('document:mouseup')
0135 onMouseUp() {
0136 if (this.isResizingLeft) {
0137 this.isResizingLeft = false;
0138 this.onEndResizeLeft.emit(this.leftPaneWidth);
0139 }
0140 if (this.isResizingRight) {
0141 this.isResizingRight = false;
0142 this.onEndResizeRight.emit(this.rightPaneWidth);
0143 }
0144 }
0145
0146 /** Programmatically add component to left pane */
0147 addComponentToLeftPane<T>(component: Type<T>, data?: Partial<T>): ComponentRef<T> {
0148 this.leftPaneContainer.clear();
0149 const componentRef = this.leftPaneContainer.createComponent(component);
0150 if (data) {
0151 Object.assign(componentRef.instance as object, data);
0152 }
0153 return componentRef;
0154 }
0155
0156 /** Programmatically add component to right pane */
0157 addComponentToRightPane<T>(component: Type<T>, data?: Partial<T>): ComponentRef<T> {
0158 this.rightPaneContainer.clear();
0159 const componentRef = this.rightPaneContainer.createComponent(component);
0160 if (data) {
0161 Object.assign(componentRef.instance as object, data);
0162 }
0163 return componentRef;
0164 }
0165
0166 toggleLeftPane() {
0167 this.isLeftPaneVisible = !this.isLeftPaneVisible;
0168 this.onVisibilityChangeLeft.emit(this.isLeftPaneVisible);
0169 }
0170
0171 toggleRightPane() {
0172 this.isRightPaneVisible = !this.isRightPaneVisible;
0173 this.onVisibilityChangeRight.emit(this.isRightPaneVisible);
0174 }
0175
0176 /** Toggle the mobile hamburger menu */
0177 toggleNavConfig() {
0178 this.navOpen = !this.navOpen;
0179 }
0180
0181 /**
0182 * Returns the visible dimensions of the main content area.
0183 * Uses clientWidth/clientHeight to exclude scrollbars,
0184 * and subtracts side panel widths if they are visible.
0185 */
0186 getMainAreaVisibleDimensions(): { width: number; height: number } {
0187
0188
0189 const visibleWidth = this.mainContent.nativeElement.clientWidth -
0190 (this.isLeftPaneVisible ? this.leftPaneWidth : 0) -
0191 (this.isRightPaneVisible ? this.rightPaneWidth : 0);
0192 const visibleHeight = this.mainContent.nativeElement.clientHeight;
0193
0194
0195 console.log("Shell resize information: ")
0196 console.log(" mainContent.clientWidth:", this.mainContent.nativeElement.clientWidth);
0197 console.log(" mainContent.clientHeight:", this.mainContent.nativeElement.clientHeight);
0198 console.log(" isLeftPaneVisible:", this.isLeftPaneVisible);
0199 if(this.isLeftPaneVisible)
0200 {
0201 console.log(" leftPaneWidth:", this.leftPaneWidth);
0202 }
0203 console.log(" isRightPaneVisible:", this.isRightPaneVisible);
0204 if(this.isRightPaneVisible)
0205 {
0206 console.log(" rightPaneWidth:", this.rightPaneWidth);
0207 }
0208 console.log(" visibleWidth:", visibleWidth);
0209 console.log(" visibleHeight:", visibleHeight);
0210
0211 return { width: visibleWidth, height: visibleHeight };
0212 }
0213
0214 /** Clicking a nav item => external link or internal route */
0215 onNavItemClick(item: NavItem) {
0216 if (item.external) {
0217 window.open(item.route, '_blank');
0218 } else {
0219 this.router.navigateByUrl(item.route);
0220 }
0221 this.navOpen = false;
0222 }
0223 }