2023-07-27 08:31:52 +03:00
|
|
|
<!--
|
2024-02-12 04:37:45 +02:00
|
|
|
SPDX-FileCopyrightText: syuilo and misskey-project
|
2023-07-27 08:31:52 +03:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
-->
|
|
|
|
|
2020-10-17 14:12:00 +03:00
|
|
|
<template>
|
2023-01-14 04:39:35 +02:00
|
|
|
<Transition
|
2023-05-19 07:58:09 +03:00
|
|
|
:enterActiveClass="defaultStore.state.animation ? $style.transition_window_enterActive : ''"
|
|
|
|
:leaveActiveClass="defaultStore.state.animation ? $style.transition_window_leaveActive : ''"
|
|
|
|
:enterFromClass="defaultStore.state.animation ? $style.transition_window_enterFrom : ''"
|
|
|
|
:leaveToClass="defaultStore.state.animation ? $style.transition_window_leaveTo : ''"
|
2023-01-14 04:39:35 +02:00
|
|
|
appear
|
2023-05-19 07:58:09 +03:00
|
|
|
@afterLeave="$emit('closed')"
|
2023-01-14 04:39:35 +02:00
|
|
|
>
|
|
|
|
<div v-if="showing" ref="rootEl" :class="[$style.root, { [$style.maximized]: maximized }]">
|
|
|
|
<div :class="$style.body" class="_shadow" @mousedown="onBodyMousedown" @keydown="onKeydown">
|
|
|
|
<div :class="[$style.header, { [$style.mini]: mini }]" @contextmenu.prevent.stop="onContextmenu">
|
2023-04-08 07:01:28 +03:00
|
|
|
<span :class="$style.headerLeft">
|
|
|
|
<template v-if="!minimized">
|
|
|
|
<button v-for="button in buttonsLeft" v-tooltip="button.title" class="_button" :class="[$style.headerButton, { [$style.highlighted]: button.highlighted }]" @click="button.onClick"><i :class="button.icon"></i></button>
|
|
|
|
</template>
|
2021-10-08 16:03:06 +03:00
|
|
|
</span>
|
2023-01-14 04:39:35 +02:00
|
|
|
<span :class="$style.headerTitle" @mousedown.prevent="onHeaderMousedown" @touchstart.prevent="onHeaderMousedown">
|
2020-10-17 14:12:00 +03:00
|
|
|
<slot name="header"></slot>
|
|
|
|
</span>
|
2023-01-14 04:39:35 +02:00
|
|
|
<span :class="$style.headerRight">
|
2023-04-08 06:57:05 +03:00
|
|
|
<template v-if="!minimized">
|
|
|
|
<button v-for="button in buttonsRight" v-tooltip="button.title" class="_button" :class="[$style.headerButton, { [$style.highlighted]: button.highlighted }]" @click="button.onClick"><i :class="button.icon"></i></button>
|
|
|
|
</template>
|
2023-04-08 06:55:05 +03:00
|
|
|
<button v-if="canResize && minimized" v-tooltip="i18n.ts.windowRestore" class="_button" :class="$style.headerButton" @click="unMinimize()"><i class="ti ti-maximize"></i></button>
|
|
|
|
<button v-else-if="canResize && !maximized" v-tooltip="i18n.ts.windowMinimize" class="_button" :class="$style.headerButton" @click="minimize()"><i class="ti ti-minimize"></i></button>
|
2023-01-14 04:39:35 +02:00
|
|
|
<button v-if="canResize && maximized" v-tooltip="i18n.ts.windowRestore" class="_button" :class="$style.headerButton" @click="unMaximize()"><i class="ti ti-picture-in-picture"></i></button>
|
2023-04-08 06:55:05 +03:00
|
|
|
<button v-else-if="canResize && !maximized && !minimized" v-tooltip="i18n.ts.windowMaximize" class="_button" :class="$style.headerButton" @click="maximize()"><i class="ti ti-rectangle"></i></button>
|
2023-01-14 04:39:35 +02:00
|
|
|
<button v-if="closeButton" v-tooltip="i18n.ts.close" class="_button" :class="$style.headerButton" @click="close()"><i class="ti ti-x"></i></button>
|
2021-10-08 16:03:06 +03:00
|
|
|
</span>
|
2020-10-17 14:12:00 +03:00
|
|
|
</div>
|
2023-05-08 12:30:40 +03:00
|
|
|
<div :class="$style.content">
|
2020-10-17 14:12:00 +03:00
|
|
|
<slot></slot>
|
|
|
|
</div>
|
|
|
|
</div>
|
2023-04-08 06:55:05 +03:00
|
|
|
<template v-if="canResize && !minimized">
|
2023-01-14 04:39:35 +02:00
|
|
|
<div :class="$style.handleTop" @mousedown.prevent="onTopHandleMousedown"></div>
|
|
|
|
<div :class="$style.handleRight" @mousedown.prevent="onRightHandleMousedown"></div>
|
|
|
|
<div :class="$style.handleBottom" @mousedown.prevent="onBottomHandleMousedown"></div>
|
|
|
|
<div :class="$style.handleLeft" @mousedown.prevent="onLeftHandleMousedown"></div>
|
|
|
|
<div :class="$style.handleTopLeft" @mousedown.prevent="onTopLeftHandleMousedown"></div>
|
|
|
|
<div :class="$style.handleTopRight" @mousedown.prevent="onTopRightHandleMousedown"></div>
|
|
|
|
<div :class="$style.handleBottomRight" @mousedown.prevent="onBottomRightHandleMousedown"></div>
|
|
|
|
<div :class="$style.handleBottomLeft" @mousedown.prevent="onBottomLeftHandleMousedown"></div>
|
2020-10-17 14:12:00 +03:00
|
|
|
</template>
|
|
|
|
</div>
|
2022-12-30 06:37:14 +02:00
|
|
|
</Transition>
|
2020-10-17 14:12:00 +03:00
|
|
|
</template>
|
|
|
|
|
2022-07-17 18:31:55 +03:00
|
|
|
<script lang="ts" setup>
|
2023-12-07 07:42:09 +02:00
|
|
|
import { onBeforeUnmount, onMounted, provide, shallowRef, ref } from 'vue';
|
2023-09-19 10:37:43 +03:00
|
|
|
import contains from '@/scripts/contains.js';
|
|
|
|
import * as os from '@/os.js';
|
2023-12-13 09:56:19 +02:00
|
|
|
import { MenuItem } from '@/types/menu.js';
|
2023-09-19 10:37:43 +03:00
|
|
|
import { i18n } from '@/i18n.js';
|
|
|
|
import { defaultStore } from '@/store.js';
|
2020-10-17 14:12:00 +03:00
|
|
|
|
|
|
|
const minHeight = 50;
|
|
|
|
const minWidth = 250;
|
|
|
|
|
2024-01-30 12:53:53 +02:00
|
|
|
function dragListen(fn: (ev: MouseEvent | TouchEvent) => void) {
|
2022-06-20 11:38:49 +03:00
|
|
|
window.addEventListener('mousemove', fn);
|
|
|
|
window.addEventListener('touchmove', fn);
|
2020-10-17 14:12:00 +03:00
|
|
|
window.addEventListener('mouseleave', dragClear.bind(null, fn));
|
2022-06-20 11:38:49 +03:00
|
|
|
window.addEventListener('mouseup', dragClear.bind(null, fn));
|
|
|
|
window.addEventListener('touchend', dragClear.bind(null, fn));
|
2020-10-17 14:12:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function dragClear(fn) {
|
2022-06-20 11:38:49 +03:00
|
|
|
window.removeEventListener('mousemove', fn);
|
|
|
|
window.removeEventListener('touchmove', fn);
|
2020-10-17 14:12:00 +03:00
|
|
|
window.removeEventListener('mouseleave', dragClear);
|
2022-06-20 11:38:49 +03:00
|
|
|
window.removeEventListener('mouseup', dragClear);
|
|
|
|
window.removeEventListener('touchend', dragClear);
|
2020-10-17 14:12:00 +03:00
|
|
|
}
|
|
|
|
|
2022-07-17 18:31:55 +03:00
|
|
|
const props = withDefaults(defineProps<{
|
2023-01-07 07:44:50 +02:00
|
|
|
initialWidth: number;
|
|
|
|
initialHeight: number | null;
|
2022-07-17 18:31:55 +03:00
|
|
|
canResize?: boolean;
|
|
|
|
closeButton?: boolean;
|
|
|
|
mini?: boolean;
|
|
|
|
front?: boolean;
|
|
|
|
contextmenu?: MenuItem[] | null;
|
|
|
|
buttonsLeft?: any[];
|
|
|
|
buttonsRight?: any[];
|
|
|
|
}>(), {
|
|
|
|
initialWidth: 400,
|
|
|
|
initialHeight: null,
|
|
|
|
canResize: false,
|
|
|
|
closeButton: true,
|
|
|
|
mini: false,
|
2022-07-18 19:20:36 +03:00
|
|
|
front: false,
|
2022-07-17 18:31:55 +03:00
|
|
|
contextmenu: null,
|
|
|
|
buttonsLeft: () => [],
|
|
|
|
buttonsRight: () => [],
|
|
|
|
});
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
(ev: 'closed'): void;
|
|
|
|
}>();
|
|
|
|
|
|
|
|
provide('inWindow', true);
|
|
|
|
|
2023-12-07 07:42:09 +02:00
|
|
|
const rootEl = shallowRef<HTMLElement | null>();
|
|
|
|
const showing = ref(true);
|
2022-07-17 18:31:55 +03:00
|
|
|
let beforeClickedAt = 0;
|
2023-12-07 07:42:09 +02:00
|
|
|
const maximized = ref(false);
|
|
|
|
const minimized = ref(false);
|
2023-04-08 06:55:05 +03:00
|
|
|
let unResizedTop = '';
|
|
|
|
let unResizedLeft = '';
|
|
|
|
let unResizedWidth = '';
|
|
|
|
let unResizedHeight = '';
|
2022-07-17 18:31:55 +03:00
|
|
|
|
|
|
|
function close() {
|
2023-12-07 07:42:09 +02:00
|
|
|
showing.value = false;
|
2022-07-17 18:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function onKeydown(evt) {
|
|
|
|
if (evt.which === 27) { // Esc
|
|
|
|
evt.preventDefault();
|
|
|
|
evt.stopPropagation();
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function onContextmenu(ev: MouseEvent) {
|
|
|
|
if (props.contextmenu) {
|
|
|
|
os.contextMenu(props.contextmenu, ev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 最前面へ移動
|
|
|
|
function top() {
|
2023-12-07 07:42:09 +02:00
|
|
|
if (rootEl.value) {
|
2024-01-30 12:53:53 +02:00
|
|
|
rootEl.value.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low').toString();
|
2022-07-17 23:03:39 +03:00
|
|
|
}
|
2022-07-17 18:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function maximize() {
|
2024-01-05 05:38:06 +02:00
|
|
|
if (rootEl.value == null) return;
|
2023-12-07 07:42:09 +02:00
|
|
|
maximized.value = true;
|
|
|
|
unResizedTop = rootEl.value.style.top;
|
|
|
|
unResizedLeft = rootEl.value.style.left;
|
|
|
|
unResizedWidth = rootEl.value.style.width;
|
|
|
|
unResizedHeight = rootEl.value.style.height;
|
|
|
|
rootEl.value.style.top = '0';
|
|
|
|
rootEl.value.style.left = '0';
|
|
|
|
rootEl.value.style.width = '100%';
|
|
|
|
rootEl.value.style.height = '100%';
|
2022-07-17 18:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function unMaximize() {
|
2024-01-05 05:38:06 +02:00
|
|
|
if (rootEl.value == null) return;
|
2023-12-07 07:42:09 +02:00
|
|
|
maximized.value = false;
|
|
|
|
rootEl.value.style.top = unResizedTop;
|
|
|
|
rootEl.value.style.left = unResizedLeft;
|
|
|
|
rootEl.value.style.width = unResizedWidth;
|
|
|
|
rootEl.value.style.height = unResizedHeight;
|
2023-04-08 06:55:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function minimize() {
|
2024-01-05 05:38:06 +02:00
|
|
|
if (rootEl.value == null) return;
|
2023-12-07 07:42:09 +02:00
|
|
|
minimized.value = true;
|
|
|
|
unResizedWidth = rootEl.value.style.width;
|
|
|
|
unResizedHeight = rootEl.value.style.height;
|
|
|
|
rootEl.value.style.width = minWidth + 'px';
|
|
|
|
rootEl.value.style.height = props.mini ? '32px' : '39px';
|
2023-04-08 06:55:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function unMinimize() {
|
2024-01-05 05:38:06 +02:00
|
|
|
if (rootEl.value == null) return;
|
2023-12-07 07:42:09 +02:00
|
|
|
const main = rootEl.value;
|
2023-04-08 06:55:05 +03:00
|
|
|
|
2023-12-07 07:42:09 +02:00
|
|
|
minimized.value = false;
|
|
|
|
rootEl.value.style.width = unResizedWidth;
|
|
|
|
rootEl.value.style.height = unResizedHeight;
|
2023-04-08 06:55:05 +03:00
|
|
|
const browserWidth = window.innerWidth;
|
|
|
|
const browserHeight = window.innerHeight;
|
|
|
|
const windowWidth = main.offsetWidth;
|
|
|
|
const windowHeight = main.offsetHeight;
|
|
|
|
|
|
|
|
const position = main.getBoundingClientRect();
|
|
|
|
if (position.top + windowHeight > browserHeight) main.style.top = browserHeight - windowHeight + 'px';
|
|
|
|
if (position.left + windowWidth > browserWidth) main.style.left = browserWidth - windowWidth + 'px';
|
2022-07-17 18:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function onBodyMousedown() {
|
|
|
|
top();
|
|
|
|
}
|
|
|
|
|
|
|
|
function onDblClick() {
|
2023-12-07 07:42:09 +02:00
|
|
|
if (minimized.value) {
|
2023-04-08 06:56:21 +03:00
|
|
|
unMinimize();
|
|
|
|
} else {
|
|
|
|
maximize();
|
|
|
|
}
|
2022-07-17 18:31:55 +03:00
|
|
|
}
|
|
|
|
|
2024-01-30 12:53:53 +02:00
|
|
|
function getPositionX(event: MouseEvent | TouchEvent) {
|
|
|
|
return 'touches' in event && event.touches.length > 0 ? event.touches[0].clientX : 'clientX' in event ? event.clientX : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPositionY(event: MouseEvent | TouchEvent) {
|
|
|
|
return 'touches' in event && event.touches.length > 0 ? event.touches[0].clientY : 'clientY' in event ? event.clientY : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function onHeaderMousedown(evt: MouseEvent | TouchEvent) {
|
2022-07-17 18:31:55 +03:00
|
|
|
// 右クリックはコンテキストメニューを開こうとした可能性が高いため無視
|
2024-01-30 12:53:53 +02:00
|
|
|
if ('button' in evt && evt.button === 2) return;
|
2022-07-17 18:31:55 +03:00
|
|
|
|
|
|
|
let beforeMaximized = false;
|
|
|
|
|
2023-12-07 07:42:09 +02:00
|
|
|
if (maximized.value) {
|
2022-07-17 18:31:55 +03:00
|
|
|
beforeMaximized = true;
|
|
|
|
unMaximize();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ダブルクリック判定
|
|
|
|
if (Date.now() - beforeClickedAt < 300) {
|
|
|
|
beforeClickedAt = Date.now();
|
|
|
|
onDblClick();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
beforeClickedAt = Date.now();
|
|
|
|
|
2023-12-07 07:42:09 +02:00
|
|
|
const main = rootEl.value;
|
2022-07-20 18:32:41 +03:00
|
|
|
if (main == null) return;
|
2022-07-17 18:31:55 +03:00
|
|
|
|
|
|
|
if (!contains(main, document.activeElement)) main.focus();
|
|
|
|
|
|
|
|
const position = main.getBoundingClientRect();
|
|
|
|
|
2024-01-30 12:53:53 +02:00
|
|
|
const clickX = getPositionX(evt);
|
|
|
|
const clickY = getPositionY(evt);
|
2023-04-08 06:55:05 +03:00
|
|
|
const moveBaseX = beforeMaximized ? parseInt(unResizedWidth, 10) / 2 : clickX - position.left; // TODO: parseIntやめる
|
2022-07-17 18:31:55 +03:00
|
|
|
const moveBaseY = beforeMaximized ? 20 : clickY - position.top;
|
|
|
|
const browserWidth = window.innerWidth;
|
|
|
|
const browserHeight = window.innerHeight;
|
|
|
|
const windowWidth = main.offsetWidth;
|
|
|
|
const windowHeight = main.offsetHeight;
|
|
|
|
|
|
|
|
function move(x: number, y: number) {
|
|
|
|
let moveLeft = x - moveBaseX;
|
|
|
|
let moveTop = y - moveBaseY;
|
|
|
|
|
|
|
|
// 下はみ出し
|
|
|
|
if (moveTop + windowHeight > browserHeight) moveTop = browserHeight - windowHeight;
|
|
|
|
|
|
|
|
// 左はみ出し
|
|
|
|
if (moveLeft < 0) moveLeft = 0;
|
|
|
|
|
|
|
|
// 上はみ出し
|
|
|
|
if (moveTop < 0) moveTop = 0;
|
|
|
|
|
|
|
|
// 右はみ出し
|
|
|
|
if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth;
|
|
|
|
|
2024-01-30 12:53:53 +02:00
|
|
|
if (rootEl.value) {
|
|
|
|
rootEl.value.style.left = moveLeft + 'px';
|
|
|
|
rootEl.value.style.top = moveTop + 'px';
|
|
|
|
}
|
2022-07-17 18:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (beforeMaximized) {
|
|
|
|
move(clickX, clickY);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 動かした時
|
|
|
|
dragListen(me => {
|
2024-01-30 12:53:53 +02:00
|
|
|
const x = getPositionX(me);
|
|
|
|
const y = getPositionY(me);
|
2022-07-17 18:31:55 +03:00
|
|
|
|
|
|
|
move(x, y);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// 上ハンドル掴み時
|
2024-01-30 12:53:53 +02:00
|
|
|
function onTopHandleMousedown(evt: MouseEvent | TouchEvent) {
|
2023-12-07 07:42:09 +02:00
|
|
|
const main = rootEl.value;
|
2022-12-18 12:52:50 +02:00
|
|
|
// どういうわけかnullになることがある
|
|
|
|
if (main == null) return;
|
2022-07-17 18:31:55 +03:00
|
|
|
|
2024-01-30 12:53:53 +02:00
|
|
|
const base = getPositionY(evt);
|
2022-07-17 18:31:55 +03:00
|
|
|
const height = parseInt(getComputedStyle(main, '').height, 10);
|
|
|
|
const top = parseInt(getComputedStyle(main, '').top, 10);
|
|
|
|
|
|
|
|
// 動かした時
|
|
|
|
dragListen(me => {
|
2024-01-30 12:53:53 +02:00
|
|
|
const move = getPositionY(me) - base;
|
2022-07-17 18:31:55 +03:00
|
|
|
if (top + move > 0) {
|
|
|
|
if (height + -move > minHeight) {
|
|
|
|
applyTransformHeight(height + -move);
|
|
|
|
applyTransformTop(top + move);
|
|
|
|
} else { // 最小の高さより小さくなろうとした時
|
|
|
|
applyTransformHeight(minHeight);
|
|
|
|
applyTransformTop(top + (height - minHeight));
|
|
|
|
}
|
|
|
|
} else { // 上のはみ出し時
|
|
|
|
applyTransformHeight(top + height);
|
|
|
|
applyTransformTop(0);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// 右ハンドル掴み時
|
2024-01-30 12:53:53 +02:00
|
|
|
function onRightHandleMousedown(evt: MouseEvent | TouchEvent) {
|
2023-12-07 07:42:09 +02:00
|
|
|
const main = rootEl.value;
|
2022-12-18 12:52:50 +02:00
|
|
|
if (main == null) return;
|
2022-07-17 18:31:55 +03:00
|
|
|
|
2024-01-30 12:53:53 +02:00
|
|
|
const base = getPositionX(evt);
|
2022-07-17 18:31:55 +03:00
|
|
|
const width = parseInt(getComputedStyle(main, '').width, 10);
|
|
|
|
const left = parseInt(getComputedStyle(main, '').left, 10);
|
|
|
|
const browserWidth = window.innerWidth;
|
|
|
|
|
|
|
|
// 動かした時
|
|
|
|
dragListen(me => {
|
2024-01-30 12:53:53 +02:00
|
|
|
const move = getPositionX(me) - base;
|
2022-07-17 18:31:55 +03:00
|
|
|
if (left + width + move < browserWidth) {
|
|
|
|
if (width + move > minWidth) {
|
|
|
|
applyTransformWidth(width + move);
|
|
|
|
} else { // 最小の幅より小さくなろうとした時
|
|
|
|
applyTransformWidth(minWidth);
|
2020-10-17 14:12:00 +03:00
|
|
|
}
|
2022-07-17 18:31:55 +03:00
|
|
|
} else { // 右のはみ出し時
|
|
|
|
applyTransformWidth(browserWidth - left);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// 下ハンドル掴み時
|
2024-01-30 12:53:53 +02:00
|
|
|
function onBottomHandleMousedown(evt: MouseEvent | TouchEvent) {
|
2023-12-07 07:42:09 +02:00
|
|
|
const main = rootEl.value;
|
2022-12-18 12:52:50 +02:00
|
|
|
if (main == null) return;
|
2022-07-17 18:31:55 +03:00
|
|
|
|
2024-01-30 12:53:53 +02:00
|
|
|
const base = getPositionY(evt);
|
2022-07-17 18:31:55 +03:00
|
|
|
const height = parseInt(getComputedStyle(main, '').height, 10);
|
|
|
|
const top = parseInt(getComputedStyle(main, '').top, 10);
|
|
|
|
const browserHeight = window.innerHeight;
|
|
|
|
|
|
|
|
// 動かした時
|
|
|
|
dragListen(me => {
|
2024-01-30 12:53:53 +02:00
|
|
|
const move = getPositionY(me) - base;
|
2022-07-17 18:31:55 +03:00
|
|
|
if (top + height + move < browserHeight) {
|
|
|
|
if (height + move > minHeight) {
|
|
|
|
applyTransformHeight(height + move);
|
|
|
|
} else { // 最小の高さより小さくなろうとした時
|
|
|
|
applyTransformHeight(minHeight);
|
|
|
|
}
|
|
|
|
} else { // 下のはみ出し時
|
|
|
|
applyTransformHeight(browserHeight - top);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2022-07-17 18:31:55 +03:00
|
|
|
// 左ハンドル掴み時
|
2024-01-30 12:53:53 +02:00
|
|
|
function onLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
|
2023-12-07 07:42:09 +02:00
|
|
|
const main = rootEl.value;
|
2022-12-18 12:52:50 +02:00
|
|
|
if (main == null) return;
|
2022-07-17 18:31:55 +03:00
|
|
|
|
2024-01-30 12:53:53 +02:00
|
|
|
const base = getPositionX(evt);
|
2022-07-17 18:31:55 +03:00
|
|
|
const width = parseInt(getComputedStyle(main, '').width, 10);
|
|
|
|
const left = parseInt(getComputedStyle(main, '').left, 10);
|
|
|
|
|
|
|
|
// 動かした時
|
|
|
|
dragListen(me => {
|
2024-01-30 12:53:53 +02:00
|
|
|
const move = getPositionX(me) - base;
|
2022-07-17 18:31:55 +03:00
|
|
|
if (left + move > 0) {
|
|
|
|
if (width + -move > minWidth) {
|
|
|
|
applyTransformWidth(width + -move);
|
|
|
|
applyTransformLeft(left + move);
|
|
|
|
} else { // 最小の幅より小さくなろうとした時
|
|
|
|
applyTransformWidth(minWidth);
|
|
|
|
applyTransformLeft(left + (width - minWidth));
|
2020-10-24 19:21:41 +03:00
|
|
|
}
|
2022-07-17 18:31:55 +03:00
|
|
|
} else { // 左のはみ出し時
|
|
|
|
applyTransformWidth(left + width);
|
|
|
|
applyTransformLeft(0);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// 左上ハンドル掴み時
|
2024-01-30 12:53:53 +02:00
|
|
|
function onTopLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
|
2022-07-17 18:31:55 +03:00
|
|
|
onTopHandleMousedown(evt);
|
|
|
|
onLeftHandleMousedown(evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 右上ハンドル掴み時
|
2024-01-30 12:53:53 +02:00
|
|
|
function onTopRightHandleMousedown(evt: MouseEvent | TouchEvent) {
|
2022-07-17 18:31:55 +03:00
|
|
|
onTopHandleMousedown(evt);
|
|
|
|
onRightHandleMousedown(evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 右下ハンドル掴み時
|
2024-01-30 12:53:53 +02:00
|
|
|
function onBottomRightHandleMousedown(evt: MouseEvent | TouchEvent) {
|
2022-07-17 18:31:55 +03:00
|
|
|
onBottomHandleMousedown(evt);
|
|
|
|
onRightHandleMousedown(evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 左下ハンドル掴み時
|
2024-01-30 12:53:53 +02:00
|
|
|
function onBottomLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
|
2022-07-17 18:31:55 +03:00
|
|
|
onBottomHandleMousedown(evt);
|
|
|
|
onLeftHandleMousedown(evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 高さを適用
|
|
|
|
function applyTransformHeight(height) {
|
|
|
|
if (height > window.innerHeight) height = window.innerHeight;
|
2024-01-30 12:53:53 +02:00
|
|
|
if (rootEl.value) rootEl.value.style.height = height + 'px';
|
2022-07-17 18:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// 幅を適用
|
|
|
|
function applyTransformWidth(width) {
|
|
|
|
if (width > window.innerWidth) width = window.innerWidth;
|
2024-01-30 12:53:53 +02:00
|
|
|
if (rootEl.value) rootEl.value.style.width = width + 'px';
|
2022-07-17 18:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Y座標を適用
|
|
|
|
function applyTransformTop(top) {
|
2024-01-30 12:53:53 +02:00
|
|
|
if (rootEl.value) rootEl.value.style.top = top + 'px';
|
2022-07-17 18:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// X座標を適用
|
|
|
|
function applyTransformLeft(left) {
|
2024-01-30 12:53:53 +02:00
|
|
|
if (rootEl.value) rootEl.value.style.left = left + 'px';
|
2022-07-17 18:31:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function onBrowserResize() {
|
2023-12-07 07:42:09 +02:00
|
|
|
const main = rootEl.value;
|
2022-12-18 12:52:50 +02:00
|
|
|
if (main == null) return;
|
|
|
|
|
2022-07-17 18:31:55 +03:00
|
|
|
const position = main.getBoundingClientRect();
|
|
|
|
const browserWidth = window.innerWidth;
|
|
|
|
const browserHeight = window.innerHeight;
|
|
|
|
const windowWidth = main.offsetWidth;
|
|
|
|
const windowHeight = main.offsetHeight;
|
|
|
|
if (position.left < 0) main.style.left = '0'; // 左はみ出し
|
|
|
|
if (position.top + windowHeight > browserHeight) main.style.top = browserHeight - windowHeight + 'px'; // 下はみ出し
|
|
|
|
if (position.left + windowWidth > browserWidth) main.style.left = browserWidth - windowWidth + 'px'; // 右はみ出し
|
|
|
|
if (position.top < 0) main.style.top = '0'; // 上はみ出し
|
|
|
|
}
|
|
|
|
|
|
|
|
onMounted(() => {
|
2023-01-07 07:44:50 +02:00
|
|
|
applyTransformWidth(props.initialWidth);
|
2022-07-17 18:31:55 +03:00
|
|
|
if (props.initialHeight) applyTransformHeight(props.initialHeight);
|
|
|
|
|
2024-01-30 12:53:53 +02:00
|
|
|
if (rootEl.value) {
|
|
|
|
applyTransformTop((window.innerHeight / 2) - (rootEl.value.offsetHeight / 2));
|
|
|
|
applyTransformLeft((window.innerWidth / 2) - (rootEl.value.offsetWidth / 2));
|
|
|
|
}
|
2022-07-17 18:31:55 +03:00
|
|
|
|
|
|
|
// 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする
|
|
|
|
top();
|
|
|
|
|
|
|
|
window.addEventListener('resize', onBrowserResize);
|
|
|
|
});
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
window.removeEventListener('resize', onBrowserResize);
|
|
|
|
});
|
|
|
|
|
|
|
|
defineExpose({
|
|
|
|
close,
|
2020-10-17 14:12:00 +03:00
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
<style lang="scss" module>
|
|
|
|
.transition_window_enterActive,
|
|
|
|
.transition_window_leaveActive {
|
2021-10-16 13:30:31 +03:00
|
|
|
transition: opacity 0.2s, transform 0.2s !important;
|
2020-10-17 14:12:00 +03:00
|
|
|
}
|
2023-01-14 04:39:35 +02:00
|
|
|
.transition_window_enterFrom,
|
|
|
|
.transition_window_leaveTo {
|
2020-10-17 14:12:00 +03:00
|
|
|
pointer-events: none;
|
|
|
|
opacity: 0;
|
|
|
|
transform: scale(0.9);
|
|
|
|
}
|
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.root {
|
2020-10-17 14:12:00 +03:00
|
|
|
position: fixed;
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
2021-02-27 06:08:34 +02:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
&.maximized {
|
|
|
|
> .body {
|
|
|
|
border-radius: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.body {
|
|
|
|
overflow: clip;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
contain: content;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
border-radius: var(--radius);
|
|
|
|
}
|
2021-02-27 06:08:34 +02:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.header {
|
|
|
|
--height: 39px;
|
2021-02-27 06:08:34 +02:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
&.mini {
|
|
|
|
--height: 32px;
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
display: flex;
|
|
|
|
position: relative;
|
|
|
|
z-index: 1;
|
|
|
|
flex-shrink: 0;
|
|
|
|
user-select: none;
|
|
|
|
height: var(--height);
|
|
|
|
background: var(--windowHeader);
|
|
|
|
-webkit-backdrop-filter: var(--blur, blur(15px));
|
|
|
|
backdrop-filter: var(--blur, blur(15px));
|
|
|
|
//border-bottom: solid 1px var(--divider);
|
2023-03-01 10:30:23 +02:00
|
|
|
font-size: 90%;
|
2023-01-14 04:39:35 +02:00
|
|
|
font-weight: bold;
|
|
|
|
}
|
2022-06-20 11:38:49 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.headerButton {
|
|
|
|
height: var(--height);
|
|
|
|
width: var(--height);
|
2021-12-24 05:34:24 +02:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
&:hover {
|
|
|
|
color: var(--fgHighlighted);
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
&.highlighted {
|
|
|
|
color: var(--accent);
|
2020-10-17 14:12:00 +03:00
|
|
|
}
|
2023-01-14 04:39:35 +02:00
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.headerLeft {
|
|
|
|
margin-right: 16px;
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.headerRight {
|
|
|
|
min-width: 16px;
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.headerTitle {
|
|
|
|
flex: 1;
|
|
|
|
position: relative;
|
|
|
|
line-height: var(--height);
|
|
|
|
white-space: nowrap;
|
|
|
|
overflow: hidden;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
cursor: move;
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.content {
|
|
|
|
flex: 1;
|
|
|
|
overflow: auto;
|
|
|
|
background: var(--panel);
|
2023-05-08 12:30:40 +03:00
|
|
|
container-type: size;
|
2023-01-14 04:39:35 +02:00
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
$handleSize: 8px;
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.handle {
|
|
|
|
position: absolute;
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.handleTop {
|
|
|
|
composes: handle;
|
|
|
|
top: -($handleSize);
|
|
|
|
left: 0;
|
|
|
|
width: 100%;
|
|
|
|
height: $handleSize;
|
|
|
|
cursor: ns-resize;
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.handleRight {
|
|
|
|
composes: handle;
|
|
|
|
top: 0;
|
|
|
|
right: -($handleSize);
|
|
|
|
width: $handleSize;
|
|
|
|
height: 100%;
|
|
|
|
cursor: ew-resize;
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.handleBottom {
|
|
|
|
composes: handle;
|
|
|
|
bottom: -($handleSize);
|
|
|
|
left: 0;
|
|
|
|
width: 100%;
|
|
|
|
height: $handleSize;
|
|
|
|
cursor: ns-resize;
|
|
|
|
}
|
2020-10-17 14:12:00 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.handleLeft {
|
|
|
|
composes: handle;
|
|
|
|
top: 0;
|
|
|
|
left: -($handleSize);
|
|
|
|
width: $handleSize;
|
|
|
|
height: 100%;
|
|
|
|
cursor: ew-resize;
|
|
|
|
}
|
2022-07-17 23:03:39 +03:00
|
|
|
|
2023-01-14 04:39:35 +02:00
|
|
|
.handleTopLeft {
|
|
|
|
composes: handle;
|
|
|
|
top: -($handleSize);
|
|
|
|
left: -($handleSize);
|
|
|
|
width: $handleSize * 2;
|
|
|
|
height: $handleSize * 2;
|
|
|
|
cursor: nwse-resize;
|
|
|
|
}
|
|
|
|
|
|
|
|
.handleTopRight {
|
|
|
|
composes: handle;
|
|
|
|
top: -($handleSize);
|
|
|
|
right: -($handleSize);
|
|
|
|
width: $handleSize * 2;
|
|
|
|
height: $handleSize * 2;
|
|
|
|
cursor: nesw-resize;
|
|
|
|
}
|
|
|
|
|
|
|
|
.handleBottomRight {
|
|
|
|
composes: handle;
|
|
|
|
bottom: -($handleSize);
|
|
|
|
right: -($handleSize);
|
|
|
|
width: $handleSize * 2;
|
|
|
|
height: $handleSize * 2;
|
|
|
|
cursor: nwse-resize;
|
|
|
|
}
|
|
|
|
|
|
|
|
.handleBottomLeft {
|
|
|
|
composes: handle;
|
|
|
|
bottom: -($handleSize);
|
|
|
|
left: -($handleSize);
|
|
|
|
width: $handleSize * 2;
|
|
|
|
height: $handleSize * 2;
|
|
|
|
cursor: nesw-resize;
|
2020-10-17 14:12:00 +03:00
|
|
|
}
|
|
|
|
</style>
|