mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-01 20:29:29 +00:00
refactor(Dashboard Grid): updates replace logic when moving widgets and fixes position detection between mousemove and mousedown
This commit is contained in:
@@ -40,15 +40,30 @@ export default {
|
||||
"loading",
|
||||
"item_data",
|
||||
"place",
|
||||
"setup",
|
||||
],
|
||||
computed: {
|
||||
maxHeight(){
|
||||
return this.setup?.height?.max;
|
||||
},
|
||||
maxWidth(){
|
||||
if (Object.prototype.toString.call(this.setup?.width) == "[object Number]"){
|
||||
return this.setup?.width;
|
||||
}
|
||||
return this.setup?.width?.max;
|
||||
},
|
||||
minHeight() {
|
||||
return this.setup?.height?.min;
|
||||
},
|
||||
minWidth() {
|
||||
return this.setup?.width?.min;
|
||||
},
|
||||
isResizeable(){
|
||||
return this.maxWidth >1 || this.maxHeight >1;
|
||||
},
|
||||
isPinned(){
|
||||
return this.place?.pinned ? true : false;
|
||||
},
|
||||
isResizeable() {
|
||||
if (!this.widget) return false;
|
||||
return this.widget.setup.width.max || this.widget.setup.height.max;
|
||||
},
|
||||
ready() {
|
||||
return this.component && this.arguments !== null;
|
||||
},
|
||||
@@ -138,7 +153,7 @@ export default {
|
||||
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="!hidden || editMode" class="dashboard-item card overflow-hidden h-100 position-relative" :class="arguments && arguments.className ? arguments.className : ''">
|
||||
<div v-else-if="!hidden || editMode" :id="widgetID" class="dashboard-item card overflow-hidden h-100 position-relative" :class="arguments && arguments.className ? arguments.className : ''">
|
||||
<div v-if="widget" class="card-header d-flex ps-0 pe-2 align-items-center">
|
||||
<Transition>
|
||||
<span v-if="editMode && !isPinned" drag-action="move" class="col-auto mx-2 px-2 cursor-move"><i class="fa-solid fa-grip-vertical"></i></span>
|
||||
@@ -188,8 +203,22 @@ export default {
|
||||
</template>
|
||||
</bs-modal>
|
||||
<height-transition>
|
||||
<div v-if="editMode && isResizeable && !isPinned" class="card-footer d-flex justify-content-end p-0">
|
||||
<span drag-action="resize" class="col-auto px-1 cursor-nw-resize"><i class="fa-solid fa-up-right-and-down-left-from-center mirror-x"></i></span>
|
||||
<div v-if="editMode && isResizeable && !isPinned " class="card-footer d-flex justify-content-end p-0">
|
||||
<template v-if="maxWidth < 2">
|
||||
<span drag-action="resize" class="col-auto px-1 cursor-ns-resize">
|
||||
<i class="fa-solid fa-up-down pe-2"></i>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="maxHeight < 2">
|
||||
<span drag-action="resize" class="col-auto px-1 cursor-ew-resize">
|
||||
<i class="fa-solid fa-left-right pe-2"></i>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span drag-action="resize" class="col-auto px-1 cursor-nw-resize">
|
||||
<i class="fa-solid fa-up-right-and-down-left-from-center mirror-x"></i>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</height-transition>
|
||||
</div>`,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import BsConfirm from "../Bootstrap/Confirm.js";
|
||||
import SectionModal from "../Bootstrap/Alert.js";
|
||||
import DropGrid from '../Drop/Grid.js'
|
||||
import DashboardItem from "./Item.js";
|
||||
import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js";
|
||||
@@ -39,6 +40,7 @@ export default {
|
||||
configOpened: false,
|
||||
gridWidth: 1,
|
||||
gridHeight: null,
|
||||
draggedItem:null,
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
@@ -50,6 +52,13 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
computedWidgetsSetup(){
|
||||
if(!this.widgetsSetup) return {};
|
||||
return this.widgetsSetup.reduce((acc, setup)=>{
|
||||
acc[setup.widget_id] = setup.setup;
|
||||
return acc;
|
||||
},{})
|
||||
},
|
||||
editModeIsActive() {
|
||||
return (this.editMode || this.adminMode) && !this.configOpened
|
||||
},
|
||||
@@ -81,6 +90,9 @@ export default {
|
||||
|
||||
},
|
||||
methods: {
|
||||
showSectionInformation(){
|
||||
SectionModal.popup(`this is the information for the section ${name}`);
|
||||
},
|
||||
handleConfigOpened() {
|
||||
this.configOpened = true
|
||||
},
|
||||
@@ -173,12 +185,17 @@ export default {
|
||||
});
|
||||
},
|
||||
template: `
|
||||
<h4 v-if="items.length>0 && editMode" class=" mb-0">
|
||||
<i @click="showSectionInformation(name)" class="fa-solid fa-circle-info section-info" ></i>
|
||||
{{name}}:
|
||||
</h4>
|
||||
<div class="dashboard-section position-relative pb-3 border-bottom" ref="container" :style="getSectionStyle">
|
||||
<drop-grid v-model:cols="gridWidth" :items="items" :active="editModeIsActive" :resize-limit="checkResizeLimit" :margin-for-extra-row=".01" @rearrange-items="updatePositions" @gridHeight="gridHeight=$event" >
|
||||
<drop-grid v-model:cols="gridWidth" :items="items" :active="editModeIsActive" :resize-limit="checkResizeLimit" :margin-for-extra-row=".01" @draggedItem="draggedItem=$event" @rearrange-items="updatePositions" @gridHeight="gridHeight=$event" >
|
||||
<template #default="item">
|
||||
|
||||
<div v-if="item.placeholder" class="empty-tile-hover" @click="$emit('widgetAdd', name, { widget: 1, config: {}, place: {[gridWidth]: {x:item.x,y:item.y,w:1,h:1}}, custom: 1 })"></div>
|
||||
<div v-else-if="item.widgetid == draggedItem?.data.widgetid" class="draggedItem" ></div>
|
||||
<dashboard-item
|
||||
v-if="!item.placeholder"
|
||||
v-else
|
||||
:id="item.widget"
|
||||
:widgetID="item.id"
|
||||
:width="item.w"
|
||||
@@ -190,6 +207,7 @@ export default {
|
||||
:hidden="item.hidden"
|
||||
:editMode="editMode"
|
||||
:place="item.place[gridWidth]"
|
||||
:setup="computedWidgetsSetup[item.widget]"
|
||||
@change="saveConfig($event, item)"
|
||||
@remove="removeWidget(item, $event)"
|
||||
@config-opened="handleConfigOpened"
|
||||
@@ -197,7 +215,6 @@ export default {
|
||||
@pinItem="updatePositions($event,true)"
|
||||
@unPinItem="updatePositions">
|
||||
</dashboard-item>
|
||||
<div v-else class="empty-tile-hover" @click="$emit('widgetAdd', name, { widget: 1, config: {}, place: {[gridWidth]: {x:item.x,y:item.y,w:1,h:1}}, custom: 1 })"></div>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import GridLogic from '../../composables/GridLogic.js';
|
||||
const MODE_IDLE = 0;
|
||||
const MODE_MOVE = 1;
|
||||
const MODE_RESIZE = 2;
|
||||
const MODE_MOUSE_DOWN = 3;
|
||||
|
||||
export default {
|
||||
name: 'Grid',
|
||||
@@ -29,6 +30,7 @@ export default {
|
||||
"rearrangeItems",
|
||||
"newItem",
|
||||
"gridHeight",
|
||||
"draggedItem",
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
@@ -45,7 +47,6 @@ export default {
|
||||
draggedOffset: [0,0],
|
||||
draggedItem: null,
|
||||
draggedNode: null,
|
||||
draggedItemIcon: null,
|
||||
additionalRow: null,
|
||||
reorderedItems:[],
|
||||
clonedWidget:null,
|
||||
@@ -377,11 +378,13 @@ export default {
|
||||
return;
|
||||
|
||||
this.mode = MODE_MOVE;
|
||||
|
||||
this.draggedItem = item;
|
||||
this.$emit('draggedItem',item);
|
||||
this.draggedNode = evt.target;
|
||||
//clones the widget for the drag Image
|
||||
let clone = evt.target.cloneNode(true);
|
||||
clone.style.zIndex = 20;
|
||||
clone.style.zIndex = 5;
|
||||
clone.classList.add("widgetClone");
|
||||
this.$refs.container.appendChild(clone);
|
||||
this.clonedWidget = clone;
|
||||
@@ -400,6 +403,7 @@ export default {
|
||||
if (!this.active)
|
||||
return this.dragCancel();
|
||||
|
||||
this.checkPinnedWidgetAnimation();
|
||||
if (this.updateCursor(evt)) {
|
||||
switch(this.mode) {
|
||||
case MODE_MOVE: {
|
||||
@@ -442,14 +446,9 @@ export default {
|
||||
this.positionUpdates = null;
|
||||
this.draggedOffset = [0,0],
|
||||
this.draggedItem = null;
|
||||
this.$emit('draggedItem',null);
|
||||
this.draggedNode = null;
|
||||
|
||||
if(this.draggedItemIcon){
|
||||
this.draggedItemIcon.style.top = '-999px';
|
||||
this.draggedItemIcon.style.left = '-999px';
|
||||
this.draggedItemIcon = null;
|
||||
}
|
||||
|
||||
},
|
||||
dragEnd() {
|
||||
let widgetClones = document.getElementsByClassName("widgetClone");
|
||||
@@ -486,11 +485,33 @@ export default {
|
||||
this.$emit('newItem', this.x, this.y);
|
||||
},
|
||||
updateCursorOnMouseMove(evt){
|
||||
if(this.mode == MODE_IDLE)
|
||||
if(this.mode == MODE_IDLE){
|
||||
this.updateCursor(evt);
|
||||
}
|
||||
}
|
||||
},
|
||||
checkPinnedWidgetAnimation(){
|
||||
let itemAtPosition = this.items.filter(item => item.x == this.x && item.y == this.y).pop();
|
||||
if (itemAtPosition && itemAtPosition.place[this.cols] && itemAtPosition.place[this.cols].pinned) {
|
||||
let pinnedWidget = document.getElementById(itemAtPosition.widgetid);
|
||||
let test = pinnedWidget.querySelector("[pinned='true']");
|
||||
if (!test.classList.contains("denied-dragging-animation")) {
|
||||
test.classList.add("denied-dragging-animation");
|
||||
}
|
||||
} else {
|
||||
Array.from(document.getElementsByClassName("denied-dragging-animation"))?.forEach(ele => {
|
||||
ele.classList.remove("denied-dragging-animation");
|
||||
})
|
||||
}
|
||||
},
|
||||
mouseDown(){
|
||||
this.mode = MODE_MOUSE_DOWN;
|
||||
},
|
||||
mouseUp() {
|
||||
this.mode = MODE_IDLE;
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<p style="position:fixed; top:200px; left:300px; z-index:200;">{{x}}-{{y}}</p>
|
||||
<div
|
||||
ref="container"
|
||||
class="drop-grid position-relative h-0"
|
||||
@@ -503,10 +524,13 @@ export default {
|
||||
@mouseleave="mouseLeave">
|
||||
<TransitionGroup tag="div">
|
||||
<grid-item
|
||||
ref="gridItems"
|
||||
v-for="(item,index) in (mode == 0 && active ? placedItems_withPlaceholders : placedItems)"
|
||||
:key="item.data.id"
|
||||
:item="item"
|
||||
@start-move="startMove"
|
||||
@mouse-down="mouseDown"
|
||||
@mouse-up="mouseUp"
|
||||
@start-resize="startResize"
|
||||
@dragging="dragging"
|
||||
@end-drag="dragCancel"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export default {
|
||||
name:'GridItem',
|
||||
components: {
|
||||
},
|
||||
inject: {
|
||||
@@ -8,6 +9,8 @@ export default {
|
||||
active: Boolean
|
||||
},
|
||||
emits: [
|
||||
"mouseDown",
|
||||
"mouseUp",
|
||||
"startMove",
|
||||
"startResize",
|
||||
"dragging",
|
||||
@@ -26,6 +29,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
registerDragAction(evt) {
|
||||
this.$emit('mouseDown', evt);
|
||||
if (evt.target.hasAttribute('drag-action')) {
|
||||
this.dragAction = evt.target.getAttribute('drag-action');
|
||||
} else {
|
||||
@@ -64,6 +68,7 @@ export default {
|
||||
template: `
|
||||
<div class="drop-grid-item"
|
||||
@mousedown="registerDragAction"
|
||||
@mouseup="$emit('mouseUp', $event)"
|
||||
@touchstart.prevent="touchStart"
|
||||
@touchend="touchDragEnd"
|
||||
@dragstart="tryDragStart($event, item)"
|
||||
|
||||
@@ -96,7 +96,7 @@ class GridLogic {
|
||||
}
|
||||
}
|
||||
move(item, x, y) {
|
||||
if (item.data.place[this.w].pinned)
|
||||
if (item.data.place[this.w]?.pinned)
|
||||
return [];
|
||||
if (item.x == x && item.y == y)
|
||||
return [];
|
||||
@@ -116,6 +116,8 @@ class GridLogic {
|
||||
prefer = DIR_RIGHT;
|
||||
}
|
||||
|
||||
const originalFrame = [...item.frame];
|
||||
|
||||
const currItem = {...item};
|
||||
currItem.x = x;
|
||||
currItem.y = y;
|
||||
@@ -123,27 +125,27 @@ class GridLogic {
|
||||
let occupiers = this.getItemsInFrame(currItem.frame);
|
||||
|
||||
// does not update if the target conatins pinned widgets
|
||||
if (occupiers.some(frame => this.data[frame]?.data.place[this.w].pinned)) {
|
||||
if (occupiers.some(frame => this.data[frame]?.data.place[this.w]?.pinned)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// checks if target contains widget with the same high and width
|
||||
let replace = occupiers
|
||||
.map(occupier => this.data[occupier])
|
||||
.filter( occupier => {
|
||||
return occupier.data.w == item.w && occupier.data.h == item.h
|
||||
});
|
||||
|
||||
// replaces positions of widget and target widget if they have same height and width
|
||||
if(replace.length > 0){
|
||||
let replaceUpdate =[];
|
||||
replaceUpdate[replace[0].index] = { index: replace[0].index, x:item.x, y:item.y };
|
||||
replaceUpdate[item.index] = { index: item.index, x: replace[0].x, y: replace[0].y};
|
||||
//update Grid and dataGrid
|
||||
replace[0].frame.forEach(f => this.grid[f] = item.index)
|
||||
item.frame.forEach(f => this.grid[f] = replace[0].index);
|
||||
this.data[replace[0].index] = item;
|
||||
this.data[item.index] = replace[0];
|
||||
let occupiersData = occupiers.map(occupier => this.data[occupier]);
|
||||
let occupiersFrame = occupiersData.map(occupier => occupier.frame).flat();
|
||||
if (!occupiersFrame.some(frame => !currItem.frame.includes(frame)) && !occupiersFrame.some(frame => originalFrame.includes(frame))){
|
||||
let replaceUpdate = [];
|
||||
let newOccupierFrames = [];
|
||||
for(let f of originalFrame){
|
||||
if(newOccupierFrames.includes(f)){
|
||||
continue;
|
||||
}
|
||||
let occ = occupiersData.shift();
|
||||
if(occ){
|
||||
newOccupierFrames = [...newOccupierFrames, ...this.getItemFrame({ ...occ, ...this.getSingleFramePosition(f) })];
|
||||
replaceUpdate[occ.index] = { index: occ.index, ...this.getSingleFramePosition(f)}
|
||||
}
|
||||
}
|
||||
replaceUpdate[item.index] = { index: item.index, x, y };
|
||||
return replaceUpdate;
|
||||
}
|
||||
|
||||
@@ -257,6 +259,9 @@ class GridLogic {
|
||||
frame.push(i + item.x + (j + item.y) * this.w);
|
||||
return frame;
|
||||
}
|
||||
getSingleFramePosition(frame){
|
||||
return { x: frame % this.w, y: Math.floor(frame / this.w)};
|
||||
}
|
||||
debug() {
|
||||
return this.grid;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user