ソースを参照

refactor: Moved drag box handling to mixin

Owen Diffey 2 年 前
コミット
2607236a11

+ 24 - 100
frontend/src/components/AdvancedTable.vue

@@ -707,8 +707,8 @@
 			v-if="hasCheckboxes && selectedRows.length > 0"
 			class="bulk-popup"
 			:style="{
-				top: bulkPopup.top + 'px',
-				left: bulkPopup.left + 'px'
+				top: dragBox.top + 'px',
+				left: dragBox.left + 'px'
 			}"
 			ref="bulk-popup"
 		>
@@ -729,7 +729,7 @@
 					class="material-icons drag-icon"
 					@mousedown.left="onDragBox"
 					@touchstart="onDragBox"
-					@dblclick="resetBulkActionsPosition()"
+					@dblclick="resetBoxPosition()"
 				>
 					drag_indicator
 				</span>
@@ -745,6 +745,8 @@ import draggable from "vuedraggable";
 import Toast from "toasters";
 import AutoSuggest from "@/components/AutoSuggest.vue";
 
+import DragBox from "@/mixins/DragBox.vue";
+
 import keyboardShortcuts from "@/keyboardShortcuts";
 import ws from "@/ws";
 
@@ -753,6 +755,7 @@ export default {
 		draggable,
 		AutoSuggest
 	},
