Files
docuseal/app/javascript/template_builder/page.vue
T

275 lines
6.4 KiB
Vue
Raw Normal View History

2023-05-21 17:59:36 +03:00
<template>
2023-10-06 03:21:14 +03:00
<div
class="relative cursor-crosshair select-none"
:style="drawField ? 'touch-action: none' : ''"
>
2023-05-21 17:59:36 +03:00
<img
ref="image"
2023-10-22 01:54:20 +03:00
loading="lazy"
2023-05-21 17:59:36 +03:00
:src="image.url"
:width="width"
:height="height"
2023-10-22 01:54:20 +03:00
class="border rounded mb-4"
@load="onImageLoad"
2023-05-21 17:59:36 +03:00
>
<div
class="top-0 bottom-0 left-0 right-0 absolute"
2023-06-03 00:53:46 +03:00
@pointerdown="onStartDraw"
2023-05-21 17:59:36 +03:00
>
<FieldArea
v-for="(item, i) in areas"
:key="i"
2023-06-03 00:53:46 +03:00
:ref="setAreaRefs"
2023-06-04 01:08:45 +03:00
:area="item.area"
2024-07-16 19:01:44 +03:00
:input-mode="inputMode"
2023-05-21 17:59:36 +03:00
:field="item.field"
2023-11-04 01:19:32 +02:00
:editable="editable"
2024-07-15 14:03:36 +03:00
:with-field-placeholder="withFieldPlaceholder"
2025-01-07 17:30:21 +02:00
:default-field="defaultFieldsIndex[item.field.name]"
2024-01-23 23:20:19 +02:00
:default-submitters="defaultSubmitters"
2024-12-28 13:08:00 +02:00
:max-page="totalPages - 1"
2023-06-27 00:57:29 +03:00
@start-resize="resizeDirection = $event"
@stop-resize="resizeDirection = null"
2023-06-09 01:43:18 +03:00
@remove="$emit('remove-area', item.area)"
2024-05-05 16:57:04 +03:00
@scroll-to="$emit('scroll-to', $event)"
2023-05-21 17:59:36 +03:00
/>
<FieldArea
v-if="newArea"
2023-06-27 00:57:29 +03:00
:is-draw="true"
2024-01-10 23:18:30 +02:00
:field="{ submitter_uuid: selectedSubmitter.uuid, type: drawField?.type || defaultFieldType }"
2023-06-04 01:08:45 +03:00
:area="newArea"
2023-05-21 17:59:36 +03:00
/>
</div>
<div
2024-06-12 20:05:55 +03:00
v-show="resizeDirection || isDrag || showMask || (drawField && isMobile) || fieldsDragFieldRef.value"
2023-05-21 17:59:36 +03:00
id="mask"
ref="mask"
2023-11-06 22:08:49 +02:00
class="top-0 bottom-0 left-0 right-0 absolute"
2024-06-12 20:05:55 +03:00
:class="{ 'z-10': !isMobile, 'cursor-grab': isDrag, 'cursor-nwse-resize': drawField, [resizeDirectionClasses[resizeDirection]]: !!resizeDirectionClasses }"
2023-05-21 17:59:36 +03:00
@pointermove="onPointermove"
2023-10-06 03:21:14 +03:00
@pointerdown="onStartDraw"
2023-05-23 00:03:26 +03:00
@dragover.prevent
@drop="onDrop"
2023-05-21 17:59:36 +03:00
@pointerup="onPointerup"
/>
</div>
</template>
<script>
import FieldArea from './area'
export default {
2023-05-31 23:19:20 +03:00
name: 'TemplatePage',
2023-05-21 17:59:36 +03:00
components: {
FieldArea
},
2024-02-27 01:41:57 +02:00
inject: ['fieldTypes', 'defaultDrawFieldType', 'fieldsDragFieldRef'],
2023-05-21 17:59:36 +03:00
props: {
image: {
type: Object,
required: true
},
areas: {
type: Array,
required: false,
default: () => []
},
2024-07-16 19:01:44 +03:00
inputMode: {
type: Boolean,
required: false,
default: false
},
2023-11-27 20:24:58 +02:00
defaultFields: {
type: Array,
required: false,
default: () => []
},
2024-07-15 14:03:36 +03:00
withFieldPlaceholder: {
type: Boolean,
required: false,
default: false
},
2024-12-28 13:08:00 +02:00
totalPages: {
type: Number,
required: true
},
2024-03-27 13:47:17 +02:00
drawFieldType: {
type: String,
required: false,
default: ''
},
2024-01-05 18:40:45 +02:00
allowDraw: {
type: Boolean,
required: false,
default: true
},
2023-06-07 01:16:43 +03:00
selectedSubmitter: {
2023-06-04 01:08:45 +03:00
type: Object,
2023-06-07 01:16:43 +03:00
required: true
2023-06-04 01:08:45 +03:00
},
2024-01-23 23:20:19 +02:00
defaultSubmitters: {
type: Array,
required: false,
default: () => []
},
2023-06-27 00:57:29 +03:00
drawField: {
type: Object,
2023-06-08 00:20:42 +03:00
required: false,
2023-06-27 00:57:29 +03:00
default: null
2023-06-08 00:20:42 +03:00
},
2023-11-04 01:19:32 +02:00
editable: {
type: Boolean,
required: false,
default: true
},
2023-05-23 00:03:26 +03:00
isDrag: {
type: Boolean,
required: false,
default: false
},
2023-05-21 17:59:36 +03:00
number: {
type: Number,
required: true
}
},
2024-05-05 16:57:04 +03:00
emits: ['draw', 'drop-field', 'remove-area', 'scroll-to'],
2023-05-21 17:59:36 +03:00
data () {
return {
2023-06-03 00:53:46 +03:00
areaRefs: [],
2023-05-21 17:59:36 +03:00
showMask: false,
2023-06-27 00:57:29 +03:00
resizeDirection: null,
2023-05-21 17:59:36 +03:00
newArea: null
}
},
computed: {
2025-01-07 17:30:21 +02:00
defaultFieldsIndex () {
return this.defaultFields.reduce((acc, field) => {
acc[field.name] = field
return acc
}, {})
},
2024-01-10 23:18:30 +02:00
defaultFieldType () {
2024-03-27 13:47:17 +02:00
if (this.drawFieldType) {
return this.drawFieldType
} else if (this.defaultDrawFieldType && this.defaultDrawFieldType !== 'text') {
2024-02-20 22:15:21 +02:00
return this.defaultDrawFieldType
} else if (this.fieldTypes.length !== 0 && !this.fieldTypes.includes('text')) {
2024-01-10 23:18:30 +02:00
return this.fieldTypes[0]
} else {
return 'text'
}
},
2023-10-06 03:21:14 +03:00
isMobile () {
return /android|iphone|ipad/i.test(navigator.userAgent)
},
2023-06-27 00:57:29 +03:00
resizeDirectionClasses () {
return {
nwse: 'cursor-nwse-resize',
ew: 'cursor-ew-resize'
}
},
2023-05-21 17:59:36 +03:00
width () {
return this.image.metadata.width
},
height () {
return this.image.metadata.height
}
},
2023-06-03 00:53:46 +03:00
beforeUpdate () {
this.areaRefs = []
},
2023-05-21 17:59:36 +03:00
methods: {
2023-10-22 01:54:20 +03:00
onImageLoad (e) {
e.target.setAttribute('width', e.target.naturalWidth)
e.target.setAttribute('height', e.target.naturalHeight)
},
2023-06-03 00:53:46 +03:00
setAreaRefs (el) {
if (el) {
this.areaRefs.push(el)
}
},
2023-05-23 00:03:26 +03:00
onDrop (e) {
this.$emit('drop-field', {
2024-03-05 20:53:54 +02:00
x: e.offsetX,
y: e.offsetY,
2023-06-04 01:08:45 +03:00
maskW: this.$refs.mask.clientWidth,
maskH: this.$refs.mask.clientHeight,
2023-05-23 00:03:26 +03:00
page: this.number
})
},
2023-06-03 00:53:46 +03:00
onStartDraw (e) {
2024-01-05 18:40:45 +02:00
if (!this.allowDraw) {
return
}
2023-10-06 03:21:14 +03:00
if (this.isMobile && !this.drawField) {
return
}
2023-11-04 01:19:32 +02:00
if (!this.editable) {
return
}
2023-06-03 00:53:46 +03:00
this.showMask = true
this.$nextTick(() => {
2023-05-21 17:59:36 +03:00
this.newArea = {
2024-03-05 20:53:54 +02:00
initialX: e.offsetX / this.$refs.mask.clientWidth,
initialY: e.offsetY / this.$refs.mask.clientHeight,
x: e.offsetX / this.$refs.mask.clientWidth,
y: e.offsetY / this.$refs.mask.clientHeight,
2023-05-21 17:59:36 +03:00
w: 0,
h: 0
}
2023-06-03 00:53:46 +03:00
})
2023-05-21 17:59:36 +03:00
},
onPointermove (e) {
if (this.newArea) {
2024-03-05 20:53:54 +02:00
const dx = e.offsetX / this.$refs.mask.clientWidth - this.newArea.initialX
const dy = e.offsetY / this.$refs.mask.clientHeight - this.newArea.initialY
2023-05-21 17:59:36 +03:00
if (dx > 0) {
this.newArea.x = this.newArea.initialX
} else {
2024-03-05 20:53:54 +02:00
this.newArea.x = e.offsetX / this.$refs.mask.clientWidth
2023-05-21 17:59:36 +03:00
}
if (dy > 0) {
this.newArea.y = this.newArea.initialY
} else {
2024-03-05 20:53:54 +02:00
this.newArea.y = e.offsetY / this.$refs.mask.clientHeight
2023-05-21 17:59:36 +03:00
}
2024-04-21 00:31:52 +03:00
if ((this.drawField?.type || this.drawFieldType) === 'cells') {
2023-06-27 00:57:29 +03:00
this.newArea.cell_w = this.newArea.h * (this.$refs.mask.clientHeight / this.$refs.mask.clientWidth)
}
2023-05-21 17:59:36 +03:00
this.newArea.w = Math.abs(dx)
this.newArea.h = Math.abs(dy)
}
},
onPointerup (e) {
2023-06-03 00:53:46 +03:00
if (this.newArea) {
2023-06-27 00:57:29 +03:00
const area = {
2023-05-21 17:59:36 +03:00
x: this.newArea.x,
y: this.newArea.y,
2023-06-03 00:53:46 +03:00
w: this.newArea.w,
h: this.newArea.h,
2023-05-21 17:59:36 +03:00
page: this.number
2023-06-27 00:57:29 +03:00
}
if ('cell_w' in this.newArea) {
area.cell_w = this.newArea.cell_w
}
this.$emit('draw', area)
2023-05-21 17:59:36 +03:00
}
2023-06-03 00:53:46 +03:00
this.showMask = false
2023-05-21 17:59:36 +03:00
this.newArea = null
}
}
}
</script>