fix(Dashboard Mobile Touch Events): fixes the behaviour of the touch event for the dashboard grid

This commit is contained in:
SimonGschnell
2025-04-01 13:06:23 +02:00
parent 702a84a25d
commit 33f800fa28
7 changed files with 118 additions and 50 deletions
+8
View File
@@ -77,3 +77,11 @@
z-index: 2;
}
.dragged-widget-icon{
position: absolute;
height: 250px;
width: 200px;
top: -999px;
left: -999px;
z-index: 3;
}
@@ -34,6 +34,7 @@ export default {
provide() {
return {
editMode: Vue.computed(()=>this.editMode),
widgetsSetup: Vue.computed(() => this.widgets),
}
},
computed: {
@@ -142,6 +143,19 @@ export default {
}
},
created() {
axios.get(this.apiurl + '/Widget/getWidgetsForDashboard', {
params: {
db: this.dashboard
}
}).then(res => {
res.data.retval.forEach(widget => {
widget.arguments = JSON.parse(widget.arguments);
widget.setup = JSON.parse(widget.setup);
});
this.widgets = res.data.retval;
}).catch(err => console.error('ERROR:', err));
axios.get(this.apiurl + '/Config', {params:{
db: this.dashboard
}}).then(res => {
+15 -14
View File
@@ -49,18 +49,23 @@ export default {
return 'margin-bottom: 8px;';
},
items() {
const computeNearestPlace = (item, gridWidth) =>{
let place;
if (Object.keys(item.place).length > 0) {
const nearestIndex = Object.keys(item.place)
.sort((a, b) => Math.abs(a - gridWidth) - Math.abs(b - gridWidth))
.pop();
place = item.place[nearestIndex];
}
else{
place = { x: 0, y: 0, w: 1, h: 1 };
}
return place;
}
return this.widgets.map(item => {
let place;
if(!item.place[this.gridWidth]){
const nearestIndex = Object.keys(item.place).sort((a,b)=>Math.abs(a-this.gridWidth)-Math.abs(b-this.gridWidth)).pop();
if (nearestIndex === null){
place = {x:0,y:0,w:1,h:1};
}else{
place = item.place[nearestIndex];
}
}
return { ...item, ...(item.place[this.gridWidth] || place)};
return { ...item, ...(item.place[this.gridWidth] || computeNearestPlace(item, this.gridWidth))};
});
},
items_hashmap() {
@@ -73,10 +78,6 @@ export default {
items_placeholders(){
let placeholders = [];
let col_max = this.gridWidth;
// OLD way of calculating the max rows
//let max_row = Math.max(...this.items.map(item => item.y)) + 1;
//let max_row_max_height = Math.max(...this.items.filter(item => item.y == (max_row - 1)).map(item => item.h));
//max_row + max_row_max_height - 1;
let rows_max = this.gridHeight;
// occupied hashmap to keep track of the occupied cells
@@ -1,8 +1,10 @@
import BsModal from "../../Bootstrap/Modal.js";
import WidgetIcon from "./WidgetIcon.js";
export default {
components: {
BsModal
BsModal,
WidgetIcon,
},
props: [
"widgets"
@@ -28,11 +30,7 @@ export default {
this.callbacks = {};
this.$refs.modal.hide();
},
path(src) {
if (src[0] == '/')
return FHC_JS_DATA_STORAGE_OBJECT.app_root + src;
return src;
}
},
template: `<div class="dashboard-widget-picker">
<bs-modal ref="modal" class="fade" :dialog-class="{'modal-fullscreen-sm-down': 1, 'modal-xl': widgets && widgets.length > 0}" @hiddenBsModal="close">
@@ -43,13 +41,7 @@ export default {
No Widgets available
</div>
<div v-for="widget in widgets" :key="widget.widget_id" class="col-sm-6 col-md-4 col-lg-3 col-xl-2">
<div class="card h-100" @click="pick(widget.widget_id)">
<img class="card-img-top" :src="path(widget.setup.icon)" :alt="'pictogram for ' + (widget.setup.name || widget.widget_kurzbz)">
<div class="card-body">
<h5 class="card-title">{{ widget.setup.name || widget.widget_kurzbz }}</h5>
<p class="card-text">{{ widget.beschreibung }}</p>
</div>
</div>
<widget-icon @select="pick" :widget="widget" ></widget-icon>
</div>
</div>
<div v-else class="text-center"><i class="fa-solid fa-spinner fa-pulse fa-3x"></i></div>
@@ -0,0 +1,29 @@
export default {
data(){
return {
}
},
props:{
widget:{
type:Object,
required:true,
}
},
methods:{
path(src) {
if (src[0] == '/')
return FHC_JS_DATA_STORAGE_OBJECT.app_root + src;
return src;
}
},
emits:["select"],
template: /*html */`
<div class="card h-100" @click="$emit('select',widget.widget_id); pick(widget.widget_id)">
<img class="card-img-top" :src="path(widget.setup.icon)" :alt="'pictogram for ' + (widget.setup.name || widget.widget_kurzbz)">
<div class="card-body">
<h5 class="card-title">{{ widget.setup.name || widget.widget_kurzbz }}</h5>
<p class="card-text">{{ widget.beschreibung }}</p>
</div>
</div>`,
}
+39 -16
View File
@@ -2,6 +2,7 @@
import GridItem from './Grid/Item.js';
import GridLogic from '../../composables/GridLogic.js';
import WidgetIcon from '../Dashboard/Widget/WidgetIcon.js';
const MODE_IDLE = 0;
const MODE_MOVE = 1;
@@ -10,10 +11,10 @@ const MODE_RESIZE = 2;
export default {
name: 'Grid',
components: {
GridItem
},
inject: {
GridItem,
WidgetIcon,
},
inject: ["widgetsSetup"],
props: {
cols: Number,
items: Array,
@@ -50,6 +51,7 @@ export default {
fixedPositionUpdates: null,
draggedOffset: [0,0],
draggedItem: null,
draggedItemIcon: null,
additionalRow: null,
}
},
@@ -125,7 +127,15 @@ export default {
if (!this.active || !this.grid || this.mode != MODE_IDLE || this.x < 0 || this.y < 0 || this.x >= this.cols || this.y >= this.rows)
return false;
return this.grid.isFreeSlot(this.x, this.y);
}
},
widgetSetup(){
if (!this.widgetsSetup)
return;
return this.widgetsSetup.reduce((acc, ele) => {
acc[ele.widget_id] =ele;
return acc;
} ,{});
},
},
watch: {
active(active) {
@@ -157,6 +167,8 @@ export default {
},
methods: {
toggleDraggedItemOverlay(condition){
if(!this.draggedItem)
return;
let draggedItemNode = document.getElementById(this.draggedItem.data.id);
if(condition){
draggedItemNode.classList.add("dashboard-item-overlay");
@@ -165,13 +177,11 @@ export default {
}
},
dragging(event){
if(this.mode == MODE_MOVE)
if(this.mode == MODE_MOVE){
this.toggleDraggedItemOverlay(true);
/* use case: instead of showing the ghost widget when dragging,
change the x and y position of the widget when dragging to drag the whole widget
event.target.style.top = `${this.clientY}px`;
event.target.style.left = `${this.clientX}px`;
*/
this.draggedItemIcon.style.top = `${this.clientY}px`;
this.draggedItemIcon.style.left = `${this.clientX}px`;
}
},
createNewGrid(items) {
this.grid = new GridLogic(this.cols);
@@ -270,10 +280,9 @@ export default {
return true;
},
_dragStart(evt) {
_dragStart(evt, item) {
if (evt.dataTransfer) {
if(this.mode == MODE_RESIZE)
evt.dataTransfer.setDragImage(evt.target, -99999, -99999);
evt.dataTransfer.setDragImage(evt.target, -99999, -99999);
evt.dataTransfer.dropEffect = 'move';
evt.dataTransfer.effectAllowed = 'move';
}
@@ -282,10 +291,11 @@ export default {
if (!this.active)
return;
this._dragStart(evt);
this.mode = MODE_MOVE;
this.draggedItem = item;
this.draggedItemIcon = document.getElementById(`widget${this.draggedItem?.data.widget}`);
this.draggedOffset = [item.x - this.x, item.y - this.y];
this._dragStart(evt, item);
},
startResize(evt, item) {
if (!this.active)
@@ -334,14 +344,21 @@ export default {
}
},
dragCancel() {
this.toggleDraggedItemOverlay(false);
this.mode = MODE_IDLE;
this.positionUpdates = null;
this.draggedOffset = [0,0],
this.draggedItem = null;
if(this.draggedItemIcon){
this.draggedItemIcon.style.top = '-999px';
this.draggedItemIcon.style.left = '-999px';
this.draggedItemIcon = null;
}
},
dragEnd() {
if(this.mode == MODE_MOVE)
this.toggleDraggedItemOverlay(false);
if (this.mode == MODE_IDLE)
return;
if (!this.active || this.x < 0 || this.y < 0 || this.x >= this.cols)
@@ -388,6 +405,11 @@ export default {
@mousemove="updateCursorOnMouseMove"
@mouseleave="mouseLeave">
<TransitionGroup tag="div">
<template v-for="(item,index) in placedItems">
<div class="dragged-widget-icon" :id="'widget'+item.data.widget" >
<widget-icon v-if="widgetsSetup" :widget="widgetSetup[item.data.widget]"></widget-icon>
</div>
</template>
<grid-item
v-for="(item,index) in (mode == 0 && active ? placedItems_withPlaceholders : placedItems)"
:key="item.data.id"
@@ -397,6 +419,7 @@ export default {
@dragging="dragging"
@end-drag="dragCancel"
@drop-drag="dragEnd"
@touchEvent="updateCursorOnMouseMove"
class="position-absolute"
:active="active"
:style="{
+8 -7
View File
@@ -13,7 +13,8 @@ export default {
"dragging",
"endDrag",
"dropDrag",
"item"
"item",
"touchEvent"
],
data() {
return {
@@ -54,20 +55,20 @@ export default {
this.dragging = false;
this.$emit('dropDrag', evt);
},
test(evt) {
let dragAction = this.dragAction || evt.target.getAttribute('drag-action');
if (dragAction) {
this.dragging = true;
}
touchStart(event){
this.$emit('touchEvent', event);
this.registerDragAction(event);
this.tryDragStart(event, this.item);
}
},
template: `
<div class="drop-grid-item"
@mousedown="registerDragAction"
@touchstart.prevent="tryDragStart($event, item)"
@touchstart.prevent="touchStart"
@touchend="touchDragEnd"
@dragstart="tryDragStart($event, item)"
@drag="$emit('dragging',$event)"
@touchmove="$emit('dragging',$event)"
@dragend="$emit('endDrag', $event)"
:draggable="active && !item.placeholder && dragAction != ''">
<slot v-bind="item"></slot>