mirror of https://github.com/docusealco/docuseal
				
				
				
			
			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.
		
		
		
		
		
			
		
			
				
					
					
						
							736 lines
						
					
					
						
							21 KiB
						
					
					
				
			
		
		
	
	
							736 lines
						
					
					
						
							21 KiB
						
					
					
				| <template>
 | |
|   <div
 | |
|     v-if="field.type === 'number'"
 | |
|     class="py-1.5 px-1 relative"
 | |
|     @click.stop
 | |
|   >
 | |
|     <select
 | |
|       :placeholder="t('format')"
 | |
|       class="select select-bordered select-xs font-normal w-full max-w-xs !h-7 !outline-0 bg-transparent"
 | |
|       @change="[field.preferences ||= {}, field.preferences.format = $event.target.value, save()]"
 | |
|     >
 | |
|       <option
 | |
|         v-for="format in numberFormats"
 | |
|         :key="format"
 | |
|         :value="format"
 | |
|         :selected="format === field.preferences?.format || (format === 'none' && !field.preferences?.format)"
 | |
|       >
 | |
|         {{ formatNumber(123456789.567, format) }}
 | |
|       </option>
 | |
|     </select>
 | |
|     <label
 | |
|       :style="{ backgroundColor }"
 | |
|       class="absolute -top-1 left-2.5 px-1 h-4"
 | |
|       style="font-size: 8px"
 | |
|     >
 | |
|       {{ t('format') }}
 | |
|     </label>
 | |
|   </div>
 | |
|   <div
 | |
|     v-if="field.type === 'verification'"
 | |
|     class="py-1.5 px-1 relative"
 | |
|     @click.stop
 | |
|   >
 | |
|     <select
 | |
|       :placeholder="t('method')"
 | |
|       class="select select-bordered select-xs font-normal w-full max-w-xs !h-7 !outline-0 bg-transparent"
 | |
|       @change="[field.preferences ||= {}, field.preferences.method = $event.target.value, save()]"
 | |
|     >
 | |
|       <option
 | |
|         v-for="method in ['QeS', 'AeS']"
 | |
|         :key="method"
 | |
|         :value="method.toLowerCase()"
 | |
|         :selected="method.toLowerCase() === field.preferences?.method || (method === 'QeS' && !field.preferences?.method)"
 | |
|       >
 | |
|         {{ method }}
 | |
|       </option>
 | |
|     </select>
 | |
|     <label
 | |
|       :style="{ backgroundColor }"
 | |
|       class="absolute -top-1 left-2.5 px-1 h-4"
 | |
|       style="font-size: 8px"
 | |
|     >
 | |
|       {{ t('method') }}
 | |
|     </label>
 | |
|   </div>
 | |
|   <div
 | |
|     v-if="['cells'].includes(field.type)"
 | |
|     class="py-1.5 px-1 relative"
 | |
|     @click.stop
 | |
|   >
 | |
|     <select
 | |
|       class="select select-bordered select-xs w-full max-w-xs h-7 !outline-0 font-normal bg-transparent"
 | |
|       @change="[field.preferences ||= {}, field.preferences.align = $event.target.value, save()]"
 | |
|     >
 | |
|       <option
 | |
|         v-for="value in ['left', 'right', field.type === 'cells' ? null : 'center'].filter(Boolean)"
 | |
|         :key="value"
 | |
|         :selected="field.preferences?.align ? value === field.preferences.align : value === 'left'"
 | |
|         :value="value"
 | |
|       >
 | |
|         {{ t(value) }}
 | |
|       </option>
 | |
|     </select>
 | |
|     <label
 | |
|       :style="{ backgroundColor }"
 | |
|       class="absolute -top-1 left-2.5 px-1 h-4"
 | |
|       style="font-size: 8px"
 | |
|     >
 | |
|       {{ t('align') }}
 | |
|     </label>
 | |
|   </div>
 | |
