mirror of https://github.com/OpenVidu/openvidu.git
ov-components: enhance toolbar media buttons for responsive design and improved visibility
parent
677a9129a2
commit
2a9f3a62fa
|
@ -1,11 +1,12 @@
|
|||
<!-- Camera button -->
|
||||
@if (showCameraButton) {
|
||||
@if (showCameraButtonDirect()) {
|
||||
<button
|
||||
id="camera-btn"
|
||||
mat-icon-button
|
||||
(click)="onCameraToggle()"
|
||||
[disabled]="isConnectionLost || !hasVideoDevices || cameraMuteChanging"
|
||||
[class.disabled]="!isCameraEnabled"
|
||||
[class.mobile-btn]="isMobileView()"
|
||||
[matTooltip]="isCameraEnabled ? ('TOOLBAR.STOP_VIDEO' | translate) : ('TOOLBAR.START_VIDEO' | translate)"
|
||||
[matTooltipDisabled]="!hasVideoDevices"
|
||||
>
|
||||
|
@ -16,13 +17,14 @@
|
|||
}
|
||||
|
||||
<!-- Microphone button -->
|
||||
@if (showMicrophoneButton) {
|
||||
@if (showMicrophoneButtonDirect()) {
|
||||
<button
|
||||
id="mic-btn"
|
||||
mat-icon-button
|
||||
(click)="onMicrophoneToggle()"
|
||||
[disabled]="isConnectionLost || !hasAudioDevices || microphoneMuteChanging"
|
||||
[class.disabled]="!isMicrophoneEnabled"
|
||||
[class.mobile-btn]="isMobileView()"
|
||||
[matTooltip]="isMicrophoneEnabled ? ('TOOLBAR.MUTE_AUDIO' | translate) : ('TOOLBAR.UNMUTE_AUDIO' | translate)"
|
||||
[matTooltipDisabled]="!hasAudioDevices"
|
||||
>
|
||||
|
@ -32,14 +34,15 @@
|
|||
</button>
|
||||
}
|
||||
|
||||
<!-- Enable Screenshare button -->
|
||||
@if (!isMinimal && showScreenshareButton) {
|
||||
<!-- Screenshare button - Only visible on tablet+ or when active -->
|
||||
@if (showScreenshareButtonDirect()) {
|
||||
<button
|
||||
mat-icon-button
|
||||
id="screenshare-btn"
|
||||
[matMenuTriggerFor]="isScreenShareEnabled ? screenshareMenu : null"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isScreenShareEnabled"
|
||||
[class.mobile-btn]="isMobileView()"
|
||||
matTooltip="{{ isScreenShareEnabled ? ('TOOLBAR.DISABLE_SCREEN' | translate) : ('TOOLBAR.ENABLE_SCREEN' | translate) }}"
|
||||
(click)="!isScreenShareEnabled && onScreenShareToggle()"
|
||||
>
|
||||
|
@ -65,17 +68,42 @@
|
|||
</ng-container>
|
||||
|
||||
<!-- More options button -->
|
||||
@if (!isMinimal && showMoreOptionsButton) {
|
||||
@if (showMoreOptionsButtonDirect()) {
|
||||
<button
|
||||
mat-icon-button
|
||||
id="more-options-btn"
|
||||
[matMenuTriggerFor]="settingsMenu"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.mobile-btn]="isMobileView()"
|
||||
[matBadge]="hasActiveFeatures() && isMobileView() ? '!' : ''"
|
||||
matBadgeColor="accent"
|
||||
matBadgeSize="small"
|
||||
[matBadgeHidden]="!hasActiveFeatures() || !isMobileView()"
|
||||
matTooltip="{{ 'TOOLBAR.MORE_OPTIONS' | translate }}"
|
||||
>
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #settingsMenu="matMenu" id="more-options-menu">
|
||||
|
||||
<!-- Dynamic mobile buttons -->
|
||||
@for (button of buttonsInMoreOptions(); track button.key) {
|
||||
@if (button.show) {
|
||||
<button
|
||||
mat-menu-item
|
||||
[id]="button.key + '-btn'"
|
||||
[disabled]="button.disabled"
|
||||
(click)="button.action()"
|
||||
>
|
||||
<mat-icon [color]="button.color || ''">{{ button.icon }}</mat-icon>
|
||||
<span>{{ button.label | translate }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
||||
<!-- Divider if there are mobile buttons -->
|
||||
@if (buttonsInMoreOptions().length > 0) {
|
||||
<mat-divider class="divider"></mat-divider>
|
||||
}
|
||||
<!-- Fullscreen button -->
|
||||
@if (showFullscreenButton) {
|
||||
<button mat-menu-item id="fullscreen-btn" (click)="onFullscreenToggle()">
|
||||
|
@ -181,8 +209,14 @@
|
|||
</ng-container>
|
||||
|
||||
<!-- Leave session button -->
|
||||
@if (showLeaveButton) {
|
||||
<button mat-icon-button (click)="onLeaveClick()" id="leave-btn" matTooltip="{{ 'TOOLBAR.LEAVE' | translate }}">
|
||||
@if (showLeaveButtonDirect()) {
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="onLeaveClick()"
|
||||
id="leave-btn"
|
||||
[class.mobile-btn]="isMobileView()"
|
||||
matTooltip="{{ 'TOOLBAR.LEAVE' | translate }}"
|
||||
>
|
||||
<mat-icon>call_end</mat-icon>
|
||||
</button>
|
||||
}
|
||||
|
|
|
@ -2,12 +2,19 @@
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
|
||||
.mat-mdc-icon-button {
|
||||
--mat-mdc-button-persistent-ripple-color: transparent !important;
|
||||
--mat-mdc-button-ripple-color: transparent !important;
|
||||
}
|
||||
|
||||
// Responsive container adjustments
|
||||
@media (max-width: 767px) {
|
||||
gap: 1px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
@ -28,9 +35,19 @@
|
|||
background-color: var(--ov-primary-action-color);
|
||||
color: var(--ov-secondary-action-color);
|
||||
margin: 6px;
|
||||
}
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
#screenshare-menu {
|
||||
// Mobile responsive styles
|
||||
&.mobile-btn {
|
||||
margin: 3px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
text-align: justify;
|
||||
|
||||
mat-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#disable-screen-button > mat-icon {
|
||||
|
@ -47,6 +64,13 @@
|
|||
border-radius: var(--ov-leave-button-radius) !important;
|
||||
width: 65px !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
&.mobile-btn {
|
||||
width: 56px !important;
|
||||
height: 36px !important;
|
||||
margin: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-mdc-icon-button[disabled] {
|
||||
|
@ -94,6 +118,23 @@
|
|||
border-radius: var(--ov-surface-radius) !important;
|
||||
background-color: var(--ov-surface-color) !important;
|
||||
box-shadow: var(--ov-border-shadow) !important;
|
||||
|
||||
// Mobile menu improvements
|
||||
@media (max-width: 767px) {
|
||||
min-width: 200px;
|
||||
max-width: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile badge styling
|
||||
::ng-deep .mat-badge-content {
|
||||
background-color: var(--ov-accent-action-color) !important;
|
||||
color: white !important;
|
||||
font-size: 10px !important;
|
||||
font-weight: bold !important;
|
||||
width: 16px !important;
|
||||
height: 16px !important;
|
||||
line-height: 16px !important;
|
||||
}
|
||||
|
||||
/* Styles for XS screens */
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { Component, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
|
||||
import { Component, 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';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-toolbar-media-buttons',
|
||||
|
@ -69,6 +70,99 @@ export class ToolbarMediaButtonsComponent {
|
|||
_recordingStatus = RecordingStatus;
|
||||
_broadcastingStatus = BroadcastingStatus;
|
||||
|
||||
// Viewport service for responsive behavior
|
||||
private viewportService = inject(ViewportService);
|
||||
|
||||
// Computed properties for responsive button grouping
|
||||
readonly isMobileView = computed(() => this.viewportService.isMobile());
|
||||
readonly isTabletView = computed(() => this.viewportService.isTablet());
|
||||
readonly isDesktopView = computed(() => this.viewportService.isDesktop());
|
||||
|
||||
// Essential buttons that always stay visible
|
||||
readonly showCameraButtonDirect = computed(() =>
|
||||
this.showCameraButton && !this.isMinimal
|
||||
);
|
||||
|
||||
readonly showMicrophoneButtonDirect = computed(() =>
|
||||
this.showMicrophoneButton && !this.isMinimal
|
||||
);
|
||||
|
||||
// Screenshare button - visible on tablet+ or when already active
|
||||
readonly showScreenshareButtonDirect = computed(() =>
|
||||
this.showScreenshareButton &&
|
||||
!this.isMinimal &&
|
||||
(!this.isMobileView() || this.isScreenShareEnabled)
|
||||
);
|
||||
|
||||
// More options button - always visible when not minimal
|
||||
readonly showMoreOptionsButtonDirect = computed(() =>
|
||||
this.showMoreOptionsButton && !this.isMinimal
|
||||
);
|
||||
|
||||
// Leave button - always visible
|
||||
readonly showLeaveButtonDirect = computed(() =>
|
||||
this.showLeaveButton
|
||||
);
|
||||
|
||||
// Buttons that should be moved to "More Options" on mobile
|
||||
readonly buttonsInMoreOptions = computed(() => {
|
||||
const buttons: Array<{
|
||||
key: string;
|
||||
show: boolean;
|
||||
label: string;
|
||||
icon: string;
|
||||
action: () => void;
|
||||
disabled?: boolean;
|
||||
active?: boolean;
|
||||
color?: string;
|
||||
}> = [];
|
||||
|
||||
const isMobile = this.isMobileView();
|
||||
|
||||
// On mobile, screenshare goes to more options when not active
|
||||
if (isMobile && this.showScreenshareButton && !this.isScreenShareEnabled) {
|
||||
buttons.push({
|
||||
key: 'screenshare',
|
||||
show: true,
|
||||
label: 'TOOLBAR.ENABLE_SCREEN',
|
||||
icon: 'screen_share',
|
||||
action: () => this.onScreenShareToggle(),
|
||||
disabled: this.isConnectionLost
|
||||
});
|
||||
}
|
||||
|
||||
// Replace screenshare option when active on mobile
|
||||
if (isMobile && this.showScreenshareButton && this.isScreenShareEnabled) {
|
||||
buttons.push({
|
||||
key: 'screenshare-replace',
|
||||
show: true,
|
||||
label: 'STREAM.REPLACE_SCREEN',
|
||||
icon: 'picture_in_picture',
|
||||
action: () => this.onScreenTrackReplace(),
|
||||
disabled: this.isConnectionLost
|
||||
});
|
||||
|
||||
buttons.push({
|
||||
key: 'screenshare-stop',
|
||||
show: true,
|
||||
label: 'TOOLBAR.DISABLE_SCREEN',
|
||||
icon: 'stop_screen_share',
|
||||
action: () => this.onScreenShareToggle(),
|
||||
disabled: this.isConnectionLost,
|
||||
color: 'warn'
|
||||
});
|
||||
}
|
||||
|
||||
return buttons;
|
||||
});
|
||||
|
||||
// Check if there are active features that should show a badge on More Options
|
||||
readonly hasActiveFeatures = computed(() =>
|
||||
this.isScreenShareEnabled ||
|
||||
this.recordingStatus === this._recordingStatus.STARTED ||
|
||||
this.broadcastingStatus === this._broadcastingStatus.STARTED
|
||||
);
|
||||
|
||||
// Media button outputs
|
||||
@Output() cameraToggled = new EventEmitter<void>();
|
||||
@Output() microphoneToggled = new EventEmitter<void>();
|
||||
|
|
Loading…
Reference in New Issue