You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
docuseal/app/javascript/template_builder/page.vue

200 lines
4.6 KiB

<template>
<div
class="relative cursor-crosshair select-none"
:style="drawField ? 'touch-action: none' : ''"
>
<img
ref="image"
:src="image.url"
:width="width"
class="border rounded mb-4"
:height="height"
loading="lazy"
>
<div
class="top-0 bottom-0 left-0 right-0 absolute"
@pointerdown="onStartDraw"
>
<FieldArea
v-for="(item, i) in areas"
:key="i"
:ref="setAreaRefs"
:area="item.area"
:field="item.field"
@start-resize="resizeDirection = $event"
@stop-resize="resizeDirection = null"
@start-drag="isMove = true"
@stop-drag="isMove = false"
@remove="$emit('remove-area', item.area)"
/>
<FieldArea
v-if="newArea"
:is-draw="true"
:field="{ submitter_uuid: selectedSubmitter.uuid, type: drawField?.type || 'text' }"
:area="newArea"
/>
</div>
<div
v-show="resizeDirection || isMove || isDrag || showMask || (drawField && isMobile)"
id="mask"
ref="mask"
class="top-0 bottom-0 left-0 right-0 absolute z-10"
:class="{ 'cursor-grab': isDrag || isMove, 'cursor-nwse-resize': drawField, [resizeDirectionClasses[resizeDirection]]: !!resizeDirectionClasses }"
@pointermove="onPointermove"
@pointerdown="onStartDraw"
@dragover.prevent
@drop="onDrop"
@pointerup="onPointerup"
/>
</div>
</template>
<script>
import FieldArea from './area'
export default {
name: 'TemplatePage',
components: {
FieldArea
},
props: {
image: {
type: Object,
required: true
},
areas: {
type: Array,
required: false,
default: () => []
},
selectedSubmitter: {
type: Object,
required: true
},
drawField: {
type: Object,
required: false,
default: null
},
isDrag: {
type: Boolean,
required: false,
default: false
},
number: {
type: Number,
required: true
}
},
emits: ['draw', 'drop-field', 'remove-area'],
data () {
return {
areaRefs: [],
showMask: false,
isMove: false,
resizeDirection: null,
newArea: null
}
},
computed: {
isMobile () {
return /android|iphone|ipad/i.test(navigator.userAgent)
},
resizeDirectionClasses () {
return {
nwse: 'cursor-nwse-resize',
ew: 'cursor-ew-resize'
}
},
width () {
return this.image.metadata.width
},
height () {
return this.image.metadata.height
}
},
beforeUpdate () {
this.areaRefs = []
},
methods: {
setAreaRefs (el) {
if (el) {
this.areaRefs.push(el)
}
},
onDrop (e) {
this.$emit('drop-field', {
x: e.layerX,
y: e.layerY,
maskW: this.$refs.mask.clientWidth,
maskH: this.$refs.mask.clientHeight,
page: this.number
})
},
onStartDraw (e) {
if (this.isMobile && !this.drawField) {
return
}
this.showMask = true
this.$nextTick(() => {
this.newArea = {
initialX: e.layerX / this.$refs.mask.clientWidth,
initialY: e.layerY / this.$refs.mask.clientHeight,
x: e.layerX / this.$refs.mask.clientWidth,
y: e.layerY / this.$refs.mask.clientHeight,
w: 0,
h: 0
}
})
},
onPointermove (e) {
if (this.newArea) {
const dx = e.layerX / this.$refs.mask.clientWidth - this.newArea.initialX
const dy = e.layerY / this.$refs.mask.clientHeight - this.newArea.initialY
if (dx > 0) {
this.newArea.x = this.newArea.initialX
} else {
this.newArea.x = e.layerX / this.$refs.mask.clientWidth
}
if (dy > 0) {
this.newArea.y = this.newArea.initialY
} else {
this.newArea.y = e.layerY / this.$refs.mask.clientHeight
}
if (this.drawField?.type === 'cells') {
this.newArea.cell_w = this.newArea.h * (this.$refs.mask.clientHeight / this.$refs.mask.clientWidth)
}
this.newArea.w = Math.abs(dx)
this.newArea.h = Math.abs(dy)
}
},
onPointerup (e) {
if (this.newArea) {
const area = {
x: this.newArea.x,
y: this.newArea.y,
w: this.newArea.w,
h: this.newArea.h,
page: this.number
}
if ('cell_w' in this.newArea) {
area.cell_w = this.newArea.cell_w
}
this.$emit('draw', area)
}
this.showMask = false
this.newArea = null
}
}
}
</script>