enhance(client): tweak contextmenu position calculation
This commit is contained in:
parent
3c01c0cbf2
commit
e7a1046b87
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue