use derive_more::Display;
use yew::prelude::*;
use crate::components::dropdown::DropdownMsg;
pub enum NavbarMsg {
ToggleMenu,
}
#[derive(Clone, Debug, Properties, PartialEq)]
pub struct NavbarProps {
#[prop_or_default]
pub children: Children,
#[prop_or_default]
pub classes: Classes,
#[prop_or_default]
pub fixed: Option<NavbarFixed>,
#[prop_or_default]
pub transparent: bool,
#[prop_or_default]
pub spaced: bool,
#[prop_or_default]
pub padded: bool,
#[prop_or_default]
pub navbrand: Option<Html>,
#[prop_or_default]
pub navstart: Option<Html>,
#[prop_or_default]
pub navend: Option<Html>,
#[prop_or_else(|| true)]
pub navburger: bool,
#[prop_or_default]
pub navburger_classes: Classes,
}
pub struct Navbar {
is_menu_open: bool,
}
impl Component for Navbar {
type Message = NavbarMsg;
type Properties = NavbarProps;
fn create(_ctx: &Context<Self>) -> Self {
Self { is_menu_open: false }
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
NavbarMsg::ToggleMenu => {
self.is_menu_open = !self.is_menu_open;
}
}
true
}
fn view(&self, ctx: &Context<Self>) -> Html {
let mut class = Classes::from("navbar");
class.push(ctx.props().classes.clone());
if let Some(fixed) = &ctx.props().fixed {
class.push(&fixed.to_string());
}
let mut navclasses = Classes::from("navbar-menu");
let mut burgerclasses = Classes::from("navbar-burger");
burgerclasses.push(ctx.props().navburger_classes.clone());
if self.is_menu_open {
navclasses.push("is-active");
burgerclasses.push("is-active");
}
let togglecb = ctx.link().callback(|_| NavbarMsg::ToggleMenu);
let navbrand = if let Some(navbrand) = &ctx.props().navbrand {
html! {
<div class="navbar-brand">
{navbrand.clone()}
{if ctx.props().navburger {
html! {
<a class={burgerclasses} onclick={togglecb}
role="button" aria-label="menu"
aria-expanded={if self.is_menu_open { "true" } else { "false" }}
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
}
} else {
html! {}
}}
</div>
}
} else {
html! {}
};
let navstart = if let Some(navstart) = &ctx.props().navstart {
html! {<div class="navbar-start">{navstart.clone()}</div>}
} else {
html! {}
};
let navend = if let Some(navend) = &ctx.props().navend {
html! {<div class="navbar-end">{navend.clone()}</div>}
} else {
html! {}
};
let contents = html! {
<>
{navbrand}
<div class={navclasses}>
{navstart}
{navend}
</div>
</>
};
if ctx.props().padded {
html! {
<nav {class} role="navigation" aria-label="main navigation">
<div class="container">{contents}</div>
</nav>
}
} else {
html! {
<nav {class} role="navigation" aria-label="main navigation">{contents}</nav>
}
}
}
}
#[derive(Clone, Debug, Display, PartialEq, Eq)]
#[display(fmt = "is-{}")]
pub enum NavbarFixed {
#[display(fmt = "fixed-top")]
Top,
#[display(fmt = "fixed-bottom")]
Bottom,
}
#[derive(Clone, Debug, Display, PartialEq, Eq)]
pub enum NavbarItemTag {
#[display(fmt = "a")]
A,
#[display(fmt = "div")]
Div,
}
#[derive(Clone, Debug, Properties, PartialEq)]
pub struct NavbarItemProps {
#[prop_or_default]
pub children: Children,
#[prop_or_default]
pub classes: Classes,
#[prop_or_else(|| NavbarItemTag::Div)]
pub tag: NavbarItemTag,
#[prop_or_default]
pub has_dropdown: bool,
#[prop_or_default]
pub expanded: bool,
#[prop_or_default]
pub tab: bool,
#[prop_or_default]
pub active: bool,
#[prop_or_default]
pub href: Option<String>,
#[prop_or_default]
pub rel: Option<String>,
#[prop_or_default]
pub target: Option<String>,
}
#[function_component(NavbarItem)]
pub fn navbar_item(props: &NavbarItemProps) -> Html {
let class = classes!(
"navbar-item",
props.classes.clone(),
props.has_dropdown.then_some("has-dropdown"),
props.expanded.then_some("is-expanded"),
props.tab.then_some("is-tab"),
props.active.then_some("is-active"),
);
match props.tag {
NavbarItemTag::A => {
html! {
<a
{class}
href={props.href.clone().unwrap_or_default()}
rel={props.rel.clone().unwrap_or_default()}
target={props.target.clone().unwrap_or_default()}
>
{props.children.clone()}
</a>
}
}
NavbarItemTag::Div => {
html! {
<div {class}>
{props.children.clone()}
</div>
}
}
}
}
#[derive(Clone, Debug, Properties, PartialEq)]
pub struct NavbarDividerProps {
#[prop_or_default]
pub classes: Classes,
}
#[function_component(NavbarDivider)]
pub fn navbar_divider(props: &NavbarDividerProps) -> Html {
html! { <hr class={classes!("navbar-divider", props.classes.clone())} /> }
}
#[derive(Clone, Debug, Properties, PartialEq)]
pub struct NavbarDropdownProps {
#[prop_or_default]
pub children: Children,
#[prop_or_default]
pub classes: Classes,
pub navlink: Html,
#[prop_or_default]
pub hoverable: bool,
#[prop_or_default]
pub dropup: bool,
#[prop_or_default]
pub right: bool,
#[prop_or_default]
pub arrowless: bool,
#[prop_or_default]
pub boxed: bool,
}
pub struct NavbarDropdown {
is_menu_active: bool,
}
impl Component for NavbarDropdown {
type Message = DropdownMsg;
type Properties = NavbarDropdownProps;
fn create(_ctx: &Context<Self>) -> Self {
Self { is_menu_active: false }
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
if ctx.props().hoverable {
return false;
}
match msg {
DropdownMsg::Open => self.is_menu_active = true,
DropdownMsg::Close => self.is_menu_active = false,
}
true
}
fn view(&self, ctx: &Context<Self>) -> Html {
let mut class = Classes::from("navbar-item has-dropdown");
class.push(ctx.props().classes.clone());
if ctx.props().dropup {
class.push("has-dropdown-up");
}
let mut dropclasses = Classes::from("navbar-dropdown");
if ctx.props().right {
dropclasses.push("is-right");
}
if ctx.props().boxed {
dropclasses.push("is-boxed");
}
let mut linkclasses = Classes::from("navbar-link");
if ctx.props().arrowless {
linkclasses.push("is-arrowless");
}
let opencb = if ctx.props().hoverable {
class.push("is-hoverable");
Callback::noop()
} else {
ctx.link().callback(|_| DropdownMsg::Open)
};
let overlay = if self.is_menu_active {
class.push("is-active");
html! {<div onclick={ctx.link().callback(|_| DropdownMsg::Close)} style="z-index:10;background-color:rgba(0,0,0,0);position:fixed;top:0;bottom:0;left:0;right:0;"></div>}
} else {
html! {}
};
html! {
<div {class}>
{overlay}
<a class={linkclasses} onclick={opencb}>{ctx.props().navlink.clone()}</a>
<div class={dropclasses}>
{ctx.props().children.clone()}
</div>
</div>
}
}
}