import { Component, Inject, OnInit, OnDestroy, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { MatSelectModule } from '@angular/material/select'; import { MatFormFieldModule } from '@angular/material/form-field'; import { BreakpointObserver } from '@angular/cdk/layout'; import { I18nService } from '../../services/i18n.service'; import { MainCommandService } from '../../services/main-command.service'; import { SocketService } from '../../services/socket.service'; import { RedisStateService } from '../../services/redis-state.service'; @Component({ selector: 'p3xr-database-header', standalone: true, imports: [ CommonModule, FormsModule, MatToolbarModule, MatButtonModule, MatIconModule, MatTooltipModule, MatSelectModule, MatFormFieldModule, ], template: `
@if (!isXs) {

{{ strings().intention?.main || 'P3X Redis UI' }}

} @if (hasConnection) { @if (!isCluster) {
DB: {{ hasKeys(currentDatabase) ? 'radio_button_checked' : 'radio_button_unchecked' }} {{ currentDatabase }} @for (dbIndex of databaseIndexes; track dbIndex) { {{ hasKeys(dbIndex) ? 'radio_button_checked' : 'radio_button_unchecked' }} {{ dbIndex }} }
} @if (!isReadonly) { @if (isWide) { } @else { } } @if (isWide) { } @else { } @if (isWide) { } @else { } }
`, styles: [` :host { display: block; } .p3xr-database-header-toolbar { height: 48px; min-height: 48px; max-height: 48px; padding: 0 8px 0 16px; border-radius: 4px 4px 0 0; } .p3xr-database-header-tools { display: flex; align-items: center; width: 100%; height: 48px; } .p3xr-database-header-title { flex: 1; font-size: 20px; font-weight: 400; margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .p3xr-database-header-link { cursor: pointer; text-decoration: none; color: inherit; } .p3xr-database-header-db-selector { display: flex; align-items: center; margin: 0; padding: 0; } .p3xr-database-header-db-label { font-size: 14px; font-weight: bold; margin-right: 2px; } .p3xr-database-header-db-field { width: 80px; position: relative; top: 1px; } .p3xr-database-header-db-field ::ng-deep .mdc-text-field { background: transparent !important; padding: 0 8px !important; } .p3xr-database-header-db-field ::ng-deep .mat-mdc-form-field-subscript-wrapper { display: none; } .p3xr-database-header-db-field ::ng-deep .mdc-line-ripple { display: none; } .p3xr-database-header-db-field ::ng-deep .mat-mdc-select-arrow-wrapper { padding-left: 0; } .p3xr-database-header-db-field ::ng-deep .mat-mdc-select-trigger { display: flex; align-items: center; } .p3xr-database-header-db-field ::ng-deep .mat-mdc-select-value { display: flex; align-items: center; } .p3xr-database-header-db-field ::ng-deep .mat-mdc-select-value-text { display: flex; align-items: center; } .p3xr-database-header-db-field ::ng-deep mat-select-trigger { display: flex; align-items: center; gap: 4px; } .p3xr-database-header-db-field ::ng-deep mat-select-trigger .p3xr-db-indicator { font-size: 18px !important; width: 18px !important; height: 18px !important; line-height: 18px !important; overflow: hidden; flex-shrink: 0; } `], changeDetection: ChangeDetectionStrategy.OnPush, }) export class DatabaseHeaderComponent implements OnInit, OnDestroy { readonly strings; isXs = false; isWide = true; hasConnection = false; isCluster = false; isReadonly = false; currentDatabase: number = 0; databaseIndexes: number[] = []; private keyspaceDatabases: Record = {}; private readonly unsubs: Array<() => void> = []; constructor( @Inject(BreakpointObserver) private readonly breakpointObserver: BreakpointObserver, @Inject(I18nService) private readonly i18n: I18nService, @Inject(MainCommandService) private readonly cmd: MainCommandService, @Inject(SocketService) private readonly socket: SocketService, @Inject(ChangeDetectorRef) private readonly cdr: ChangeDetectorRef, @Inject(RedisStateService) private readonly state: RedisStateService, ) { this.strings = this.i18n.strings; } ngOnInit(): void { this.syncFromGlobal(); // Subscribe to socket events for reactive state updates const sub1 = this.socket.connections$.subscribe(() => this.syncFromGlobal()); const sub2 = this.socket.redisDisconnected$.subscribe(() => this.syncFromGlobal()); const sub3 = this.socket.stateChanged$.subscribe(() => this.syncFromGlobal()); this.unsubs.push(() => { sub1.unsubscribe(); sub2.unsubscribe(); sub3.unsubscribe(); }); const xsSub = this.breakpointObserver.observe('(max-width: 599px)').subscribe(result => { this.isXs = result.matches; this.cdr.markForCheck(); }); this.unsubs.push(() => xsSub.unsubscribe()); const wideSub = this.breakpointObserver.observe('(min-width: 720px)').subscribe(result => { this.isWide = result.matches; this.cdr.markForCheck(); }); this.unsubs.push(() => wideSub.unsubscribe()); } ngOnDestroy(): void { this.unsubs.forEach(fn => fn()); } hasKeys(dbIndex: number): boolean { return !!this.keyspaceDatabases[dbIndex]; } selectDatabase(dbIndex: number): void { this.currentDatabase = dbIndex; this.cmd.selectDatabase(dbIndex).then(() => { this.syncFromGlobal(); }); // Force re-render after mat-select closes setTimeout(() => this.cdr.detectChanges()); } save(): void { this.cmd.save(); } goStatistics(): void { this.cmd.statistics(); } refresh(): void { this.cmd.refresh({ withoutParent: false }); } private syncFromGlobal(): void { const conn = this.state.connection(); this.hasConnection = conn !== undefined; this.isCluster = conn?.cluster === true; this.isReadonly = conn?.readonly === true; this.databaseIndexes = this.state.databaseIndexes() ?? []; this.keyspaceDatabases = this.state.info()?.keyspaceDatabases ?? {}; this.currentDatabase = this.cmd.currentDatabase; this.cdr.detectChanges(); } }