mirror of
https://github.com/n8n-io/n8n.git
synced 2025-11-20 17:46:34 +00:00
fix(ai-builder): Fixing scroll issues when workflow builder when NDV is open (#22005)
This commit is contained in:
parent
fe871058e2
commit
4d525e83b1
@ -223,19 +223,6 @@ function scrollToBottom() {
|
||||
scrollAreaRef.value?.scrollToBottom({ smooth: true });
|
||||
}
|
||||
|
||||
function isScrolledToBottom(): boolean {
|
||||
const position = scrollAreaRef.value?.getScrollPosition();
|
||||
if (!position) return false;
|
||||
|
||||
const threshold = 10; // Allow for small rounding errors
|
||||
const isAtBottom =
|
||||
Math.abs(
|
||||
position.height - position.top - (messagesRef.value?.parentElement?.clientHeight || 0),
|
||||
) <= threshold;
|
||||
|
||||
return isAtBottom;
|
||||
}
|
||||
|
||||
function scrollToBottomImmediate() {
|
||||
scrollAreaRef.value?.scrollToBottom({ smooth: false });
|
||||
}
|
||||
@ -257,106 +244,31 @@ watch(
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
// Setup ResizeObserver to maintain scroll position when input height changes
|
||||
let resizeObserver: ResizeObserver | null = null;
|
||||
let scrollLockActive = false;
|
||||
let scrollHandler: (() => void) | null = null;
|
||||
let userIsAtBottom = true;
|
||||
// Setup initial scroll to bottom
|
||||
let isMounted = true;
|
||||
|
||||
function setupInputObservers() {
|
||||
if (!isMounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inputWrapperRef.value || !scrollAreaRef.value || !('ResizeObserver' in window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up any existing observers first
|
||||
cleanupInputObservers();
|
||||
|
||||
// Reset state
|
||||
userIsAtBottom = true;
|
||||
|
||||
// Get the viewport element to attach scroll listener
|
||||
const viewport = messagesRef.value?.parentElement;
|
||||
if (!viewport) return;
|
||||
|
||||
// Create scroll handler function so we can remove it later
|
||||
scrollHandler = () => {
|
||||
if (!scrollLockActive) {
|
||||
userIsAtBottom = isScrolledToBottom();
|
||||
}
|
||||
};
|
||||
|
||||
// Monitor user scrolling
|
||||
viewport.addEventListener('scroll', scrollHandler);
|
||||
|
||||
// Monitor input size changes
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
if (!isMounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only maintain scroll if user was at bottom
|
||||
if (userIsAtBottom) {
|
||||
scrollLockActive = true;
|
||||
// Double RAF for layout stability
|
||||
requestAnimationFrame(() => {
|
||||
if (!isMounted) {
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
if (!isMounted) {
|
||||
return;
|
||||
}
|
||||
scrollToBottomImmediate();
|
||||
// Check if we're still at bottom after auto-scroll
|
||||
userIsAtBottom = isScrolledToBottom();
|
||||
scrollLockActive = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
resizeObserver.observe(inputWrapperRef.value);
|
||||
|
||||
// Start at bottom
|
||||
scrollToBottomImmediate();
|
||||
}
|
||||
|
||||
function cleanupInputObservers() {
|
||||
const viewport = messagesRef.value?.parentElement;
|
||||
if (scrollHandler && viewport) {
|
||||
viewport.removeEventListener('scroll', scrollHandler);
|
||||
scrollHandler = null;
|
||||
}
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect();
|
||||
resizeObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Watch for when the input becomes available and set up observers
|
||||
watch(
|
||||
showBottomInput,
|
||||
async (isShown) => {
|
||||
if (isShown) {
|
||||
// Wait for the input to be mounted in the DOM
|
||||
await nextTick();
|
||||
setupInputObservers();
|
||||
} else {
|
||||
// Clean up when input is hidden
|
||||
cleanupInputObservers();
|
||||
if (!isShown) {
|
||||
return;
|
||||
}
|
||||
// Wait for the input to be mounted in the DOM
|
||||
await nextTick();
|
||||
|
||||
if (!isMounted || !inputWrapperRef.value || !scrollAreaRef.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start at bottom
|
||||
scrollToBottomImmediate();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
isMounted = false;
|
||||
cleanupInputObservers();
|
||||
});
|
||||
|
||||
function getMessageStyles(message: ChatUI.AssistantMessage, messageCount: number) {
|
||||
|
||||
@ -200,12 +200,14 @@ defineExpose({
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.viewport {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
overflow-anchor: none;
|
||||
}
|
||||
|
||||
.scrollbar {
|
||||
|
||||
@ -227,7 +227,11 @@ async function onExecute() {
|
||||
}
|
||||
|
||||
function scrollIntoView() {
|
||||
containerRef.value?.scrollIntoView({ behavior: 'smooth' });
|
||||
containerRef.value?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
inline: 'nearest',
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(scrollIntoView);
|
||||
@ -235,11 +239,6 @@ onMounted(scrollIntoView);
|
||||
onBeforeUnmount(() => {
|
||||
stopExecutionWatcher();
|
||||
});
|
||||
|
||||
watch(workflowIssues, async () => {
|
||||
await nextTick();
|
||||
scrollIntoView();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user