fix: 优化 SelectDropdown 下拉菜单定位逻辑以支持向上展开
This commit is contained in:
1 parent
8d1f3d0703
commit
a3322f9750
1 file changed
+42
-5
@@ -28,6 +28,12 @@
|
|||||||
let triggerElement = $state<HTMLButtonElement>();
|
let triggerElement = $state<HTMLButtonElement>();
|
||||||
let menuElement = $state<HTMLDivElement>();
|
let menuElement = $state<HTMLDivElement>();
|
||||||
|
|
||||||
|
let menuTop = $state(0);
|
||||||
|
let menuBottom = $state(0);
|
||||||
|
let menuLeft = $state(0);
|
||||||
|
let menuWidth = $state(0);
|
||||||
|
let openUpwards = $state(false);
|
||||||
|
|
||||||
let selectedOption = $derived(options.find((option) => String(option.value) === String(value)));
|
let selectedOption = $derived(options.find((option) => String(option.value) === String(value)));
|
||||||
let selectedLabel = $derived(selectedOption?.label ?? emptyLabel);
|
let selectedLabel = $derived(selectedOption?.label ?? emptyLabel);
|
||||||
|
|
||||||
@@ -46,10 +52,37 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openMenu(preferLast = false) {
|
function openMenu(preferLast = false) {
|
||||||
|
if (triggerElement) {
|
||||||
|
const rect = triggerElement.getBoundingClientRect();
|
||||||
|
const spaceBelow = window.innerHeight - rect.bottom;
|
||||||
|
const spaceAbove = rect.top;
|
||||||
|
|
||||||
|
if (spaceBelow < 240 && spaceAbove > spaceBelow) {
|
||||||
|
openUpwards = true;
|
||||||
|
menuBottom = window.innerHeight - rect.top;
|
||||||
|
} else {
|
||||||
|
openUpwards = false;
|
||||||
|
menuTop = rect.bottom;
|
||||||
|
}
|
||||||
|
menuLeft = rect.left;
|
||||||
|
menuWidth = rect.width;
|
||||||
|
}
|
||||||
isOpen = true;
|
isOpen = true;
|
||||||
focusOption(preferLast);
|
focusOption(preferLast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function preventScroll(node: HTMLElement) {
|
||||||
|
const handler = (e: Event) => e.preventDefault();
|
||||||
|
node.addEventListener('wheel', handler, { passive: false });
|
||||||
|
node.addEventListener('touchmove', handler, { passive: false });
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
node.removeEventListener('wheel', handler);
|
||||||
|
node.removeEventListener('touchmove', handler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function closeMenu(restoreFocus = false) {
|
function closeMenu(restoreFocus = false) {
|
||||||
isOpen = false;
|
isOpen = false;
|
||||||
if (restoreFocus) triggerElement?.focus();
|
if (restoreFocus) triggerElement?.focus();
|
||||||
@@ -95,6 +128,8 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:window onresize={() => isOpen && closeMenu()} />
|
||||||
|
|
||||||
<div class="select-dropdown">
|
<div class="select-dropdown">
|
||||||
<input type="hidden" {name} {value} />
|
<input type="hidden" {name} {value} />
|
||||||
<button
|
<button
|
||||||
@@ -120,6 +155,7 @@
|
|||||||
aria-label={`关闭${label}菜单`}
|
aria-label={`关闭${label}菜单`}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
onclick={() => closeMenu()}
|
onclick={() => closeMenu()}
|
||||||
|
use:preventScroll
|
||||||
></button>
|
></button>
|
||||||
<div
|
<div
|
||||||
class="select-dropdown-menu"
|
class="select-dropdown-menu"
|
||||||
@@ -129,6 +165,11 @@
|
|||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
bind:this={menuElement}
|
bind:this={menuElement}
|
||||||
onkeydown={handleMenuKeydown}
|
onkeydown={handleMenuKeydown}
|
||||||
|
style="top: {openUpwards ? 'auto' : `${menuTop}px`}; bottom: {openUpwards
|
||||||
|
? `${menuBottom}px`
|
||||||
|
: 'auto'}; left: {menuLeft}px; width: {menuWidth}px; margin-top: {openUpwards
|
||||||
|
? '0'
|
||||||
|
: '4px'}; margin-bottom: {openUpwards ? '4px' : '0'};"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -219,15 +260,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.select-dropdown-menu {
|
.select-dropdown-menu {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 51;
|
z-index: 51;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-height: 240px;
|
max-height: 240px;
|
||||||
margin-top: 4px;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border: 1px solid var(--text-color);
|
border: 1px solid var(--text-color);
|
||||||
background: var(--bg-color, #ffffff);
|
background: var(--bg-color, #ffffff);
|
||||||
|
|||||||
Reference in new issue
Block a user