mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-05 02:41:34 +00:00
fix: add scroll support to Select dropdown for long lists
This commit is contained in:
parent
1a97751e37
commit
3ce299f1c5
2 changed files with 55 additions and 3 deletions
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
let triggerEl;
|
||||
let contentEl;
|
||||
let contentMaxHeight = '';
|
||||
|
||||
/** Svelte action: moves the node to document.body */
|
||||
function portal(node) {
|
||||
|
|
@ -68,6 +69,22 @@
|
|||
contentEl.style.position = 'fixed';
|
||||
contentEl.style.zIndex = '9999';
|
||||
|
||||
// Find the trigger's nearest vertically-scrollable ancestor to use as boundary
|
||||
let minTop = 8;
|
||||
let scrollParent = triggerEl.parentElement;
|
||||
while (scrollParent && scrollParent !== document.body) {
|
||||
const style = getComputedStyle(scrollParent);
|
||||
const oy = style.overflowY;
|
||||
if (
|
||||
(oy === 'auto' || oy === 'scroll') &&
|
||||
scrollParent.scrollHeight > scrollParent.clientHeight
|
||||
) {
|
||||
minTop = scrollParent.getBoundingClientRect().top;
|
||||
break;
|
||||
}
|
||||
scrollParent = scrollParent.parentElement;
|
||||
}
|
||||
|
||||
const contentHeight = contentEl.offsetHeight || 0;
|
||||
const spaceBelow = window.innerHeight - rect.bottom - sideOffset;
|
||||
const spaceAbove = rect.top - sideOffset;
|
||||
|
|
@ -83,9 +100,14 @@
|
|||
if (openAbove) {
|
||||
contentEl.style.bottom = `${window.innerHeight - rect.top + sideOffset}px`;
|
||||
contentEl.style.top = 'auto';
|
||||
const maxAbove = rect.top - minTop - sideOffset;
|
||||
contentMaxHeight = `${maxAbove}px`;
|
||||
} else {
|
||||
contentEl.style.top = `${rect.bottom + sideOffset}px`;
|
||||
const dropdownTop = Math.max(minTop, rect.bottom + sideOffset);
|
||||
contentEl.style.top = `${dropdownTop}px`;
|
||||
contentEl.style.bottom = 'auto';
|
||||
const availableHeight = window.innerHeight - dropdownTop - 8;
|
||||
contentMaxHeight = `${availableHeight}px`;
|
||||
}
|
||||
|
||||
if (align === 'end') {
|
||||
|
|
@ -187,6 +209,8 @@
|
|||
bind:this={contentEl}
|
||||
class={contentClass}
|
||||
role="menu"
|
||||
style:max-height={contentMaxHeight}
|
||||
style:overflow-y="auto"
|
||||
transition:flyAndScale
|
||||
on:click={(e) => e.stopPropagation()}
|
||||
on:pointerdown={(e) => e.stopPropagation()}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
let triggerEl;
|
||||
let contentEl;
|
||||
let contentMaxHeight = '';
|
||||
|
||||
$: selectedLabel = items.find((i) => i.value === value)?.label ?? placeholder;
|
||||
|
||||
|
|
@ -59,9 +60,29 @@
|
|||
|
||||
contentEl.style.position = 'fixed';
|
||||
contentEl.style.zIndex = '9999';
|
||||
contentEl.style.top = `${rect.bottom + 4}px`;
|
||||
contentEl.style.minWidth = `${rect.width}px`;
|
||||
|
||||
// Find the trigger's nearest vertically-scrollable ancestor and use its
|
||||
// top edge as the minimum dropdown position so the dropdown never overlaps
|
||||
// fixed headers above the scroll area (e.g. workspace nav bar)
|
||||
let minTop = 8;
|
||||
let parent = triggerEl.parentElement;
|
||||
while (parent && parent !== document.body) {
|
||||
const style = getComputedStyle(parent);
|
||||
const oy = style.overflowY;
|
||||
if ((oy === 'auto' || oy === 'scroll') && parent.scrollHeight > parent.clientHeight) {
|
||||
minTop = parent.getBoundingClientRect().top;
|
||||
break;
|
||||
}
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
const dropdownTop = Math.max(minTop, rect.bottom + 4);
|
||||
contentEl.style.top = `${dropdownTop}px`;
|
||||
|
||||
// Constrain dropdown height to available viewport space below the clamped top
|
||||
const availableHeight = window.innerHeight - dropdownTop - 8;
|
||||
contentMaxHeight = `${availableHeight}px`;
|
||||
|
||||
if (align === 'end') {
|
||||
contentEl.style.right = `${window.innerWidth - rect.right}px`;
|
||||
contentEl.style.left = 'auto';
|
||||
|
|
@ -123,7 +144,14 @@
|
|||
</button>
|
||||
|
||||
{#if open}
|
||||
<div use:portal bind:this={contentEl} class={contentClass} transition:flyAndScale>
|
||||
<div
|
||||
use:portal
|
||||
bind:this={contentEl}
|
||||
class={contentClass}
|
||||
style:max-height={contentMaxHeight}
|
||||
style:overflow-y="auto"
|
||||
transition:flyAndScale
|
||||
>
|
||||
<slot {open} {selectItem}>
|
||||
{#each items as item}
|
||||
<button class={itemClass} type="button" on:click={() => selectItem(item)}>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue