feat: add smooth scroll
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Kasper Juul Hermansen 2025-01-12 16:15:13 +01:00
parent 13e6fa474d
commit 955946d623
Signed by: kjuulh
SSH Key Fingerprint: SHA256:RjXh0p7U6opxnfd3ga/Y9TCo18FYlHFdSpRIV72S/QM
3 changed files with 62 additions and 10 deletions

1
Cargo.lock generated
View File

@ -75,6 +75,7 @@ dependencies = [
"serde_json",
"server_fn",
"thiserror 2.0.11",
"web-sys",
]
[[package]]

View File

@ -20,6 +20,10 @@ cfg-if.workspace = true
thiserror.workspace = true
reqwest = { version = "0.12.11", optional = true, features = ["json"] }
serde_json = { version = "1.0.134", optional = true }
web-sys = { version = "0.3.76", features = [
"ScrollBehavior",
"ScrollToOptions",
] }
[features]
default = []

View File

@ -1,8 +1,9 @@
use crate::error_template::{AppError, ErrorTemplate};
use leptos::prelude::*;
use leptos::{ev::SubmitEvent, html, prelude::*};
use leptos_meta::*;
use leptos_router::{components::*, StaticSegment};
use message::Message;
pub mod error_template;
@ -55,9 +56,49 @@ pub fn App() -> impl IntoView {
}
}
fn smooth_scroll_to_bottom() {
if let Some(window) = web_sys::window() {
if let Some(document) = window.document() {
if let Some(body) = document.get_element_by_id("messages") {
body.set_scroll_top(body.scroll_height());
leptos::logging::log!("moving to top");
}
}
}
}
#[component]
pub fn HomePage() -> impl IntoView {
let messages = message::get_messages();
let (messages, set_messages) = signal(
message::get_messages()
.into_iter()
.enumerate()
.map(|(index, value)| (index, ArcRwSignal::new(value)))
.collect::<Vec<_>>(),
);
let (input, set_input) = signal("".to_string());
let on_submit = move |ev: SubmitEvent| {
// stop the page from reloading!
ev.prevent_default();
let messages_len = messages.get().len();
let mut messages = set_messages.write();
messages.push((
messages_len,
ArcRwSignal::new(Message {
role: "user".into(),
content: input.get().into(),
}),
));
set_input.set("".into());
request_animation_frame(move || {
smooth_scroll_to_bottom();
});
};
view! {
<div class="flex flex-col h-screen bg-gray-50">
@ -70,11 +111,14 @@ pub fn HomePage() -> impl IntoView {
</div>
</header>
<div class="overflow-y-auto flex-1 px-4">
<div class="overflow-y-auto flex-1 px-4" id="messages">
<div class="py-6 mx-auto space-y-6 max-w-5xl">
{messages
.iter()
.map(|message| {
<For
each=move || messages.get()
key=|message| message.0
children=move |(_id, message)| {
let message = message.read();
view! {
<div class=format!(
"flex {}",
@ -94,24 +138,27 @@ pub fn HomePage() -> impl IntoView {
)>{message.content.clone()}</div>
</div>
}
})
.collect_view()}
}
/>
<div class="flex justify-start">
<div class="py-3 px-4 bg-white rounded-sm border border-gray-200 max-w-[80%]">
"Loading..."
</div>
</div> <div />
</div>
<div />
</div>
</div>
<div class="py-4 px-4 bg-white border-t border-gray-200">
<form class="mx-auto max-w-5xl">
<form class="mx-auto max-w-5xl" on:submit=on_submit>
<div class="flex gap-4">
<input
type="text"
placeholder="Type your medical question here..."
class="flex-1 py-2 px-4 rounded-sm border border-gray-200 focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none"
bind:value=(input, set_input)
/>
<button
type="submit"
class="flex gap-2 items-center py-2 px-4 text-white bg-blue-500 rounded-sm hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed"