set default value

pull/381/merge
Pete Matsyburka 2 months ago
parent 4685bac4b3
commit 719c1786f1

@ -111,6 +111,7 @@ module Api
:required, :readonly, :default_value, :required, :readonly, :default_value,
:title, :description, :prefillable, :title, :description, :prefillable,
{ preferences: {}, { preferences: {},
default_value: [],
conditions: [%i[field_uuid value action operation]], conditions: [%i[field_uuid value action operation]],
options: [%i[value uuid]], options: [%i[value uuid]],
validation: %i[message pattern min max step], validation: %i[message pattern min max step],

@ -122,6 +122,7 @@ class TemplatesController < ApplicationController
:required, :readonly, :default_value, :required, :readonly, :default_value,
:title, :description, :prefillable, :title, :description, :prefillable,
{ preferences: {}, { preferences: {},
default_value: [],
conditions: [%i[field_uuid value action operation]], conditions: [%i[field_uuid value action operation]],
options: [%i[value uuid]], options: [%i[value uuid]],
validation: %i[message pattern min max step], validation: %i[message pattern min max step],

@ -422,10 +422,14 @@ export default {
}, },
formattedDate () { formattedDate () {
if (this.field.type === 'date' && this.modelValue) { if (this.field.type === 'date' && this.modelValue) {
return this.formatDate( try {
this.modelValue === '{{date}}' ? new Date() : new Date(this.modelValue), return this.formatDate(
this.field.preferences?.format || (this.locale.endsWith('-US') ? 'MM/DD/YYYY' : 'DD/MM/YYYY') this.modelValue === '{{date}}' ? new Date() : new Date(this.modelValue),
) this.field.preferences?.format || (this.locale.endsWith('-US') ? 'MM/DD/YYYY' : 'DD/MM/YYYY')
)
} catch {
return this.modelValue
}
} else { } else {
return '' return ''
} }

@ -72,11 +72,11 @@
@blur="onNameBlur" @blur="onNameBlur"
>{{ optionIndexText }} {{ (defaultField ? (defaultField.title || field.title || field.name) : field.name) || defaultName }}</span> >{{ optionIndexText }} {{ (defaultField ? (defaultField.title || field.title || field.name) : field.name) || defaultName }}</span>
<div <div
v-if="isSettingsFocus || (isValueInput && field.type !== 'heading') || (isNameFocus && !['checkbox', 'phone'].includes(field.type))" v-if="isSettingsFocus || isSelectInput || (isValueInput && field.type !== 'heading') || (isNameFocus && !['checkbox', 'phone'].includes(field.type))"
class="flex items-center ml-1.5" class="flex items-center ml-1.5"
> >
<input <input
v-if="!isValueInput" v-if="!isValueInput && !isSelectInput"
:id="`required-checkbox-${field.uuid}`" :id="`required-checkbox-${field.uuid}`"
v-model="field.required" v-model="field.required"
type="checkbox" type="checkbox"
@ -84,14 +84,14 @@
@mousedown.prevent @mousedown.prevent
> >
<label <label
v-if="!isValueInput" v-if="!isValueInput && !isSelectInput"
:for="`required-checkbox-${field.uuid}`" :for="`required-checkbox-${field.uuid}`"
class="label text-xs" class="label text-xs"
@click.prevent="field.required = !field.required" @click.prevent="field.required = !field.required"
@mousedown.prevent @mousedown.prevent
>{{ t('required') }}</label> >{{ t('required') }}</label>
<input <input
v-if="isValueInput" v-if="isValueInput || isSelectInput"
:id="`readonly-checkbox-${field.uuid}`" :id="`readonly-checkbox-${field.uuid}`"
type="checkbox" type="checkbox"
class="checkbox checkbox-xs no-animation rounded" class="checkbox checkbox-xs no-animation rounded"
@ -100,7 +100,7 @@
@mousedown.prevent @mousedown.prevent
> >
<label <label
v-if="isValueInput" v-if="isValueInput || isSelectInput"
:for="`readonly-checkbox-${field.uuid}`" :for="`readonly-checkbox-${field.uuid}`"
class="label text-xs" class="label text-xs"
@click.prevent="field.readonly = !(field.readonly ?? true)" @click.prevent="field.readonly = !(field.readonly ?? true)"
@ -164,30 +164,39 @@
ref="touchValueTarget" ref="touchValueTarget"
class="flex h-full w-full field-area" class="flex h-full w-full field-area"
dir="auto" dir="auto"
:class="[isValueInput ? 'bg-opacity-50' : 'bg-opacity-80', field.type === 'heading' ? 'bg-gray-50' : bgColors[submitterIndex % bgColors.length], isDefaultValuePresent || isValueInput || (withFieldPlaceholder && field.areas) ? fontClasses : 'justify-center items-center']" :class="[isValueInput ? 'cursor-text' : '', isValueInput || isCheckboxInput || isSelectInput ? 'bg-opacity-50' : 'bg-opacity-80', field.type === 'heading' ? 'bg-gray-50' : bgColors[submitterIndex % bgColors.length], isDefaultValuePresent || isValueInput || (withFieldPlaceholder && field.areas) ? fontClasses : 'justify-center items-center']"
@click="focusValueInput" @click="focusValueInput"
> >
<span <span
v-if="field" v-if="field"
class="flex justify-center items-center space-x-1" class="flex justify-center items-center space-x-1"
:class="{ 'w-full': ['cells', 'checkbox'].includes(field.type), 'h-full': !isValueInput && !isDefaultValuePresent }" :class="{ 'w-full': isWFullType, 'h-full': !isValueInput && !isDefaultValuePresent }"
> >
<div <div
v-if="isDefaultValuePresent || isValueInput || (withFieldPlaceholder && field.areas && field.type !== 'checkbox')" v-if="isDefaultValuePresent || isValueInput || isSelectInput || (withFieldPlaceholder && field.areas && field.type !== 'checkbox')"
:class="{ 'w-full h-full': ['cells', 'checkbox'].includes(field.type) }" :class="{ 'w-full h-full': isWFullType }"
:style="fontStyle" :style="fontStyle"
> >
<div <div
ref="textContainer" ref="textContainer"
class="flex items-center px-0.5" class="flex items-center px-0.5"
:style="{ color: field.preferences?.color }" :style="{ color: field.preferences?.color }"
:class="{ 'w-full h-full': ['cells', 'checkbox'].includes(field.type) }" :class="{ 'w-full h-full': isWFullType }"
> >
<IconCheck <IconCheck
v-if="field.type == 'checkbox'" v-if="field.type == 'checkbox'"
class="aspect-square mx-auto" class="aspect-square mx-auto"
: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 }"
/> />
<template
v-else-if="(field.type === 'radio' || field.type === 'multiple') && field?.areas?.length > 1"
>
<IconCheck
v-if="field.type === 'multiple' ? field.default_value.includes(optionsUuidIndex[area.option_uuid].value) : optionsUuidIndex[area.option_uuid].value === field.default_value"
class="aspect-square mx-auto"
:class="{ '!w-auto !h-full': area.w > area.h, '!w-full !h-auto': area.w <= area.h }"
/>
</template>
<span <span
v-else-if="field.type === 'number' && !isValueInput && (field.default_value || field.default_value == 0)" v-else-if="field.type === 'number' && !isValueInput && (field.default_value || field.default_value == 0)"
class="whitespace-pre-wrap" class="whitespace-pre-wrap"
@ -210,14 +219,39 @@
{{ char }} {{ char }}
</div> </div>
</div> </div>
<select
v-else-if="isSelectInput"
ref="defaultValueSelect"
class="bg-transparent outline-none focus:outline-none w-full"
@change="[field.default_value = $event.target.value, field.readonly = !!field.default_value?.length, save()]"
@focus="selectedAreaRef.value = area"
@keydown.enter="onDefaultValueEnter"
>
<option
:disabled="!field.default_value?.length"
:selected="!field.default_value?.length"
:value="''"
>
{{ t(field.default_value?.length ? 'none' : 'select') }}
</option>
<option
v-for="(option, index) in field.options"
:key="index"
:selected="field.default_value === option.value"
:value="option.value"
>
{{ option.value }}
</option>
</select>
<span <span
v-else v-else
ref="defaultValue" ref="defaultValue"
:contenteditable="isValueInput" :contenteditable="isValueInput"
class="whitespace-pre-wrap outline-none empty:before:content-[attr(placeholder)] before:text-gray-400" class="whitespace-pre-wrap outline-none empty:before:content-[attr(placeholder)] before:text-base-content/30"
:class="{ 'cursor-text': isValueInput }" :class="{ 'cursor-text': isValueInput }"
:placeholder="withFieldPlaceholder && !isValueInput ? defaultField?.title || field.title || field.name || defaultName : t('type_value')" :placeholder="withFieldPlaceholder && !isValueInput ? defaultField?.title || field.title || field.name || defaultName : (field.type === 'date' ? field.preferences.format || t('type_value') : t('type_value'))"
@blur="onDefaultValueBlur" @blur="onDefaultValueBlur"
@focus="selectedAreaRef.value = area"
@paste.prevent="onPaste" @paste.prevent="onPaste"
@keydown.enter="onDefaultValueEnter" @keydown.enter="onDefaultValueEnter"
>{{ field.default_value }}</span> >{{ field.default_value }}</span>
@ -225,7 +259,7 @@
</div> </div>
<component <component
:is="fieldIcons[field.type]" :is="fieldIcons[field.type]"
v-else v-else-if="!isCheckboxInput"
width="100%" width="100%"
height="100%" height="100%"
class="max-h-10 opacity-50" class="max-h-10 opacity-50"
@ -233,12 +267,12 @@
</span> </span>
</div> </div>
<div <div
v-if="!isValueInput" v-if="!isValueInput && !isSelectInput"
ref="touchTarget" ref="touchTarget"
class="absolute top-0 bottom-0 right-0 left-0" class="absolute top-0 bottom-0 right-0 left-0"
:class="isDragged ? 'cursor-grab' : 'cursor-pointer'" :class="isDragged ? 'cursor-grab' : 'cursor-pointer'"
@dblclick="maybeToggleDefaultValue" @dblclick="maybeToggleDefaultValue"
@click="maybeToggleCheckboxValue" @click="inputMode && maybeToggleCheckboxValue()"
/> />
<span <span
v-if="field?.type && editable" v-if="field?.type && editable"
@ -398,6 +432,9 @@ export default {
fieldNames: FieldType.computed.fieldNames, fieldNames: FieldType.computed.fieldNames,
fieldLabels: FieldType.computed.fieldLabels, fieldLabels: FieldType.computed.fieldLabels,
fieldIcons: FieldType.computed.fieldIcons, fieldIcons: FieldType.computed.fieldIcons,
isWFullType () {
return ['cells', 'checkbox', 'radio', 'multiple', 'select'].includes(this.field.type)
},
fontStyle () { fontStyle () {
let fontSize = '' let fontSize = ''
@ -417,6 +454,13 @@ export default {
return { fontSize, lineHeight: `calc(${fontSize} * ${this.lineHeight})` } return { fontSize, lineHeight: `calc(${fontSize} * ${this.lineHeight})` }
}, },
optionsUuidIndex () {
return this.field.options.reduce((acc, option) => {
acc[option.uuid] = option
return acc
}, {})
},
fontSizePx () { fontSizePx () {
return parseInt(this.field?.preferences?.font_size || 11) * this.fontScale return parseInt(this.field?.preferences?.font_size || 11) * this.fontScale
}, },
@ -427,14 +471,17 @@ export default {
return 1040 / 612.0 return 1040 / 612.0
}, },
isDefaultValuePresent () { isDefaultValuePresent () {
if (this.field?.type === 'radio' && this.field?.areas?.length > 1) { return this.field?.default_value || this.field?.default_value === 0
return false },
} else { isSelectInput () {
return this.field?.default_value || this.field?.default_value === 0 return this.inputMode && (this.field.type === 'select' || (this.field.type === 'radio' && this.field.areas?.length < 2))
} },
isCheckboxInput () {
return this.inputMode && (this.field.type === 'checkbox' || (['radio', 'multiple'].includes(this.field.type) && this.area.option_uuid))
}, },
isValueInput () { isValueInput () {
return (this.field.type === 'heading' && this.isHeadingSelected) || this.isContenteditable || (this.inputMode && ['text', 'number', 'date'].includes(this.field.type)) return (this.field.type === 'heading' && this.isHeadingSelected) || this.isContenteditable ||
(this.inputMode && (['text', 'number'].includes(this.field.type) || (this.field.type === 'date' && this.field.default_value !== '{{date}}')))
}, },
modalContainerEl () { modalContainerEl () {
return this.$el.getRootNode().querySelector('#docuseal_modal_container') return this.$el.getRootNode().querySelector('#docuseal_modal_container')
@ -558,22 +605,45 @@ export default {
this.isContenteditable = true this.isContenteditable = true
this.focusValueInput() this.focusValueInput()
} else if (this.field.type === 'checkbox') {
this.field.readonly = !this.field.readonly
this.field.default_value === true ? delete this.field.default_value : this.field.default_value = true
this.save()
} else if (this.field.type === 'date') { } else if (this.field.type === 'date') {
this.field.readonly = !this.field.readonly this.field.readonly = !this.field.readonly
this.field.default_value === '{{date}}' ? delete this.field.default_value : this.field.default_value = '{{date}}' this.field.default_value === '{{date}}' ? delete this.field.default_value : this.field.default_value = '{{date}}'
this.save() this.save()
} else {
this.maybeToggleCheckboxValue()
} }
}, },
maybeToggleCheckboxValue () { maybeToggleCheckboxValue () {
if (this.inputMode && this.field.type === 'checkbox') { if (this.field.type === 'checkbox') {
this.field.readonly = !this.field.readonly
this.field.default_value === true ? delete this.field.default_value : this.field.default_value = true this.field.default_value === true ? delete this.field.default_value : this.field.default_value = true
this.field.readonly = this.field.default_value === true
this.save()
} else if (this.field.type === 'radio' && this.area.option_uuid) {
const option = this.optionsUuidIndex[this.area.option_uuid]
const value = option.value || `${this.t('option')} ${this.field.options.indexOf(option) + 1}`
this.field.default_value === value ? delete this.field.default_value : this.field.default_value = value
this.field.readonly = !!this.field.default_value?.length
this.save()
} else if (this.field.type === 'multiple' && this.area.option_uuid) {
const option = this.optionsUuidIndex[this.area.option_uuid]
const value = option.value || `${this.t('option')} ${this.field.options.indexOf(option) + 1}`
if (this.field.default_value?.includes(value)) {
this.field.default_value.splice(this.field.default_value.indexOf(value), 1)
if (!this.field.default_value?.length) delete this.field.default_value
} else {
Array.isArray(this.field.default_value) ? this.field.default_value.push(value) : this.field.default_value = [value]
}
this.field.readonly = !!this.field.default_value?.length
this.save() this.save()
} }
@ -749,7 +819,7 @@ export default {
} }
}, },
drag (e) { drag (e) {
if (e.target.id === 'mask') { if (e.target.id === 'mask' && this.editable) {
this.isDragged = true this.isDragged = true
this.area.x = (e.offsetX - this.dragFrom.x) / e.target.clientWidth this.area.x = (e.offsetX - this.dragFrom.x) / e.target.clientWidth
@ -765,7 +835,9 @@ export default {
e.preventDefault() e.preventDefault()
this.isDragged = true if (this.editable) {
this.isDragged = true
}
const rect = e.target.getBoundingClientRect() const rect = e.target.getBoundingClientRect()
@ -818,7 +890,9 @@ export default {
e.preventDefault() e.preventDefault()
this.isDragged = true if (this.editable) {
this.isDragged = true
}
const rect = e.target.getBoundingClientRect() const rect = e.target.getBoundingClientRect()

@ -1,7 +1,7 @@
<template> <template>
<div <div
class="relative select-none mb-4 before:border before:rounded before:top-0 before:bottom-0 before:left-0 before:right-0 before:absolute" class="relative select-none mb-4 before:border before:rounded before:top-0 before:bottom-0 before:left-0 before:right-0 before:absolute"
:class="{ 'cursor-crosshair': allowDraw, 'touch-none': !!drawField }" :class="{ 'cursor-crosshair': allowDraw && editable, 'touch-none': !!drawField }"
style="container-type: size" style="container-type: size"
:style="{ aspectRatio: `${width} / ${height}`}" :style="{ aspectRatio: `${width} / ${height}`}"
> >

@ -337,7 +337,7 @@ module Submitters
end end
def replace_default_variables(value, attrs, submission, with_time: false) def replace_default_variables(value, attrs, submission, with_time: false)
return value if value.in?([true, false]) || value.is_a?(Numeric) return value if value.in?([true, false]) || value.is_a?(Numeric) || value.is_a?(Array)
return if value.blank? return if value.blank?
value.to_s.gsub(VARIABLE_REGEXP) do |e| value.to_s.gsub(VARIABLE_REGEXP) do |e|

Loading…
Cancel
Save