improve drag'n'drop in the builder
This commit is contained in:
committed by
Pete Matsyburka
parent
2573d8cb19
commit
7a4629bf79
@@ -1,9 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
ref="dragContainer"
|
||||||
style="max-width: 1600px"
|
style="max-width: 1600px"
|
||||||
class="mx-auto pl-3 h-full"
|
class="mx-auto pl-3 h-full"
|
||||||
:class="isMobile ? 'pl-4' : 'md:pl-4'"
|
: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
|
<div
|
||||||
v-if="pendingFieldAttachmentUuids.length && editable"
|
v-if="pendingFieldAttachmentUuids.length && editable"
|
||||||
class="top-1.5 sticky h-0 z-20 max-w-2xl mx-auto"
|
class="top-1.5 sticky h-0 z-20 max-w-2xl mx-auto"
|
||||||
@@ -269,6 +278,7 @@
|
|||||||
:allow-draw="!onlyDefinedFields || drawField"
|
:allow-draw="!onlyDefinedFields || drawField"
|
||||||
:data-document-uuid="document.uuid"
|
:data-document-uuid="document.uuid"
|
||||||
:default-submitters="defaultSubmitters"
|
:default-submitters="defaultSubmitters"
|
||||||
|
:drag-field-placeholder="fieldsDragFieldRef.value || dragField"
|
||||||
:with-field-placeholder="withFieldPlaceholder"
|
:with-field-placeholder="withFieldPlaceholder"
|
||||||
:draw-field="drawField"
|
:draw-field="drawField"
|
||||||
:draw-field-type="drawFieldType"
|
:draw-field-type="drawFieldType"
|
||||||
@@ -378,8 +388,9 @@
|
|||||||
@set-draw="[drawField = $event.field, drawOption = $event.option]"
|
@set-draw="[drawField = $event.field, drawOption = $event.option]"
|
||||||
@set-draw-type="[drawFieldType = $event, showDrawField = true]"
|
@set-draw-type="[drawFieldType = $event, showDrawField = true]"
|
||||||
@set-drag="dragField = $event"
|
@set-drag="dragField = $event"
|
||||||
|
@set-drag-placeholder="$refs.dragPlaceholder.dragPlaceholder = $event"
|
||||||
@change-submitter="selectedSubmitter = $event"
|
@change-submitter="selectedSubmitter = $event"
|
||||||
@drag-end="dragField = null"
|
@drag-end="[dragField = null, $refs.dragPlaceholder.dragPlaceholder = null]"
|
||||||
@scroll-to-area="scrollToArea"
|
@scroll-to-area="scrollToArea"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -418,6 +429,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Upload from './upload'
|
import Upload from './upload'
|
||||||
import Dropzone from './dropzone'
|
import Dropzone from './dropzone'
|
||||||
|
import DragPlaceholder from './drag_placeholder'
|
||||||
import Fields from './fields'
|
import Fields from './fields'
|
||||||
import MobileDrawField from './mobile_draw_field'
|
import MobileDrawField from './mobile_draw_field'
|
||||||
import Document from './document'
|
import Document from './document'
|
||||||
@@ -429,13 +441,14 @@ import MobileFields from './mobile_fields'
|
|||||||
import FieldSubmitter from './field_submitter'
|
import FieldSubmitter from './field_submitter'
|
||||||
import { IconPlus, IconUsersPlus, IconDeviceFloppy, IconChevronDown, IconEye, IconWritingSign, IconInnerShadowTop, IconInfoCircle, IconAdjustments } from '@tabler/icons-vue'
|
import { IconPlus, IconUsersPlus, IconDeviceFloppy, IconChevronDown, IconEye, IconWritingSign, IconInnerShadowTop, IconInfoCircle, IconAdjustments } from '@tabler/icons-vue'
|
||||||
import { v4 } from 'uuid'
|
import { v4 } from 'uuid'
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, toRaw } from 'vue'
|
||||||
import * as i18n from './i18n'
|
import * as i18n from './i18n'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TemplateBuilder',
|
name: 'TemplateBuilder',
|
||||||
components: {
|
components: {
|
||||||
Upload,
|
Upload,
|
||||||
|
DragPlaceholder,
|
||||||
Document,
|
Document,
|
||||||
Fields,
|
Fields,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
@@ -460,6 +473,7 @@ export default {
|
|||||||
template: this.template,
|
template: this.template,
|
||||||
save: this.save,
|
save: this.save,
|
||||||
t: this.t,
|
t: this.t,
|
||||||
|
assignDropAreaSize: this.assignDropAreaSize,
|
||||||
currencies: this.currencies,
|
currencies: this.currencies,
|
||||||
locale: this.locale,
|
locale: this.locale,
|
||||||
baseFetch: this.baseFetch,
|
baseFetch: this.baseFetch,
|
||||||
@@ -836,6 +850,17 @@ export default {
|
|||||||
this.documentRefs = []
|
this.documentRefs = []
|
||||||
},
|
},
|
||||||
methods: {
|
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) {
|
reorderFields (item) {
|
||||||
const itemFields = []
|
const itemFields = []
|
||||||
const fields = []
|
const fields = []
|
||||||
@@ -1319,6 +1344,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDropfield (area) {
|
onDropfield (area) {
|
||||||
|
if (this.$refs.dragPlaceholder) {
|
||||||
|
this.$refs.dragPlaceholder.dragPlaceholder = null
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.editable) {
|
if (!this.editable) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1331,6 +1360,10 @@ export default {
|
|||||||
...this.dragField
|
...this.dragField
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!field.type) {
|
||||||
|
field.type = 'text'
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.fieldsDragFieldRef.value) {
|
if (!this.fieldsDragFieldRef.value) {
|
||||||
if (['select', 'multiple', 'radio'].includes(field.type)) {
|
if (['select', 'multiple', 'radio'].includes(field.type)) {
|
||||||
if (this.dragField?.options?.length) {
|
if (this.dragField?.options?.length) {
|
||||||
@@ -1357,70 +1390,18 @@ export default {
|
|||||||
attachment_uuid: area.attachment_uuid
|
attachment_uuid: area.attachment_uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousField = [...this.template.fields].reverse().find((f) => f.type === field.type)
|
this.assignDropAreaSize(fieldArea, field, area)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field.width) {
|
if (field.width) {
|
||||||
fieldArea.w = field.width / area.maskW
|
|
||||||
delete field.width
|
delete field.width
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.height) {
|
if (field.height) {
|
||||||
fieldArea.h = field.height / area.maskH
|
|
||||||
delete field.height
|
delete field.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
field.areas ||= []
|
||||||
|
|
||||||
field.areas.push(fieldArea)
|
field.areas.push(fieldArea)
|
||||||
|
|
||||||
this.selectedAreaRef.value = 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 () {
|
addBlankPage () {
|
||||||
this.isLoadingBlankPage = true
|
this.isLoadingBlankPage = true
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
:is-drag="isDrag"
|
:is-drag="isDrag"
|
||||||
:with-field-placeholder="withFieldPlaceholder"
|
:with-field-placeholder="withFieldPlaceholder"
|
||||||
:default-fields="defaultFields"
|
:default-fields="defaultFields"
|
||||||
|
:drag-field-placeholder="dragFieldPlaceholder"
|
||||||
:default-submitters="defaultSubmitters"
|
:default-submitters="defaultSubmitters"
|
||||||
:draw-field="drawField"
|
:draw-field="drawField"
|
||||||
:draw-field-type="drawFieldType"
|
:draw-field-type="drawFieldType"
|
||||||
@@ -39,6 +40,11 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
dragFieldPlaceholder: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
inputMode: {
|
inputMode: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<Field
|
||||||
|
v-if="dragPlaceholder && isField && !isMask && field"
|
||||||
|
ref="dragPlaceholder"
|
||||||
|
:style="dragPlaceholderStyle"
|
||||||
|
:field="field"
|
||||||
|
:with-options="false"
|
||||||
|
class="fixed z-20 pointer-events-none"
|
||||||
|
:editable="false"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else-if="dragPlaceholder && (isDefault || isRequired) && !isMask && field"
|
||||||
|
ref="dragPlaceholder"
|
||||||
|
:style="[dragPlaceholderStyle, { backgroundColor: backgroundColor }]"
|
||||||
|
class="fixed z-20 border border-base-300 rounded group default-field fields-list-item pointer-events-none"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between relative cursor-grab">
|
||||||
|
<div class="flex items-center p-1 space-x-1">
|
||||||
|
<IconDrag />
|
||||||
|
<component
|
||||||
|
:is="fieldIcons[field.type || 'text']"
|
||||||
|
:stroke-width="1.6"
|
||||||
|
:width="20"
|
||||||
|
/>
|
||||||
|
<span class="block pl-0.5">
|
||||||
|
{{ field.title || field.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
v-if="isRequired"
|
||||||
|
:data-tip="t('required')"
|
||||||
|
class="text-red-400 text-3xl pr-1.5 tooltip tooltip-left h-8"
|
||||||
|
>
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
v-else-if="dragPlaceholder && !isMask && field"
|
||||||
|
ref="dragPlaceholder"
|
||||||
|
class="fixed field-type-button z-20 flex items-center justify-center border border-dashed w-full rounded border-base-content/20 opacity-90 pointer-events-none"
|
||||||
|
:style="[dragPlaceholderStyle, { backgroundColor }]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex items-console cursor-grab h-full absolute left-0 bg-base-200/50"
|
||||||
|
>
|
||||||
|
<IconDrag class="my-auto" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center flex-col px-2 py-2">
|
||||||
|
<component :is="fieldIcons[field.type || 'text']" />
|
||||||
|
<span class="text-xs mt-1">
|
||||||
|
{{ fieldNames[field.type || 'text'] }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Field from './field'
|
||||||
|
import IconDrag from './icon_drag'
|
||||||
|
import FieldType from './field_type'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DragPlaceholder',
|
||||||
|
components: {
|
||||||
|
Field,
|
||||||
|
IconDrag
|
||||||
|
},
|
||||||
|
inject: ['t', 'backgroundColor'],
|
||||||
|
props: {
|
||||||
|
field: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
isDefault: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
isRequired: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
isField: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isMask: false,
|
||||||
|
dragPlaceholder: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dragPlaceholderStyle () {
|
||||||
|
if (this.dragPlaceholder) {
|
||||||
|
return {
|
||||||
|
left: this.dragPlaceholder.x + 'px',
|
||||||
|
top: this.dragPlaceholder.y + 'px',
|
||||||
|
width: this.dragPlaceholder.w + 'px',
|
||||||
|
height: this.dragPlaceholder.h + 'px'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fieldNames: FieldType.computed.fieldNames,
|
||||||
|
fieldIcons: FieldType.computed.fieldIcons
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
class="list-field group mb-2"
|
class="list-field group mb-2"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="border border-base-300 rounded rounded-tr-none relative group fields-list-item"
|
class="border border-base-300 rounded relative group fields-list-item"
|
||||||
:style="{ backgroundColor: backgroundColor }"
|
:style="{ backgroundColor: backgroundColor }"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between relative group/contenteditable-container">
|
<div class="flex items-center justify-between relative group/contenteditable-container">
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="field.options"
|
v-if="field.options && withOptions"
|
||||||
ref="options"
|
ref="options"
|
||||||
class="border-t border-base-300 mx-2 pt-2 space-y-1.5"
|
class="border-t border-base-300 mx-2 pt-2 space-y-1.5"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
@@ -302,6 +302,11 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
withOptions: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
defaultField: {
|
defaultField: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: false,
|
required: false,
|
||||||
|
|||||||
@@ -25,11 +25,11 @@
|
|||||||
:data-uuid="field.uuid"
|
:data-uuid="field.uuid"
|
||||||
:field="field"
|
:field="field"
|
||||||
:type-index="fields.filter((f) => f.type === field.type).indexOf(field)"
|
:type-index="fields.filter((f) => f.type === field.type).indexOf(field)"
|
||||||
:editable="editable && (!fieldsDragFieldRef.value || fieldsDragFieldRef.value !== field)"
|
:editable="editable"
|
||||||
:default-field="defaultFieldsIndex[field.name]"
|
:default-field="defaultFieldsIndex[field.name]"
|
||||||
:draggable="editable"
|
:draggable="editable"
|
||||||
@dragstart="fieldsDragFieldRef.value = field"
|
@dragstart="[fieldsDragFieldRef.value = field, removeDragOverlay($event), setDragPlaceholder($event)]"
|
||||||
@dragend="fieldsDragFieldRef.value = null"
|
@dragend="[fieldsDragFieldRef.value = null, $emit('set-drag-placeholder', null)]"
|
||||||
@remove="removeField"
|
@remove="removeField"
|
||||||
@scroll-to="$emit('scroll-to-area', $event)"
|
@scroll-to="$emit('scroll-to-area', $event)"
|
||||||
@set-draw="$emit('set-draw', $event)"
|
@set-draw="$emit('set-draw', $event)"
|
||||||
@@ -74,8 +74,8 @@
|
|||||||
<div
|
<div
|
||||||
:style="{ backgroundColor }"
|
:style="{ backgroundColor }"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
class="border border-base-300 rounded rounded-tr-none relative group mb-2 default-field fields-list-item"
|
class="border border-base-300 rounded relative group mb-2 default-field fields-list-item"
|
||||||
@dragstart="onDragstart({ type: 'text', ...field })"
|
@dragstart="onDragstart($event, field)"
|
||||||
@dragend="$emit('drag-end')"
|
@dragend="$emit('drag-end')"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between relative cursor-grab">
|
<div class="flex items-center justify-between relative cursor-grab">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
class="field-type-button group flex items-center justify-center border border-dashed w-full rounded relative fields-grid-item"
|
class="field-type-button group flex items-center justify-center border border-dashed w-full rounded relative fields-grid-item"
|
||||||
:style="{ backgroundColor }"
|
:style="{ backgroundColor }"
|
||||||
:class="drawFieldType === type ? 'border-base-content/40' : 'border-base-300 hover:border-base-content/20'"
|
:class="drawFieldType === type ? 'border-base-content/40' : 'border-base-300 hover:border-base-content/20'"
|
||||||
@dragstart="onDragstart({ type: type })"
|
@dragstart="onDragstart($event, { type: type })"
|
||||||
@dragend="$emit('drag-end')"
|
@dragend="$emit('drag-end')"
|
||||||
@click="['file', 'payment', 'verification'].includes(type) ? $emit('add-field', type) : $emit('set-draw-type', type)"
|
@click="['file', 'payment', 'verification'].includes(type) ? $emit('add-field', type) : $emit('set-draw-type', type)"
|
||||||
>
|
>
|
||||||
@@ -292,7 +292,7 @@ export default {
|
|||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['add-field', 'set-draw', 'set-draw-type', 'set-drag', 'drag-end', 'scroll-to-area', 'change-submitter'],
|
emits: ['add-field', 'set-draw', 'set-draw-type', 'set-drag', 'drag-end', 'scroll-to-area', 'change-submitter', 'set-drag-placeholder'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
defaultFieldsSearch: ''
|
defaultFieldsSearch: ''
|
||||||
@@ -343,22 +343,53 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onDragstart (field) {
|
onDragstart (event, field) {
|
||||||
|
this.removeDragOverlay(event)
|
||||||
|
|
||||||
|
this.setDragPlaceholder(event)
|
||||||
|
|
||||||
this.$emit('set-drag', field)
|
this.$emit('set-drag', field)
|
||||||
},
|
},
|
||||||
|
setDragPlaceholder (event) {
|
||||||
|
this.$emit('set-drag-placeholder', {
|
||||||
|
offsetX: event.offsetX,
|
||||||
|
offsetY: event.offsetY,
|
||||||
|
x: event.clientX - event.offsetX,
|
||||||
|
y: event.clientY - event.offsetY,
|
||||||
|
w: event.currentTarget.clientWidth + 2,
|
||||||
|
h: event.currentTarget.clientHeight + 2
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeDragOverlay (event) {
|
||||||
|
const root = this.$el.getRootNode()
|
||||||
|
const hiddenEl = document.createElement('div')
|
||||||
|
|
||||||
|
hiddenEl.style.width = '1px'
|
||||||
|
hiddenEl.style.height = '1px'
|
||||||
|
hiddenEl.style.opacity = '0'
|
||||||
|
hiddenEl.style.position = 'fixed'
|
||||||
|
|
||||||
|
root.querySelector('#docuseal_modal_container').appendChild(hiddenEl)
|
||||||
|
|
||||||
|
event.dataTransfer.setDragImage(hiddenEl, 0, 0)
|
||||||
|
|
||||||
|
setTimeout(() => { hiddenEl.remove() }, 1000)
|
||||||
|
},
|
||||||
onFieldDragover (e) {
|
onFieldDragover (e) {
|
||||||
const targetField = e.target.closest('[data-uuid]')
|
if (this.fieldsDragFieldRef.value) {
|
||||||
const dragField = this.$refs.fields.querySelector(`[data-uuid="${this.fieldsDragFieldRef.value.uuid}"]`)
|
const targetField = e.target.closest('[data-uuid]')
|
||||||
|
const dragField = this.$refs.fields.querySelector(`[data-uuid="${this.fieldsDragFieldRef.value.uuid}"]`)
|
||||||
|
|
||||||
if (dragField && targetField && targetField !== dragField) {
|
if (dragField && targetField && targetField !== dragField) {
|
||||||
const fields = Array.from(this.$refs.fields.children)
|
const fields = Array.from(this.$refs.fields.children)
|
||||||
const currentIndex = fields.indexOf(dragField)
|
const currentIndex = fields.indexOf(dragField)
|
||||||
const targetIndex = fields.indexOf(targetField)
|
const targetIndex = fields.indexOf(targetField)
|
||||||
|
|
||||||
if (currentIndex < targetIndex) {
|
if (currentIndex < targetIndex) {
|
||||||
targetField.after(dragField)
|
targetField.after(dragField)
|
||||||
} else {
|
} else {
|
||||||
targetField.before(dragField)
|
targetField.before(dragField)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
<FieldArea
|
<FieldArea
|
||||||
v-if="newArea"
|
v-if="newArea"
|
||||||
:is-draw="true"
|
:is-draw="true"
|
||||||
:field="{ submitter_uuid: selectedSubmitter.uuid, type: drawField?.type || defaultFieldType }"
|
:field="{ submitter_uuid: selectedSubmitter.uuid, type: drawField?.type || dragFieldPlaceholder?.type || defaultFieldType }"
|
||||||
:area="newArea"
|
:area="newArea"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,7 +49,9 @@
|
|||||||
:class="{ 'z-10': !isMobile, 'cursor-grab': isDrag, 'cursor-nwse-resize': drawField, [resizeDirectionClasses[resizeDirection]]: !!resizeDirectionClasses }"
|
:class="{ 'z-10': !isMobile, 'cursor-grab': isDrag, 'cursor-nwse-resize': drawField, [resizeDirectionClasses[resizeDirection]]: !!resizeDirectionClasses }"
|
||||||
@pointermove="onPointermove"
|
@pointermove="onPointermove"
|
||||||
@pointerdown="onStartDraw"
|
@pointerdown="onStartDraw"
|
||||||
@dragover.prevent
|
@dragover.prevent="onDragover"
|
||||||
|
@dragenter="onDragenter"
|
||||||
|
@dragleave="newArea = null"
|
||||||
@drop="onDrop"
|
@drop="onDrop"
|
||||||
@pointerup="onPointerup"
|
@pointerup="onPointerup"
|
||||||
/>
|
/>
|
||||||
@@ -64,12 +66,17 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
FieldArea
|
FieldArea
|
||||||
},
|
},
|
||||||
inject: ['fieldTypes', 'defaultDrawFieldType', 'fieldsDragFieldRef'],
|
inject: ['fieldTypes', 'defaultDrawFieldType', 'fieldsDragFieldRef', 'assignDropAreaSize'],
|
||||||
props: {
|
props: {
|
||||||
image: {
|
image: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
dragFieldPlaceholder: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
areas: {
|
areas: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: false,
|
required: false,
|
||||||
@@ -192,7 +199,24 @@ export default {
|
|||||||
this.areaRefs.push(el)
|
this.areaRefs.push(el)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onDragenter (e) {
|
||||||
|
this.newArea = {}
|
||||||
|
|
||||||
|
this.assignDropAreaSize(this.newArea, this.dragFieldPlaceholder, {
|
||||||
|
maskW: this.$refs.mask.clientWidth,
|
||||||
|
maskH: this.$refs.mask.clientHeight
|
||||||
|
})
|
||||||
|
|
||||||
|
this.newArea.x = (e.offsetX - 6) / this.$refs.mask.clientWidth
|
||||||
|
this.newArea.y = e.offsetY / this.$refs.mask.clientHeight - this.newArea.h / 2
|
||||||
|
},
|
||||||
|
onDragover (e) {
|
||||||
|
this.newArea.x = (e.offsetX - 6) / this.$refs.mask.clientWidth
|
||||||
|
this.newArea.y = e.offsetY / this.$refs.mask.clientHeight - this.newArea.h / 2
|
||||||
|
},
|
||||||
onDrop (e) {
|
onDrop (e) {
|
||||||
|
this.newArea = null
|
||||||
|
|
||||||
this.$emit('drop-field', {
|
this.$emit('drop-field', {
|
||||||
x: e.offsetX,
|
x: e.offsetX,
|
||||||
y: e.offsetY,
|
y: e.offsetY,
|
||||||
|
|||||||
Reference in New Issue
Block a user