@@ -8,7 +8,6 @@ import { initializeForm, type FetchFormAttachment, type RootNode } from '@getodk
8
8
import Button from ' primevue/button' ;
9
9
import Card from ' primevue/card' ;
10
10
import PrimeMessage from ' primevue/message' ;
11
- import type { ComponentPublicInstance } from ' vue' ;
12
11
import { computed , getCurrentInstance , provide , reactive , ref , watchEffect } from ' vue' ;
13
12
import { FormInitializationError } from ' ../lib/error/FormInitializationError.ts' ;
14
13
import FormLoadFailureDialog from ' ./Form/FormLoadFailureDialog.vue' ;
@@ -105,6 +104,8 @@ const emit = defineEmits<OdkWebFormEmits>();
105
104
106
105
const odkForm = ref <RootNode >();
107
106
const submitPressed = ref (false );
107
+ const floatingErrorActive = ref (false );
108
+ const showValidationError = ref (false );
108
109
const initializeFormError = ref <FormInitializationError | null >();
109
110
110
111
initializeForm (props .formXml , {
@@ -125,18 +126,18 @@ const handleSubmit = () => {
125
126
const root = odkForm .value ;
126
127
127
128
if (root ?.validationState .violations ?.length === 0 ) {
129
+ floatingErrorActive .value = false ;
128
130
// eslint-disable-next-line @typescript-eslint/no-floating-promises
129
131
emitSubmit (root );
130
132
// eslint-disable-next-line @typescript-eslint/no-floating-promises
131
133
emitSubmitChunked (root );
132
134
} else {
135
+ floatingErrorActive .value = true ;
133
136
submitPressed .value = true ;
134
137
document .scrollingElement ?.scrollTo (0 , 0 );
135
138
}
136
139
};
137
140
138
- const validationErrorMessagePopover = ref <ComponentPublicInstance | null >(null );
139
-
140
141
provide (' submitPressed' , submitPressed );
141
142
142
143
const validationErrorMessage = computed (() => {
@@ -149,10 +150,10 @@ const validationErrorMessage = computed(() => {
149
150
});
150
151
151
152
watchEffect (() => {
152
- if (submitPressed .value && validationErrorMessage .value ) {
153
- ( validationErrorMessagePopover .value ?. $el as HTMLElement )?. showPopover ?.() ;
153
+ if (floatingErrorActive .value && validationErrorMessage .value ?. length ) {
154
+ showValidationError .value = true ;
154
155
} else {
155
- ( validationErrorMessagePopover .value ?. $el as HTMLElement )?. hidePopover ?.() ;
156
+ showValidationError .value = false ;
156
157
}
157
158
});
158
159
</script >
@@ -186,8 +187,9 @@ watchEffect(() => {
186
187
:class="{ 'submit-pressed': submitPressed }"
187
188
>
188
189
<div class="form-wrapper">
189
- <div v-show="submitPressed && validationErrorMessage" class="error-banner-placeholder" />
190
- <PrimeMessage ref="validationErrorMessagePopover" popover="manual" severity="error" icon="icon-error_outline" class="form-error-message" :closable="false">
190
+ <div v-if="showValidationError" class="error-banner-placeholder" />
191
+ <!-- Closable error message to clear the view and avoid overlap with other elements -->
192
+ <PrimeMessage v-if="showValidationError" severity="error" icon="icon-error_outline" class="form-error-message" @close="floatingErrorActive = false">
191
193
{{ validationErrorMessage }}
192
194
</PrimeMessage>
193
195
@@ -266,6 +268,8 @@ watchEffect(() => {
266
268
}
267
269
268
270
.form-error-message.p-message.p-message-error {
271
+ position: fixed;
272
+ z-index: 100;
269
273
border-radius: 10px;
270
274
background-color: var(--error-bg-color);
271
275
border: 1px solid var(--error-text-color);
@@ -275,7 +279,7 @@ watchEffect(() => {
275
279
top: 1rem;
276
280
277
281
:deep(.p-message-wrapper) {
278
- padding: 0.75rem 0.75rem ;
282
+ padding: 8px 15px ;
279
283
flex-grow: 1;
280
284
}
281
285
@@ -349,6 +353,7 @@ watchEffect(() => {
349
353
margin: var(--wf-error-banner-gap) 1rem 0 1rem;
350
354
max-width: unset;
351
355
width: calc(100% - 2rem);
356
+ top: 0.27rem;
352
357
}
353
358
354
359
.questions-card {
0 commit comments