add ability to draw areas for radio options

pull/142/head
Pete Matsyburka 2 years ago
parent 9aa68fe955
commit 333a5d6b7b

@ -5,7 +5,7 @@
:class="{ 'text-[1.5vw] lg:text-base': !textOverflowChars, 'text-[1.0vw] lg:text-xs': textOverflowChars, 'cursor-default': !submittable, 'bg-red-100 border cursor-pointer ': submittable, 'border-red-100': !isActive && submittable, 'bg-opacity-70': !isActive && !isValueSet && submittable, 'border-red-500 border-dashed z-10': isActive && submittable, 'bg-opacity-30': (isActive || isValueSet) && submittable }" :class="{ 'text-[1.5vw] lg:text-base': !textOverflowChars, 'text-[1.0vw] lg:text-xs': textOverflowChars, 'cursor-default': !submittable, 'bg-red-100 border cursor-pointer ': submittable, 'border-red-100': !isActive && submittable, 'bg-opacity-70': !isActive && !isValueSet && submittable, 'border-red-500 border-dashed z-10': isActive && submittable, 'bg-opacity-30': (isActive || isValueSet) && submittable }"
> >
<div <div
v-if="!isActive && !isValueSet && field.type !== 'checkbox' && submittable" v-if="!isActive && !isValueSet && field.type !== 'checkbox' && submittable && !area.option_uuid"
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
@ -21,7 +21,7 @@
</span> </span>
</div> </div>
<div <div
v-if="isActive && withLabel" v-if="isActive && withLabel && !area.option_uuid"
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] }}
@ -87,6 +87,44 @@
:class="{ '!w-auto !h-full': area.w > area.h, '!w-full !h-auto': area.w <= area.h }" :class="{ '!w-auto !h-full': area.w > area.h, '!w-full !h-auto': area.w <= area.h }"
/> />
</div> </div>
<div
v-else-if="field.type === 'radio' && area.option_uuid"
class="w-full p-[0.2vw] flex items-center justify-center"
>
<input
v-if="submittable"
type="radio"
:value="false"
class="aspect-square base-radio"
:class="{ '!w-auto !h-full': area.w > area.h, '!w-full !h-auto': area.w <= area.h }"
:checked="modelValue && modelValue === field.options.find((o) => o.uuid === area.option_uuid)?.value"
@click="$emit('update:model-value', field.options.find((o) => o.uuid === area.option_uuid)?.value)"
>
<IconCheck
v-else-if="modelValue && modelValue === field.options.find((o) => o.uuid === area.option_uuid)?.value"
class="aspect-square"
:class="{ '!w-auto !h-full': area.w > area.h, '!w-full !h-auto': area.w <= area.h }"
/>
</div>
<div
v-else-if="field.type === 'multiple' && area.option_uuid"
class="w-full p-[0.2vw] flex items-center justify-center"
>
<input
v-if="submittable"
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 && modelValue.includes(field.options.find((o) => o.uuid === area.option_uuid)?.value)"
@change="updateMultipleSelectValue(field.options.find((o) => o.uuid === area.option_uuid)?.value)"
>
<IconCheck
v-else-if="modelValue && modelValue.includes(field.options.find((o) => o.uuid === area.option_uuid)?.value)"
class="aspect-square"
:class="{ '!w-auto !h-full': area.w > area.h, '!w-full !h-auto': area.w <= area.h }"
/>
</div>
<div <div
v-else-if="field.type === 'cells'" v-else-if="field.type === 'cells'"
class="w-full flex items-center" class="w-full flex items-center"
@ -271,6 +309,23 @@ export default {
this.textOverflowChars = this.$refs.textContainer.scrollHeight > this.$refs.textContainer.clientHeight ? this.modelValue.length : 0 this.textOverflowChars = this.$refs.textContainer.scrollHeight > this.$refs.textContainer.clientHeight ? this.modelValue.length : 0
}) })
} }
},
methods: {
updateMultipleSelectValue (value) {
if (this.modelValue?.includes(value)) {
const newValue = [...this.modelValue]
newValue.splice(newValue.indexOf(value), 1)
this.$emit('update:model-value', newValue)
} else {
const newValue = this.modelValue ? [...this.modelValue] : []
newValue.push(value)
this.$emit('update:model-value', newValue)
}
}
} }
} }
</script> </script>

