PluginProbe
The Innovative Form Builder – IvyForms / 0.8
The Innovative Form Builder – IvyForms v0.8
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 / admin / form-builder / fields / address / AddressField.vue

AddressField.vue in The Innovative Form Builder – IvyForms 0.8, at frontend/src/views/admin/form-builder/fields/address/AddressField.vue

496 lines 16.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <template>
2 <div
3 class="ivyforms-field__address ivyforms-width-100"
4 :class="[
5 { 'ivyforms-field__address--readonly': !!field?.readOnly },
6 `ivyforms-field__address--label-${effectiveLabelPosition}`,
7 ]"
8 >
9 <div
10 v-if="
11 !field?.hideLabel &&
12 (effectiveLabelPosition === 'top' || effectiveLabelPosition === 'default')
13 "
14 class="ivyforms-field__address-label ivyforms-mb-8 regular-16"
15 >
16 {{ field?.label || getLabel('address') }}
17 </div>
18 <div
19 class="ivyforms-field__address-container ivyforms-width-100"
20 :class="{
21 'ivyforms-field__address-container--flex-row':
22 effectiveLabelPosition === 'left' || effectiveLabelPosition === 'right',
23 'ivyforms-field__address-container--flex-column':
24 effectiveLabelPosition === 'top' || effectiveLabelPosition === 'default',
25 }"
26 >
27 <template v-if="effectiveLabelPosition === 'left'">
28 <div
29 v-if="!field?.hideLabel"
30 class="ivyforms-field__address-label ivyforms-field__address-label--left regular-16 ivyforms-mr-16 ivyforms-flex ivyforms-align-items-center"
31 >
32 {{ field?.label || getLabel('address') }}
33 </div>
34 <div
35 class="ivyforms-field__address-fields ivyforms-width-100 ivyforms-flex ivyforms-flex-direction-column ivyforms-gap-16"
36 >
37 <!-- First 3 fields: streetAddress, addressLine2, city -->
38 <div
39 v-for="fieldData in addressFields.slice(0, 3)"
40 :key="fieldData.type"
41 class="ivyforms-field__address-row ivyforms-field__address-row--full ivyforms-flex ivyforms-flex-direction-row ivyforms-width-100"
42 >
43 <div
44 :class="[
45 'ivyforms-field__address-field ivyforms-flex-1',
46 fieldData.type.toLowerCase() + '-address',
47 ]"
48 >
49 <IvyFormItem
50 :label="fieldData.hideLabel ? '' : fieldData.label"
51 :required="fieldData.required"
52 >
53 <IvyTextInput
54 :id="fieldData.id"
55 :aria-label="fieldData.label"
56 readonly
57 :model-value="fieldData.value"
58 type="text"
59 :placeholder="fieldData.placeholder"
60 />
61 <div
62 v-if="fieldData.description"
63 class="ivyforms-field__address-description regular-14"
64 >
65 {{ fieldData.description }}
66 </div>
67 </IvyFormItem>
68 </div>
69 </div>
70 <!-- Last 3 fields: state, zip, country -->
71 <div
72 class="ivyforms-field__address-row ivyforms-field__address-row--three-cols ivyforms-flex ivyforms-flex-direction-row ivyforms-gap-16 ivyforms-width-100"
73 >
74 <div
75 v-for="fieldData in addressFields.slice(3, 6)"
76 :key="fieldData.type"
77 :class="[
78 'ivyforms-field__address-field ivyforms-flex-1',
79 fieldData.type.toLowerCase() + '-address',
80 ]"
81 >
82 <IvyFormItem
83 :label="fieldData.hideLabel ? '' : fieldData.label"
84 :required="fieldData.required"
85 >
86 <template v-if="fieldData.type === 'country'">
87 <IvySelectInput
88 :id="fieldData.id"
89 v-model="countryValue"
90 :placeholder="getLabel('select_country')"
91 disabled
92 readonly
93 >
94 <IvySelectOption value="">{{ getLabel('select_country') }}</IvySelectOption>
95 <IvySelectOption
96 v-for="country in countryOptions"
97 :key="country"
98 :value="country"
99 >{{ country }}</IvySelectOption
100 >
101 </IvySelectInput>
102 </template>
103 <template v-else>
104 <IvyTextInput
105 :id="fieldData.id"
106 :aria-label="fieldData.label"
107 readonly
108 :model-value="fieldData.value"
109 type="text"
110 :placeholder="fieldData.placeholder"
111 />
112 </template>
113 <div
114 v-if="fieldData.description"
115 class="ivyforms-field__address-description regular-14"
116 >
117 {{ fieldData.description }}
118 </div>
119 </IvyFormItem>
120 </div>
121 </div>
122 </div>
123 </template>
124 <template v-else-if="effectiveLabelPosition === 'right'">
125 <div
126 class="ivyforms-field__address-fields ivyforms-width-100 ivyforms-flex ivyforms-flex-direction-column ivyforms-gap-16"
127 >
128 <!-- First 3 fields: streetAddress, addressLine2, city -->
129 <div
130 v-for="fieldData in addressFields.slice(0, 3)"
131 :key="fieldData.type"
132 class="ivyforms-field__address-row ivyforms-field__address-row--full ivyforms-flex ivyforms-flex-direction-row ivyforms-width-100"
133 >
134 <div
135 :class="[
136 'ivyforms-field__address-field ivyforms-flex-1',
137 fieldData.type.toLowerCase() + '-address',
138 ]"
139 >
140 <IvyFormItem
141 :label="fieldData.hideLabel ? '' : fieldData.label"
142 :required="fieldData.required"
143 >
144 <IvyTextInput
145 :id="fieldData.id"
146 :aria-label="fieldData.label"
147 readonly
148 :model-value="fieldData.value"
149 type="text"
150 :placeholder="fieldData.placeholder"
151 />
152 <div
153 v-if="fieldData.description"
154 class="ivyforms-field__address-description regular-14"
155 >
156 {{ fieldData.description }}
157 </div>
158 </IvyFormItem>
159 </div>
160 </div>
161 <!-- Last 3 fields: state, zip, country -->
162 <div
163 class="ivyforms-field__address-row ivyforms-field__address-row--three-cols ivyforms-flex ivyforms-flex-direction-row ivyforms-gap-16 ivyforms-width-100"
164 >
165 <div
166 v-for="fieldData in addressFields.slice(3, 6)"
167 :key="fieldData.type"
168 :class="[
169 'ivyforms-field__address-field ivyforms-flex-1',
170 fieldData.type.toLowerCase() + '-address',
171 ]"
172 >
173 <IvyFormItem
174 :label="fieldData.hideLabel ? '' : fieldData.label"
175 :required="fieldData.required"
176 >
177 <template v-if="fieldData.type === 'country'">
178 <IvySelectInput
179 :id="fieldData.id"
180 v-model="countryValue"
181 :placeholder="getLabel('select_country')"
182 disabled
183 readonly
184 >
185 <IvySelectOption value="">{{ getLabel('select_country') }}</IvySelectOption>
186 <IvySelectOption
187 v-for="country in countryOptions"
188 :key="country"
189 :value="country"
190 >{{ country }}</IvySelectOption
191 >
192 </IvySelectInput>
193 </template>
194 <template v-else>
195 <IvyTextInput
196 :id="fieldData.id"
197 :aria-label="fieldData.label"
198 readonly
199 :model-value="fieldData.value"
200 type="text"
201 :placeholder="fieldData.placeholder"
202 />
203 </template>
204 <div
205 v-if="fieldData.description"
206 class="ivyforms-field__address-description regular-14"
207 >
208 {{ fieldData.description }}
209 </div>
210 </IvyFormItem>
211 </div>
212 </div>
213 </div>
214 <div
215 v-if="!field?.hideLabel"
216 class="ivyforms-field__address-label ivyforms-field__address-label--right regular-16 ivyforms-ml-16 ivyforms-flex ivyforms-align-items-center"
217 >
218 {{ field?.label || getLabel('address') }}
219 </div>
220 </template>
221 <template v-else>
222 <div
223 class="ivyforms-field__address-fields ivyforms-width-100 ivyforms-flex ivyforms-flex-direction-column ivyforms-gap-16"
224 >
225 <!-- First 3 fields: streetAddress, addressLine2, city -->
226 <div
227 v-for="fieldData in addressFields.slice(0, 3)"
228 :key="fieldData.type"
229 class="ivyforms-field__address-row ivyforms-field__address-row--full ivyforms-flex ivyforms-flex-direction-row ivyforms-width-100"
230 >
231 <div
232 :class="[
233 'ivyforms-field__address-field ivyforms-flex-1',
234 fieldData.type.toLowerCase() + '-address',
235 ]"
236 >
237 <IvyFormItem
238 :label="fieldData.hideLabel ? '' : fieldData.label"
239 :required="fieldData.required"
240 >
241 <IvyTextInput
242 :id="fieldData.id"
243 :aria-label="fieldData.label"
244 readonly
245 :model-value="fieldData.value"
246 type="text"
247 :placeholder="fieldData.placeholder"
248 />
249 <div
250 v-if="fieldData.description"
251 class="ivyforms-field__address-description regular-14"
252 >
253 {{ fieldData.description }}
254 </div>
255 </IvyFormItem>
256 </div>
257 </div>
258 <!-- Last 3 fields: state, zip, country -->
259 <div
260 class="ivyforms-field__address-row ivyforms-field__address-row--three-cols ivyforms-flex ivyforms-flex-direction-row ivyforms-gap-16 ivyforms-width-100"
261 >
262 <div
263 v-for="fieldData in addressFields.slice(3, 6)"
264 :key="fieldData.type"
265 :class="[
266 'ivyforms-field__address-field ivyforms-flex-1',
267 fieldData.type.toLowerCase() + '-address',
268 ]"
269 >
270 <IvyFormItem
271 :label="fieldData.hideLabel ? '' : fieldData.label"
272 :required="fieldData.required"
273 >
274 <template v-if="fieldData.type === 'country'">
275 <IvySelectInput
276 :id="fieldData.id"
277 v-model="countryValue"
278 :placeholder="getLabel('select_country')"
279 disabled
280 readonly
281 >
282 <IvySelectOption value="">{{ getLabel('select_country') }}</IvySelectOption>
283 <IvySelectOption
284 v-for="country in countryOptions"
285 :key="country"
286 :value="country"
287 >{{ country }}</IvySelectOption
288 >
289 </IvySelectInput>
290 </template>
291 <template v-else>
292 <IvyTextInput
293 :id="fieldData.id"
294 :aria-label="fieldData.label"
295 readonly
296 :model-value="fieldData.value"
297 type="text"
298 :placeholder="fieldData.placeholder"
299 />
300 </template>
301 <div
302 v-if="fieldData.description"
303 class="ivyforms-field__address-description regular-14"
304 >
305 {{ fieldData.description }}
306 </div>
307 </IvyFormItem>
308 </div>
309 </div>
310 </div>
311 </template>
312 </div>
313 </div>
314 </template>
315
316 <script setup lang="ts">
317 import { computed } from 'vue'
318 import { useFormBuilder } from '@/stores/useFormBuilder'
319 import { useLabels } from '@/composables/useLabels'
320 import { countryList } from '@/constants/countries'
321 import type { AddressSubField } from '@/types/field.d.ts'
322
323 const { getLabel } = useLabels()
324
325 const props = defineProps<{ fieldIndex: number }>()
326 const formBuilderStore = useFormBuilder()
327
328 const field = computed(() => formBuilderStore.fields.find((f) => f.fieldIndex === props.fieldIndex))
329
330 const addressFields = computed<AddressSubField[]>(() =>
331 (field.value?.addressFields || []).filter((f: AddressSubField) => f.visible !== false),
332 )
333
334 const countryOptions = countryList.map((c) => c.name)
335
336 const countryValue = computed({
337 get: () => addressFields.value.find((f) => f.type === 'country')?.value || '',
338 set: () => {}, // No-op, readonly in builder
339 })
340
341 const effectiveLabelPosition = computed(() => {
342 const position = field.value?.labelPosition
343 if (position === 'default' || !position) {
344 return 'top'
345 }
346 return position
347 })
348 </script>
349
350 <style lang="scss" scoped>
351 .ivyforms-field__address {
352 cursor: default;
353 .ivyforms-field__address-label {
354 color: var(--map-base-text-0);
355 }
356 &--readonly {
357 opacity: 0.6;
358 }
359
360 &--label-left,
361 &--label-right {
362 .ivyforms-field__address-container {
363 display: flex;
364 flex-direction: row;
365 align-items: flex-start;
366 }
367 .ivyforms-field__address-label {
368 &--left {
369 display: flex;
370 align-items: center;
371 margin-right: 16px;
372 min-width: fit-content;
373 }
374 &--right {
375 display: flex;
376 align-items: center;
377 margin-left: 16px;
378 min-width: fit-content;
379 }
380 }
381 }
382
383 &--label-top,
384 &--label-default {
385 .ivyforms-field__address {
386 &-container {
387 display: flex;
388 flex-direction: column;
389 }
390 }
391 }
392
393 &-row {
394 display: flex;
395 flex-direction: row;
396 width: 100%;
397 &--full {
398 width: 100%;
399 }
400 &--three-cols {
401 gap: 16px;
402 width: 100%;
403 }
404 }
405
406 &-description {
407 font-size: 14px;
408 margin-top: 4px;
409 }
410
411 .ivyforms-form-item {
412 margin-bottom: 0;
413 cursor: default;
414
415 :deep(.ivyforms-form-item__label) {
416 display: flex;
417 align-items: center;
418 color: var(--map-base-text-0);
419 font-size: 14px;
420 font-style: normal;
421 font-weight: 500;
422 height: 20px !important;
423 cursor: default !important;
424 }
425
426 :deep(.el-form-item__label) {
427 cursor: default;
428 }
429
430 // Text inputs styling to match TextField behavior
431 .ivyforms-input.el-input {
432 :deep(.el-input__wrapper:hover),
433 :deep(.el-input__wrapper.is-focus),
434 :deep(.el-input__wrapper) {
435 border-radius: var(--Radius-radius-md, 8px);
436 border: 1px solid var(--map-base-dusk-stroke--2);
437 background: transparent;
438 box-shadow: none;
439 padding: 0 12px;
440 transition: none;
441 cursor: default;
442 }
443 :deep(input) {
444 border: none;
445 background: transparent;
446 cursor: default;
447 box-shadow: none;
448
449 &:focus {
450 outline: none;
451 border: 1px solid transparent;
452 background: none;
453 box-shadow: none;
454 }
455 }
456 }
457
458 // Select inputs styling to prevent focus
459 .ivyforms-form-item-select.el-select {
460 cursor: default;
461 :deep(.el-select__wrapper:hover),
462 :deep(.el-select__wrapper.is-focus),
463 :deep(.el-select__wrapper) {
464 border-radius: var(--Radius-radius-md, 8px);
465 border: 1px solid var(--map-base-dusk-stroke--2);
466 background: transparent !important;
467 box-shadow: none;
468 padding: 0 12px;
469 transition: none;
470 cursor: default;
471 }
472 :deep(.el-input) {
473 cursor: default;
474 }
475 :deep(.el-input__wrapper) {
476 cursor: default;
477 box-shadow: none;
478 background: transparent !important;
479 }
480 :deep(.el-input__inner) {
481 cursor: default;
482 &:focus {
483 outline: none;
484 border: 1px solid transparent;
485 background: none;
486 box-shadow: none;
487 }
488 }
489 :deep(.el-select__caret) {
490 cursor: default;
491 }
492 }
493 }
494 }
495 </style>
496