Spaces:
Build error
Build error
Fix scroll to bottom (#78)
Browse files* fix snap scroll to bottom not updating properly to bottom when switching conversation
* scroll to bottom when a user sends a new message ( fixes #75)
* update comment to be detailing the scroll offset
Co-authored-by: Eliott C. <[email protected]>
* use tick() instead of afterUpdate
---------
Co-authored-by: Eliott C. <[email protected]>
src/lib/actions/snapScrollToBottom.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
| 1 |
import { navigating } from "$app/stores";
|
|
|
|
| 2 |
import { get } from "svelte/store";
|
| 3 |
|
|
|
|
|
|
|
| 4 |
/**
|
| 5 |
* @param node element to snap scroll to bottom
|
| 6 |
* @param dependency pass in a dependency to update scroll on changes.
|
|
@@ -15,24 +18,25 @@ export const snapScrollToBottom = (node: HTMLElement, dependency: any) => {
|
|
| 15 |
isDetached = true;
|
| 16 |
}
|
| 17 |
|
| 18 |
-
// if user scrolled back to bottom, we reattach
|
| 19 |
-
if (node.scrollTop
|
| 20 |
isDetached = false;
|
| 21 |
}
|
| 22 |
|
| 23 |
prevScrollValue = node.scrollTop;
|
| 24 |
};
|
| 25 |
|
| 26 |
-
const updateScroll = (_options: { force?: boolean } = {}) => {
|
| 27 |
const defaultOptions = { force: false };
|
| 28 |
const options = { ...defaultOptions, ..._options };
|
| 29 |
const { force } = options;
|
| 30 |
|
| 31 |
if (!force && isDetached && !get(navigating)) return;
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
| 36 |
};
|
| 37 |
|
| 38 |
node.addEventListener("scroll", handleScroll);
|
|
|
|
| 1 |
import { navigating } from "$app/stores";
|
| 2 |
+
import { tick } from "svelte";
|
| 3 |
import { get } from "svelte/store";
|
| 4 |
|
| 5 |
+
const detachedOffset = 10;
|
| 6 |
+
|
| 7 |
/**
|
| 8 |
* @param node element to snap scroll to bottom
|
| 9 |
* @param dependency pass in a dependency to update scroll on changes.
|
|
|
|
| 18 |
isDetached = true;
|
| 19 |
}
|
| 20 |
|
| 21 |
+
// if user scrolled back to within 10px of bottom, we reattach
|
| 22 |
+
if (node.scrollTop - (node.scrollHeight - node.clientHeight) >= -detachedOffset) {
|
| 23 |
isDetached = false;
|
| 24 |
}
|
| 25 |
|
| 26 |
prevScrollValue = node.scrollTop;
|
| 27 |
};
|
| 28 |
|
| 29 |
+
const updateScroll = async (_options: { force?: boolean } = {}) => {
|
| 30 |
const defaultOptions = { force: false };
|
| 31 |
const options = { ...defaultOptions, ..._options };
|
| 32 |
const { force } = options;
|
| 33 |
|
| 34 |
if (!force && isDetached && !get(navigating)) return;
|
| 35 |
|
| 36 |
+
// wait for next tick to ensure that the DOM is updated
|
| 37 |
+
await tick();
|
| 38 |
+
|
| 39 |
+
node.scrollTo({ top: node.scrollHeight });
|
| 40 |
};
|
| 41 |
|
| 42 |
node.addEventListener("scroll", handleScroll);
|
src/lib/components/chat/ChatMessages.svelte
CHANGED
|
@@ -2,6 +2,8 @@
|
|
| 2 |
import type { Message } from "$lib/types/Message";
|
| 3 |
import { snapScrollToBottom } from "$lib/actions/snapScrollToBottom";
|
| 4 |
import ScrollToBottomBtn from "$lib/components/ScrollToBottomBtn.svelte";
|
|
|
|
|
|
|
| 5 |
import ChatIntroduction from "./ChatIntroduction.svelte";
|
| 6 |
import ChatMessage from "./ChatMessage.svelte";
|
| 7 |
|
|
@@ -10,6 +12,16 @@
|
|
| 10 |
export let pending: boolean;
|
| 11 |
|
| 12 |
let chatContainer: HTMLElement;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
</script>
|
| 14 |
|
| 15 |
<div class="overflow-y-auto h-full" use:snapScrollToBottom={messages} bind:this={chatContainer}>
|
|
|
|
| 2 |
import type { Message } from "$lib/types/Message";
|
| 3 |
import { snapScrollToBottom } from "$lib/actions/snapScrollToBottom";
|
| 4 |
import ScrollToBottomBtn from "$lib/components/ScrollToBottomBtn.svelte";
|
| 5 |
+
import { tick } from "svelte";
|
| 6 |
+
|
| 7 |
import ChatIntroduction from "./ChatIntroduction.svelte";
|
| 8 |
import ChatMessage from "./ChatMessage.svelte";
|
| 9 |
|
|
|
|
| 12 |
export let pending: boolean;
|
| 13 |
|
| 14 |
let chatContainer: HTMLElement;
|
| 15 |
+
|
| 16 |
+
async function scrollToBottom() {
|
| 17 |
+
await tick();
|
| 18 |
+
chatContainer.scrollTop = chatContainer.scrollHeight;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
// If last message is from user, scroll to bottom
|
| 22 |
+
$: if (messages.at(-1)?.from === "user") {
|
| 23 |
+
scrollToBottom();
|
| 24 |
+
}
|
| 25 |
</script>
|
| 26 |
|
| 27 |
<div class="overflow-y-auto h-full" use:snapScrollToBottom={messages} bind:this={chatContainer}>
|