feat: adds Tooltip component

This commit is contained in:
luoxiao 2024-08-14 17:40:06 +08:00 committed by luoxiaozero
parent 61284d57fc
commit 5aac76c558
9 changed files with 290 additions and 106 deletions

View file

@ -107,6 +107,9 @@ fn TheRouter() -> impl IntoView {
<Route path=path!("/textarea") view=TextareaMdPage/>
<Route path=path!("/time-picker") view=TimePickerMdPage/>
<Route path=path!("/toast") view=ToastMdPage />
<Route path=path!("/tooltip") view=TooltipMdPage />
</ParentRoute>
<ParentRoute path=path!("/components") view=ComponentsPage>
<Route path=path!("/upload") view=UploadMdPage/>
</ParentRoute>
</Routes>

View file

@ -250,8 +250,8 @@ fn gen_search_all_options() -> Vec<AutoCompleteOption> {
.into_iter()
.flat_map(|group| {
group.children.into_iter().map(|item| AutoCompleteOption {
value: item.value,
label: item.label,
value: item.value.to_string(),
label: item.label.to_string(),
})
})
.collect()

View file

@ -86,225 +86,229 @@ pub fn ComponentsPage() -> impl IntoView {
}
pub(crate) struct NavGroupOption {
pub label: String,
pub label: &'static str,
pub children: Vec<NavItemOption>,
}
pub(crate) struct NavItemOption {
pub label: String,
pub value: String,
pub label: &'static str,
pub value: &'static str,
}
pub(crate) fn gen_nav_data() -> Vec<NavGroupOption> {
vec![
NavGroupOption {
label: "Getting Started".into(),
label: "Getting Started",
children: vec![
NavItemOption {
value: "/guide/installation".into(),
label: "Installation".into(),
value: "/guide/installation",
label: "Installation",
},
NavItemOption {
value: "/guide/server-sider-rendering".into(),
label: "Server Sider Rendering".into(),
value: "/guide/server-sider-rendering",
label: "Server Sider Rendering",
},
],
},
// NavGroupOption {
// label: "Development".into(),
// label: "Development",
// children: vec![
// NavItemOption {
// value: "/guide/development/components".into(),
// label: "Components".into(),
// value: "/guide/development/components",
// label: "Components",
// },
// ],
// },
NavGroupOption {
label: "Components".into(),
label: "Components",
children: vec![
NavItemOption {
value: "/components/accordion".into(),
label: "Accordion".into(),
value: "/components/accordion",
label: "Accordion",
},
NavItemOption {
value: "/components/anchor".into(),
label: "Anchor".into(),
value: "/components/anchor",
label: "Anchor",
},
NavItemOption {
value: "/components/auto-complete".into(),
label: "Auto Complete".into(),
value: "/components/auto-complete",
label: "Auto Complete",
},
NavItemOption {
value: "/components/avatar".into(),
label: "Avatar".into(),
value: "/components/avatar",
label: "Avatar",
},
NavItemOption {
value: "/components/back-top".into(),
label: "Back Top".into(),
value: "/components/back-top",
label: "Back Top",
},
NavItemOption {
value: "/components/badge".into(),
label: "Badge".into(),
value: "/components/badge",
label: "Badge",
},
NavItemOption {
value: "/components/breadcrumb".into(),
label: "Breadcrumb".into(),
value: "/components/breadcrumb",
label: "Breadcrumb",
},
NavItemOption {
value: "/components/button".into(),
label: "Button".into(),
value: "/components/button",
label: "Button",
},
NavItemOption {
value: "/components/calendar".into(),
label: "Calendar".into(),
value: "/components/calendar",
label: "Calendar",
},
NavItemOption {
value: "/components/card".into(),
label: "Card".into(),
value: "/components/card",
label: "Card",
},
NavItemOption {
value: "/components/checkbox".into(),
label: "Checkbox".into(),
value: "/components/checkbox",
label: "Checkbox",
},
NavItemOption {
value: "/components/color-picker".into(),
label: "Color Picker".into(),
value: "/components/color-picker",
label: "Color Picker",
},
NavItemOption {
value: "/components/combobox".into(),
label: "Combobox".into(),
value: "/components/combobox",
label: "Combobox",
},
NavItemOption {
value: "/components/config-provider".into(),
label: "Config Provider".into(),
value: "/components/config-provider",
label: "Config Provider",
},
NavItemOption {
value: "/components/date-picker".into(),
label: "Date Picker".into(),
value: "/components/date-picker",
label: "Date Picker",
},
NavItemOption {
value: "/components/dialog".into(),
label: "Dialog".into(),
value: "/components/dialog",
label: "Dialog",
},
NavItemOption {
value: "/components/divider".into(),
label: "Divider".into(),
value: "/components/divider",
label: "Divider",
},
NavItemOption {
value: "/components/drawer".into(),
label: "Drawer".into(),
value: "/components/drawer",
label: "Drawer",
},
NavItemOption {
value: "/components/grid".into(),
label: "Grid".into(),
value: "/components/grid",
label: "Grid",
},
NavItemOption {
value: "/components/icon".into(),
label: "Icon".into(),
value: "/components/icon",
label: "Icon",
},
NavItemOption {
value: "/components/image".into(),
label: "Image".into(),
value: "/components/image",
label: "Image",
},
NavItemOption {
value: "/components/input".into(),
label: "Input".into(),
value: "/components/input",
label: "Input",
},
NavItemOption {
value: "/components/layout".into(),
label: "Layout".into(),
value: "/components/layout",
label: "Layout",
},
NavItemOption {
value: "/components/loading-bar".into(),
label: "Loading Bar".into(),
value: "/components/loading-bar",
label: "Loading Bar",
},
NavItemOption {
value: "/components/menu".into(),
label: "Menu".into(),
value: "/components/menu",
label: "Menu",
},
NavItemOption {
value: "/components/message-bar".into(),
label: "Message Bar".into(),
value: "/components/message-bar",
label: "Message Bar",
},
NavItemOption {
value: "/components/nav".into(),
label: "Nav".into(),
value: "/components/nav",
label: "Nav",
},
NavItemOption {
value: "/components/pagination".into(),
label: "Pagination".into(),
value: "/components/pagination",
label: "Pagination",
},
NavItemOption {
value: "/components/popover".into(),
label: "Popover".into(),
value: "/components/popover",
label: "Popover",
},
NavItemOption {
value: "/components/progress-bar".into(),
label: "ProgressBar".into(),
value: "/components/progress-bar",
label: "ProgressBar",
},
NavItemOption {
value: "/components/radio".into(),
label: "Radio".into(),
value: "/components/radio",
label: "Radio",
},
NavItemOption {
value: "/components/scrollbar".into(),
label: "Scrollbar".into(),
value: "/components/scrollbar",
label: "Scrollbar",
},
NavItemOption {
value: "/components/skeleton".into(),
label: "Skeleton".into(),
value: "/components/skeleton",
label: "Skeleton",
},
NavItemOption {
value: "/components/slider".into(),
label: "Slider".into(),
value: "/components/slider",
label: "Slider",
},
NavItemOption {
value: "/components/space".into(),
label: "Space".into(),
value: "/components/space",
label: "Space",
},
NavItemOption {
value: "/components/spin-button".into(),
label: "Spin Button".into(),
value: "/components/spin-button",
label: "Spin Button",
},
NavItemOption {
value: "/components/spinner".into(),
label: "Spinner".into(),
value: "/components/spinner",
label: "Spinner",
},
NavItemOption {
value: "/components/switch".into(),
label: "Switch".into(),
value: "/components/switch",
label: "Switch",
},
NavItemOption {
value: "/components/tab-list".into(),
label: "Tab List".into(),
value: "/components/tab-list",
label: "Tab List",
},
NavItemOption {
value: "/components/table".into(),
label: "Table".into(),
value: "/components/table",
label: "Table",
},
NavItemOption {
value: "/components/tag".into(),
label: "Tag".into(),
value: "/components/tag",
label: "Tag",
},
NavItemOption {
value: "/components/text".into(),
label: "Text".into(),
value: "/components/text",
label: "Text",
},
NavItemOption {
value: "/components/textarea".into(),
label: "Textarea".into(),
value: "/components/textarea",
label: "Textarea",
},
NavItemOption {
value: "/components/time-picker".into(),
label: "Time Picker".into(),
value: "/components/time-picker",
label: "Time Picker",
},
NavItemOption {
value: "/components/toast".into(),
label: "Toast".into(),
value: "/components/toast",
label: "Toast",
},
NavItemOption {
value: "/components/upload".into(),
label: "Upload".into(),
value: "/components/tooltip",
label: "Tooltip",
},
NavItemOption {
value: "/components/upload",
label: "Upload",
},
],
},

View file

@ -69,6 +69,7 @@ pub fn include_md(_token_stream: proc_macro::TokenStream) -> proc_macro::TokenSt
"TextareaMdPage" => "../../thaw/src/textarea/docs/mod.md",
"TimePickerMdPage" => "../../thaw/src/time_picker/docs/mod.md",
"ToastMdPage" => "../../thaw/src/toast/docs/mod.md",
"TooltipMdPage" => "../../thaw/src/tooltip/docs/mod.md",
"UploadMdPage" => "../../thaw/src/upload/docs/mod.md"
};

View file

@ -46,6 +46,7 @@ mod textarea;
mod theme;
mod time_picker;
mod toast;
mod tooltip;
mod upload;
pub use accordion::*;
@ -96,4 +97,5 @@ pub use thaw_utils::{ComponentRef, SignalWatch};
pub use theme::*;
pub use time_picker::*;
pub use toast::*;
pub use tooltip::*;
pub use upload::*;

View file

@ -0,0 +1,11 @@
# Tooltip
```rust demo
view! {
<Tooltip content="Example tooltip">
<Button>
"Example"
</Button>
</Tooltip>
}
```

3
thaw/src/tooltip/mod.rs Normal file
View file

@ -0,0 +1,3 @@
mod tooltip;
pub use tooltip::*;

View file

160
thaw/src/tooltip/tooltip.rs Normal file
View file

@ -0,0 +1,160 @@
use crate::ConfigInjection;
use leptos::{html, leptos_dom::helpers::TimeoutHandle, prelude::*};
use std::time::Duration;
use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement};
use thaw_utils::{class_list, mount_style};
#[slot]
pub struct TooltipContent {
#[prop(optional, into)]
class: MaybeProp<String>,
children: Children,
}
#[component]
pub fn Tooltip(
#[prop(optional, into)] class: MaybeProp<String>,
#[prop(optional)] tooltip_content: Option<TooltipContent>,
/// The text of the tooltip.
#[prop(optional, into)]
content: MaybeProp<String>,
/// Configure the positioning of the tooltip
#[prop(optional)]
position: TooltipPosition,
/// The tooltip's visual appearance.
#[prop(optional, into)]
appearance: MaybeProp<TooltipAppearance>,
children: Children,
) -> impl IntoView {
mount_style("tooltip", include_str!("./tooltip.css"));
let config_provider = ConfigInjection::expect_context();
let content_ref = NodeRef::<html::Div>::new();
let tooltip_ref = NodeRef::<html::Div>::new();
let is_show_content = RwSignal::new(false);
let content_handle = StoredValue::new(None::<TimeoutHandle>);
let on_mouse_enter = move |_| {
content_handle.update_value(|handle| {
if let Some(handle) = handle.take() {
handle.clear();
}
});
is_show_content.set(true);
};
let on_mouse_leave = move |_| {
content_handle.update_value(|handle| {
if let Some(handle) = handle.take() {
handle.clear();
}
*handle = set_timeout_with_handle(
move || {
is_show_content.set(false);
},
Duration::from_millis(100),
)
.ok();
});
};
Owner::on_cleanup(move || {
content_handle.update_value(|handle| {
if let Some(handle) = handle.take() {
handle.clear();
}
});
});
view! {
<Binder target_ref=tooltip_ref>
<div
class=class_list!["thaw-tooltip", class]
node_ref=tooltip_ref
on:mouseenter=on_mouse_enter
on:mouseleave=on_mouse_leave
>
{children()}
</div>
<Follower slot show=is_show_content placement=position>
<CSSTransition
node_ref=content_ref
name="tooltip-transition"
appear=is_show_content.get_untracked()
show=is_show_content
let:display
>
<div
class=class_list![
"thaw-config-provider thaw-tooltip-content",
move || appearance.get().map(|a| format!("thaw-tooltip-content--{}", a.as_str()))
]
data-thaw-id=config_provider.id().clone()
style=move || display.get().unwrap_or_default()
role="tooltip"
node_ref=content_ref
on:mouseenter=on_mouse_enter
on:mouseleave=on_mouse_leave
>
{
move || {
content.get()
}
}
<div class="thaw-tooltip-content__angle"></div>
</div>
</CSSTransition>
</Follower>
</Binder>
}
}
#[derive(Clone)]
pub enum TooltipAppearance {
Normal,
Inverted,
}
impl TooltipAppearance {
pub fn as_str(&self) -> &'static str {
match self {
Self::Normal => "normal",
Self::Inverted => "inverted",
}
}
}
#[derive(Default)]
pub enum TooltipPosition {
#[default]
Top,
Bottom,
Left,
Right,
TopStart,
TopEnd,
LeftStart,
LeftEnd,
RightStart,
RightEnd,
BottomStart,
BottomEnd,
}
impl From<TooltipPosition> for FollowerPlacement {
fn from(value: TooltipPosition) -> Self {
match value {
TooltipPosition::Top => Self::Top,
TooltipPosition::Bottom => Self::Bottom,
TooltipPosition::Left => Self::Left,
TooltipPosition::Right => Self::Right,
TooltipPosition::TopStart => Self::TopStart,
TooltipPosition::TopEnd => Self::TopEnd,
TooltipPosition::LeftStart => Self::LeftStart,
TooltipPosition::LeftEnd => Self::LeftEnd,
TooltipPosition::RightStart => Self::RightStart,
TooltipPosition::RightEnd => Self::RightEnd,
TooltipPosition::BottomStart => Self::BottomStart,
TooltipPosition::BottomEnd => Self::BottomEnd,
}
}
}