|   <div
 | |
|     v-if="['select', 'radio'].includes(field.type) && !defaultField"
 | |
|     class="py-1.5 px-1 relative"
 | |
|     @click.stop
 | |
|   >
 | |
|     <select
 | |
|       :placeholder="t('default_value')"
 | |
|       dir="auto"
 | |
|       class="select select-bordered select-xs w-full max-w-xs h-7 !outline-0 font-normal bg-transparent"
 | |
|       @change="[field.default_value = $event.target.value, !field.default_value && delete field.default_value, save()]"
 | |
|     >
 | |
|       <option
 | |
|         value=""
 | |
|         :selected="!field.default_value"
 | |
|       >
 | |
|         {{ t('none') }}
 | |
|       </option>
 | |
|       <option
 | |
|         v-for="(option, index) in field.options || []"
 | |
|         :key="option.uuid"
 | |
|         :value="option.value || `${t('option')} ${index + 1}`"
 | |
|         :selected="field.default_value === (option.value || `${t('option')} ${index + 1}`)"
 | |
|       >
 | |
|         {{ option.value || `${t('option')} ${index + 1}` }}
 | |
|       </option>
 | |
|     </select>
 | |
|     <label
 | |
|       :style="{ backgroundColor }"
 | |
|       class="absolute -top-1 left-2.5 px-1 h-4"
 | |
|       style="font-size: 8px"
 | |
|     >
 | |
|       {{ t('default_value') }}
 | |
|     </label>
 | |
|   </div>
 | |
|   <div
 | |
|     v-if="['text', 'number'].includes(field.type) && !defaultField"
 | |
|     class="py-1.5 px-1 relative"
 | |
|     @click.stop
 | |
|   >
 | |
|     <input
 | |
|       v-model="field.default_value"
 | |
|       :placeholder="t('default_value')"
 | |
|       dir="auto"
 | |
|       :type="field.type"
 | |
|       class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0 bg-transparent"
 | |
|       @blur="save"
 | |
|     >
 | |
|     <label
 | |
|       v-if="field.default_value"
 | |
|       :style="{ backgroundColor }"
 | |
|       class="absolute -top-1 left-2.5 px-1 h-4"
 | |
|       style="font-size: 8px"
 | |
|     >
 | |
|       {{ t('default_value') }}
 | |
|     </label>
 | |
|   </div>
 | |
|   <div
 | |
|     v-if="['text', 'cells'].includes(field.type)"
 | |
|     class="py-1.5 px-1 relative"
 | |
|     @click.stop
 | |
|   >
 | |
|     <select
 | |
|       class="select select-bordered select-xs w-full max-w-xs h-7 !outline-0 font-normal bg-transparent"
 | |
|       @change="onChangeValidation"
 | |
|     >
 | |
|       <option
 | |
|         :selected="!field.validation"
 | |
|         value=""
 | |
|       >
 | |
|         {{ t('none') }}
 | |
|       </option>
 | |
|       <option
 | |
|         v-for="(key, value) in validations"
 | |
|         :key="key"
 | |
|         :selected="lengthValidation ? key == 'length' : (field.validation?.pattern ? value === field.validation.pattern : key === 'none')"
 | |
|         :value="key"
 | |
|       >
 | |
|         {{ t(key) }}
 | |
|       </option>
 | |
|       <option
 | |
|         :selected="field.validation && !validations[field.validation.pattern] && !lengthValidation"
 | |
|         :value="validations[field.validation?.pattern] || !field.validation?.pattern ? 'custom' : field.validation?.pattern"
 | |
|       >
 | |
|         {{ t('custom') }}
 | |
|       </option>
 | |
|     </select>
 | |
|     <label
 | |
|       :style="{ backgroundColor }"
 | |
|       class="absolute -top-1 left-2.5 px-1 h-4"
 | |
|       style="font-size: 8px"
 | |
