1
1
<script setup lang="ts">
2
- import type { MissingResourceBehavior } from ' @getodk/xforms-engine' ;
2
+ import type {
3
+ ChunkedSubmissionResult ,
4
+ MissingResourceBehavior ,
5
+ MonolithicSubmissionResult ,
6
+ } from ' @getodk/xforms-engine' ;
3
7
import { initializeForm , type FetchFormAttachment , type RootNode } from ' @getodk/xforms-engine' ;
4
8
import Button from ' primevue/button' ;
5
9
import Card from ' primevue/card' ;
6
10
import PrimeMessage from ' primevue/message' ;
7
- import { computed , provide , reactive , ref , watchEffect , type ComponentPublicInstance } from ' vue' ;
11
+ import type { ComponentPublicInstance } from ' vue' ;
12
+ import { computed , getCurrentInstance , provide , reactive , ref , watchEffect } from ' vue' ;
8
13
import { FormInitializationError } from ' ../lib/error/FormInitializationError.ts' ;
9
14
import FormLoadFailureDialog from ' ./Form/FormLoadFailureDialog.vue' ;
10
15
import FormHeader from ' ./FormHeader.vue' ;
11
16
import QuestionList from ' ./QuestionList.vue' ;
12
17
13
18
const webFormsVersion = __WEB_FORMS_VERSION__ ;
14
19
15
- const props = defineProps < {
20
+ interface OdkWebFormsProps {
16
21
formXml: string ;
17
22
fetchFormAttachment: FetchFormAttachment ;
18
23
missingResourceBehavior? : MissingResourceBehavior ;
19
- }>();
20
- const emit = defineEmits ([' submit' ]);
24
+
25
+ /**
26
+ * Note: this parameter must be set when subscribing to the
27
+ * {@link OdkWebFormEmits.submitChunked | submitChunked} event.
28
+ */
29
+ submissionMaxSize? : number ;
30
+ }
31
+
32
+ const props = defineProps <OdkWebFormsProps >();
33
+
34
+ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- evidently a type must be used for this to be assigned to a name (which we use!); as an interface, it won't satisfy the `Record` constraint of `defineEmits`.
35
+ type OdkWebFormEmits = {
36
+ submit: [submissionPayload : MonolithicSubmissionResult ];
37
+ submitChunked: [submissionPayload : ChunkedSubmissionResult ];
38
+ };
39
+
40
+ /**
41
+ * Supports {@link isEmitSubscribed } .
42
+ *
43
+ * @see
44
+ * {@link https://mokkapps.de/vue-tips/check-if-component-has-event-listener-attached }
45
+ *
46
+ * Usage here is intentionally different from the linked article: for reasons
47
+ * unknown, {@link getCurrentInstance } returns `null` called in a
48
+ * {@link computed } function body (or any function body), but produces the
49
+ * expected value assigned to a top level value as it is here.
50
+ */
51
+ const instance = getCurrentInstance ();
52
+
53
+ type OdkWebFormEmitsEventType = keyof OdkWebFormEmits ;
54
+
55
+ /**
56
+ * A Vue _template_ event handler is subscribed with syntax like:
57
+ *
58
+ * ```vue
59
+ * <OdkWebForm @whatever-event-type="handler" />
60
+ * ```
61
+ *
62
+ * At runtime, its props key is a concatenation of the prefix "on" and the
63
+ * PascalCase variant of the same event type. Since we already
64
+ * {@link defineEmits } in camelCase, this type represents that key format.
65
+ */
66
+ type EventKey = ` on${Capitalize <OdkWebFormEmitsEventType >} ` ;
67
+
68
+ /**
69
+ * @see {@link https://mokkapps.de/vue-tips/check-if-component-has-event-listener-attached }
70
+ * @see {@link instance }
71
+ * @see {@link EventKey }
72
+ */
73
+ const isEmitSubscribed = (eventKey : EventKey ): boolean => {
74
+ return eventKey in (instance ?.vnode .props ?? {});
75
+ };
76
+
77
+ const emitSubmit = async (root : RootNode ) => {
78
+ if (isEmitSubscribed (' onSubmit' )) {
79
+ const payload = await root .prepareSubmission ({
80
+ chunked: ' monolithic' ,
81
+ });
82
+
83
+ emit (' submit' , payload );
84
+ }
85
+ };
86
+
87
+ const emitSubmitChunked = async (root : RootNode ) => {
88
+ if (isEmitSubscribed (' onSubmitChunked' )) {
89
+ const maxSize = props .submissionMaxSize ;
90
+
91
+ if (maxSize == null ) {
92
+ throw new Error (' The `submissionMaxSize` prop is required for chunked submissions' );
93
+ }
94
+
95
+ const payload = await root .prepareSubmission ({
96
+ chunked: ' chunked' ,
97
+ maxSize ,
98
+ });
99
+
100
+ emit (' submitChunked' , payload );
101
+ }
102
+ };
103
+
104
+ const emit = defineEmits <OdkWebFormEmits >();
21
105
22
106
const odkForm = ref <RootNode >();
23
107
const submitPressed = ref (false );
@@ -38,8 +122,13 @@ initializeForm(props.formXml, {
38
122
});
39
123
40
124
const handleSubmit = () => {
41
- if (odkForm .value ?.validationState .violations ?.length === 0 ) {
42
- emit (' submit' );
125
+ const root = odkForm .value ;
126
+
127
+ if (root ?.validationState .violations ?.length === 0 ) {
128
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
129
+ emitSubmit (root );
130
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
131
+ emitSubmitChunked (root );
43
132
} else {
44
133
submitPressed .value = true ;
45
134
document .scrollingElement ?.scrollTo (0 , 0 );
0 commit comments