@ -66,7 +66,7 @@
@keydown.enter.prevent="onNameEnter" @keydown.enter.prevent="onNameEnter"
@focus="onNameFocus" @focus="onNameFocus"
@blur="onNameBlur" @blur="onNameBlur"
>{{ field.name || defaultName }}</span> >{{ optionIndexText }} {{ field.name || defaultName }}</span>
<div <div
v-if="isNameFocus && !['checkbox', 'phone'].includes(field.type)" v-if="isNameFocus && !['checkbox', 'phone'].includes(field.type)"
class="flex items-center ml-1.5" class="flex items-center ml-1.5"
@ -185,6 +185,13 @@ export default {
defaultName: Field.computed.defaultName, defaultName: Field.computed.defaultName,
fieldNames: FieldType.computed.fieldNames, fieldNames: FieldType.computed.fieldNames,
fieldIcons: FieldType.computed.fieldIcons, fieldIcons: FieldType.computed.fieldIcons,
optionIndexText () {
if (this.area.option_uuid && this.field.options) {
return `${this.field.options.findIndex((o) => o.uuid === this.area.option_uuid) + 1}.`
} else {
return ''
}
},
cells () { cells () {
const cells = [] const cells = []

@ -168,7 +168,7 @@
:selected-submitter="selectedSubmitter" :selected-submitter="selectedSubmitter"
class="md:hidden" class="md:hidden"
:editable="editable" :editable="editable"
@cancel="drawField = null" @cancel="[drawField = null, drawOption = null]"
@change-submitter="[selectedSubmitter = $event, drawField.submitter_uuid = $event.uuid]" @change-submitter="[selectedSubmitter = $event, drawField.submitter_uuid = $event.uuid]"
/> />
<FieldType <FieldType
@ -209,7 +209,7 @@
<p> <p>
<button <button
class="base-button" class="base-button"
@click="drawField = null" @click="[drawField = null, drawOption = null]"
> >
Cancel Cancel
</button> </button>
@ -225,7 +225,7 @@
:default-fields="defaultFields" :default-fields="defaultFields"
:with-sticky-submitters="withStickySubmitters" :with-sticky-submitters="withStickySubmitters"
:editable="editable" :editable="editable"
@set-draw="drawField = $event" @set-draw="[drawField = $event.field, drawOption = $event.option]"
@set-drag="dragField = $event" @set-drag="dragField = $event"
@change-submitter="selectedSubmitter = $event" @change-submitter="selectedSubmitter = $event"
@drag-end="dragField = null" @drag-end="dragField = null"
@ -354,6 +354,7 @@ export default {
isSaving: false, isSaving: false,
selectedSubmitter: null, selectedSubmitter: null,
drawField: null, drawField: null,
drawOption: null,
dragField: null dragField: null
} }
}, },
@ -433,6 +434,7 @@ export default {
} }
this.drawField = field this.drawField = field
this.drawOption = null
}, },
undo () { undo () {
if (this.undoStack.length > 1) { if (this.undoStack.length > 1) {
@ -482,6 +484,7 @@ export default {
onKeyUp (e) { onKeyUp (e) {
if (e.code === 'Escape') { if (e.code === 'Escape') {
this.drawField = null this.drawField = null
this.drawOption = null
this.selectedAreaRef.value = null this.selectedAreaRef.value = null
} }
@ -528,6 +531,16 @@ export default {
}, },
onDraw (area) { onDraw (area) {
if (this.drawField) { if (this.drawField) {
if (this.drawOption) {
const areaWithoutOption = this.drawField.areas?.find((a) => !a.option_uuid)
if (areaWithoutOption && !this.drawField.areas.find((a) => a.option_uuid === this.drawField.options[0].uuid)) {
areaWithoutOption.option_uuid = this.drawField.options[0].uuid
}
area.option_uuid = this.drawOption.uuid
}
this.drawField.areas ||= [] this.drawField.areas ||= []
this.drawField.areas.push(area) this.drawField.areas.push(area)
@ -536,6 +549,7 @@ export default {
} }
this.drawField = null this.drawField = null
this.drawOption = null
this.selectedAreaRef.value = area this.selectedAreaRef.value = area

@ -58,7 +58,7 @@
v-if="field && !field.areas.length" v-if="field && !field.areas.length"
title="Draw" title="Draw"
class="relative cursor-pointer text-transparent group-hover:text-base-content" class="relative cursor-pointer text-transparent group-hover:text-base-content"
@click="$emit('set-draw', field)" @click="$emit('set-draw', { field })"
> >
<IconNewSection <IconNewSection
:width="18" :width="18"
@ -151,11 +151,11 @@
Page {{ area.page + 1 }} Page {{ area.page + 1 }}
</a> </a>
</li> </li>
<li> <li v-if="!field.areas?.length || !['radio', 'multiple'].includes(field.type)">
<a <a
href="#" href="#"
class="text-sm py-1 px-2" class="text-sm py-1 px-2"
@click.prevent="$emit('set-draw', field)" @click.prevent="$emit('set-draw', { field })"
> >
<IconNewSection <IconNewSection
:width="20" :width="20"
@ -205,16 +205,39 @@
<span class="text-sm w-3.5"> <span class="text-sm w-3.5">
{{ index + 1 }}. {{ index + 1 }}.
</span> </span>
<div
v-if="['radio', 'multiple'].includes(field.type) && (index > 0 || field.areas.find((a) => a.option_uuid)) && !field.areas.find((a) => a.option_uuid === option.uuid)"
class="items-center flex w-full"
>
<input
v-model="option.value"
class="w-full input input-primary input-xs text-sm bg-transparent !pr-7 -mr-6"
type="text"
required
@blur="save"
>
<button
title="Draw"
@click.prevent="$emit('set-draw', { field, option })"
>
<IconNewSection
:width="18"
:stroke-width="1.6"
/>
</button>
</div>
<input <input
v-else
v-model="option.value" v-model="option.value"
class="w-full input input-primary input-xs text-sm bg-transparent" class="w-full input input-primary input-xs text-sm bg-transparent"
type="text" type="text"
required required
@focus="maybeFocusOnOptionArea(option)"
@blur="save" @blur="save"
> >
<button <button
class="text-sm w-3.5" class="text-sm w-3.5"
@click="[field.options.splice(index, 1), save()]" @click="removeOption(option)"
> >
&times; &times;
</button> </button>
@ -248,7 +271,7 @@ export default {
IconCopy, IconCopy,
FieldType FieldType
}, },
inject: ['template', 'save', 'backgroundColor'], inject: ['template', 'save', 'backgroundColor', 'selectedAreaRef'],
props: { props: {
field: { field: {
type: Object, type: Object,
@ -306,6 +329,13 @@ export default {
}, 1) }, 1)
} }
}, },
maybeFocusOnOptionArea (option) {
const area = this.field.areas.find((a) => a.option_uuid === option.uuid)
if (area) {
this.selectedAreaRef.value = area
}
},
scrollToFirstArea () { scrollToFirstArea () {
return this.field.areas?.[0] && this.$emit('scroll-to', this.field.areas[0]) return this.field.areas?.[0] && this.$emit('scroll-to', this.field.areas[0])
}, },
@ -317,6 +347,12 @@ export default {
this.save() this.save()
}, },
removeOption (option) {
this.field.options.splice(this.field.options.indexOf(option), 1)
this.field.areas.splice(this.field.areas.findIndex((a) => a.option_uuid === option.uuid), 1)
this.save()
},
maybeUpdateOptions () { maybeUpdateOptions () {
delete this.field.default_value delete this.field.default_value

@ -11,9 +11,16 @@
<% end %> <% end %>
</div> </div>
<% elsif field['type'] == 'checkbox' %> <% elsif field['type'] == 'checkbox' %>
<div class="w-full p-[0.2vw] flex items-center justify-center"> <div class="w-full flex items-center justify-center">
<%= svg_icon('check', class: "aspect-square #{area['w'] > area['h'] ? '!w-auto !h-full' : '!w-full !h-auto'}") %> <%= svg_icon('check', class: "aspect-square #{area['w'] > area['h'] ? '!w-auto !h-full' : '!w-full !h-auto'}") %>
</div> </div>
<% elsif field['type'].in?(%w[multiple radio]) && area['option_uuid'] %>
<% option = field['options']&.find { |o| o['uuid'] == area['option_uuid'] } %>
<% if option && Array.wrap(value).include?(option['value']) %>
<div class="w-full flex items-center justify-center">
<%= svg_icon('check', class: "aspect-square #{area['w'] > area['h'] ? '!w-auto !h-full' : '!w-full !h-auto'}") %>
</div>
<% end %>
<% elsif field['type'] == 'cells' %> <% elsif field['type'] == 'cells' %>
<% cell_width = area['cell_w'] / area['w'] * 100 %> <% cell_width = area['cell_w'] / area['w'] * 100 %>
<div class="w-full flex items-center"> <div class="w-full flex items-center">

@ -124,7 +124,13 @@ module Submissions
layouter.fit(items, area['w'] * width, height_diff.positive? ? box_height : area['h'] * height) layouter.fit(items, area['w'] * width, height_diff.positive? ? box_height : area['h'] * height)
.draw(canvas, (area['x'] * width) + TEXT_LEFT_MARGIN, .draw(canvas, (area['x'] * width) + TEXT_LEFT_MARGIN,
height - (area['y'] * height) + height_diff - TEXT_TOP_MARGIN) height - (area['y'] * height) + height_diff - TEXT_TOP_MARGIN)
when 'checkbox' when ->(type) { type == 'checkbox' || (type.in?(%w[multiple radio]) && area['option_uuid'].present?) }
if field['type'].in?(%w[multiple radio])
option = field['options']&.find { |o| o['uuid'] == area['option_uuid'] }
value = Array.wrap(value).include?(option['value'])
end
next unless value == true next unless value == true
scale = [(area['w'] * width) / PdfIcons::WIDTH, (area['h'] * height) / PdfIcons::HEIGHT].min scale = [(area['w'] * width) / PdfIcons::WIDTH, (area['h'] * height) / PdfIcons::HEIGHT].min

Loading…
Cancel
Save