|     >
 | |
|       {{ t('validation') }}
 | |
|     </label>
 | |
|   </div>
 | |
|   <div
 | |
|     v-if="['text', 'cells'].includes(field.type) && field.validation && lengthValidation"
 | |
|     class="py-1.5 px-1 relative flex space-x-1"
 | |
|     @click.stop
 | |
|   >
 | |
|     <div class="w-1/2 relative">
 | |
|       <input
 | |
|         :placeholder="t('min')"
 | |
|         type="number"
 | |
|         min="0"
 | |
|         :value="lengthValidation.min"
 | |
|         class="input input-bordered w-full input-xs h-7 !outline-0 bg-transparent"
 | |
|         @input="field.validation.pattern = `.{${$event.target.value || 0},${lengthValidation.max || ''}}`"
 | |
|         @blur="save"
 | |
|       >
 | |
|       <label
 | |
|         v-if="lengthValidation.min"
 | |
|         :style="{ backgroundColor }"
 | |
|         class="absolute -top-2.5 left-1.5 px-1 h-4"
 | |
|         style="font-size: 8px"
 | |
|       >
 | |
|         {{ t('min') }}
 | |
|       </label>
 | |
|     </div>
 | |
|     <div class="w-1/2 relative">
 | |
|       <input
 | |
|         :placeholder="t('max')"
 | |
|         type="number"
 | |
|         min="1"
 | |
|         class="input input-bordered w-full input-xs h-7 !outline-0 bg-transparent"
 | |
|         :value="lengthValidation.max"
 | |
|         @input="field.validation.pattern = `.{${lengthValidation.min},${$event.target.value || ''}}`"
 | |
|         @blur="save"
 | |
|       >
 | |
|       <label
 | |
|         v-if="lengthValidation.max"
 | |
|         :style="{ backgroundColor }"
 | |
|         class="absolute -top-2.5 left-1.5 px-1 h-4"
 | |
|         style="font-size: 8px"
 | |
|       >
 | |
|         {{ t('max') }}
 | |
|       </label>
 | |
|     </div>
 | |
|   </div>
 | |
|   <div
 | |
|     v-if="['text', 'cells'].includes(field.type) && field.validation && !validations[field.validation.pattern] && !lengthValidation"
 | |
|     class="py-1.5 px-1 relative"
 | |
|     @click.stop
 | |
|   >
 | |
|     <input
 | |
|       ref="validationCustom"
 | |
|       v-model="field.validation.pattern"
 | |
|       :placeholder="t('regexp_validation')"
 | |
|       dir="auto"
 | |
|       class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0 bg-transparent"
 | |
|       @blur="save"
 | |
|     >
 | |
|     <label
 | |
|       v-if="field.validation.pattern"
 | |
|       :style="{ backgroundColor }"
 | |
|       class="absolute -top-1 left-2.5 px-1 h-4"
 | |
|       style="font-size: 8px"
 | |
|     >
 | |
|       {{ t('regexp_validation') }}
 | |
|     </label>
 | |
|   </div>
 | |
|   <div
 | |
|     v-if="['text', 'cells'].includes(field.type) && field.validation && !validations[field.validation.pattern] && !lengthValidation"
 | |
|     class="py-1.5 px-1 relative"
 | |
|     @click.stop
 | |
|   >
 | |
|     <input
 | |
|       v-model="field.validation.message"
 | |
|       :placeholder="t('error_message')"
 | |
|       dir="auto"
 | |
|       class="input input-bordered input-xs w-full max-w-xs h-7 !outline-0 bg-transparent"
 | |
|       @blur="save"
 | |
|     >
 | |
|     <label
 | |
|       v-if="field.validation.message"
 | |
|       :style="{ backgroundColor }"
 | |
|       class="absolute -top-1 left-2.5 px-1 h-4"
 | |
|       style="font-size: 8px"
 | |
|     >
 | |
