mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
feat: adds Tooltip component
This commit is contained in:
parent
61284d57fc
commit
5aac76c558
9 changed files with 290 additions and 106 deletions
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
};
|
||||
|
||||
|
|
|
@ -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::*;
|
||||
|
|
11
thaw/src/tooltip/docs/mod.md
Normal file
11
thaw/src/tooltip/docs/mod.md
Normal 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
3
thaw/src/tooltip/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod tooltip;
|
||||
|
||||
pub use tooltip::*;
|
0
thaw/src/tooltip/tooltip.css
Normal file
0
thaw/src/tooltip/tooltip.css
Normal file
160
thaw/src/tooltip/tooltip.rs
Normal file
160
thaw/src/tooltip/tooltip.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue