最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - Drag-and-Drop Form Customizer: Duplicate and Empty Fields Issue When Reordering - Stack Overflow

programmeradmin4浏览0评论

I am working on a drag-and-drop form customizer where users can add, reorder, and remove fields using Alpine.js and Sortable.js. However, I am encountering two issues that I cannot seem to fix:

  1. Duplicate Fields: When a user reorders the fields, duplicate fields are created in the list. For example, if I reorder an already existing field (like "First Name"), it is added again in the list.

  2. Empty Fields: When reordering a field (e.g., moving "Mobile" to a new position), an empty field appears in the list.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Drag and Drop Form Customizer</title>
    <script src="/@tailwindcss/browser@4"></script>
    <link href="output.css" rel="stylesheet">
    <script defer src="/[email protected]/dist/cdn.min.js"></script>
    <script defer src="/[email protected]/Sortable.min.js"></script>
    <style>
        .sortable-chosen { background: #f0f0f0; }
        .sortable-ghost { opacity: 0.5; }
    </style>
</head>
<body>
    <div x-data="customizer" class="grid grid-cols-1 gap-4 p-4 lg:grid-cols-3">
        <!-- Available Fields -->
        <div x-show="!isPreview">
            <div>
                <p class="py-4 font-bold">Header</p>
                <ul class="grid grid-cols-1 gap-4 p-4 text-sm font-semibold bg-gray-100 rounded-md lg:grid-cols-2">
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Company Logo</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Company Name</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Address</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Mobile No.</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Email</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>GST Number</p>
                    </li>
                </ul>
            </div>
            <div>
                <p class="py-4 font-bold">Available Fields</p>
                <div id="availableFields" class="grid grid-cols-2 gap-2 p-4 pt-4 bg-gray-100 rounded-md"
                    x-ref="availableFields"
                    @drop="dropField($event, 'available')"
                    @dragover.prevent
                    @dragenter.prevent>
                    <template x-for="field in availableFields" :key="field">
                        <button class="w-full px-2 py-1 text-sm font-semibold bg-white border cursor-grab"
                            draggable="true"
                            @dragstart="dragField($event, field)"
                            x-text="field"></button>
                    </template>
                </div>
            </div>           
        </div>

        <!-- Customize Sections -->
        <div class="col-span-2">
            <div class="flex items-center w-full gap-4 px-4 py-2 bg-gray-100">
                <p class="font-bold">Customize</p>
                <button class="text-blue-600" @click="togglePreview()" x-text="isPreview ? 'Close Preview' : 'Preview'"></button>
            </div>

            <!-- Sections -->
            <template x-for="(section, index) in sections" :key="index">
                <div :class="isPreview ? 'bg-transparent' : 'border p-4 mt-4 bg-white shadow-md'">
                    <div class="flex items-center justify-between">
                        <div class="flex items-center gap-4">
                            <template x-if="!section.isEditing">
                                <p class="font-bold" x-text="section.name"></p>
                            </template>
                            <template x-if="section.isEditing">
                                <input type="text" class="px-2 py-1 border" x-model="section.name">
                            </template>
                            <button class="text-blue-600" x-show="!isPreview" @click="toggleEdit(index)">
                                <span x-show="!section.isEditing">Edit</span>
                                <span x-show="section.isEditing">✔</span>
                            </button>
                        </div>
                        <div class="flex items-center gap-4" x-show="!isPreview">
                            <button class="text-blue-600" @click="toggleLayout(index)">Single</button>
                            <button class="text-blue-600" @click="toggleLayout(index, true)">Double</button>
                            <button class="text-red-600" @click="removeSection(index)">Delete</button>
                        </div>
                    </div>

                    <!-- Droppable Area -->
                    <div :class="[
        section.layout === 'double' ? 'grid grid-cols-2 gap-2' : 'grid grid-cols-1 gap-2',
        section.isDraggingOver ? 'bg-green-100 border-2 border-green-500 border-dotted p-4' : 'border bg-gray-50'
    ]"
    class="mt-2 p-2 min-h-[50px]"
    @dragover.prevent="setDraggingOver(index, true)"
    @dragenter.prevent="setDraggingOver(index, true)"
    @dragleave="setDraggingOver(index, false)"
    @drop="dropField($event, index)"
    x-ref="sortableSection"
    x-init="Sortable.create($refs.sortableSection, {
        animation: 150,
        onEnd: (evt) => {
            if (evt.oldIndex !== evt.newIndex) {
                let movedField = sections[index].fields.splice(evt.oldIndex, 1)[0];
                sections[index].fields.splice(evt.newIndex, 0, movedField);
            }
        }
    })">
    <template x-for="(field, fieldIndex) in section.fields" :key="field">
        <div class="flex items-center justify-between p-2 bg-white border cursor-grab"
            :class="isPreview ? 'border-none' : 'border'"
            draggable="true">
            <p x-text="field"></p>
            <button class="text-red-600" @click="!isPreview && removeField(index, field)" x-show="!isPreview">✖</button>
        </div>
    </template>
