add cell field type

This commit is contained in:
Alex Turchyn
2023-06-27 00:57:29 +03:00
parent 419ede5b1d
commit 897df83633
14 changed files with 188 additions and 47 deletions
+71 -2
View File
@@ -6,10 +6,29 @@
@mousedown.stop="startDrag"
>
<div
v-if="isSelected || !field?.type"
v-if="isSelected || isDraw"
class="top-0 bottom-0 right-0 left-0 absolute border border-1.5 pointer-events-none"
:class="borderColors[submitterIndex]"
/>
<div
v-if="field.type === 'cells' && (isSelected || isDraw)"
class="top-0 bottom-0 right-0 left-0 absolute"
>
<div
v-for="(cellW, index) in cells"
:key="index"
class="absolute top-0 bottom-0 border-r"
:class="borderColors[submitterIndex]"
:style="{ left: (cellW / area.w * 100) + '%' }"
>
<span
v-if="index === 0"
class="h-2.5 w-2.5 rounded-full -bottom-1 border-gray-400 bg-white shadow-md border absolute cursor-ew-resize z-10"
style="left: -4px"
@mousedown.stop="startResizeCell"
/>
</div>
</div>
<div
v-if="field?.type"
class="absolute bg-white rounded-t border overflow-visible whitespace-nowrap group-hover:flex group-hover:z-10"
@@ -118,6 +137,11 @@ export default {
type: Object,
required: true
},
isDraw: {
type: Boolean,
required: false,
default: false
},
field: {
type: Object,
required: false,
@@ -135,7 +159,21 @@ export default {
},
computed: {
defaultName: Field.computed.defaultName,
fieldNames: FieldType.computed.fieldNames,
fieldIcons: FieldType.computed.fieldIcons,
cells () {
const cells = []
let currentWidth = 0
while (currentWidth + (this.area.cell_w + this.area.cell_w / 4) < this.area.w) {
currentWidth += this.area.cell_w || 9999999
cells.push(currentWidth)
}
return cells
},
submitter () {
return this.template.submitters.find((s) => s.uuid === this.field.submitter_uuid)
},
@@ -187,6 +225,29 @@ export default {
}, 1)
}
},
startResizeCell (e) {
document.addEventListener('mousemove', this.onResizeCell)
document.addEventListener('mouseup', this.stopResizeCell)
this.$emit('start-resize', 'ew')
},
stopResizeCell (e) {
document.removeEventListener('mousemove', this.onResizeCell)
document.removeEventListener('mouseup', this.stopResizeCell)
this.$emit('stop-resize')
this.save()
},
onResizeCell (e) {
if (e.toElement.id === 'mask') {
const positionX = e.layerX / (e.toElement.clientWidth - 1)
if (positionX > this.area.x) {
this.area.cell_w = positionX - this.area.x
}
}
},
maybeUpdateOptions () {
if (!['radio', 'multiple', 'select'].includes(this.field.type)) {
delete this.field.options
@@ -195,6 +256,14 @@ export default {
if (['select', 'multiple', 'radio'].includes(this.field.type)) {
this.field.options ||= ['']
}
(this.field.areas || []).forEach((area) => {
if (this.field.type === 'cells') {
area.cell_w = area.w * 2 / Math.floor(area.w / area.h)
} else {
delete area.cell_w
}
})
},
onNameBlur (e) {
this.isNameFocus = false
@@ -256,7 +325,7 @@ export default {
document.addEventListener('mousemove', this.resize)
document.addEventListener('mouseup', this.stopResize)
this.$emit('start-resize')
this.$emit('start-resize', 'nwse')
},
stopResize () {
document.removeEventListener('mousemove', this.resize)
+5 -3
View File
@@ -85,7 +85,7 @@
:selected-submitter="selectedSubmitter"
:document="document"
:is-drag="!!dragFieldType"
:is-draw="!!drawField"
:draw-field="drawField"
@draw="onDraw"
@drop-field="onDropfield"
@remove-area="removeArea"
@@ -141,12 +141,10 @@ import Contenteditable from './contenteditable'
import DocumentPreview from './preview'
import { IconUsersPlus, IconDeviceFloppy, IconInnerShadowTop } from '@tabler/icons-vue'
import { v4 } from 'uuid'
import i18n from './i18n'
import { ref, computed } from 'vue'
export default {
name: 'TemplateBuilder',
i18n,
components: {
Upload,
Document,
@@ -352,6 +350,10 @@ export default {
fieldArea.h = baseArea.h
fieldArea.y = fieldArea.y - baseArea.h / 2
if (this.dragFieldType === 'cells') {
fieldArea.cell_w = baseArea.cell_w || (baseArea.w / 5)
}
field.areas = [fieldArea]
this.selectedAreaRef.value = fieldArea
+4 -4
View File
@@ -7,7 +7,7 @@
:number="index"
:areas="areasIndex[index]"
:is-drag="isDrag"
:is-draw="isDraw"
:draw-field="drawField"
:selected-submitter="selectedSubmitter"
:image="image"
@drop-field="$emit('drop-field', {...$event, attachment_uuid: document.uuid })"
@@ -38,10 +38,10 @@ export default {
type: Object,
required: true
},
isDraw: {
type: Boolean,
drawField: {
type: Object,
required: false,
default: false
default: null
},
isDrag: {
type: Boolean,
+10 -1
View File
@@ -206,12 +206,13 @@ export default {
}
},
computed: {
fieldNames: FieldType.computed.fieldNames,
defaultName () {
const typeIndex = this.template.fields.filter((f) => f.type === this.field.type).indexOf(this.field)
const suffix = { multiple: 'Select', radio: 'Group' }[this.field.type] || 'Field'
return `${this.$t(this.field.type)} ${suffix} ${typeIndex + 1}`
return `${this.fieldNames[this.field.type]} ${suffix} ${typeIndex + 1}`
},
areas () {
return this.field.areas || []
@@ -241,6 +242,14 @@ export default {
if (['radio', 'multiple', 'select'].includes(this.field.type)) {
this.field.options ||= ['']
}
(this.field.areas || []).forEach((area) => {
if (this.field.type === 'cells') {
area.cell_w = area.w * 2 / Math.floor(area.w / area.h)
} else {
delete area.cell_w
}
})
},
onNameBlur (e) {
if (e.target.innerText.trim()) {
+18 -3
View File
@@ -2,7 +2,7 @@
<span class="dropdown">
<label
tabindex="0"
:title="$t(modelValue)"
:title="fieldNames[modelValue]"
class="cursor-pointer"
>
<component
@@ -33,7 +33,7 @@
:stroke-width="1.6"
:width="20"
/>
{{ $t(type) }}
{{ fieldNames[type] }}
</a>
</li>
</ul>
@@ -41,7 +41,7 @@
</template>
<script>
import { IconTextSize, IconWritingSign, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot, IconChecks } from '@tabler/icons-vue'
import { IconTextSize, IconWritingSign, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot, IconChecks, IconColumns3 } from '@tabler/icons-vue'
export default {
name: 'FiledTypeDropdown',
@@ -68,6 +68,20 @@ export default {
},
emits: ['update:model-value'],
computed: {
fieldNames () {
return {
text: 'Text',
signature: 'Signature',
date: 'Date',
image: 'Image',
file: 'File',
select: 'Select',
checkbox: 'Checkbox',
multiple: 'Multiple',
radio: 'Radio',
cells: 'Cells'
}
},
fieldIcons () {
return {
text: IconTextSize,
@@ -77,6 +91,7 @@ export default {
file: IconPaperclip,
select: IconSelect,
checkbox: IconCheckbox,
cells: IconColumns3,
multiple: IconChecks,
radio: IconCircleDot
}
+2 -1
View File
@@ -62,7 +62,7 @@
<div class="flex items-center flex-col px-2 py-2">
<component :is="icon" />
<span class="text-xs mt-1">
{{ $t(type) }}
{{ fieldNames[type] }}
</span>
</div>
</button>
@@ -98,6 +98,7 @@ export default {
},
emits: ['set-draw', 'set-drag', 'drag-end', 'scroll-to-area', 'change-submitter'],
computed: {
fieldNames: FieldType.computed.fieldNames,
fieldIcons: FieldType.computed.fieldIcons,
submitterFields () {
return this.fields.filter((f) => f.submitter_uuid === this.selectedSubmitter.uuid)
-11
View File
@@ -1,11 +0,0 @@
export default {
text: 'Text',
signature: 'Signature',
date: 'Date',
image: 'Image',
file: 'File',
select: 'Select',
checkbox: 'Checkbox',
multiple: 'Multiple',
radio: 'Radio'
}
+30 -13
View File
@@ -18,24 +18,25 @@
:ref="setAreaRefs"
:area="item.area"
:field="item.field"
@start-resize="[showMask = true, isResize = true]"
@stop-resize="[showMask = false, isResize = false]"
@start-drag="[showMask = true, isMove = true]"
@stop-drag="[showMask = false, isMove = false]"
@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"
:field="{ submitter_uuid: selectedSubmitter.uuid }"
:is-draw="true"
:field="{ submitter_uuid: selectedSubmitter.uuid, type: drawField?.type || 'text' }"
:area="newArea"
/>
</div>
<div
v-show="isDrag || showMask"
v-show="resizeDirection || isMove || isDrag || showMask"
id="mask"
ref="mask"
class="top-0 bottom-0 left-0 right-0 absolute z-10"
:class="{ 'cursor-grab': isDrag || isMove, 'cursor-nwse-resize': isResize || isDraw }"
:class="{ 'cursor-grab': isDrag || isMove, 'cursor-nwse-resize': drawField, [resizeDirectionClasses[resizeDirection]]: !!resizeDirectionClasses }"
@pointermove="onPointermove"
@dragover.prevent
@drop="onDrop"
@@ -66,10 +67,10 @@ export default {
type: Object,
required: true
},
isDraw: {
type: Boolean,
drawField: {
type: Object,
required: false,
default: false
default: null
},
isDrag: {
type: Boolean,
@@ -87,11 +88,17 @@ export default {
areaRefs: [],
showMask: false,
isMove: false,
isResize: false,
resizeDirection: null,
newArea: null
}
},
computed: {
resizeDirectionClasses () {
return {
nwse: 'cursor-nwse-resize',
ew: 'cursor-ew-resize'
}
},
width () {
return this.image.metadata.width
},
@@ -148,19 +155,29 @@ export default {
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) {
this.$emit('draw', {
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