improve drag'n'drop in the builder

This commit is contained in:
Alex Turchyn
2025-02-17 19:47:46 +02:00
committed by Pete Matsyburka
parent 2573d8cb19
commit 7a4629bf79
6 changed files with 307 additions and 80 deletions
+103 -57
View File
@@ -1,9 +1,18 @@
<template>
<div
ref="dragContainer"
style="max-width: 1600px"
class="mx-auto pl-3 h-full"
:class="isMobile ? 'pl-4' : 'md:pl-4'"
@dragover="onDragover"
>
<DragPlaceholder
ref="dragPlaceholder"
:field="fieldsDragFieldRef.value || toRaw(dragField)"
:is-field="template.fields.includes(fieldsDragFieldRef.value)"
:is-default="defaultFields.includes(toRaw(dragField))"
:is-required="defaultRequiredFields.includes(toRaw(dragField))"
/>
<div
v-if="pendingFieldAttachmentUuids.length && editable"
class="top-1.5 sticky h-0 z-20 max-w-2xl mx-auto"
@@ -269,6 +278,7 @@
:allow-draw="!onlyDefinedFields || drawField"
:data-document-uuid="document.uuid"
:default-submitters="defaultSubmitters"
:drag-field-placeholder="fieldsDragFieldRef.value || dragField"
:with-field-placeholder="withFieldPlaceholder"
:draw-field="drawField"
:draw-field-type="drawFieldType"
@@ -378,8 +388,9 @@
@set-draw="[drawField = $event.field, drawOption = $event.option]"
@set-draw-type="[drawFieldType = $event, showDrawField = true]"
@set-drag="dragField = $event"
@set-drag-placeholder="$refs.dragPlaceholder.dragPlaceholder = $event"
@change-submitter="selectedSubmitter = $event"
@drag-end="dragField = null"
@drag-end="[dragField = null, $refs.dragPlaceholder.dragPlaceholder = null]"
@scroll-to-area="scrollToArea"
/>
</div>
@@ -418,6 +429,7 @@
<script>
import Upload from './upload'
import Dropzone from './dropzone'
import DragPlaceholder from './drag_placeholder'
import Fields from './fields'
import MobileDrawField from './mobile_draw_field'
import Document from './document'
@@ -429,13 +441,14 @@ import MobileFields from './mobile_fields'
import FieldSubmitter from './field_submitter'
import { IconPlus, IconUsersPlus, IconDeviceFloppy, IconChevronDown, IconEye, IconWritingSign, IconInnerShadowTop, IconInfoCircle, IconAdjustments } from '@tabler/icons-vue'
import { v4 } from 'uuid'
import { ref, computed } from 'vue'
import { ref, computed, toRaw } from 'vue'
import * as i18n from './i18n'
export default {
name: 'TemplateBuilder',
components: {
Upload,
DragPlaceholder,
Document,
Fields,
IconInfoCircle,
@@ -460,6 +473,7 @@ export default {
template: this.template,
save: this.save,
t: this.t,
assignDropAreaSize: this.assignDropAreaSize,
currencies: this.currencies,
locale: this.locale,
baseFetch: this.baseFetch,
@@ -836,6 +850,17 @@ export default {
this.documentRefs = []
},
methods: {
toRaw,
onDragover (e) {
if (this.$refs.dragPlaceholder?.dragPlaceholder) {
this.$refs.dragPlaceholder.isMask = e.target.id === 'mask'
const ref = this.$refs.dragPlaceholder.dragPlaceholder
ref.x = e.clientX - ref.offsetX
ref.y = e.clientY - ref.offsetY
}
},
reorderFields (item) {
const itemFields = []
const fields = []
@@ -1319,6 +1344,10 @@ export default {
}
},
onDropfield (area) {
if (this.$refs.dragPlaceholder) {
this.$refs.dragPlaceholder.dragPlaceholder = null
}
if (!this.editable) {
return
}
@@ -1331,6 +1360,10 @@ export default {
...this.dragField
}
if (!field.type) {
field.type = 'text'
}
if (!this.fieldsDragFieldRef.value) {
if (['select', 'multiple', 'radio'].includes(field.type)) {
if (this.dragField?.options?.length) {
@@ -1357,70 +1390,18 @@ export default {
attachment_uuid: area.attachment_uuid
}
const previousField = [...this.template.fields].reverse().find((f) => f.type === field.type)
let baseArea
if (this.selectedField?.type === field.type) {
baseArea = this.selectedAreaRef.value
} else if (previousField?.areas?.length) {
baseArea = previousField.areas[previousField.areas.length - 1]
} else {
if (['checkbox'].includes(field.type)) {
baseArea = {
w: area.maskW / 30 / area.maskW,
h: area.maskW / 30 / area.maskW * (area.maskW / area.maskH)
}
} else if (field.type === 'image') {
baseArea = {
w: area.maskW / 5 / area.maskW,
h: (area.maskW / 5 / area.maskW) * (area.maskW / area.maskH)
}
} else if (field.type === 'signature' || field.type === 'stamp' || field.type === 'verification') {
baseArea = {
w: area.maskW / 5 / area.maskW,
h: (area.maskW / 5 / area.maskW) * (area.maskW / area.maskH) / 2
}
} else if (field.type === 'initials') {
baseArea = {
w: area.maskW / 10 / area.maskW,
h: area.maskW / 35 / area.maskW
}
} else {
baseArea = {
w: area.maskW / 5 / area.maskW,
h: area.maskW / 35 / area.maskW
}
}
}
fieldArea.w = baseArea.w
fieldArea.h = baseArea.h
fieldArea.y = fieldArea.y - baseArea.h / 2
if (field.type === 'cells') {
fieldArea.cell_w = baseArea.cell_w || (baseArea.w / 5)
}
field.areas ||= []
const lastArea = field.areas[field.areas.length - 1]
if (lastArea) {
fieldArea.w = lastArea.w
fieldArea.h = lastArea.h
}
this.assignDropAreaSize(fieldArea, field, area)
if (field.width) {
fieldArea.w = field.width / area.maskW
delete field.width
}
if (field.height) {
fieldArea.h = field.height / area.maskH
delete field.height
}
field.areas ||= []
field.areas.push(fieldArea)
this.selectedAreaRef.value = fieldArea
@@ -1444,6 +1425,71 @@ export default {
})
}
},
assignDropAreaSize (fieldArea, field, area) {
const fieldType = field.type || 'text'
const previousField = [...this.template.fields].reverse().find((f) => f.type === fieldType)
let baseArea
if (this.selectedField?.type === fieldType) {
baseArea = this.selectedAreaRef.value
} else if (previousField?.areas?.length) {
baseArea = previousField.areas[previousField.areas.length - 1]
} else {
if (['checkbox'].includes(fieldType)) {
baseArea = {
w: area.maskW / 30 / area.maskW,
h: area.maskW / 30 / area.maskW * (area.maskW / area.maskH)
}
} else if (fieldType === 'image') {
baseArea = {
w: area.maskW / 5 / area.maskW,
h: (area.maskW / 5 / area.maskW) * (area.maskW / area.maskH)
}
} else if (fieldType === 'signature' || fieldType === 'stamp' || fieldType === 'verification') {
baseArea = {
w: area.maskW / 5 / area.maskW,
h: (area.maskW / 5 / area.maskW) * (area.maskW / area.maskH) / 2
}
} else if (fieldType === 'initials') {
baseArea = {
w: area.maskW / 10 / area.maskW,
h: area.maskW / 35 / area.maskW
}
} else {
baseArea = {
w: area.maskW / 5 / area.maskW,
h: area.maskW / 35 / area.maskW
}
}
}
fieldArea.w = baseArea.w
fieldArea.h = baseArea.h
fieldArea.y = fieldArea.y - baseArea.h / 2
if (fieldType === 'cells') {
fieldArea.cell_w = baseArea.cell_w || (baseArea.w / 5)
}
if (field.areas?.length) {
const lastArea = field.areas[field.areas.length - 1]
if (lastArea) {
fieldArea.w = lastArea.w
fieldArea.h = lastArea.h
}
}
if (field.width) {
fieldArea.w = field.width / area.maskW
}
if (field.height) {
fieldArea.h = field.height / area.maskH
}
},
addBlankPage () {
this.isLoadingBlankPage = true