</div>

                </div>
            </template>

            <!-- Add Section Button -->
            <div class="mt-4" x-show="!isPreview">
                <button @click="addSection()" class="px-4 py-2 text-xs font-semibold text-blue-600 bg-blue-100 border-blue-600 rounded-md">Add Section</button>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener("alpine:init", () => {
            Alpine.data("customizer", () => ({
                availableFields: [
                    "Employee ID", "First Name", "Last Name", "Email", "Mobile", "Date of Birth", "Gender", "Address",
                    "City", "State", "Postal Code", "Country", "Department", "Designation", "Joining Date", "Salary",
                    "Employment Type", "Work Shift", "Reporting Manager", "Status"
                ],
                originalOrder: [
                    "Employee ID", "First Name", "Last Name", "Email", "Mobile", "Date of Birth", "Gender", "Address",
                    "City", "State", "Postal Code", "Country", "Department", "Designation", "Joining Date", "Salary",
                    "Employment Type", "Work Shift", "Reporting Manager", "Status"
                ],
                sections: [],
                isPreview: false,
                togglePreview() {
                    this.isPreview = !this.isPreview;
                },
                toggleEdit(index) {
                    this.sections[index].isEditing = !this.sections[index].isEditing;
                },
                addSection() {
                    this.sections.push({
                        name: `Section ${this.sections.length + 1}`,
                        isEditing: false,
                        layout: "single",
                        fields: [],
                        isDraggingOver: false
                    });
                },
                removeSection(index) {
                    this.sections[index].fields.forEach(field => this.restoreFieldOrder(field));
                    this.sections.splice(index, 1);
                },
                removeField(sectionIndex, field) {
                    this.restoreFieldOrder(field);
                    this.sections[sectionIndex].fields = this.sections[sectionIndex].fields.filter(f => f !== field);
                },
                toggleLayout(index, double = false) {
                    this.sections[index].layout = double ? "double" : "single";
                },
                dragField(event, field) {
                    event.dataTransfer.setData("text/plain", field);
                },
                dropField(event, target) {
                    if (this.isPreview) return;
                    const field = event.dataTransfer.getData("text/plain");
                    if (target === "available") {
                        if (!this.availableFields.includes(field)) {
                            this.restoreFieldOrder(field);
                        }
                    } else {
                        const section = this.sections[target];
                        if (!section.fields.includes(field)) {
                            section.fields.push(field);
                        }
                        this.availableFields = this.availableFields.filter(f => f !== field);
                    }
                    this.setDraggingOver(target, false);
                },
                setDraggingOver(index, status) {
                    this.sections[index].isDraggingOver = status;
                },
                restoreFieldOrder(field) {
                    this.availableFields = [...new Set([...this.originalOrder.filter(f => !this.availableFields.includes(f)), ...this.availableFields])];
                }
            }));
        });
    </script>
</body>

</html>