|       {{ t('error_message') }}
 | |
|     </label>
 | |
|   </div>
 | |
|   <div
 | |
|     v-if="field.type === 'date'"
 | |
|     class="py-1.5 px-1 relative"
 | |
|     @click.stop
 | |
|   >
 | |
|     <select
 | |
|       v-model="field.preferences.format"
 | |
|       :placeholder="t('format')"
 | |
|       class="select select-bordered select-xs font-normal w-full max-w-xs !h-7 !outline-0 bg-transparent"
 | |
|       @change="save"
 | |
|     >
 | |
|       <option
 | |
|         v-for="format in dateFormats"
 | |
|         :key="format"
 | |
|         :value="format"
 | |
|       >
 | |
|         {{ formatDate(new Date(), format) }}
 | |
|       </option>
 | |
|     </select>
 | |
|     <label
 | |
|       :style="{ backgroundColor }"
 | |
|       class="absolute -top-1 left-2.5 px-1 h-4"
 | |
|       style="font-size: 8px"
 | |
|     >
 | |
|       {{ t('format') }}
 | |
|     </label>
 | |
|   </div>
 | |
|   <div
 | |
|     v-if="field.type === 'signature'"
 | |
|     class="py-1.5 px-1 relative"
 | |
|     @click.stop
 | |
|   >
 | |
|     <select
 | |
|       :placeholder="t('format')"
 | |
|       class="select select-bordered select-xs font-normal w-full max-w-xs !h-7 !outline-0 bg-transparent"
 | |
|       @change="[field.preferences.format = $event.target.value, save()]"
 | |
|     >
 | |
|       <option
 | |
|         value="any"
 | |
|         :selected="!field.preferences?.format || field.preferences.format === 'any'"
 | |
|       >
 | |
|         {{ t('any') }}
 | |
|       </option>
 | |
|       <option
 | |
|         v-for="type in ['drawn', 'typed', 'drawn_or_typed', 'upload']"
 | |
|         :key="type"
 | |
|         :value="type"
 | |
|         :selected="field.preferences?.format === type"
 | |
|       >
 | |
|         {{ t(type) }}
 | |
|       </option>
 | |
|     </select>
 | |
|     <label
 | |
|       :style="{ backgroundColor }"
 | |
|       class="absolute -top-1 left-2.5 px-1 h-4"
 | |
|       style="font-size: 8px"
 | |
|     >
 | |
|       {{ t('format') }}
 | |
|     </label>
 | |
|   </div>
 | |
|   <li
 | |
|     v-if="[true, false].includes(withSignatureId) && field.type === 'signature'"
 | |
|     @click.stop
 | |
|   >
 | |
|     <label class="cursor-pointer py-1.5">
 | |
|       <input
 | |
|         :checked="field.preferences?.with_signature_id"
 | |
|         type="checkbox"
 | |
|         :disabled="!editable || (defaultField && [true, false].includes(defaultField.required))"
 | |
|         class="toggle toggle-xs"
 | |
|         @change="[field.preferences ||= {}, field.preferences.with_signature_id = $event.target.checked, save()]"
 | |
|       >
 | |
|       <span class="label-text">{{ t('signature_id') }}</span>
 | |
|     </label>
 | |
|   </li>
 | |
|   <li
 | |
|     v-if="withRequired && field.type !== 'phone' && field.type !== 'stamp' && field.type !== 'verification'"
 | |
|     @click.stop
 | |
|   >
 | |
|     <label class="cursor-pointer py-1.5">
 | |
|       <input
 | |
|         v-model="field.required"
 | |
|         type="checkbox"
 | |
|         :disabled="!editable || (defaultField && [true, false].includes(defaultField.required))"
 | |
|         class="toggle toggle-xs"
 | |
|         @update:model-value="save"
 | |
|       >
 | |
|       <span class="label-text">{{ t('required') }}</span>
 | |
