frontend: enhance hidden participants indicator with improved layout and description text
This commit is contained in:
parent
3a83efa668
commit
b4f482f9d7
@ -4,7 +4,11 @@
|
||||
<!-- Horizontal/Bar Layout -->
|
||||
<div class="horizontal-content">
|
||||
<div class="count-badge-horizontal">
|
||||
{{ displayText() }}
|
||||
@if (count() === 1) {
|
||||
<mat-icon>person</mat-icon>
|
||||
} @else {
|
||||
<mat-icon>people</mat-icon>
|
||||
}
|
||||
</div>
|
||||
<div class="text-horizontal">
|
||||
<span class="count-number">{{ count() }}</span>
|
||||
@ -14,27 +18,11 @@
|
||||
} @else {
|
||||
<!-- Vertical/Standard Layout -->
|
||||
<div class="vertical-content">
|
||||
<div class="header-section">
|
||||
<div class="icon-group">
|
||||
<svg class="participants-icon" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="title">Hidden Participants</div>
|
||||
</div>
|
||||
<div class="count-section">
|
||||
<div class="count-badge-vertical">
|
||||
{{ displayText() }}
|
||||
</div>
|
||||
<div class="count-label">
|
||||
{{ count() }} {{ count() === 1 ? 'participant' : 'participants' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-section">
|
||||
<svg class="info-icon" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
|
||||
</svg>
|
||||
<div class="info-text">Not currently visible in the layout</div>
|
||||
<div class="count-label"> {{ count() }} {{ descriptionText() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -11,6 +11,9 @@
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
container-type: size;
|
||||
container-name: hidden-indicator;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
@ -71,11 +74,11 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
gap: 16px;
|
||||
padding: clamp(4px, 3cqh, 20px) clamp(4px, 2cqw, 16px);
|
||||
gap: clamp(4px, 2cqh, 16px);
|
||||
}
|
||||
|
||||
.header-section {
|
||||
@ -111,7 +114,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: clamp(4px, 1.5cqh, 12px);
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
|
||||
@ -119,23 +122,28 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
// width: clamp(32px, 40cqw, 80px);
|
||||
height: clamp(44px, 40cqh, 80px);
|
||||
aspect-ratio: 1 / 1;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 50%;
|
||||
font-size: 36px;
|
||||
font-size: clamp(22px, 6cqh, 36px);
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.5);
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.count-label {
|
||||
font-size: 15px;
|
||||
font-size: clamp(14px, 2.5cqh, 15px);
|
||||
font-weight: 500;
|
||||
color: #e0e0e0;
|
||||
text-align: center;
|
||||
letter-spacing: 0.3px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,18 +172,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.5);
|
||||
|
||||
@container hidden-indicator (max-height: 100px) {
|
||||
.count-label {
|
||||
display: none;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.7);
|
||||
|
||||
.count-section {
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@container hidden-indicator (max-width: 120px) {
|
||||
.count-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.count-section {
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Para espacios extra pequeños, optimizar padding y sombras
|
||||
@container hidden-indicator (max-width: 100px) {
|
||||
.vertical-content {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.count-badge-vertical {
|
||||
box-shadow: 0 2px 6px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
// Responsive adjustments
|
||||
@media (max-width: 768px), (max-height: 500px) {
|
||||
.count-badge-horizontal {
|
||||
min-width: 40px;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, computed, input } from '@angular/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
||||
/**
|
||||
* Component that displays an indicator for participants not visible in the current layout.
|
||||
@ -10,7 +11,7 @@ import { Component, computed, input } from '@angular/core';
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ov-hidden-participants-indicator',
|
||||
imports: [CommonModule],
|
||||
imports: [CommonModule, MatIconModule],
|
||||
templateUrl: './hidden-participants-indicator.component.html',
|
||||
styleUrl: './hidden-participants-indicator.component.scss'
|
||||
})
|
||||
@ -30,11 +31,10 @@ export class HiddenParticipantsIndicatorComponent {
|
||||
* Get the display text for the hidden participants count
|
||||
*/
|
||||
protected displayText = computed(() => {
|
||||
if (this.count() === 0) return '';
|
||||
return `+${this.count()}`;
|
||||
});
|
||||
|
||||
protected descriptionText = computed(() => {
|
||||
return this.count() === 1 ? 'participant not visible' : 'participants not visible';
|
||||
return this.count() === 1 ? 'participant not currently visible' : 'participants not currently visible';
|
||||
});
|
||||
}
|
||||
|
||||
@ -15,8 +15,16 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
} @else if (isSmartMosaicActive() && hiddenParticipantsCount() > 0) {
|
||||
<ng-container *ovLayoutAdditionalElements>
|
||||
<div [ngClass]="{ 'OV_top-bar': showTopBarHiddenParticipantsIndicator() }">
|
||||
<!-- Use bottom slot to position indicator at the end -->
|
||||
<ng-container *ovLayoutAdditionalElements="'bottom'">
|
||||
<div
|
||||
[ngClass]="{
|
||||
'OV_top-bar': showTopBarHiddenParticipantsIndicator(),
|
||||
OV_last: !showTopBarHiddenParticipantsIndicator(),
|
||||
'participant-indicator-container': true
|
||||
}"
|
||||
(click)="toggleParticipantsPanel()"
|
||||
>
|
||||
<ov-hidden-participants-indicator
|
||||
[count]="hiddenParticipantsCount()"
|
||||
[mode]="showTopBarHiddenParticipantsIndicator() ? 'topbar' : 'standard'"
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
@use '../../../../../../../src/assets/styles/design-tokens';
|
||||
|
||||
|
||||
.remote-participant {
|
||||
height: -webkit-fill-available;
|
||||
height: -moz-available;
|
||||
@ -80,6 +79,9 @@
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.participant-indicator-container {
|
||||
cursor: pointer;
|
||||
}
|
||||
.OV_top-bar {
|
||||
box-sizing: border-box;
|
||||
padding: 4px 4px 2px 4px !important;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, computed, effect, inject, signal, untracked } from '@angular/core';
|
||||
import { ILogger, LoggerService, OpenViduComponentsUiModule, ParticipantModel } from 'openvidu-components-angular';
|
||||
import { ILogger, LoggerService, OpenViduComponentsUiModule, PanelService, PanelType, ParticipantModel } from 'openvidu-components-angular';
|
||||
import { HiddenParticipantsIndicatorComponent, ShareMeetingLinkComponent } from '../../../components';
|
||||
import { CustomParticipantModel } from '../../../models';
|
||||
import { MeetingContextService, MeetingService, MeetLayoutService } from '../../../services';
|
||||
@ -21,6 +21,7 @@ export class MeetingCustomLayoutComponent {
|
||||
protected readonly layoutService = inject(MeetLayoutService);
|
||||
protected readonly meetingContextService = inject(MeetingContextService);
|
||||
protected readonly meetingService = inject(MeetingService);
|
||||
protected readonly panelService = inject(PanelService);
|
||||
protected readonly linkOverlayConfig = {
|
||||
title: 'Start collaborating',
|
||||
subtitle: 'Share this link to bring others into the meeting',
|
||||
@ -84,6 +85,10 @@ export class MeetingCustomLayoutComponent {
|
||||
return this.isLayoutSwitchingAllowed() && this.layoutService.isSmartMosaicEnabled();
|
||||
}
|
||||
|
||||
protected toggleParticipantsPanel(): void {
|
||||
this.panelService.togglePanel(PanelType.PARTICIPANTS);
|
||||
}
|
||||
|
||||
private setupVisibleParticipantsUpdate(): void {
|
||||
effect(
|
||||
() => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user