I am working on a drag-and-drop form customizer where users can add, reorder, and remove fields using Alpine.js and Sortable.js. However, I am encountering two issues that I cannot seem to fix:

  1. Duplicate Fields: When a user reorders the fields, duplicate fields are created in the list. For example, if I reorder an already existing field (like "First Name"), it is added again in the list.

  2. Empty Fields: When reordering a field (e.g., moving "Mobile" to a new position), an empty field appears in the list.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Drag and Drop Form Customizer</title>
    <script src="https://unpkg/@tailwindcss/browser@4"></script>
    <link href="output.css" rel="stylesheet">
    <script defer src="https://cdn.jsdelivr/npm/[email protected]/dist/cdn.min.js"></script>
    <script defer src="https://cdn.jsdelivr/npm/[email protected]/Sortable.min.js"></script>
    <style>
        .sortable-chosen { background: #f0f0f0; }
        .sortable-ghost { opacity: 0.5; }
    </style>
</head>
<body>
    <div x-data="customizer" class="grid grid-cols-1 gap-4 p-4 lg:grid-cols-3">
        <!-- Available Fields -->
        <div x-show="!isPreview">
            <div>
                <p class="py-4 font-bold">Header</p>
                <ul class="grid grid-cols-1 gap-4 p-4 text-sm font-semibold bg-gray-100 rounded-md lg:grid-cols-2">
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Company Logo</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Company Name</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Address</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Mobile No.</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Email</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>GST Number</p>
                    </li>
                </ul>
            </div>
            <div>
                <p class="py-4 font-bold">Available Fields</p>
                <div id="availableFields" class="grid grid-cols-2 gap-2 p-4 pt-4 bg-gray-100 rounded-md"
                    x-ref="availableFields"
                    @drop="dropField($event, 'available')"
                    @dragover.prevent
                    @dragenter.prevent>
                    <template x-for="field in availableFields" :key="field">
                        <button class="w-full px-2 py-1 text-sm font-semibold bg-white border cursor-grab"
                            draggable="true"
                            @dragstart="dragField($event, field)"
                            x-text="field"></button>
                    </template>
                </div>
            </div>           
        </div>

        <!-- Customize Sections -->
        <div class="col-span-2">
            <div class="flex items-center w-full gap-4 px-4 py-2 bg-gray-100">
                <p class="font-bold">Customize</p>
                <button class="text-blue-600" @click="togglePreview()" x-text="isPreview ? 'Close Preview' : 'Preview'"></button>
            </div>

            <!-- Sections -->
            <template x-for="(section, index) in sections" :key="index">
                <div :class="isPreview ? 'bg-transparent' : 'border p-4 mt-4 bg-white shadow-md'">
                    <div class="flex items-center justify-between">
                        <div class="flex items-center gap-4">
                            <template x-if="!section.isEditing">
                                <p class="font-bold" x-text="section.name"></p>
                            </template>
                            <template x-if="section.isEditing">
                                <input type="text" class="px-2 py-1 border" x-model="section.name">
                            </template>
                            <button class="text-blue-600" x-show="!isPreview" @click="toggleEdit(index)">
                                <span x-show="!section.isEditing">Edit</span>
                                <span x-show="section.isEditing">✔</span>
                            </button>
                        </div>
                        <div class="flex items-center gap-4" x-show="!isPreview">
                            <button class="text-blue-600" @click="toggleLayout(index)">Single</button>
                            <button class="text-blue-600" @click="toggleLayout(index, true)">Double</button>
                            <button class="text-red-600" @click="removeSection(index)">Delete</button>
                        </div>
                    </div>

                    <!-- Droppable Area -->
                    <div :class="[
        section.layout === 'double' ? 'grid grid-cols-2 gap-2' : 'grid grid-cols-1 gap-2',
        section.isDraggingOver ? 'bg-green-100 border-2 border-green-500 border-dotted p-4' : 'border bg-gray-50'
    ]"
    class="mt-2 p-2 min-h-[50px]"
    @dragover.prevent="setDraggingOver(index, true)"
    @dragenter.prevent="setDraggingOver(index, true)"
    @dragleave="setDraggingOver(index, false)"
    @drop="dropField($event, index)"
    x-ref="sortableSection"
    x-init="Sortable.create($refs.sortableSection, {
        animation: 150,
        onEnd: (evt) => {
            if (evt.oldIndex !== evt.newIndex) {
                let movedField = sections[index].fields.splice(evt.oldIndex, 1)[0];
                sections[index].fields.splice(evt.newIndex, 0, movedField);
            }
        }
    })">
    <template x-for="(field, fieldIndex) in section.fields" :key="field">
        <div class="flex items-center justify-between p-2 bg-white border cursor-grab"
            :class="isPreview ? 'border-none' : 'border'"
            draggable="true">
            <p x-text="field"></p>
            <button class="text-red-600" @click="!isPreview && removeField(index, field)" x-show="!isPreview">✖</button>
        </div>
    </template>
