ov-components: enhance toolbar media buttons for responsive design and improved visibility

master
Carlos Santos 2025-09-18 12:14:07 +02:00
parent 677a9129a2
commit 2a9f3a62fa
3 changed files with 179 additions and 10 deletions

View File

@ -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>
}

View File

@ -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 */

View File

@ -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>();