|     </label>
 | |
|   </li>
 | |
|   <li
 | |
|     v-if="field.type == 'stamp'"
 | |
|     @click.stop
 | |
|   >
 | |
|     <label class="cursor-pointer py-1.5">
 | |
|       <input
 | |
|         :checked="field.preferences?.with_logo != false"
 | |
|         type="checkbox"
 | |
|         class="toggle toggle-xs"
 | |
|         @change="[field.preferences ||= {}, field.preferences.with_logo = field.preferences.with_logo == false, save()]"
 | |
|       >
 | |
|       <span class="label-text">{{ t('with_logo') }}</span>
 | |
|     </label>
 | |
|   </li>
 | |
|   <li
 | |
|     v-if="field.type == 'checkbox'"
 | |
|     @click.stop
 | |
|   >
 | |
|     <label class="cursor-pointer py-1.5">
 | |
|       <input
 | |
|         v-model="field.default_value"
 | |
|         type="checkbox"
 | |
|         class="toggle toggle-xs"
 | |
|         @update:model-value="[field.default_value = $event, field.readonly = $event, save()]"
 | |
|       >
 | |
|       <span class="label-text">{{ t('checked') }}</span>
 | |
|     </label>
 | |
|   </li>
 | |
|   <li
 | |
|     v-if="field.type == 'date'"
 | |
|     @click.stop
 | |
|   >
 | |
|     <label class="cursor-pointer py-1.5">
 | |
|       <input
 | |
|         v-model="field.readonly"
 | |
|         type="checkbox"
 | |
|         class="toggle toggle-xs"
 | |
|         @update:model-value="[field.default_value = $event ? '{{date}}' : null, field.readonly = $event, save()]"
 | |
|       >
 | |
|       <span class="label-text">{{ t('set_signing_date') }}</span>
 | |
|     </label>
 | |
|   </li>
 | |
|   <li
 | |
|     v-if="['text', 'number'].includes(field.type) && !defaultField"
 | |
|     @click.stop
 | |
|   >
 | |
|     <label class="cursor-pointer py-1.5">
 | |
|       <input
 | |
|         v-model="field.readonly"
 | |
|         type="checkbox"
 | |
|         class="toggle toggle-xs"
 | |
|         @update:model-value="save"
 | |
|       >
 | |
|       <span class="label-text">{{ t('read_only') }}</span>
 | |
|     </label>
 | |
|   </li>
 | |
|   <hr
 | |
|     v-if="field.type != 'stamp'"
 | |
|     class="pb-0.5 mt-0.5"
 | |
|   >
 | |
|   <li v-if="['text', 'number', 'date', 'select'].includes(field.type)">
 | |
|     <label
 | |
|       class="label-text cursor-pointer text-center w-full flex items-center"
 | |
|       @click="$emit('click-font')"
 | |
|     >
 | |
|       <IconTypography
 | |
|         width="18"
 | |
|       />
 | |
|       <span class="text-sm">
 | |
|         {{ t('font') }}
 | |
|       </span>
 | |
|     </label>
 | |
|   </li>
 | |
|   <li
 | |
|     v-if="field.type != 'stamp'"
 | |
|   >
 | |
|     <label
 | |
|       class="label-text cursor-pointer text-center w-full flex items-center"
 | |
|       @click="$emit('click-description')"
 | |
|     >
 | |
|       <IconInfoCircle
 | |
|         width="18"
 | |
|       />
 | |
|       <span class="text-sm">
 | |
|         {{ t('description') }}
 | |
|       </span>
 | |
|     </label>
 | |
|   </li>
 | |
|   <li
 | |
|     v-if="field.type != 'stamp'"
 | |
|   >
 | |
|     <label
 | |
|       class="label-text cursor-pointer text-center w-full flex items-center"
 | |
|       @click="$emit('click-condition')"
 | |
|     >
 | |
|       <IconRouteAltLeft
 | |
