mirror of
https://github.com/OpenBMB/ChatDev.git
synced 2026-05-23 17:04:03 +00:00
Merge pull request #618 from zxrys/fix/ui-scrolling-issue-fix
Fix nested UI scroll behavior
This commit is contained in:
commit
b23950d035
@ -7,10 +7,11 @@ const route = useRoute()
|
|||||||
|
|
||||||
// Hide the sidebar on LaunchView, BatchRunView and WorkflowWorkbench
|
// Hide the sidebar on LaunchView, BatchRunView and WorkflowWorkbench
|
||||||
const showSidebar = computed(() => route.path !== '/launch' && route.path !== '/batch-run')
|
const showSidebar = computed(() => route.path !== '/launch' && route.path !== '/batch-run')
|
||||||
|
const isHomeRoute = computed(() => route.path === '/')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container" :class="{ 'home-route': isHomeRoute }">
|
||||||
<Sidebar v-if="showSidebar" />
|
<Sidebar v-if="showSidebar" />
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
<router-view />
|
<router-view />
|
||||||
@ -25,11 +26,23 @@ const showSidebar = computed(() => route.path !== '/launch' && route.path !== '/
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-container.home-route {
|
||||||
|
height: 100dvh;
|
||||||
|
min-height: 100dvh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home-route .main-content {
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: system-ui, sans-serif;
|
font-family: system-ui, sans-serif;
|
||||||
|
|||||||
@ -251,7 +251,16 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
/>
|
/>
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<div v-if="showDropdown && !isReadOnly" class="custom-select-dropdown" :style="dropdownStyle">
|
<div
|
||||||
|
v-if="showDropdown && !isReadOnly"
|
||||||
|
class="custom-select-dropdown"
|
||||||
|
data-local-scroll
|
||||||
|
:style="dropdownStyle"
|
||||||
|
@mousedown.prevent
|
||||||
|
@wheel="handleLocalScrollWheel"
|
||||||
|
@scroll.stop
|
||||||
|
@touchmove.stop
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-if="!field.required"
|
v-if="!field.required"
|
||||||
class="custom-select-option"
|
class="custom-select-option"
|
||||||
@ -389,7 +398,10 @@
|
|||||||
:id="`${modalId}-${field.name}`"
|
:id="`${modalId}-${field.name}`"
|
||||||
:value="formData[field.name]"
|
:value="formData[field.name]"
|
||||||
@input="onInput($event.target.value)"
|
@input="onInput($event.target.value)"
|
||||||
|
@wheel="handleLocalScrollWheel"
|
||||||
|
@scroll.stop
|
||||||
class="form-textarea"
|
class="form-textarea"
|
||||||
|
data-local-scroll
|
||||||
rows="4"
|
rows="4"
|
||||||
:readonly="isReadOnly"
|
:readonly="isReadOnly"
|
||||||
:class="{'input-readonly': isReadOnly}"
|
:class="{'input-readonly': isReadOnly}"
|
||||||
@ -771,6 +783,34 @@ const onFilterInput = (event) => {
|
|||||||
showDropdown.value = true
|
showDropdown.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleLocalScrollWheel = (event) => {
|
||||||
|
const target = event.currentTarget
|
||||||
|
if (!target) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDropdown = target.classList.contains('custom-select-dropdown')
|
||||||
|
|
||||||
|
if (target.scrollHeight <= target.clientHeight) {
|
||||||
|
if (isDropdown) {
|
||||||
|
event.stopPropagation()
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isScrollingUp = event.deltaY < 0
|
||||||
|
const isScrollingDown = event.deltaY > 0
|
||||||
|
const atTop = target.scrollTop <= 0
|
||||||
|
const atBottom = Math.ceil(target.scrollTop + target.clientHeight) >= target.scrollHeight
|
||||||
|
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
if ((isScrollingUp && atTop) || (isScrollingDown && atBottom)) {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleInputBlur = () => {
|
const handleInputBlur = () => {
|
||||||
// Delay hiding dropdown to allow option selection
|
// Delay hiding dropdown to allow option selection
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -897,6 +937,7 @@ const getSelectedLabel = () => {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
|
overscroll-behavior: contain;
|
||||||
transition: border-color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;
|
transition: border-color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1565,6 +1606,7 @@ input:checked + .switch-slider:before {
|
|||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
background-color: #1e1e1e;
|
background-color: #1e1e1e;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button class="close-button" @click="closeModal(modal.id)">×</button>
|
<button class="close-button" @click="closeModal(modal.id)">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body" data-local-scroll @wheel="handleLocalScrollWheel">
|
||||||
<div
|
<div
|
||||||
v-for="field in getMainDisplayFields(modal)"
|
v-for="field in getMainDisplayFields(modal)"
|
||||||
:key="field.name + '-' + (modal.formData[field.name]?.type || '')"
|
:key="field.name + '-' + (modal.formData[field.name]?.type || '')"
|
||||||
@ -128,7 +128,7 @@
|
|||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button class="close-button" @click="closeVarModal">×</button>
|
<button class="close-button" @click="closeVarModal">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body" data-local-scroll @wheel="handleLocalScrollWheel">
|
||||||
<div v-if="varFormError" class="submit-error">
|
<div v-if="varFormError" class="submit-error">
|
||||||
{{ varFormError }}
|
{{ varFormError }}
|
||||||
</div>
|
</div>
|
||||||
@ -169,7 +169,7 @@
|
|||||||
<h3 class="modal-title">{{ editingListItemIndex !== null ? t('form_generator.edit_entry') : t('form_generator.add_entry') }}</h3>
|
<h3 class="modal-title">{{ editingListItemIndex !== null ? t('form_generator.edit_entry') : t('form_generator.add_entry') }}</h3>
|
||||||
<button class="close-button" @click="closeListItemModal">×</button>
|
<button class="close-button" @click="closeListItemModal">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body" data-local-scroll @wheel="handleLocalScrollWheel">
|
||||||
<div v-if="listItemFormError" class="submit-error">
|
<div v-if="listItemFormError" class="submit-error">
|
||||||
{{ listItemFormError }}
|
{{ listItemFormError }}
|
||||||
</div>
|
</div>
|
||||||
@ -1672,6 +1672,29 @@ const submitForm = async (modalId) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Press Enter to submit topmost modal, Esc to close
|
// Press Enter to submit topmost modal, Esc to close
|
||||||
|
const handleLocalScrollWheel = (event) => {
|
||||||
|
const target = event.currentTarget
|
||||||
|
if (!target) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
if (target.scrollHeight <= target.clientHeight) {
|
||||||
|
event.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isScrollingUp = event.deltaY < 0
|
||||||
|
const isScrollingDown = event.deltaY > 0
|
||||||
|
const atTop = target.scrollTop <= 0
|
||||||
|
const atBottom = Math.ceil(target.scrollTop + target.clientHeight) >= target.scrollHeight
|
||||||
|
|
||||||
|
if ((isScrollingUp && atTop) || (isScrollingDown && atBottom)) {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleKeystrokes = (event) => {
|
const handleKeystrokes = (event) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
// Do not intercept Enter inside textarea to allow newlines
|
// Do not intercept Enter inside textarea to allow newlines
|
||||||
@ -2140,6 +2163,7 @@ defineExpose({
|
|||||||
padding: 15px 20px 30px 15px;
|
padding: 15px 20px 30px 15px;
|
||||||
max-height: none;
|
max-height: none;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.04);
|
border-top: 1px solid rgba(255, 255, 255, 0.04);
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
|
|||||||
@ -213,7 +213,11 @@ const calculatePosition = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Listen to scroll and zoom events to dismiss tooltip
|
// Listen to scroll and zoom events to dismiss tooltip
|
||||||
const handleScroll = () => {
|
const handleScroll = (event) => {
|
||||||
|
if (event.target instanceof Element && event.target.closest('[data-local-scroll]')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (isVisible.value && !keyboardActive.value) {
|
if (isVisible.value && !keyboardActive.value) {
|
||||||
hideTooltip()
|
hideTooltip()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,10 @@ const isWorkflowsActive = computed(() => route.path.startsWith('/workflows'))
|
|||||||
|
|
||||||
let lastScrollY = 0
|
let lastScrollY = 0
|
||||||
const handleScroll = (e) => {
|
const handleScroll = (e) => {
|
||||||
|
if (e.target instanceof Element && e.target.closest('[data-local-scroll]')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const currentScrollY = e.target.scrollTop || window.scrollY || 0;
|
const currentScrollY = e.target.scrollTop || window.scrollY || 0;
|
||||||
// Minimize small scroll jitters
|
// Minimize small scroll jitters
|
||||||
if (Math.abs(currentScrollY - lastScrollY) < 5) return;
|
if (Math.abs(currentScrollY - lastScrollY) < 5) return;
|
||||||
|
|||||||
@ -68,7 +68,7 @@ const cubes = Array.from({ length: 80 }, (_, i) => ({
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.home-view {
|
.home-view {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: calc(100vh - 55px); /* Match sidebar height to avoid bottom gap */
|
height: 100%;
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user