summaryrefslogtreecommitdiff
path: root/packages/client/src/components
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-07-18 00:31:55 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2022-07-18 00:31:55 +0900
commite9a97b471796c58b946a656372aeaddab871a292 (patch)
treeffca4a3cf9534e3c95c3a276b2ec851c4c23f209 /packages/client/src/components
parent:art: (diff)
downloadmisskey-e9a97b471796c58b946a656372aeaddab871a292.tar.gz
misskey-e9a97b471796c58b946a656372aeaddab871a292.tar.bz2
misskey-e9a97b471796c58b946a656372aeaddab871a292.zip
enhance(client): ウィンドウを最大化できるように
Diffstat (limited to 'packages/client/src/components')
-rw-r--r--packages/client/src/components/ui/window.vue562
1 files changed, 295 insertions, 267 deletions
diff --git a/packages/client/src/components/ui/window.vue b/packages/client/src/components/ui/window.vue
index d155033824..1208619177 100644
--- a/packages/client/src/components/ui/window.vue
+++ b/packages/client/src/components/ui/window.vue
@@ -1,6 +1,6 @@
<template>
<transition :name="$store.state.animation ? 'window' : ''" appear @after-leave="$emit('closed')">
- <div v-if="showing" class="ebkgocck">
+ <div v-if="showing" ref="rootEl" class="ebkgocck">
<div class="body _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown">
<div class="header" :class="{ mini }" @contextmenu.prevent.stop="onContextmenu">
<span class="left">
@@ -11,6 +11,8 @@
</span>
<span class="right">
<button v-for="button in buttonsRight" v-tooltip="button.title" class="button _button" :class="{ highlighted: button.highlighted }" @click="button.onClick"><i :class="button.icon"></i></button>
+ <button v-if="canResize && maximized" class="button _button" @click="unMaximize()"><i class="fas fa-window-restore"></i></button>
+ <button v-else-if="canResize && !maximized" class="button _button" @click="maximize()"><i class="fas fa-window-maximize"></i></button>
<button v-if="closeButton" class="button _button" @click="close()"><i class="fas fa-times"></i></button>
</span>
</div>
@@ -32,15 +34,16 @@
</transition>
</template>
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onBeforeUnmount, onMounted, provide } from 'vue';
import contains from '@/scripts/contains';
import * as os from '@/os';
+import { MenuItem } from '@/types/menu';
const minHeight = 50;
const minWidth = 250;
-function dragListen(fn) {
+function dragListen(fn: (ev: MouseEvent) => void) {
window.addEventListener('mousemove', fn);
window.addEventListener('touchmove', fn);
window.addEventListener('mouseleave', dragClear.bind(null, fn));
@@ -56,315 +59,340 @@ function dragClear(fn) {
window.removeEventListener('touchend', dragClear);
}
-export default defineComponent({
- provide: {
- inWindow: true,
- },
+const props = withDefaults(defineProps<{
+ initialWidth?: number;
+ initialHeight?: number | null;
+ 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,
+ front: true,
+ contextmenu: null,
+ buttonsLeft: () => [],
+ buttonsRight: () => [],
+});
- props: {
- initialWidth: {
- type: Number,
- required: false,
- default: 400,
- },
- initialHeight: {
- type: Number,
- required: false,
- default: null,
- },
- canResize: {
- type: Boolean,
- required: false,
- default: false,
- },
- closeButton: {
- type: Boolean,
- required: false,
- default: true,
- },
- mini: {
- type: Boolean,
- required: false,
- default: false,
- },
- front: {
- type: Boolean,
- required: false,
- default: false,
- },
- contextmenu: {
- type: Array,
- required: false,
- },
- buttonsLeft: {
- type: Array,
- required: false,
- default: () => [],
- },
- buttonsRight: {
- type: Array,
- required: false,
- default: () => [],
- },
- },
+const emit = defineEmits<{
+ (ev: 'closed'): void;
+}>();
- emits: ['closed'],
+provide('inWindow', true);
- data() {
- return {
- showing: true,
- id: Math.random().toString(), // TODO: UUIDとかにする
- };
- },
+let rootEl = $ref<HTMLElement>();
+let showing = $ref(true);
+let beforeClickedAt = 0;
+let maximized = $ref(false);
+let unMaximizedTop = '';
+let unMaximizedLeft = '';
+let unMaximizedWidth = '';
+let unMaximizedHeight = '';
- mounted() {
- if (this.initialWidth) this.applyTransformWidth(this.initialWidth);
- if (this.initialHeight) this.applyTransformHeight(this.initialHeight);
+function close() {
+ showing = false;
+}
- this.applyTransformTop((window.innerHeight / 2) - (this.$el.offsetHeight / 2));
- this.applyTransformLeft((window.innerWidth / 2) - (this.$el.offsetWidth / 2));
+function onKeydown(evt) {
+ if (evt.which === 27) { // Esc
+ evt.preventDefault();
+ evt.stopPropagation();
+ close();
+ }
+}
- // 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする
- this.top();
+function onContextmenu(ev: MouseEvent) {
+ if (props.contextmenu) {
+ os.contextMenu(props.contextmenu, ev);
+ }
+}
- window.addEventListener('resize', this.onBrowserResize);
- },
+// 最前面へ移動
+function top() {
+ rootEl.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low');
+}
- unmounted() {
- window.removeEventListener('resize', this.onBrowserResize);
- },
+function maximize() {
+ maximized = true;
+ unMaximizedTop = rootEl.style.top;
+ unMaximizedLeft = rootEl.style.left;
+ unMaximizedWidth = rootEl.style.width;
+ unMaximizedHeight = rootEl.style.height;
+ rootEl.style.top = '0';
+ rootEl.style.left = '0';
+ rootEl.style.width = '100%';
+ rootEl.style.height = '100%';
+}
- methods: {
- close() {
- this.showing = false;
- },
+function unMaximize() {
+ maximized = false;
+ rootEl.style.top = unMaximizedTop;
+ rootEl.style.left = unMaximizedLeft;
+ rootEl.style.width = unMaximizedWidth;
+ rootEl.style.height = unMaximizedHeight;
+}
- onKeydown(evt) {
- if (evt.which === 27) { // Esc
- evt.preventDefault();
- evt.stopPropagation();
- this.close();
- }
- },
+function onBodyMousedown() {
+ top();
+}
- onContextmenu(ev: MouseEvent) {
- if (this.contextmenu) {
- os.contextMenu(this.contextmenu, ev);
- }
- },
+function onDblClick() {
+ maximize();
+}
- // 最前面へ移動
- top() {
- (this.$el as any).style.zIndex = os.claimZIndex(this.front ? 'middle' : 'low');
- },
+function onHeaderMousedown(evt: MouseEvent) {
+ // 右クリックはコンテキストメニューを開こうとした可能性が高いため無視
+ if (evt.button === 2) return;
- onBodyMousedown() {
- this.top();
- },
+ let beforeMaximized = false;
- onHeaderMousedown(evt: MouseEvent) {
- // 右クリックはコンテキストメニューを開こうとした可能性が高いため無視
- if (evt.button === 2) return;
+ if (maximized) {
+ beforeMaximized = true;
+ unMaximize();
+ }
- const main = this.$el as any;
+ // ダブルクリック判定
+ if (Date.now() - beforeClickedAt < 300) {
+ beforeClickedAt = Date.now();
+ onDblClick();
+ return;
+ }
- if (!contains(main, document.activeElement)) main.focus();
+ beforeClickedAt = Date.now();
- const position = main.getBoundingClientRect();
+ const main = rootEl;
- const clickX = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientX : evt.clientX;
- const clickY = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientY : evt.clientY;
- const moveBaseX = clickX - position.left;
- const moveBaseY = clickY - position.top;
- const browserWidth = window.innerWidth;
- const browserHeight = window.innerHeight;
- const windowWidth = main.offsetWidth;
- const windowHeight = main.offsetHeight;
+ if (!contains(main, document.activeElement)) main.focus();
- // 動かした時
- dragListen(me => {
- const x = me.touches && me.touches.length > 0 ? me.touches[0].clientX : me.clientX;
- const y = me.touches && me.touches.length > 0 ? me.touches[0].clientY : me.clientY;
+ const position = main.getBoundingClientRect();
- let moveLeft = x - moveBaseX;
- let moveTop = y - moveBaseY;
+ const clickX = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientX : evt.clientX;
+ const clickY = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientY : evt.clientY;
+ const moveBaseX = beforeMaximized ? parseInt(unMaximizedWidth, 10) / 2 : clickX - position.left; // TODO: parseIntやめる
+ const moveBaseY = beforeMaximized ? 20 : clickY - position.top;
+ const browserWidth = window.innerWidth;
+ const browserHeight = window.innerHeight;
+ const windowWidth = main.offsetWidth;
+ const windowHeight = main.offsetHeight;
- // 下はみ出し
- if (moveTop + windowHeight > browserHeight) moveTop = browserHeight - windowHeight;
+ function move(x: number, y: number) {
+ let moveLeft = x - moveBaseX;
+ let moveTop = y - moveBaseY;
- // 左はみ出し
- if (moveLeft < 0) moveLeft = 0;
+ // 下はみ出し
+ if (moveTop + windowHeight > browserHeight) moveTop = browserHeight - windowHeight;
- // 上はみ出し
- if (moveTop < 0) moveTop = 0;
+ // 左はみ出し
+ if (moveLeft < 0) moveLeft = 0;
- // 右はみ出し
- if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth;
+ // 上はみ出し
+ if (moveTop < 0) moveTop = 0;
- this.$el.style.left = moveLeft + 'px';
- this.$el.style.top = moveTop + 'px';
- });
- },
+ // 右はみ出し
+ if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth;
- // 上ハンドル掴み時
- onTopHandleMousedown(evt) {
- const main = this.$el as any;
+ rootEl.style.left = moveLeft + 'px';
+ rootEl.style.top = moveTop + 'px';
+ }
- const base = evt.clientY;
- const height = parseInt(getComputedStyle(main, '').height, 10);
- const top = parseInt(getComputedStyle(main, '').top, 10);
+ if (beforeMaximized) {
+ move(clickX, clickY);
+ }
- // 動かした時
- dragListen(me => {
- const move = me.clientY - base;
- if (top + move > 0) {
- if (height + -move > minHeight) {
- this.applyTransformHeight(height + -move);
- this.applyTransformTop(top + move);
- } else { // 最小の高さより小さくなろうとした時
- this.applyTransformHeight(minHeight);
- this.applyTransformTop(top + (height - minHeight));
- }
- } else { // 上のはみ出し時
- this.applyTransformHeight(top + height);
- this.applyTransformTop(0);
- }
- });
- },
+ // 動かした時
+ dragListen(me => {
+ const x = me.touches && me.touches.length > 0 ? me.touches[0].clientX : me.clientX;
+ const y = me.touches && me.touches.length > 0 ? me.touches[0].clientY : me.clientY;
- // 右ハンドル掴み時
- onRightHandleMousedown(evt) {
- const main = this.$el as any;
+ move(x, y);
+ });
+}
- const base = evt.clientX;
- const width = parseInt(getComputedStyle(main, '').width, 10);
- const left = parseInt(getComputedStyle(main, '').left, 10);
- const browserWidth = window.innerWidth;
+// 上ハンドル掴み時
+function onTopHandleMousedown(evt) {
+ const main = rootEl;
- // 動かした時
- dragListen(me => {
- const move = me.clientX - base;
- if (left + width + move < browserWidth) {
- if (width + move > minWidth) {
- this.applyTransformWidth(width + move);
- } else { // 最小の幅より小さくなろうとした時
- this.applyTransformWidth(minWidth);
- }
- } else { // 右のはみ出し時
- this.applyTransformWidth(browserWidth - left);
- }
- });
- },
+ const base = evt.clientY;
+ const height = parseInt(getComputedStyle(main, '').height, 10);
+ const top = parseInt(getComputedStyle(main, '').top, 10);
- // 下ハンドル掴み時
- onBottomHandleMousedown(evt) {
- const main = this.$el as any;
+ // 動かした時
+ dragListen(me => {
+ const move = me.clientY - base;
+ 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);
+ }
+ });
+}
- const base = evt.clientY;
- const height = parseInt(getComputedStyle(main, '').height, 10);
- const top = parseInt(getComputedStyle(main, '').top, 10);
- const browserHeight = window.innerHeight;
+// 右ハンドル掴み時
+function onRightHandleMousedown(evt) {
+ const main = rootEl;
- // 動かした時
- dragListen(me => {
- const move = me.clientY - base;
- if (top + height + move < browserHeight) {
- if (height + move > minHeight) {
- this.applyTransformHeight(height + move);
- } else { // 最小の高さより小さくなろうとした時
- this.applyTransformHeight(minHeight);
- }
- } else { // 下のはみ出し時
- this.applyTransformHeight(browserHeight - top);
- }
- });
- },
+ const base = evt.clientX;
+ const width = parseInt(getComputedStyle(main, '').width, 10);
+ const left = parseInt(getComputedStyle(main, '').left, 10);
+ const browserWidth = window.innerWidth;
- // 左ハンドル掴み時
- onLeftHandleMousedown(evt) {
- const main = this.$el as any;
+ // 動かした時
+ dragListen(me => {
+ const move = me.clientX - base;
+ if (left + width + move < browserWidth) {
+ if (width + move > minWidth) {
+ applyTransformWidth(width + move);
+ } else { // 最小の幅より小さくなろうとした時
+ applyTransformWidth(minWidth);
+ }
+ } else { // 右のはみ出し時
+ applyTransformWidth(browserWidth - left);
+ }
+ });
+}
- const base = evt.clientX;
- const width = parseInt(getComputedStyle(main, '').width, 10);
- const left = parseInt(getComputedStyle(main, '').left, 10);
+// 下ハンドル掴み時
+function onBottomHandleMousedown(evt) {
+ const main = rootEl;
- // 動かした時
- dragListen(me => {
- const move = me.clientX - base;
- if (left + move > 0) {
- if (width + -move > minWidth) {
- this.applyTransformWidth(width + -move);
- this.applyTransformLeft(left + move);
- } else { // 最小の幅より小さくなろうとした時
- this.applyTransformWidth(minWidth);
- this.applyTransformLeft(left + (width - minWidth));
- }
- } else { // 左のはみ出し時
- this.applyTransformWidth(left + width);
- this.applyTransformLeft(0);
- }
- });
- },
+ const base = evt.clientY;
+ const height = parseInt(getComputedStyle(main, '').height, 10);
+ const top = parseInt(getComputedStyle(main, '').top, 10);
+ const browserHeight = window.innerHeight;
- // 左上ハンドル掴み時
- onTopLeftHandleMousedown(evt) {
- this.onTopHandleMousedown(evt);
- this.onLeftHandleMousedown(evt);
- },
+ // 動かした時
+ dragListen(me => {
+ const move = me.clientY - base;
+ if (top + height + move < browserHeight) {
+ if (height + move > minHeight) {
+ applyTransformHeight(height + move);
+ } else { // 最小の高さより小さくなろうとした時
+ applyTransformHeight(minHeight);
+ }
+ } else { // 下のはみ出し時
+ applyTransformHeight(browserHeight - top);
+ }
+ });
+}
- // 右上ハンドル掴み時
- onTopRightHandleMousedown(evt) {
- this.onTopHandleMousedown(evt);
- this.onRightHandleMousedown(evt);
- },
+// 左ハンドル掴み時
+function onLeftHandleMousedown(evt) {
+ const main = rootEl;
- // 右下ハンドル掴み時
- onBottomRightHandleMousedown(evt) {
- this.onBottomHandleMousedown(evt);
- this.onRightHandleMousedown(evt);
- },
+ const base = evt.clientX;
+ const width = parseInt(getComputedStyle(main, '').width, 10);
+ const left = parseInt(getComputedStyle(main, '').left, 10);
- // 左下ハンドル掴み時
- onBottomLeftHandleMousedown(evt) {
- this.onBottomHandleMousedown(evt);
- this.onLeftHandleMousedown(evt);
- },
+ // 動かした時
+ dragListen(me => {
+ const move = me.clientX - base;
+ if (left + move > 0) {
+ if (width + -move > minWidth) {
+ applyTransformWidth(width + -move);
+ applyTransformLeft(left + move);
+ } else { // 最小の幅より小さくなろうとした時
+ applyTransformWidth(minWidth);
+ applyTransformLeft(left + (width - minWidth));
+ }
+ } else { // 左のはみ出し時
+ applyTransformWidth(left + width);
+ applyTransformLeft(0);
+ }
+ });
+}
- // 高さを適用
- applyTransformHeight(height) {
- if (height > window.innerHeight) height = window.innerHeight;
- (this.$el as any).style.height = height + 'px';
- },
+// 左上ハンドル掴み時
+function onTopLeftHandleMousedown(evt) {
+ onTopHandleMousedown(evt);
+ onLeftHandleMousedown(evt);
+}
+
+// 右上ハンドル掴み時
+function onTopRightHandleMousedown(evt) {
+ onTopHandleMousedown(evt);
+ onRightHandleMousedown(evt);
+}
- // 幅を適用
- applyTransformWidth(width) {
- if (width > window.innerWidth) width = window.innerWidth;
- (this.$el as any).style.width = width + 'px';
- },
+// 右下ハンドル掴み時
+function onBottomRightHandleMousedown(evt) {
+ onBottomHandleMousedown(evt);
+ onRightHandleMousedown(evt);
+}
- // Y座標を適用
- applyTransformTop(top) {
- (this.$el as any).style.top = top + 'px';
- },
+// 左下ハンドル掴み時
+function onBottomLeftHandleMousedown(evt) {
+ onBottomHandleMousedown(evt);
+ onLeftHandleMousedown(evt);
+}
+
+// 高さを適用
+function applyTransformHeight(height) {
+ if (height > window.innerHeight) height = window.innerHeight;
+ rootEl.style.height = height + 'px';
+}
- // X座標を適用
- applyTransformLeft(left) {
- (this.$el as any).style.left = left + 'px';
- },
+// 幅を適用
+function applyTransformWidth(width) {
+ if (width > window.innerWidth) width = window.innerWidth;
+ rootEl.style.width = width + 'px';
+}
+
+// Y座標を適用
+function applyTransformTop(top) {
+ rootEl.style.top = top + 'px';
+}
+
+// X座標を適用
+function applyTransformLeft(left) {
+ rootEl.style.left = left + 'px';
+}
+
+function onBrowserResize() {
+ const main = rootEl;
+ 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(() => {
+ if (props.initialWidth) applyTransformWidth(props.initialWidth);
+ if (props.initialHeight) applyTransformHeight(props.initialHeight);
+
+ applyTransformTop((window.innerHeight / 2) - (rootEl.offsetHeight / 2));
+ applyTransformLeft((window.innerWidth / 2) - (rootEl.offsetWidth / 2));
+
+ // 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする
+ top();
+
+ window.addEventListener('resize', onBrowserResize);
+});
+
+onBeforeUnmount(() => {
+ window.removeEventListener('resize', onBrowserResize);
+});
- onBrowserResize() {
- const main = this.$el as any;
- 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; // 上はみ出し
- },
- },
+defineExpose({
+ close,
});
</script>