ov-components: Add directive for injecting custom menu items into toolbar more options

master
Carlos Santos 2025-11-24 13:38:29 +01:00
parent 171a5104ae
commit 9918b07f51
9 changed files with 145 additions and 12 deletions

View File

@ -177,6 +177,11 @@
</ng-container>
}
<!-- Additional menu items injected via directive -->
@if (moreOptionsAdditionalMenuItemsTemplate) {
<ng-container *ngTemplateOutlet="moreOptionsAdditionalMenuItemsTemplate"></ng-container>
}
<!-- Divider before settings -->
@if (showSettingsButton) {
<mat-divider class="divider"></mat-divider>

View File

@ -1,8 +1,9 @@
import { Component, EventEmitter, Input, Output, TemplateRef, computed, inject } from '@angular/core';
import { Component, ContentChild, EventEmitter, Input, Output, TemplateRef, computed, inject } from '@angular/core';
import { RecordingStatus } from '../../../models/recording.model';
import { BroadcastingStatus } from '../../../models/broadcasting.model';
import { ToolbarAdditionalButtonsPosition } from '../../../models/toolbar.model';
import { ViewportService } from '../../../services/viewport/viewport.service';
import { ToolbarMoreOptionsAdditionalMenuItemsDirective } from '../../../directives/template/internals.directive';
/**
* @internal
@ -71,6 +72,21 @@ export class ToolbarMediaButtonsComponent {
// Leave button template
@Input() toolbarLeaveButtonTemplate: TemplateRef<any> | null = null;
/**
* @internal
* ContentChild for custom menu items in more options menu
*/
@ContentChild(ToolbarMoreOptionsAdditionalMenuItemsDirective)
externalMoreOptionsAdditionalMenuItems!: ToolbarMoreOptionsAdditionalMenuItemsDirective;
/**
* @internal
* Gets the template for additional menu items in more options
*/
get moreOptionsAdditionalMenuItemsTemplate(): TemplateRef<any> | undefined {
return this.externalMoreOptionsAdditionalMenuItems?.template;
}
// Status enums for template usage
_recordingStatus = RecordingStatus;
_broadcastingStatus = BroadcastingStatus;

View File

@ -82,7 +82,12 @@
(captionsToggled)="onCaptionsToggle()"
(settingsToggled)="toggleSettings()"
(leaveClicked)="disconnect()"
></ov-toolbar-media-buttons>
>
<!-- Inject additional menu items via content projection -->
<ng-container *ovToolbarMoreOptionsAdditionalMenuItems>
<ng-container *ngTemplateOutlet="externalMoreOptionsAdditionalMenuItems?.template"></ng-container>
</ng-container>
</ov-toolbar-media-buttons>
</div>
<!-- Panel buttons -->

View File

@ -50,7 +50,7 @@ import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.servic
import { ParticipantLeftEvent, ParticipantLeftReason, ParticipantModel } from '../../models/participant.model';
import { Room, RoomEvent } from 'livekit-client';
import { ToolbarAdditionalButtonsPosition } from '../../models/toolbar.model';
import { LeaveButtonDirective } from '../../directives/template/internals.directive';
import { LeaveButtonDirective, ToolbarMoreOptionsAdditionalMenuItemsDirective } from '../../directives/template/internals.directive';
/**
* The **ToolbarComponent** is hosted inside of the {@link VideoconferenceComponent}.
@ -80,6 +80,28 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
| TemplateRef<any>
| undefined;
/**
* @internal
* Template for additional menu items in the more options menu
*/
moreOptionsAdditionalMenuItemsTemplate: TemplateRef<any> | undefined;
private _externalMoreOptionsAdditionalMenuItems?: ToolbarMoreOptionsAdditionalMenuItemsDirective;
/**
* @internal
*/
@ContentChild(ToolbarMoreOptionsAdditionalMenuItemsDirective)
set externalMoreOptionsAdditionalMenuItems(value: ToolbarMoreOptionsAdditionalMenuItemsDirective) {
this._externalMoreOptionsAdditionalMenuItems = value;
this.setupTemplates();
}
/**
* @internal
*/
get externalMoreOptionsAdditionalMenuItems(): ToolbarMoreOptionsAdditionalMenuItemsDirective | undefined {
return this._externalMoreOptionsAdditionalMenuItems;
}
/**
* @ignore
*/
@ -494,7 +516,8 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
this.templateConfig = this.templateManagerService.setupToolbarTemplates(
this._externalAdditionalButtons,
this._externalAdditionalPanelButtons,
this._externalLeaveButton
this._externalLeaveButton,
this._externalMoreOptionsAdditionalMenuItems
);
// Apply templates to component properties for backward compatibility
@ -515,6 +538,9 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
if (this.templateConfig.toolbarLeaveButtonTemplate) {
this.toolbarLeaveButtonTemplate = this.templateConfig.toolbarLeaveButtonTemplate;
}
if (this.templateConfig.toolbarMoreOptionsAdditionalMenuItemsTemplate) {
this.moreOptionsAdditionalMenuItemsTemplate = this.templateConfig.toolbarMoreOptionsAdditionalMenuItemsTemplate;
}
}
/**

View File

@ -96,6 +96,11 @@
<ng-template #toolbarLeaveButton>
<ng-container *ngTemplateOutlet="openviduAngularToolbarLeaveButtonTemplate"></ng-container>
</ng-template>
<!-- Inject additional menu items in toolbar more options -->
<ng-container *ovToolbarMoreOptionsAdditionalMenuItems>
<ng-container *ngTemplateOutlet="ovToolbarMoreOptionsAdditionalMenuItemsTemplate"></ng-container>
</ng-container>
</ov-toolbar>
</ng-template>

View File

@ -63,7 +63,8 @@ import {
ParticipantPanelAfterLocalParticipantDirective,
PreJoinDirective,
LeaveButtonDirective,
SettingsPanelGeneralAdditionalElementsDirective
SettingsPanelGeneralAdditionalElementsDirective,
ToolbarMoreOptionsAdditionalMenuItemsDirective
} from '../../directives/template/internals.directive';
import { OpenViduThemeService } from '../../services/theme/theme.service';
import { E2eeService } from '../../services/e2ee/e2ee.service';
@ -392,6 +393,22 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
return this._externalSettingsPanelGeneralAdditionalElements;
}
private _externalToolbarMoreOptionsAdditionalMenuItems?: ToolbarMoreOptionsAdditionalMenuItemsDirective;
/**
* @internal
*/
@ContentChild(ToolbarMoreOptionsAdditionalMenuItemsDirective)
set externalToolbarMoreOptionsAdditionalMenuItems(value: ToolbarMoreOptionsAdditionalMenuItemsDirective) {
this._externalToolbarMoreOptionsAdditionalMenuItems = value;
this.setupTemplates();
}
/**
* @internal
*/
get externalToolbarMoreOptionsAdditionalMenuItems(): ToolbarMoreOptionsAdditionalMenuItemsDirective | undefined {
return this._externalToolbarMoreOptionsAdditionalMenuItems;
}
/**
* @internal
*/
@ -498,6 +515,10 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
* @internal
*/
ovSettingsPanelGeneralAdditionalElementsTemplate: TemplateRef<any>;
/**
* @internal
*/
ovToolbarMoreOptionsAdditionalMenuItemsTemplate: TemplateRef<any>;
/**
* @internal
@ -808,7 +829,8 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
stream: this.externalStream,
preJoin: this.externalPreJoin,
layoutAdditionalElements: this.externalLayoutAdditionalElements,
settingsPanelGeneralAdditionalElements: this.externalSettingsPanelGeneralAdditionalElements
settingsPanelGeneralAdditionalElements: this.externalSettingsPanelGeneralAdditionalElements,
toolbarMoreOptionsAdditionalMenuItems: this.externalToolbarMoreOptionsAdditionalMenuItems
};
const defaultTemplates: DefaultTemplates = {
@ -886,6 +908,9 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
if (this.templateConfig.settingsPanelGeneralAdditionalElementsTemplate) {
assignIfChanged('ovSettingsPanelGeneralAdditionalElementsTemplate', this.templateConfig.settingsPanelGeneralAdditionalElementsTemplate);
}
if (this.templateConfig.toolbarMoreOptionsAdditionalMenuItemsTemplate) {
assignIfChanged('ovToolbarMoreOptionsAdditionalMenuItemsTemplate', this.templateConfig.toolbarMoreOptionsAdditionalMenuItemsTemplate);
}
}
/**

View File

@ -343,3 +343,38 @@ export class SettingsPanelGeneralAdditionalElementsDirective {
public container: ViewContainerRef
) {}
}
/**
* The ***ovToolbarMoreOptionsAdditionalMenuItems** directive allows you to inject custom HTML or Angular templates
* into the "more options" menu (three dots button) of the toolbar.
* This enables you to add custom menu items to extend the toolbar functionality.
*
* Usage example:
* ```html
* <ov-videoconference>
* <ng-container *ovToolbarMoreOptionsAdditionalMenuItems>
* <button mat-menu-item (click)="onCustomAction()">
* <mat-icon>star</mat-icon>
* <span>Custom Action</span>
* </button>
* <mat-divider></mat-divider>
* <button mat-menu-item (click)="onAnotherAction()">
* <mat-icon>info</mat-icon>
* <span>Another Action</span>
* </button>
* </ng-container>
* </ov-videoconference>
* ```
*
* @internal
*/
@Directive({
selector: '[ovToolbarMoreOptionsAdditionalMenuItems]',
standalone: false
})
export class ToolbarMoreOptionsAdditionalMenuItemsDirective {
constructor(
public template: TemplateRef<any>,
public container: ViewContainerRef
) {}
}

