yew使用bootstrap的Tooltip和Popover
yew使用bootstrap的Tooltip和Popover 前言
popperjs,根据bootstrap官方在页面最后加入下面的激活代码也是无效的。const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))
bootstrap.Popover这个方法,然后执行创建新的元素才可以显示冒泡组件。 冒泡组件
data-bs-toggle属性,每当页面加载完成会执行上面的javascript代码过滤属性为[data-bs-toggle="popover"]的元素(父组件),然后调用bootstrap.Popover在body标签下创建以toggle属性开头的随机字符串为id的冒泡组件,最后在父组件上添加这个随机生成的id进行关联。这样就会父组件附近(指定的placement属性)显示这个提示帮助信息。<button type="button" class="btn btn-lg btn-danger" data-bs-toggle="popover" data-bs-title="Popover title" data-bs-content="And here's some amazing content. It's very engaging. Right?">Click to toggle popover</button>
Yew封装组件
TooltipPopover,先按照冒泡组件的option创建Prop属性,并且给属性设置默认值。#[derive(PartialEq, Clone, Default, Properties)]
pub struct TooltipPopoverProp {
#[prop_or_else(default_toggle)]
pub toggle: AttrValue,
#[prop_or_else(default_trigger)]
pub trigger: AttrValue,
#[prop_or_else(default_placement)]
pub placement: AttrValue,
#[prop_or(false)]
pub html: bool,
#[prop_or_default]
pub content: AttrValue,
#[prop_or_default]
pub class: Classes,
#[prop_or_default]
pub children: Html,
}
fn default_toggle() -> AttrValue {
AttrValue::from("popover")
}
fn default_trigger() -> AttrValue {
AttrValue::from("hover")
}
fn default_placement() -> AttrValue {
AttrValue::from("top")
}
Properties属性为上面的TooltipPopoverProp,初始化方法,然后就是补充渲染方法。pub struct TooltipPopover;
impl Component for TooltipPopover {
type Message = ();
type Properties = TooltipPopoverProp;
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, ctx: &Context<Self>) -> Html {
let TooltipPopoverProp { class, content, html, placement, toggle, children, trigger, .. } =
ctx.props();
let on_mouse_enter = {
Callback::from(move |event: MouseEvent| {
event.stop_propagation();
let target = event.target_unchecked_into::<HtmlElement>();
let window = web_sys::window().unwrap();
let bootstrap = js_sys::Reflect
::get(&window, &wasm_bindgen::JsValue::from("bootstrap"))
.expect("Error! Failed to get property bootstrap");
let popover = js_sys::Reflect
::get(&bootstrap, &JsValue::from("Popover"))
.expect("Error! Failed to get property Popover");
let get_or_create_instance = js_sys::Reflect
::get(&popover, &JsValue::from("getOrCreateInstance"))
.expect("Error! Failed to get property getOrCreateInstance");
let popover_bootstrap = get_or_create_instance
.clone()
.unchecked_into::<js_sys::Function>()
.call1(&popover, &target)
.expect("Error! Failed to call function getOrCreateInstance");
let show_popover = js_sys::Reflect
::get(&popover_bootstrap, &JsValue::from("show"))
.expect("Error! Failed to get property show");
let show_callback = Closure::wrap(
Box::new(move |_: MouseEvent| {
show_popover
.clone()
.unchecked_into::<js_sys::Function>()
.call0(&popover_bootstrap)
.expect("Error! Failed to call function show");
}) as Box<dyn FnMut(_)>
);
target
.clone()
.unchecked_into::<Element>()
.add_event_listener_with_callback(
"click",
show_callback.as_ref().unchecked_ref()
)
.expect("Error! Failed to add event listener");
show_callback.forget();
})
};
html! {
<div class={class.clone()}>
<span
tabindex=0
data-bs-trigger={trigger}
onmouseenter={on_mouse_enter}
data-bs-toggle={toggle.clone()}
data-bs-container="body"
data-bs-placement={placement}
data-bs-html={html.to_string()}
data-bs-content={content.clone()}
>
{children.clone()}
</span>
</div>
}
}
}
// getOrCreateInstance example
const popover = bootstrap.Popover.getOrCreateInstance('#example') // Returns a Bootstrap popover instance
// setContent example
myPopover.setContent({
'.popover-header': 'another title',
'.popover-body': 'another content'
})
data-bs-*的属性创建冒泡组件,这个冒泡组件会和父组件的aria-describedby属性下绑定同一个id。 let on_mouse_enter = {
Callback::from(move |event: MouseEvent| {
event.stop_propagation();
let target = event.target_unchecked_into::<HtmlElement>();
let window = web_sys::window().unwrap();
let bootstrap = js_sys::Reflect::get(&window, &wasm_bindgen::JsValue::from("bootstrap"))
.expect("Error! Failed to get property bootstrap");
let popover = js_sys::Reflect::get(&bootstrap, &JsValue::from("Popover"))
.expect("Error! Failed to get property Popover");
let get_or_create_instance =
js_sys::Reflect::get(&popover, &JsValue::from("getOrCreateInstance"))
.expect("Error! Failed to get property getOrCreateInstance");
let popover_bootstrap = get_or_create_instance
.clone()
.unchecked_into::<js_sys::Function>()
.call1(&popover, &target)
.expect("Error! Failed to call function getOrCreateInstance");
let show_popover = js_sys::Reflect::get(&popover_bootstrap, &JsValue::from("show"))
.expect("Error! Failed to get property show");
let show_callback =
Closure::wrap(Box::new(move |_: MouseEvent| {
show_popover
.clone()
.unchecked_into::<js_sys::Function>()
.call0(&popover_bootstrap)
.expect("Error! Failed to call function show");
}) as Box<dyn FnMut(_)>);
target
.clone()
.unchecked_into::<Element>()
.add_event_listener_with_callback("click", show_callback.as_ref().unchecked_ref())
.expect("Error! Failed to add event listener");
show_callback.forget();
})
};
bootstrap这个对象,然后再获取Popover这个类,获取getOrCreateInstance,最后和javascript一样调用getOrCreateInstance函数得到一个实例化的对象。继续从这个实例化对象中获取一个显示函数,也就是文档上面的show函数,然后这个实例化对象注册回调函数(就是当鼠标点击的时候调用show函数),这里使用click点击事件,因为鼠标进入事件已经被用了。 调用冒泡组件
html! {
<TooltipPopover
class={classes!(["form-help"])}
toggle={"popover"}
placement={"left"}
content={"Running On/With"}>
<i class={classes!( ["ti","ti-stack-2"])}></i>
</TooltipPopover>
...
<TooltipPopover
toggle={"toggle"}
placement={"top"}
content={des}>
<span class={classes!(["badge",class_str])}>
<i class={classes!( ["ti",icon])}></i>{value}
</span>
</TooltipPopover>
};
效果

参考