+	mixins: [DragBox],
 	props: {
 		/*
 		Column properties:
@@ -863,19 +866,6 @@ export default {
 					displayName: "Boolean"
 				}
 			},
-			bulkPopup: {
-				top: 0,
-				left: 0,
-				pos1: 0,
-				pos2: 0,
-				pos3: 0,
-				pos4: 0,
-				initial: {
-					top: null,
-					left: null
-				},
-				debounceTimeout: null
-			},
 			addFilterValue: null,
 			showFiltersDropdown: false,
 			showColumnsDropdown: false,
@@ -884,7 +874,9 @@ export default {
 			lastBulkActionsTappedDate: 0,
 			autosuggest: {
 				allItems: {}
-			}
+			},
+			storeTableSettingsDebounceTimeout: null,
+			windowResizeDebounceTimeout: null
 		};
 	},
 	computed: {
@@ -1083,8 +1075,6 @@ export default {
 			}
 		}
 
-		this.resetBulkActionsPosition();
-
 		this.$nextTick(() => {
 			this.onWindowResize();
 			window.addEventListener("resize", this.onWindowResize);
@@ -1304,6 +1294,8 @@ export default {
 		window.removeEventListener("resize", this.onWindowResize);
 		if (this.storeTableSettingsDebounceTimeout)
 			clearTimeout(this.storeTableSettingsDebounceTimeout);
+		if (this.windowResizeDebounceTimeout)
+			clearTimeout(this.windowResizeDebounceTimeout);
 
 		if (this.keyboardShortcuts) {
 			const shortcutNames = [
@@ -1705,71 +1697,6 @@ export default {
 					this.editingFilters[index].filter.defaultFilterType
 				];
 		},
-		onDragBox(e) {
-			const e1 = e || window.event;
-			const e1IsTouch = e1.type === "touchstart";
-			e1.preventDefault();
-
-			if (e1IsTouch) {
-				// Handle double click from touch (if this method is twice in a row within one second)
-				if (Date.now() - this.lastBulkActionsTappedDate <= 1000) {
-					this.resetBulkActionsPosition();
-					this.lastBulkActionsTappedDate = 0;
-					return;
-				}
-				this.lastBulkActionsTappedDate = Date.now();
-			}
-
-			this.bulkPopup.pos3 = e1IsTouch
-				? e1.changedTouches[0].clientX
-				: e1.clientX;
-			this.bulkPopup.pos4 = e1IsTouch
-				? e1.changedTouches[0].clientY
-				: e1.clientY;
-
-			document.onmousemove = document.ontouchmove = e => {
-				const e2 = e || window.event;
-				const e2IsTouch = e2.type === "touchmove";
-				if (!e2IsTouch) e2.preventDefault();
-
-				// Get the clientX and clientY
-				const e2ClientX = e2IsTouch
-					? e2.changedTouches[0].clientX
-					: e2.clientX;
-				const e2ClientY = e2IsTouch
-					? e2.changedTouches[0].clientY
-					: e2.clientY;
-
-				// calculate the new cursor position:
-				this.bulkPopup.pos1 = this.bulkPopup.pos3 - e2ClientX;
-				this.bulkPopup.pos2 = this.bulkPopup.pos4 - e2ClientY;
-				this.bulkPopup.pos3 = e2ClientX;
-				this.bulkPopup.pos4 = e2ClientY;
-				// set the element's new position:
-				this.bulkPopup.top -= this.bulkPopup.pos2;
-				this.bulkPopup.left -= this.bulkPopup.pos1;
-
-				if (this.bulkPopup.top < 0) this.bulkPopup.top = 0;
-				if (this.bulkPopup.top > document.body.clientHeight - 50)
-					this.bulkPopup.top = document.body.clientHeight - 50;
-				if (this.bulkPopup.left < 0) this.bulkPopup.left = 0;
-				if (this.bulkPopup.left > document.body.clientWidth - 400)
-					this.bulkPopup.left = document.body.clientWidth - 400;
-			};
-
-			document.onmouseup = document.ontouchend = () => {
-				document.onmouseup = null;
-				document.ontouchend = null;
-				document.onmousemove = null;
-				document.ontouchmove = null;
-			};
-		},
-		resetBulkActionsPosition() {
-			this.bulkPopup.top = document.body.clientHeight - 56;
-			this.bulkPopup.left = document.body.clientWidth / 2 - 200;
-			this.bulkPopup.initial.top = this.bulkPopup.top;
-			this.bulkPopup.initial.left = this.bulkPopup.left;
-		},
 		applyFilterAndGetData() {
 			this.appliedFilters = JSON.parse(
 				JSON.stringify(this.editingFilters)
@@ -1930,25 +1857,22 @@ export default {
 			);
 		},
 		onWindowResize() {
-			if (this.bulkPopup.debounceTimeout)
-				clearTimeout(this.bulkPopup.debounceTimeout);
+			if (this.windowResizeDebounceTimeout)
+				clearTimeout(this.windowResizeDebounceTimeout);
 
-			this.bulkPopup.debounceTimeout = setTimeout(() => {
+			this.windowResizeDebounceTimeout = setTimeout(() => {
 				// Only change the position if the popup is actually visible
 				if (this.selectedRows.length === 0) return;
-				if (
-					this.bulkPopup.top === this.bulkPopup.initial.top &&
-					this.bulkPopup.left === this.bulkPopup.initial.left
-				)
-					this.resetBulkActionsPosition();
-				else {
-					if (this.bulkPopup.top < 0) this.bulkPopup.top = 0;
-					if (this.bulkPopup.top > document.body.clientHeight - 50)
-						this.bulkPopup.top = document.body.clientHeight - 50;
-					if (this.bulkPopup.left < 0) this.bulkPopup.left = 0;
-					if (this.bulkPopup.left > document.body.clientWidth - 400)
-						this.bulkPopup.left = document.body.clientWidth - 400;
-				}
+
+				this.setInitialBox(
+					{
+						top: document.body.clientHeight - 56,
+						left: document.body.clientWidth / 2 - 200,
+						width: 400,
+						height: 50
+					},
+					true
+				);
 			}, 50);
 		},
 		updateData(index, data) {

+ 70 - 67
frontend/src/components/FloatingBox.vue

@@ -8,15 +8,17 @@
 		:id="id"
 		v-if="persist || shown"
 		:style="{
-			width: width + 'px',
-			height: height + 'px',
-			top: top + 'px',
-			left: left + 'px'
+			width: dragBox.width + 'px',
+			height: dragBox.height + 'px',
+			top: dragBox.top + 'px',
+			left: dragBox.left + 'px'
 		}"
 		@mousedown.left="onResizeBox"
 	>
 		<div class="box-header item-draggable" @mousedown.left="onDragBox">
-			<span class="drag material-icons">drag_indicator</span>
+			<span class="drag material-icons" @dblclick="resetBoxPosition()"
+				>drag_indicator</span
+			>
 			<span v-if="title" class="box-title">{{ title }}</span>
 			<span
 				v-if="!persist"
@@ -32,64 +34,46 @@
 </template>
 
 <script>
+import DragBox from "@/mixins/DragBox.vue";
+
 export default {
+	mixins: [DragBox],
 	props: {
 		id: { type: String, default: null },
 		column: { type: Boolean, default: true },
 		title: { type: String, default: null },
-		persist: { type: Boolean, default: false }
+		persist: { type: Boolean, default: false },
+		initial: { type: String, default: "align-top" },
+		minWidth: { type: Number, default: 100 },
+		maxWidth: { type: Number, default: 1000 },
+		minHeight: { type: Number, default: 100 },
+		maxHeight: { type: Number, default: 1000 }
 	},
 	data() {
 		return {
-			width: 200,
-			height: 200,
-			top: 0,
-			left: 0,
-			shown: false,
-			pos1: 0,
-			pos2: 0,
-			pos3: 0,
-			pos4: 0
+			shown: false
 		};
 	},
 	mounted() {
+		let initial = {
+			top: 10,
+			left: 10,
+			width: 200,
+			height: 400
+		};
 		if (this.id !== null && localStorage[`box:${this.id}`]) {
 			const json = JSON.parse(localStorage.getItem(`box:${this.id}`));
-			this.height = json.height;
-			this.width = json.width;
-			this.top = json.top;
-			this.left = json.left;
+			initial = { ...initial, ...json };
 			this.shown = json.shown;
+		} else {
+			initial.top =
+				this.initial === "align-bottom"
+					? document.body.clientHeight - 10 - initial.height
+					: 10;
 		}
+		this.setInitialBox(initial, true);
 	},
 	methods: {
-		onDragBox(e) {
-			const e1 = e || window.event;
-			e1.preventDefault();
-
-			this.pos3 = e1.clientX;
-			this.pos4 = e1.clientY;
-
-			document.onmousemove = e => {
-				const e2 = e || window.event;
-				e2.preventDefault();
-				// calculate the new cursor position:
-				this.pos1 = this.pos3 - e.clientX;
-				this.pos2 = this.pos4 - e.clientY;
-				this.pos3 = e.clientX;
-				this.pos4 = e.clientY;
-				// set the element's new position:
-				this.top -= this.pos2;
-				this.left -= this.pos1;
-			};
-
-			document.onmouseup = () => {
-				document.onmouseup = null;
-				document.onmousemove = null;
-
-				this.saveBox();
-			};
-		},
 		onResizeBox(e) {
 			if (e.target !== this.$refs.box) return;
 
@@ -98,17 +82,30 @@ export default {
 
 				const { height, width } = e.target.style;
 
-				this.height = Number(
-					height
-						.split("")
-						.splice(0, height.length - 2)
-						.join("")
+				this.dragBox.height = Math.min(
+					Math.max(
+						Number(
+							height
+								.split("")
+								.splice(0, height.length - 2)
+								.join("")
+						),
+						this.minHeight
+					),
+					this.maxHeight
 				);
-				this.width = Number(
-					width
-						.split("")
-						.splice(0, width.length - 2)
-						.join("")
+
+				this.dragBox.width = Math.min(
+					Math.max(
+						Number(
+							width
+								.split("")
+								.splice(0, width.length - 2)
+								.join("")
+						),
+						this.minWidth
+					),
+					this.maxWidth
 				);
 
 				this.saveBox();
@@ -118,11 +115,9 @@ export default {
 			this.shown = !this.shown;
 			this.saveBox();
 		},
-		resetBox() {
-			this.top = 0;
-			this.left = 0;
-			this.width = 200;
-			this.height = 200;
+		resetBoxDimensions() {
+			this.dragBox.width = 200;
+			this.dragBox.height = 200;
 			this.saveBox();
 		},
 		saveBox() {
@@ -130,13 +125,23 @@ export default {
 			localStorage.setItem(
 				`box:${this.id}`,
 				JSON.stringify({
-					height: this.height,
-					width: this.width,
-					top: this.top,
-					left: this.left,
+					height: this.dragBox.height,
+					width: this.dragBox.width,
+					top: this.dragBox.top,
+					left: this.dragBox.left,
 					shown: this.shown
 				})
 			);
+			this.setInitialBox({
+				top:
+					this.initial === "align-bottom"
+						? document.body.clientHeight - 10 - this.dragBox.height
+						: 10,
+				left: 10
+			});
+		},
+		onDragBoxUpdate() {
+			this.saveBox();
 		}
 	}
 };
@@ -162,8 +167,6 @@ export default {
 	overflow: auto;
 	border: 1px solid var(--light-grey-2);
 	border-radius: @border-radius;
-	min-height: 50px !important;
-	min-width: 50px !important;
 	padding: 0;
 
 	.box-header {

+ 4 - 3
frontend/src/components/LongJobs.vue

@@ -5,6 +5,10 @@
 		id="longJobs"
 		ref="longJobs"
 		:persist="true"
+		initial="align-bottom"
+		:min-width="200"
+		:max-width="400"
+		:min-height="200"
 	>
 		<template #body>
 			<div class="active-jobs">
@@ -101,9 +105,6 @@ export default {
 }
 
 #longJobs {
-	min-width: 200px !important;
-	max-width: 400px !important;
-	min-height: 200px !important;
 	z-index: 5000 !important;
 
 	.active-jobs {

+ 156 - 0
frontend/src/mixins/DragBox.vue

@@ -0,0 +1,156 @@
+<script>
+export default {
+	data() {
+		return {
+			dragBox: {
+				top: 0,
+				left: 0,
+				pos1: 0,
+				pos2: 0,
+				pos3: 0,
+				pos4: 0,
+				width: 400,
+				height: 50,
+				initial: {
+					top: 0,
+					left: 0
+				},
+				latest: {
+					top: null,
+					left: null
+				},
+				debounceTimeout: null,
+				lastTappedDate: 0
+			}
+		};
+	},
+	mounted() {
+		this.resetBoxPosition(true);
+
+		this.$nextTick(() => {
+			this.onWindowResizeDragBox();
+			window.addEventListener("resize", this.onWindowResizeDragBox);
+		});
+	},
+	unmounted() {
+		window.removeEventListener("resize", this.onWindowResizeDragBox);
+		if (this.dragBox.debounceTimeout)
+			clearTimeout(this.dragBox.debounceTimeout);
+	},
+	methods: {
+		setInitialBox(initial, reset) {
+			this.dragBox.initial = initial || this.dragBox.initial;
+			if (reset)
+				this.dragBox = { ...this.dragBox, ...this.dragBox.initial };
+		},
+		onDragBox(e) {
+			const e1 = e || window.event;
+			const e1IsTouch = e1.type === "touchstart";
+			e1.preventDefault();
+
+			if (e1IsTouch) {
+				// Handle double click from touch (if this method is twice in a row within one second)
+				if (Date.now() - this.dragBox.lastTappedDate <= 1000) {
+					this.resetBoxPosition();
+					this.dragBox.lastTappedDate = 0;
+					return;
+				}
+				this.dragBox.lastTappedDate = Date.now();
+			}
+
+			this.dragBox.pos3 = e1IsTouch
+				? e1.changedTouches[0].clientX
+				: e1.clientX;
+			this.dragBox.pos4 = e1IsTouch
+				? e1.changedTouches[0].clientY
+				: e1.clientY;
+
+			document.onmousemove = document.ontouchmove = e => {
+				const e2 = e || window.event;
+				const e2IsTouch = e2.type === "touchmove";
+				if (!e2IsTouch) e2.preventDefault();
+
+				// Get the clientX and clientY
+				const e2ClientX = e2IsTouch
+					? e2.changedTouches[0].clientX
+					: e2.clientX;
+				const e2ClientY = e2IsTouch
+					? e2.changedTouches[0].clientY
+					: e2.clientY;
+
+				// calculate the new cursor position:
+				this.dragBox.pos1 = this.dragBox.pos3 - e2ClientX;
+				this.dragBox.pos2 = this.dragBox.pos4 - e2ClientY;
+				this.dragBox.pos3 = e2ClientX;
+				this.dragBox.pos4 = e2ClientY;
+				// set the element's new position:
+				this.dragBox.top -= this.dragBox.pos2;
+				this.dragBox.left -= this.dragBox.pos1;
+
+				if (this.dragBox.top < 0) this.dragBox.top = 0;
+				if (
+					this.dragBox.top >
+					document.body.clientHeight - this.dragBox.height
+				)
+					this.dragBox.top =
+						document.body.clientHeight - this.dragBox.height;
+				if (this.dragBox.left < 0) this.dragBox.left = 0;
+				if (
+					this.dragBox.left >
+					document.body.clientWidth - this.dragBox.width
+				)
+					this.dragBox.left =
+						document.body.clientWidth - this.dragBox.width;
+			};
+
+			document.onmouseup = document.ontouchend = () => {
+				document.onmouseup = null;
+				document.ontouchend = null;
+				document.onmousemove = null;
+				document.ontouchmove = null;
+
+				if (typeof this.onDragBoxUpdate === "function")
+					this.onDragBoxUpdate();
+			};
+		},
+		resetBoxPosition(preventUpdate) {
+			this.setInitialBox(null, true);
+			this.dragBox.latest.top = this.dragBox.top;
+			this.dragBox.latest.left = this.dragBox.left;
+			if (!preventUpdate && typeof this.onDragBoxUpdate === "function")
+				this.onDragBoxUpdate();
+		},
+		onWindowResizeDragBox() {
+			if (this.dragBox.debounceTimeout)
+				clearTimeout(this.dragBox.debounceTimeout);
+
+			this.dragBox.debounceTimeout = setTimeout(() => {
+				if (
+					this.dragBox.top === this.dragBox.latest.top &&
+					this.dragBox.left === this.dragBox.latest.left
+				)
+					this.resetBoxPosition();
+				else {
+					if (this.dragBox.top < 0) this.dragBox.top = 0;
+					if (
+						this.dragBox.top >
+						document.body.clientHeight - this.dragBox.height
+					)
+						this.dragBox.top =
+							document.body.clientHeight - this.dragBox.height;
+					if (this.dragBox.left < 0) this.dragBox.left = 0;
+					if (
+						this.dragBox.left >
+						document.body.clientWidth - this.dragBox.width
+					)
+						this.dragBox.left =
+							document.body.clientWidth - this.dragBox.width;
+
+					if (typeof this.onDragBoxUpdate === "function")
+						this.onDragBoxUpdate();
+				}
+			}, 50);
+		}
+	}
+};
+</script>