use crate::*;
pub fn nav_item(route_signal: Signal<String>, label: &str, target: &str) -> VirtualNode {
let target_string: String = target.to_string();
let current_route_value: String = route_signal.get();
let is_active: bool = current_route_value == target;
html! {
a {
href: format!("#{}", target_string)
class: if { is_active } { c_nav_item_active() } else { c_nav_item_inactive() }
onclick: link_handler(target_string)
label
}
}
}
fn mobile_nav_item(
route_signal: Signal<String>,
drawer_open: Signal<bool>,
label: &str,
target: &str,
) -> VirtualNode {
let target_string: String = target.to_string();
let current_route_value: String = route_signal.get();
let is_active: bool = current_route_value == target;
let nav_target: String = target_string.clone();
html! {
a {
href: format!("#{}", target_string)
class: if { is_active } { c_nav_item_active() } else { c_nav_item_inactive() }
onclick: move |_event: NativeEvent| {
navigate(&nav_target);
drawer_open.set(false);
}
label
}
}
}
fn build_desktop_nav_items(route_signal: Signal<String>) -> VirtualNode {
html! {
div {
class: c_nav_items_scroll()
{ nav_item(route_signal, "Signals", "/signals") }
{ nav_item(route_signal, "Event", "/event") }
{ nav_item(route_signal, "List", "/list") }
{ nav_item(route_signal, "Conditional", "/conditional") }
{ nav_item(route_signal, "Modal", "/modal") }
{ nav_item(route_signal, "Select", "/select") }
{ nav_item(route_signal, "Async", "/async") }
{ nav_item(route_signal, "Form", "/form") }
{ nav_item(route_signal, "Timer", "/timer") }
{ nav_item(route_signal, "Animation", "/animation") }
{ nav_item(route_signal, "Browser", "/browser") }
{ nav_item(route_signal, "Lifecycle", "/lifecycle") }
{ nav_item(route_signal, "Custom Attrs", "/custom-attrs") }
}
}
}
fn build_mobile_nav_items(route_signal: Signal<String>, drawer_open: Signal<bool>) -> VirtualNode {
html! {
div {
class: c_nav_items_scroll()
{ mobile_nav_item(route_signal, drawer_open, "Signals", "/signals") }
{ mobile_nav_item(route_signal, drawer_open, "Event", "/event") }
{ mobile_nav_item(route_signal, drawer_open, "List", "/list") }
{ mobile_nav_item(route_signal, drawer_open, "Conditional", "/conditional") }
{ mobile_nav_item(route_signal, drawer_open, "Modal", "/modal") }
{ mobile_nav_item(route_signal, drawer_open, "Select", "/select") }
{ mobile_nav_item(route_signal, drawer_open, "Async", "/async") }
{ mobile_nav_item(route_signal, drawer_open, "Form", "/form") }
{ mobile_nav_item(route_signal, drawer_open, "Timer", "/timer") }
{ mobile_nav_item(route_signal, drawer_open, "Animation", "/animation") }
{ mobile_nav_item(route_signal, drawer_open, "Browser", "/browser") }
{ mobile_nav_item(route_signal, drawer_open, "Lifecycle", "/lifecycle") }
{ mobile_nav_item(route_signal, drawer_open, "Custom Attrs", "/custom-attrs") }
}
}
}
fn desktop_layout(
route_signal: Signal<String>,
theme_signal: Signal<String>,
root_class_signal: Signal<String>,
panel_open: Signal<bool>,
) -> VirtualNode {
html! {
div {
class: root_class_signal
nav {
class: c_app_nav()
a {
href: "https://github.com/euv-dev/euv"
target: "_blank"
class: c_nav_header()
span {
class: c_nav_logo()
"E"
}
span {
class: c_inline()
"Euv"
}
}
p {
class: c_nav_section_label()
"Pages"
}
{ build_desktop_nav_items(route_signal) }
div {
class: c_nav_theme_toggle()
button {
class: c_nav_theme_button()
onclick: toggle_theme(theme_signal)
if { theme_signal.get() == "dark" } {
"☀"
} else {
"🌙"
}
}
}
a {
href: "https://github.com/euv-dev/euv"
target: "_blank"
class: c_nav_footer()
"Built with Euv & WASM"
}
}
main {
class: c_app_main()
{ render_page(route_signal) }
}
{ vconsole_panel(panel_open) }
}
}
}
fn mobile_layout(
route_signal: Signal<String>,
theme_signal: Signal<String>,
root_class_signal: Signal<String>,
panel_open: Signal<bool>,
drawer_open: Signal<bool>,
) -> VirtualNode {
html! {
div {
class: root_class_signal
header {
class: c_mobile_header()
div {
class: c_mobile_header_left()
button {
class: if { drawer_open.get() } { c_mobile_menu_button_active() } else { c_mobile_menu_button() }
onclick: use_toggle(drawer_open)
"☰"
}
span {
class: c_nav_logo()
"E"
}
span {
class: c_inline()
"euv"
}
}
button {
class: c_mobile_menu_button()
onclick: toggle_theme(theme_signal)
if { theme_signal.get() == "dark" } {
"☀"
} else {
"🌙"
}
}
}
main {
class: c_mobile_main()
{ render_page(route_signal) }
}
{ vconsole_panel(panel_open) }
if { drawer_open.get() } {
div {
class: c_mobile_overlay()
onclick: move |_event: NativeEvent| {
drawer_open.set(false);
}
}
nav {
class: c_mobile_nav_drawer()
div {
class: c_mobile_nav_drawer_header()
a {
href: "https://github.com/euv-dev/euv"
target: "_blank"
class: c_nav_header()
span {
class: c_nav_logo()
"E"
}
span {
class: c_inline()
"Euv"
}
}
button {
class: c_mobile_nav_drawer_close()
onclick: move |_event: NativeEvent| {
drawer_open.set(false);
}
"✕"
}
}
p {
class: c_nav_section_label()
"Pages"
}
{ build_mobile_nav_items(route_signal, drawer_open) }
div {
class: c_nav_theme_toggle()
button {
class: c_nav_theme_button()
onclick: toggle_theme(theme_signal)
if { theme_signal.get() == "dark" } {
"☀"
} else {
"🌙"
}
}
}
a {
href: "https://github.com/euv-dev/euv"
target: "_blank"
class: c_nav_footer()
"Built with Euv & WASM"
}
}
} else {
""
}
}
}
}
fn render_page(route_signal: Signal<String>) -> VirtualNode {
let current_route: String = route_signal.get();
match current_route.as_str() {
"/" | "/signals" => page_signals(),
"/event" => page_event(),
"/list" => page_list(),
"/conditional" => page_conditional(),
"/modal" => page_modal(),
"/select" => page_select(),
"/async" => page_async_demo(),
"/form" => page_form(),
"/timer" => page_timer(),
"/animation" => page_animation(),
"/browser" => page_browser(),
"/lifecycle" => page_lifecycle(),
"/custom-attrs" => page_custom_attrs(),
_ => page_not_found(),
}
}
pub fn app() -> VirtualNode {
init_console();
let route_signal: Signal<String> = use_signal(current_route);
let panel_open: Signal<bool> = use_signal(|| false);
let drawer_open: Signal<bool> = use_signal(|| false);
let mobile_signal: Signal<bool> = use_resize();
let theme_state: ThemeState = use_theme();
let theme_signal: Signal<String> = theme_state.theme;
let root_class_signal: Signal<String> = theme_state.root_class;
use_hash_change(route_signal);
let is_mobile: bool = mobile_signal.get();
let current_root_class: String = if is_mobile {
format!(
"{} {}",
c_mobile_app_root().get_name(),
theme_class_name(&theme_signal.get())
)
} else {
root_class_signal.get()
};
let root_class: Signal<String> = use_signal(move || current_root_class);
watch!(mobile_signal, theme_signal, |mobile, theme| {
if mobile {
root_class.set(format!(
"{} {}",
c_mobile_app_root().get_name(),
theme_class_name(&theme)
));
} else {
root_class.set(format!(
"{} {}",
c_app_root().get_name(),
theme_class_name(&theme)
));
}
});
if is_mobile {
mobile_layout(
route_signal,
theme_signal,
root_class,
panel_open,
drawer_open,
)
} else {
desktop_layout(route_signal, theme_signal, root_class, panel_open)
}
}