feat: improved Tooltip component

This commit is contained in:
luoxiao 2024-08-15 00:23:18 +08:00 committed by luoxiaozero
parent 5aac76c558
commit e4593a2bf3
7 changed files with 194 additions and 63 deletions

View file

@ -14,29 +14,21 @@
} }
.demo-demo__toolbar { .demo-demo__toolbar {
display: flex; height: 26px;
justify-content: center; box-sizing: border-box;
align-items: center; text-align: center;
padding: 0.4rem;
border-top: 1px dashed var(--demo-border-color); border-top: 1px dashed var(--demo-border-color);
border-bottom: 1px dashed var(--demo-border-color); border-bottom: 1px dashed var(--demo-border-color);
} }
.demo-demo__toolbar > .thaw-tooltip {
height: 24px;
}
.demo-demo__toolbar--code { .demo-demo__toolbar--code {
border-bottom-width: 0; border-bottom-width: 0;
} }
.demo-demo__toolbar-btn {
display: flex;
cursor: pointer;
color: var(--demo-color);
transition: color .3s;
}
.demo-demo__toolbar-btn:hover {
color: var(--demo-color-hover);
}
.demo-demo__code { .demo-demo__code {
font-weight: 400; font-weight: 400;
font-size: 0.875em; font-size: 0.875em;

View file

@ -17,13 +17,9 @@ pub fn Demo(demo_code: DemoCode, #[prop(optional)] children: Option<Children>) -
let mut css_vars = String::new(); let mut css_vars = String::new();
theme.with(|theme| { theme.with(|theme| {
if theme.color.color_scheme == "dark" { if theme.color.color_scheme == "dark" {
css_vars.push_str("--demo-color: #ffffff60;");
css_vars.push_str("--demo-color-hover: #ffffffe0;");
css_vars.push_str("--demo-border-color: #383f52;"); css_vars.push_str("--demo-border-color: #383f52;");
css_vars.push_str("--demo-background-color: #242832;"); css_vars.push_str("--demo-background-color: #242832;");
} else { } else {
css_vars.push_str("--demo-color: #00000060;");
css_vars.push_str("--demo-color-hover: #000000e0;");
css_vars.push_str(&format!("--demo-border-color: var(--colorNeutralStroke2);",)); css_vars.push_str(&format!("--demo-border-color: var(--colorNeutralStroke2);",));
css_vars.push_str("--demo-background-color: #f9fafb;"); css_vars.push_str("--demo-background-color: #f9fafb;");
} }
@ -52,31 +48,24 @@ pub fn Demo(demo_code: DemoCode, #[prop(optional)] children: Option<Children>) -
view! { view! {
<div class="demo-demo__view">{children()}</div> <div class="demo-demo__view">{children()}</div>
<div class="demo-demo__toolbar" class=("demo-demo__toolbar--code", move || !is_show_code.get())> <div class="demo-demo__toolbar" class=("demo-demo__toolbar--code", move || !is_show_code.get())>
<Popover appearance=PopoverAppearance::Inverted> <Tooltip
<PopoverTrigger slot> content=MaybeSignal::derive(move || if is_show_code.get() {
<span on:click=move |_| is_show_code.update(|show| *show = !*show) class="demo-demo__toolbar-btn"> "Hide code".to_string()
{
move || if is_show_code.get() {
view! {
<Icon icon=icondata::LuCode2/>
}
} else { } else {
view! { "Show code".to_string()
<Icon icon=icondata::LuCode/> })
} appearance=TooltipAppearance::Inverted
} >
} <Button icon=MaybeProp::derive(move || if is_show_code.get() {
</span> Some(icondata::LuCode2)
</PopoverTrigger>
{
move || if is_show_code.get() {
"Hide code"
} else { } else {
"Show code" Some(icondata::LuCode)
} })
} on:click=move |_| is_show_code.update(|show| *show = !*show)
</Popover> appearance=ButtonAppearance::Transparent
size=ButtonSize::Small
/>
</Tooltip>
</div> </div>
}.into() }.into()
} else { } else {

View file

@ -44,6 +44,9 @@ pub struct ColorTheme {
pub color_neutral_stroke_accessible_hover: String, pub color_neutral_stroke_accessible_hover: String,
pub color_neutral_stroke_accessible_pressed: String, pub color_neutral_stroke_accessible_pressed: String,
pub color_neutral_shadow_ambient: String,
pub color_neutral_shadow_key: String,
pub color_neutral_stencil_1: String, pub color_neutral_stencil_1: String,
pub color_neutral_stencil_2: String, pub color_neutral_stencil_2: String,
@ -160,6 +163,9 @@ impl ColorTheme {
color_neutral_stroke_accessible_hover: "#575757".into(), color_neutral_stroke_accessible_hover: "#575757".into(),
color_neutral_stroke_accessible_pressed: "#4d4d4d".into(), color_neutral_stroke_accessible_pressed: "#4d4d4d".into(),
color_neutral_shadow_ambient: "rgba(0,0,0,0.12)".into(),
color_neutral_shadow_key: "rgba(0,0,0,0.14)".into(),
color_neutral_stencil_1: "#e6e6e6".into(), color_neutral_stencil_1: "#e6e6e6".into(),
color_neutral_stencil_2: "#fafafa".into(), color_neutral_stencil_2: "#fafafa".into(),
@ -277,6 +283,9 @@ impl ColorTheme {
color_neutral_stroke_accessible_hover: "#bdbdbd".into(), color_neutral_stroke_accessible_hover: "#bdbdbd".into(),
color_neutral_stroke_accessible_pressed: "#b3b3b3".into(), color_neutral_stroke_accessible_pressed: "#b3b3b3".into(),
color_neutral_shadow_ambient: "rgba(0,0,0,0.24)".into(),
color_neutral_shadow_key: "rgba(0,0,0,0.28)".into(),
color_neutral_stencil_1: "#575757".into(), color_neutral_stencil_1: "#575757".into(),
color_neutral_stencil_2: "#333333".into(), color_neutral_stencil_2: "#333333".into(),

View file

@ -9,3 +9,25 @@ view! {
</Tooltip> </Tooltip>
} }
``` ```
### Appearance: inverted
```rust demo
view! {
<Tooltip content="Example tooltip" appearance=TooltipAppearance::Inverted>
<Button>
"Example"
</Button>
</Tooltip>
}
```
### Textarea Props
| Name | Type | Default | Description |
| ---------- | -------------------------------- | ------------------------- | -------------------------------------- |
| class | `MaybeProp<String>` | `Default::default()` | |
| content | `Option<MaybeSignal<String>>` | `None` | The text of the tooltip. |
| position | `TooltipPosition` | `TooltipPosition::Top` | Configure the position of the tooltip. |
| appearance | `MaybeSignal<TooltipAppearance>` | `TooltipAppearance::None` | The tooltip's visual appearance. |
| children | `Children` | | |

View file

@ -0,0 +1,130 @@
div.thaw-tooltip-content {
position: relative;
transform-origin: inherit;
padding: 4px 11px 6px;
border-radius: var(--borderRadiusMedium);
border: 1px solid var(--colorTransparentStroke);
line-height: var(--lineHeightBase200);
font-size: var(--fontSizeBase200);
font-family: var(--fontFamilyBase);
max-width: 240px;
overflow-wrap: break-word;
box-sizing: border-box;
filter: drop-shadow(0 0 2px var(--colorNeutralShadowAmbient))
drop-shadow(0 4px 8px var(--colorNeutralShadowKey));
}
div.thaw-tooltip-content--normal {
background-color: var(--colorNeutralBackground1);
color: var(--colorNeutralForeground1);
}
div.thaw-tooltip-content--inverted {
background-color: var(--colorNeutralBackgroundStatic);
color: var(--colorNeutralForegroundStaticInverted);
}
.thaw-tooltip-content__angle {
position: absolute;
background-color: inherit;
width: 8px;
height: 8px;
}
.thaw-tooltip {
display: inline-block;
}
[data-thaw-placement="top-start"] > .thaw-tooltip-content,
[data-thaw-placement="top-end"] > .thaw-tooltip-content,
[data-thaw-placement="top"] > .thaw-tooltip-content {
margin-bottom: 7px;
}
[data-thaw-placement="top-start"] .thaw-tooltip-content__angle,
[data-thaw-placement="top-end"] .thaw-tooltip-content__angle,
[data-thaw-placement="top"] .thaw-tooltip-content__angle {
transform: rotate(45deg) translateX(-7px);
bottom: -10px;
left: 50%;
}
[data-thaw-placement="bottom-start"] > .thaw-tooltip-content,
[data-thaw-placement="bottom-end"] > .thaw-tooltip-content,
[data-thaw-placement="bottom"] > .thaw-tooltip-content {
margin-top: 7px;
}
[data-thaw-placement="bottom-start"] .thaw-tooltip-content__angle,
[data-thaw-placement="bottom-end"] .thaw-tooltip-content__angle,
[data-thaw-placement="bottom"] .thaw-tooltip-content__angle {
transform: rotate(45deg) translateY(7px);
top: -10px;
left: 50%;
}
[data-thaw-placement="left-start"] > .thaw-tooltip-content,
[data-thaw-placement="left-end"] > .thaw-tooltip-content,
[data-thaw-placement="left"] > .thaw-tooltip-content {
margin-right: 7px;
}
[data-thaw-placement="left-start"] .thaw-tooltip-content__angle,
[data-thaw-placement="left-end"] .thaw-tooltip-content__angle,
[data-thaw-placement="left"] .thaw-tooltip-content__angle {
transform: rotate(45deg) translateX(-7px);
top: 50%;
right: -10px;
}
[data-thaw-placement="right-start"] > .thaw-tooltip-content,
[data-thaw-placement="right-end"] > .thaw-tooltip-content,
[data-thaw-placement="right"] > .thaw-tooltip-content {
margin-left: 7px;
}
[data-thaw-placement="right-start"] .thaw-tooltip-content__angle,
[data-thaw-placement="right-end"] .thaw-tooltip-content__angle,
[data-thaw-placement="right"] .thaw-tooltip-content__angle {
transform: rotate(45deg) translateY(-7px);
top: 50%;
left: -10px;
}
[data-thaw-placement="bottom-start"] .thaw-tooltip-content__angle,
[data-thaw-placement="top-start"] .thaw-tooltip-content__angle {
left: 16px;
}
[data-thaw-placement="bottom-end"] .thaw-tooltip-content__angle,
[data-thaw-placement="top-end"] .thaw-tooltip-content__angle {
left: initial;
right: 7px;
}
[data-thaw-placement="right-start"] .thaw-tooltip-content__angle,
[data-thaw-placement="left-start"] .thaw-tooltip-content__angle {
top: 16px;
}
[data-thaw-placement="right-end"] .thaw-tooltip-content__angle,
[data-thaw-placement="left-end"] .thaw-tooltip-content__angle {
top: initial;
bottom: 7px;
}
.thaw-tooltip-content.tooltip-transition-enter-from,
.thaw-tooltip-content.tooltip-transition-leave-to {
opacity: 0;
transform: scale(0.85);
}
.thaw-tooltip-content.tooltip-transition-enter-to,
.thaw-tooltip-content.tooltip-transition-leave-from {
transform: scale(1);
opacity: 1;
}
.thaw-tooltip-content.tooltip-transition-leave-active,
.thaw-tooltip-content.tooltip-transition-enter-active {
transition: opacity var(--durationNormal) var(--curveDecelerateMid),
transform var(--durationNormal) var(--curveDecelerateMid);
}

View file

@ -4,26 +4,18 @@ use std::time::Duration;
use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement}; use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement};
use thaw_utils::{class_list, mount_style}; use thaw_utils::{class_list, mount_style};
#[slot]
pub struct TooltipContent {
#[prop(optional, into)]
class: MaybeProp<String>,
children: Children,
}
#[component] #[component]
pub fn Tooltip( pub fn Tooltip(
#[prop(optional, into)] class: MaybeProp<String>, #[prop(optional, into)] class: MaybeProp<String>,
#[prop(optional)] tooltip_content: Option<TooltipContent>,
/// The text of the tooltip. /// The text of the tooltip.
#[prop(optional, into)] #[prop(optional, into)]
content: MaybeProp<String>, content: Option<MaybeSignal<String>>,
/// Configure the positioning of the tooltip /// Configure the position of the tooltip.
#[prop(optional)] #[prop(optional)]
position: TooltipPosition, position: TooltipPosition,
/// The tooltip's visual appearance. /// The tooltip's visual appearance.
#[prop(optional, into)] #[prop(optional, into)]
appearance: MaybeProp<TooltipAppearance>, appearance: MaybeSignal<TooltipAppearance>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("tooltip", include_str!("./tooltip.css")); mount_style("tooltip", include_str!("./tooltip.css"));
@ -86,7 +78,7 @@ pub fn Tooltip(
<div <div
class=class_list![ class=class_list![
"thaw-config-provider thaw-tooltip-content", "thaw-config-provider thaw-tooltip-content",
move || appearance.get().map(|a| format!("thaw-tooltip-content--{}", a.as_str())) move || format!("thaw-tooltip-content--{}", appearance.get().as_str())
] ]
data-thaw-id=config_provider.id().clone() data-thaw-id=config_provider.id().clone()
style=move || display.get().unwrap_or_default() style=move || display.get().unwrap_or_default()
@ -95,11 +87,7 @@ pub fn Tooltip(
on:mouseenter=on_mouse_enter on:mouseenter=on_mouse_enter
on:mouseleave=on_mouse_leave on:mouseleave=on_mouse_leave
> >
{ {move || { content.as_ref().map(|c| c.get()).unwrap_or_default() }}
move || {
content.get()
}
}
<div class="thaw-tooltip-content__angle"></div> <div class="thaw-tooltip-content__angle"></div>
</div> </div>
</CSSTransition> </CSSTransition>
@ -108,8 +96,9 @@ pub fn Tooltip(
} }
} }
#[derive(Clone)] #[derive(Clone, Default)]
pub enum TooltipAppearance { pub enum TooltipAppearance {
#[default]
Normal, Normal,
Inverted, Inverted,
} }