Skip to main content

logicaffeine_web/ui/components/
lexicon_gate.rs

1//! Gate component for content that draws exercise words from the lexicon.
2//!
3//! On wasm the lexicon arrives via `/data/lexicon.json` (see
4//! [`crate::generator::ensure_lexicon`]); this gate holds its children back until
5//! the index is pinned so `Generator::new()` is always safe inside. Native builds
6//! resolve instantly, so tests and prerendering render children directly.
7
8use dioxus::prelude::*;
9
10const LEXICON_GATE_STYLE: &str = r#"
11.lexicon-gate-status {
12    color: rgba(229,231,235,0.72);
13    font-size: 16px;
14    text-align: center;
15    padding: 48px 20px;
16}
17
18.lexicon-gate-status.error {
19    color: #fca5a5;
20}
21
22.lexicon-gate-retry {
23    display: block;
24    margin: 0 auto;
25    padding: 10px 28px;
26    border-radius: 10px;
27    border: 1px solid rgba(255,255,255,0.2);
28    background: rgba(255,255,255,0.08);
29    color: #e8e8e8;
30    font-size: 15px;
31    cursor: pointer;
32}
33
34.lexicon-gate-retry:hover {
35    background: rgba(255,255,255,0.16);
36}
37"#;
38
39#[component]
40pub fn LexiconGate(children: Element) -> Element {
41    let mut ready = use_resource(|| crate::generator::ensure_lexicon());
42    let state = ready.read_unchecked();
43    match &*state {
44        Some(Ok(())) => rsx! {
45            {children}
46        },
47        Some(Err(e)) => rsx! {
48            style { "{LEXICON_GATE_STYLE}" }
49            p { class: "lexicon-gate-status error", "The word bank failed to load: {e}" }
50            button {
51                class: "lexicon-gate-retry",
52                onclick: move |_| ready.restart(),
53                "Retry"
54            }
55        },
56        None => rsx! {
57            style { "{LEXICON_GATE_STYLE}" }
58            p { class: "lexicon-gate-status", "Loading the word bank\u{2026}" }
59        },
60    }
61}