Previously we had the bubbles drawn on the avatar canvas. Now it's actually text, so is the label.
74 lines
2.2 KiB
Rust
74 lines
2.2 KiB
Rust
//! HTML-based username label component.
|
|
//!
|
|
//! Renders display names as HTML/CSS elements instead of canvas,
|
|
//! allowing independent updates without triggering avatar redraws.
|
|
|
|
use leptos::prelude::*;
|
|
use uuid::Uuid;
|
|
|
|
use super::avatar_canvas::AvatarBoundsStore;
|
|
|
|
/// Base text size multiplier. Text at 100% slider = base_sizes * 1.4
|
|
const BASE_TEXT_SCALE: f64 = 1.4;
|
|
|
|
/// Individual username label component.
|
|
///
|
|
/// Renders as HTML/CSS for efficient updates independent of avatar canvas.
|
|
/// Reads avatar position from the shared bounds store written by AvatarCanvas.
|
|
#[component]
|
|
pub fn UsernameLabel(
|
|
/// The user ID this label belongs to (for reading bounds from store).
|
|
user_id: Uuid,
|
|
/// The display name to show.
|
|
display_name: String,
|
|
/// Shared store containing avatar bounds (written by AvatarCanvas).
|
|
avatar_bounds_store: AvatarBoundsStore,
|
|
/// Text size multiplier.
|
|
#[prop(default = 1.0.into())]
|
|
text_em_size: Signal<f64>,
|
|
/// Optional opacity for fading members.
|
|
#[prop(default = 1.0)]
|
|
opacity: f64,
|
|
) -> impl IntoView {
|
|
// Compute style based on avatar bounds
|
|
let style = Memo::new(move |_| {
|
|
let te = text_em_size.get();
|
|
let text_scale = te * BASE_TEXT_SCALE;
|
|
let font_size = 12.0 * text_scale;
|
|
|
|
let avatar_bounds = avatar_bounds_store
|
|
.get()
|
|
.get(&user_id)
|
|
.copied()
|
|
.unwrap_or_default();
|
|
|
|
// If bounds are all zero, avatar hasn't rendered yet
|
|
if avatar_bounds.content_center_x == 0.0 && avatar_bounds.content_top_y == 0.0 {
|
|
return "display: none;".to_string();
|
|
}
|
|
|
|
let x = avatar_bounds.content_center_x;
|
|
let y = avatar_bounds.content_bottom_y + 15.0 * text_scale;
|
|
|
|
format!(
|
|
"position: absolute; \
|
|
left: {}px; \
|
|
top: {}px; \
|
|
transform: translateX(-50%); \
|
|
--font-size: {}px; \
|
|
opacity: {}; \
|
|
z-index: 99998;",
|
|
x, y, font_size, opacity
|
|
)
|
|
});
|
|
|
|
view! {
|
|
<div
|
|
class="username-label"
|
|
style=style
|
|
data-user-id=user_id.to_string()
|
|
>
|
|
{display_name}
|
|
</div>
|
|
}
|
|
}
|