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 {
display: flex;
justify-content: center;
align-items: center;
padding: 0.4rem;
height: 26px;
box-sizing: border-box;
text-align: center;
border-top: 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 {
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 {
font-weight: 400;
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();
theme.with(|theme| {
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-background-color: #242832;");
} 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("--demo-background-color: #f9fafb;");
}
@ -52,31 +48,24 @@ pub fn Demo(demo_code: DemoCode, #[prop(optional)] children: Option<Children>) -
view! {
<div class="demo-demo__view">{children()}</div>
<div class="demo-demo__toolbar" class=("demo-demo__toolbar--code", move || !is_show_code.get())>
<Popover appearance=PopoverAppearance::Inverted>
<PopoverTrigger slot>
<span on:click=move |_| is_show_code.update(|show| *show = !*show) class="demo-demo__toolbar-btn">
{
move || if is_show_code.get() {
view! {
<Icon icon=icondata::LuCode2/>
}
} else {
view! {
<Icon icon=icondata::LuCode/>
}
}
}
</span>
</PopoverTrigger>
{
move || if is_show_code.get() {
"Hide code"
<Tooltip
content=MaybeSignal::derive(move || if is_show_code.get() {
"Hide code".to_string()
} else {
"Show code".to_string()
})
appearance=TooltipAppearance::Inverted
>
<Button icon=MaybeProp::derive(move || if is_show_code.get() {
Some(icondata::LuCode2)
} else {
"Show code"
}
}
</Popover>
Some(icondata::LuCode)
})
on:click=move |_| is_show_code.update(|show| *show = !*show)
appearance=ButtonAppearance::Transparent
size=ButtonSize::Small
/>
</Tooltip>
</div>
}.into()
} else {

View file

@ -44,6 +44,9 @@ pub struct ColorTheme {
pub color_neutral_stroke_accessible_hover: 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_2: String,
@ -160,6 +163,9 @@ impl ColorTheme {
color_neutral_stroke_accessible_hover: "#575757".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_2: "#fafafa".into(),
@ -277,6 +283,9 @@ impl ColorTheme {
color_neutral_stroke_accessible_hover: "#bdbdbd".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_2: "#333333".into(),

View file

@ -8,4 +8,26 @@ view! {
</Button>
</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

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

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_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
content: Option<MaybeSignal<String>>,
/// Configure the position of the tooltip.
#[prop(optional)]
position: TooltipPosition,
/// The tooltip's visual appearance.
#[prop(optional, into)]
appearance: MaybeProp<TooltipAppearance>,
appearance: MaybeSignal<TooltipAppearance>,
children: Children,
) -> impl IntoView {
mount_style("tooltip", include_str!("./tooltip.css"));
@ -86,7 +78,7 @@ pub fn Tooltip(
<div
class=class_list![
"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()
style=move || display.get().unwrap_or_default()
@ -95,11 +87,7 @@ pub fn Tooltip(
on:mouseenter=on_mouse_enter
on:mouseleave=on_mouse_leave
>
{
move || {
content.get()
}
}
{move || { content.as_ref().map(|c| c.get()).unwrap_or_default() }}
<div class="thaw-tooltip-content__angle"></div>
</div>
</CSSTransition>
@ -108,8 +96,9 @@ pub fn Tooltip(
}
}
#[derive(Clone)]
#[derive(Clone, Default)]
pub enum TooltipAppearance {
#[default]
Normal,
Inverted,
}