1use crossterm::event::KeyCode;
12use telex::prelude::*;
13
14telex::require_api!(0, 2);
15
16fn main() {
17 telex::run(App).unwrap();
18}
19
20struct App;
21
22impl Component for App {
23 fn render(&self, cx: Scope) -> View {
24 let show_help = state!(cx, || false);
25
26 cx.use_command(
28 KeyBinding::key(KeyCode::F(1)),
29 with!(show_help => move || show_help.update(|v| *v = !*v)),
30 );
31
32 let count = state!(cx, || 0);
33 let name = state!(cx, String::new);
34 let last_effect = state!(cx, || String::from("(none yet)"));
35 let init_done = state!(cx, || false);
36
37 effect_once!(cx, with!(init_done, last_effect => move || {
40 init_done.set(true);
41 last_effect.set("effect_once!: initialized!".to_string());
42 || {}
43 }));
44
45 effect!(cx, count.get(), with!(last_effect => move |&val| {
48 last_effect.set(format!("effect!: count → {}", val));
49 || {}
50 }));
51
52 effect!(cx, name.get(), with!(last_effect => move |n: &String| {
54 if !n.is_empty() {
55 last_effect.set(format!("effect!: name → \"{}\"", n));
56 }
57 || {}
58 }));
59
60 View::vstack()
61 .spacing(1)
62 .child(View::styled_text("effect! Demo").bold().build())
63 .child(View::text(""))
64 .child(View::text(format!("Counter: {}", count.get())))
65 .child(
66 View::hstack()
67 .spacing(1)
68 .child(
69 View::button()
70 .label("[ - ]")
71 .on_press(with!(count => move || count.update(|n| *n -= 1)))
72 .build(),
73 )
74 .child(
75 View::button()
76 .label("[ + ]")
77 .on_press(with!(count => move || count.update(|n| *n += 1)))
78 .build(),
79 )
80 .build(),
81 )
82 .child(View::text(""))
83 .child({
84 let n = name.get();
85 View::text(format!(
86 "Name: {}",
87 if n.is_empty() { "(empty)" } else { &n }
88 ))
89 })
90 .child(
91 View::text_input()
92 .value(name.get())
93 .placeholder("Type your name...")
94 .on_change(with!(name => move |s| name.set(s)))
95 .build(),
96 )
97 .child(View::text(""))
98 .child(View::styled_text("─── Effect Status ───").dim().build())
99 .child(View::text(""))
100 .child(View::text(format!(
101 "Initialized: {}",
102 if init_done.get() { "✓ yes" } else { "no" }
103 )))
104 .child(View::text(format!("Last effect: {}", last_effect.get())))
105 .child(View::text(""))
106 .child(View::styled_text("─── How it works ───").dim().build())
107 .child(View::text(""))
108 .child(View::text("effect_once! → Ran once at startup"))
109 .child(View::text("effect! → Runs when deps change"))
110 .child(View::text(""))
111 .child(
112 View::styled_text("Press +/- or type to see effects trigger")
113 .dim()
114 .build(),
115 )
116 .child(View::text(""))
117 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
118 .child(
119 View::modal()
120 .visible(show_help.get())
121 .title("Example 32: Effects")
122 .on_dismiss(with!(show_help => move || show_help.set(false)))
123 .child(
124 View::vstack()
125 .child(View::styled_text("What you're seeing").bold().build())
126 .child(View::text("• effect_once! runs at startup"))
127 .child(View::text("• effect! runs when deps change"))
128 .child(View::text("• Last effect shows what triggered"))
129 .child(View::gap(1))
130 .child(View::styled_text("Key concepts").bold().build())
131 .child(View::text("• effect_once!(cx, || { cleanup })"))
132 .child(View::text("• effect!(cx, deps, |&d| { cleanup })"))
133 .child(View::text("• Return || {} for cleanup"))
134 .child(View::text("• Effects run AFTER render"))
135 .child(View::text("• Safe in conditionals!"))
136 .child(View::gap(1))
137 .child(View::styled_text("Try this").bold().build())
138 .child(View::text("• Click +/- to change counter"))
139 .child(View::text("• Type in the name field"))
140 .child(View::text("• Watch 'Last effect' update"))
141 .child(View::gap(1))
142 .child(View::styled_text("Press Escape to close").dim().build())
143 .build(),
144 )
145 .build(),
146 )
147 .build()
148 }
149}