Fixed dropdown clipping

This commit is contained in:
lucast committed 2026-09-25 19:45:55 +02:00
1 parent d0886cd9a3
commit 4196db1759
1 file changed
+26 -19
+26 -19
View File
@@ -14,6 +14,7 @@ const props = defineProps<DropdownMenuProps>()
const open = ref(false) const open = ref(false)
const placement = ref<"bottom" | "top">("bottom") const placement = ref<"bottom" | "top">("bottom")
const root = useTemplateRef("root") const root = useTemplateRef("root")
const menuPosition = ref({ top: 0, left: 0 })
const toggle = async () => { const toggle = async () => {
open.value = !open.value open.value = !open.value
@@ -37,16 +38,12 @@ const updatePlacement = () => {
// TODO: Calculate height instead // TODO: Calculate height instead
const menuHeight = 220 const menuHeight = 220
console.log( const showAbove = spaceBelow < menuHeight && spaceAbove > spaceBelow
spaceBelow, placement.value = showAbove ? "top" : "bottom"
menuHeight, menuPosition.value = {
spaceAbove, top: showAbove ? rect.top : rect.bottom,
spaceBelow < menuHeight && spaceAbove > spaceBelow, left: rect.right,
spaceBelow < menuHeight && spaceAbove > spaceBelow ? "top" : "bottom", }
)
placement.value =
spaceBelow < menuHeight && spaceAbove > spaceBelow ? "top" : "bottom"
} }
const handleOutsideClick = (e: MouseEvent) => { const handleOutsideClick = (e: MouseEvent) => {
@@ -84,7 +81,16 @@ onBeforeMount(() => {
</div> </div>
<Transition name="dropdown"> <Transition name="dropdown">
<div v-show="open" class="menu" :class="placement"> <Teleport to="body">
<div
v-show="open"
class="dropdown-menu"
:class="placement"
:style="{
top: `${menuPosition.top}px`,
left: `${menuPosition.left}px`,
}"
>
<div <div
v-for="(group, i) in items" v-for="(group, i) in items"
:key="`group-${i}`" :key="`group-${i}`"
@@ -105,7 +111,8 @@ onBeforeMount(() => {
<span>{{ item.label }}</span> <span>{{ item.label }}</span>
</button> </button>
</div> </div>
</div> </div>
</Teleport>
</Transition> </Transition>
</div> </div>
</template> </template>
@@ -116,10 +123,11 @@ onBeforeMount(() => {
.dropdown { .dropdown {
position: relative; position: relative;
display: inline-block; display: inline-block;
}
.menu { .dropdown-menu {
position: absolute; position: fixed;
right: 0; z-index: 1000;
padding: 0.5rem; padding: 0.5rem;
min-width: 180px; min-width: 180px;
@@ -131,18 +139,17 @@ onBeforeMount(() => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
z-index: 1000; transform: translateX(-100%);
&.bottom { &.bottom {
top: calc(100% + 0.5rem); transform: translate(-100%, 0.5rem);
transform-origin: top right; transform-origin: top right;
} }
&.top { &.top {
bottom: calc(100% + 0.5rem); transform: translate(-100%, calc(-100% - 0.5rem));
transform-origin: bottom right; transform-origin: bottom right;
} }
}
.group { .group {
width: 100%; width: 100%;