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
|
// More options button - always visible when not minimal
|
||||||
readonly showMoreOptionsButtonDirect = computed(() => this.showMoreOptionsButton && !this.isMinimal);
|
readonly showMoreOptionsButtonDirect = computed(() => this.showMoreOptionsButton && !this.isMinimal);
|
||||||
|
|
||||||
// Leave button - always visible
|
// Leave button
|
||||||
readonly showLeaveButtonDirect = computed(() => this.showLeaveButton);
|
readonly showLeaveButtonDirect = computed(() => this.showLeaveButton);
|
||||||
|
|
||||||
// Check if there are active features that should show a badge on More Options
|
// Check if there are active features that should show a badge on More Options
|
||||||
|
|
|
@ -1,55 +1,128 @@
|
||||||
<!-- Default activities button -->
|
<!-- Responsive container: show individual buttons on larger screens, collapsed menu on smaller screens -->
|
||||||
<button
|
<ng-container *ngIf="shouldShowCollapsed; else uncollapsedButtons">
|
||||||
mat-icon-button
|
<div class="collapsed-menu-container" *ngIf="visibleButtonsCount > 0">
|
||||||
id="activities-panel-btn"
|
<button
|
||||||
class="panel-button"
|
mat-icon-button
|
||||||
*ngIf="!isMinimal && showActivitiesPanelButton"
|
class="fab-menu-trigger"
|
||||||
matTooltip="{{ 'TOOLBAR.ACTIVITIES' | translate }}"
|
[matMenuTriggerFor]="panelsMenu"
|
||||||
(click)="onToggleActivities()"
|
[disabled]="isConnectionLost"
|
||||||
[disabled]="isConnectionLost"
|
[class.active-btn]="isAnyPanelOpened"
|
||||||
[class.active-btn]="isActivitiesOpened"
|
matTooltip="{{ 'TOOLBAR.PANELS' | translate }}"
|
||||||
>
|
#menuTrigger="matMenuTrigger"
|
||||||
<mat-icon>category</mat-icon>
|
>
|
||||||
</button>
|
<mat-icon class="material-symbols-outlined" [class.rotated]="menuTrigger.menuOpen">keyboard_arrow_up</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- Default participants button -->
|
<!-- Vertical panels menu -->
|
||||||
<button
|
<mat-menu #panelsMenu="matMenu" class="panels-menu">
|
||||||
mat-icon-button
|
<!-- Activities menu item -->
|
||||||
class="panel-button"
|
<button
|
||||||
id="participants-panel-btn"
|
mat-menu-item
|
||||||
*ngIf="!isMinimal && showParticipantsPanelButton"
|
class="panel-menu-item"
|
||||||
matTooltip="{{ 'TOOLBAR.PARTICIPANTS' | translate }}"
|
*ngIf="!isMinimal && showActivitiesPanelButton"
|
||||||
(click)="onToggleParticipants()"
|
(click)="onToggleActivities()"
|
||||||
[disabled]="isConnectionLost"
|
[disabled]="isConnectionLost"
|
||||||
[class.active-btn]="isParticipantsOpened"
|
[class.active-menu-item]="isActivitiesOpened"
|
||||||
>
|
>
|
||||||
<mat-icon>people</mat-icon>
|
<mat-icon>category</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Default chat button -->
|
<!-- Participants menu item -->
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-menu-item
|
||||||
class="panel-button"
|
class="panel-menu-item"
|
||||||
id="chat-panel-btn"
|
*ngIf="!isMinimal && showParticipantsPanelButton"
|
||||||
*ngIf="!isMinimal && showChatPanelButton"
|
(click)="onToggleParticipants()"
|
||||||
matTooltip="{{ 'TOOLBAR.CHAT' | translate }}"
|
[disabled]="isConnectionLost"
|
||||||
(click)="onToggleChat()"
|
[class.active-menu-item]="isParticipantsOpened"
|
||||||
[disabled]="isConnectionLost"
|
>
|
||||||
[class.active-btn]="isChatOpened"
|
<mat-icon>people</mat-icon>
|
||||||
>
|
</button>
|
||||||
<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 -->
|
<!-- Chat menu item -->
|
||||||
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate">
|
<button
|
||||||
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
|
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>
|
</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 {
|
::ng-deep .active-btn {
|
||||||
background-color: var(--ov-accent-action-color) !important;
|
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 { Component, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
|
||||||
|
import { ViewportService } from '../../../services/viewport/viewport.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ov-toolbar-panel-buttons',
|
selector: 'ov-toolbar-panel-buttons',
|
||||||
|
@ -28,6 +29,27 @@ export class ToolbarPanelButtonsComponent {
|
||||||
@Output() toggleParticipantsPanel: EventEmitter<void> = new EventEmitter();
|
@Output() toggleParticipantsPanel: EventEmitter<void> = new EventEmitter();
|
||||||
@Output() toggleChatPanel: 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
|
// Local methods that emit events
|
||||||
onToggleActivities(expand?: string) {
|
onToggleActivities(expand?: string) {
|
||||||
this.toggleActivitiesPanel.emit(expand);
|
this.toggleActivitiesPanel.emit(expand);
|
||||||
|
|
|
@ -45,6 +45,9 @@
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
::ng-deep .menu-buttons-container:has(.collapsed-menu-container) {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#info-container > div {
|
#info-container > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
Loading…
Reference in New Issue