enhance(client): tweak contextmenu position calculation

This commit is contained in:
syuilo 2023-02-25 13:44:37 +09:00
parent 3c01c0cbf2
commit e7a1046b87
3 changed files with 45 additions and 23 deletions

View file

@ -32,6 +32,8 @@ let rootEl = $shallowRef<HTMLDivElement>();
let zIndex = $ref<number>(os.claimZIndex('high')); let zIndex = $ref<number>(os.claimZIndex('high'));
const SCROLLBAR_THICKNESS = 16;
onMounted(() => { onMounted(() => {
let left = props.ev.pageX + 1; // + 1 let left = props.ev.pageX + 1; // + 1
let top = props.ev.pageY + 1; // + 1 let top = props.ev.pageY + 1; // + 1
@ -39,12 +41,12 @@ onMounted(() => {
const width = rootEl.offsetWidth; const width = rootEl.offsetWidth;
const height = rootEl.offsetHeight; const height = rootEl.offsetHeight;
if (left + width - window.pageXOffset > window.innerWidth) { if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
left = window.innerWidth - width + window.pageXOffset; left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
} }
if (top + height - window.pageYOffset > window.innerHeight) { if (top + height - window.pageYOffset >= (window.innerHeight - SCROLLBAR_THICKNESS)) {
top = window.innerHeight - height + window.pageYOffset; top = (window.innerHeight - SCROLLBAR_THICKNESS) - height + window.pageYOffset;
} }
if (top < 0) { if (top < 0) {

View file

@ -1,11 +1,11 @@
<template> <template>
<div ref="el" class="sfhdhdhr"> <div ref="el" :class="$style.root">
<MkMenu ref="menu" :items="items" :align="align" :width="width" :as-drawer="false" @close="onChildClosed"/> <MkMenu :items="items" :align="align" :width="width" :as-drawer="false" @close="onChildClosed"/>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, shallowRef, watch } from 'vue'; import { nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue';
import MkMenu from './MkMenu.vue'; import MkMenu from './MkMenu.vue';
import { MenuItem } from '@/types/menu'; import { MenuItem } from '@/types/menu';
@ -25,11 +25,21 @@ const emit = defineEmits<{
const el = shallowRef<HTMLElement>(); const el = shallowRef<HTMLElement>();
const align = 'left'; const align = 'left';
const SCROLLBAR_THICKNESS = 16;
function setPosition() { function setPosition() {
const rootRect = props.rootElement.getBoundingClientRect(); const rootRect = props.rootElement.getBoundingClientRect();
const rect = props.targetElement.getBoundingClientRect(); const parentRect = props.targetElement.getBoundingClientRect();
const left = props.targetElement.offsetWidth; const myRect = el.value.getBoundingClientRect();
const top = (rect.top - rootRect.top) - 8;
let left = props.targetElement.offsetWidth;
let top = (parentRect.top - rootRect.top) - 8;
if (rootRect.left + left + myRect.width >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
left = -myRect.width;
}
if (rootRect.top + top + myRect.height >= (window.innerHeight - SCROLLBAR_THICKNESS)) {
top = top - ((rootRect.top + top + myRect.height) - (window.innerHeight - SCROLLBAR_THICKNESS));
}
el.value.style.left = left + 'px'; el.value.style.left = left + 'px';
el.value.style.top = top + 'px'; el.value.style.top = top + 'px';
} }
@ -46,13 +56,22 @@ watch(() => props.targetElement, () => {
setPosition(); setPosition();
}); });
const ro = new ResizeObserver((entries, observer) => {
setPosition();
});
onMounted(() => { onMounted(() => {
ro.observe(el.value);
setPosition(); setPosition();
nextTick(() => { nextTick(() => {
setPosition(); setPosition();
}); });
}); });
onUnmounted(() => {
ro.disconnect();
});
defineExpose({ defineExpose({
checkHit: (ev: MouseEvent) => { checkHit: (ev: MouseEvent) => {
return (ev.target === el.value || el.value.contains(ev.target)); return (ev.target === el.value || el.value.contains(ev.target));
@ -60,8 +79,8 @@ defineExpose({
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.sfhdhdhr { .root {
position: absolute; position: absolute;
} }
</style> </style>

View file

@ -125,7 +125,7 @@ function onBgClick() {
} }
if (type === 'drawer') { if (type === 'drawer') {
maxHeight = window.innerHeight / 1.5; maxHeight = (window.innerHeight - SCROLLBAR_THICKNESS) / 1.5;
} }
const keymap = { const keymap = {
@ -133,6 +133,7 @@ const keymap = {
}; };
const MARGIN = 16; const MARGIN = 16;
const SCROLLBAR_THICKNESS = 16;
const align = () => { const align = () => {
if (props.src == null) return; if (props.src == null) return;
@ -170,15 +171,15 @@ const align = () => {
if (fixed) { if (fixed) {
// //
if (left + width > window.innerWidth) { if (left + width > (window.innerWidth - SCROLLBAR_THICKNESS)) {
left = window.innerWidth - width; left = (window.innerWidth - SCROLLBAR_THICKNESS) - width;
} }
const underSpace = (window.innerHeight - MARGIN) - top; const underSpace = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - top;
const upperSpace = (srcRect.top - MARGIN); const upperSpace = (srcRect.top - MARGIN);
// //
if (top + height > (window.innerHeight - MARGIN)) { if (top + height > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
if (props.noOverlap && props.anchor.x === 'center') { if (props.noOverlap && props.anchor.x === 'center') {
if (underSpace >= (upperSpace / 3)) { if (underSpace >= (upperSpace / 3)) {
maxHeight = underSpace; maxHeight = underSpace;
@ -187,22 +188,22 @@ const align = () => {
top = (upperSpace + MARGIN) - height; top = (upperSpace + MARGIN) - height;
} }
} else { } else {
top = (window.innerHeight - MARGIN) - height; top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height;
} }
} else { } else {
maxHeight = underSpace; maxHeight = underSpace;
} }
} else { } else {
// //
if (left + width - window.pageXOffset > window.innerWidth) { if (left + width - window.pageXOffset > (window.innerWidth - SCROLLBAR_THICKNESS)) {
left = window.innerWidth - width + window.pageXOffset - 1; left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset - 1;
} }
const underSpace = (window.innerHeight - MARGIN) - (top - window.pageYOffset); const underSpace = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - (top - window.pageYOffset);
const upperSpace = (srcRect.top - MARGIN); const upperSpace = (srcRect.top - MARGIN);
// //
if (top + height - window.pageYOffset > (window.innerHeight - MARGIN)) { if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
if (props.noOverlap && props.anchor.x === 'center') { if (props.noOverlap && props.anchor.x === 'center') {
if (underSpace >= (upperSpace / 3)) { if (underSpace >= (upperSpace / 3)) {
maxHeight = underSpace; maxHeight = underSpace;
@ -211,7 +212,7 @@ const align = () => {
top = window.pageYOffset + ((upperSpace + MARGIN) - height); top = window.pageYOffset + ((upperSpace + MARGIN) - height);
} }
} else { } else {
top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1; top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1;
} }
} else { } else {
maxHeight = underSpace; maxHeight = underSpace;