View File

@ -20,7 +20,8 @@ import {
ParticipantPanelParticipantBadgeDirective,
PreJoinDirective,
LeaveButtonDirective,
SettingsPanelGeneralAdditionalElementsDirective
SettingsPanelGeneralAdditionalElementsDirective,
ToolbarMoreOptionsAdditionalMenuItemsDirective
} from './internals.directive';
@NgModule({
@ -42,7 +43,8 @@ import {
ParticipantPanelAfterLocalParticipantDirective,
LayoutAdditionalElementsDirective,
ParticipantPanelParticipantBadgeDirective,
SettingsPanelGeneralAdditionalElementsDirective
SettingsPanelGeneralAdditionalElementsDirective,
ToolbarMoreOptionsAdditionalMenuItemsDirective
// BackgroundEffectsPanelDirective
],
exports: [
@ -63,7 +65,8 @@ import {
ParticipantPanelAfterLocalParticipantDirective,
LayoutAdditionalElementsDirective,
ParticipantPanelParticipantBadgeDirective,
SettingsPanelGeneralAdditionalElementsDirective
SettingsPanelGeneralAdditionalElementsDirective,
ToolbarMoreOptionsAdditionalMenuItemsDirective
// BackgroundEffectsPanelDirective
]
})

View File

@ -20,7 +20,8 @@ import {
ParticipantPanelAfterLocalParticipantDirective,
LayoutAdditionalElementsDirective,
LeaveButtonDirective,
SettingsPanelGeneralAdditionalElementsDirective
SettingsPanelGeneralAdditionalElementsDirective,
ToolbarMoreOptionsAdditionalMenuItemsDirective
} from '../../directives/template/internals.directive';
/**
@ -53,6 +54,9 @@ export interface TemplateConfiguration {
// Settings panel templates
settingsPanelGeneralAdditionalElementsTemplate?: TemplateRef<any>;
// Toolbar templates
toolbarMoreOptionsAdditionalMenuItemsTemplate?: TemplateRef<any>;
// PreJoin template
preJoinTemplate?: TemplateRef<any>;
}
@ -76,6 +80,7 @@ export interface ToolbarTemplateConfiguration {
toolbarAdditionalButtonsTemplate?: TemplateRef<any>;
toolbarAdditionalPanelButtonsTemplate?: TemplateRef<any>;
toolbarLeaveButtonTemplate?: TemplateRef<any>;
toolbarMoreOptionsAdditionalMenuItemsTemplate?: TemplateRef<any>;
}
/**
@ -131,6 +136,7 @@ export interface ExternalDirectives {
preJoin?: PreJoinDirective;
layoutAdditionalElements?: LayoutAdditionalElementsDirective;
settingsPanelGeneralAdditionalElements?: SettingsPanelGeneralAdditionalElementsDirective;
toolbarMoreOptionsAdditionalMenuItems?: ToolbarMoreOptionsAdditionalMenuItemsDirective;
}
/**
@ -218,6 +224,11 @@ export class TemplateManagerService {
config.settingsPanelGeneralAdditionalElementsTemplate = externalDirectives.settingsPanelGeneralAdditionalElements.template;
}
if (externalDirectives.toolbarMoreOptionsAdditionalMenuItems) {
this.log.v('Setting EXTERNAL TOOLBAR MORE OPTIONS ADDITIONAL MENU ITEMS');
config.toolbarMoreOptionsAdditionalMenuItemsTemplate = externalDirectives.toolbarMoreOptionsAdditionalMenuItems.template;
}
this.log.v('Template setup completed', config);
return config;
}
@ -378,14 +389,16 @@ export class TemplateManagerService {
setupToolbarTemplates(
externalAdditionalButtons?: ToolbarAdditionalButtonsDirective,
externalAdditionalPanelButtons?: ToolbarAdditionalPanelButtonsDirective,
externalLeaveButton?: LeaveButtonDirective
externalLeaveButton?: LeaveButtonDirective,
externalMoreOptionsAdditionalMenuItems?: ToolbarMoreOptionsAdditionalMenuItemsDirective
): ToolbarTemplateConfiguration {
this.log.v('Setting up toolbar templates...');
return {
toolbarAdditionalButtonsTemplate: externalAdditionalButtons?.template,
toolbarAdditionalPanelButtonsTemplate: externalAdditionalPanelButtons?.template,
toolbarLeaveButtonTemplate: externalLeaveButton?.template
toolbarLeaveButtonTemplate: externalLeaveButton?.template,
toolbarMoreOptionsAdditionalMenuItemsTemplate: externalMoreOptionsAdditionalMenuItems?.template
};
}