PluginProbe
The Innovative Form Builder – IvyForms / 0.4
The Innovative Form Builder – IvyForms v0.4
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / views / public / fields / EmailFormField.vue

EmailFormField.vue in The Innovative Form Builder – IvyForms 0.4, at frontend/src/views/public/fields/EmailFormField.vue

193 lines 5.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <template>
2 <div :class="['ivyforms-field__email', 'ivyforms-field__email_' + field.id, field.cssClasses]">
3 <IvyFormItem
4 :label="field.hideLabel ? '' : field.label"
5 :required="field.required"
6 :error="error"
7 :prop="field.type + '_' + field.fieldIndex"
8 :show-info="!!field.description && !error"
9 :info-text="field.description || ''"
10 :show-info-icon="false"
11 :label-position="field.labelPosition"
12 >
13 <IvyTextInput
14 :id="fieldID"
15 v-model="localModelValue"
16 class="ivyforms-field__email-input"
17 type="email"
18 :placeholder="field.placeholder"
19 :disabled="disabled"
20 :readonly="field.readOnly"
21 :required="field.required"
22 :maxlength="field.limitMaxLength ? field.maxLength : undefined"
23 @blur="syncMainFromDom"
24 />
25 </IvyFormItem>
26
27 <!-- Confirmation field: appears only when enabled AND main email not empty -->
28 <IvyFormItem
29 v-if="field.confirmFieldEnabled && !!localModelValue"
30 :label="field.confirmFieldHideLabel ? '' : confirmLabel"
31 :required="true"
32 :prop="confirmKey"
33 >
34 <IvyTextInput
35 :id="confirmFieldID"
36 v-model="confirmModelValue"
37 class="ivyforms-field__email-input ivyforms-field__email-input--confirm"
38 type="email"
39 :placeholder="confirmPlaceholder"
40 :disabled="disabled"
41 :readonly="field.readOnly"
42 required
43 @blur="syncConfirmFromDom"
44 />
45 </IvyFormItem>
46 </div>
47 </template>
48
49 <script setup lang="ts">
50 import { computed, inject, watch, type Ref } from 'vue'
51
52 interface Field {
53 id: number
54 formId: number
55 fieldIndex: number
56 type: string
57 label: string
58 placeholder?: string
59 required?: boolean
60 hideLabel?: boolean
61 readOnly?: boolean
62 description?: string
63 cssClasses?: string
64 confirmFieldEnabled?: boolean
65 confirmFieldLabel?: string
66 confirmFieldPlaceholder?: string
67 confirmFieldHideLabel?: boolean
68 limitMaxLength?: boolean
69 maxLength?: number
70 labelPosition?: string
71 noDuplicates?: boolean
72 // Any other field properties from your data
73 }
74
75 interface FieldProps {
76 modelValue: string | number | null | undefined
77 field: Field
78 disabled?: boolean
79 error?: string
80 }
81
82 const props = withDefaults(defineProps<FieldProps>(), {
83 modelValue: '',
84 disabled: false,
85 error: '',
86 })
87
88 const emit = defineEmits(['update:modelValue'])
89
90 // Define acceptable form values shape
91 type FormValues = Record<string, string | number | null | undefined>
92 type InjectedFormValues = Ref<FormValues> | FormValues | null
93
94 // Inject global formValues (may be provided as a ref) and error clearer
95 const injectedFormValues = inject<InjectedFormValues>('formValues', null)
96 const formValues = computed(() => {
97 if (!injectedFormValues) return {} as FormValues
98 return (
99 typeof injectedFormValues === 'object' && 'value' in injectedFormValues
100 ? (injectedFormValues as Ref<FormValues>).value
101 : injectedFormValues
102 ) as FormValues
103 })
104 const clearFieldError = inject<(fieldKey: string) => void>('clearFieldError', () => {})
105
106 // Keys
107 const mainKey = computed(() => props.field.type + '_' + props.field.fieldIndex)
108 const confirmKey = computed(() => 'emailConfirm_' + props.field.fieldIndex)
109
110 // Handle main input
111 const handleInput = (value: string | number | null | undefined) => {
112 emit('update:modelValue', value)
113 clearFieldError(mainKey.value)
114 }
115
116 const localModelValue = computed({
117 get() {
118 return props.modelValue
119 },
120 set(value) {
121 handleInput(value)
122 },
123 })
124
125 // Confirmation input model (stored directly in injected formValues)
126 const confirmModelValue = computed<string | number | null | undefined>({
127 get() {
128 return formValues.value[confirmKey.value]
129 },
130 set(value) {
131 formValues.value[confirmKey.value] = value
132 clearFieldError(confirmKey.value)
133 },
134 })
135
136 // Labels / placeholders
137 const confirmLabel = computed(() => props.field.confirmFieldLabel)
138 const confirmPlaceholder = computed(() => props.field.confirmFieldPlaceholder)
139
140 // IDs
141 const fieldID = computed(
142 () => `ivyforms-field__email-input_${props.field.formId || ''}_${props.field.fieldIndex || ''}`,
143 )
144 const confirmFieldID = computed(
145 () =>
146 `ivyforms-field__email-confirm-input_${props.field.formId || ''}_${props.field.fieldIndex || ''}`,
147 )
148 // Sync main input from DOM on blur (includes autofill)
149 function syncMainFromDom(e: FocusEvent) {
150 const el = e.target as HTMLInputElement | null
151 if (!el) return
152 if (el.value && el.value !== localModelValue.value) {
153 localModelValue.value = el.value
154 }
155 }
156
157 // Sync confirm input from DOM
158 function syncConfirmFromDom(e: FocusEvent) {
159 const el = e.target as HTMLInputElement | null
160 if (!el) return
161 if (el.value !== formValues.value[confirmKey.value]) {
162 formValues.value[confirmKey.value] = el.value
163 clearFieldError(confirmKey.value)
164 }
165 }
166 // Auto clear mismatch error when emails become equal
167 watch(
168 () => [formValues.value[confirmKey.value], localModelValue.value],
169 ([confirmVal, mainVal]) => {
170 if (confirmVal && mainVal && confirmVal === mainVal) {
171 clearFieldError(confirmKey.value)
172 }
173 },
174 )
175 </script>
176
177 <style scoped lang="scss">
178 .ivyforms-field__email-input--confirm {
179 margin-top: 4px;
180 }
181
182 .ivyforms-field__email {
183 &.is-readonly {
184 :deep(.el-input__wrapper),
185 :deep(.el-input__wrapper:hover),
186 :deep(.el-input__wrapper.is-focus) {
187 border-color: var(--map-base-dusk-stroke--2) !important;
188 box-shadow: none !important;
189 }
190 }
191 }
192 </style>
193