From 2a9f3a62fab8d5f79b1c706a73f02982f0a306c8 Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Thu, 18 Sep 2025 12:14:07 +0200 Subject: [PATCH] ov-components: enhance toolbar media buttons for responsive design and improved visibility --- .../toolbar-media-buttons.component.html | 48 ++++++++-- .../toolbar-media-buttons.component.scss | 45 ++++++++- .../toolbar-media-buttons.component.ts | 96 ++++++++++++++++++- 3 files changed, 179 insertions(+), 10 deletions(-) diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.html index 02b926ee..70f1f817 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.html @@ -1,11 +1,12 @@ -@if (showCameraButton) { +@if (showCameraButtonDirect()) { } - -@if (!isMinimal && showScreenshareButton) { + +@if (showScreenshareButtonDirect()) { + + + @for (button of buttonsInMoreOptions(); track button.key) { + @if (button.show) { + + } + } + + + @if (buttonsInMoreOptions().length > 0) { + + } @if (showFullscreenButton) { } diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.scss index dbb5f9b0..20d425ae 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.scss +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.scss @@ -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 */ diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.ts index 05ebfa2b..4fa97038 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component.ts @@ -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(); @Output() microphoneToggled = new EventEmitter();