mirror of https://github.com/OpenVidu/openvidu.git
ov-components: refactor toolbar panel buttons to use Angular signals for state management and enhance badge styles
parent
13cfff1aad
commit
703403f182
|
|
@ -1,12 +1,12 @@
|
|||
<!-- Responsive container: show individual buttons on larger screens, collapsed menu on smaller screens -->
|
||||
<ng-container *ngIf="shouldShowCollapsed; else uncollapsedButtons">
|
||||
<div class="collapsed-menu-container" *ngIf="visibleButtonsCount > 0">
|
||||
<div class="collapsed-menu-container" *ngIf="visibleButtonsCount() > 0">
|
||||
<button
|
||||
mat-icon-button
|
||||
class="fab-menu-trigger"
|
||||
[matMenuTriggerFor]="panelsMenu"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isAnyPanelOpened"
|
||||
[disabled]="isConnectionLost()"
|
||||
[class.active-btn]="isAnyPanelOpened()"
|
||||
matTooltip="{{ 'TOOLBAR.PANELS' | translate }}"
|
||||
#menuTrigger="matMenuTrigger"
|
||||
>
|
||||
|
|
@ -19,41 +19,43 @@
|
|||
<button
|
||||
mat-menu-item
|
||||
class="panel-menu-item"
|
||||
*ngIf="!isMinimal && showActivitiesPanelButton"
|
||||
*ngIf="!isMinimal() && showActivitiesPanelButton()"
|
||||
(click)="onToggleActivities()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-menu-item]="isActivitiesOpened"
|
||||
[disabled]="isConnectionLost()"
|
||||
[class.active-menu-item]="isActivitiesOpened()"
|
||||
>
|
||||
<mat-icon>category</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- Participants menu item -->
|
||||
<button
|
||||
*ngIf="!isMinimal() && showParticipantsPanelButton()"
|
||||
mat-menu-item
|
||||
[matBadge]="totalParticipants()"
|
||||
[matBadgeHidden]="totalParticipants() === 0"
|
||||
matBadgePosition="above before"
|
||||
class="panel-menu-item"
|
||||
*ngIf="!isMinimal && showParticipantsPanelButton"
|
||||
(click)="onToggleParticipants()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-menu-item]="isParticipantsOpened"
|
||||
[disabled]="isConnectionLost()"
|
||||
[class.active-menu-item]="isParticipantsOpened()"
|
||||
>
|
||||
<mat-icon>people</mat-icon>
|
||||
<mat-icon aria-hidden="false"> people</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- Chat menu item -->
|
||||
<button
|
||||
mat-menu-item
|
||||
class="panel-menu-item"
|
||||
*ngIf="!isMinimal && showChatPanelButton"
|
||||
*ngIf="!isMinimal() && showChatPanelButton()"
|
||||
(click)="onToggleChat()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-menu-item]="isChatOpened"
|
||||
[disabled]="isConnectionLost()"
|
||||
[class.active-menu-item]="isChatOpened()"
|
||||
>
|
||||
<mat-icon
|
||||
matBadge="{{ unreadMessages }}"
|
||||
[matBadgeHidden]="unreadMessages === 0"
|
||||
[matBadge]="unreadMessages()"
|
||||
[matBadgeHidden]="unreadMessages() === 0"
|
||||
matBadgePosition="above before"
|
||||
matBadgeSize="small"
|
||||
matBadgeColor="accent"
|
||||
aria-hidden="false"
|
||||
>
|
||||
chat
|
||||
|
|
@ -61,8 +63,8 @@
|
|||
</button>
|
||||
|
||||
<!-- External additional panel buttons in menu -->
|
||||
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate">
|
||||
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
|
||||
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate()">
|
||||
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate()"></ng-container>
|
||||
</ng-container>
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
|
@ -75,11 +77,11 @@
|
|||
mat-icon-button
|
||||
id="activities-panel-btn"
|
||||
class="panel-button"
|
||||
*ngIf="!isMinimal && showActivitiesPanelButton"
|
||||
*ngIf="!isMinimal() && showActivitiesPanelButton()"
|
||||
matTooltip="{{ 'TOOLBAR.ACTIVITIES' | translate }}"
|
||||
(click)="onToggleActivities()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isActivitiesOpened"
|
||||
[disabled]="isConnectionLost()"
|
||||
[class.active-btn]="isActivitiesOpened()"
|
||||
>
|
||||
<mat-icon>category</mat-icon>
|
||||
</button>
|
||||
|
|
@ -89,13 +91,20 @@
|
|||
mat-icon-button
|
||||
class="panel-button"
|
||||
id="participants-panel-btn"
|
||||
*ngIf="!isMinimal && showParticipantsPanelButton"
|
||||
*ngIf="!isMinimal() && showParticipantsPanelButton()"
|
||||
matTooltip="{{ 'TOOLBAR.PARTICIPANTS' | translate }}"
|
||||
(click)="onToggleParticipants()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isParticipantsOpened"
|
||||
[disabled]="isConnectionLost()"
|
||||
[class.active-btn]="isParticipantsOpened()"
|
||||
>
|
||||
<mat-icon>people</mat-icon>
|
||||
<mat-icon
|
||||
[matBadge]="totalParticipants()"
|
||||
[matBadgeHidden]="totalParticipants() === 0"
|
||||
matBadgePosition="above before"
|
||||
matBadgeSize="small"
|
||||
aria-hidden="false"
|
||||
>people</mat-icon
|
||||
>
|
||||
</button>
|
||||
|
||||
<!-- Default chat button -->
|
||||
|
|
@ -103,18 +112,17 @@
|
|||
mat-icon-button
|
||||
class="panel-button"
|
||||
id="chat-panel-btn"
|
||||
*ngIf="!isMinimal && showChatPanelButton"
|
||||
*ngIf="!isMinimal() && showChatPanelButton()"
|
||||
matTooltip="{{ 'TOOLBAR.CHAT' | translate }}"
|
||||
(click)="onToggleChat()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isChatOpened"
|
||||
[disabled]="isConnectionLost()"
|
||||
[class.active-btn]="isChatOpened()"
|
||||
>
|
||||
<mat-icon
|
||||
matBadge="{{ unreadMessages }}"
|
||||
[matBadgeHidden]="unreadMessages === 0"
|
||||
[matBadge]="unreadMessages()"
|
||||
[matBadgeHidden]="unreadMessages() === 0"
|
||||
matBadgePosition="above before"
|
||||
matBadgeSize="small"
|
||||
matBadgeColor="accent"
|
||||
aria-hidden="false"
|
||||
>
|
||||
chat
|
||||
|
|
@ -122,7 +130,7 @@
|
|||
</button>
|
||||
|
||||
<!-- External additional panel buttons -->
|
||||
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate">
|
||||
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
|
||||
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate()">
|
||||
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate()"></ng-container>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,24 @@
|
|||
.panel-button {
|
||||
border-radius: var(--ov-toolbar-buttons-radius);
|
||||
color: var(--ov-text-primary-color);
|
||||
|
||||
// Custom badge styles for participants and chat buttons
|
||||
::ng-deep .mat-badge-content {
|
||||
background-color: var(--ov-accent-action-color) !important;
|
||||
color: var(--ov-text-primary-color) !important;
|
||||
font-weight: 600;
|
||||
font-size: 10px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
::ng-deep .mat-badge-small .mat-badge-content {
|
||||
font-size: 9px;
|
||||
min-width: 14px;
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.active-btn,
|
||||
|
|
@ -93,18 +111,11 @@
|
|||
}
|
||||
|
||||
// Chat badge adjustments for menu
|
||||
.mat-badge {
|
||||
mat-icon {
|
||||
font-size: 18px !important;
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-badge-content {
|
||||
background-color: var(--ov-warn-color) !important;
|
||||
::ng-deep .mat-badge-content {
|
||||
background-color: var(--ov-accent-action-color) !important;
|
||||
color: var(--ov-text-primary-color) !important;
|
||||
font-size: 10px !important;
|
||||
font-weight: 600 !important;
|
||||
min-width: 14px !important;
|
||||
height: 14px !important;
|
||||
line-height: 14px !important;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
|
||||
import { Component, TemplateRef, computed, input, output } from '@angular/core';
|
||||
import { ViewportService } from '../../../services/viewport/viewport.service';
|
||||
|
||||
@Component({
|
||||
|
|
@ -8,24 +8,38 @@ import { ViewportService } from '../../../services/viewport/viewport.service';
|
|||
standalone: false
|
||||
})
|
||||
export class ToolbarPanelButtonsComponent {
|
||||
// Inputs from toolbar
|
||||
@Input() isMinimal: boolean = false;
|
||||
@Input() isConnectionLost: boolean = false;
|
||||
@Input() isActivitiesOpened: boolean = false;
|
||||
@Input() isParticipantsOpened: boolean = false;
|
||||
@Input() isChatOpened: boolean = false;
|
||||
@Input() unreadMessages: number = 0;
|
||||
@Input() showActivitiesPanelButton: boolean = true;
|
||||
@Input() showParticipantsPanelButton: boolean = true;
|
||||
@Input() showChatPanelButton: boolean = true;
|
||||
@Input() recordingStatus: any;
|
||||
@Input() broadcastingStatus: any;
|
||||
@Input() toolbarAdditionalPanelButtonsTemplate: TemplateRef<any> | undefined;
|
||||
// Signal inputs from toolbar
|
||||
isMinimal = input<boolean>(false);
|
||||
isConnectionLost = input<boolean>(false);
|
||||
isActivitiesOpened = input<boolean>(false);
|
||||
isParticipantsOpened = input<boolean>(false);
|
||||
isChatOpened = input<boolean>(false);
|
||||
unreadMessages = input<number>(0);
|
||||
showActivitiesPanelButton = input<boolean>(true);
|
||||
showParticipantsPanelButton = input<boolean>(true);
|
||||
showChatPanelButton = input<boolean>(true);
|
||||
recordingStatus = input<any>();
|
||||
broadcastingStatus = input<any>();
|
||||
toolbarAdditionalPanelButtonsTemplate = input<TemplateRef<any> | undefined>();
|
||||
totalParticipants = input<number>(0);
|
||||
|
||||
// Outputs back to toolbar
|
||||
@Output() toggleActivitiesPanel: EventEmitter<string | undefined> = new EventEmitter();
|
||||
@Output() toggleParticipantsPanel: EventEmitter<void> = new EventEmitter();
|
||||
@Output() toggleChatPanel: EventEmitter<void> = new EventEmitter();
|
||||
// Signal outputs back to toolbar
|
||||
toggleActivitiesPanel = output<string | undefined>();
|
||||
toggleParticipantsPanel = output<void>();
|
||||
toggleChatPanel = output<void>();
|
||||
|
||||
// Computed signals
|
||||
visibleButtonsCount = computed(() => {
|
||||
let count = 0;
|
||||
if (!this.isMinimal() && this.showActivitiesPanelButton()) count++;
|
||||
if (!this.isMinimal() && this.showParticipantsPanelButton()) count++;
|
||||
if (!this.isMinimal() && this.showChatPanelButton()) count++;
|
||||
return count;
|
||||
});
|
||||
|
||||
isAnyPanelOpened = computed(() => {
|
||||
return this.isActivitiesOpened() || this.isParticipantsOpened() || this.isChatOpened();
|
||||
});
|
||||
|
||||
constructor(public viewportService: ViewportService) {}
|
||||
|
||||
|
|
@ -34,20 +48,6 @@ export class ToolbarPanelButtonsComponent {
|
|||
return this.viewportService.isMobileView()
|
||||
}
|
||||
|
||||
// Get count of visible buttons
|
||||
get visibleButtonsCount(): number {
|
||||
let count = 0;
|
||||
if (!this.isMinimal && this.showActivitiesPanelButton) count++;
|
||||
if (!this.isMinimal && this.showParticipantsPanelButton) count++;
|
||||
if (!this.isMinimal && this.showChatPanelButton) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Check if any panel is currently opened
|
||||
get isAnyPanelOpened(): boolean {
|
||||
return this.isActivitiesOpened || this.isParticipantsOpened || this.isChatOpened;
|
||||
}
|
||||
|
||||
// Local methods that emit events
|
||||
onToggleActivities(expand?: string) {
|
||||
this.toggleActivitiesPanel.emit(expand);
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@
|
|||
[showChatPanelButton]="showChatPanelButton"
|
||||
[recordingStatus]="recordingStatus"
|
||||
[broadcastingStatus]="broadcastingStatus"
|
||||
[totalParticipants]="totalParticipants()"
|
||||
[toolbarAdditionalPanelButtonsTemplate]="toolbarAdditionalPanelButtonsTemplate"
|
||||
(toggleActivitiesPanel)="toggleActivitiesPanel($event)"
|
||||
(toggleParticipantsPanel)="toggleParticipantsPanel()"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import {
|
|||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
computed,
|
||||
ContentChild,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
|
|
@ -18,6 +19,8 @@ import { DocumentService } from '../../services/document/document.service';
|
|||
import { PanelService } from '../../services/panel/panel.service';
|
||||
|
||||
import { MatMenuTrigger } from '@angular/material/menu';
|
||||
import { Room, RoomEvent } from 'livekit-client';
|
||||
import { LeaveButtonDirective, ToolbarMoreOptionsAdditionalMenuItemsDirective } from '../../directives/template/internals.directive';
|
||||
import {
|
||||
ToolbarAdditionalButtonsDirective,
|
||||
ToolbarAdditionalPanelButtonsDirective
|
||||
|
|
@ -26,6 +29,7 @@ import { BroadcastingStatus, BroadcastingStatusInfo, BroadcastingStopRequestedEv
|
|||
import { ChatMessage } from '../../models/chat.model';
|
||||
import { ILogger } from '../../models/logger.model';
|
||||
import { PanelStatusInfo, PanelType } from '../../models/panel.model';
|
||||
import { ParticipantLeftEvent, ParticipantLeftReason, ParticipantModel } from '../../models/participant.model';
|
||||
import {
|
||||
RecordingInfo,
|
||||
RecordingStartRequestedEvent,
|
||||
|
|
@ -33,8 +37,10 @@ import {
|
|||
RecordingStatusInfo,
|
||||
RecordingStopRequestedEvent
|
||||
} from '../../models/recording.model';
|
||||
import { ToolbarAdditionalButtonsPosition } from '../../models/toolbar.model';
|
||||
import { ActionService } from '../../services/action/action.service';
|
||||
import { BroadcastingService } from '../../services/broadcasting/broadcasting.service';
|
||||
import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service';
|
||||
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
|
||||
import { DeviceService } from '../../services/device/device.service';
|
||||
import { LayoutService } from '../../services/layout/layout.service';
|
||||
|
|
@ -46,11 +52,6 @@ import { RecordingService } from '../../services/recording/recording.service';
|
|||
import { StorageService } from '../../services/storage/storage.service';
|
||||
import { TemplateManagerService, ToolbarTemplateConfiguration } from '../../services/template/template-manager.service';
|
||||
import { TranslateService } from '../../services/translate/translate.service';
|
||||
import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service';
|
||||
import { ParticipantLeftEvent, ParticipantLeftReason, ParticipantModel } from '../../models/participant.model';
|
||||
import { Room, RoomEvent } from 'livekit-client';
|
||||
import { ToolbarAdditionalButtonsPosition } from '../../models/toolbar.model';
|
||||
import { LeaveButtonDirective, ToolbarMoreOptionsAdditionalMenuItemsDirective } from '../../directives/template/internals.directive';
|
||||
|
||||
/**
|
||||
* The **ToolbarComponent** is hosted inside of the {@link VideoconferenceComponent}.
|
||||
|
|
@ -405,6 +406,16 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
*/
|
||||
templateConfig: ToolbarTemplateConfiguration = {};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Computed signal for total participants count (local + remote)
|
||||
*/
|
||||
totalParticipants = computed(() => {
|
||||
const local = this.participantService.localParticipantSignal();
|
||||
const remotes = this.participantService.remoteParticipantsSignal();
|
||||
return (local ? 1 : 0) + remotes.length;
|
||||
});
|
||||
|
||||
// Store directive references for template setup
|
||||
private _externalAdditionalButtons?: ToolbarAdditionalButtonsDirective;
|
||||
private _externalLeaveButton?: LeaveButtonDirective;
|
||||
|
|
@ -624,8 +635,8 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
await this.openviduService.disconnectRoom(() => {
|
||||
this.onParticipantLeft.emit({
|
||||
roomName: this.openviduService.getRoomName(),
|
||||
participantName: this.participantService.getLocalParticipant()?.name || '',
|
||||
identity: this.participantService.getLocalParticipant()?.identity || '',
|
||||
participantName: this.participantService.localParticipantSignal()!.name || '',
|
||||
identity: this.participantService.localParticipantSignal()!.identity || '',
|
||||
reason: ParticipantLeftReason.LEAVE
|
||||
});
|
||||
this.onRoomDisconnected.emit();
|
||||
|
|
|
|||
Loading…
Reference in New Issue