aex/src/components/character/attunements.rs

66 lines
2.2 KiB
Rust
Raw Normal View History

use reactive_stores::Field;
use crate::prelude::*;
use leptos::prelude::*;
/// The three attunement slots: each is either empty (free-text input and
/// "+" button) or holds an item name with a "×" clear button.
#[component]
pub fn Attunements () -> impl IntoView {
let state = expect_context::<Context>();
let attunements: Field<Attunements> = state.player().attunements().into();
view! {
<div class="attunements">
<h6>"attunements/слоты привязки:"</h6>
<For
// Same state-in-key convention as prepared spells, so a
// slot whose value changed re-renders.
each=move || attunements.get().0.into_iter().enumerate()
key=|(i, s)| format!("{}-{}", i, s.as_deref().unwrap_or(""))
let((idx, slot))
>
{slot_row(attunements, idx, slot)}
</For>
</div>
}
}
/// One attunement slot: the free-text entry (empty) or the item itself.
/// A plain function: component tags are not allowed directly inside
/// `For`'s `let(...)` children.
fn slot_row (
attunements: Field<Attunements>,
idx: usize,
slot: Option<String>,
) -> impl IntoView {
let text_sig = RwSignal::new(String::new());
// Binds the typed item name; empty text is ignored.
let bind = move |_| {
let text = text_sig.get_untracked();
if text.trim().is_empty() {
return;
}
attunements.update(|a| a.0[idx] = Some(text.trim().to_string()));
};
let clear = move |_| attunements.update(|a| a.0[idx] = None);
view! {
<div class="attunement-slot">
{if slot.is_some() {
view! {
<div class="attuned-item">{slot.clone().unwrap_or_default()}</div>
<button title="Снять привязку" on:click=clear>"×"</button>
}.into_any()
} else {
view! {
<input type="text" placeholder="Предмет…" bind:value=text_sig />
<button title="Привязать предмет" on:click=bind>"+"</button>
}.into_any()
}}
</div>
}
}