Created
May 1, 2022 12:43
-
-
Save WorldSEnder/7fbb846fac5ba8a063ec1c3826c53115 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use gloo_events::EventListener; | |
use wasm_bindgen::{JsCast, UnwrapThrowExt}; | |
use yew::events::{Event, KeyboardEvent}; | |
use yew::html::IntoPropValue; | |
use yew::prelude::*; | |
use yew::virtual_dom::VNode; | |
#[derive(Copy, Clone, Debug, PartialEq)] | |
pub enum TooltipPosition { | |
Top, | |
Right, | |
Left, | |
Bottom, | |
} | |
impl Default for TooltipPosition { | |
fn default() -> Self { | |
Self::Top | |
} | |
} | |
#[derive(Clone, Debug, PartialEq, Properties)] | |
pub struct TooltipProps { | |
#[prop_or_default] | |
pub position: TooltipPosition, | |
#[prop_or_default] | |
pub with_arrow: bool, | |
pub tooltip_text: String, | |
#[prop_or_default] | |
pub children: Children, | |
} | |
#[function_component(Tooltip)] | |
pub fn tooltip(props: &TooltipProps) -> Html { | |
let tooltip_active = use_state_eq(|| false); | |
let [node]: [VNode; 1] = props | |
.children | |
.iter() | |
.collect::<Vec<_>>() | |
.try_into() | |
.expect("Expected a single vtag child"); | |
let mut tag = match node { | |
VNode::VTag(tag) => *tag, | |
_ => panic!("The immediate children must be of type `VTag`"), | |
}; | |
tag.add_attribute("data-tooltip", props.tooltip_text.clone()); | |
let attributes = &mut tag.attributes; | |
let class_entry = attributes.get_mut_index_map().entry("class".into()); | |
let class_str = class_entry.or_insert("".into()); | |
let mut class_vec: Vec<String> = Vec::new(); | |
for s in class_str.split_whitespace() { | |
class_vec.push(s.to_string()); | |
} | |
let mut classes = Classes::from(class_vec.as_slice()); | |
classes.push("tooltip"); | |
if props.with_arrow { | |
classes.push("has-tooltip-arrow"); | |
} | |
classes.push(match props.position { | |
TooltipPosition::Left => "has-tooltip-left", | |
TooltipPosition::Bottom => "has-tooltip-bottom", | |
TooltipPosition::Right => "has-tooltip-right", | |
TooltipPosition::Top => "has-tooltip-top", | |
}); | |
if *tooltip_active { | |
classes.push("has-tooltip-active"); | |
} | |
*class_str = classes.into_prop_value(); | |
let node_ref = tag.node_ref.clone(); | |
let tooltip_setter = tooltip_active.setter(); | |
use_effect_with_deps( | |
move |_| { | |
gloo_console::debug!("Attaching listeners..."); | |
let el = node_ref.cast::<web_sys::HtmlElement>().unwrap_throw(); | |
let focus_listener = EventListener::new(&el, "focus", { | |
let tooltip_setter = tooltip_setter.clone(); | |
move |_: &Event| { | |
gloo_console::debug!("Focus gained!"); | |
tooltip_setter.set(true); | |
} | |
}); | |
let blur_listener = EventListener::new(&el, "blur", { | |
let tooltip_setter = tooltip_setter.clone(); | |
move |_: &Event| { | |
gloo_console::debug!("Focus lost!"); | |
tooltip_setter.set(false); | |
} | |
}); | |
let mouseover_listener = EventListener::new(&el, "mouseover", { | |
let tooltip_setter = tooltip_setter.clone(); | |
move |_: &Event| { | |
gloo_console::debug!("Mouse over!"); | |
tooltip_setter.set(true); | |
} | |
}); | |
let mouseleave_listener = EventListener::new(&el, "mouseleave", { | |
move |_: &Event| { | |
gloo_console::debug!("Mouse leave!"); | |
tooltip_setter.set(false); | |
} | |
}); | |
let keydown_listener = EventListener::new(&el.clone(), "keydown", { | |
move |e: &Event| { | |
let e = e.clone().unchecked_into::<KeyboardEvent>(); | |
if e.key() == "Escape" { | |
gloo_console::debug!(format!("Key down! {}", e.key()), &e); | |
el.blur().unwrap_throw(); | |
} | |
} | |
}); | |
|| { | |
drop(focus_listener); | |
drop(blur_listener); | |
drop(mouseover_listener); | |
drop(mouseleave_listener); | |
drop(keydown_listener); | |
} | |
}, | |
(), | |
); | |
VNode::from(tag) | |
} | |
#[function_component] | |
fn App() -> Html { | |
html! { | |
<Tooltip tooltip_text="Tooltip"> | |
<button type="text">{"Some input..."}</button> | |
</Tooltip> | |
} | |
} | |
fn main() { | |
yew::Renderer::<App>::new().render(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Kinda late, but many thanks for coming up with this :-)