feat: popover appearance

This commit is contained in:
luoxiao 2024-06-11 16:48:41 +08:00
parent c41d177782
commit f999e252e4
6 changed files with 93 additions and 77 deletions

View file

@ -68,7 +68,7 @@ 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 tooltip=true>
<Popover appearance=PopoverAppearance::Inverted>
<PopoverTrigger slot>
<span on:click=move |_| is_show_code.update(|show| *show = !*show) class="demo-demo__toolbar-btn">
{

View file

@ -54,7 +54,7 @@ view! {
</Popover>
</GridItem>
<GridItem>
<Popover placement=PopoverPlacement::LeftStart>
<Popover placement=PopoverPlacement::LeftStart trigger_type=PopoverTriggerType::Click>
<PopoverTrigger slot>
<Button>"Left Start"</Button>
</PopoverTrigger>
@ -70,7 +70,7 @@ view! {
</Popover>
</GridItem>
<GridItem>
<Popover placement=PopoverPlacement::Left>
<Popover placement=PopoverPlacement::Left trigger_type=PopoverTriggerType::Click>
<PopoverTrigger slot>
<Button>"Left"</Button>
</PopoverTrigger>
@ -129,12 +129,18 @@ view! {
}
```
### Tooltip
### Appearance
```rust demo
view! {
<Space>
<Popover tooltip=true>
<Popover appearance=PopoverAppearance::Brand>
<PopoverTrigger slot>
<Button>"Hover"</Button>
</PopoverTrigger>
"Content"
</Popover>
<Popover appearance=PopoverAppearance::Inverted>
<PopoverTrigger slot>
<Button>"Hover"</Button>
</PopoverTrigger>

View file

@ -1,32 +1,33 @@
use crate::ConfigInjection;
use leptos::{leptos_dom::helpers::TimeoutHandle, *};
use palette::bool_mask::BoolMask;
use std::time::Duration;
use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement};
use thaw_utils::{add_event_listener, class_list, mount_style, OptionalProp};
use thaw_utils::{add_event_listener, class_list, mount_style};
#[slot]
pub struct PopoverTrigger {
#[prop(optional, into)]
class: OptionalProp<MaybeSignal<String>>,
class: MaybeProp<String>,
children: Children,
}
#[component]
pub fn Popover(
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] class: MaybeProp<String>,
#[prop(optional)] trigger_type: PopoverTriggerType,
popover_trigger: PopoverTrigger,
#[prop(optional)] placement: PopoverPlacement,
#[prop(optional)] tooltip: bool,
#[prop(optional, into)] appearance: Option<MaybeSignal<PopoverAppearance>>,
children: Children,
) -> impl IntoView {
mount_style("popover", include_str!("./popover.css"));
let config_provider = ConfigInjection::use_();
let popover_ref = create_node_ref::<html::Div>();
let target_ref = create_node_ref::<html::Div>();
let is_show_popover = create_rw_signal(false);
let show_popover_handle = store_value(None::<TimeoutHandle>);
let popover_ref = NodeRef::<html::Div>::new();
let target_ref = NodeRef::<html::Div>::new();
let is_show_popover = RwSignal::new(false);
let show_popover_handle = StoredValue::new(None::<TimeoutHandle>);
let on_mouse_enter = move |_| {
if trigger_type != PopoverTriggerType::Hover {
@ -63,6 +64,9 @@ pub fn Popover(
if trigger_type != PopoverTriggerType::Click {
return;
}
if !is_show_popover.get_untracked() {
return;
}
let el = ev.target();
let mut el: Option<web_sys::Element> =
el.into_js_result().map_or(None, |el| Some(el.into()));
@ -101,7 +105,7 @@ pub fn Popover(
view! {
<Binder target_ref>
<div
class=class_list!["thaw-popover-trigger", trigger_class.map(| c | move || c.get())]
class=class_list!["thaw-popover-trigger", trigger_class]
ref=target_ref
on:mouseenter=on_mouse_enter
on:mouseleave=on_mouse_leave
@ -117,11 +121,11 @@ pub fn Popover(
let:display
>
<div
class=if tooltip {
"thaw-config-provider thaw-popover-surface thaw-popover--tooltip" }
else {
"thaw-config-provider thaw-popover-surface"
}
class=class_list![
"thaw-config-provider thaw-popover-surface",
appearance.map(|appearance| move || format!("thaw-popover-surface--{}", appearance.get().as_str())),
class
]
data-thaw-id=config_provider.id().clone()
style=move || display.get()
@ -129,7 +133,7 @@ pub fn Popover(
on:mouseenter=on_mouse_enter
on:mouseleave=on_mouse_leave
>
<div class=class.map(|c| move || c.get())>{children()}</div>
{children()}
<div class="thaw-popover-surface__angle">
</div>
</div>
@ -139,6 +143,21 @@ pub fn Popover(
}
}
#[derive(Clone)]
pub enum PopoverAppearance {
Brand,
Inverted,
}
impl PopoverAppearance {
pub fn as_str(&self) -> &'static str {
match self {
PopoverAppearance::Brand => "brand",
PopoverAppearance::Inverted => "inverted",
}
}
}
#[derive(Default, PartialEq, Clone)]
pub enum PopoverTriggerType {
#[default]

View file

@ -14,6 +14,16 @@ div.thaw-popover-surface {
color: var(--colorNeutralForeground1);
}
div.thaw-popover-surface--brand {
background-color: var(--colorBrandBackground);
color: var(--colorNeutralForegroundOnBrand);
}
div.thaw-popover-surface--inverted {
background-color: var(--colorNeutralBackgroundStatic);
color: var(--colorNeutralForegroundStaticInverted);
}
.thaw-popover-surface__angle {
position: absolute;
background-color: inherit;
@ -53,99 +63,70 @@ div.thaw-popover-surface {
left: 50%;
}
[data-thaw-placement="left-start"] > .thaw-popover,
[data-thaw-placement="left-end"] > .thaw-popover,
[data-thaw-placement="left"] > .thaw-popover {
[data-thaw-placement="left-start"] > .thaw-popover-surface,
[data-thaw-placement="left-end"] > .thaw-popover-surface,
[data-thaw-placement="left"] > .thaw-popover-surface {
margin-right: 10px;
box-shadow: 3px 0 6px -4px rgba(0, 0, 0, 0.12),
6px 0 16px 0 rgba(0, 0, 0, 0.08), 9px 0 28px 8px rgba(0, 0, 0, 0.05);
}
[data-thaw-placement="left-start"] .thaw-popover-surface__angle,
[data-thaw-placement="left-end"] .thaw-popover-surface__angle,
[data-thaw-placement="left"] .thaw-popover-surface__angle {
width: 10px;
height: 100%;
right: -10px;
top: 0;
bottom: 0;
}
[data-thaw-placement="left-start"] .thaw-popover-surface__angle::before,
[data-thaw-placement="left-end"] .thaw-popover-surface__angle::before,
[data-thaw-placement="left"] .thaw-popover-surface__angle::before {
top: 50%;
transform: rotate(45deg) translateX(-7px);
top: 50%;
right: -10px;
}
[data-thaw-placement="right-start"] > .thaw-popover,
[data-thaw-placement="right-end"] > .thaw-popover,
[data-thaw-placement="right"] > .thaw-popover {
[data-thaw-placement="right-start"] > .thaw-popover-surface,
[data-thaw-placement="right-end"] > .thaw-popover-surface,
[data-thaw-placement="right"] > .thaw-popover-surface {
margin-left: 10px;
box-shadow: -3px 0 6px -4px rgba(0, 0, 0, 0.12),
-6px 0 16px 0 rgba(0, 0, 0, 0.08), -9px 0 28px 8px rgba(0, 0, 0, 0.05);
}
[data-thaw-placement="right-start"] .thaw-popover-surface__angle,
[data-thaw-placement="right-end"] .thaw-popover-surface__angle,
[data-thaw-placement="right"] .thaw-popover-surface__angle {
width: 10px;
height: 100%;
left: -10px;
top: 0;
bottom: 0;
}
[data-thaw-placement="right-start"] .thaw-popover-surface__angle::before,
[data-thaw-placement="right-end"] .thaw-popover-surface__angle::before,
[data-thaw-placement="right"] .thaw-popover-surface__angle::before {
top: 50%;
transform: rotate(45deg) translateY(-7px);
top: 50%;
left: -10px;
}
[data-thaw-placement="bottom-start"] .thaw-popover-surface__angle,
[data-thaw-placement="top-start"] .thaw-popover-surface__angle {
left: 16px;
}
[data-thaw-placement="bottom-end"] .thaw-popover-surface__angle::before,
[data-thaw-placement="top-end"] .thaw-popover-surface__angle::before {
[data-thaw-placement="bottom-end"] .thaw-popover-surface__angle,
[data-thaw-placement="top-end"] .thaw-popover-surface__angle {
left: initial;
right: 7px;
}
[data-thaw-placement="right-start"] .thaw-popover-surface__angle::before,
[data-thaw-placement="left-start"] .thaw-popover-surface__angle::before {
[data-thaw-placement="right-start"] .thaw-popover-surface__angle,
[data-thaw-placement="left-start"] .thaw-popover-surface__angle {
top: 16px;
}
[data-thaw-placement="right-end"] .thaw-popover-surface__angle::before,
[data-thaw-placement="left-end"] .thaw-popover-surface__angle::before {
[data-thaw-placement="right-end"] .thaw-popover-surface__angle,
[data-thaw-placement="left-end"] .thaw-popover-surface__angle {
top: initial;
bottom: 7px;
}
.thaw-popover.popover-transition-enter-from,
.thaw-popover.popover-transition-leave-to {
.thaw-popover-surface.popover-transition-enter-from,
.thaw-popover-surface.popover-transition-leave-to {
opacity: 0;
transform: scale(0.85);
}
.thaw-popover.popover-transition-enter-to,
.thaw-popover.popover-transition-leave-from {
.thaw-popover-surface.popover-transition-enter-to,
.thaw-popover-surface.popover-transition-leave-from {
transform: scale(1);
opacity: 1;
}
.thaw-popover.popover-transition-enter-active {
transition: box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1),
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.15s cubic-bezier(0, 0, 0.2, 1),
transform 0.15s cubic-bezier(0, 0, 0.2, 1);
}
.thaw-popover.popover-transition-leave-active {
transition: box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1),
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.15s cubic-bezier(0.4, 0, 1, 1),
transform 0.15s cubic-bezier(0.4, 0, 1, 1);
.thaw-popover-surface.popover-transition-leave-active,
.thaw-popover-surface.popover-transition-enter-active {
transition: box-shadow var(--durationSlow) var(--curveDecelerateMid),
background-color var(--durationSlow) var(--curveDecelerateMid),
color var(--durationSlow) var(--curveDecelerateMid),
opacity var(--durationNormal) var(--curveDecelerateMid),
transform var(--durationNormal) var(--curveDecelerateMid);
}

View file

@ -2,6 +2,7 @@ use thaw_macro::WriteCSSVars;
#[derive(Clone, WriteCSSVars)]
pub struct ColorTheme {
pub color_neutral_background_static: String,
pub color_neutral_background_disabled: String,
pub color_neutral_background_1: String,
pub color_neutral_background_1_hover: String,
@ -15,6 +16,7 @@ pub struct ColorTheme {
pub color_neutral_background_5: String,
pub color_neutral_background_6: String,
pub color_neutral_foreground_static_inverted: String,
pub color_neutral_foreground_disabled: String,
pub color_neutral_foreground_1: String,
pub color_neutral_foreground_1_hover: String,
@ -100,6 +102,7 @@ pub struct ColorTheme {
impl ColorTheme {
pub fn light() -> Self {
Self {
color_neutral_background_static: "#333333".into(),
color_neutral_background_disabled: "#f0f0f0".into(),
color_neutral_background_1: "#ffffff".into(),
color_neutral_background_1_hover: "#f5f5f5".into(),
@ -113,6 +116,7 @@ impl ColorTheme {
color_neutral_background_5: "#ebebeb".into(),
color_neutral_background_6: "#e6e6e6".into(),
color_neutral_foreground_static_inverted: "#ffffff".into(),
color_neutral_foreground_disabled: "#bdbdbd".into(),
color_neutral_foreground_1: "#242424".into(),
color_neutral_foreground_1_hover: "#242424".into(),
@ -199,6 +203,7 @@ impl ColorTheme {
pub fn dark() -> Self {
Self {
color_neutral_background_static: "#3d3d3d".into(),
color_neutral_background_disabled: "#141414".into(),
color_neutral_background_1: "#292929".into(),
color_neutral_background_1_hover: "#3d3d3d".into(),
@ -212,6 +217,7 @@ impl ColorTheme {
color_neutral_background_5: "#000000".into(),
color_neutral_background_6: "#333333".into(),
color_neutral_foreground_static_inverted: "#ffffff".into(),
color_neutral_foreground_disabled: "#5c5c5c".into(),
color_neutral_foreground_1: "#fff".into(),
color_neutral_foreground_1_hover: "#fff".into(),

View file

@ -260,7 +260,11 @@ fn get_timeout(mut delays: Vec<String>, durations: &Vec<String>) -> u64 {
return 0;
}
let s = s.split_at(s.len() - 1).0;
let s = if s.ends_with("ms") {
s.split_at(s.len() - 2).0
} else {
s.split_at(s.len() - 1).0
};
(s.parse::<f32>().unwrap_or_default() * 1000.0).floor() as u64
}