Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/back top #169

Merged
merged 3 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions demo/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ fn TheRouter(is_routing: RwSignal<bool>) -> impl IntoView {
<Route path="/alert" view=AlertMdPage/>
<Route path="/auto-complete" view=AutoCompleteMdPage/>
<Route path="/avatar" view=AvatarMdPage/>
<Route path="/back-top" view=BackTopMdPage/>
<Route path="/badge" view=BadgeMdPage/>
<Route path="/breadcrumb" view=BreadcrumbMdPage/>
<Route path="/button" view=ButtonMdPage/>
Expand Down
4 changes: 4 additions & 0 deletions demo/src/pages/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ pub(crate) fn gen_menu_data() -> Vec<MenuGroupOption> {
MenuGroupOption {
label: "Navigation Components".into(),
children: vec![
MenuItemOption {
value: "back-top".into(),
label: "Back Top".into(),
},
MenuItemOption {
value: "breadcrumb".into(),
label: "Breadcrumb".into(),
Expand Down
46 changes: 46 additions & 0 deletions demo_markdown/docs/back_top/mod.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Back Top

BackTop will find its first scrollable ascendant element and listen scroll event on it.

```rust demo
view! {
<BackTop />
}
```

### Visibility height

```rust demo
view! {
<BackTop bottom=100 visibility_height=280>
<div style="width: 200px; text-align: center;">
"Visibility Height: 280px"
</div>
</BackTop>
}
```

### Change position

```rust demo
view! {
<BackTop right=40 bottom=160>
<div style="width: 200px; text-align: center;">
"Change Position"
</div>
</BackTop>
}
```

### BackTop Props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| class | `OptionalProp<MaybeSignal<String>>` | `Default::default()` | Addtional classes for the back top element. |
| right | `MaybeSignal<i32>` | `40` | The width of BackTop from the right side of the page. |
| bottom | `MaybeSignal<i32>` | `40` | The height of BackTop from the bottom of the page. |
| bottom | `MaybeSignal<i32>` | `180` | BackTop's trigger scroll top. |
| children | `Option<Children>` | `None` | BackTop's content. |

<div style="height: 600px">
</div>
1 change: 1 addition & 0 deletions demo_markdown/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub fn include_md(_token_stream: proc_macro::TokenStream) -> proc_macro::TokenSt
"AlertMdPage" => "../docs/alert/mod.md",
"AutoCompleteMdPage" => "../docs/auto_complete/mod.md",
"AvatarMdPage" => "../docs/avatar/mod.md",
"BackTopMdPage" => "../docs/back_top/mod.md",
"BadgeMdPage" => "../docs/badge/mod.md",
"BreadcrumbMdPage" => "../docs/breadcrumb/mod.md",
"ButtonMdPage" => "../docs/button/mod.md",
Expand Down
2 changes: 2 additions & 0 deletions thaw/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ web-sys = { version = "0.3.69", features = [
"File",
"FileList",
"DataTransfer",
"ScrollToOptions",
"ScrollBehavior",
] }
wasm-bindgen = "0.2.92"
icondata_core = "0.1.0"
Expand Down
60 changes: 60 additions & 0 deletions thaw/src/back_top/back-top.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
.thaw-back-top {
position: fixed;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1),
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border-radius: 22px;
height: 44px;
min-width: 44px;
box-shadow: 0 2px 8px 0px rgba(0, 0, 0, 0.12);
background-color: var(--thaw-background-color);
}

.thaw-back-top.fade-in-scale-up-transition-leave-active {
transform-origin: inherit;
transition: opacity 0.2s cubic-bezier(0.4, 0, 1, 1),
transform 0.2s cubic-bezier(0.4, 0, 1, 1);
}

.thaw-back-top.fade-in-scale-up-transition-enter-active {
transform-origin: inherit;
transition: opacity 0.2s cubic-bezier(0, 0, 0.2, 1),
transform 0.2s cubic-bezier(0, 0, 0.2, 1);
}

.thaw-back-top.fade-in-scale-up-transition-enter-from,
.thaw-back-top.fade-in-scale-up-transition-leave-to {
opacity: 0;
transform: scale(0.9);
}

.thaw-back-top.fade-in-scale-up-transition-leave-from,
.thaw-back-top.fade-in-scale-up-transition-enter-to {
opacity: 1;
transform: scale(1);
}

.thaw-back-top > svg {
font-size: 24px;
transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.thaw-back-top:hover {
box-shadow: 0 2px 12px 0px #0000002e;
}

.thaw-back-top:hover > svg {
color: var(--thaw-icon-color-hover);
}

.thaw-back-top:active {
box-shadow: 0 2px 12px 0px #0000002e;
}

.thaw-back-top:active svg {
color: var(--thaw-icon-color-active);
}
126 changes: 126 additions & 0 deletions thaw/src/back_top/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
mod theme;

pub use theme::BackTopTheme;

use crate::{use_theme, Icon, Theme};
use leptos::{html::ToHtmlElement, *};
use thaw_components::{CSSTransition, Fallback, OptionComp, Teleport};
use thaw_utils::{
add_event_listener, class_list, get_scroll_parent, mount_style, EventListenerHandle, OptionalProp,
};

#[component]
pub fn BackTop(
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(default=40.into(), into)] right: MaybeSignal<i32>,
#[prop(default=40.into(), into)] bottom: MaybeSignal<i32>,
#[prop(default=180.into(), into)] visibility_height: MaybeSignal<i32>,
#[prop(optional)] children: Option<Children>,
) -> impl IntoView {
mount_style("back-top", include_str!("./back-top.css"));
let theme = use_theme(Theme::light);
let style = Memo::new(move |_| {
let mut style = String::new();
style.push_str(&format!("right: {}px;", right.get_untracked()));
style.push_str(&format!("bottom: {}px;", bottom.get_untracked()));
theme.with(|theme| {
style.push_str(&format!(
"--thaw-icon-color-hover: {};",
theme.common.color_primary_hover
));
style.push_str(&format!(
"--thaw-icon-color-active: {};",
theme.common.color_primary_active
));
style.push_str(&format!(
"--thaw-background-color: {};",
theme.back_top.background_color
));
});
style
});
let placeholder_ref = NodeRef::<html::Div>::new();
let back_top_ref = NodeRef::<html::Div>::new();
let is_show_back_top = RwSignal::new(false);
let scroll_top = RwSignal::new(0);

let _ = watch(
move || scroll_top.get(),
move |scroll_top, _, _| {
is_show_back_top.set(scroll_top > &visibility_height.get());
},
false,
);

let scroll_to_top = StoredValue::new(None::<Callback<()>>);
let scroll_handle = StoredValue::new(None::<EventListenerHandle>);

placeholder_ref.on_load(move |el| {
request_animation_frame(move || {
let scroll_el = get_scroll_parent(&el.into_any())
.unwrap_or_else(|| document().document_element().unwrap().to_leptos_element());

{
let scroll_el = scroll_el.clone();
scroll_to_top.set_value(Some(Callback::new(move |_| {
scroll_el.scroll_to_with_scroll_to_options(
web_sys::ScrollToOptions::new()
.top(0.0)
.behavior(web_sys::ScrollBehavior::Smooth),
);
})));
}

let handle = add_event_listener(scroll_el.clone(), ev::scroll, move |_| {
scroll_top.set(scroll_el.scroll_top());
});
scroll_handle.set_value(Some(handle));
});
});

on_cleanup(move || {
scroll_handle.update_value(|handle| {
if let Some(handle) = handle.take() {
handle.remove();
}
});
});

let on_click = move |_| {
scroll_to_top.with_value(|scroll_to_top| {
if let Some(scroll_to_top) = scroll_to_top {
scroll_to_top.call(());
}
});
};

view! {
<div style="display: none" class="thaw-back-top-placeholder" ref=placeholder_ref>
<Teleport immediate=is_show_back_top>
<CSSTransition
node_ref=back_top_ref
name="fade-in-scale-up-transition"
appear=is_show_back_top.get_untracked()
show=is_show_back_top
let:display
>
<div
class=class_list!["thaw-back-top", class.map(| c | move || c.get())]
ref=back_top_ref
style=move || {
display.get().map(|d| d.to_string()).unwrap_or_else(|| style.get())
}
on:click=on_click
>
<OptionComp value=children let:children>
<Fallback slot>
<Icon icon=icondata_ai::AiVerticalAlignTopOutlined/>
</Fallback>
{children()}
</OptionComp>
</div>
</CSSTransition>
</Teleport>
</div>
}
}
21 changes: 21 additions & 0 deletions thaw/src/back_top/theme.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use crate::theme::ThemeMethod;

#[derive(Clone)]
pub struct BackTopTheme {
pub background_color: String,

}

impl ThemeMethod for BackTopTheme {
fn light() -> Self {
Self {
background_color: "#fff".into(),
}
}

fn dark() -> Self {
Self {
background_color: "#48484e".into(),
}
}
}
2 changes: 2 additions & 0 deletions thaw/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod alert;
mod auto_complete;
mod avatar;
mod back_top;
mod badge;
mod breadcrumb;
mod button;
Expand Down Expand Up @@ -46,6 +47,7 @@ mod upload;
pub use alert::*;
pub use auto_complete::*;
pub use avatar::*;
pub use back_top::*;
pub use badge::*;
pub use breadcrumb::*;
pub use button::*;
Expand Down
12 changes: 8 additions & 4 deletions thaw/src/theme/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ mod common;
use self::common::CommonTheme;
use crate::{
mobile::{NavBarTheme, TabbarTheme},
AlertTheme, AutoCompleteTheme, AvatarTheme, BreadcrumbTheme, ButtonTheme, CalendarTheme,
CollapseTheme, ColorPickerTheme, DatePickerTheme, InputTheme, MenuTheme, MessageTheme,
PopoverTheme, ProgressTheme, ScrollbarTheme, SelectTheme, SkeletionTheme, SliderTheme,
SpinnerTheme, SwitchTheme, TableTheme, TagTheme, TimePickerTheme, TypographyTheme, UploadTheme,
AlertTheme, AutoCompleteTheme, AvatarTheme, BackTopTheme, BreadcrumbTheme, ButtonTheme,
CalendarTheme, CollapseTheme, ColorPickerTheme, DatePickerTheme, InputTheme, MenuTheme,
MessageTheme, PopoverTheme, ProgressTheme, ScrollbarTheme, SelectTheme, SkeletionTheme,
SliderTheme, SpinnerTheme, SwitchTheme, TableTheme, TagTheme, TimePickerTheme, TypographyTheme,
UploadTheme,
};
use leptos::*;

Expand Down Expand Up @@ -46,6 +47,7 @@ pub struct Theme {
pub popover: PopoverTheme,
pub collapse: CollapseTheme,
pub scrollbar: ScrollbarTheme,
pub back_top: BackTopTheme,
}

impl Theme {
Expand Down Expand Up @@ -80,6 +82,7 @@ impl Theme {
popover: PopoverTheme::light(),
collapse: CollapseTheme::light(),
scrollbar: ScrollbarTheme::light(),
back_top: BackTopTheme::light(),
}
}
pub fn dark() -> Self {
Expand Down Expand Up @@ -113,6 +116,7 @@ impl Theme {
popover: PopoverTheme::dark(),
collapse: CollapseTheme::dark(),
scrollbar: ScrollbarTheme::dark(),
back_top: BackTopTheme::dark(),
}
}
}
Expand Down
Loading
Loading