</div>

                </div>
            </template>

            <!-- Add Section Button -->
            <div class="mt-4" x-show="!isPreview">
                <button @click="addSection()" class="px-4 py-2 text-xs font-semibold text-blue-600 bg-blue-100 border-blue-600 rounded-md">Add Section</button>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener("alpine:init", () => {
            Alpine.data("customizer", () => ({
                availableFields: [
                    "Employee ID", "First Name", "Last Name", "Email", "Mobile", "Date of Birth", "Gender", "Address",
                    "City", "State", "Postal Code", "Country", "Department", "Designation", "Joining Date", "Salary",
                    "Employment Type", "Work Shift", "Reporting Manager", "Status"
                ],
                originalOrder: [
                    "Employee ID", "First Name", "Last Name", "Email", "Mobile", "Date of Birth", "Gender", "Address",
                    "City", "State", "Postal Code", "Country", "Department", "Designation", "Joining Date", "Salary",
                    "Employment Type", "Work Shift", "Reporting Manager", "Status"
                ],
                sections: [],
                isPreview: false,
                togglePreview() {
                    this.isPreview = !this.isPreview;
                },
                toggleEdit(index) {
                    this.sections[index].isEditing = !this.sections[index].isEditing;
                },
                addSection() {
                    this.sections.push({
                        name: `Section ${this.sections.length + 1}`,
                        isEditing: false,
                        layout: "single",
                        fields: [],
                        isDraggingOver: false
                    });
                },
                removeSection(index) {
                    this.sections[index].fields.forEach(field => this.restoreFieldOrder(field));
                    this.sections.splice(index, 1);
                },
                removeField(sectionIndex, field) {
                    this.restoreFieldOrder(field);
                    this.sections[sectionIndex].fields = this.sections[sectionIndex].fields.filter(f => f !== field);
                },
                toggleLayout(index, double = false) {
                    this.sections[index].layout = double ? "double" : "single";
                },
                dragField(event, field) {
                    event.dataTransfer.setData("text/plain", field);
                },
                dropField(event, target) {
                    if (this.isPreview) return;
                    const field = event.dataTransfer.getData("text/plain");
                    if (target === "available") {
                        if (!this.availableFields.includes(field)) {
                            this.restoreFieldOrder(field);
                        }
                    } else {
                        const section = this.sections[target];
                        if (!section.fields.includes(field)) {
                            section.fields.push(field);
                        }
                        this.availableFields = this.availableFields.filter(f => f !== field);
                    }
                    this.setDraggingOver(target, false);
                },
                setDraggingOver(index, status) {
                    this.sections[index].isDraggingOver = status;
                },
                restoreFieldOrder(field) {
                    this.availableFields = [...new Set([...this.originalOrder.filter(f => !this.availableFields.includes(f)), ...this.availableFields])];
                }
            }));
        });
    </script>
</body>

</html>

Share Improve this question edited Feb 17 at 10:07 rozsazoltan 8,2555 gold badges17 silver badges38 bronze badges asked Feb 17 at 8:53 R. ElavarasanR. Elavarasan 153 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

What i found out is that it takes the text and from that it creates a new one this is than including the cross.