|         width="18"
 | |
|       />
 | |
|       <span class="text-sm">
 | |
|         {{ t('condition') }}
 | |
|       </span>
 | |
|     </label>
 | |
|   </li>
 | |
|   <li v-if="field.type == 'number'">
 | |
|     <label
 | |
|       class="label-text cursor-pointer text-center w-full flex items-center"
 | |
|       @click="$emit('click-formula')"
 | |
|     >
 | |
|       <IconMathFunction
 | |
|         width="18"
 | |
|       />
 | |
|       <span class="text-sm">
 | |
|         {{ t('formula') }}
 | |
|       </span>
 | |
|     </label>
 | |
|   </li>
 | |
|   <hr class="pb-0.5 mt-0.5">
 | |
|   <template v-if="withAreas">
 | |
|     <li
 | |
|       v-for="(area, index) in sortedAreas"
 | |
|       :key="index"
 | |
|     >
 | |
|       <a
 | |
|         href="#"
 | |
|         class="text-sm py-1 px-2 group/1"
 | |
|         @click.prevent="$emit('scroll-to', area)"
 | |
|       >
 | |
|         <IconShape
 | |
|           :width="20"
 | |
|           :stroke-width="1.6"
 | |
|         />
 | |
|         {{ t('page') }}
 | |
|         <template v-if="template.schema.length > 1">{{ template.schema.findIndex((item) => item.attachment_uuid === area.attachment_uuid) + 1 }}-</template>{{ area.page + 1 }}
 | |
|         <IconX
 | |
|           :width="12"
 | |
|           class="group-hover/1:inline hidden"
 | |
|           @click.prevent.stop="[$emit('remove-area', area), $event.target.closest('.dropdown').querySelector('label').focus()]"
 | |
|         />
 | |
|       </a>
 | |
|     </li>
 | |
|     <li v-if="!field.areas?.length || !['radio', 'multiple'].includes(field.type)">
 | |
|       <a
 | |
|         href="#"
 | |
|         class="text-sm py-1 px-2"
 | |
|         @click.prevent="$emit('set-draw', { field })"
 | |
|       >
 | |
|         <IconNewSection
 | |
|           :width="20"
 | |
|           :stroke-width="1.6"
 | |
|         />
 | |
|         {{ t('draw_new_area') }}
 | |
|       </a>
 | |
|     </li>
 | |
|   </template>
 | |
|   <li v-if="field.areas?.length === 1 && ['date', 'signature', 'initials', 'text', 'cells', 'stamp'].includes(field.type)">
 | |
|     <a
 | |
|       href="#"
 | |
|       class="text-sm py-1 px-2"
 | |
|       @click.prevent="copyToAllPages(field)"
 | |
|     >
 | |
|       <IconCopy
 | |
|         :width="20"
 | |
|         :stroke-width="1.6"
 | |
|       />
 | |
|       {{ t('copy_to_all_pages') }}
 | |
|     </a>
 | |
|   </li>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| import { IconRouteAltLeft, IconTypography, IconShape, IconX, IconMathFunction, IconNewSection, IconInfoCircle, IconCopy } from '@tabler/icons-vue'
 | |
| 
 | |
