mirror of https://github.com/OpenVidu/openvidu.git
ov-components: enhance toolbar panel buttons with responsive design
parent
d223acefcf
commit
6da901e7bb
|
@ -96,7 +96,7 @@ export class ToolbarMediaButtonsComponent {
|
|||
// More options button - always visible when not minimal
|
||||
readonly showMoreOptionsButtonDirect = computed(() => this.showMoreOptionsButton && !this.isMinimal);
|
||||
|
||||
// Leave button - always visible
|
||||
// Leave button
|
||||
readonly showLeaveButtonDirect = computed(() => this.showLeaveButton);
|
||||
|
||||
// Check if there are active features that should show a badge on More Options
|
||||
|
|
|
@ -1,55 +1,128 @@
|
|||
<!-- Default activities button -->
|
||||
<button
|
||||
mat-icon-button
|
||||
id="activities-panel-btn"
|
||||
class="panel-button"
|
||||
*ngIf="!isMinimal && showActivitiesPanelButton"
|
||||
matTooltip="{{ 'TOOLBAR.ACTIVITIES' | translate }}"
|
||||
(click)="onToggleActivities()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isActivitiesOpened"
|
||||
>
|
||||
<mat-icon>category</mat-icon>
|
||||
</button>
|
||||
<!-- 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">
|
||||
<button
|
||||
mat-icon-button
|
||||
class="fab-menu-trigger"
|
||||
[matMenuTriggerFor]="panelsMenu"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isAnyPanelOpened"
|
||||
matTooltip="{{ 'TOOLBAR.PANELS' | translate }}"
|
||||
#menuTrigger="matMenuTrigger"
|
||||
>
|
||||
<mat-icon class="material-symbols-outlined" [class.rotated]="menuTrigger.menuOpen">keyboard_arrow_up</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- Default participants button -->
|
||||
<button
|
||||
mat-icon-button
|
||||
class="panel-button"
|
||||
id="participants-panel-btn"
|
||||
*ngIf="!isMinimal && showParticipantsPanelButton"
|
||||
matTooltip="{{ 'TOOLBAR.PARTICIPANTS' | translate }}"
|
||||
(click)="onToggleParticipants()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isParticipantsOpened"
|
||||
>
|
||||
<mat-icon>people</mat-icon>
|
||||
</button>
|
||||
<!-- Vertical panels menu -->
|
||||
<mat-menu #panelsMenu="matMenu" class="panels-menu">
|
||||
<!-- Activities menu item -->
|
||||
<button
|
||||
mat-menu-item
|
||||
class="panel-menu-item"
|
||||
*ngIf="!isMinimal && showActivitiesPanelButton"
|
||||
(click)="onToggleActivities()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-menu-item]="isActivitiesOpened"
|
||||
>
|
||||
<mat-icon>category</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- Default chat button -->
|
||||
<button
|
||||
mat-icon-button
|
||||
class="panel-button"
|
||||
id="chat-panel-btn"
|
||||
*ngIf="!isMinimal && showChatPanelButton"
|
||||
matTooltip="{{ 'TOOLBAR.CHAT' | translate }}"
|
||||
(click)="onToggleChat()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isChatOpened"
|
||||
>
|
||||
<mat-icon
|
||||
matBadge="{{ unreadMessages }}"
|
||||
[matBadgeHidden]="unreadMessages === 0"
|
||||
matBadgePosition="above before"
|
||||
matBadgeSize="small"
|
||||
matBadgeColor="accent"
|
||||
aria-hidden="false"
|
||||
>
|
||||
chat
|
||||
</mat-icon>
|
||||
</button>
|
||||
<!-- Participants menu item -->
|
||||
<button
|
||||
mat-menu-item
|
||||
class="panel-menu-item"
|
||||
*ngIf="!isMinimal && showParticipantsPanelButton"
|
||||
(click)="onToggleParticipants()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-menu-item]="isParticipantsOpened"
|
||||
>
|
||||
<mat-icon>people</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- External additional panel buttons -->
|
||||
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate">
|
||||
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
|
||||
<!-- Chat menu item -->
|
||||
<button
|
||||
mat-menu-item
|
||||
class="panel-menu-item"
|
||||
*ngIf="!isMinimal && showChatPanelButton"
|
||||
(click)="onToggleChat()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-menu-item]="isChatOpened"
|
||||
>
|
||||
<mat-icon
|
||||
matBadge="{{ unreadMessages }}"
|
||||
[matBadgeHidden]="unreadMessages === 0"
|
||||
matBadgePosition="above before"
|
||||
matBadgeSize="small"
|
||||
matBadgeColor="accent"
|
||||
aria-hidden="false"
|
||||
>
|
||||
chat
|
||||
</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- External additional panel buttons in menu -->
|
||||
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate">
|
||||
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
|
||||
</ng-container>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- Collapsed menu template for small screens -->
|
||||
<ng-template #uncollapsedButtons>
|
||||
<!-- Default activities button -->
|
||||
<button
|
||||
mat-icon-button
|
||||
id="activities-panel-btn"
|
||||
class="panel-button"
|
||||
*ngIf="!isMinimal && showActivitiesPanelButton"
|
||||
matTooltip="{{ 'TOOLBAR.ACTIVITIES' | translate }}"
|
||||
(click)="onToggleActivities()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isActivitiesOpened"
|
||||
>
|
||||
<mat-icon>category</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- Default participants button -->
|
||||
<button
|
||||
mat-icon-button
|
||||
class="panel-button"
|
||||
id="participants-panel-btn"
|
||||
*ngIf="!isMinimal && showParticipantsPanelButton"
|
||||
matTooltip="{{ 'TOOLBAR.PARTICIPANTS' | translate }}"
|
||||
(click)="onToggleParticipants()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isParticipantsOpened"
|
||||
>
|
||||
<mat-icon>people</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- Default chat button -->
|
||||
<button
|
||||
mat-icon-button
|
||||
class="panel-button"
|
||||
id="chat-panel-btn"
|
||||
*ngIf="!isMinimal && showChatPanelButton"
|
||||
matTooltip="{{ 'TOOLBAR.CHAT' | translate }}"
|
||||
(click)="onToggleChat()"
|
||||
[disabled]="isConnectionLost"
|
||||
[class.active-btn]="isChatOpened"
|
||||
>
|
||||
<mat-icon
|
||||
matBadge="{{ unreadMessages }}"
|
||||
[matBadgeHidden]="unreadMessages === 0"
|
||||
matBadgePosition="above before"
|
||||
matBadgeSize="small"
|
||||
matBadgeColor="accent"
|
||||
aria-hidden="false"
|
||||
>
|
||||
chat
|
||||
</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- External additional panel buttons -->
|
||||
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate">
|
||||
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
|
|
@ -8,4 +8,136 @@
|
|||
::ng-deep .active-btn {
|
||||
background-color: var(--ov-accent-action-color) !important;
|
||||
}
|
||||
|
||||
// Collapsed menu styles for small screens
|
||||
.collapsed-menu-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.fab-menu-trigger:has(.rotated) {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
// FAB-style trigger button
|
||||
.fab-menu-trigger {
|
||||
color: var(--ov-secondary-action-color);
|
||||
background-color: var(--ov-primary-action-color);
|
||||
transition: all 0.2s ease-in-out;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
padding: 8px 0;
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
mat-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global styles for the collapsible panels menu
|
||||
|
||||
.panels-menu,
|
||||
::ng-deep .panels-menu,
|
||||
.panels-menu .mat-mdc-menu-panel,
|
||||
.panels-menu .mat-mdc-menu-content,
|
||||
::ng-deep .panels-menu .mat-mdc-menu-panel,
|
||||
::ng-deep .panels-menu .mat-mdc-menu-content {
|
||||
background-color: transparent !important;
|
||||
box-shadow: none !important;
|
||||
border: none !important;
|
||||
min-width: 55px !important;
|
||||
width: 55px !important;
|
||||
max-width: 55px !important;
|
||||
padding: 4px 0 !important;
|
||||
justify-content: flex-end !important;
|
||||
display: grid !important;
|
||||
}
|
||||
|
||||
.panels-menu,
|
||||
::ng-deep .panels-menu {
|
||||
.panel-menu-item {
|
||||
background-color: var(--ov-primary-action-color) !important;
|
||||
color: var(--ov-secondary-action-color) !important;
|
||||
border-radius: var(--ov-toolbar-buttons-radius) !important;
|
||||
min-height: 36px !important;
|
||||
padding: 8px !important;
|
||||
margin: 5px 0;
|
||||
height: 36px !important;
|
||||
width: 36px !important;
|
||||
display: flex !important;
|
||||
box-shadow: 0 2px 4px color-mix(in srgb, var(--ov-secondary-action-color) 100%, transparent);
|
||||
transition:
|
||||
box-shadow 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
|
||||
&.active-menu-item {
|
||||
background-color: var(--ov-accent-action-color) !important;
|
||||
color: var(--ov-text-primary-color) !important;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
font-size: 20px !important;
|
||||
width: 20px !important;
|
||||
height: 20px !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
// 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;
|
||||
color: var(--ov-text-primary-color) !important;
|
||||
font-size: 10px !important;
|
||||
min-width: 14px !important;
|
||||
height: 14px !important;
|
||||
line-height: 14px !important;
|
||||
border-radius: 7px !important;
|
||||
top: -2px !important;
|
||||
right: -2px !important;
|
||||
}
|
||||
|
||||
// Remove default mat-menu-item styles
|
||||
.mat-mdc-menu-item-text {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// Remove focus outline
|
||||
&:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
// Remove ripple effect
|
||||
.mat-mdc-menu-ripple {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom scrollbar for menu if needed
|
||||
.mat-mdc-menu-content {
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(var(--ov-secondary-action-color-rgb), 0.3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
|
||||
import { ViewportService } from '../../../services/viewport/viewport.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ov-toolbar-panel-buttons',
|
||||
|
@ -28,6 +29,27 @@ export class ToolbarPanelButtonsComponent {
|
|||
@Output() toggleParticipantsPanel: EventEmitter<void> = new EventEmitter();
|
||||
@Output() toggleChatPanel: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
constructor(public viewportService: ViewportService) {}
|
||||
|
||||
// Computed property to determine if we should show collapsed menu
|
||||
get shouldShowCollapsed(): boolean {
|
||||
return this.viewportService.isMobileView() || this.viewportService.isTabletDown();
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
::ng-deep .menu-buttons-container:has(.collapsed-menu-container) {
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
#info-container > div {
|
||||
display: flex;
|
||||
|
|
Loading…
Reference in New Issue