what i changed is when retrieving an plain text have an regex execute which is the following:

    let field = event.dataTransfer.getData("text/plain").trim();
    field = field.replace(/\s+\W+/g, '');

Now what it tries to input is the same as already exists.
Note:
The filter function on where it needs to be sorted does not work and did not work in your example i did NOT fix this for you since it is not what was asked + it would take extra time.

full example preview with in my oppinion some best practice changes:

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Drag and Drop Form Customizer</title>
    <script src="https://unpkg/@tailwindcss/browser@4"></script>
    <link href="output.css" rel="stylesheet">
    <script defer src="https://cdn.jsdelivr/npm/[email protected]/dist/cdn.min.js"></script>
    <script defer src="https://cdn.jsdelivr/npm/[email protected]/Sortable.min.js"></script>
    <style>
        .sortable-chosen { background: #f0f0f0; }
        .sortable-ghost { opacity: 0.5; }
    </style>
</head>
<body>
    <div x-data="customizer" class="grid grid-cols-1 gap-4 p-4 lg:grid-cols-3">
        <!-- Available Fields -->
        <div x-show="!isPreview">
            <div>
                <p class="py-4 font-bold">Header</p>
                <ul class="grid grid-cols-1 gap-4 p-4 text-sm font-semibold bg-gray-100 rounded-md lg:grid-cols-2">
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Company Logo</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Company Name</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Address</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Mobile No.</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>Email</p>
                    </li>
                    <li class="flex items-center gap-2">
                        <input class="w-4 h-4" type="checkbox">
                        <p>GST Number</p>
                    </li>
                </ul>
            </div>
            <div>
                <p class="py-4 font-bold">Available Fields</p>
                <div id="availableFields" class="grid grid-cols-2 gap-2 p-4 pt-4 bg-gray-100 rounded-md"
                    x-ref="availableFields"
                    @drop="dropField($event, 'available')"
                    @dragover.prevent
                    @dragenter.prevent>
                    <template x-for="field in availableFields" :key="field">
                        <button class="w-full px-2 py-1 text-sm font-semibold bg-white border cursor-grab"
                            draggable="true"
                            @dragstart="dragField($event, field)"
                            x-text="field"></button>
                    </template>
                </div>
            </div>           
        </div>

        <!-- Customize Sections -->
        <div class="col-span-2">
            <div class="flex items-center w-full gap-4 px-4 py-2 bg-gray-100">
                <p class="font-bold">Customize</p>
                <button class="text-blue-600" @click="togglePreview()" x-text="isPreview ? 'Close Preview' : 'Preview'"></button>
            </div>

            <!-- Sections -->
            <template x-for="(section, index) in sections" :key="index">
                <div :class="isPreview ? 'bg-transparent' : 'border p-4 mt-4 bg-white shadow-md'">
                    <div class="flex items-center justify-between">
                        <div class="flex items-center gap-4">
                            <template x-if="!section.isEditing">
                                <p class="font-bold" x-text="section.name"></p>
                            </template>
                            <template x-if="section.isEditing">
                                <input type="text" class="px-2 py-1 border" x-model="section.name">
                            </template>
                            <button class="text-blue-600" x-show="!isPreview" @click="toggleEdit(index)">
                                <span x-show="!section.isEditing">Edit</span>
                                <span x-show="section.isEditing">✔</span>
                            </button>
                        </div>
                        <div class="flex items-center gap-4" x-show="!isPreview">
                            <button class="text-blue-600" @click="toggleLayout(index)">Single</button>
                            <button class="text-blue-600" @click="toggleLayout(index, true)">Double</button>
                            <button class="text-red-600" @click="removeSection(index)">Delete</button>
                        </div>
                    </div>

                    <div :class="[
        section.layout === 'double' ? 'grid grid-cols-2 gap-2' : 'grid grid-cols-1 gap-2',
        section.isDraggingOver ? 'bg-green-100 border-2 border-green-500 border-dotted p-4' : 'border bg-gray-50'
    ]"
    class="mt-2 p-2 min-h-[50px]"
    @dragover.prevent="setDraggingOver(index, true)"
    @dragenter.prevent="setDraggingOver(index, true)"
    @dragleave="setDraggingOver(index, false)"
    @drop="dropField($event, index)"
    x-ref="sortableSection"
    x-init="Sortable.create($refs.sortableSection, {
        animation: 150,
        onEnd: (evt) => {
            if (evt.oldIndex !== evt.newIndex) {
                let movedField = sections[index].fields.splice(evt.oldIndex, 1)[0];
                sections[index].fields.splice(evt.newIndex, 0, movedField);
            }
        }
    })">
    <template x-for="(field, fieldIndex) in section.fields" :key="field">
        <div class="flex items-center justify-between p-2 bg-white border cursor-grab"
            :class="isPreview ? 'border-none' : 'border'"
            draggable="true">
            <p x-text="field"></p>
            <button class="text-red-600" @click="!isPreview && removeField(index, field)" x-show="!isPreview">✖</button>
        </div>
    </template>
