From 8fbe8fb716bdb470d515732d693a3a93db5e4027 Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Tue, 23 Dec 2025 12:49:13 +0100 Subject: [PATCH] frontend: enhance hidden participants indicator to display names of hidden participants in both horizontal and vertical layouts --- ...dden-participants-indicator.component.html | 55 ++++- ...dden-participants-indicator.component.scss | 222 ++++++++++-------- ...hidden-participants-indicator.component.ts | 30 ++- .../meeting-custom-layout.component.html | 1 + .../meeting-custom-layout.component.ts | 7 + 5 files changed, 198 insertions(+), 117 deletions(-) diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.html b/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.html index b0b1fbd4..0ab05cb7 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.html +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.html @@ -3,26 +3,55 @@ @if (isTopBarMode()) {
-
- @if (count() === 1) { - person - } @else { - people - } -
-
- {{ count() }} - {{ descriptionText() }} +
+
+ + @if (count() === 1) { + person + } @else { + people + } + + +{{ count() }} +
+
+ +{{ count() }} + {{ descriptionText() }} +
+ @if (formattedParticipantNames()) { +
+
+ visibility_off + {{ formattedParticipantNames() }} +
+
+ }
} @else {
-
- {{ displayText() }} +
+ + @if (count() === 1) { + person + } @else { + people + } + + +{{ count() }}
-
{{ count() }} {{ descriptionText() }}
+
+ +{{ count() }} + {{ descriptionText() }} +
+ @if (formattedParticipantNames()) { +
+ visibility_off + {{ formattedParticipantNames() }} +
+ }
} diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.scss b/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.scss index 59353890..b8c725fa 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.scss +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.scss @@ -25,14 +25,21 @@ .horizontal-content { display: flex; align-items: center; - justify-content: center; - gap: 16px; + justify-content: space-between; + gap: 24px; padding: 0 24px; width: 100%; height: 100%; } -.count-badge-horizontal { +.left-section { + display: flex; + align-items: center; + gap: 16px; + flex-shrink: 0; +} + +.count-badge { display: flex; align-items: center; justify-content: center; @@ -45,9 +52,20 @@ color: #ffffff; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4); flex-shrink: 0; + position: relative; + + .badge-icon { + display: block; + } + + .badge-number { + display: none; + position: absolute; + font-size: inherit; + } } -.text-horizontal { +.count-info { display: flex; align-items: baseline; gap: 8px; @@ -67,6 +85,43 @@ } } +.right-section { + display: flex; + align-items: center; + justify-content: flex-end; + flex: 1; + min-width: 0; // Allow flex item to shrink below content size +} + +.participant-names { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + background-color: rgba(102, 126, 234, 0.15); + border-radius: 20px; + border: 1px solid rgba(102, 126, 234, 0.3); + max-width: 100%; + + .names-icon { + font-size: 18px; + width: 18px; + height: 18px; + color: #667eea; + flex-shrink: 0; + } + + .names-text { + font-size: 14px; + font-weight: 500; + color: #e0e0e0; + letter-spacing: 0.2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +} + // ============================================ // VERTICAL MODE (Tile estándar 16:9) // ============================================ @@ -81,35 +136,6 @@ gap: clamp(4px, 2cqh, 16px); } -.header-section { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; - width: 100%; - - .icon-group { - display: flex; - align-items: center; - justify-content: center; - } - - .participants-icon { - width: 40px; - height: 40px; - color: #667eea; - opacity: 0.9; - } - - .title { - font-size: 16px; - font-weight: 600; - color: #ffffff; - text-align: center; - letter-spacing: 0.5px; - } -} - .count-section { display: flex; flex-direction: column; @@ -117,80 +143,87 @@ gap: clamp(4px, 1.5cqh, 12px); flex: 1; justify-content: center; + width: 100%; - .count-badge-vertical { + .participant-names-vertical { display: flex; align-items: center; justify-content: center; - // 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: clamp(22px, 6cqh, 36px); - font-weight: 700; - color: #ffffff; - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.5); - } + gap: 6px; + padding: clamp(4px, 1.5cqh, 8px) clamp(8px, 3cqw, 12px); + background-color: rgba(102, 126, 234, 0.15); + border-radius: 12px; + border: 1px solid rgba(102, 126, 234, 0.3); + max-width: 90%; - .count-label { - 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; - } -} + .names-icon { + font-size: clamp(14px, 3cqw, 16px); + width: clamp(14px, 3cqw, 16px); + height: clamp(14px, 3cqw, 16px); + color: #667eea; + flex-shrink: 0; + } -.info-section { - display: flex; - align-items: center; - gap: 8px; - padding: 12px 16px; - background-color: rgba(255, 255, 255, 0.05); - border-radius: 6px; - width: 100%; - - .info-icon { - width: 20px; - height: 20px; - color: #667eea; - flex-shrink: 0; - } - - .info-text { - font-size: 12px; - font-weight: 500; - color: #b0b0b0; - line-height: 1.4; - letter-spacing: 0.2px; + .names-text { + font-size: clamp(11px, 2cqh, 13px); + font-weight: 500; + color: #e0e0e0; + letter-spacing: 0.2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; + flex: 1; + min-width: 0; + } } } @container hidden-indicator (max-height: 100px) { - .count-label { + .vertical .count-info { display: none; } .count-section { gap: 0; } + .participant-names-vertical { + display: none !important; + } + + // Show number instead of icon when small + .vertical .count-badge { + .badge-icon { + display: none; + } + .badge-number { + display: block; + } + } } @container hidden-indicator (max-width: 120px) { - .count-label { + .vertical .count-info { display: none; } .count-section { gap: 0; } + .participant-names-vertical { + display: none !important; + } + + // Show number instead of icon when small + .vertical .count-badge { + .badge-icon { + display: none; + } + .badge-number { + display: block; + } + } } // Para espacios extra pequeños, optimizar padding y sombras @@ -199,19 +232,19 @@ padding: 4px; } - .count-badge-vertical { + .count-badge { box-shadow: 0 2px 6px rgba(102, 126, 234, 0.4); } } @media (max-width: 768px), (max-height: 500px) { - .count-badge-horizontal { + .count-badge { min-width: 40px; height: 40px; font-size: 18px; } - .text-horizontal { + .count-info { .count-number { font-size: 20px; } @@ -225,31 +258,14 @@ gap: 12px; } - .header-section { - .participants-icon { - width: 32px; - height: 32px; - } - .title { - font-size: 14px; - } - } - .count-section { - .count-badge-vertical { + .count-badge { width: 64px; height: 64px; font-size: 28px; } - .count-label { + .count-info { font-size: 13px; } } - - .info-section { - padding: 10px 12px; - .info-text { - font-size: 11px; - } - } } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.ts index eedbdda0..7d9fd13d 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/components/hidden-participants-indicator/hidden-participants-indicator.component.ts @@ -21,6 +21,11 @@ export class HiddenParticipantsIndicatorComponent { */ count = input(0); + /** + * Names of hidden participants (used in topbar mode to show who is hidden) + */ + hiddenParticipantNames = input([]); + mode = input<'topbar' | 'standard'>('standard'); protected isTopBarMode = computed(() => this.mode() === 'topbar'); @@ -35,6 +40,29 @@ export class HiddenParticipantsIndicatorComponent { }); protected descriptionText = computed(() => { - return this.count() === 1 ? 'participant not currently visible' : 'participants not currently visible'; + return `hidden participant${this.count() === 1 ? '' : 's'}`; + }); + + /** + * Get formatted participant names for display in topbar mode + * Shows up to 3 names, then "and X more" if there are additional participants + */ + protected formattedParticipantNames = computed(() => { + const names = this.hiddenParticipantNames(); + const total = this.count(); + + if (!names || names.length === 0) { + return ''; + } + + const maxNamesToShow = 3; + const visibleNames = names.slice(0, maxNamesToShow); + const remaining = total - visibleNames.length; + + if (remaining > 0) { + return `${visibleNames.join(', ')} and ${remaining} more`; + } + + return visibleNames.join(', '); }); } diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/customization/components/meeting-custom-layout/meeting-custom-layout.component.html b/meet-ce/frontend/projects/shared-meet-components/src/lib/customization/components/meeting-custom-layout/meeting-custom-layout.component.html index 84abe4a3..c2df2155 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/customization/components/meeting-custom-layout/meeting-custom-layout.component.html +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/customization/components/meeting-custom-layout/meeting-custom-layout.component.html @@ -28,6 +28,7 @@
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/customization/components/meeting-custom-layout/meeting-custom-layout.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/customization/components/meeting-custom-layout/meeting-custom-layout.component.ts index 717c3f52..4f7cbad2 100644 --- a/meet-ce/frontend/projects/shared-meet-components/src/lib/customization/components/meeting-custom-layout/meeting-custom-layout.component.ts +++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/customization/components/meeting-custom-layout/meeting-custom-layout.component.ts @@ -51,6 +51,13 @@ export class MeetingCustomLayoutComponent { return Math.max(0, total - visible); }); + protected readonly hiddenParticipantNames = computed(() => { + const visibleIds = new Set(this.visibleRemoteParticipants().map((p) => p.identity)); + return this.remoteParticipants() + .filter((p) => !visibleIds.has(p.identity)) + .map((p) => p.name || 'Unknown'); + }); + /** * Indicates whether to show the hidden participants indicator in the top bar * when in smart mosaic mode.