Geisterstunde

This commit is contained in:
Timo Reichl 2025-04-29 19:23:31 +02:00
parent fac7665f8f
commit 4af1e60f76
6 changed files with 107 additions and 25 deletions

View File

@ -4,10 +4,10 @@
:is="resolvedComponent"
v-bind="element.props"
@update-items="updateSchema"
@dragover.prevent="onDragOver($event)"
@dragover.prevent="onDragOver($event, index)"
@drop="onDrop($event, index)"
@dragleave="onDragLeave()"
:class="getDropClass"
:class="(getDropClass, isGhost)"
/>
</div>
</template>
@ -43,6 +43,7 @@ const FlexLayoutRenderer = defineAsyncComponent(() =>
"@/modules/process/components/formDesigner/renderer/FlexLayoutRenderer.vue"
)
);
import { useDragStore } from "@/modules/process/store/formBlockDragStatus";
import { InputTextBlock } from "@/modules/process/models/formDesigner/blocks/InputTextBlock";
import { ButtonBlock } from "@/modules/process/models/formDesigner/blocks/ButtonBlock";
@ -64,6 +65,7 @@ export default defineComponent({
data() {
return {
dropIndicator: null,
dragOver: false,
};
},
props: {
@ -108,11 +110,28 @@ export default defineComponent({
return "";
}
},
isGhost() {
return this.element.ghost ? "ghost" : "";
},
},
methods: {
updateSchema(newItems) {
this.$emit("update-schema", newItems);
},
addGhost(type, index) {
if (
this.dropIndicator?.position === "right" ||
this.dropIndicator?.position === "below"
) {
index = index + 1;
}
let newElement = this.createElementByType(type);
newElement.ghost = true;
this.dropIndicator = null;
if (newElement) {
this.$emit("insert-item", { index, newElement });
}
},
onDrop(event, index) {
event.stopPropagation();
event.preventDefault();
@ -129,27 +148,47 @@ export default defineComponent({
this.$emit("insert-item", { index, newElement });
}
},
onDragOver(event) {
event.stopPropagation();
const bounds = event.currentTarget.getBoundingClientRect();
const offsetX = event.clientX - bounds.left;
const offsetY = event.clientY - bounds.top;
async onDragOver(event, index) {
if (this.element.ghost) {
// do nothing
} else {
event.stopPropagation();
const bounds = event.currentTarget.getBoundingClientRect();
const offsetX = event.clientX - bounds.left;
const offsetY = event.clientY - bounds.top;
const isHorizontal = this.direction === "horizontal";
const isHorizontal = this.direction === "horizontal";
const position = isHorizontal
? offsetX < bounds.width / 2
? "left"
: "right"
: offsetY < bounds.height / 2
? "above"
: "below";
const position = isHorizontal
? offsetX < bounds.width / 2
? "left"
: "right"
: offsetY < bounds.height / 2
? "above"
: "below";
const curElement = this.element;
this.dropIndicator = {
type: curElement.type,
position,
};
const curElement = this.element;
if (
position !== this.dropIndicator?.position &&
this.dropIndicator?.position
) {
this.dragOver = false;
await this.$emit("remove-ghosts");
console.log("neue Position");
}
this.dropIndicator = {
type: curElement.type,
position,
};
if (this.dragOver === false) {
const dragStore = useDragStore();
this.addGhost(dragStore.currentDraggedType, index);
}
this.dragOver = true;
}
},
onDragLeave() {
if (
@ -158,6 +197,8 @@ export default defineComponent({
) {
this.dropIndicator = null;
}
this.dragOver = false;
this.$emit("remove-ghosts");
},
createElementByType(type) {
switch (type) {
@ -235,4 +276,8 @@ export default defineComponent({
bottom: 0;
width: 2px;
}
.component-container .ghost {
opacity: 0.3;
}
</style>

View File

@ -31,6 +31,7 @@
class="row ms-0 me-0 d-flex formItem align-items-center justify-content-center"
draggable="true"
@dragstart="startDrag($event, 'InputText')"
@dragend="endDrag"
>
<div
class="col d-flex justify-content-xxl-start align-items-xxl-center"
@ -59,6 +60,7 @@
class="row ms-0 me-0 d-flex formItem align-items-center justify-content-center"
draggable="true"
@dragstart="startDrag($event, 'InputNumber')"
@dragend="endDrag"
>
<div
class="col d-flex justify-content-xxl-start align-items-xxl-center"
@ -86,6 +88,7 @@
class="row ms-0 me-0 d-flex formItem align-items-center justify-content-center"
draggable="true"
@dragstart="startDrag($event, 'InputDate')"
@dragend="endDrag"
>
<div
class="col d-flex justify-content-xxl-start align-items-xxl-center"
@ -113,6 +116,7 @@
class="row ms-0 me-0 d-flex formItem align-items-center justify-content-center"
draggable="true"
@dragstart="startDrag($event, 'Button')"
@dragend="endDrag"
>
<div
class="col d-flex justify-content-xxl-start align-items-xxl-center"
@ -236,6 +240,7 @@
</template>
<script>
import { useDragStore } from "@/modules/process/store/formBlockDragStatus";
export default {
name: "FormDesignerToolsComponent",
// Verwendete Komponenten
@ -277,9 +282,15 @@ export default {
// Methoden
methods: {
startDrag(event, type) {
console.log("Dragging:", type);
const dragStore = useDragStore();
dragStore.setDraggedType(type);
event.dataTransfer.setData("text/plain", type); // Speichere das Objekt als JSON
},
endDrag() {
const dragStore = useDragStore();
dragStore.clearDraggedType();
this.$emit("remove-ghosts");
},
setActive(type) {
switch (type) {
case "element":

View File

@ -8,6 +8,7 @@
:direction="direction"
@update-schema="updateChildItems(key, $event)"
@insert-item="insertItem"
@remove-ghosts="removeGhosts"
/>
</div>
</template>
@ -52,6 +53,9 @@ export default defineComponent({
},
},
methods: {
removeGhosts() {
this.$emit("remove-ghosts");
},
updateChildItems(index, newItems) {
const updatedItems = [...this.items];
updatedItems[index] = { ...updatedItems[index], items: newItems };

View File

@ -144,11 +144,13 @@
<div class="tab-content" style="height: 100%">
<div id="tab-tree" class="tab-pane" role="tabpanel">
<FormDesignerTreeViewComponent
:nodes="formJSON"
:nodes="schema"
></FormDesignerTreeViewComponent>
</div>
<div id="tab-elements" class="tab-pane active" role="tabpanel">
<FormDesignerToolsComponent></FormDesignerToolsComponent>
<FormDesignerToolsComponent
@remove-ghosts="removeGhosts"
></FormDesignerToolsComponent>
</div>
</div>
</div>
@ -164,6 +166,7 @@
<FormRendererComponent
:schema="schema"
@update-schema="addElementToForm"
@remove-ghosts="removeGhosts"
/>
<slot name="middle"></slot>
</div>
@ -245,6 +248,9 @@ export default {
window.removeEventListener("resize", this.updateSizes);
},
methods: {
removeGhosts() {
this.schema = this.schema.filter((item) => item.ghost !== true);
},
updateSizes() {
this.middleWidth = window.innerWidth - this.leftWidth - this.rightWidth;
this.bottomHeight = window.innerHeight - this.topHeight;
@ -320,7 +326,7 @@ export default {
},
computed: {
jsonOutput() {
return JSON.stringify(this.formJSON, null, 2); // JSON sauber formatieren
return JSON.stringify(this.schema, null, 2); // JSON sauber formatieren
},
},
};

View File

@ -1,7 +1,8 @@
export class FormBlock {
constructor(type, props = {}) {
constructor(type, props = {}, ghost) {
this.type = type;
this.props = props;
this.ghost = ghost ?? false;
}
toJSON() {

View File

@ -0,0 +1,15 @@
import { defineStore } from "pinia";
export const useDragStore = defineStore("drag", {
state: () => ({
currentDraggedType: null,
}),
actions: {
setDraggedType(type) {
this.currentDraggedType = type;
},
clearDraggedType() {
this.currentDraggedType = null;
},
},
});