</div>

                </div>
            </template>

            <!-- Add Section Button -->
            <div class="mt-4" x-show="!isPreview">
                <button @click="addSection()" class="px-4 py-2 text-xs font-semibold text-blue-600 bg-blue-100 border-blue-600 rounded-md">Add Section</button>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener("alpine:init", () => {
            Alpine.data("customizer", () => ({
                availableFields: [
                    "Employee ID", "First Name", "Last Name", "Email", "Mobile", "Date of Birth", "Gender", "Address",
                    "City", "State", "Postal Code", "Country", "Department", "Designation", "Joining Date", "Salary",
                    "Employment Type", "Work Shift", "Reporting Manager", "Status"
                ],
                originalOrder: [
                    "Employee ID", "First Name", "Last Name", "Email", "Mobile", "Date of Birth", "Gender", "Address",
                    "City", "State", "Postal Code", "Country", "Department", "Designation", "Joining Date", "Salary",
                    "Employment Type", "Work Shift", "Reporting Manager", "Status"
                ],
                sections: [],
                isPreview: false,
                togglePreview() {
                    this.isPreview = !this.isPreview;
                },
                toggleEdit(index) {
                    this.sections[index].isEditing = !this.sections[index].isEditing;
                },
                addSection() {
                    this.sections.push({
                        name: `Section ${this.sections.length + 1}`,
                        isEditing: false,
                        layout: "single",
                        fields: [],
                        isDraggingOver: false
                    });
                },
                removeSection(index) {
                    this.sections[index].fields.forEach(field => this.restoreFieldOrder(field));
                    this.sections.splice(index, 1);
                },
                removeField(sectionIndex, field) {
                    this.restoreFieldOrder(field);
                    this.sections[sectionIndex].fields = this.sections[sectionIndex].fields.filter(f => f !== field);
                },
                toggleLayout(index, double = false) {
                    this.sections[index].layout = double ? "double" : "single";
                },
                                dragField(event, field) {
                                        event.dataTransfer.setData("text/plain", field.trim());
                                },
                dropField(event, target) {
                                    if (this.isPreview) return;
                                    let field = event.dataTransfer.getData("text/plain").trim();
                                    field = field.replace(/\s+\W+/g, '');
                                    if (target === "available") {
                                        if (!this.availableFields.includes(field)) {
                                            this.restoreFieldOrder(field);
                                        }
                                    } else {
                                        const section = this.sections[target];
                                        if (!section.fields.includes(field)) { // Voorkomt duplicaten
                                            section.fields.push(field);
                                        }
                                        // this.availableFields = this.availableFields.filter(f => f !== field);
                                    }
                                    this.setDraggingOver(target, false);
                                },
                setDraggingOver(index, status) {
                    this.sections[index].isDraggingOver = status;
                },
                restoreFieldOrder(field) {
                    this.availableFields = [...new Set([...this.originalOrder.filter(f => !this.availableFields.includes(f)), ...this.availableFields])];
                }
            }));
        });
    </script>
</body>

发布评论

评论列表(0)

  1. 暂无评论