| export default {
 | |
|   name: 'FieldSettings',
 | |
|   components: {
 | |
|     IconShape,
 | |
|     IconInfoCircle,
 | |
|     IconMathFunction,
 | |
|     IconRouteAltLeft,
 | |
|     IconCopy,
 | |
|     IconNewSection,
 | |
|     IconTypography,
 | |
|     IconX
 | |
|   },
 | |
|   inject: ['template', 'save', 't'],
 | |
|   props: {
 | |
|     field: {
 | |
|       type: Object,
 | |
|       required: true
 | |
|     },
 | |
|     withSignatureId: {
 | |
|       type: Boolean,
 | |
|       required: false,
 | |
|       default: null
 | |
|     },
 | |
|     backgroundColor: {
 | |
|       type: String,
 | |
|       required: false,
 | |
|       default: null
 | |
|     },
 | |
|     defaultField: {
 | |
|       type: Object,
 | |
|       required: false,
 | |
|       default: null
 | |
|     },
 | |
|     withRequired: {
 | |
|       type: Boolean,
 | |
|       required: false,
 | |
|       default: true
 | |
|     },
 | |
|     withAreas: {
 | |
|       type: Boolean,
 | |
|       required: false,
 | |
|       default: true
 | |
|     },
 | |
|     editable: {
 | |
|       type: Boolean,
 | |
|       required: false,
 | |
|       default: true
 | |
|     }
 | |
|   },
 | |
|   emits: ['set-draw', 'scroll-to', 'click-formula', 'click-description', 'click-condition', 'click-font', 'remove-area'],
 | |
|   data () {
 | |
|     return {
 | |
|       selectedValidation: ''
 | |
|     }
 | |
|   },
 | |
|   computed: {
 | |
|     schemaAttachmentsIndexes () {
 | |
|       return (this.template.schema || []).reduce((acc, item, index) => {
 | |
|         acc[item.attachment_uuid] = index
 | |
| 
 | |
|         return acc
 | |
|       }, {})
 | |
|     },
 | |
|     numberFormats () {
 | |
|       return [
 | |
|         'none',
 | |
|         'usd',
 | |
|         'eur',
 | |
|         'gbp',
 | |
|         'comma',
 | |
|         'dot',
 | |
|         'space'
 | |
|       ]
 | |
|     },
 | |
|     dateFormats () {
 | |
|       const formats = [
 | |
|         'MM/DD/YYYY',
 | |
|         'DD/MM/YYYY',
 | |
|         'YYYY-MM-DD',
 | |
|         'DD-MM-YYYY',
 | |
|         'DD.MM.YYYY',
 | |
|         'MMM D, YYYY',
 | |
|         'MMMM D, YYYY',
 | |
|         'D MMM YYYY',
 | |
|         'D MMMM YYYY'
 | |
|       ]
 | |
| 
 | |
|       if (Intl.DateTimeFormat().resolvedOptions().timeZone?.includes('Seoul') || navigator.language?.startsWith('ko')) {
 | |
|         formats.push('YYYY년 MM월 DD일')
 | |
|       }
 | |
| 
 | |
|       if (this.field.preferences?.format && !formats.includes(this.field.preferences.format)) {
 | |
|         formats.unshift(this.field.preferences.format)
 | |
|       }
 | |
| 
 | |
|       return formats
 | |
|     },
 | |
|     lengthValidation () {
 | |
|       if (this.field.validation?.pattern && this.selectedValidation !== 'custom') {
 | |
|         return this.field.validation.pattern.match(/^\.{(?<min>\d+),(?<max>\d+)?}$/)?.groups
 | |
|       } else {
 | |
|         return null
 | |
|       }
 | |
|     },
 | |
|     validations () {
 | |
|       return {
 | |
|         '.{0,}': 'length',
 | |
|         '^[0-9]{3}-[0-9]{2}-[0-9]{4}$': 'ssn',
 | |
|         '^[0-9]{2}-[0-9]{7}$': 'ein',
 | |
|         '^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$': 'email',
 | |
|         '^https?://.*': 'url',
 | |
|         '^[0-9]{5}(?:-[0-9]{4})?$': 'zip',
 | |
|         '^[0-9]+$': 'numbers_only',
 | |
|         '^[a-zA-Z]+$': 'letters_only'
 | |
|       }
 | |
|     },
 | |
|     sortedAreas () {
 | |
|       return (this.field.areas || []).sort((a, b) => {
 | |
|         return this.schemaAttachmentsIndexes[a.attachment_uuid] - this.schemaAttachmentsIndexes[b.attachment_uuid]
 | |
|       })
 | |
|     }
 | |
|   },
 | |
|   methods: {
 | |
|     onChangeValidation (event) {
 | |
|       if (event.target.value === 'custom') {
 | |
|         this.selectedValidation = 'custom'
 | |
| 
 | |
|         this.field.validation = { pattern: '', message: '' }
 | |
| 
 | |
|         this.$nextTick(() => this.$refs.validationCustom.focus())
 | |
|       } else if (event.target.value) {
 | |
|         this.field.validation ||= {}
 | |
|         this.field.validation.pattern =
 | |
|           Object.keys(this.validations).find(key => this.validations[key] === event.target.value)
 | |
| 
 | |
|         this.selectedValidation = event.target.value
 | |
|         delete this.field.validation.message
 | |
|       } else {
 | |
|         this.selectedValidation = ''
 | |
| 
 | |
|         delete this.field.validation
 | |
|       }
 | |
| 
 | |
|       this.save()
 | |
|     },
 | |
|     copyToAllPages (field) {
 | |
|       const areaString = JSON.stringify(field.areas[0])
 | |
| 
 | |
|       this.template.documents.forEach((attachment) => {
 | |
|         const numberOfPages = attachment.metadata?.pdf?.number_of_pages || attachment.preview_images.length
 | |
| 
 | |
|         for (let page = 0; page <= numberOfPages - 1; page++) {
 | |
|           if (!field.areas.find((area) => area.attachment_uuid === attachment.uuid && area.page === page)) {
 | |
|             field.areas.push({ ...JSON.parse(areaString), attachment_uuid: attachment.uuid, page })
 | |
|           }
 | |
|         }
 | |
|       })
 | |
| 
 | |
|       this.$emit('scroll-to', this.field.areas[this.field.areas.length - 1])
 | |
| 
 | |
|       this.save()
 | |
|     },
 | |
|     formatNumber (number, format) {
 | |
|       if (format === 'comma') {
 | |
|         return new Intl.NumberFormat('en-US').format(number)
 | |
|       } else if (format === 'usd') {
 | |
|         return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(number)
 | |
|       } else if (format === 'gbp') {
 | |
|         return new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP', minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(number)
 | |
|       } else if (format === 'eur') {
 | |
|         return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(number)
 | |
|       } else if (format === 'dot') {
 | |
|         return new Intl.NumberFormat('de-DE').format(number)
 | |
|       } else if (format === 'space') {
 | |
|         return new Intl.NumberFormat('fr-FR').format(number)
 | |
|       } else {
 | |
|         return number
 | |
|       }
 | |
|     },
 | |
|     formatDate (date, format) {
 | |
|       const monthFormats = {
 | |
|         M: 'numeric',
 | |
|         MM: '2-digit',
 | |
|         MMM: 'short',
 | |
|         MMMM: 'long'
 | |
|       }
 | |
| 
 | |
|       const dayFormats = {
 | |
|         D: 'numeric',
 | |
|         DD: '2-digit'
 | |
|       }
 | |
| 
 | |
|       const yearFormats = {
 | |
|         YYYY: 'numeric',
 | |
|         YY: '2-digit'
 | |
|       }
 | |
| 
 | |
|       const parts = new Intl.DateTimeFormat([], {
 | |
|         day: dayFormats[format.match(/D+/)],
 | |
|         month: monthFormats[format.match(/M+/)],
 | |
|         year: yearFormats[format.match(/Y+/)]
 | |
|       }).formatToParts(date)
 | |
| 
 | |
|       return format
 | |
|         .replace(/D+/, parts.find((p) => p.type === 'day').value)
 | |
|         .replace(/M+/, parts.find((p) => p.type === 'month').value)
 | |
|         .replace(/Y+/, parts.find((p) => p.type === 'year').value)
 | |
|     }
 | |
|   }
 | |
| }
 | |
| </script>
 |