group form checkboxes into single step
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex cursor-pointer bg-red-100 absolute border"
|
class="flex cursor-pointer bg-red-100 absolute border text-[1.5vw] lg:text-base"
|
||||||
:style="computedStyle"
|
:style="computedStyle"
|
||||||
:class="{ 'border-red-100 bg-opacity-70': !isActive, 'border-red-500 border-dashed bg-opacity-30 z-10': isActive }"
|
:class="{ 'border-red-100': !isActive, 'bg-opacity-70': !isActive && !isValue, 'border-red-500 border-dashed z-10': isActive, 'bg-opacity-30': isActive || isValue }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="!isActive && !modelValue"
|
v-if="!isActive && !isValue && field.type !== 'checkbox'"
|
||||||
class="absolute top-0 bottom-0 right-0 left-0 items-center justify-center h-full w-full"
|
class="absolute top-0 bottom-0 right-0 left-0 items-center justify-center h-full w-full"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -25,6 +25,9 @@
|
|||||||
class="absolute -top-7 rounded bg-base-content text-base-100 px-2 text-sm whitespace-nowrap"
|
class="absolute -top-7 rounded bg-base-content text-base-100 px-2 text-sm whitespace-nowrap"
|
||||||
>
|
>
|
||||||
{{ field.name || fieldNames[field.type] }}
|
{{ field.name || fieldNames[field.type] }}
|
||||||
|
<template v-if="field.type === 'checkbox'">
|
||||||
|
{{ fieldIndex + 1 }}
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="isActive"
|
v-if="isActive"
|
||||||
@@ -33,39 +36,83 @@
|
|||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
v-if="field.type === 'image' && image"
|
v-if="field.type === 'image' && image"
|
||||||
class="object-contain"
|
class="object-contain mx-auto"
|
||||||
:src="image.url"
|
:src="image.url"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-else-if="field.type === 'signature' && signature"
|
v-else-if="field.type === 'signature' && signature"
|
||||||
class="object-contain"
|
class="object-contain mx-auto"
|
||||||
:src="signature.url"
|
:src="signature.url"
|
||||||
>
|
>
|
||||||
<div v-else-if="field.type === 'attachment'">
|
<div
|
||||||
|
v-else-if="field.type === 'file'"
|
||||||
|
class="px-0.5 flex items-center"
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
v-for="(attachment, index) in attachments"
|
v-for="(attachment, index) in attachments"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
target="_blank"
|
||||||
:href="attachment.url"
|
:href="attachment.url"
|
||||||
>
|
>
|
||||||
|
<IconPaperclip
|
||||||
|
class="inline w-[1.5vw] h-[1.5vw] lg:w-4 lg:h-4"
|
||||||
|
/>
|
||||||
{{ attachment.filename }}
|
{{ attachment.filename }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<span v-else>
|
<div
|
||||||
{{ modelValue }}
|
v-else-if="field.type === 'checkbox'"
|
||||||
</span>
|
class="w-full p-[0.2vw] flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:value="false"
|
||||||
|
class="aspect-square base-checkbox"
|
||||||
|
:class="{ '!w-auto !h-full': area.w > area.h, '!w-full !h-auto': area.w <= area.h }"
|
||||||
|
:checked="!!modelValue"
|
||||||
|
@click="$emit('update:model-value', !modelValue)"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex items-center px-0.5"
|
||||||
|
>
|
||||||
|
<span v-if="Array.isArray(modelValue)">
|
||||||
|
{{ modelValue.join(', ') }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="field.type === 'date'">
|
||||||
|
{{ formattedDate }}
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ modelValue }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { IconTextSize, IconWriting, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot } from '@tabler/icons-vue'
|
import { IconTextSize, IconWriting, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot, IconChecks } from '@tabler/icons-vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FieldArea',
|
name: 'FieldArea',
|
||||||
|
components: {
|
||||||
|
IconPaperclip
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
field: {
|
field: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
step: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
values: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: [Array, String, Number, Object, Boolean],
|
type: [Array, String, Number, Object, Boolean],
|
||||||
required: false,
|
required: false,
|
||||||
@@ -76,6 +123,11 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
fieldIndex: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
attachmentsIndex: {
|
attachmentsIndex: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: false,
|
required: false,
|
||||||
@@ -97,9 +149,13 @@ export default {
|
|||||||
file: 'File',
|
file: 'File',
|
||||||
select: 'Select',
|
select: 'Select',
|
||||||
checkbox: 'Checkbox',
|
checkbox: 'Checkbox',
|
||||||
radio: 'Radio'
|
radio: 'Radio',
|
||||||
|
multiple: 'Multiple Select'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isValue () {
|
||||||
|
return this.step.some((f) => ![null, undefined, ''].includes(this.values[f.uuid]))
|
||||||
|
},
|
||||||
fieldIcons () {
|
fieldIcons () {
|
||||||
return {
|
return {
|
||||||
text: IconTextSize,
|
text: IconTextSize,
|
||||||
@@ -109,7 +165,8 @@ export default {
|
|||||||
file: IconPaperclip,
|
file: IconPaperclip,
|
||||||
select: IconSelect,
|
select: IconSelect,
|
||||||
checkbox: IconCheckbox,
|
checkbox: IconCheckbox,
|
||||||
radio: IconCircleDot
|
radio: IconCircleDot,
|
||||||
|
multiple: IconChecks
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
image () {
|
image () {
|
||||||
@@ -126,8 +183,15 @@ export default {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
formattedDate () {
|
||||||
|
if (this.field.type === 'date' && this.modelValue) {
|
||||||
|
return new Intl.DateTimeFormat({ year: 'numeric', month: 'numeric', day: 'numeric' }).format(new Date(this.modelValue))
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
attachments () {
|
attachments () {
|
||||||
if (this.field.type === 'attachment') {
|
if (this.field.type === 'file') {
|
||||||
return (this.modelValue || []).map((uuid) => this.attachmentsIndex[uuid])
|
return (this.modelValue || []).map((uuid) => this.attachmentsIndex[uuid])
|
||||||
} else {
|
} else {
|
||||||
return []
|
return []
|
||||||
|
|||||||
@@ -1,23 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<template
|
<template
|
||||||
v-for="field in fields"
|
v-for="step in steps"
|
||||||
:key="field.uuid"
|
:key="step[0].uuid"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-for="(area, index) in field.areas"
|
v-for="(field, fieldIndex) in step"
|
||||||
:key="index"
|
:key="field.uuid"
|
||||||
>
|
>
|
||||||
<Teleport :to="`#page-${area.attachment_uuid}-${area.page}`">
|
<template
|
||||||
<FieldArea
|
v-for="(area, areaIndex) in field.areas"
|
||||||
:ref="setAreaRef"
|
:key="areaIndex"
|
||||||
v-model="values[field.uuid]"
|
>
|
||||||
:field="field"
|
<Teleport :to="`#page-${area.attachment_uuid}-${area.page}`">
|
||||||
:area="area"
|
<FieldArea
|
||||||
:is-active="currentField === field"
|
:ref="setAreaRef"
|
||||||
:attachments-index="attachmentsIndex"
|
v-model="values[field.uuid]"
|
||||||
@click="$emit('focus-field', field)"
|
:field="field"
|
||||||
/>
|
:values="values"
|
||||||
</Teleport>
|
:area="area"
|
||||||
|
:field-index="fieldIndex"
|
||||||
|
:step="step"
|
||||||
|
:is-active="currentStep === step"
|
||||||
|
:attachments-index="attachmentsIndex"
|
||||||
|
@click="$emit('focus-step', step)"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
@@ -31,7 +39,7 @@ export default {
|
|||||||
FieldArea
|
FieldArea
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
fields: {
|
steps: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: false,
|
required: false,
|
||||||
default: () => []
|
default: () => []
|
||||||
@@ -46,13 +54,13 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
},
|
},
|
||||||
currentField: {
|
currentStep: {
|
||||||
type: Object,
|
type: Array,
|
||||||
required: false,
|
required: false,
|
||||||
default: () => ({})
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['focus-field'],
|
emits: ['focus-step'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
areaRefs: []
|
areaRefs: []
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<FieldAreas
|
<FieldAreas
|
||||||
ref="areas"
|
ref="areas"
|
||||||
:fields="submitterFields"
|
:steps="stepFields"
|
||||||
:values="values"
|
:values="values"
|
||||||
:attachments-index="attachmentsIndex"
|
:attachments-index="attachmentsIndex"
|
||||||
:current-field="currentField"
|
:current-step="currentStepFields"
|
||||||
@focus-field="goToField($event, false, true)"
|
@focus-step="goToStep($event, false, true)"
|
||||||
/>
|
/>
|
||||||
<form
|
<form
|
||||||
v-if="!isCompleted"
|
v-if="!isCompleted"
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
:value="authenticityToken"
|
:value="authenticityToken"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
v-if="currentStep === submitterFields.length - 1"
|
v-if="currentStep === stepFields.length - 1"
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name="completed"
|
name="completed"
|
||||||
value="true"
|
value="true"
|
||||||
@@ -42,12 +42,12 @@
|
|||||||
<input
|
<input
|
||||||
:id="currentField.uuid"
|
:id="currentField.uuid"
|
||||||
v-model="values[currentField.uuid]"
|
v-model="values[currentField.uuid]"
|
||||||
autofocus
|
|
||||||
class="base-input !text-2xl w-full"
|
class="base-input !text-2xl w-full"
|
||||||
:required="currentField.required"
|
:required="currentField.required"
|
||||||
placeholder="Type here..."
|
placeholder="Type here..."
|
||||||
type="text"
|
type="text"
|
||||||
:name="`values[${currentField.uuid}]`"
|
:name="`values[${currentField.uuid}]`"
|
||||||
|
@focus="$refs.areas.scrollIntoField(currentField)"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -57,15 +57,15 @@
|
|||||||
:for="currentField.uuid"
|
:for="currentField.uuid"
|
||||||
class="label text-2xl mb-2"
|
class="label text-2xl mb-2"
|
||||||
>{{ currentField.name }}</label>
|
>{{ currentField.name }}</label>
|
||||||
<div>
|
<div class="text-center">
|
||||||
<input
|
<input
|
||||||
:id="currentField.uuid"
|
:id="currentField.uuid"
|
||||||
v-model="values[currentField.uuid]"
|
v-model="values[currentField.uuid]"
|
||||||
class="base-input !text-2xl w-full text-center"
|
class="base-input !text-2xl text-center w-full"
|
||||||
autofocus
|
|
||||||
:required="currentField.required"
|
:required="currentField.required"
|
||||||
type="date"
|
type="date"
|
||||||
:name="`values[${currentField.uuid}]`"
|
:name="`values[${currentField.uuid}]`"
|
||||||
|
@focus="$refs.areas.scrollIntoField(currentField)"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -81,6 +81,7 @@
|
|||||||
class="select base-input !text-2xl w-full text-center font-normal"
|
class="select base-input !text-2xl w-full text-center font-normal"
|
||||||
:name="`values[${currentField.uuid}]`"
|
:name="`values[${currentField.uuid}]`"
|
||||||
@change="values[currentField.uuid] = $event.target.value"
|
@change="values[currentField.uuid] = $event.target.value"
|
||||||
|
@focus="$refs.areas.scrollIntoField(currentField)"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
value=""
|
value=""
|
||||||
@@ -98,63 +99,73 @@
|
|||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="currentField.type === 'radio' && currentField.options?.length">
|
<div v-else-if="currentField.type === 'radio'">
|
||||||
<label
|
<label
|
||||||
v-if="currentField.name"
|
v-if="currentField.name"
|
||||||
:for="currentField.uuid"
|
:for="currentField.uuid"
|
||||||
class="label text-2xl mb-2"
|
class="label text-2xl mb-2"
|
||||||
>{{ currentField.name }}</label>
|
>{{ currentField.name }}</label>
|
||||||
<div class="space-y-3.5">
|
<div class="flex w-full">
|
||||||
<div
|
<div class="space-y-3.5 mx-auto">
|
||||||
v-for="(option, index) in currentField.options"
|
<div
|
||||||
:key="index"
|
v-for="(option, index) in currentField.options"
|
||||||
>
|
:key="index"
|
||||||
<label
|
|
||||||
:for="currentField.uuid + option"
|
|
||||||
class="flex items-center space-x-3"
|
|
||||||
>
|
>
|
||||||
<input
|
<label
|
||||||
:id="currentField.uuid + option"
|
:for="currentField.uuid + option"
|
||||||
v-model="values[currentField.uuid]"
|
class="flex items-center space-x-3"
|
||||||
type="radio"
|
|
||||||
class="base-radio !h-7 !w-7"
|
|
||||||
:name="`values[${currentField.uuid}]`"
|
|
||||||
:value="option"
|
|
||||||
required
|
|
||||||
>
|
>
|
||||||
<span class="text-xl">
|
<input
|
||||||
{{ option }}
|
:id="currentField.uuid + option"
|
||||||
</span>
|
v-model="values[currentField.uuid]"
|
||||||
</label>
|
type="radio"
|
||||||
|
class="base-radio !h-7 !w-7"
|
||||||
|
:name="`values[${currentField.uuid}]`"
|
||||||
|
:value="option"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<span class="text-xl">
|
||||||
|
{{ option }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CheckboxStep
|
<MultiSelectStep
|
||||||
v-else-if="currentField.type === 'checkbox' && currentField.options?.length"
|
v-else-if="currentField.type === 'multiple'"
|
||||||
v-model="values[currentField.uuid]"
|
v-model="values[currentField.uuid]"
|
||||||
:field="currentField"
|
:field="currentField"
|
||||||
/>
|
/>
|
||||||
<div v-else-if="['radio', 'checkbox'].includes(currentField.type)">
|
<div
|
||||||
<div class="flex justify-center">
|
v-else-if="currentField.type === 'checkbox'"
|
||||||
<label
|
class="flex w-full"
|
||||||
:for="currentField.uuid"
|
>
|
||||||
class="flex items-center space-x-3"
|
<div
|
||||||
|
class="space-y-3.5 mx-auto"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(field, index) in currentStepFields"
|
||||||
|
:key="field.uuid"
|
||||||
>
|
>
|
||||||
<input
|
<label
|
||||||
:id="currentField.uuid"
|
:for="field.uuid"
|
||||||
:model-value="values[currentField.uuid]"
|
class="flex items-center space-x-3"
|
||||||
:type="currentField.type"
|
|
||||||
:name="`values[${currentField.uuid}]`"
|
|
||||||
:value="true"
|
|
||||||
class="!h-7 !w-7"
|
|
||||||
:class="{'base-radio' : currentField.type === 'radio', 'base-checkbox': currentField.type === 'checkbox'}"
|
|
||||||
:checked="!!values[currentField.uuid]"
|
|
||||||
@click="values[currentField.uuid] = !values[currentField.uuid]"
|
|
||||||
>
|
>
|
||||||
<span class="text-xl">
|
<input
|
||||||
{{ currentField.name || currentField.type }}
|
:id="field.uuid"
|
||||||
</span>
|
type="checkbox"
|
||||||
</label>
|
:name="`values[${field.uuid}]`"
|
||||||
|
:value="false"
|
||||||
|
class="base-checkbox !h-7 !w-7"
|
||||||
|
:checked="!!values[field.uuid]"
|
||||||
|
@click="values[field.uuid] = !values[field.uuid]"
|
||||||
|
>
|
||||||
|
<span class="text-xl">
|
||||||
|
{{ currentField.name || currentField.type + ' ' + (index + 1) }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ImageStep
|
<ImageStep
|
||||||
@@ -199,12 +210,12 @@
|
|||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="flex items-center mt-5 mb-1">
|
<div class="flex items-center mt-5 mb-1">
|
||||||
<a
|
<a
|
||||||
v-for="(field, index) in submitterFields"
|
v-for="(step, index) in stepFields"
|
||||||
:key="field.uuid"
|
:key="step[0].uuid"
|
||||||
href="#"
|
href="#"
|
||||||
class="inline border border-base-300 h-3 w-3 rounded-full mx-1"
|
class="inline border border-base-300 h-3 w-3 rounded-full mx-1"
|
||||||
:class="{ 'bg-base-200': index === currentStep, 'bg-base-content': index < currentStep, 'bg-white': index > currentStep }"
|
:class="{ 'bg-base-200': index === currentStep, 'bg-base-content': index < currentStep, 'bg-white': index > currentStep }"
|
||||||
@click.prevent="goToField(field, true)"
|
@click.prevent="goToStep(step, true)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -220,7 +231,7 @@ import FieldAreas from './areas'
|
|||||||
import ImageStep from './image_step'
|
import ImageStep from './image_step'
|
||||||
import SignatureStep from './signature_step'
|
import SignatureStep from './signature_step'
|
||||||
import AttachmentStep from './attachment_step'
|
import AttachmentStep from './attachment_step'
|
||||||
import CheckboxStep from './checkbox_step'
|
import MultiSelectStep from './multi_select_step'
|
||||||
import FormCompleted from './completed'
|
import FormCompleted from './completed'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -230,7 +241,7 @@ export default {
|
|||||||
ImageStep,
|
ImageStep,
|
||||||
SignatureStep,
|
SignatureStep,
|
||||||
AttachmentStep,
|
AttachmentStep,
|
||||||
CheckboxStep,
|
MultiSelectStep,
|
||||||
FormCompleted
|
FormCompleted
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@@ -270,12 +281,28 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
currentStepFields () {
|
||||||
|
return this.stepFields[this.currentStep]
|
||||||
|
},
|
||||||
currentField () {
|
currentField () {
|
||||||
return this.submitterFields[this.currentStep]
|
return this.currentStepFields[0]
|
||||||
},
|
},
|
||||||
submitterFields () {
|
submitterFields () {
|
||||||
return this.fields.filter((f) => f.submitter_uuid === this.submitterUuid)
|
return this.fields.filter((f) => f.submitter_uuid === this.submitterUuid)
|
||||||
},
|
},
|
||||||
|
stepFields () {
|
||||||
|
return this.submitterFields.reduce((acc, f) => {
|
||||||
|
const prevStep = acc[acc.length - 1]
|
||||||
|
|
||||||
|
if (f.type === 'checkbox' && Array.isArray(prevStep) && prevStep[0].type === 'checkbox') {
|
||||||
|
prevStep.push(f)
|
||||||
|
} else {
|
||||||
|
acc.push([f])
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
},
|
||||||
attachmentsIndex () {
|
attachmentsIndex () {
|
||||||
return this.attachments.reduce((acc, a) => {
|
return this.attachments.reduce((acc, a) => {
|
||||||
acc[a.uuid] = a
|
acc[a.uuid] = a
|
||||||
@@ -289,17 +316,17 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.currentStep = Math.min(
|
this.currentStep = Math.min(
|
||||||
this.submitterFields.indexOf([...this.submitterFields].reverse().find((field) => !!this.values[field.uuid])) + 1,
|
this.stepFields.indexOf([...this.stepFields].reverse().find((fields) => fields.some((f) => !!this.values[f.uuid]))) + 1,
|
||||||
this.submitterFields.length - 1
|
this.stepFields.length - 1
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goToField (field, scrollToArea = false, clickUpload = false) {
|
goToStep (step, scrollToArea = false, clickUpload = false) {
|
||||||
this.currentStep = this.submitterFields.indexOf(field)
|
this.currentStep = this.stepFields.indexOf(step)
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (scrollToArea) {
|
if (scrollToArea) {
|
||||||
this.$refs.areas.scrollIntoField(field)
|
this.$refs.areas.scrollIntoField(step[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$refs.form.querySelector('input[type="date"], input[type="text"], select')?.focus()
|
this.$refs.form.querySelector('input[type="date"], input[type="text"], select')?.focus()
|
||||||
@@ -322,10 +349,10 @@ export default {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: new FormData(this.$refs.form)
|
body: new FormData(this.$refs.form)
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
const nextField = this.submitterFields[this.currentStep + 1]
|
const nextStep = this.stepFields[this.currentStep + 1]
|
||||||
|
|
||||||
if (nextField) {
|
if (nextStep) {
|
||||||
this.goToField(this.submitterFields[this.currentStep + 1], true)
|
this.goToStep(this.stepFields[this.currentStep + 1], true)
|
||||||
} else {
|
} else {
|
||||||
this.isCompleted = true
|
this.isCompleted = true
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-22
@@ -4,36 +4,38 @@
|
|||||||
:for="field.uuid"
|
:for="field.uuid"
|
||||||
class="label text-2xl mb-2"
|
class="label text-2xl mb-2"
|
||||||
>{{ field.name }}</label>
|
>{{ field.name }}</label>
|
||||||
<div class="space-y-3.5">
|
<div class="flex w-full">
|
||||||
<div
|
<div class="space-y-3.5 mx-auto">
|
||||||
v-for="(option, index) in field.options"
|
<div
|
||||||
:key="index"
|
v-for="(option, index) in field.options"
|
||||||
>
|
:key="index"
|
||||||
<label
|
|
||||||
:for="field.uuid + option"
|
|
||||||
class="flex items-center space-x-3"
|
|
||||||
>
|
>
|
||||||
<input
|
<label
|
||||||
:id="field.uuid + option"
|
:for="field.uuid + option"
|
||||||
:ref="setInputRef"
|
class="flex items-center space-x-3"
|
||||||
type="checkbox"
|
|
||||||
:name="`values[${field.uuid}][]`"
|
|
||||||
:value="option"
|
|
||||||
class="base-checkbox !h-7 !w-7"
|
|
||||||
:checked="modelValue.includes(option)"
|
|
||||||
@change="onChange"
|
|
||||||
>
|
>
|
||||||
<span class="text-xl">
|
<input
|
||||||
{{ option }}
|
:id="field.uuid + option"
|
||||||
</span>
|
:ref="setInputRef"
|
||||||
</label>
|
type="checkbox"
|
||||||
|
:name="`values[${field.uuid}][]`"
|
||||||
|
:value="option"
|
||||||
|
class="base-checkbox !h-7 !w-7"
|
||||||
|
:checked="modelValue.includes(option)"
|
||||||
|
@change="onChange"
|
||||||
|
>
|
||||||
|
<span class="text-xl">
|
||||||
|
{{ option }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'SheckboxStep',
|
name: 'MultiSelectStep',
|
||||||
props: {
|
props: {
|
||||||
field: {
|
field: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
@click="selectedAreaRef.value = area"
|
@click="selectedAreaRef.value = area"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
v-if="withName"
|
v-if="field.type !== 'checkbox' || field.name"
|
||||||
ref="name"
|
ref="name"
|
||||||
contenteditable
|
contenteditable
|
||||||
class="pr-1 cursor-text outline-none block"
|
class="pr-1 cursor-text outline-none block"
|
||||||
@@ -138,9 +138,6 @@ export default {
|
|||||||
'bg-purple-100'
|
'bg-purple-100'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
withName () {
|
|
||||||
return !['checkbox', 'radio'].includes(this.field.type) || this.field.options
|
|
||||||
},
|
|
||||||
isSelected () {
|
isSelected () {
|
||||||
return this.selectedAreaRef.value === this.area
|
return this.selectedAreaRef.value === this.area
|
||||||
},
|
},
|
||||||
@@ -169,11 +166,11 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
maybeUpdateOptions () {
|
maybeUpdateOptions () {
|
||||||
if (!['radio', 'checkbox', 'select'].includes(this.field.type)) {
|
if (!['radio', 'multiple', 'select'].includes(this.field.type)) {
|
||||||
delete this.field.options
|
delete this.field.options
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.field.type === 'select') {
|
if (['select', 'multiple', 'radio'].includes(this.field.type)) {
|
||||||
this.field.options ||= ['']
|
this.field.options ||= ['']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ export default {
|
|||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.dragFieldType === 'select') {
|
if (['select', 'multiple', 'radio'].includes(this.dragFieldType)) {
|
||||||
field.options = ['']
|
field.options = ['']
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +311,7 @@ export default {
|
|||||||
} else if (previousField?.areas) {
|
} else if (previousField?.areas) {
|
||||||
baseArea = previousField.areas[previousField.areas.length - 1]
|
baseArea = previousField.areas[previousField.areas.length - 1]
|
||||||
} else {
|
} else {
|
||||||
if (['checkbox', 'radio'].includes(this.dragFieldType)) {
|
if (['checkbox'].includes(this.dragFieldType)) {
|
||||||
baseArea = {
|
baseArea = {
|
||||||
w: area.maskW / 30 / area.maskW,
|
w: area.maskW / 30 / area.maskW,
|
||||||
h: area.maskW / 30 / area.maskW * (area.maskW / area.maskH)
|
h: area.maskW / 30 / area.maskW * (area.maskW / area.maskH)
|
||||||
|
|||||||
@@ -182,7 +182,9 @@ export default {
|
|||||||
defaultName () {
|
defaultName () {
|
||||||
const typeIndex = this.template.fields.filter((f) => f.type === this.field.type).indexOf(this.field)
|
const typeIndex = this.template.fields.filter((f) => f.type === this.field.type).indexOf(this.field)
|
||||||
|
|
||||||
return `${this.$t(this.field.type)} Field ${typeIndex + 1}`
|
const suffix = { multiple: 'Select', radio: 'Group' }[this.field.type] || 'Field'
|
||||||
|
|
||||||
|
return `${this.$t(this.field.type)} ${suffix} ${typeIndex + 1}`
|
||||||
},
|
},
|
||||||
areas () {
|
areas () {
|
||||||
return this.field.areas || []
|
return this.field.areas || []
|
||||||
@@ -203,11 +205,11 @@ export default {
|
|||||||
document.activeElement.blur()
|
document.activeElement.blur()
|
||||||
},
|
},
|
||||||
maybeUpdateOptions () {
|
maybeUpdateOptions () {
|
||||||
if (!['radio', 'checkbox', 'select'].includes(this.field.type)) {
|
if (!['radio', 'multiple', 'select'].includes(this.field.type)) {
|
||||||
delete this.field.options
|
delete this.field.options
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.field.type === 'select') {
|
if (['radio', 'multiple', 'select'].includes(this.field.type)) {
|
||||||
this.field.options ||= ['']
|
this.field.options ||= ['']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { IconTextSize, IconWriting, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot } from '@tabler/icons-vue'
|
import { IconTextSize, IconWriting, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot, IconChecks } from '@tabler/icons-vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FiledTypeDropdown',
|
name: 'FiledTypeDropdown',
|
||||||
@@ -77,6 +77,7 @@ export default {
|
|||||||
file: IconPaperclip,
|
file: IconPaperclip,
|
||||||
select: IconSelect,
|
select: IconSelect,
|
||||||
checkbox: IconCheckbox,
|
checkbox: IconCheckbox,
|
||||||
|
multiple: IconChecks,
|
||||||
radio: IconCircleDot
|
radio: IconCircleDot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<FieldSubmitter
|
<div class="sticky -top-1 bg-base-100 pt-1 -mt-1 z-10">
|
||||||
:model-value="selectedSubmitter.uuid"
|
<FieldSubmitter
|
||||||
class="w-full"
|
:model-value="selectedSubmitter.uuid"
|
||||||
:submitters="submitters"
|
class="w-full bg-base-100"
|
||||||
@remove="removeSubmitter"
|
:submitters="submitters"
|
||||||
@update:model-value="$emit('change-submitter', submitters.find((s) => s.uuid === $event))"
|
@remove="removeSubmitter"
|
||||||
/>
|
@update:model-value="$emit('change-submitter', submitters.find((s) => s.uuid === $event))"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="mb-1 mt-2">
|
<div class="mb-1 mt-2">
|
||||||
<Field
|
<Field
|
||||||
v-for="field in submitterFields"
|
v-for="field in submitterFields"
|
||||||
@@ -19,7 +21,7 @@
|
|||||||
@set-draw="$emit('set-draw', $event)"
|
@set-draw="$emit('set-draw', $event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-3 gap-1">
|
<div class="grid grid-cols-3 gap-1 pb-2">
|
||||||
<button
|
<button
|
||||||
v-for="(icon, type) in fieldIcons"
|
v-for="(icon, type) in fieldIcons"
|
||||||
:key="type"
|
:key="type"
|
||||||
@@ -145,7 +147,7 @@ export default {
|
|||||||
type
|
type
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['select', 'checkbox', 'radio'].includes(type)) {
|
if (['select', 'multiple', 'radio'].includes(type)) {
|
||||||
field.options = ['']
|
field.options = ['']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ export default {
|
|||||||
file: 'File',
|
file: 'File',
|
||||||
select: 'Select',
|
select: 'Select',
|
||||||
checkbox: 'Checkbox',
|
checkbox: 'Checkbox',
|
||||||
|
multiple: 'Multiple',
|
||||||
radio: 'Radio'
|
radio: 'Radio'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<div class="text-center px-2">
|
<div class="text-center px-2">
|
||||||
Powered by
|
Powered by
|
||||||
<a href="https://www.docuseal.co" class="underline">DocuSeal</a> - An open source documents software
|
<a href="https://www.docuseal.co" class="underline">DocuSeal</a> - open source documents software
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="fixed bottom-0 w-full h-0 z-20">
|
<div class="fixed bottom-0 w-full h-0 z-20">
|
||||||
|
|||||||
Reference in New Issue
Block a user